浅析 DjangoModel 设计禅道
前言
在阅读源码前一直有个疑问,我一直好奇DjangoModel
是如何将字段转换成属性? ... 带着这样的疑问,我开始阅读Django框架Model
部分源码。
在通过简单的阅读剖析后,愈发感受到
Django
框架设计的精妙与优美。
单刀直入
与往常一样,剖析前先寻找一个适合的切入点,打开VScode
,在引入model
那一行代码按下command + b
..
我看到这一行陌生的写法
metaclass=ModelBase
# 版本: Django 2.2.2 class Model(metaclass=ModelBase):
原来是你!
在看到这么一个陌生写法后,我决定对ModelBase
类,进行分析。
ModelBase继承了type
,实现了5个方法
def __new__(cls, name, bases, attrs, **kwargs):
...
def add_to_class(cls, name, value):
...
def _prepare(cls):
...
def _base_manager(cls):
...
def _default_manager(cls):
...
Python原类
这五个方法中,重点在
__new__
这个方法,而且能够实现字段生成属性的功能也来源于此。
随着查阅资料,了解到一个之前没接触到的?姿势点:原类
。
那什么是原类?
A metaclass is the class of a class.
原类是
类的类
.
原来在Python世界里,所有的类都是由type
创建生成的!而继承type实现__new__
方法,是生成类其中一种方法。omgomgomgomg.(在没了解之前,我一直以为type只是用于判断类型?)
这里的new做了什么?
回到源码,简单分析一下创建都做了些什么.
def __new__(cls, name, bases, attrs, **kwargs):
# 继承__new__
super_new = super().__new__
# 判断子类是否是modelBase
parents = [b for b in bases if isinstance(b, ModelBase)]
if not parents:
return super_new(cls, name, bases, attrs)
# 创建__module__
module = attrs.pop('__module__')
new_attrs = {'__module__': module}
# 创建classcell
classcell = attrs.pop('__classcell__', None)
if classcell is not None:
new_attrs['__classcell__'] = classcell
# 对Meta属性的处理
attr_meta = attrs.pop('Meta', None)
# Pass all attrs without a (Django-specific) contribute_to_class()
# method to type.__new__() so that they're properly initialized
# (i.e. __set_name__()).
contributable_attrs = {}
for obj_name, obj in list(attrs.items()):
if _has_contribute_to_class(obj):
contributable_attrs[obj_name] = obj
else:
new_attrs[obj_name] = obj
# 建立一个新的class
new_class = super_new(cls, name, bases, new_attrs, **kwargs)
# 获取Meta里的abstract
abstract = getattr(attr_meta, 'abstract', False)
meta = attr_meta or getattr(new_class, 'Meta', None)
# 获取_meta属性
base_meta = getattr(new_class, '_meta', None)
app_label = None
# 加载配置附加到attach里
app_config = apps.get_containing_app_config(module)
# 如果meta里面没有app_label
if getattr(meta, 'app_label', None) is None:
# 如果app_config里面也是空的
if app_config is None:
# 如果Meta里的abstract也是空的
if not abstract:
raise RuntimeError(
"Model class %s.%s doesn't declare an explicit "
"app_label and isn't in an application in "
"INSTALLED_APPS." % (module, name)
)
else:
# app_config不为空则覆盖
app_label = app_config.label
new_class.add_to_class('_meta', Options(meta, app_label))
...(偷懒省略)
new_class._prepare()
# 新class调用apps.register_model进行注册
new_class._meta.apps.register_model(new_class._meta.app_label, new_class)
return new_class
这一部分代码工作主要是: 按照一些特定的方式创建类属性,重点 __module__
与Meta
的处理。
Model主体
弄明白了ModelBase
的工作后,再来简单看看主体部分做了什么
class Model(metaclass=ModelBase):
def __init__(self, *args, **kwargs):
cls = self.__class__
opts = self._meta
_setattr = setattr
# 自我描述类
# 实现__repr__与__str__
_DEFERRED = DEFERRED
pre_init.send(sender=cls, args=args, kwargs=kwargs)
# 重写默认__get__方法, 读取缓存
self._state = ModelState()
# 返回模型及其父项上所有具体字段的列表。
if len(args) > len(opts.concrete_fields):
# Daft, but matches old exception sans the err msg.
raise IndexError("Number of args exceeds number of fields")
if not kwargs:
# 所有字段的迭代器
for val, field in zip(args, fields_iter):
# 如果val是描述类 跳过
if val is _DEFERRED:
continue
# 设置属性
_setattr(self, field.attname, val)
else:
# 设置属性
fields_iter = iter(opts.fields)
for val, field in zip(args, fields_iter):
if val is _DEFERRED:
continue
_setattr(self, field.attname, val)
kwargs.pop(field.name, None)
# 对属性进行处理
for field in fields_iter:
is_related_object = False
# Virtual field
if field.attname not in kwargs and field.column is None:
continue
if kwargs:
# 判断是否为远程连接属性 (one to one, many to many 等类型)
# 下面是一些具体处理
if isinstance(field.remote_field, ForeignObjectRel):
try:
# Assume object instance was passed in.
# 假设传递了实例
rel_obj = kwargs.pop(field.name)
is_related_object = True
except KeyError:
try:
val = kwargs.pop(field.attname)
except KeyError:
val = field.get_default()
else:
# Object instance was passed in. Special case: You can
# pass in "None" for related objects if it's allowed.
if rel_obj is None and field.null:
val = None
# 普通类型的处理
else:
try:
val = kwargs.pop(field.attname)
except KeyError:
val = field.get_default()
else:
# 返回字段默认结果
val = field.get_default()
# 针对实例或非实例对象进行属性设置
if is_related_object:
if rel_obj is not _DEFERRED:
_setattr(self, field.name, rel_obj)
else:
if val is not _DEFERRED:
_setattr(self, field.attname, val)
...(偷懒省略)
super().__init__()
post_init.send(sender=cls, instance=self)
这一部分代码主要工作内容:遍历属性,对不同的属性做不同的设置操作.
结语
在弄明白DjangoModel
是如何将字段转换成属性? 问题后,对Model
部分的分析就暂时告一段落了。
总结:Model将字段生成类是由ModelBase
生成类属性,再由Model
遍历属性,对不同属性进行设置。 完成这样一个功能。
最后建议: command + b
一把梭,哪里不会b
哪里。
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: