管理器 — Django 文档

来自菜鸟教程
Django/docs/2.2.x/topics/db/managers
跳转至:导航、​搜索

经理

class Manager

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

Manager 类的工作方式记录在 Making queries; 本文档专门涉及自定义 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 返回的初始 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 的基础 QuerySetget_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 方法创建管理器

代替上述需要在 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 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 处理自定义管理器和 模型继承 的方式:

  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 添加简单方法,则不太可能无意中使 Manager 的实例不可复制。 但是,如果您要覆盖 __getattr__ 或控制对象状态的 Manager 对象的某些其他私有方法,则应确保不会影响 Manager 的能力] 被复制。