Django 之 QuerySet特性及高级使用技巧

对于网站和 app 来说,最耗性能的就是数据库的查询了,反复读写数据库和查询数据集queryset较大时都会占据很多内存。
queryset:
queryset是django提供的一个数据库接口,我们可以使用 filter、exclude等方法进行查询从而得到一个queryset对象,queryset一般是一个集合,这里叫做queryset。
复习django中查询方法:

filetr:返回一个QuerySet包含与给定查找参数匹配的新对象。
exclude:返回一个新的QuerySet包含对象,这些对象与给定的查找参数不匹配。
	Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3), headline='Hello')
order_by:排序
	order_by('id') 按照id升序 -id:倒叙排列
	order_by('?') 随机查询一条,但是django已经不建议使用,查询缓慢且耗费资源
	order_by('id','-date') 如果id相同则按照日期降序排列
reverse:对结果做反转,必须在已经定义好的order_by上面使用
distinct:去掉重复数据
values:返回一个字典数据,可用于优化查询,如果查询出来很多字段,可以只指定自己想要的字段
values_list:返回一个元组数据,如果仅传递单个字段,可以使用flat参数,是true则返回单个值,结果是一个列表
all:查询全部,结果是一个queryset
select_related:连表查询,适用于一对一和外键(单对多)
prefetch_related:将自动为每个指定的查询分批检索相关对象,多对多和多对一
defer:如果指定某个字段则查询时不检索
only:只加载某些字段
get:查找参数匹配的对象,结果是一个model对象
create:创建对象并保存
bulk_create:批量创建数据
count:返回一个整数,该整数表示数据库中与匹配的对象数QuerySet
latest:最新对象
first:匹配的第一个对象
last:匹配的最后一个对象
aggregate:聚合,通常结合 Count、Q、F、Age、Sum使用
exists:是否存在,结果是true/flase
delete:删除匹配的对象

queryset的惰性:
复习完以上常用的方法之后,来看一下queryset是怎么工作的。
在内部,QuerySet可以构造,过滤,切片和大致传递a,而无需实际访问数据库。除非您执行某些操作来评估查询集,否则实际上不会发生数据库活动。

article_list = Article.objects.filter(title__contains="django")

以上代码并不会执行查询,无论写的多么复杂,只有做进一步操作时(如:exists\统计长度等),django才会执行对数据库的查询,被称为数据库的执行,减少不必要的查询和无效操作,被称为queryset的惰性。

for article in article_list:
    print(article.title)

queryset的缓存:
当你循环article_list时,会从数据库中查询匹配的项的title数据,这些结果会先存到quseryset的缓存中,这样后续你再次便利article_list时django就不需要再次查询了,减少数据库的操作。
但下面梨子可不一样:
这样就没办法复用了,这要看你后续还要不要使用该对象。

for article in Article.objects.filter(title__contains="django"):
    print(article.title)

判断也会触发queryset的执行:

article_list = Article.objects.filter(title__contains="django")
if article_list:
   for article in article_list:
    print(article.title)

但是有时我们只想查询数据在不在,使用if触发就变成一个不好的选择,这是可以使用exists:

article_list = Article.objects.filter(title__contains="django")
if article_list.exists()pass

注意: 判断查询结果是否存在到底用if还是exists取决于你是否希望缓存查询数据集复用,如果是用if,反之用exists。

统计查询结果最好不要用len()方法,len()方法会触发queryset的执行,可以使用count,因为count是住在数据库层面就已经查询好了的,但是queryset已经在缓存里了,使用len快。

推荐使用:
count = Article.objects.filter(title__contains="django").count()
不推荐使用:
count = Article.objects.filter(title__contains="django")
len(count)
同样推荐使用:
article_list = Article.objects.filter(title__contains="django")
if article_list:
	len(article_list)

当queryset很大时,只需要按需取就行

article_list = Article.objects.filter(title__contains="django").values('title')
if article_list:
    print(article.title)
 
article_list = Article.objects.filter(title__contains="django").values_list('id', 'title')
if article_list:
    print(article.title)

更新数据建议使用update方法:

id10的数据提取出来,放到缓存中,变更信息后再写入数据库,显然时触发了queryset的。
article = Article.objects.get(id=10)
Article.title = "Django"
article.save()

不需要把数据载入到缓存,显然更高效。
Article.objects.filter(id=10).update(title='Django')

但是也有update搞不定的事情,比如批量更新等

使用explain方法可以统计一个查询所消耗的时间:

print(Blog.objects.filter(title='My Blog').explain(verbose=True))
 
# output
Seq Scan on public.blog  (cost=0.00..35.50 rows=10 width=12) (actual time=0.004..0.004 rows=10 loops=1)
  Output: id, title
  Filter: (blog.title = 'My Blog'::bpchar)
Planning time: 0.064 ms
Execution time: 0.058 ms

小结:
可以根据不同的场景,来合理的使用queryset中的方法,比如上述讲的那些。

猜你喜欢

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