zoukankan      html  css  js  c++  java
  • 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.
  • 相关阅读:
    jquery 自执行笔记
    快速搭建动态web工程并且进行数据库交互页面呈现
    仿途牛导航
    一些jquery技巧
    Jquery 事件冒泡 以及阻止默认事件
    absolute(绝对定位)和 relative(相对定位)
    java 日期格式转换,加减等
    StringTokenizer 简单的描述
    运算符和表达式
    Eclipse常用命令+ 简单的自动售票程序
  • 原文地址:https://www.cnblogs.com/LTEF/p/9619420.html
Copyright © 2011-2022 走看看