Django documentation

当前文档仅适用于 Django SVN 版本,与上个版本有显著不同。上个版本文档请查阅: Django 1.0

Managers

class Manager

Manager 是 Django 提供给 model 的数据操作接口,用来操作数据库查询。Django 应用中的每个 model 至少都有一个 Manager

Manager 类的工作方式在 生成查询(Making queries) 中详细介绍;本文档重点介绍自定义 Manager 行为的 model 选项。

Manager的名称(Manager names)

默认情况下,Django 给每个 model 添加一个 Manager,它的名称是 objects 。但是,如果你想使用 objects 做为某个字段的名称,或是你不想使用默认的 objects 。那么你可以将所有 model 的 Manager 名称都改掉,只要将 models.Manager() 赋予 model 中的某个类属性即可。例如:

from django.db import models

class Person(models.Model):
    #...
    people = models.Manager()

在这个例子中,如果还使用 Person.objects 将抛出一个 AttributeError 异常,但 Person.people.all() 却能提供一个包含所有 Person 对象的列表。

自定义Manager(Custom Managers)

扩展原始 Manager 类并在 model 中实例化 Manager ,然你就可以使用自定义的 Manager

使用自定义 Manager 可能是出于以下两种原因:添加额外的 Manager 方法;或是修改 Manager 返回的查询集 QuerySet

添加额外的 Manager 方法(Adding extra Manager methods)

为你的 model 添加表级(table-level)功能时,采用添加额外 Manager 方法是更好的处理方式。(如果要添加行级功能--就是说该功能只对某个 model 的实例对象起作用。在这种情况下,使用 Model methods 比使用自定义的 Manager 方法要更好。)

自定义的 Manager 方法可以返回你想要的任何数据,而不只是 QuerySet

例如,下面这个自定义的 Manager 提供了一个 with_counts() 方法,它返回所有 OpinionPoll 对象的列表,而且列表中的每个对象都多了一个名为 num_responses 的属性,这个属性保存一个聚合查询(COUNT*)的结果:

class PollManager(models.Manager):
    def with_counts(self):
        from django.db import connection
        cursor = connection.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 1, 2, 3
            ORDER BY 3 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(Poll)
    person_name = models.CharField(max_length=50)
    response = models.TextField()

在这个例子中,你已经可以使用 OpinionPoll.objects.with_counts() 得到所有含有 num_responses 属性的 OpinionPoll 对象。

在这个例子要注意的一点是: Manager 方法可以访问 self.model 来得到它所用到的 model 类。

修改Manager初始的查询集(Modifying initial Manager QuerySets)

Manager 自带的 QuerySet 返回系统中所有的对象。例如,使用下面这个 model:

class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.CharField(max_length=50)

... Book.objects.all() 语句将返回数据库中所有的 Book 对象。

你可以通过重写 Manager.get_query_set() 的方法来覆盖 Manager 自带的 QuerySetget_query_set() 会根据你所需要的属性返回 QuerySet

例如,下面的 model 有两个 Manager,一个返回所有的对象,另一个则只返回作者是 Roald Dahl 的对象:

# First, define the Manager subclass.
class DahlBookManager(models.Manager):
    def get_query_set(self):
        return super(DahlBookManager, self).get_query_set().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_query_set() 返回的是一个 QuerySet 对象,所以你仍可以对它使用 filter(), exclude() 和其他 QuerySet 方法。所以下面这些例子都是可用的:

Book.dahl_objects.all()
Book.dahl_objects.filter(title='Matilda')
Book.dahl_objects.count()

这个例子还展示了另外一个很有意思技术:在同一个 model 中使用多个 Manager。你可以随你所意在一个 model 里面添加多个 Manager() 实例。下面就用很简单的方法,给 model 添加通用过滤器:

例如:

class MaleManager(models.Manager):
    def get_query_set(self):
        return super(MaleManager, self).get_query_set().filter(sex='M')

class FemaleManager(models.Manager):
    def get_query_set(self):
        return super(FemaleManager, self).get_query_set().filter(sex='F')

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    sex = models.CharField(max_length=1, choices=(('M', 'Male'), ('F', 'Female')))
    people = models.Manager()
    men = MaleManager()
    women = FemaleManager()

在这个例子中,你使用 Person.men.all(), Person.women.all(),以及 Person.people.all(), 都会得果和名称相符的结果。

如果你使用了自定义 Manager 对象,要注意 Django 中的第一个 Manager (按照 model 中出现的顺序而定) 拥有特殊的地位。Django 会将 model 中定义的Manager 解释为默认Manager,并且 Django 中的一部分应用(但不包括管理后台)只使用默认的 Manager。因此,要决定默认的 Manager 时,要小心谨慎,仔细考量,这样才能避免重写 get_query_set() 导致无法正确地获得数据。

自定义 manager 和 model 继续(Custom managers and model inheritance)

类继承和 model manager 两者之间配合得并不是很好。 Manager 一般只对其定义所在的类起作用,在子类中对其继承绝对不是一个好主意。而且,因为第一个 manager 会被 Djange 声明为默认 manager,所以对默认 manager 进行控制是非常必要的。下面就是 Django 如何处理自定义 manager 和 model 继承(model inheritance)的:

  1. 定义在非抽象基类中的 manager 是不会被子类继承的。如果你想从一个非抽象基类中重用 manager,只能在子类中重定义 manager。这是因为这种 manager 与定义它的 model 的绑定得非常紧密,所以继承它们经常会导致异常的结果(特别是默认 manager 运行的时候)。因此,它们不应继承给子类。
  2. 定义在抽象基类中的 manager 总是被子类继续的,是按 Python 的命名解析顺序解析的(首先是子类中的命名覆盖所有的,然后是第一个父类的,以此推类)。抽象类是用来提取子类中的公共信息和行为,定义公共 manager 也是公共信息的一部分。
  3. 如果类当中显示定义了默认 manager ,Django 就会以此做为默认 manager;否则就会从第一个抽象基类中继承默认 manager;如果这种情况都不符合,那么 Django 就会自动添加默认 manager。

如果你想在一组 model 上安装一系列自定义 manager,上面提到的这些规则就已经为你的实现提供了必要的灵活性。你可以继承一个抽象基类,但仍要自定义默认的 manager:

class AbstractBase(models.Model):
    ...
    objects = CustomerManager()

    class Meta:
        abstract = True

如果你在基类中没有定义 manager,直接使用上面的代码,默认 manager 就是从基类中继承的 objects 就是:

class ChildA(AbstractBase):
    ...
    # This class has CustomManager as the default manager.

如果你想从 AbstractBase 继承,却又想提供另一个默认 manager,那么你可以在子类中定义默认 manager:

class ChildB(AbstractBase):
    ...
    # An explicit default manager.
    default_manager = OtherManager()

在这个例子中, default_manager 就是默认的 manager。从基类中继承的 objects manager 仍是可用的。只不过它不再是默认 manager 罢了。

最后再举个例子,假设你想在子类中再添加一个额外的 manager,但是很想使用从 AbstractBase 继承的 manager 做为默认 manager。那么,你不在直接在子类中添加新的 manager,否则就会覆盖掉默认 manager 而且你必须对派生自这个基类的所有子类都显示指定 manager。解决办法就是在另一个基类中添加新的 manager,然后继承时将其放在默认 manager 所在的基类之后。例如下:

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.

控制Automatic Manager 类型(Controlling Automatic Manager Types)

在 Django 创建 manager 类中,对这节内容也有所提取: default managers 和在 访问关联对象(access related objects) 中提到的链式 manager。在 Django 的实现中也有很多地方用到了临时链式 manager。 正常情况下,django.db.models.Manager 类的实例会自动创建 manager。

贯穿这一节,我们将那种由 Django 为你创建的 manager 称之为 "automatic manager",既有因为没有 manager 而被 Django 自动添加的默认 manager, 也包括在访问关联 model 时使用的临时 manager。

有时,默认 manager 也并非是 automatic manager 。一个例子就是 Django 自带的 django.contrib.gis 应用,所有 gis model 都必须使用一个特殊的 manager 类(GeoManager),因为它们需要运行特殊的查询集 queryset (GeoQuerySet) 与数据库进行交互。这表明无论 automatic manager 是否被创建,那些要使用特殊的 manager 的 model 仍要使用这个特殊的 manager 类。

Django 为自定义 manager 开发者提供了一种方式:无论开发的 manager 类是不是默认 manager,它都应该可以用做 automatic manager。可以通过在 manager 类中设置 use_for_related_fields 属性来做到这点:

class MyManager(models.Manager):
    use_for_related_fields = True

    ...

如果在 model 中的默认 manager (在这些情况中仅考虑默认 manager)中设置了这个属性,那么无论它是否需要被自动创建,Django 都会自动使用它。否则 Django 就会使用 django.db.models.Manager

历史回顾

从它使用目的来看,这个属性的名称(use_for_related_fields)好象有点古怪。原本,这个属性仅仅是用来控制访问关联字段的 manager 的类型,这就是它名字的由来。后来它的作用更加拓宽了,但是名称一直未变。因为要保证现在的代码在 Django 以后的版本中仍可以正常工作(continue to work),这就是它名称不变的原因。

在 Automatic Manager 中编写正确的Manager(Writing Correct Managers For Use In Automatic Manager Instances)

在上面的 django.contrib.gis 已经提到了, use_for_related_fields 这个特性是在 manager 中使用的。针对的是需要返回一个自定义的查询集 QuerySet 的子类。要在你的 manager 中提供这个功能,要注意以下几点,这也是这一节标题的来历。

不要在这种类型 manager 子类中过滤掉任何结果(Do not filter away any results in this type of manager subclass)

一个原因是 automatic manager 是用来访问关联 model 的对象。在这种情况下,Django 必须要能看到相关 model 的所有对象,所以才能根据关联关系得到任何数据。

如果你重写了 get_query_set() 方法并且过滤掉了一些行数据,Django 将返回不正确的结果。不要这么做!在 get_query_set() 方法中过滤掉数据,会使得它所在的 manager 不适于用做 automatic manager。

Questions/Feedback

Having trouble? We'd like to help!