管理器

class Manager[source]

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

Manager 类的工作方式在 执行查询 中有详细说明;本文档特别介绍了自定义 Manager 行为的模型选项。

管理器名称

默认情况下,Django 会为每个 Django 模型类添加一个名为 objectsManager。但是,如果您想将 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 方法,和/或修改 Manager 返回的初始 QuerySet

添加额外的管理器方法

添加额外的 Manager 方法是在您的模型中添加“表级”功能的首选方法。(对于“行级”功能——即作用于模型对象单个实例的函数——请使用 模型方法,而不是自定义 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() 获取一个带有附加的额外 num_responses 属性的 OpinionPoll 对象的 QuerySet

自定义 Manager 方法可以返回您想要的任何内容。它不必返回 QuerySet

需要注意的另一点是,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 的基本 QuerySetget_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() 实例附加到模型。这是一种非重复的方式来定义模型的常用“过滤器”。

例如

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 管理器。

基本管理器

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()

此示例允许您直接从管理器Person.people调用authors()editors()

使用QuerySet方法创建管理器

作为上述方法的替代方案(需要在QuerySetManager上都复制方法),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 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 如何处理自定义管理器和模型继承

  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管理器是继承的,因此仍然可用,但不用作默认值。

最后,对于此示例,假设您想向子类添加额外的管理器,但仍然使用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的可复制性。

返回顶部