Python · 2 分钟阅读
ORM 实现原理
想真正吃透 ORM,建议先理解 Python 的两个底层机制:元类(metaclass)与描述符(descriptor)。
Model 自动获得 id 与 objects
Django 在你写 class Topic(models.Model) 时通过元类做了两件事:
- 注入
id自增主键 - 注入
objects = Manager()查询入口
这一切发生在类创建的瞬间,靠的就是 元类。
1. 元类
类的类型本身就是 type:
type(int) # <class 'type'>
type(str) # <class 'type'>
type(type) # <class 'type'>
type 还能动态创建类:
# 三参数:名称、父类元组、属性字典
A = type("A", (), {"hello": "World"})
def fun(self):
return "Hello"
B = type("B", (A,), {"fun": fun})
用元类自动改写类
class BBSMeta(type):
def __new__(mcs, name, bases, attrs):
attrs["desc"] = lambda self: "django bbs"
return super().__new__(mcs, name, bases, attrs)
class A(metaclass=BBSMeta):
pass
A().desc() # "django bbs"
Python 创建类时的元类查找顺序:
- 类自身的
metaclass - 父类中的
metaclass - 模块中的
metaclass - 最终回退到
type
Django 的 ModelBase 就是一个元类,它会在类创建时遍历 Field、生成 objects、补 id、注册到 apps。
2. 描述符
实现了 __get__ / __set__ / __delete__ 任意一个的类对象,称为描述符。
class Tracker:
def __get__(self, instance, owner):
return instance.__dict__.get(self.name)
def __set__(self, instance, value):
instance.__dict__[self.name] = value
instance.save() # 拦截赋值
Django 的 Field 是描述符家族:
| 协议 | 作用 |
|---|---|
__get__ |
读取字段值(自动 SELECT) |
__set__ |
写入字段值(标记 dirty) |
Django 5.x 的 DeferredAttribute(懒加载字段)就是一个非数据描述符的经典实现。
3. QuerySet 简述
QuerySet 内部维护一棵 查询表达式树,链式调用只是往树上挂节点;真正迭代时才编译成 SQL 并执行——这就是 惰性求值。
qs = Book.objects.filter(price__gt=50).order_by("-id")
# 此时不查数据库
list(qs) # 此处触发 SQL
一句话总结
元类负责"在类诞生时做改造",描述符负责"在属性访问时做拦截"。两者合力,让
Model既像普通 Python 类,又能映射到数据库表。