Skip to main content

经理

class Manager[源代码]

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 方法可以返回任何你想要的。它不必返回 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 s - 一个返回所有对象,一个只返回Roald Dahl的书籍:

# First, define the Manager subclass.
class DahlBookManager(models.Manager):
    def get_queryset(self):
        return super(DahlBookManager, self).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(AuthorManager, self).get_queryset().filter(role='A')

class EditorManager(models.Manager):
    def get_queryset(self):
        return super(EditorManager, self).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必须能够看到它正在获取的模型的所有对象,以便可以检索所引用的 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() 可以用于创建具有定制 QuerySet 的方法的副本的 Manager 的实例:

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() 来返回你的基地 Managersubclass 与自定义 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 选择的,或在模型上声明的第一个管理器,或第一个父模型的默认管理器。

Changed in Django 1.10:

在旧版本中,管理器继承取决于模型继承的类型(即 抽象基类多表继承代理模型),特别是关于选择默认管理器。

如果要通过抽象基类在一组模型上安装自定义管理器集合,但仍然自定义默认管理器,这些规则提供了必要的灵活性。例如,假设你有这个基类:

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。您不能直接在子类中添加新管理器,因为这将覆盖默认值,您还必须显式包括抽象基类中的所有管理器。解决方案是将额外的管理器放在另一个基类中,并将其引入继承层次结构 after 的默认值:

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

注意,虽然你可以在抽象模型上使用 define 自定义管理器,但是你不能使用 invoke 任何方法使用抽象模型。那是:

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 的复制能力。