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 小时前
二手物品交易平台
spring boot·小程序·django
luoluoal9 小时前
基于python的基于深度学习的车俩特征分析系(源码+文档)
python·mysql·django·毕业设计·源码
@zulnger14 小时前
Django 框架(django-admin 命令详解)
python·django
WangYaolove13141 天前
基于深度学习的中文情感分析系统(源码+文档)
python·深度学习·django·毕业设计·源码
哈里谢顿1 天前
Django QuerySet 懒加载与缓存机制源码级拆解文档
django
龙腾AI白云1 天前
AI智能体搭建(3)深度搜索智能体如何搭建与设计 Agent#智能体搭建#多智能体#VLA#大模型
python·django·virtualenv·scikit-learn·tornado
践行见远1 天前
django之认证与权限
python·django
言之。1 天前
Django原子请求
数据库·django·sqlite
@zulnger1 天前
Django 框架
数据库·django·sqlite
开开心心_Every2 天前
一键隐藏窗口到系统托盘:支持任意软件摸鱼
服务器·前端·python·学习·edge·django·powerpoint