管理器是 Django 的模型进行数据库查询操作的接口。Django 应用的每个模型都拥有至少一个管理器。
管理器的名字
假设有以下 model:
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.CharField(max_length=200)
def __str__(self):
return self.title
默认情况下,Django 为每个模型类添加一个名为 objects
的管理器。你可以在每个模型类中重命名它们:
class Book(models.Model):
#...
book_manager = models.Manager()
现在就可以用 book_manager
来调用 Book 模型的管理器了,例如:
b = Book.book_manager.get(title='图书1')
自定义管理器
你可以在模型中使用自定义的管理器,方法是继承 Manager
基类并实例化你的自定义管理器。
自定义管理器一般有两个用途:向管理器类中添加额外的方法,或者修改管理器返回的原始查询集。
1.添加额外的管理器方法
添加额外管理器方法是为你的类增加“表级”功能的首选方式。 (如果要添加行级功能 —— 比如只对某个模型的实例起作用 ——应使用模型方法 ,而不是管理器方法。)
一个最简单例子:
from django.db import models
# 自定义管理器
class BookManager(models.Manager):
def test(self):
return '一个测试'
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.CharField(max_length=200)
objects = BookManager()
def __str__(self):
return self.title
shell 中测试下:
>>> from myApp.models import Book
>>> Book.objects.test()
'一个测试'
我们来写一个更有意义的例子,添加一个管理器方法,直接执行 SQL 语句,查询某个作者所写的图书的总数:
from django.db import models
from django.db import connection
# 自定义管理器
class BookManager(models.Manager):
# 自定义管理方法
def author_counts(self, author):
cursor = connection.cursor()
# 直接执行 SQL 语句
ins = ("""
SELECT COUNT(*)
FROM myApp_book
WHERE author='{}';
""").format(author)
counts = cursor.execute(ins)
for i in counts:
count = i[0]
return count
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.CharField(max_length=200)
objects = BookManager() # 指定使用哪个管理器
def __str__(self):
return self.title
shell 中测试下:
>>> from myApp.models import Book
>>> Book.objects.author_counts(author='作者1')
2
>>> Book.objects.author_counts(author='作者2')
1
2.修改管理器的原始查询集
你可以通过超类覆盖 Manager.get_queryset()
方法来覆盖管理器自带的 Queryset
。自定义 get_queryset()
方法能根据你所需要的属性返回查询集。
例如,下面的模型有两个管理器,一个返回所有的对象,另一个则只返回作者是 “作者1”
的对象:
from django.db import models
from django.db import connection
# 自定义管理器
class BookManager(models.Manager):
def get_queryset(self):
return super(BookManager, self).get_queryset().filter(author='作者1')
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.CharField(max_length=200)
objects = models.Manager() # 默认管理器
book_manager = BookManager() # 自定义管理器
def __str__(self):
return self.title
shell 中测试下:
from myApp.models import Book
# 使用自定义的管理器 book_manager,只会返回 author 是 “作者1” 的对象
>>> Book.book_manager.all()
<QuerySet [<Book: 图书1>, <Book: 图书2>]>
# 使用默认管理器,返回全部对象
>>> Book.objects.all()
<QuerySet [<Book: 图书1>, <Book: 图书2>, <Book: 图书3>, <Book: 图书4>]>
同一模型使用多个管理器,在一个模型里面添加多个 Manager
实例。这是给模型添加通用过滤器(选择器)的一个简单方法:
class AuthorManager(models.Manager):
def get_queryset(self):
return super(AuthorManager, self).get_queryset().filter(role='A')
class EditorManager(models.Manager):
def get_queryset(self):
return super(EditorManager, self).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() # 只选择 role=A 的对象
editors = EditorManager() # 只选择 role=E 的对象
3.从 Manager 中调用自定义的 QuerySet
虽然大多数标准查询集的方法可以从管理器中直接访问到,但是如果你需要将一些被定义到一个自定义查询集中的额外方法也在管理器上实现,下面所展示的这个例子是唯一可行办法:
from django.db import models
# 自定义的 QuerySet,并新增了 QuerySet 的方法
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()
def __str__(self):
return self.first_name + self.last_name
现在可以用 Person.people.authors()
的形式来调用了。
shell 中测试下:
from myApp.models import Person
# 选择 role=A 的对象
>>> Person.people.authors()
<QuerySet [<Person: 李小明>, <Person: 张小花>]>
# 选择 role=E 的对象
>>> Person.people.editors()
<QuerySet [<Person: 王小红>, <Person: 林小白>]>
4.使用 QuerySet 的方法作为 Manager
上面的方法要求同一个方法在查询集和管理器上都创建,QuerySet.as_manager()
可以用来创建一个带有自定义查询集方法副本的管理器实例:
class Person(models.Model):
...
people = PersonQuerySet.as_manager()
通过 QuerySet.as_manager()
创建的管理器 实例,实际上等价于上面例子中的 PersonManager
。
看一个完整例子:
from django.db import models
# 自定义的 QuerySet,并新增了 QuerySet 的方法
class PersonQuerySet(models.QuerySet):
def authors(self):
return self.filter(role='A')
def editors(self):
return self.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'))
)
# 把 PersonQuerySet 作为管理器
people = PersonQuerySet.as_manager()
def __str__(self):
return self.first_name + self.last_name
照样,我们可以用 Person.people.editors()
的形式来调用。
from myApp.models import Person
# 选择 role=A 的对象
>>> Person.people.authors()
<QuerySet [<Person: 李小明>, <Person: 张小花>]>
并不是每个查询集的方法都在管理器层面上有意义。比如 QuerySet.delete()
,我们有意防止它复制到管理器 中。
方法按照以下规则进行复制:
- 公共方法默认被复制。
- 私有方法(前面带一个下划线)默认不被复制。
- 带 queryset_only 属性,并且值为False的方法总是被复制。
- 带 queryset_only 属性,并且值为True 的方法不会被复制。
例子:
class PersonQuerySet(models.QuerySet):
......
# 可在 manager 中调用
def public_method(self):
return '公共方法'
# 不可在 manager 中调用
def _private_method(self):
return '私有方法'
# 不可在 manager 中调用
def opted_out_public_method(self):
return '私有方法'
opted_out_public_method.queryset_only = True
# 可在 manager 中调用
def _opted_in_private_method(self):
return '公共方法'
_opted_in_private_method.queryset_only = False
from_queryset
在进一步的使用中,你可能想创建一个自定义管理器和一个自定义查询集。
你可以调用 Manager.from_queryset()
,它会返回管理器的一个子类,带有自定义查询集所有方法的副本:
from django.db import models
class BaseManager(models.Manager):
def manager_only_method(self):
return '管理器独有方法'
class PersonQuerySet(models.QuerySet):
def manager_and_queryset_method(self):
return '管理器和查询集共有方法'
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'))
)
objects = BaseManager.from_queryset(PersonQuerySet)()
def __str__(self):
return self.first_name + self.last_name
shell 中测试下:
>>> Person.objects.manager_only_method()
'管理器独有方法'
>>> Person.objects.manager_and_queryset_method()
'管理器和查询集共有方法'
>>> Person.objects.all().manager_and_queryset_method()
'管理器和查询集共有方法'