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

相关推荐
大邳草民9 小时前
Django 的动态特性:从 Python 动态机制到框架设计思想
笔记·python·django
麦麦大数据9 小时前
D026 vue3+django 论文知识图谱推荐可视化系统 | vue3+vite前端|neo4j 图数据库
前端·django·vue3·知识图谱·推荐算法·论文文献·科研图谱
Q_Q196328847514 小时前
python+uniapp基于微信美食点餐系统小程序
spring boot·python·微信·django·flask·uni-app·node.js
麦麦大数据16 小时前
D025 摩托车推荐价格预测可视化系统|推荐算法|机器学习|预测算法|用户画像与数据分析
mysql·算法·机器学习·django·vue·推荐算法·价格预测
workflower1 天前
单元测试-例子
java·开发语言·算法·django·个人开发·结对编程
唐古乌梁海1 天前
【python】在Django中,执行原生SQL查询
python·sql·django
小宁爱Python1 天前
Django Web 开发系列(二):视图进阶、快捷函数与请求响应处理
前端·django·sqlite
Q_Q19632884752 天前
python+uniapp基于微信小程序的助眠小程序
spring boot·python·小程序·django·flask·uni-app·node.js
小宁爱Python2 天前
Django Web 开发系列(一):视图基础与 URL 路由配置全解析
后端·python·django