django模型类-管理器

820 阅读4分钟

管理器

这是我参与8月更文挑战的第16天,活动详情查看:8月更文挑战

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

1、管理器名称

重写的时候一定不要忘了继承models.Manager

默认情况下,Django 为每个模型类添加了一个名为 objectsManager。不过,若你想将 objects 用作字段名,或想使用 objects 以外的 Manager 名字,就要在模型基类中重命名。要为指定类重命名 Manager,在该模型中定义一个类型为 models.Manager 的属性。例如:

from django.db import models
​
class Person(models.Model):
    people = models.Manager()
​
>>> Person.objects   报错(因为以及重新指定成people)
>>> Person.people.all()

使用这个实例模型时, Person.objects 会产生一个 AttributeError 异常,而 Person.people.all() 会返回包含所有 Person 对象的列表。

2、自定义管理器

有两种原因可能使你想要自定义 Manager

  • 添加额外的 Manager 方法。
  • 修改 Manager 返回的原始 QuerySet

2.1、添加额外的管理器方法

添加额外的 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  # 不用强制返回quersetclass OpinionPoll(models.Model):
    question = models.CharField(max_length=200)
    poll_date = models.DateField()
    objects = PollManager()
​
# 其实就是调用这个方法,给每个对象添加一个num_responses属性
OpinionPoll.objects.with_counts() 返回一个包含 OpinionPoll 对象的列表,每个对象都有 num_responses 属性。

注意

注意事项是 Manager 方法能通过 self.model 获取所依附的模型类。

2.2、修改管理器的初始 QuerySet

这样是为了方便减少view.py视图的代码(不用在里面写过滤查询,也不会乱)

重写 get_queryset() 方法来覆盖 ManagerQuerySetget_queryset() 里面编写我们需要的属性。

下面模型有 两个 Manager 一个返回所有对象,另一个仅返回 大壮 写的书:

class DahlBookManager(models.Manager):  # 重写的时候一定不要忘了继承models.Manager
    def get_queryset(self):
        return super().get_queryset().filter(author='大壮')
​
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() # 仅返回大壮写的书

当然,因为 get_queryset() 返回一个 QuerySet 对象,你可以在上面调用 filter()exclude() 和其它的 QuerySet 方法。所以,以下语句是等效的:

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

在一个模型中可以使用多个管理器。你可以为一个模型添加任意多个 Manager()。为模型定义通用 "filters" 的非重复方式。

3、默认管理器(_default_manager)

使用定义Manager对象,注意的Django遇到的第一个Manager(按照你在模型中定义的顺序)会拥有一个独特的状态.Django类将定义中的第一个Manager视作“默认” Manager,则Django的几一些组件(包括dumpdata在该模型时会独立调用该模型Manager。因此,选择默认管理器要万分小心,避免出现get_queryset()无法获取获取结果的情况。

你可以通过Meta.default_manager_name指定一个自定义的默认管理器。

4、基础管理器(_base_manager)

Django 访问关联对象(即choice.question)时使用Model._base_manager管理器类的实例,而不是关联对象的_default_manager。这是因为那些可能被默认管理器检索掉的 Django 可能会被删除(因此无法访问关联对象)。

5、管理器调用自定义QuerySet方法

QuerySet方法可以直接从Manager访问,这个实例仅适用于你在自定义QuerySet中定义的Manager额外方法,并且在中实现了它们:

class PersonQuerySet(models.QuerySet):
    def authors(self):
        return self.filter(role='A')
​
class PersonManager(models.Manager):
    def get_queryset(self):
        return PersonQuerySet(self.model, using=self._db)
​
    def authors(self):
        return self.get_queryset().authors()
​
class Person(models.Model):
    people = PersonManager()

本例允许你从管理器Person.people直接调用authors()editors()