管理器(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()
'管理器和查询集共有方法'
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容

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