---
title: "管理器"
version: 4.0
locale: zh-hans
source: https://docs.djangoproject.com/zh-hans/4.0/topics/db/managers/
canonical: https://djangodocs.dev/zh-hans/4.0/topics/db/managers/
---
# 管理器

#### `class Manager`

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

`Manager` 类的文档介绍位于 [执行查询](/zh-hans/4.0/topics/db/queries/)；本页着重介绍自定义 `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` 方法一般是为模型添加 “表级” 功能的更好方法。（对于 “行级” 功能 —— 即，只操作单个模型对象 —— 通过 [模型方法](/zh-hans/4.0/topics/db/models/#model-methods)，而不是自定义 `Manager` 的方法。）

For example, this custom `Manager` adds a method `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)
    # ...
```

With this example, you'd use `OpinionPoll.objects.with_counts()` to get a
`QuerySet` of `OpinionPoll` objects with the extra `num_responses`
attribute attached.

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

Another thing to note is that `Manager` methods can access `self.model` to
get the model class to which they're attached.

### 修改管理器的初始 `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`](/zh-hans/4.0/ref/django-admin/#django-admin-dumpdata)）在用到该模型时会独立地调用该 `Manager`。故此，选择默认管理器时要万分小心，避免遇到重写的 `get_queryset()` 无法获取期望的结果这种情况。

你可以通过 [`Meta.default_manager_name`](/zh-hans/4.0/ref/models/options/#django.db.models.Options.default_manager_name) 指定一个自定义的默认管理器。

若你正在编写的代码必须处理未知模型，例如，在实现了通用视图的第三方应用中使用这个管理器（或 [`_base_manager`](#django.db.models.Model._base_manager)），而不是假定该模型有一个名为 `objects` 的管理器。

### 基础管理器

#### `Model._base_manager`

#### 用于访问关联对象的管理器

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

若基本管理器类 ([`django.db.models.Manager`](#django.db.models.Manager)) 无法满足需求，你可以通过设置 [`Meta.base_manager_name`](/zh-hans/4.0/ref/models/options/#django.db.models.Options.base_manager_name) 告诉 Django 使用哪个类。

在关联模型上执行查询时不会使用基础管理器，或者当访问一对多或多对多关系( [accessing a one-to-many or many-to-many relationship](/zh-hans/4.0/topics/db/queries/#backwards-related-objects) )。例如，若 [来自教程](/zh-hans/4.0/intro/tutorial02/#creating-models) 的模型 `Question` 有个 `deleted` 字段，还有一个基础管理器，用于过滤掉 `deleted=True` 的实例。由 `Choice.objects.filter(question__name__startswith='What')` 返回的查询结果集会包含关联至已删除的问题的选项。

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

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

因此，你不应该覆盖 `get_queryset()` 来过滤任何rows。如果你这么做，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` 方法的管理器

要替换前面的要求复制 `QuerySet` 和 `Manager`  方法的方案， 可以用 [`QuerySet.as_manager()`](/zh-hans/4.0/ref/models/querysets/#django.db.models.query.QuerySet.as_manager) 创建一个 `Manager` 实例，拷贝了自定义 `QuerySet` 的方法:

```
class Person(models.Model):
    ...
    people = PersonQuerySet.as_manager()
```

由 [`QuerySet.as_manager()`](/zh-hans/4.0/ref/models/querysets/#django.db.models.query.QuerySet.as_manager) 创建的 `Manager` 实例实质上等价于前面例子中的 `PersonManager`。

不是每个 `QuerySet` 方法在 `Manager` 层都是有意义的；例如，我们故意阻止 [`QuerySet.delete()`](/zh-hans/4.0/ref/models/querysets/#django.db.models.query.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 如何处理自定义管理器和 [模型继承](/zh-hans/4.0/topics/db/models/#model-inheritance)：

1. 基类的管理器总是被子类以 Python 的普通名称解析顺序继承（子类上的属性会覆盖所有父类上的同名属性；直接父类会覆盖更上一级的，以此类推）。
2. 如果没有在模型或其父类申明管理器，Django 会自动创建 `objects` 管理器。
3. 一个类的默认管理器要么由 [`Meta.default_manager_name`](/zh-hans/4.0/ref/models/options/#django.db.models.Options.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` 被复制。
