Django中的“管理器”是什么?

30 投票
3 回答
15791 浏览
提问于 2025-04-17 14:42

我看过Django官方文档里的定义,还是搞不清楚什么是Manager

文档上说它们可以让你对数据库表或模型进行操作,但我还是不太明白。

有没有人能给我解释一下管理器和它的作用?如果能举个例子就更好了。

3 个回答

10

定义

根据文档的说法:

管理器是一个Django类,它提供了数据库查询操作和Django模型之间的接口。

换句话说,在Django模型中,管理器就是和数据库打交道的接口。例如,当你想从数据库中获取对象时,你需要通过模型类上的管理器来构建一个 QuerySet

默认情况下,管理器可以通过 Model.objects 属性访问。这个管理器是 django.db.models.Manager。不过,扩展它并改变默认管理器是非常简单的。

自定义管理器

根据文档

你可以通过扩展基础管理器类,在模型中实例化你的自定义管理器来使用它。

你可能想要自定义管理器的原因有两个(这两个原因并不互斥):

  1. 添加额外的管理器方法
  2. 修改管理器返回的初始 QuerySet

示例:给管理器添加额外的方法

from django.db import models

class DocumentManager(models.Manager):
    def pdfs(self):
        return self.filter(file_type='pdf')

    def smaller_than(self, size):
        return self.filter(size__lt=size)


class Document(models.Model):
    name = models.CharField(max_length=30)
    size = models.PositiveIntegerField(default=0)
    file_type = models.CharField(max_length=10, blank=True)

    objects = DocumentManager()

    def __str__(self) -> str:
        return self.name

示例:修改管理器返回的初始 QuerySet

from django.db import models


class AuthorManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(first_name__startswith='M')

class Author(models.Model):
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    email = models.EmailField()

    objects = AuthorManager()

    def __str__(self) -> str:
        return f"{self.first_name} {self.last_name}"

示例:同时使用多个管理器

同一个模型可以有多个管理器。

from django.db import models
from django.db.models.functions import Length

class BookTitleManager(models.Manager):
    def short_titles(self):
        return self.annotate(length=Length('title')).filter(length__lte=20)

    def long_titles(self):
        return self.annotate(length=Length('title')).filter(length__gt=20, length__lte=30)
    
    def very_long_titles(self):
        return self.annotate(length=Length('title')).filter(length__gt=30)

    def starts_with(self, letter):
        return self.filter(title__startswith=letter)


class BookPagesManager(models.Manager):
    def small_books(self):
        return self.filter(pages__lt=200)
    
    def medium_books(self):
        return self.filter(pages__gte=200, pages__lt=300)
    
    def large_books(self):
        return self.filter(pages__gte=300, pages__lte=500)


class Book(models.Model):
    title = models.CharField(max_length=255)
    author = models.CharField(max_length=255)
    pages = models.IntegerField()

    objects = models.Manager()
    titles = BookTitleManager()
    sizes = BookPagesManager()

    def __str__(self) -> str:
        return f'{self.title} by {self.author}'

在之前的代码示例中,有三个管理器:默认的 models.ManagerBookTitleManagerBookPagesManager,分别分配给 objectstitlessizes

之前管理器的问题在于你不能像这样链接它们:

>>> Book.titles.long_titles().starts_with('P')
AttributeError: 'QuerySet' object has no attribute 'starts_with'

示例:自定义管理器和查询集(允许链接)

如果你想链接管理器中定义的方法,你应该定义一个自定义的 QuerySet,如下所示:

from django.db import models
from django.db.models.functions import Length


class AuthorQuerySet(models.QuerySet):
    def long_first_name(self):
        return self.annotate(length=Length("first_name")).filter(length__gte=10)

    def short_last_name(self):
        return self.annotate(length=Length("last_name")).filter(length__lte=10)


class AuthorManager(models.Manager):
    def get_queryset(self):
        return AuthorQuerySet(self.model, using=self._db)

    def long_first_name(self):
        return self.get_queryset().long_first_name()

    def short_last_name(self):
        return self.get_queryset().short_last_name()


class Author(models.Model):
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    email = models.EmailField()

    objects = AuthorManager()

    def __str__(self) -> str:
        return f"{self.first_name} {self.last_name}"

示例:将自定义查询集用作管理器

在管理器中仅定义自定义 QuerySets 时,可以简单地扩展 QuerySet 并将其设置为管理器。

from django.db import models
from django.db.models.functions import Length


class PublisherQuerySet(models.QuerySet):
    def long_name(self):
        return self.annotate(length=Length("name")).filter(length__gte=15)

    def long_address(self):
        return self.annotate(length=Length("address")).filter(length__gte=25)

    def country_starts_with(self, letter):
        return self.filter(country__startswith=letter)


class Publisher(models.Model):
    name = models.CharField(max_length=100)
    address = models.CharField(max_length=255)
    country = models.CharField(max_length=100)

    objects = PublisherQuerySet.as_manager() # uses QuerySet as Manager

    def __str__(self) -> str:
        return self.name

还需注意的事项

  • 如果你想使用 objects 作为字段名,或者想给管理器使用其他名称,你可以在每个模型中重命名它。要为特定类重命名管理器,可以在该模型上定义一个类型为 models.Manager() 的类属性。
class Document(models.Model):
    name = models.CharField(max_length=30)
    size = models.PositiveIntegerField(default=0)
    file_type = models.CharField(max_length=10, blank=True)

    stuff = models.Manager()

    def __str__(self) -> str:
        return self.name

在之前的代码示例中,调用 Document.objects 会产生一个 AttributeError 异常,因为默认管理器已经被重命名,现在可以使用 Document.stuff

  • 管理器只能通过模型类访问,而不能通过模型实例访问,以确保“表级”操作和“记录级”操作之间的分离。

  • 如果一个模型有外键,外键模型的实例将可以访问一个管理器,该管理器返回第一个模型的所有实例。默认情况下,这个管理器被命名为 FOO_set,其中 FOO 是源模型的名称,转换为小写。

12

管理器可以看作是应用程序和数据库之间的一个“门”。它的一个好处是你可以为模型定义自己的基础查询集。举个例子:如果你有一个名为“书籍”的模型,并且它有一个“可用性”字段,你可以准备自己的查询集,来过滤特定类型的可用性:

在models.py文件中:

class AvailableBookManager(models.Manager):
    def get_query_set(self):
        return super(AvailableBookManager, self).get_query_set().filter(availability=1)

class Book(models.Model):
   (...)#fields definition
 
   objects = models.Manager() #default manager
   available_objects = AvailableBookManager() #own manager

现在你可以这样使用:

books = Book.available_objects.all()

而不是使用:

books = Book.objects.filter(available=1)
32

在Django中,管理器通常是一个隐藏的东西,它帮助程序员在模型代码和数据库之间进行沟通。

当你使用Django的ORM(对象关系映射)进行查询时,其实是通过调用

from my_app.models import MyModel

mms = MyModel.objects.all()

来实现的。在这个例子中,函数中的objects部分就是管理器返回的内容。如果你希望你的MyModel只获取blue(蓝色)的实例(数据库中可能还有red(红色)的模型),那么你可以创建一个管理器,并这样修改你的模型:

class BlueManager(models.Manager):
    def get_query_set(self):
        return super(BlueManager, self).get_query_set().filter(colour='Blue')

class MyModel(models.Model):
     colour = models.CharField(max_length=64)
     blue_objects = BlueManager()

然后调用

MyModel.blue_objects.all()

就只会返回颜色为blue的对象。需要注意的是,这种过滤模型的方法并不好!

通常情况下,如果你想修改管理器返回的QuerySet(查询集),或者需要添加“表”级别的查询(而不是普通的Django“行”级别查询),你就需要修改Manager接口。关于管理器的文档非常详细,并且包含了多个示例。

撰写回答