在django中我们通常使用models.objects的相关方法来完成对数据库的增删改查操作,这里我们就来看下这些简单方法的背后是如何实现的。
要理解当我们使用models.objects时,objects代表什么,我们知道models表示自定义的表,继承自django.db.models.base.Model这个类,那么猜测objects应该是Model的一个属性,注意这个属性并不是Model基类中定义的,来看下Model类的声明,以及相关上下文:
def with_metaclass(meta, *bases):
class metaclass(meta):
def __new__(cls, name, this_bases, d):
return meta(name, bases, d)
return type.__new__(metaclass, 'temporary_class', (), {})
class ModelBase(type):
# functions ...
class Model(six.with_metaclass(ModelBase)):
# functions ...
Model类,继承自with_metaclass(ModelBase),with_metaclass方法中使用type返回了一个动态创建的类,这里type是python的一个build-in class,注意并不是一个方法。我们通常用它来获取对象的类型,它的另一个更强大的功能是被用来动态的创建类:
class type(object)
| type(object) -> the object's type
| type(name, bases, dict) -> a new type
在python中,用来创建类的东西被称作元类(metaclass),python中用type这个内建类,或者它的子类来实现元类的功能。如果类中定义了__metaclass__属性,会在这个类定义时,执行元类__metaclass__指向的类)的__new__方法(如果有定义)。注意with_metaclass方法结尾处:
return type.__new__(metaclass, 'temporary_class', (), {})
这里通过type._new_,新建并返回一个类名(class._name_)为temporary_class的动态类,这里第一个参数metaclass在这里的含义是指被创建的类的元类类型,也就是说temporary_class这个动态类的元类是metaclass,它定义在with_metaclass方法中,并且继承自meta,根据Model与with_metaclass的定义可以看出meta实际上是一个名为ModelBase的基类,ModelBase类继承自type,而metaclass继承自ModelBase,它同样是一个元类。
不过这里并没有出现我们之前提到的__metaclass__属性,在网上查阅了相关的文档,找到以下说明:
Create a new class with base class base and metaclass metaclass. This is designed to be used in class declarations like this:
from six import with_metaclass
class Meta(type):
pass
class Base(object):
pass
class MyClass(with_metaclass(Meta, Base)):
pass
This is needed because the syntax to attach a metaclass changed between Python 2 and 3:
Python 2:
class MyClass(object):
__metaclass__ = Meta
Python 3:
class MyClass(metaclass=Meta):
pass
The with_metaclass() function makes use of the fact that metaclasses are:
1) inherited by subclasses
2) a metaclass can be used to generate new classes
it effectively creates a new base class by using the metaclass as a factory to generate an empty class
文中提到使用with_metaclass来创建一个拥有基类base以及元类metaclass新类,with_metaclass的必要性在于元类的语法在python2和pyhton3中有改变,并且指出如何使用with_metaclass实现元类功能。
现在我们知道了Model实际由metaclass动态生成的,在这个类定义时,执行元类的__new__方法,由元类来创建我们定义的类。我们来看下这里的元类metaclass的__new__方法,它会直接返回meta类实例,也就是ModelBase的实例,而在python创建类实例时,会先调用__new__方法,接着会在__new__返回的实例上进行__init__方法,完成实例的初始化,通过meta(name, bases, d)会调用ModelBase的__new__方法。到这里,我们需要再梳理一下:
假设我们自定义的一个User类,继承自django.db.models.base.Model,而Model类本身继承自with_metaclass(ModelBase),这个方法返回由metaclass创建的一个动态类,metaclass是一个元类,继承自ModelBase,在其构造函数中(metaclass._new_)新建并返回一个ModelBase实例,也就是说,我们自定义的User类最终会通过这个名为ModelBase的元类来创建。
ModelBase.__new__方法定义的较为复杂,这里不单独展示出来了,它主要完成的功能如下:
- 使用元类(ModelBase)新建一个类类型new_class
- 根据new_class类的__module__属性,查找其app_config(根据settings.py中INSTALLED_APP中的APP加载到的相关信息,具体可以查看Command模块相关代码)
- 设置new_class类的_meta属性,new_class.add_to_class('_meta', Opt ions(meta, **kwargs))
- 根据创建new_class时的attrs,设置其属性,比如表中各字段的Field
- 设置Model的object属性,cls.add_to_class('objects', Manager())
其在设置Manager实例时,通过ensure_default_manager方法完成:
# django.db.models.manager.py
def ensure_default_manager(cls):
if cls._meta.abstract:
setattr(cls, 'objects', AbstractManagerDescriptor(cls))
return
elif cls._meta.swapped:
setattr(cls, 'objects', SwappedManagerDescriptor(cls))
return
if not getattr(cls, '_default_manager', None):
if any(f.name == 'objects' for f in cls._meta.fields):
raise ValueError(
"Model %s must specify a custom Manager, because it has a "
"field named 'objects'" % cls.__name__
)
# Create the default manager, if needed.
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
raise AssertionError(
"Should never get here. Please report a bug, including your "
"model and model manager setup."
)
参数cls表示自定义的Models类(由ModelBase.__new__创建),来看下设置objects属性的语句及其实现:
cls.add_to_class('objects', Manager())
class ModelBase(type):
# other functions ...
def add_to_class(cls, name, value):
# We should call the contribute_to_class method only if it's bound
if not inspect.isclass(value) and hasattr(value, 'contribute_to_class'):
value.contribute_to_class(cls, name)
else:
setattr(cls, name, value)
# other functions ...
在add_to_class方法中,value为Manager实例,它继承自BaseManager类,而BaseManager中有定义contribute_to_class方法,接着调用它的contribute_to_class方法对objects进行设置:
class BaseManager(object):
# other functions ...
def contribute_to_class(self, model, name):
# TODO: Use weakref because of possible memory leak / circular reference.
self.model = model
if not self.name:
self.name = name
# Only contribute the manager if the model is concrete
if model._meta.abstract:
setattr(model, name, AbstractManagerDescriptor(model))
elif model._meta.swapped:
setattr(model, name, SwappedManagerDescriptor(model))
else:
# if not model._meta.abstract and not model._meta.swapped:
setattr(model, name, ManagerDescriptor(self))
if (not getattr(model, '_default_manager', None) or
self.creation_counter < model._default_manager.creation_counter):
model._default_manager = self
abstract = False
if model._meta.abstract or (self._inherited and not self.model._meta.proxy):
abstract = True
model._meta.managers.append((self.creation_counter, self, abstract))
# other functions ...
实际的设置语句是:
setattr(model, name, ManagerDescriptor(self))
model表示新建的model类,name表示'object'字符串,self表示的是Manager实例本身,ManagerDescriptor从名称来看是一个描述符,用来修饰Manager实例。这里有疑问的是为什么不将object属性直接设置成Manager实例,即:
setattr(model, name, self)
而是要设置为一个描述符,通过描述符来修饰Manager实例,我们先来看下ManagerDescription的定义:
class ManagerDescriptor(object):
# 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 is not None:
raise AttributeError("Manager isn't accessible via %s instances" % type.__name__)
return self.manager
这里需要解释下__get__方法的参数,instance表示类实例,type表示类类型,当我们通过类实例来访问属性时,instance表示的是实例本身,而当我们通过类名来访问属性时,instance为None。在__get__定义中,可以看到如果是实例来调用它时,那么会抛出异常,也就是说,在django中,只有 Model 类可以使用 objects, 而Model 类的实例则不行。
结合上下文,现在我们知道objects实际上是一个Manger类实例,并在使用中需要以类属性的方式调用而非实例属性,来看下Manager类的声明,以及相关上下文:
# django.db.models.manager.py
class Manager(BaseManager.from_queryset(QuerySet)):
# functions ...
class BaseManager(object):
# other function ...
@classmethod
def from_queryset(cls, queryset_class, class_name=None):
if class_name is None:
class_name = '%sFrom%s' % (cls.__name__, queryset_class.__name__)
class_dict = {
'_queryset_class': queryset_class,
}
class_dict.update(cls._get_queryset_methods(queryset_class))
return type(class_name, (cls,), class_dict)
@classmethod
def _get_queryset_methods(cls, queryset_class):
def create_method(name, method):
def manager_method(self, *args, **kwargs):
return getattr(self.get_queryset(), name)(*args, **kwargs)
manager_method.__name__ = method.__name__
manager_method.__doc__ = method.__doc__
return manager_method
new_methods = {}
predicate = inspect.isfunction if six.PY3 else inspect.ismethod
for name, method in inspect.getmembers(queryset_class, predicate=predicate):
# Only copy missing methods.
if hasattr(cls, name):
continue
# Only copy public methods or methods with the attribute `queryset_only=False`.
queryset_only = getattr(method, 'queryset_only', None)
if queryset_only or (queryset_only is None and name.startswith('_')):
continue
# Copy the method onto the manager.
new_methods[name] = create_method(name, method)
return new_methods
# other function ...
我们已经知道type可以用来动态的生成一个类,在这里Manager继承自这个动态生成的类,结合上下文的参数
type(class_name, (cls,), class_dict)
返回的类继承自BaseManger,类名(class._name_)为BaseManagerFromQuerySet,类的属性通过BaseManager._get_queryset_methods方法进行设置,通过代码可以看出这个方法实际是将QuerySet类的一些方法设置成自身的方法,那么在我们使用model.objects时,实际是通过QuerySet这个类进行操作的,QuerySet类包含了我们通过obejcts查询的方法:all、count、filter、create、exists、update、values、bulk_create、get_or_create等等。篇幅有限,这里只列出QuerySet中一些常见方法的声明:
# django.db.models.query.py
class QuerySet(object):
def __init__(self, model=None, query=None, using=None, hints=None):
# 初始化
def __len__(self):
# 实现len方法
def __iter__(self):
# 迭代
def __bool__(self):
# 用于if判空
def __getitem__(self, k):
# 用于切片
def iterator(self):
# 迭代方法
def aggregate(self, *args, **kwargs):
# 聚合方法
def count(self):
# 统计个数
def get(self, *args, **kwargs):
# 获取
def create(self, **kwargs):
# 新建
def bulk_create(self, objs, batch_size=None):
# 批量新建
def update(self, **kwargs):
# 更新
def exists(self):
# 存在与否
def all(self):
# 所有集合
def filter(self, *args, **kwargs):
# 根据条件过滤集合
def exclude(self, *args, **kwargs):
# 根据条件排除集合
# other functions ...
先来看看查询相关操作,在对all、count、filter等方法抽丝剥茧之后,会发现它们都是通过QuerySet中一个名为query的属性变量来完成的,query是一个sql.Query的类实例,有如下定义:
# django.db.models.sql.query.py
class Query(object):
"""
A single SQL query.
"""
alias_prefix = 'T'
subq_aliases = frozenset([alias_prefix])
query_terms = QUERY_TERMS
compiler = 'SQLCompiler'
def get_compiler(self, using=None, connection=None):
if using is None and connection is None:
raise ValueError("Need either using or connection")
if using:
connection = connections[using]
return connection.ops.compiler(self.compiler)(self, connection, using)
# other function ...
Query的类属性compiler表示SQLCompiler类,在使用objects进行查询时,首先通过query.get_compiler获取到SQLCompiler类实例,再由SQLCompiler.execute_sql方法完成表的查询操作,定义如下:
# django.db.models.sql.compiler.py
class SQLCompiler(object):
# other functions ...
def execute_sql(self, result_type=MULTI):
if not result_type:
result_type = NO_RESULTS
try:
sql, params = self.as_sql()
if not sql:
raise EmptyResultSet
except EmptyResultSet:
if result_type == MULTI:
return iter([])
else:
return
cursor = self.connection.cursor()
try:
cursor.execute(sql, params)
except Exception:
cursor.close()
raise
if result_type == CURSOR:
return cursor
if result_type == SINGLE:
try:
val = cursor.fetchone()
if val:
return val[0:self.col_count]
return val
finally:
cursor.close()
if result_type == NO_RESULTS:
cursor.close()
return
result = cursor_iter(
cursor, self.connection.features.empty_fetchmany_value,
self.col_count
)
if not self.connection.features.can_use_chunked_reads:
try:
return list(result)
finally:
cursor.close()
return result
# other functions ...
在execute_sql方法中as_sql会创建查询语句以及执行它所需的参数,由cursor来执行查询语句并将结果返回。这里的cursor是通过connection获取的,它是一个DatabaseWrapper类对象,继承自BaseDatabaseWrapper,我们结合之前的文章已经介绍过,调用cursor方法会去创建真正的连接,在这里,不同的数据库类型需要安装不同的第三方python包完成数据库的操作,比如mysql需要MySQLdb,oracle需要cx_Oracle,具体的数据库操作还是需要通过这些第三方的包来完成。
通过对源码简单的分析,我们了解了使用model.objects来对model对应的表进行查询时,django后台是如何实现的,接着我们来看下如果通过model.objects完成create/update/delete等增删改等操作是如何实现的。我们已经知道objects返回Manager实例,它实际是通过QuerySet类实例提供的方法来进行增删改查的,那么这些对数据进行写的操作,同样由QuerySet完成,首先来看下create/bulk_create等插入数据的方法,只关注数据库相关的操作,抽丝剥茧后,会发现最终都会通过_insert这个私有方法来进行实际的插入,这里的步骤与查询类似,先通过get_compiler获取到SQLCompiler类实例,再由SQLCompiler.execute_sql方法完成插入操作:
class QuerySet(object):
# other functions...
def _insert(self, objs, fields, return_id=False, raw=False, using=None):
"""
Inserts a new record for the given model. This provides an interface to
the InsertQuery class and is how Model.save() is implemented.
"""
self._for_write = True
if using is None:
using = self.db
query = sql.InsertQuery(self.model)
query.insert_values(fields, objs, raw=raw)
return query.get_compiler(using=using).execute_sql(return_id)
# other functions...
这里query是InsertQuery类实例,继承自Query类:
# django.db.models.sql.subqueries.py
class InsertQuery(Query):
compiler = 'SQLInsertCompiler'
# functions ...
调用get_compiler方法时,会返回SQLInsertCompiler类实例,它继承自SQLCompiler类,并重写了父类的execute_sql和as_sql,用以完成插入的操作,而实际的插入操作,仍然由各自数据库对应的python包来实现。
与create方法类似,通过models.objects进行update和delete相关操作时,同样有UpdateQuery和DeleteQuery类实例,继承自Query类,在执行update/delete时,创建相应的SQLUpdateCompiler和SQLDeleteCompiler实例,重写execute_sql和as_sql方法完成相应的更新和删除操作,这里不再赘述。
这节我们分析了django model相关的源码,主要了解了在通过model.objects进行CURD操作时,背后的处理逻辑,实际是通过QuerySet这个代理类来完成的,而最终会通过我们之前分析到的connections相关操作,来与数据库进行交互。