管理器(Manager)

管理器是 Django 的模型进行数据库查询操作的接口。Django 应用的每个模型都拥有至少一个管理器。




管理器的名字

假设有以下 model:

from django.db import models

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

    def __str__(self):
        return self.title

默认情况下,Django 为每个模型类添加一个名为 objects 的管理器。你可以在每个模型类中重命名它们:

class Book(models.Model):
    #...
    book_manager = models.Manager()

现在就可以用 book_manager 来调用 Book 模型的管理器了,例如:

b = Book.book_manager.get(title='图书1')




自定义管理器

你可以在模型中使用自定义的管理器,方法是继承 Manager 基类并实例化你的自定义管理器。

自定义管理器一般有两个用途:向管理器类中添加额外的方法,或者修改管理器返回的原始查询集。




1.添加额外的管理器方法

添加额外管理器方法是为你的类增加“表级”功能的首选方式。 (如果要添加行级功能 —— 比如只对某个模型的实例起作用 ——应使用模型方法 ,而不是管理器方法。)

一个最简单例子:

from django.db import models

# 自定义管理器
class BookManager(models.Manager):
    def test(self):
        return '一个测试'

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

    objects = BookManager()

    def __str__(self):
        return self.title

shell 中测试下:

>>> from myApp.models import Book
>>> Book.objects.test()

'一个测试'

我们来写一个更有意义的例子,添加一个管理器方法,直接执行 SQL 语句,查询某个作者所写的图书的总数:

from django.db import models
from django.db import connection 

# 自定义管理器
class BookManager(models.Manager):
    # 自定义管理方法
    def author_counts(self, author):
        cursor = connection.cursor()

        # 直接执行 SQL 语句
        ins = ("""
            SELECT COUNT(*)
            FROM myApp_book
            WHERE author='{}';
            """).format(author)
        counts = cursor.execute(ins)

        for i in counts:
            count = i[0]

        return count

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

    objects = BookManager() # 指定使用哪个管理器

    def __str__(self):
        return self.title

shell 中测试下:

>>> from myApp.models import Book

>>> Book.objects.author_counts(author='作者1')
2

>>> Book.objects.author_counts(author='作者2')
1




2.修改管理器的原始查询集

你可以通过超类覆盖 Manager.get_queryset() 方法来覆盖管理器自带的 Queryset。自定义 get_queryset() 方法能根据你所需要的属性返回查询集。

例如,下面的模型有两个管理器,一个返回所有的对象,另一个则只返回作者是 “作者1” 的对象:

from django.db import models
from django.db import connection 

# 自定义管理器
class BookManager(models.Manager):

    def get_queryset(self):
        return super(BookManager, self).get_queryset().filter(author='作者1')

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

    objects = models.Manager() # 默认管理器
    book_manager = BookManager() # 自定义管理器

    def __str__(self):
        return self.title

shell 中测试下:

from myApp.models import Book

# 使用自定义的管理器 book_manager,只会返回 author 是 “作者1” 的对象
>>> Book.book_manager.all()
<QuerySet [<Book: 图书1>, <Book: 图书2>]>

# 使用默认管理器,返回全部对象
>>> Book.objects.all()
<QuerySet [<Book: 图书1>, <Book: 图书2>, <Book: 图书3>, <Book: 图书4>]>

同一模型使用多个管理器,在一个模型里面添加多个 Manager 实例。这是给模型添加通用过滤器(选择器)的一个简单方法:

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() # 只选择 role=A 的对象
    editors = EditorManager() # 只选择 role=E 的对象




3.从 Manager 中调用自定义的 QuerySet

虽然大多数标准查询集的方法可以从管理器中直接访问到,但是如果你需要将一些被定义到一个自定义查询集中的额外方法也在管理器上实现,下面所展示的这个例子是唯一可行办法:

from django.db import models

# 自定义的 QuerySet,并新增了 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()

    def __str__(self):
        return self.first_name + self.last_name

现在可以用 Person.people.authors() 的形式来调用了。

shell 中测试下:

from myApp.models import Person

# 选择 role=A 的对象
>>> Person.people.authors() 
<QuerySet [<Person: 李小明>, <Person: 张小花>]>

# 选择 role=E 的对象
>>> Person.people.editors()
<QuerySet [<Person: 王小红>, <Person: 林小白>]>




4.使用 QuerySet 的方法作为 Manager

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

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

通过 QuerySet.as_manager() 创建的管理器 实例,实际上等价于上面例子中的 PersonManager

看一个完整例子:

from django.db import models

# 自定义的 QuerySet,并新增了 QuerySet 的方法
class PersonQuerySet(models.QuerySet):
    def authors(self):
        return self.filter(role='A')

    def editors(self):
        return self.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'))
        )

    # 把 PersonQuerySet 作为管理器
    people = PersonQuerySet.as_manager()

    def __str__(self):
        return self.first_name + self.last_name

照样,我们可以用 Person.people.editors() 的形式来调用。

from myApp.models import Person

# 选择 role=A 的对象
>>> Person.people.authors() 
<QuerySet [<Person: 李小明>, <Person: 张小花>]>

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

方法按照以下规则进行复制:

  • 公共方法默认被复制。
  • 私有方法(前面带一个下划线)默认不被复制。
  • 带 queryset_only 属性,并且值为False的方法总是被复制。
  • 带 queryset_only 属性,并且值为True 的方法不会被复制。

例子:

class PersonQuerySet(models.QuerySet):
    ......

    # 可在 manager 中调用
    def public_method(self):
        return '公共方法'

    # 不可在 manager 中调用
    def _private_method(self):
        return '私有方法'

    # 不可在 manager 中调用
    def opted_out_public_method(self):
        return '私有方法'
    opted_out_public_method.queryset_only = True

    # 可在 manager 中调用
    def _opted_in_private_method(self):
        return '公共方法'
    _opted_in_private_method.queryset_only = False




from_queryset

在进一步的使用中,你可能想创建一个自定义管理器和一个自定义查询集。

你可以调用 Manager.from_queryset(),它会返回管理器的一个子类,带有自定义查询集所有方法的副本:

from django.db import models

class BaseManager(models.Manager):
    def manager_only_method(self):
        return '管理器独有方法'

class PersonQuerySet(models.QuerySet):
    def manager_and_queryset_method(self):
        return '管理器和查询集共有方法'


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'))
        )

    objects = BaseManager.from_queryset(PersonQuerySet)()

    def __str__(self):
        return self.first_name + self.last_name

shell 中测试下:

>>> Person.objects.manager_only_method()
'管理器独有方法'

>>> Person.objects.manager_and_queryset_method()
'管理器和查询集共有方法'

>>> Person.objects.all().manager_and_queryset_method()
'管理器和查询集共有方法'
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 136,989评论 19 139
  • 原文:https://my.oschina.net/liuyuantao/blog/751438 查询集API 参...
    阳光小镇少爷阅读 3,978评论 0 8
  • Django 文档协作翻译小组人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606网站:h...
    布客飞龙阅读 662评论 0 35
  • Django 1.8.2 文档Home | Table of contents | Index | Modules...
    轩辕小爱阅读 2,432评论 0 2
  • 安宁的手机上显示着一条微信:“班长。” 是高中同学肖兑。 从见到安宁那一刻她就一直这样叫安宁。 肖兑的的特点是男朋...
    威德阅读 397评论 1 0

友情链接更多精彩内容