前言:基础篇可以看之前发布的文章:https://blog.csdn.net/qq_39253370/article/details/108502177?spm=1001.2014.3001.5501
默认 django 的模型都有一个管理器,叫 Manager,可以看到 Manager 继承 BaseManager并且调用了 from_queryset 将 QuerySet 类传进去。
class Manager(BaseManager.from_queryset(QuerySet)):
pass
为什么将 QuerySet 传进去,可以看下面源码:
注意:版本有差异的话可能会不一样,下面为 3.2 源码
@classmethod
def _get_queryset_methods(cls, queryset_class):
# 创建方法
def create_method(name, method):
def manager_method(self, *args, **kwargs):
return getattr(self.get_queryset(), name)(*args, **kwargs)
manager_method.__name__ = method.__name__
manager_method.__doc__ = method.__doc__
return manager_method
"""
循环获取 QuerySet 中的方法
inspect.getmembers: 获取对象的属性
判断 QuerySet 中的方法是否以 _ 开头,如果开头则跳过或是获取到 queryset_only 方法也跳过
"""
new_methods = {}
for name, method in inspect.getmembers(queryset_class, predicate=inspect.isfunction):
# Only copy missing methods.
if hasattr(cls, name):
continue
# Only copy public methods or methods with the attribute `queryset_only=False`.
queryset_only = getattr(method, 'queryset_only', None)
if queryset_only or (queryset_only is None and name.startswith('_')):
continue
# Copy the method onto the manager.
# 创建方法复制到管理器中
new_methods[name] = create_method(name, method)
return new_methods
"""
queryset_class: 就是上面默认管理器中传入的 QuerySet
"""
@classmethod
def from_queryset(cls, queryset_class, class_name=None):
if class_name is None:
class_name = '%sFrom%s' % (cls.__name__, queryset_class.__name__)
return type(class_name, (cls,), {
'_queryset_class': queryset_class,
**cls._get_queryset_methods(queryset_class),
})
那么 QuerySet 里面就是我们所有操作数据库的一些方法,上述源码得出结论,不管是默认的管理还是自定义的管理器都拥有QuerySet 中的所有方法。
这个要熟悉管理器的查询逻辑,上述源码看到管理器是具有 QuerySet 的方法的,那么如果想修改初始的 QuerySet 方法,可以在管理器中重写该方法,比如:
class NewsManager(models.Manager):
def filter(self):
return super(NewsManager, self).filter(title__icontains="ceshi").values("title")
views.py:
def test(request):
qs = News.objects.filter()
print(qs)
return JsonResponse({"count": list(qs)})
或者想添加额外的 Manager 方法和一些具有辨识度的方法,比如 AbstractUser 的默认管理器中就有 create_user 这样的方法。
class UserManager(BaseUserManager):
def create_user(self, username, email=None, password=None, **extra_fields):
extra_fields.setdefault('is_staff', False)
extra_fields.setdefault('is_superuser', False)
return self._create_user(username, email, password, **extra_fields)
def create_superuser(self, username, email=None, password=None, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
注意:Manager 中自定义的方法不支持链式调用,比如下面的:
class NewsManager(models.Manager):
def with_contain_title(self):
return self.filter(title__icontains="ceshi").values("title")
def with_is_published(self):
return self.filter(is_published=True).values("title", "is_published")
# views.py
def test(request):
qs = News.objects.with_contain_title().with_is_published()
print(qs)
这将引发:AttributeError: 'QuerySet' object has no attribute 'with_is_published'
但是可以支持调用原有 queryset 中的方法:
qs = News.objects.with_contain_title().filter(is_published=True).values("title", "is_published")
print(qs) #
上面 Manager 定义的方法我们也可以自定义在 QuerySet 中,使用QuerySet.as_manager() 访问这些方法, 比如:
class NewsQuerySet(models.QuerySet):
def with_contain_title(self):
return self.filter(title__icontains="ceshi").values("title")
def with_is_published(self):
return self.filter(is_published=True).values("title", "is_published")
class News(models.Model):
objects = NewsQuerySet.as_manager()
# views.py
def test(request):
qs = News.objects.with_contain_title()
print(qs) #
似乎在如何组织自定义Manager或自定义QuerySet类之间的逻辑方面有很大的灵活性。应该根据什么原则来决定使用那种呢?
上述管理器的缺点也看到了,主要是没办法链式查询,管理器更像是贴合模型的内容,queryset 更像是公共的查询 api。
QuerySet 主要是便于组合查询:
def test(request):
qs = News.objects.with_contain_title().with_is_published()
print(qs) #
从逻辑上讲,这些定义主要对查询集的扩展或是避免写一些重复的查询。如果你不在查询集上做很多的操作或是你根本不想返回查询集,这可能用不到模型管理器,一般自定义 QuerySet 比较常用。
自定义 QuerySet 的好处和管理器差不多,可以重写原有里面的方法或是想定制属于自己的 QuerySet 方法。
但是有一个问题就是:我们自己定义的方法怎么从管理器中访问,因为QuerySet 中所有的方法都是通过管理器去访问的,那么如何将我们自定义的方法添加到管理器中?有两种方法:
第一种: 通过管理器去承载
这种方法像是从 QuerySet 中拷贝到 Manager 中,但是多了一些重复的代码,比如我再 QuerySetz 中定义的方法还要再 NewsManager 中再写一遍,显得有些冗余,那么有没有更为方便的写法呢,这就用到了第二种方法。
class NewsQuerySet(models.QuerySet):
def with_contain_title(self):
return self.filter(title__icontains="ceshi").values("title")
def with_is_published(self):
return self.filter(is_published=True).values("title", "is_published")
class NewsManager(models.Manager):
def get_queryset(self):
return NewsQuerySet(model=self.model, using=self._db)
def with_contain_title(self):
return self.get_queryset().with_contain_title()
def with_is_published(self):
return self.get_queryset().with_is_published()
第二种: as_manager
先看源码:
"""
from_queryset:有没有很熟悉,在上述管理器继承哪里也有这个,实际是一个东西,就是去复制 QuerySet 中的方法。
"""
def as_manager(cls):
# Address the circular dependency between `Queryset` and `Manager`.
from django.db.models.manager import Manager
manager = Manager.from_queryset(cls)()
manager._built_with_as_manager = True
return manager
as_manager.queryset_only = True
as_manager = classmethod(as_manager)
QuerySet 中的 as_manager 实际作用就是再去执行一遍 copy 的动作,将我们自定义的方法给copy到管理器中,等价于方法一。
class NewsQuerySet(models.QuerySet):
def with_contain_title(self):
return self.filter(title__icontains="ceshi").values("title")
def with_is_published(self):
return self.filter(is_published=True).values("title", "is_published")
# class NewsManager(models.Manager):
#
# def get_queryset(self):
# return NewsQuerySet(model=self.model, using=self._db)
#
# def with_contain_title(self):
# return self.get_queryset().with_contain_title()
#
# def with_is_published(self):
# return self.get_queryset().with_is_published()
class News(models.Model):
"""
研究进展
"""
# objects = NewsManager()
objects = NewsQuerySet.as_manager()
# views.py
def test(request):
# qs = News.objects.with_contain_title()
qs = News.objects.with_is_published()
print(qs)
有时我们可能要同时定义Manager 和 QuerySet,这个时候可以调用 from_queryset,他将返回一个基础的 Manager 子类和带有一份 QuerySet 方法的拷贝:
class NewsQuerySet(models.QuerySet):
def with_contain_title(self):
return self.filter(title__icontains="ceshi").values("title")
def with_is_published(self):
return self.filter(is_published=True).values("title", "is_published")
class NewsManager(models.Manager):
# def get_queryset(self):
# return NewsQuerySet(model=self.model, using=self._db)
# def with_contain_title(self):
# return self.get_queryset().with_contain_title()
#
# def with_is_published(self):
# return self.get_queryset().with_is_published()
def with_counts(self):
self.count()
class News(models.Model):
"""
研究进展
"""
# objects = NewsManager()
# objects = NewsQueryset.as_manager()
objects = NewsManager.from_queryset(NewsQuerySet)()
# views.py
def test(request):
# qs = News.objects.with_contain_title()
qs = News.objects.with_is_published()
print(qs) #
return JsonResponse({"count": list(qs)})
Django之管理器Manager(之前我写的一篇文章):https://blog.csdn.net/qq_39253370/article/details/108502177?spm=1001.2014.3001.5501
官网:https://docs.djangoproject.com/zh-hans/4.1/topics/db/managers/