Django ORM 查询管理器源码解析
ORM查询管理器
对于ORM定义:对象关系映射,ObjectRelationalMapping,ORM,是一种程序设计技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。从效果上说,它其实是创建了一个可在编程语言里使用的“虚拟对象数据库”。ORM能大大简化并抽象数据库的操作.
假设django的一个工程中包含一个名为Book的模块(model),在views.py的函数中可能会写出查询语句:
#views.py defindex(request): book_set=Book.objects.filter(id=1) 或者 book_set=Book.objects.all() ......
ORM的作用就是这样,并不需要写更复杂的SQL语句,所有的的事情都被ORM代劳了.
上面中,Book实际上是一个Model实例,但先是从Book.objects开始说起.Book.objects实际上是一个Manager类实例,每个Model都会有一个,用户的查询操作几乎是从这里开始的.万万可以将Model实例理解为关系表中的一个表项数据,而Manager实例可以理解数据库查询的入口.
实际上,无论如何都在Model类的源码中找到任何objects属性的字眼,因此它肯定是在某个时间点上增加的.可以在django.db.models.manager.py中找到下面的函数:
这个函数确保每一个model都有一个管理器objects
defensure_default_manager(sender,**kwargs): ...... ifnotgetattr(cls,'_default_manager',None): #Createthedefaultmanager,ifneeded. try: cls._meta.get_field('objects') raiseValueError("Model%smustspecifyacustomManager,becauseithasafieldnamed'objects'"%cls.__name__) exceptFieldDoesNotExist: pass """ 关键的一步,将一个Manager实例挂钩到cls.objects,将model.add_to_class()方法罗列如下; defadd_to_class(cls,name,value): ifhasattr(value,'contribute_to_class'): value.contribute_to_class(cls,name) else: setattr(cls,name,value) 关键是Manager有contribute_to_class()方法,由此看来,model.objects并不是一个Manager实例,实际上他是一个ManagerDescriptor实例. """ cls.add_to_class('objects',Manager()) cls._base_manager=cls.objects elifnotgetattr(cls,'_base_manager',None): default_mgr=cls._default_manager.__class__ if(default_mgrisManageror getattr(default_mgr,"use_for_related_fields",False)): cls._base_manager=cls._default_manager else: #Defaultmanagerisn'taplainManagerclass,orasuitable #replacement,sowewalkupthebaseclasshierarchyuntilwehit #somethingappropriate. forbase_classindefault_mgr.mro()[1:]: if(base_classisManageror getattr(base_class,"use_for_related_fields",False)): cls.add_to_class('_base_manager',base_class()) return
由此可以发现,Model.objects在这个时候被添加了.因此用户可以在代码中使用Book.objects.至于这个函数在何时被调用,待后面会详述django内部的信号机制.暂且你可以将其理解为在django服务器启动的时候,这些会被自动调用就好了.
Manager实现
Manager保护技法
如果可以在book_set=Book.objects.filter(id=1)这一句上设置断点,并stepinto的时候,发现Book.objects实际上的实际上不是一个Manager实例,而是一个ManagerDescriptor实例,这是django特意为Manager做的一层包装.为什么要这么做?
django规定,只有Model类可以使用objects,Model类实例不可以.请注意区分类和类实例之间的区别.
我认为这样做是有道理的.Book.objects.filter(id=1)返回的是QuerySet对象,而QuerySet对象可以看成是Model实例的集合,也就是book_set是Model实例的集合.假使「Model类的实例可以使用objects属性」,即「从一本书中查询书」这在语意上不通过.只能是「从书的集合(Book)中查询书」.
所以django用ManagerDescriptor特意为Manager做的一层包装.可以在django.db.models.manager.py中找到
ManagerDescriptor的实现:
classManagerDescriptor(object): """
很经典的掩饰,为Manager特殊设定Descriptor,从而,只能让类访问,而不能让类的实例来访问.具体是靠__get__(self,instance,type=None)方法来实现来的:第二个参数instance,当class.attr的时候,instance为None;当obj.attr的时候,instance为obj.
""" #Thisclassensuresmanagersaren'taccessibleviamodelinstances. #Forexample,Poll.objectsworks,butpoll_obj.objectsraisesAttributeError. def__init__(self,manager): self.manager=manager def__get__(self,instance,type=None): ifinstance!=None: raiseAttributeError("Managerisn'taccessiblevia%sinstances"%type.__name__) returnself.manager
所要详述的是__get__()函数.python的语法里有修饰器(descriptor)这么一说,而python的属性类型就是这么实现的.descriptor实现__get__(),__set__(),接着将其添加到一个类中.譬如下面的例子:
classCelsius(object): def__init__(self,value=0.0): self.value=float(value) def__get__(self,instance,owner): printinstance,owner returnself.value def__set__(self,instance,value): printinstance,value self.value=float(value) classTemperature(object): celsius=Celsius() t=Temperature() t.celsius Temperature.celsius
当对descriptor赋值的时候,其本身__set__会被调用,取值的时候__get__()会被调用.__set__,__get__函数的instance参数即为类实例(所以,t.cellsius调用__get__()的时候,instance参数是t,Temperature.celsius调用__get__()的时候,instance参数是Temperature).
所以,可以通过判断instance来判断调用者是否是类实例.也就由此可以拒绝类实例的访问,发现ManagerDescriptor就是这么实现的.
总结
Book.objects实际上是一个Manager,实际上的实际上却是一个ManagerDescriptor,但真正起作用的还是Manager,ManagerDescriptor是修饰器,是django的保护技法.
从Manager的实现来看,它的多数函数会返回QuerySet对象,而且透漏了一个重点:QuerySet对象可以看成是Model实例的集合.
我已经在github备份了Django源码的注释:Decode-Django,有兴趣的童鞋fork吧.
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。