Django ORM 查询管理器源码解析
作者:捣乱小子 发布时间:2023-01-30 12:28:47
ORM 查询管理器
对于 ORM 定义: 对象关系映射, Object Relational Mapping, ORM, 是一种程序设计技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。从效果上说,它其实是创建了一个可在编程语言里使用的“虚拟对象数据库”。ORM 能大大简化并抽象数据库的操作.
假设 django 的一个工程中包含一个名为 Book 的模块(model), 在 views.py 的函数中可能会写出查询语句:
# views.py
def index(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
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
"""
关键的一步, 将一个 Manager 实例挂钩到 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)
关键是 Manager 有 contribute_to_class() 方法, 由此看来, model.objects 并不是一个 Manager 实例, 实际上他是一个 ManagerDescriptor 实例.
"""
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
由此可以发现, Model.objects 在这个时候被添加了. 因此用户可以在代码中使用 Book.objects. 至于这个函数在何时被调用, 待后面会详述 django 内部的信号机制. 暂且你可以将其理解为在 django 服务器启动的时候, 这些会被自动调用就好了.
Manager 实现
Manager 保护技法
如果可以在 book_set = Book.objects.filter(id=1) 这一句上设置断点, 并 step into 的时候, 发现 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 的实现:
class ManagerDescriptor(object):
"""
很经典的掩饰, 为 Manager 特殊设定 Descriptor, 从而, 只能让类访问, 而不能让类的实例来访问. 具体是靠 __get__(self, instance, type=None) 方法来实现来的: 第二个参数 instance, 当 class.attr 的时候, instance 为 None; 当 obj.attr 的时候, 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__() 函数. python 的语法里有修饰器(descriptor)这么一说, 而 python 的属性类型就是这么实现的. descriptor 实现 __get__(), __set__(), 接着将其添加到一个类中. 譬如下面的例子:
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
当对 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 吧.
来源:https://www.cnblogs.com/daoluanxiaozi/p/3420559.html
猜你喜欢
- 实例一--爬取页面import requestsurl="https//itemjd.com/2646846.html"
- 一.错误分类1. 语法错误也称为解析错误,发生在传统编程语言的编译时,在JavaScript中发生在解释时,这些错误是由代码中的意外字符直接
- 一、Python图像处理PIL库1.1 转换图像格式# PIL(Python Imaging Library)from PIL import
- 请问如何处理Oracle中较大的文本数据?我们可在ASP中予以解决,如在Oracle8i中文版中,建立数据表:CREATE TABLE SY
- 我们经常使用动态创建 JavaScript 的方式来实现 JavaScript 文件的无阻塞(Non-blocking)、并行下载(Para
- 保护你的ASP页面的两种办法 有时候你只想让人们从你的站点来访问你的某些页面, 而不允许他们从其它站点的非法链接中到达这些页面。在你想保护的
- 今天一个项目上需要,修改了一些属性,测试成功。<!--#include file="conn.asp"-
- 本文实例讲述了Python django框架应用中实现获取访问者ip地址。分享给大家供大家参考,具体如下:在django官方文档中有一段对r
- 简介在逛github时发现一个好玩的Go项目,彩色输出文本说明支持Linux彩色输出支持Windows彩色输出Golang IDE输出是不支
- 在程序的开发过程中,处理分页是大家接触比较频繁的事件,因为现在软件基本上都是与数据库进行挂钓的。但效率又是我们所追求的,如果是像原来那样把所
- 这个验证类的完成有很长时间了,一直没有分享给大家使用了这么长时间之后感觉挺顺手,用于一些不需要特殊效果的表单验证个人认为已经足够了,还是挺好
- 一、yield使用简析yield是一个生成器generator,返回一个interable对象。该对象具有next()方法,可以通过next
- 经常用FLASH嵌入到网页里面,那咋能躲过W3C那个家伙呢?看下面!<object type="applicati
- Brendan Eich于1995年在Netscape发明了JavaScript语言,这个语言在过去的一些年里曾是一个最被误解的语言,随着A
- 概述从今天开始我们将开启一段自然语言处理 (NLP) 的旅程. 自然语言处理可以让来处理, 理解, 以及运用人类的语言, 实现机器语言和人类
- FlashPaper 是Macromedia推出的一款电子文档类工具,通过使用本程序,你可以将需要的文档通过简单的设置转换为SWF格式的Fl
- 如下所示:#coding utf-8a=0.001 #定义收敛步长xd=1 #定义寻找步
- FrontPage 2003在功能上增强了不少,下面我们一起来看看新版本中比较突出的9个新功能。1.自定义浏览器分辨率预览检查 在
- 最近心血来潮加上有点闲情,动手写了第一个JavaScript版的俄罗斯方块Easy Tetris.先上Easy Tetris俄罗斯方块游戏截
- 如果管理网络设备很多,不可能靠人力每天去登录设备去查看是否在线。所以,可以利用python脚本通过每天扫描网络中的在线设备。可以部署在服务器