自定义模型管理器
- 管理器是模型进行数据库操作的接口,每个模型都拥有至少一个管理器。
- 当模型没有自定义管理器时,Django会为模型类添加一个objects管理器。
- 自定义管理器的原因:
a. 添加额外的管理器方法
b. 修改原始查询集 - 自定义模型管理器是
models.Manager
的子类
from django.db import models
class MyManager(models.Manager):
pass
class Book(models.Model):
# ...
newManager = MyManager()
- 由于
Book
使用MyManager
为管理器,所以Book
不存在objects
管理器 - 如果使用
Book.newManager.all()
将返回所有Book
对象的列表 - 如果使用
Book.objects.all()
将抛出AttributeError
异常
添加额外的管理器方法
管理器方法与模型方法的区别:
管理器方法是向模型添加“表级”功能
模型方法是向模型添加“行级”功能,即作用于模型对象单个实例的函数管理器方法返回的结果可以是任意类型,不一定是
QuerySet
from django.db import models
class MyManager(models.Manager):
def res_count(self, **kwarge):
return self.filter(**kwarge).count()
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
# ...
objects = MyManager()
- 如果使用
Book.objects.res_count(authors='jojo')
将返回int
类型结果
修改原始查询集
- 可以通过重写
Manager.get_queryset()
方法来覆盖管理器的基本QuerySet
from django.db import models
class DahlBookManager(models.Manager):
# 定义模型管理器
def get_queryset(self):
# 返回符合要求的QuerySet
return super().get_queryset().filter(author='jojo')
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=50)
objects = models.Manager() # 使用默认管理器
dahl_objects = DahlBookManager() # 使用自定义管理器
-
Book.dahl_objects.all()
与Book.objects.all().filter(author='jojo')
等价 - 由于
get_queryset()
返回一个QuerySet对象,所以可以继续使用QuerySet方法,如下例子
Book.dahl_objects.all() # Book.objects.filter(author='jojo')
Book.dahl_objects.filter(title='Matilda') # Book.objects.filter(author='jojo').filter(title='Matilda')
Book.dahl_objects.count() # Book.objects.filter(author='jojo').count()
- 由于一个模型可以使用多个管理器,所以可以根据需求将尽可能多的管理器实例附加到模型中,实现简单的“过滤器”
from django.db import models
class AuthorManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(role='A')
class EditorManager(models.Manager):
def get_queryset(self):
return super().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()
editors = EditorManager()
Person.people.all() # 返回所有Person对象的列表
Person.authors.all() # 返回所有Person对象中role='A'的列表
Person.editors.all() # 返回所有Person对象中role='E'的列表
Person.objects.all() # 将抛出AttributeError异常
默认管理器(Model._default_manager)
- 应用:Django的几个部分(包括dumpdata)将针对该模型只使用默认管理器
- 设置:
a. 当不定义管理器时,objects为默认管理器
b. 当自定义一个管理器时,该管理器为默认管理器
c. 当使用多个管理器时,则按照定义顺序,使用第一个管理器为默认管理器。
d. 可通过Meta.base_manager_name
指定默认管理器
基础管理器(Model._base_manager)
所有自定义的管理器都是在基础管理器上进行重写 get_queryset()
方法
- 应用:当访问相关对象时,Django使用基础管理器(base_manager)而非默认管理器(default_manager),基础管理器用于访问与其他模型相关的对象。
- 设置:
a. 默认使用django.db.models.Manager
作为基础管理器
b. 可通过Meta.base_manager.name
设置基础管理器 - 条件:Django必须能够查看它所获取的模型的所有对象,以便可以检索所引用的任何内容,所以不能使用带有过滤行为的管理器作为基础管理器。如果重写get_queryset()方法并过滤掉任何行,Django将返回不正确的结果,即
get_queryset()
具有过滤结果的管理器不适合用作基础管理器。 - 由于自定义管理器重写基础管理器的
get_queryset()
方法,所以查询相关模型时使用自定义管理器。
自定义查询集(QuerySet)
- 自定义查询集是
models.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()
-
QuerySet.as_manager()
可以用来创建一个Manager实例,所以上面实现与下面实现等价
class PersonQuerySet(models.QuerySet):
def authors(self):
return self.filter(role='A')
def editors(self):
return self.filter(role='E')
class Person(models.Model):
...
people = PersonQuerySet.as_manager()
注意:并不是每个QuerySet方法在管理器级别都有意义,由于管理器方法是向模型添加“表级”功能,所以如果QuerySet实现.delete()
等“行级”功能将不会被复制到管理器方法中。
-
QuerySet.as_manager()
根据以下规则复制到Manager类。:
a. 默认情况下会复制公共方法。
b. 默认情况下不会复制私有方法(以下划线开头)。
c. 始终复制queryset_only属性设置为的方法False。
d. 永远不会复制queryset_only属性设置为的方法True。
from_queryset(queryset_class)
- 通过调用
Manager.from_queryset(queryset_class)
实现自定义管理器,该函数返回基管理器的子类和自定义QuerySet方法的副本
class BaseManager(models.Manager):
def manager_only_method(self):
return
class CustomQuerySet(models.QuerySet):
def manager_and_queryset_method(self):
return
class MyModel(models.Model):
objects = BaseManager.from_queryset(CustomQuerySet)()
# MyModel.objects.manager_only_method()
# MyModel.objects.manager_and_queryset_method()
模型继承对模型管理器的影响
- 来自基类的管理器总是由子类继承,使用Python的常规名称解析顺序(子类 > 父类)
- 如果没有在模型和/或其父模型上声明管理器,Django将自动创建对象管理器。
- 默认管理器:
Meta.default_manager_name
声明 > 模型上声明的第一个管理器 > 父模型的默认管理器
- 如何实现在子类中存在父类不存在的管理器,但是使用父类的默认管理器?
class AbstractBase(models.Model):
# ...
parent_manager = CustomManager()
class Meta:
abstract = True
class Child(AbstractBase):
# ...
# An explicit default manager.
child_manager = OtherManager()
此时由于子模型上声明了管理器child_manager = OtherManager()
,所以默认管理器为child_manager
解决方案是将额外的管理器放在另一个基类中,并在默认值之后将其引入继承层次结构
class AbstractBase(models.Model):
# ...
parent_manager = CustomManager()
class Meta:
abstract = True
class ExtraManager(models.Model):
child_manager = OtherManager()
class Meta:
abstract = True
class Child(AbstractBase, ExtraManager):
pass
根据Python的常规名称解析顺序,parent_manager
为父类中第一个管理器,子类未声明管理器,所以默认管理器为parent_manager
.