Django之管理器Manager

一.管理器

官网地址:https://docs.djangoproject.com/zh-hans/3.1/topics/db/managers/#from-queryset
class Manager
Manager 是一种接口,它赋予了 Django 模型操作数据库的能力。Django 应用中每个模型拥有至少一个 Manager,Django 中的所有的 Manage 都继承自于 QuerySet。

默认情况下,Django 为每个模型类添加了一个名为 objects 的 Manager。不过,若你想将 objects 用作字段名,或想使用 objects 以外的 Manager 名字,就要在模型基类中重命名。要为指定类重命名 Manager,在该模型中定义一个类型为 models.Manager 的属性。例如:

from django.db import models

class Person(models.Model):
    #...
    people = models.Manager()

使用这个实例模型时, Person.objects 会产生一个 AttributeError 异常,而 Person.people.all() 会返回包含所有 Person 对象的列表。

二.自定义管理器

继承基类 Manager,在模型中实例化自定义 Manager,你就可以在该模型中使用自定义的 Manager。

有两种原因可能使你想要自定义 Manager:添加额外的 Manager 方法,修改 Manager 返回的原始 QuerySet(下面会讲到)。

示例,添加额外的管理器:

class MyManager(models.Manager):

    def with_counts(self):
        # 注意事项 Manager 方法能通过 self.model 获取所依附的模型类。
        print(self.model)
        return self.all()

模型中使用:

class Author(models.Model):
    objects = models.Manager()  # 默认管理器
    objects2 = MyManager()  # 自定义管理器
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    sex = models.CharField(max_length=32, default='male')
    authorDetail = models.OneToOneField(to="AuthorDetail", on_delete=models.CASCADE)

使用方法views.py:

def all(request):
    authors = Author.objects.filter(name='xx')  # 返回单个数据---默认的Manager
    print(authors)
    authors = Author.objects2.with_counts()    # 返回所有数据----自定义Manager
    print(authors)
    return HttpResponse('ok')

自定义 Manager 方法能返回任何东西,没有强制它必须返回一个 QuerySet。

添加管理器的好处:
添加额外的 Manager 方法一般是为模型添加 “表级” 功能的更好方法。(对于 “行级” 功能 —— 即,只操作单个模型对象 —— 通过 模型方法,而不是自定义 Manager 的方法。)
"表级"功能:针对表添加一系列的拓展查询(QuerysetAPI)
"行级"功能:

class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    sex = models.CharField(max_length=32, default='male')
    authorDetail = models.OneToOneField(to="AuthorDetail", on_delete=models.CASCADE)
    
    # 行级功能
    def get_name(self):
        return self.name

自定义管理器需要注意的点:
当我们只使用自定义Manager对象查询时,各个自定义方法是不能够链接的。
错误的写法:

authors = Author.objects2.with_counts().with_counts()
# with_counts并不是QuerysetAPI不支持链式调用,
# 这里只是继承Queryset可以使用里面的方法而已,下面会提到如何解决这个问题

三.重写管理器的初始 QuerySet

有如下模型:
文章开头提到所有的 Manager 都是继承自于 QuerySet,那么我们也可以重写Manager.get_queryset() 方法来覆盖 Manager 的基础 QuerySet。
语句 Author.objects.all() 会返回数据库中所有的作者。

class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    sex = models.CharField(max_length=32, default='male')
    authorDetail = models.OneToOneField(to="AuthorDetail", on_delete=models.CASCADE)

示例,我想一个 Manager返回所有作者,一个Manager返回我自己想要的属性对象,可以这样操作:

class MyManager2(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(name='xx3')
        
class Author(models.Model):
    objects = models.Manager()
    objects2 = MyManager()
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    sex = models.CharField(max_length=32, default='male')
    authorDetail = models.OneToOneField(to="AuthorDetail", on_delete=models.CASCADE)

使用方法:

def all(request):
    authors = Author.objects.all()
    print(authors)
    authors = Author.objects2.all()  
    print(authors)
    return HttpResponse('ok')

两个QuerySet结果是不一样的,第一个是返回所有model对象,第二个则是仅返回 name是xx3的model对象 :
在这里插入图片描述
因为 get_queryset() 返回一个 QuerySet 对象,你可以在上面调用 filter(), exclude() 和其它的 QuerySet 方法。

也可以在一个模型中使用多个管理器:

class MyManager3(models.Manager):
    pass

class MyManager4(models.Manager):
    pass

class Author(models.Model):
    objects = models.Manager()
    objects2 = MyManager2()
    objects3 = MyManager3()
    objects4 = MyManager4()
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    sex = models.CharField(max_length=32, default='male')

四.管理器调用自定义 QuerySet 方法

大部分标准的 QuerySet 方法都能从 Manager 访问,因为所有的 Manager 都是继承自 queryset 的,但项目不同遇到的需求也不相同,我们总想定制属于自己的 QuerySet 方法,但自己定义的 QuerySet 方法需要 Manager 来承载,这就需要自定义 QuerySet 中定义了额外方法,且在 Manager 中实现了它们,有以下示例:

class MyManagerQueryset(models.QuerySet):

    def authors(self):
        return self.filter(name='xx')

    def authors2(self):
        return self.filter(name='xx3')


class MyManager(models.Manager):

    def get_queryset(self):
        return MyManagerQueryset(model=self.model, using=self._db)

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

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

模型中使用:

class Author(models.Model):
    objects = models.Manager()
    objects2 = MyManager()
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    sex = models.CharField(max_length=32, default='male')

视图函数中:

def all(request):
    authors = Author.objects2.authors()
    print(authors)
    authors = Author.objects2.authors2()
    print(authors)
    return HttpResponse('ok')

这里我们会发现QuerySet 和Manager 有重复代码,怎么解决这个问题:
创建带有 QuerySet 方法的管理器
可以用 QuerySet.as_manager() 创建一个 Manager 实例,拷贝了自定义 QuerySet 的方法:

class Author(models.Model):
    # 创建带有 QuerySet 方法的管理器
    all_objects = MyManagerQueryset.as_manager()

由 QuerySet.as_manager() 创建的 Manager 实例实质上等价于前面例子中的 MyManager。

这里需要注意:
不是每个 QuerySet 方法在 Manager 层都是有意义的;例如,我们故意阻止 QuerySet.delete() 被拷贝进 Manager 类中。

方法拷贝规则如下:

  • 公开方法默认会被拷贝。
  • 私有方法(以下划线打头)默认不会被复制。
  • queryset_only 属性值为 False 的方法总是会被复制。
  • queryset_only 属性值为 True 的方法永远不会被复制。

源码中的例子:
这里只展示几个作为参考

以下都会被拷贝:

def earliest(self, *fields):
    return self._earliest(*fields)

def latest(self, *fields):
    return self.reverse()._earliest(*fields)

def first(self):
    """Return the first object of a query or None if no match is found."""
    for obj in (self if self.ordered else self.order_by('pk'))[:1]:
        return obj

def last(self):
    """Return the last object of a query or None if no match is found."""
    for obj in (self.reverse() if self.ordered else self.order_by('-pk'))[:1]:
        return obj

def in_bulk(self, id_list=None, *, field_name='pk'):

def resolve_expression(self, *args, **kwargs):
        if self._fields and len(self._fields) > 1:
            # values() queryset can only be used as nested queries
            # if they are set up to select only a single field.
            raise TypeError('Cannot use multi-field values as a filter value.')
        query = self.query.resolve_expression(*args, **kwargs)
        query._db = self._db
        return query
    resolve_expression.queryset_only = True

以下都不会被拷贝:

def _update(self, values):
        """
        A version of update() that accepts field objects instead of field names.
        Used primarily for model saving and not intended for use by general
        code (it requires too much poking around at model internals to be
        useful at that level).
        """
        assert not self.query.is_sliced, \
            "Cannot update a query once a slice has been taken."
        query = self.query.chain(sql.UpdateQuery)
        query.add_update_fields(values)
        # Clear any annotations so that they won't be present in subqueries.
        query.annotations = {
    
    }
        self._result_cache = None
        return query.get_compiler(self.db).execute_sql(CURSOR)
    _update.alters_data = True
    _update.queryset_only = False

def _raw_delete(self, using):
        """
        Delete objects found from the given queryset in single direct SQL
        query. No signals are sent and there is no protection for cascades.
        """
        return sql.DeleteQuery(self.model).delete_qs(self, using)
    _raw_delete.alters_data = True
def _update(self, values):
        """
        A version of update() that accepts field objects instead of field names.
        Used primarily for model saving and not intended for use by general
        code (it requires too much poking around at model internals to be
        useful at that level).
        """
        assert not self.query.is_sliced, \
            "Cannot update a query once a slice has been taken."
        query = self.query.chain(sql.UpdateQuery)
        query.add_update_fields(values)
        # Clear any annotations so that they won't be present in subqueries.
        query.annotations = {
    
    }
        self._result_cache = None
        return query.get_compiler(self.db).execute_sql(CURSOR)
    _update.alters_data = True
    _update.queryset_only = False
def _prefetch_related_objects(self):
        # This method can only be called once the result cache has been filled.
        prefetch_related_objects(self._result_cache, *self._prefetch_related_lookups)
        self._prefetch_done = True

总结:
上述内容中可能会有疑问,继承models.Manager和继承 models.QuerySet有什么区别,Manager 全部都会继承Manager 那么为什么还要单独再继承 models.QuerySet呢?因为models.manager里面只能定义方法,不支持链式调用,里面定义的方法并不是QuerySetAPI ,只是当前对象self是queryset对象。可以使用queryset里面的方法。而models.queryset则不一样。 定义的方法全部是基于queryset来扩展。全部都是querysetAPI

五.from_queryset()

对于进阶用法,你可能同时要一个自定义 Manager 和一个自定义 QuerySet。你可以通过调用 Manager.from_queryset() 达成目的,这将会返回一个自定义基础 Manager 的子类,带有一份自定义 QuerySet 方法的拷贝:

class MyManagerQueryset(models.QuerySet):

    def authors(self):
        return self.filter(name='xx')

    def authors2(self):
        return self.filter(name='xx3')


class MyManager(models.Manager):

    def with_counts(self):
        return self.all()

class Author(models.Model):
    # from_queryset()
    test_objects = MyManager.from_queryset(MyManagerQueryset)()
    objects = models.Manager()
    objects2 = MyManager()
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    sex = models.CharField(max_length=32, default='male')
    authorDetail = models.OneToOneField(to="AuthorDetail", on_delete=models.CASCADE)

六.QueryManager

许多自定义模型管理器只返回以某种方式过滤的QuerySet。QueryManager允许您用最少的样板文件来表达这种模式:

class Author(models.Model):
	name = models.CharField(max_length=32)
    age = models.IntegerField()
    objects3 = QueryManager(age__gte=10).order_by('-id')

传递给QueryManager的kwargs将按原样传递给查询集过滤器()方法。还可以将Q对象传递给QueryManager,以表示更复杂的条件。请注意,您可以通过将调用链接到QueryManager上的.order_by()来设置QueryManager返回的QuerySet的顺序(这不是必需的)。
使用:

def all(request):
    authors = Author.objects3.all()
    print(authors)
    return HttpResponse('ok')

在这里插入图片描述

七.InheritanceManager

官网地址:https://django-model-utils.readthedocs.io/en/latest/managers.html
这个管理器(由jeffelmore提供)应该附加到模型继承树中的一个基本模型类。它允许基模型上的查询返回实际正确子类型的异类结果,而不需要任何额外的查询。
有如下模型:

# 地点
class Place(models.Model):
    objects = InheritanceManager()
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)


# 餐厅
class Restaurant(Place):
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)


# 广场
class Square(Place):
    flower = models.TextField()
    balloon = models.TextField()

源码:

class InheritanceManagerMixin:
    _queryset_class = InheritanceQuerySet

    def get_queryset(self):
        return self._queryset_class(self.model)

    def select_subclasses(self, *subclasses):
        return self.get_queryset().select_subclasses(*subclasses)

    def get_subclass(self, *args, **kwargs):
        return self.get_queryset().get_subclass(*args, **kwargs)

    def instance_of(self, *models):
        return self.get_queryset().instance_of(*models)

这里不过多介绍具体可看另一篇文章,里面有详细介绍:https://blog.csdn.net/qq_39253370/article/details/108443148

如有疑问或有异议的地方,欢迎加群讨论:934244622

猜你喜欢

转载自blog.csdn.net/qq_39253370/article/details/108502177