深入理解Queryset源码之内置缓存特性

一 背景

之前做了篇笔记,简单介绍了下Queryset的两大性质—–缓存和惰性,当时初学Django,也并没有阅读下QuerySet的底层源码。

现做此笔记,详细的从代码角度理解下什么样的操作会去查询数据库,什么样的操作会去创建清除QuerySet内置的缓存。

浏览了网上的一些博客,大多数都只是字面谈了谈Queryset的缓存和惰性两大性质,有些写的也是错的,因此我就自己去源码寻找正确的答案,毕竟自己经历一番,才会更加熟悉。


二 查询数据库,产生缓存列表的代码分析

首先进入到QuerySet类,from django.db.models import QuerySet

{width=90%}

说明:我们在实例方法__init__中可以找到存放缓存的属性的定义

接下来,我们需要找到查询数据库产生缓存的地方

{width=90%}

知道了缓存产生的函数,我们就可以依次去从源码中找哪些地方调用了该函数。

1.__getstate__使用pickle模块等序列化queryset时候,会去查询数据库,生成缓存列表,例如将查询集序列化到缓存redis中。

{width=90%}

2.__getitem__方法中,对queryset进行索引或者切片会去查询数据库,生成缓存列表

{width=90%}

3.__bool__方法中,调用if else来对查询集进行判断的时候,例如if queryset:,此时会去查询数据库,
因此当已经存在缓存列表时,使用if queryset.exists()而不是直接if queryset。这样不会再次查询数据库。

{width=90%}

4.__iter__方法中,返回一个迭代器的时候,也就是在for循环时,会去查询数据库,生成缓存列表

{width=90%}

5.__repr__ 方法中,通过print打印或者调用queryset.__repr__均会调用__repr__,因为其中并没有定义__str__

{width=90%}

说明: 其中回调用list(self[:REPR])方法,实际上调用了__getitem__内置方法,然后调用了__iter__内置方法,返回一个迭代器。


三 清空缓存的函数

1.delete删除实例的时候,清空缓存
{width=90%}

2.update更新实例的时候,清空缓存

{width=90%}

那么到这里,针对QuerySet的缓存设置和删除的相关函数已经基本了解,但是在深入想一下,如果我不手动调用delete或者update方法,那么缓存如何清除呢?

其实,QuerySet内置的缓存是List类型的,也就是存放在内存中,那么根据Python三大内存回收机制,一段时间不使用缓存列表了,则Python解释器会自动帮你回收内存资源,这样也就自然的清除了。

所以我大胆猜测,前端发送的请求,如果多个请求间隔小,则利用QuerySet的缓存特性,则可以减少数据库的查询次数。就像self.get_queryset()原理函数一样,调用缓存。


四 不产生缓存的方法

这里先主要介绍下iterator()方法,其实还有exists()也不会生成缓存

{width=90%}

{width=90%}

分析:因为yield关键字存在,所以它所在的方法就是一个生成器。生成器是一个特殊的迭代器,依赖惰性性质,需要生成数据时候才会生成数据,不占用过多内存。迭代器内部定义了__iter__方法和__next__方法。

具体想要了解迭代器和生成器的区别,可以参考以下博客:

https://www.cnblogs.com/wj-1314/p/8490822.html

https://www.zhihu.com/question/20829330