管理器
- class
Manager
[源代码] Manager
是一种接口,它赋予了 Django 模型操作数据库的能力。Django 应用中每个模型拥有至少一个Manager
。
Manager
类的文档介绍位于 执行查询;本页着重介绍自定义 Manager
行为的模型选项。
管理器名称
默认情况下,Django 为每个模型类添加了一个名为 objects
的 Manager
。不过,若你想将 objects
用作字段名,或想使用 objects
以外的 Manager
名字,就要在模型基类中重命名。要为指定类重命名 Manager
,在该模型中定义一个类型为 models.Manager
的属性。例如:
- from django.db import models
- class Person(models.Model):
- #...
- people = models.Manager()
使用这个实例模型时, Person.objects
会产生一个 AttributeError
异常,而 Person.people.all()
会返回包含所有 Person
对象的列表。
自定义管理器
继承基类 Manager
,在模型中实例化自定义 Manager
,你就可以在该模型中使用自定义的 Manager
。
有两种原因可能使你想要自定义 Manager
:添加额外的 Manager
方法,修改 Manager
返回的原始 QuerySet
。
添加额外的管理器方法
添加额外的 Manager
方法一般是为模型添加 “表级” 功能的更好方法。(对于 “行级” 功能 —— 即,只操作单个模型对象 —— 通过 模型方法,而不是自定义 Manager
的方法。)
自定义 Manager
方法能返回任何东西,没有强制它必须返回一个 QuerySet
。
例如,这个自定义 Manager
提供了一个方法 with_counts()
,它会返回包含所有 OpinionPoll
对象的列表。每个对象都有一个额外属性 num_response
,这是一次聚合查询的结果:
- from django.db import models
- class PollManager(models.Manager):
- def with_counts(self):
- from django.db import connection
- with connection.cursor() as cursor:
- cursor.execute("""
- SELECT p.id, p.question, p.poll_date, COUNT(*)
- FROM polls_opinionpoll p, polls_response r
- WHERE p.id = r.poll_id
- GROUP BY p.id, p.question, p.poll_date
- ORDER BY p.poll_date DESC""")
- result_list = []
- for row in cursor.fetchall():
- p = self.model(id=row[0], question=row[1], poll_date=row[2])
- p.num_responses = row[3]
- result_list.append(p)
- return result_list
- class OpinionPoll(models.Model):
- question = models.CharField(max_length=200)
- poll_date = models.DateField()
- objects = PollManager()
- class Response(models.Model):
- poll = models.ForeignKey(OpinionPoll, on_delete=models.CASCADE)
- person_name = models.CharField(max_length=50)
- response = models.TextField()
通过本例,你可以利用 OpinionPoll.objects.with_counts()
返回一个包含 OpinionPoll
对象的列表,每个对象都有 num_responses
属性。
本例的另一个注意事项是 Manager
方法能通过 self.model
获取所依附的模型类。
修改管理器的初始 QuerySet
Manager
的基础 QuerySet
会返回系统中所有的对象。例如,使用以下模型:
- from django.db import models
- class Book(models.Model):
- title = models.CharField(max_length=100)
- author = models.CharField(max_length=50)
……语句 Book.objects.all()
会返回数据库中所有的书。
你可以通过重写 Manager.get_queryset()
方法来覆盖 Manager
的基础 QuerySet
。 get_queryset()
返回的 QuerySet
应该包含你需要的属性。
例如,以下模型有 两个 Manager
—— 一个返回所有对象,另一个仅返回 Roald Dahl 写的书:
- # First, define the Manager subclass.
- class DahlBookManager(models.Manager):
- def get_queryset(self):
- return super().get_queryset().filter(author='Roald Dahl')
- # Then hook it into the Book model explicitly.
- class Book(models.Model):
- title = models.CharField(max_length=100)
- author = models.CharField(max_length=50)
- objects = models.Manager() # The default manager.
- dahl_objects = DahlBookManager() # The Dahl-specific manager.
使用这个实例模型时, Book.objects.all()
会返回数据库中所有的书,而 Book.dahl_objects.all()
仅返回 Roald Dahl 写的书。
当然,因为 get_queryset()
返回一个 QuerySet
对象,你可以在上面调用 filter()
, exclude()
和其它的 QuerySet
方法。所以,以下语句是等效的:
- Book.dahl_objects.all()
- Book.dahl_objects.filter(title='Matilda')
- Book.dahl_objects.count()
本例同时介绍了另一个有趣的技巧:在一个模型中使用多个管理器。你可以为一个模型添加任意多个 Manager()
。为模型定义通用 "filters" 非常简单。
例如:
- class AuthorManager(models.Manager):
- def get_queryset(self):
- return super().get_queryset().filter(role='A')
- class EditorManager(models.Manager):
- def get_queryset(self):
- return super().get_queryset().filter(role='E')
- class Person(models.Model):
- first_name = models.CharField(max_length=50)
- last_name = models.CharField(max_length=50)
- role = models.CharField(max_length=1, choices=[('A', _('Author')), ('E', _('Editor'))])
- people = models.Manager()
- authors = AuthorManager()
- editors = EditorManager()
本例允许你调用 Person.authors.all()
, Person.editors.all()
和 Person.people.all()
,返回符合期望的结果。
默认管理器
Model.
_default_manager
- 若你使用自定义
Manager
对象,注意 Django 遇到的第一个Manager
(按照你在模型中定义的顺序)会拥有一个独特的状态。Django 将类定义中的第一个Manager
视作 “默认”Manager
,Django 的几个组件(包括dumpdata
)在用到该模型时会独立地调用该Manager
。故此,选择默认管理器时要万分小心,避免遇到重写的get_queryset()
无法获取期望的结果这种情况。
你可以通过 Meta.default_manager_name
指定一个自定义的默认管理器。
若你正在编写的代码必须处理未知模型,例如,在实现了通用视图的第三方应用中使用这个管理器(或 _base_manager
),而不是假定该模型有一个名为 objects
的管理器。
基础管理器
用于访问关联对象的管理器
默认情况下,Django 访问关联对象(即 choice.question
)时使用 Model._base_manager
管理器类的实例,而不是关联对象的 _default_manager
。这是因为 Django 要检索那些可能被默认管理器筛选掉(所以无法访问)的关联对象。
若基本管理器类 (django.db.models.Manager
) 无法满足需求,你可以通过设置 Meta.base_manager_name
告诉 Django 使用哪个类。
在关联模型上执行查询时不会使用基础管理器。例如,若 来自教程 的模型 Question
有个 deleted
字段,还有一个基础管理器,用于筛选出 deleted=True
的实例。由 Choice.objects.filter(questionnamestartswith='What')
返回的查询结果集会包含关联至已删除的问题的选项。
不要在这类管理器子类中过滤掉任何结果
该管理器用于访问由其它模型关联过来的对象。这些情况下,Django 要能访问待获取模型的全部对象,这样就能检索出其指向的 任何东西。
若你重写了 get_queryset()
方法,并筛掉了全部行,Django 会返回错误的结果。所以,不要这么做。会筛选 get_queryset()
的管理器并不适合用作基础管理器。
管理器调用自定义 QuerySet 方法
因为大部分的标准 QuerySet
方法能直接从 Manager
访问,这个实例仅适用于你在自定义 QuerySet
中定义了额外方法,且在 Manager
中实现了它们:
- class PersonQuerySet(models.QuerySet):
- def authors(self):
- return self.filter(role='A')
- def editors(self):
- return self.filter(role='E')
- class PersonManager(models.Manager):
- def get_queryset(self):
- return PersonQuerySet(self.model, using=self._db)
- def authors(self):
- return self.get_queryset().authors()
- def editors(self):
- return self.get_queryset().editors()
- class Person(models.Model):
- first_name = models.CharField(max_length=50)
- last_name = models.CharField(max_length=50)
- role = models.CharField(max_length=1, choices=[('A', _('Author')), ('E', _('Editor'))])
- people = PersonManager()
本例允许你从管理器 Person.people
直接调用 authors()
和 editors()
。
创建带有 QuerySet 方法的管理器
要替换前面的要求复制 QuerySet
和 Manager
方法的方案, 可以用 QuerySet.as_manager()
创建一个 Manager
实例,拷贝了自定义 QuerySet
的方法:
- class Person(models.Model):
- ...
- people = PersonQuerySet.as_manager()
由 QuerySet.as_manager()
创建的 Manager
实例实质上等价于前面例子中的 PersonManager
。
不是每个 QuerySet
方法在 Manager
层都是有意义的;例如,我们故意阻止 QuerySet.delete()
被拷贝进 Manager
类中。
方法拷贝规则如下:
- 公开方法默认会被拷贝。
- 私有方法(以下划线打头)默认不会被复制。
queryset_only
属性值为False
的方法总是会被复制。queryset_only
属性值为True
的方法永远不会被复制。例如:
- class CustomQuerySet(models.QuerySet):
- # Available on both Manager and QuerySet.
- def public_method(self):
- return
- # Available only on QuerySet.
- def _private_method(self):
- return
- # Available only on QuerySet.
- def opted_out_public_method(self):
- return
- opted_out_public_method.queryset_only = True
- # Available on both Manager and QuerySet.
- def _opted_in_private_method(self):
- return
- _opted_in_private_method.queryset_only = False
from_queryset()
- classmethod
fromqueryset
(_queryset_class) - 对于进阶用法,你可能同时要一个自定义
Manager
和一个自定义QuerySet
。你可以通过调用Manager.from_queryset()
达成目的,这将会返回一个自定义基础Manager
的子类,带有一份自定义QuerySet
方法的拷贝:
- class BaseManager(models.Manager):
- def manager_only_method(self):
- return
- class CustomQuerySet(models.QuerySet):
- def manager_and_queryset_method(self):
- return
- class MyModel(models.Model):
- objects = BaseManager.from_queryset(CustomQuerySet)()
还可以将生成的类存储到变量中:
- CustomManager = BaseManager.from_queryset(CustomQuerySet)
- class MyModel(models.Model):
- objects = CustomManager()
自定义管理器和模型继承
下面是 Django 如何处理自定义管理器和 模型继承:
- 基类的管理器总是被子类以 Python 的普通名称解析顺序继承(子类上的属性会覆盖所有父类上的同名属性;直接父类会覆盖更上一级的,以此类推)。
- 如果没有在模型或其父类申明管理器,Django 会自动创建
objects
管理器。 - 一个类的默认管理器要么由
Meta.default_manager_name
指定,要么是模型中申明的第一个管理器,或者是直接父模型的默认管理器。如果您想通过抽象基类在一组模型上安装自定义管理器,但仍能自定义默认管理器,这些规则提供了必要的灵活性。例如,假设有此基类:
- class AbstractBase(models.Model):
- # ...
- objects = CustomManager()
- class Meta:
- abstract = True
如果您在子类中直接使用这一点,如果您在基类中没有声明任何管理器,那么 objects
将是默认的管理器:
- class ChildA(AbstractBase):
- # ...
- # This class has CustomManager as the default manager.
- pass
如果您想继承 AbstractBase
,但提供不同的默认管理器,则可以在子类上提供该默认管理器:
- class ChildB(AbstractBase):
- # ...
- # An explicit default manager.
- default_manager = OtherManager()
这里的 default_manager
是默认的。继承的 objects
管理器仍然可用,只是没有被当做默认管理器。
最后,对于这个示例,假设您想要向子类中添加额外的管理器,但是仍然使用来自 AbstractBase
的默认管理器。您不能直接在子类中添加新的管理器,因为这将覆盖默认管理器,并且您还必须显式地申明来自抽象基类的所有管理器。解决方案是将这个管理器放到另一个基类中,并在默认管理器 之后 将其引入继承层次结构:
- class ExtraManager(models.Model):
- extra_manager = OtherManager()
- class Meta:
- abstract = True
- class ChildC(AbstractBase, ExtraManager):
- # ...
- # Default manager is CustomManager, but OtherManager is
- # also available via the "extra_manager" attribute.
- pass
请注意,虽然可以在抽象模型上 定义 自定义管理器,但不能使用抽象模型 调用 任何方法。即:
- ClassA.objects.do_something()
是合法的,但:
- AbstractBase.objects.do_something()
会引发一个异常。这是因为管理器意在封装管理映射对象集合的逻辑。因为您不能拥有抽象对象的集合,所以管理抽象对象是没有意义的。如果您有适用于抽象模型的功能,则应该将该功能放在抽象模型的 静态方法
或 类方法
中。
执行关系
无论您在自定义的 Manager
中添加了什么特性,都必须能够对 Manager
实例进行简单的复制;也就是说,以下代码必须有效:
- >>> import copy
- >>> manager = MyManager()
- >>> my_copy = copy.copy(manager)
Django 在某些查询期间对管理器对象进行浅拷贝;如果您的管理器无法被复制,那么这些查询将失败。
对于大多数的资源管理器来说,这不是问题。若你只是为 Manager
添加简单的方法,一般不会疏忽地把 Manager
变的不可拷贝。但是,若重写了 Manager
对象用于控制对象状态的 getattr
或其它私有方法,你需要确认你的修改不会影响 Manager
被复制。