Django from_queryset 源码深度解析:动态生成 Manager 的奥秘

一、前言

在 Django ORM 中,模型的查询逻辑通常由 ManagerQuerySet 两部分组成。

其中:

  • 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 链式执行)都是通过类似的动态机制实现的 ------ 这正是它强大且灵活的根源。

相关推荐
Mr数据杨4 小时前
【Codex】用下载中心模块管理系统导出文件与下载任务
django·codex·项目开发
171320330计算机毕设编程13 小时前
2027计算机毕设五大方向对比&选题推荐
java·ide·python·算法·django·php·推荐算法
青 春 记 忆13 小时前
零基础入门python43:Django接口测试与库存边界
python·django·后端开发
Mr数据杨14 小时前
【Codex】用智能助手问题反馈模块收集AI使用问题
人工智能·django·codex·项目开发
ikun_文2 天前
Django框架路由Router的使用
python·pycharm·django
我的xiaodoujiao3 天前
Django 基础知识详细图文教程 4-Django 视图定义与使用
开发语言·数据库·后端·测试工具·django·sqlite
2601_962180333 天前
python——Django 框架
开发语言·python·django
2601_962066493 天前
【玩转全栈】----Django模板的继承
数据库·django·sqlite
千里码aicood3 天前
Django+Vue 基于XGBoost与随机森林的沿海地区台风天气大数据预警系统实现
vue.js·随机森林·django
可乐鸡翅yeah_3 天前
hls.js 内存泄漏实战排查,直播 M3U8 长时间播放异常定位方案
开发语言·javascript·python·django·ecmascript·m3u8·m3u8在线