Managers
class Manager
管理器是向Django模型提供数据库查询操作的接口。Django应用程序中每个模型至少有一个管理器。
Manager names
默认情况下管理器的名字为objects,如果想自定义:
from django.db import models
class Person(models.Model):
#...
people = models.Manager() # rename manager
Customer Managers
通过扩展基管理器类并在模型中实例化定制管理器,可以在特定模型中使用定制管理器。
您可能有两个原因想要定制管理器:添加额外的管理器方法,and/or修改管理器返回的初始QuerySet。
Adding extra manager methods
添加额外的管理器方法
#
这个自定义管理器提供了with_counts()方法,它返回所有OpinionPoll对象的列表,每个对象都有一个额外的num_responses属性,这是聚合查询的结果:
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
class OpinionPoll(models.Model):
question = models.CharField(max_length=200)
poll_date = models.DateField()
objects = PollManager()
class Response(models.Model):
poll = models.ForeignKey(OpinionPoll, on_delete=models.CASCADE)
person_name = models.CharField(max_length=50)
response = models.TextField()
在这个示例中,您将使用OpinionPoll.objects.with_counts()返回带有num_responses属性的OpinionPoll对象列表。
关于这个例子需要注意的另一件事是Manager方法可以访问self.model以获得它们所连接的模型类。
Modifying a manager’s initial QuerySet
管理器的基QuerySet返回系统中的所有对象。
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=50)
Book.objects.all()将返回数据库中的所有图书。
您可以通过覆盖Manager.get_queryset()方法来覆盖管理器的基查询集。get_queryset()应该返回一个具有所需属性的QuerySet。
# First, define the Manager subclass.
class DahlBookManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(author='Roald Dahl')
# Then hook it into the Book model explicitly.
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 Dahl编写的图书。
当然,因为get_queryset()返回一个QuerySet对象,所以您可以使用filter()、exclude()和它上面的所有其他QuerySet方法。所以这些声明都是合法的
这个例子还指出了另一个有趣的技术:在同一个模型上使用多个管理器。您可以将任意多的Manager()实例附加到模型中。这是为您的模型定义通用“过滤器”的一种简单方法。
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 .author .all()、person .editor .all()和Person.people.all(),以产生可预测的结果。
Default managers
如果使用自定义管理器对象,请注意,Django管理器遇到的第一个管理器(按照模型中定义它们的顺序)具有特殊的状态。Django将类中定义的第一个管理器解释为“默认”管理器,Django的一些部分(包括dumpdata)将专门为该模型使用该管理器。因此,在选择默认管理器时要小心,以免覆盖get_queryset()会导致无法检索想要使用的对象。
可以使用Meta.default_manager_name指定自定义默认管理器。
如果您正在编写一些必须处理未知模型的代码,例如,在实现通用视图的第三方应用程序中,请使用这个manager(或_base_manager),而不是假设模型有一个objects Manager。
Base managers
Model._base_manager
默认情况下,Django在访问相关对象时使用的Base managers而不是Default managers。这是因为Django需要访问相关对象,而Defalut
managers将过滤掉它。
如果普通的基管理器类(Django .db.models. manager)不适合您的情况,您可以通过设置Meta.base_manager_name告诉Django使用哪个类。
在查询相关模型时不使用基础管理器。例如,如果Question Model有一个已删除的字段和一个使用deleted=True过滤掉实例的base manager,那么像Choice.objects.filter(question__name__startswith='What')这样的queryset将包含与已删除问题相关的选项。
不要过滤掉此类manager子类中的任何结果
此管理器用于访问与其他模型相关的对象。在这些情况下,Django必须能够看到它正在获取的模型的所有对象,这样就可以检索到所有引用的对象。
如果重写get_queryset()方法并过滤掉任何行,Django将返回不正确的结果。不要这样做。筛选结果为get_queryset()的管理器不适合用作基管理器。
Calling custom QuerySet methods from the manager
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()
This example allows you to call both authors() and editors() directly from the manager Person.people.
Creating a manager with QuerySet methods
可以使用QuerySet.as_manager()创建一个Manager实例,其中包含一个定制的QuerySet方法的副本:
class Person(models.Model):
...
people = PersonQuerySet.as_manager()
由QuerySet.as_manager()创建的Manager实例与前面示例中的PersonManager几乎相同。
不是每个QuerySet方法在管理人员级别都有意义;例如,我们故意阻止QuerySet.delete()方法复制到Manager类。
方法按照以下规则复制:
默认情况下复制公共方法。
默认情况下不复制私有方法(以下划线开头)。
将queryset_only属性设置为False的方法总是被复制。
将queryset_only属性设置为True的方法永远不会被复制。
class CustomQuerySet(models.QuerySet):
# Available on both Manager and QuerySet.
def public_method(self):
return
# Available only on QuerySet.
def _private_method(self):
return
# Available only on QuerySet.
def opted_out_public_method(self):
return
opted_out_public_method.queryset_only = True
# Available on both Manager and QuerySet.
def _opted_in_private_method(self):
return
_opted_in_private_method.queryset_only = False
from_queryset()
对于高级用法,您可能需要自定义管理器和自定义查询集。您可以通过调用Manager.from_queryset()来实现这一点,它返回基管理器的一个子类,带有定制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)()
Custom managers and model inheritance
模型继承
类继承和manager模型不会完美的符合每个人的要求。Mananger一般是指特别的类,它总是自己定义的类并在子类中继承它们,但这并不是一个很好的注意。而且,因为第一个定义的manager会默认为默认manager,允许这件事可以由用户控制很重要。所以,下面是Django怎么处理自定义manager和模型继承的。
- Manager定义自非抽象基类不是继承自子类。如果你想要重用一个来自非抽象基类的manager,明确的重声明它的子类。这种managers可能 to be fairly specific to the class they are defined on,因此继承他们可能经常会引起不可预期的结果(特别是当使用默认manager时)。因此,他们不能传递到子类。
- 源自抽象基类的Manager总是继承自其子类,使用Python正常的命名解析顺序(子类的名字会覆盖其他,接下来是第一个父类,and so on)。抽象基类被设计成为了捕获其子类常见的信息和行为。定义常见的manager是一般性信息的合理部分。
- 类中的默认manager是类中第一个定义的manager,或者是父层次(in the parent hierarchy)中的第一个抽象基类的默认manager。如果默认manager并没有被明确指明,Django的常用默认manager会被使用。
上述规则提供了必要的灵活性,如果你想要通过一个抽象基类,在一组模型中安装一系列的自定义manager,但是仍然自定义默认manager。例如:
class AbstractBase(models.Model):
# ...
objects = CustomManager()
class Meta:
abstract = True
如果直接在一个子类中使用它,而不在基类中声明manager,objects将会是默认manager。
class ChildA(AbstractBase):
# ...
# This class has CustomManager as the default manager.
pass
如果你想要从AbstractBase中继承,但是却需要不同的默认manager,你可以在子类中提供默认manager。
class ChildB(AbstractBase):
# ...
# An explicit default manager.
default_manager = OtherManager()
在这里,default_manager是默认的名字。因为objects是继承来的,objects manager仍然是可以使用的。只是objects不再被作为默认manager。
例如,假如你想给你的子类添加一个额外的manager,但是还是使用从AbstractBase的默认manager。你不能直接在子类中添加一个新的manager,因为那会覆盖默认manager。你必须明确包括所有从抽象基类继承的manager。解决方式就是把额外的manager放到另一个基类中,并且把它引入默认值之后的继承层次。
class ExtraManager(models.Model):
extra_manager = OtherManager()
class Meta:
abstract = True
class ChildC(AbstractBase, ExtraManager):
# ...
# Default manager is CustomManager, but OtherManager is
# also available via the "extra_manager" attribute.
pass
注意当你可以在抽象模型中定义一个自定义manager时,你不可以调用任意抽象模型的方法,下列是合法的:
ClassA.objects.do_something()
但是:
AbstractBase.objects.do_something()
将会产生一个错误。这是因为manager期望为了管理objects的集合而封装逻辑。所以,你不可以有一个抽象对象的集合,这在管理他们上是没有意义的。如果你适用于抽象模型的功能,你应该把这些功能放在抽象模型的staticmethod、classmethod。
Implementation concerns
无论,你向默认Manager添加了什么特性,必须保证可以执行Manager实例的浅拷贝。即下列代码必须可用:
>>> import copy
>>> manager = MyManager()
>>> my_copy = copy.copy(manager)
Django在某些查询时执行manager实例的浅拷贝,所以Manager不可拷贝,那些查询就会失败。
对大部分的自定义manager不是问题。如果只是在manager中添加了简单的方法,不太可能会使你manager的实例不可拷贝。然而,如果你重写了__getattr__ 或其他的一些你控制manager对象状态的私有方法,你应该确认没有影响到Mnager的可拷贝性。
控制自动 Manager 类型
就像django.com提议的那样。在gis示例中,上面的use_for_related_fields特性主要用于需要返回自定义QuerySet子类的管理器。在经理中提供这种功能时,有几件事需要记住。
不要过滤掉这种管理器子类中的任何结果
使用自动管理器的原因之一是访问与其他模型相关的对象。在这些情况下,Django必须能够看到它正在获取的模型的所有对象,这样就可以检索到所有引用的对象。
....
在定义类时设置use_for_related_fields
use_for_related_fields属性必须设置在manager类上,而不是类的实例上。前面的示例显示了正确的设置方法,但是下面的方法不起作用:
# BAD: Incorrect code
class MyManager(models.Manager):
# ...
pass
# Sets the attribute on an instance of MyManager. Django will
# ignore this setting.
mgr = MyManager()
mgr.use_for_related_fields = True
class MyModel(models.Model):
# ...
objects = mgr
# End of incorrect code.