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和模型继承的。

  1. Manager定义自非抽象基类不是继承自子类。如果你想要重用一个来自非抽象基类的manager,明确的重声明它的子类。这种managers可能 to be fairly specific to the class they are defined on,因此继承他们可能经常会引起不可预期的结果(特别是当使用默认manager时)。因此,他们不能传递到子类。
  2. 源自抽象基类的Manager总是继承自其子类,使用Python正常的命名解析顺序(子类的名字会覆盖其他,接下来是第一个父类,and so on)。抽象基类被设计成为了捕获其子类常见的信息和行为。定义常见的manager是一般性信息的合理部分。
  3. 类中的默认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.

Django Managers管理器的更多相关文章

  1. django orm 管理器 objects

    给某张表的管理器重命名 class User(models.Model): name = models.CharField(max_length=100) people = models.Manage ...

  2. Django 利用管理器实现文章归档

    Django管理器:class Manager 管理器是Django的模型进行数据库查询的接口,Django应用的每个模型都拥有至少一个管理器.默认情况下,Django为每个模型类添加一个名为obje ...

  3. [django]上下文管理器

    上下文管理器django提取context中的数据去供模板调用 需求: 所有的页面都需要一个特定的变量 本质: python函数 , 接收一个HttpRequest对象的参数 , 且返回的必须是一个字 ...

  4. Django 上下文管理器的应用

    使用场景:模板继承可以减少页面内容的重复定义,实现页面内容的重用.个人博客右侧的导航栏都是继承base页面从而让代码得到最大程度的复用.但是当父模板中有动态数据的话,这些动态数据在子模版中是不会显示的 ...

  5. Django 上下文管理器,为父模板添加动态数据

    1.摘要:模板继承可以减少页面内容的重复定义,实现页面内容的重用. 但是当父模板中有动态数据的话,这些动态数据在子模版中是不会显示的.我们可以通过自定义上下文处理器来解决 2.Django上下文处理器 ...

  6. Django 源码小剖: Django ORM 查询管理器

    ORM 查询管理器 对于 ORM 定义: 对象关系映射, Object Relational Mapping, ORM, 是一种程序设计技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换.从 ...

  7. Django ORM 查询管理器

    Django ORM 查询管理器 ORM 查询管理器 对于 ORM 定义: 对象关系映射, Object Relational Mapping, ORM, 是一种程序设计技术,用于实现面向对象编程语言 ...

  8. django的模型类管理器-----------数据库操作的封装

    模型实例方法 str():在将对象转换成字符串时会被调用. save():将模型对象保存到数据表中,ORM框架会转换成对应的insert或update语句. delete():将模型对象从数据表中删除 ...

  9. WEB框架-Django框架学习-关联管理器(RelatedManager)

    一.class RelatedManager "关联管理器"是在一对多或者多对多的关联上下文中使用的管理器.它存在于下面两种情况: 1.一对多 ForeignKey关系的“另一边” ...

随机推荐

  1. 【300】◀▶ IDL - ENVI API

    参考:ENVI API 参考:ENVI Classic Display 序号 类名称   功能说明   语法 & 举例 01 ENVI 函数   ====<<<< De ...

  2. Elasticsearch-PHP 处理JSON数组和对象

    PHP中处理JSON数组和对象 客户端有一些混淆的资源是围绕着JSON的数组和对象,以及如何在PHP中指定它们.特别是,问题是由空对象和空数组导致的.这篇文章回告诉你一些在Elasticsearch ...

  3. 201671010140. 2016-2017-2 《Java程序设计》java学习第十二周

    java学习第十章:图形程序设计       本章,介绍的是如何编写使用图形用户界面GUI的java程序.主要讲的是如何编写定义屏幕上的窗口大小和位置的程序,如何在窗口中采用多种字体显示文本,如何显示 ...

  4. AttributeUsage

    [AttributeUsage] System.AttributeUsage声明一个Attribute的使用范围与使用原则. AllowMultiple 和 Inherited 参数是可选的,所以此代 ...

  5. 解决:EXCEL复制粘贴,精度丢失

    公司一部分数据是存在elasticsearch里面的,但里面的ID设计得特别长,我是打算把ID号考出来,用jmeter批量 删除的,但复制粘贴到excel里,ID就会精度丢失. 后来找到一个办法,解决 ...

  6. EasyBuy项目总结_20180409

    一.项目技术点 1.熟练使用jsp及el和jstl表达式 el: $() jstl: 1.导包; 2.声明<%@ taglib uri=" " preffix="c ...

  7. 68. Text Justification一行单词 两端对齐

    [抄题]: Given an array of words and a width maxWidth, format the text such that each line has exactly  ...

  8. Linux ssldump命令

    一.简介 tcpdump是一款很强大.很有用的网络侦听软件,但是对于ssl加密的数据包就无能为力了:ssldump则是一款可以侦听ssl加密的数据包的软件.   二.安装 1)通过yum安装 yum ...

  9. dll函数生成规则

    [转]http://blog.csdn.net/beanjoy/article/details/9136127 所谓名字修饰约定,就是指变量名.函数名等经过编译后重新输出名称的规则. 比如源代码中函数 ...

  10. Nginx 事件基本处理流程分析

    说明:本文章重点关注事件处理模型.有兴趣的同学可以去http://tengine.taobao.org/book/查找更多资料.Tengine应该是淘宝基于Nginx自己做的修改.这个地址的文档还在不 ...