管理器

class Manager[源代码]

A Manager 是向django模型提供数据库查询操作的接口。至少一个 Manager 存在于Django应用程序中的每个模型。

Manager 课程工作记录在 进行查询 ;此文档专门涉及自定义的模型选项 Manager 行为。

经理姓名

默认情况下,Django添加了 Manager 用名字 objects 给每一个Django模型班。但是,如果您想使用 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 方法和/或修改初始值 QuerySet 这个 Manager 返回。

添加额外的管理器方法

追加 Manager 方法是向模型添加“表级”功能的首选方法。(对于“行级”功能——即作用于模型对象的单个实例的函数——使用 Model methods 不是习惯 Manager 方法。

例如,此自定义 Manager 添加一个方法 with_counts() ::

from django.db import models
from django.db.models.functions import Coalesce


class PollManager(models.Manager):
    def with_counts(self):
        return self.annotate(num_responses=Coalesce(models.Count("response"), 0))


class OpinionPoll(models.Model):
    question = models.CharField(max_length=200)
    objects = PollManager()


class Response(models.Model):
    poll = models.ForeignKey(OpinionPoll, on_delete=models.CASCADE)
    # ...

在此示例中,您将使用 OpinionPoll.objects.with_counts() 想要拿到一个 QuerySetOpinionPoll 具有附加属性的对象 num_responses 附加的属性。

风俗习惯 Manager 方法可以返回所需的任何内容。它不必返回 QuerySet .

另外要注意的是, Manager 方法可以访问 self.model 以获取它们附加到的模型类。

修改经理的首字母 QuerySet

A ManagerQuerySet 返回系统中的所有对象。例如,使用此模型:

from django.db import models


class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.CharField(max_length=50)

…声明 Book.objects.all() 将返回数据库中的所有书籍。

您可以覆盖 ManagerQuerySet 通过覆盖 Manager.get_queryset() 方法。 get_queryset() 应该归还 QuerySet 使用您需要的属性。

例如,以下模型具有 two Manager S——一个返回所有对象,一个只返回罗尔德·达尔的书:

# 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() 将只返回罗尔德·达尔写的那些。

因为 get_queryset() 返回A 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 对象,注意第一个 Manager Django遇到(按照模型中定义的顺序)具有特殊的状态。Django解释第一个 Manager 在类中定义为“默认” Manager 以及Django的几个部分(包括 dumpdata )将使用 Manager 专为那个型号设计。因此,在选择默认管理器时要小心,以避免覆盖 get_queryset() 导致无法检索要使用的对象。

可以使用指定自定义默认管理器 Meta.default_manager_name .

如果您正在编写一些必须处理未知模型的代码,例如,在实现通用视图的第三方应用程序中,请使用此管理器(或 _base_manager )而不是假设模型 objects 经理。

基本管理人员

Model._base_manager

不要过滤掉此类管理器子类中的任何结果

此管理器用于访问与其他模型相关的对象。在这种情况下,Django必须能够看到它正在获取的模型的所有对象,以便 任何东西 可以检索到。

因此,您不应该重写 get_queryset() 过滤掉所有行。如果这样做,Django将返回不完整的结果。

调用定制 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()

此示例允许您调用 authors()editors() 直接从经理那里 Person.people .

使用创建经理 QuerySet 方法

代替上述方法,该方法要求在 QuerySet 以及 ManagerQuerySet.as_manager() 可用于创建 Manager 有一个自定义的副本 QuerySet 方法:

class Person(models.Model):
    ...
    people = PersonQuerySet.as_manager()

这个 Manager 实例创建者 QuerySet.as_manager() 将几乎等同于 PersonManager 来自上一个示例。

不是每一个 QuerySet 方法在 Manager 级别;例如,我们故意阻止 QuerySet.delete() 方法从复制到 Manager 类。

方法按照以下规则复制:

  • 默认情况下复制公共方法。

  • 默认情况下,不会复制私有方法(以下划线开头)。

  • 方法与A queryset_only 属性设置为 False 总是被复制。

  • 方法与A 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 CustomManager(models.Manager):
    def manager_only_method(self):
        return


class CustomQuerySet(models.QuerySet):
    def manager_and_queryset_method(self):
        return


class MyModel(models.Model):
    objects = CustomManager.from_queryset(CustomQuerySet)()

您还可以将生成的类存储到变量中:

MyManager = CustomManager.from_queryset(CustomQuerySet)


class MyModel(models.Model):
    objects = MyManager()

自定义管理器和模型继承

以下是Django如何处理自定义管理器和 model inheritance

  1. 使用Python的常规名称解析顺序(子类上的名称覆盖所有其他名称,然后在第一个父类上出现名称,依此类推),基类中的管理器始终由子类继承。

  2. 如果模型和/或其父代上没有声明管理器,Django将自动创建 objects 经理。

  3. 类上的默认管理器可以是 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 管理器仍然可用,因为它是继承的,但没有用作默认值。

最后,对于这个示例,假设您希望向子类添加额外的管理器,但仍然使用默认的From 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()

将引发异常。这是因为管理器旨在封装用于管理对象集合的逻辑。因为您不能拥有抽象对象的集合,所以管理它们是没有意义的。如果您有应用于抽象模型的功能,那么应该将该功能放在 staticmethodclassmethod 抽象模型。

实施问题

无论您为您的客户添加了什么功能 Manager ,必须有可能制作一个浅层副本 Manager 实例;即,以下代码必须正常工作:

>>> import copy
>>> manager = MyManager()
>>> my_copy = copy.copy(manager)

Django在某些查询期间对管理器对象进行浅复制;如果无法复制管理器,则这些查询将失败。

对于大多数自定义管理器来说,这不是一个问题。如果你只是在你的 Manager ,您不太可能无意中创建 Manager 不可复制的但是,如果你要超越 __getattr__ 或者你的私人方法 Manager 控制对象状态的对象,应确保不影响 Manager 被复制。