管理器 — Django 文档
经理
- class Manager
Manager
是向 Django 模型提供数据库查询操作的接口。 对于 Django 应用程序中的每个模型,至少存在一个 Manager
。
Manager
类的工作方式记录在 Making queries; 本文档专门涉及自定义 Manager
行为的模型选项。
经理姓名
默认情况下,Django 为每个 Django 模型类添加一个名为 objects
的 Manager
。 但是,如果您想使用 objects
作为字段名称,或者如果您想为 Manager
使用 objects
以外的名称,您可以在每个-模型基础。 要为给定类重命名 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
返回的初始 QuerySet
。
添加额外的管理器方法
添加额外的 Manager
方法是向模型添加“表级”功能的首选方法。 (对于“行级”功能——即作用于模型对象单个实例的函数——使用 模型方法 ,而不是自定义 Manager
方法。)
自定义 Manager
方法可以返回您想要的任何内容。 它不必返回 QuerySet
。
例如,这个自定义 Manager
提供了一个方法 with_counts()
,它返回所有 OpinionPoll
对象的列表,每个对象都有一个额外的 num_responses
属性作为结果聚合查询:
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()
返回具有 num_responses
属性的 OpinionPoll
对象列表。
关于此示例的另一件要注意的事情是 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
。
例如,下面的模型有 two 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()
实例附加到模型。 这是为您的模型定义常见“过滤器”的一种简单方法。
例如:
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 的几个部分(包括 :djadmin:`dumpdata`)将使用该 [ X168X] 专用于该型号。 因此,最好谨慎选择默认管理器,以避免出现覆盖 get_queryset()
导致无法检索您想要使用的对象的情况。
您可以使用 Meta.default_manager_name 指定自定义默认管理器。
如果您正在编写一些必须处理未知模型的代码,例如,在实现通用视图的第三方应用程序中,请使用此管理器(或 _base_manager),而不是假设模型具有 [ X212X] 经理。
基地经理
- Model._base_manager
不要过滤掉此类管理器子类中的任何结果
此管理器用于访问与其他模型相关的对象。 在这些情况下,Django 必须能够看到它正在获取的模型的所有对象,以便可以检索所引用的 anything。
如果覆盖 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 from_queryset(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()
将引发异常。 这是因为管理器旨在封装用于管理对象集合的逻辑。 由于您不能拥有抽象对象的集合,因此管理它们没有意义。 如果您有适用于抽象模型的功能,您应该将该功能放在抽象模型的 staticmethod
或 classmethod
中。
实施问题
无论您向自定义 Manager
添加什么功能,都必须可以制作 Manager
实例的浅拷贝; 即,以下代码必须有效:
>>> import copy
>>> manager = MyManager()
>>> my_copy = copy.copy(manager)
Django 在某些查询期间制作管理器对象的浅拷贝; 如果您的 Manager 无法复制,则这些查询将失败。
对于大多数自定义管理器来说,这不会成为问题。 如果您只是向 Manager
添加简单方法,则不太可能无意中使 Manager
的实例不可复制。 但是,如果您要覆盖 __getattr__
或控制对象状态的 Manager
对象的某些其他私有方法,则应确保不会影响 Manager
的能力] 被复制。