django官方文档——管理器(数据库操作接口)

管理器
class  Manager

管理器 是 Django 模型的数据库操作接口。每个 Django 应用中的模型至少有一个 管理器 。

管理器 类的工作方式在 构造查询 有详细的描述。本文主要谈谈自定义 管理器 。

管理器名称

缺省情况下, Django 为每个模型类添加一个名为 objects 的 管理器 。可是如果你想把 objects 作为一个字段的名称或者你不想把 objects 作为 管理器的名称,那么你可以在模型的基础定义中重命名 管理器 的名称。重命名的方法是在模型中定义一个 models.Manager() 类型的类属性。例如:

from django.db import models

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

上例中,如果使用 Person.objects 会引发一个 AttributeError 异常,但是使用 Person.people.all() 则会返回一个所有 Person 对象的列表。

自定义管理器

通过扩展基础 管理器 类可以在模型中使用自定义 管理器 ,并初始化 管理器 。

我们要使用自定义 管理器 有两个目的 :增加额外的 管理器 方法和(或)修改 管理器 返回的原始 查询集 。

增加额外的管理器方法

增加额外的 管理器 方法是为模型增加“表级别”功能的首选方式。(要增加如作用于模型对象中单个实例的“行级别”的功能请使用 模型方法 ,而不要使用 管理器 方法。)

自定义 管理器 方法可以返回任何东西,不一定要返回一个 查询集 。

例如下面的自定义 管理器 提供一个 with_counts() 方法,这个方法返回一个包含所有 OpinionPoll 的列表,每个列表中的对象都有一个 num_responses属性,这个属性代表一个合计数:

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 属性的 OpininPoll 对象列表。

另一个要注意的是 管理器 方法可以通过 self.model 来获得其所依附的模型类。

修改原始管理器查询集

一个 管理器 的基础 查询集 返回系统中的所有对象。例如,使用这个模型:

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

...语句 Book.objects.all() 会返回数据库中所有的书。

通过重载 Manager.get_query_set() 可以重载 管理器 的基础 查询集 。 get_query_set() 应当返回一个符合你需求的 查询集 。

例如,以下模型有 两个 管理器 ,一个返回所有对象,另一个只返回作者为 Roald Dah1 的对象:

# 首先定义管理器的子类。
class DahlBookManager(models.Manager):
    def get_query_set(self):
        return super(DahlBookManager, self).get_query_set().filter(author='Roald Dahl')

# 显式的把上面的子类安装到 Book 模型中。
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 Dah1 的书。

当然,因为 get_query_set() 返回的是一个 查询集 对象,所以可以对这个对象使用 filter() 、 exclude() 和所有其它 查询集 方法。以下语句都是合法的:

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

这个例子也展示了一个有趣的技巧:在同一个模型中使用多个管理器。你可以为一个模型附加任意多 Manager() 实例。 这是一个为模型定义常用“过滤器”的便捷方法。

例如:

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() ,获得明确的结果。

如果你使用自定义 管理器 对象,那么要注意第一个 管理器 (根据在模型定义的顺序)有一个特殊的状态。 Django 把第一个 管理器 作为缺省的 管理器,并且 Django 的不同组成部分(包括 dumpdata )只使用模型的缺省``管理器`` 。所以要仔细斟酌如何定义第一个 管理器 ,以免在查询进无法获得正确的结果。

自定义管理器和模型继承

模型类的继承时,模型的管理器也同时继承有时候是不适合的。因为管理器通常是针对特定的类的。同时因为第一个管理器被视作 缺省管理器 ,所以管理器是否同时继承应当可以控制是非常重要的。所以 Django 是这样处理自定义管理器和 模型继承 的:

  1. 非抽象基类中的管理器是  继承的。因为这种管理器通常是针对特定的非抽象基类的。如果你要重用非抽象基类中的管理器,那么必须在其子类中显式的重新定义这个管理器。
  2. 抽象基类的管理器总是被其子类继承的。管理器的名称使用 Python 的通常名称处理方式(子类的名称重载所有其他名称;名称由第一个父类而来等)。抽象基类就是用来其子类的通用信息和行为的,所有其管理器天生就是用来处理通用的信息的,理当被继承。
  3. 如果模型类中声明了管理器,那么第一个声明的管理器就是缺省管理器。如果没有声明,且这个类是继承自一个抽象基类,那么继承关系中第一个父抽象基类的缺省管理器就是这个类的缺省管理器。如果没有显式声明任何管理器,那么就会使用 Django 自身的一般缺省管理器。

以上规则在多个模型中使用多个自定义管理器时提供了足够的弹性。例如,假设有如下基类:

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

    class Meta:
        abstract = True

如果子类中不声明管理器,那么父类中 objects 就会成为缺省管理器:

class ChildA(AbstractBase):
    ...
    # 这个类的缺省管理器是 CustomManager 。

如果要继承 AbstractBase ,但想使用不同的缺省管理器,那么可以在子类中声明自己的缺省管理器:

class ChildB(AbstractBase):
    ...
    # 一个显式定义的缺省管理器。
    default_manager = OtherManager()

上例中 default_manager 是缺省管理器。 objects 管理器仍是可用的,但不是缺省管理器。

最后,对于上例,假设想要添加额外的管理器,但是仍然要使用父类 AbstractBase 的管理器作为缺省管理器。不能直接在子类中添加新的管理器,因为新添加的管理器会重载父类的管理器而成为缺省的管理器。那么必须先要显式的声明所有父类的管理器吗?不必,可以把要添加的管理器放在另外一个基类中,然后在要继承的基类 之后 引入另外这个基类:

class ExtraManager(models.Model):
    extra_manager = OtherManager()

    class Meta:
        abstract = True

class ChildC(AbstractBase, ExtraManager):
    ...
    # 缺省管理器是 CustomManager ,但 OtherManager 作为
    # “ extra_manager ”的属性也可以使用。

实施要点

不论你在自定义 管理器 中添加了任何功能, 管理器 实例都必须可以被浅复制。例如以下代码应当可以执行通过:

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

在执行某些查询时, Django 会执行浅复制,如果你的管理器无法被浅复制,那些查询会失败。

如果你的管理器不能被浅复制,大多数情况下不会有问题。但是,如果你重载了 __getattr__ 或管理器的一些私有方法,那么你应当确认你的管理器可以被浅复制。

控制自动管理器类型

本文已经谈了许多 Django 为你创建的管理器类:`缺省管理器`_ 和用于 操作关联对象 的“简明”管理器。但是 Django 的运行还需要一些其它的简明管理器。这些自动创建的管理器是 django.db.models.Manager 的实例。

本节我们会使用“自动管理器”来指代 Django 为你创建的管理器。“自动管理器”包括未定义管理器的模型的缺省管理器和当操作关联对象时临时使用的管理器。

有时候使用缺省的管理器不是一个正确的选择。例如 Django 自带的 django.contrib.gis 应用中,所有 gis 模型必须使用一个特殊的管理器类(GeoManager ),因为模型需要一个特殊的查询集( GeoQuerySet )来与数据库交互。即有时候模型不需要自动创建的管理器,而只使用特定的管理器。

Django 通过设置管理器 use_for_related_fields 属性来解决这个问题:

class MyManager(models.Manager):
    use_for_related_fields = True

    ...

如果这个属性在一个模型的 缺省 管理器中被设置了(只有在缺省管理器中设置才起作用),那么 Django 就会始终使用这个管理器,否则就会使用django.db.models.Manager 。

历史由来

根据属性的用途,属性的名称( use_for_related_fields )可能看上去比较琐碎。因为原来,这个属性只用于控制操作管理器的关联字段。当这个属性的概念逐渐明确后,它的名称没有改变。这只是一个初步的情况,在 Django 以后的版本中代码还会 持续改进 。

在自动管理器实例中使用正确的管理器

上文 django.contrib.gix 例子中我们已经建议过, use_for_related_fields 功能主要用于需要返回一个自定义 查询集 子类的管理器。为了让管理器有效工作,还有一些要牢记的东西:

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

一个原因是一个自动管理器是用于操作其它模型的关联对象的。在这种情况下, Django 必须可以看见获得的其它所有模型的对象。因此获得的任何东西都不要过滤掉。

如果你重载了 get_query_set() 方法并且过滤掉了任何数据库的行,那么 Django 会返回不正确的结果。所以不要这样做。一个过滤 get_query_set() 的结果的管理器不适合用作一个自动管理器。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值