Django Managers管理器

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.
原文地址:https://www.cnblogs.com/LTEF/p/9619420.html