管理者¶ T0>

Manager[source]

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

Using this example model, Person.objects will generate an AttributeError exception, but Person.people.all() will provide a list of all Person objects.

自定义管理器

您可以在模型中扩展基础Manager类并实例化您的自定义Manager,从而在特定模型中使用自定义Manager

There are two reasons you might want to customize a Manager: to add extra Manager methods, and/or to modify the initial QuerySet the Manager returns.

添加额外的管理器方法

添加额外的Manager方法是将“表级”功能添加到模型的首选方法。 (对于“行级”功能 - 即作用于模型对象单个实例的函数 - 使用Model methods,而不是自定义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() should return a QuerySet with the properties you require.

例如,下面的模型有两个 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.

With this sample model, Book.objects.all() will return all books in the database, but Book.dahl_objects.all() will only return the ones written by Roald Dahl.

Of course, because get_queryset() returns a QuerySet object, you can use filter(), exclude() and all the other QuerySet methods on it. 所以这些陈述都是合法的:

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

默认管理员

模型。 _default_manager T0> ¶ T1>

如果使用自定义Manager对象,请注意,第一个Manager Django遇到(按照在模型中定义的顺序)具有特殊状态。 Django interprets the first Manager defined in a class as the “default” Manager, and several parts of Django (including dumpdata) will use that Manager exclusively for that model. 因此,在选择默认管理器时要小心,以避免覆盖get_queryset()导致无法检索要使用的对象的情况。

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

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

基地经理

模型。 _base_manager T0> ¶ T1>

不要过滤掉这类管理员子类中的任何结果

该管理器用于访问与其他模型相关的对象。 在这些情况下,Django必须能够看到它所提取的模型的所有对象,以便可以检索被引用的什么

If you override the get_queryset() method and filter out any rows, Django will return incorrect results. 不要这样做。 过滤get_queryset()结果的管理器不适合用作基本管理器。

从管理器调用自定义QuerySet方法

尽管可以直接从Manager访问标准QuerySet中的大多数方法,但只有在自定义的QuerySet上定义的额外方法的情况下if你也可以在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方法创建管理器

In lieu of the above approach which requires duplicating methods on both the QuerySet and the Manager, QuerySet.as_manager() can be used to create an instance of Manager with a copy of a custom QuerySet’s methods:

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

类方法 from_queryset T0>( queryset_class T1>)¶ T2>

对于高级用法,您可能需要自定义Manager和自定义QuerySet 你可以通过调用Manager.from_queryset()来获得你的base 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如何处理自定义管理器和model inheritance

  1. 基类的管理器总是由子类继承,使用Python的普通名称解析顺序(子类名称覆盖所有其他类;然后在第一个父类上提供名称,等等)。
  2. 如果没有在模型和/或其父项上声明管理者,Django会自动创建objects管理器。
  3. 类中的默认管理器可以是使用Meta.default_manager_name选择的管理器,也可以是在模型上声明的第一个管理器,也可以是第一个父模型的默认管理器。

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

class AbstractBase(models.Model):
    # ...
    objects = CustomManager()

    class Meta:
        abstract = True

If you use this directly in a subclass, objects will be the default manager if you declare no managers in the base class:

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

Note that while you can define a custom manager on the abstract model, you can’t invoke any methods using the abstract model. 那是:

ClassA.objects.do_something()

是合法的,但是:

AbstractBase.objects.do_something()

会引发一个例外。 这是因为管理者打算封装用于管理对象集合的逻辑。 由于您不能拥有抽象对象的集合,因此管理它们是没有意义的。 如果您的功能适用于抽象模型,则应将该功能放在抽象模型的staticmethodclassmethod中。

实施担忧

无论添加到自定义Manager的功能如何,都必须能够创建Manager实例的浅表副本。即下面的代码必须工作:

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

Django在某些查询期间创建管理对象的浅表副本;如果您的管理器无法复制,那么这些查询将会失败。

这对于大多数定制管理者来说不成问题。 如果您只是将简单的方法添加到您的Manager中,则不太可能会无意中将Manager的实例设为不可复制的。 However, if you’re overriding __getattr__ or some other private method of your Manager object that controls object state, you should ensure that you don’t affect the ability of your Manager to be copied.