一、前言
在 Django ORM 中,模型的查询逻辑通常由 Manager
与 QuerySet
两部分组成。
其中:
-
Manager
是模型与数据库交互的"入口",提供查询的起点(如Model.objects.all()
)。 -
QuerySet
是真正实现各种查询逻辑的"核心",负责链式调用和延迟执行。
Django 为了让开发者能 在自定义 QuerySet 的同时,不失去 Manager 的便利性 ,提供了一个非常巧妙的机制:
👉 Manager.from_queryset()
二、函数源码(Django 4.x)
以下为 Django 4.0 起的 from_queryset
实现,位于 django/db/models/manager.py
:
python
@classmethod
def from_queryset(cls, queryset_class, class_name=None):
if class_name is None:
class_name = '%sFrom%s' % (cls.__name__, queryset_class.__name__)
return type(class_name, (cls,), {
'_queryset_class': queryset_class,
**cls._get_queryset_methods(queryset_class),
})
三、功能概述
这个方法的核心功能是:
动态创建一个新的 Manager 子类,并将指定的 QuerySet 类的方法"注入"进去。
换句话说:
python
MyManager = models.Manager.from_queryset(MyQuerySet)
等价于:
python
class MyManager(models.Manager):
_queryset_class = MyQuerySet
# 自动生成方法
def my_custom_method(self, *args, **kwargs):
return self.get_queryset().my_custom_method(*args, **kwargs)
四、源码逐行解析
1. 定义方法签名
python
@classmethod
def from_queryset(cls, queryset_class, class_name=None):
-
cls
:调用该方法的 Manager 类(例如models.Manager
或自定义 Manager)。 -
queryset_class
:开发者自定义的QuerySet
子类。 -
class_name
:可选,用于自定义生成类的名称。
2. 自动生成类名
python
if class_name is None:
class_name = '%sFrom%s' % (cls.__name__, queryset_class.__name__)
Django 会生成一个新的 Manager 类名,例如:
python
Manager.from_queryset(MyQuerySet)
自动命名为:
python
ManagerFromMyQuerySet
这样可以清晰地区分不同来源的 Manager 类型。
3. 动态创建新类
python
return type(class_name, (cls,), {
'_queryset_class': queryset_class,
**cls._get_queryset_methods(queryset_class),
})
这里是关键所在:
-
type(class_name, bases, attrs)
是 Python 的动态类创建方式。 -
(cls,)
表示继承自当前 Manager 类。 -
'_queryset_class': queryset_class
保存 QuerySet 类型。 -
**cls._get_queryset_methods(queryset_class)
把 QuerySet 中的方法都转换成 Manager 方法。
4. _get_queryset_methods
的作用
在同一文件中,_get_queryset_methods
的实现如下(简化版):
python
@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)
return manager_method
new_methods = {}
for name, method in queryset_class.__dict__.items():
if callable(method) and not name.startswith('_'):
new_methods[name] = create_method(name, method)
return new_methods
👉 它的职责是:
-
扫描
queryset_class
的所有公开方法; -
为每个方法生成一个同名的 Manager 包装方法;
-
当调用
Manager.some_method()
时,实际上执行QuerySet.some_method()
。
五、执行流程图
python
┌─────────────────────────────┐
│ 你定义一个自定义 QuerySet │
│ class MyQuerySet(QuerySet): │
│ def active(self): ... │
└──────────────┬──────────────┘
│
▼
┌──────────────────────────────────────────┐
│ 通过 Manager.from_queryset 动态生成类 │
│ MyManager = Manager.from_queryset(MyQS) │
└──────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ Django 动态生成 ManagerFromMyQuerySet 类 │
│ 并注入 MyQuerySet 中的所有方法 │
└──────────────┬──────────────────────────────┘
│
▼
┌───────────────────────────────┐
│ 使用时 │
│ Model.objects.active() │
│ ↓ │
│ Manager.active() │
│ ↓ │
│ MyQuerySet.active() 执行逻辑 │
└───────────────────────────────┘
六、示例代码
python
from django.db import models
class ArticleQuerySet(models.QuerySet):
def published(self):
return self.filter(status='published')
def recent(self):
return self.order_by('-created_at')
# 使用 from_queryset 生成 Manager
ArticleManager = models.Manager.from_queryset(ArticleQuerySet)
class Article(models.Model):
title = models.CharField(max_length=100)
status = models.CharField(max_length=20)
created_at = models.DateTimeField(auto_now_add=True)
objects = ArticleManager()
使用效果:
python
>>> Article.objects.published().recent()
<QuerySet [<Article: ...>, ...]>
你无需再手动写:
python
def published(self):
return self.get_queryset().published()
七、为什么设计成这样?
Django 设计 from_queryset
的目的是:
-
让 QuerySet 的自定义方法 能直接通过 Manager 调用;
-
避免重复代码;
-
保证链式调用与延迟执行特性仍然生效。
在旧版本 Django(<1.7)中,你必须自己手动定义 Manager 方法,如今 from_queryset
自动完成了这一步。
八、进阶技巧:链式 QuerySet 与 Manager 混合使用
python
class MyQuerySet(models.QuerySet):
def published(self): ...
def archived(self): ...
class MyManager(models.Manager.from_queryset(MyQuerySet)):
def custom_summary(self):
return self.get_queryset().values('status').annotate(count=models.Count('id'))
这样,你同时拥有:
-
来自
MyQuerySet
的方法; -
自定义
Manager
方法。
调用方式:
python
Model.objects.published().custom_summary()
九、总结
特性 | 说明 |
---|---|
作用 | 动态创建一个包含自定义 QuerySet 方法的 Manager |
核心技术 | Python 动态类生成 (type ) |
优势 | 避免重复包装 QuerySet 方法 |
返回值 | 新的 Manager 子类 |
常用场景 | 自定义查询逻辑(如过滤、聚合、排序) |
十、结语
from_queryset
是 Django ORM 的一处典型"优雅设计":
它利用 Python 元类机制和动态类创建,让开发者既能编写高内聚的 QuerySet 方法,又能保持调用接口简洁清晰。
在阅读源码时,你会发现 Django 的许多高级特性(如 Generic ForeignKey、QuerySet 链式执行)都是通过类似的动态机制实现的 ------ 这正是它强大且灵活的根源。