Django ORM 查詢管理器源碼解析
ORM 查詢管理器
對于 ORM 定義: 對象關(guān)系映射, Object Relational Mapping, ORM, 是一種程序設(shè)計(jì)技術(shù),用于實(shí)現(xiàn)面向?qū)ο缶幊陶Z言里不同類型系統(tǒng)的數(shù)據(jù)之間的轉(zhuǎn)換。從效果上說,它其實(shí)是創(chuàng)建了一個(gè)可在編程語言里使用的“虛擬對象數(shù)據(jù)庫”。ORM 能大大簡化并抽象數(shù)據(jù)庫的操作.
假設(shè) django 的一個(gè)工程中包含一個(gè)名為 Book 的模塊(model), 在 views.py 的函數(shù)中可能會(huì)寫出查詢語句:
# views.py def index(request): book_set = Book.objects.filter(id=1) 或者 book_set = Book.objects.all() ......
ORM 的作用就是這樣, 并不需要寫更復(fù)雜的 SQL 語句, 所有的的事情都被 ORM 代勞了.
上面中, Book 實(shí)際上是一個(gè) Model 實(shí)例, 但先是從 Book.objects 開始說起. Book.objects 實(shí)際上是一個(gè) Manager 類實(shí)例, 每個(gè) Model 都會(huì)有一個(gè), 用戶的查詢操作幾乎是從這里開始的. 萬萬可以將 Model 實(shí)例理解為關(guān)系表中的一個(gè)表項(xiàng)數(shù)據(jù), 而 Manager 實(shí)例可以理解數(shù)據(jù)庫查詢的入口.
實(shí)際上, 無論如何都在 Model 類的源碼中找到任何 objects 屬性的字眼, 因此它肯定是在某個(gè)時(shí)間點(diǎn)上增加的. 可以在 django.db.models.manager.py 中找到下面的函數(shù):
這個(gè)函數(shù)確保每一個(gè) model 都有一個(gè)管理器 objects
def ensure_default_manager(sender, **kwargs): ...... if not getattr(cls, '_default_manager', None): # Create the default manager, if needed. try: cls._meta.get_field('objects') raise ValueError("Model %s must specify a custom Manager, because it has a field named 'objects'" % cls.__name__) except FieldDoesNotExist: pass """ 關(guān)鍵的一步, 將一個(gè) Manager 實(shí)例掛鉤到 cls.objects, 將 model.add_to_class() 方法羅列如下; def add_to_class(cls, name, value): if hasattr(value, 'contribute_to_class'): value.contribute_to_class(cls, name) else: setattr(cls, name, value) 關(guān)鍵是 Manager 有 contribute_to_class() 方法, 由此看來, model.objects 并不是一個(gè) Manager 實(shí)例, 實(shí)際上他是一個(gè) ManagerDescriptor 實(shí)例. """ cls.add_to_class('objects', Manager()) cls._base_manager = cls.objects elif not getattr(cls, '_base_manager', None): default_mgr = cls._default_manager.__class__ if (default_mgr is Manager or getattr(default_mgr, "use_for_related_fields", False)): cls._base_manager = cls._default_manager else: # Default manager isn't a plain Manager class, or a suitable # replacement, so we walk up the base class hierarchy until we hit # something appropriate. for base_class in default_mgr.mro()[1:]: if (base_class is Manager or getattr(base_class, "use_for_related_fields", False)): cls.add_to_class('_base_manager', base_class()) return
由此可以發(fā)現(xiàn), Model.objects 在這個(gè)時(shí)候被添加了. 因此用戶可以在代碼中使用 Book.objects. 至于這個(gè)函數(shù)在何時(shí)被調(diào)用, 待后面會(huì)詳述 django 內(nèi)部的信號機(jī)制. 暫且你可以將其理解為在 django 服務(wù)器啟動(dòng)的時(shí)候, 這些會(huì)被自動(dòng)調(diào)用就好了.
Manager 實(shí)現(xiàn)
Manager 保護(hù)技法
如果可以在 book_set = Book.objects.filter(id=1) 這一句上設(shè)置斷點(diǎn), 并 step into 的時(shí)候, 發(fā)現(xiàn) Book.objects 實(shí)際上的實(shí)際上不是一個(gè) Manager 實(shí)例, 而是一個(gè) ManagerDescriptor 實(shí)例, 這是 django 特意為 Manager 做的一層包裝. 為什么要這么做 ?
django 規(guī)定, 只有 Model 類可以使用 objects, Model 類實(shí)例不可以. 請注意區(qū)分類和類實(shí)例之間的區(qū)別.
我認(rèn)為這樣做是有道理的. Book.objects.filter(id=1) 返回的是 QuerySet 對象, 而 QuerySet 對象可以看成是 Model 實(shí)例的集合, 也就是 book_set 是 Model 實(shí)例的集合. 假使 「Model 類的實(shí)例可以使用 objects 屬性」, 即「從一本書中查詢書」這在語意上不通過. 只能是「從書的集合(Book)中查詢書」.
所以 django 用 ManagerDescriptor 特意為 Manager 做的一層包裝. 可以在 django.db.models.manager.py 中找到
ManagerDescriptor 的實(shí)現(xiàn):
class ManagerDescriptor(object): """
很經(jīng)典的掩飾, 為 Manager 特殊設(shè)定 Descriptor, 從而, 只能讓類訪問, 而不能讓類的實(shí)例來訪問. 具體是靠 __get__(self, instance, type=None) 方法來實(shí)現(xiàn)來的: 第二個(gè)參數(shù) instance, 當(dāng) class.attr 的時(shí)候, instance 為 None; 當(dāng) obj.attr 的時(shí)候, instance 為 obj.
""" # This class ensures managers aren't accessible via model instances. # For example, Poll.objects works, but poll_obj.objects raises AttributeError. def __init__(self, manager): self.manager = manager def __get__(self, instance, type=None): if instance != None: raise AttributeError("Manager isn't accessible via %s instances" % type.__name__) return self.manager
所要詳述的是 __get__() 函數(shù). python 的語法里有修飾器(descriptor)這么一說, 而 python 的屬性類型就是這么實(shí)現(xiàn)的. descriptor 實(shí)現(xiàn) __get__(), __set__(), 接著將其添加到一個(gè)類中. 譬如下面的例子:
class Celsius(object): def __init__(self, value=0.0): self.value = float(value) def __get__(self, instance, owner): print instance,owner return self.value def __set__(self, instance, value): print instance,value self.value = float(value) class Temperature(object): celsius = Celsius() t = Temperature() t.celsius Temperature.celsius
當(dāng)對 descriptor 賦值的時(shí)候, 其本身 __set__ 會(huì)被調(diào)用, 取值的時(shí)候 __get__() 會(huì)被調(diào)用. __set__,__get__ 函數(shù)的 instance 參數(shù)即為類實(shí)例(所以, t.cellsius 調(diào)用 __get__() 的時(shí)候, instance 參數(shù)是 t, Temperature.celsius 調(diào)用 __get__() 的時(shí)候, instance 參數(shù)是 Temperature).
所以, 可以通過判斷 instance 來判斷調(diào)用者是否是類實(shí)例. 也就由此可以拒絕類實(shí)例的訪問, 發(fā)現(xiàn) ManagerDescriptor 就是這么實(shí)現(xiàn)的.
總結(jié)
Book.objects 實(shí)際上是一個(gè) Manager, 實(shí)際上的實(shí)際上卻是一個(gè) ManagerDescriptor, 但真正起作用的還是 Manager, ManagerDescriptor 是修飾器, 是 django 的保護(hù)技法.
從 Manager 的實(shí)現(xiàn)來看, 它的多數(shù)函數(shù)會(huì)返回 QuerySet 對象, 而且透漏了一個(gè)重點(diǎn): QuerySet 對象可以看成是 Model 實(shí)例的集合.
我已經(jīng)在 github 備份了 Django 源碼的注釋: Decode-Django, 有興趣的童鞋 fork 吧.
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
numpy展平數(shù)組ndarray.flatten()詳解
這篇文章主要介紹了numpy展平數(shù)組ndarray.flatten()詳解,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06Python之日期與時(shí)間處理模塊(date和datetime)
這篇文章主要介紹了Python之日期與時(shí)間處理模塊(date和datetime),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-02-02Django實(shí)現(xiàn)auth模塊下的登錄注冊與注銷功能
這篇文章主要介紹了Django實(shí)現(xiàn)auth模塊下的登錄注冊與注銷功能,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-10-10Tornado Web Server框架編寫簡易Python服務(wù)器
這篇文章主要為大家詳細(xì)介紹了Tornado Web Server框架編寫簡易Python服務(wù)器,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07Python實(shí)現(xiàn)自動(dòng)化網(wǎng)頁操作步驟
這篇文章主要介紹Python如何實(shí)現(xiàn)自動(dòng)化網(wǎng)頁操作,文中有詳細(xì)的流程步驟和代碼示例,對我們的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2023-06-06Python實(shí)現(xiàn)打印實(shí)心和空心菱形
今天小編就為大家分享一篇Python實(shí)現(xiàn)打印實(shí)心和空心菱形,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-11-11pandas滑動(dòng)窗口學(xué)習(xí)筆記(shift, diff, pct_change)
pandas中有3類窗口,分別是滑動(dòng)窗口rolling?、擴(kuò)張窗口expanding以及指數(shù)加權(quán)窗口ewm,下面就來詳細(xì)的介紹一下這三種的用法,感興趣的可以了解一下2024-03-03