django管理器

自定义管理器

为什么要自定义管理器

  • 向管理器添加额外的方法,为类增加“表级”功能的首选方式(添加行级功能,比如只对某个模型的实例起作用,应该使用模型方法)。
  • 修改管理器返回的原始查询集

添加额外的管理器方法

自定义管理器返回任何你想要的数据

下面的例子自定义管理器提供一个with_counts方法,返回所有的OpinionPoll对象列表,为该对象额外添加类一个num_responses属性,该属性保存一个聚合查询的结果。

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 demo1_opinionpoll p, demo1_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)
    person_name = models.CharField(max_length=50)
    response = models.TextField()

测试输出结果:

for op in OpinionPoll.objects.with_counts():
    print(op.num_responses)
# 1 1 2 1 1 1 1 1 1 1

修改管理器的原始查询集

模型:

class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.CharField(max_length=50)

重写Manager.get_queryset() 来覆盖管理器自带的 Queryset. 从而返回自己想要的查询集。

class DahlBookManager(models.Manager):
    def get_queryset(self):
        return super(DahlBookManager, self).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

Book 有两个manager。Book.objects.all() 返回所有的对象,Book.dahl_objects.all() 则返回只有作者是Dahl的图书对象。

因为get_queryset()返回QuerySet对象,可以使用filter(),exclude()和所有其他QuerySet方法

一个模型使用多个管理器。这是模型添加通用过滤器的简单方法。

class AuthorManager(models.Manager):
    def get_queryset(self):
        return super(AuthorManager, self).get_queryset().filter(role='A')

class EditorManager(models.Manager):
    def get_queryset(self):
        return super(EditorManager, self).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()
默认管理器

Django  将类中定义的第一个管理器解释为默认的管理器。

从Manager中调用自定义的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()

直接从管理器Person.people中调用 authors() 和 editors()

使用QuerySet的方法创建Manager

上面的方法要求同一个方法在查询集和管理器上都创建,QuerySet.as_manager() 可以用来创建一个带有自定义查询集方法副本的管理器实例:

class Person(models.Model):
    ...
    people = PersonQuerySet.as_manager()

这里PersonQuerySet.as_manager() 创建的管理器实例等价与PersonManager

并不是每个查询集的方法都在管理器层面上有意义。如  QuerySet.delete().需要防止复制到管理器中

按照规则复制:

  • 公共方法默认被复制。
  • 私有方法(前面带一个下划线)默认不被复制。
  • 带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

classmethod from_queryset(queryset_class)

在进一步的使用中,你可能想创建一个自定义管理器和一个自定义查询集。你可以调用Manager.from_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)()

自定义管理器和模型继承

  • 非抽象基类的管理器不会被子类继承(会引发错误),只能在子类重新定义。
  • 抽象基类管理器被子类继承
  • 子类显式定义了管理器,这个管理器为默认管理器,抽象基类的管理器依然可以用

抽象基类:

class AbstractBase(models.Model):
    # ...
    objects = CustomManager()
    class Meta:
        abstract = True

子类没有定义管理器。默认管理器就是继承的objects

class ChildA(AbstractBase):
    # This class has CustomManager as the default manager.
    pass

子类使用自己的管理器OtherManager

class ChildB(AbstractBase):
    # An explicit default manager.
    default_manager = OtherManager()

继承的objects依然可以使用

如果想要使用自定义的管理器OtherManager,但是又想继承的objects作为默认的管理器,但是又不能在子类中定义管理器,否则默认管理器被覆盖。可以在另外一个基类添加想要使用的管理器。然后继承时放在默认管理器的基类后面,如:

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

 

管理器实现上的注意事项

无论向自定义管理器添加了什么功能,都必须可以得到管理器实例的一个浅拷贝副本。

如下面的代码必须正常运行。

>>> import copy
>>> manager = MyManager()
>>> my_copy = copy.copy(manager)

如果不能复制,查询就会失败

如果覆盖了__getattr__,或者其它管理器中控制对象状态的私有方法,确保不会影响到管理器的复制。

 

 

转载于:https://my.oschina.net/acutesun/blog/1516601

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值