管理器
这是我参与8月更文挑战的第16天,活动详情查看:8月更文挑战
Manager 是一种接口,它赋予了 Django 模型操作数据库的能力。Django 应用中每个模型拥有至少一个 Manager。
1、管理器名称
重写的时候一定不要忘了继承models.Manager
默认情况下,Django 为每个模型类添加了一个名为 objects 的 Manager。不过,若你想将 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 # 不用强制返回querset
class 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() 方法来覆盖 Manager 的 QuerySet。 get_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()。