# Django 2.2.9.dev20191202104323 文档

# 管理器

class `Manager`

`Manager` 是一种接口，它赋予了 Django 模型操作数据库的能力。Django 应用中每个模型拥有至少一个 `Manager`。

`Manager` 类的文档介绍位于 执行查询；本页着重介绍自定义 `Manager` 行为的模型选项。

## 管理器名称

默认情况下，Django 为每个模型类添加了一个名为 `objects` 的 `Manager`。不过，若你想将 `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` 方法，修改 `Manager` 返回的原始 `QuerySet`。

### 添加额外的管理器方法

添加额外的 `Manager` 方法一般是为模型添加 “表级” 功能的更好方法。（对于 “行级” 功能 —— 即，只操作单个模型对象 —— 通过 模型方法，而不是自定义 `Manager` 的方法。）

自定义 `Manager` 方法能返回任何东西，没有强制它必须返回一个 `QuerySet`。

例如，这个自定义 `Manager` 提供了一个方法 `with_counts()`，它会返回包含所有 `OpinionPoll` 对象的列表。每个对象都有一个额外属性 `num_response`，这是一次聚合查询的结果:

    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()` 返回一个包含 `OpinionPoll` 对象的列表，每个对象都有 `num_responses` 属性。

本例的另一个注意事项是 `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()` 返回的 `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()`。为模型定义通用 "filters" 非常简单。

例如:

    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 访问关联对象（即 `choice.question`）时使用 `Model._base_manager` 管理器类的实例，而不是关联对象的 `_default_manager`。这是因为 Django 要检索那些可能被默认管理器筛选掉（所以无法访问）的关联对象。

若基本管理器类 (`django.db.models.Manager`) 无法满足需求，你可以通过设置 `Meta.base_manager_name` 告诉 Django 使用哪个类。

在关联模型上执行查询时不会使用基础管理器。例如，若 来自教程 的模型 `Question` 有个 `deleted` 字段，还有一个基础管理器，用于筛选出 `deleted=True` 的实例。由 `Choice.objects.filter(question__name__startswith='What')` 返回的查询结果集会包含关联至已删除的问题的选项。

#### 不要在这类管理器子类中过滤掉任何结果

该管理器用于访问由其它模型关联过来的对象。这些情况下，Django 要能访问待获取模型的全部对象，这样就能检索出其指向的 任何东西。

若你重写了 `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` 方法的管理器

要替换前面的要求复制 `QuerySet` 和 `Manager` 方法的方案， 可以用 `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()

会引发一个异常。这是因为管理器意在封装管理映射对象集合的逻辑。因为您不能拥有抽象对象的集合，所以管理抽象对象是没有意义的。如果您有适用于抽象模型的功能，则应该将该功能放在抽象模型的 `静态方法` 或 `类方法` 中。

### 执行关系

无论您在自定义的 `Manager` 中添加了什么特性，都必须能够对 `Manager` 实例进行简单的复制；也就是说，以下代码必须有效：

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

Django 在某些查询期间对管理器对象进行浅拷贝；如果您的管理器无法被复制，那么这些查询将失败。

对于大多数的资源管理器来说，这不是问题。若你只是为 `Manager` 添加简单的方法，一般不会疏忽地把 `Manager` 变的不可拷贝。但是，若重写了 `Manager` 对象用于控制对象状态的 `__getattr__` 或其它私有方法，你需要确认你的修改不会影响 `Manager` 被复制。