文章目录
- 1. QuerySet被计算的场合
- 2. queryset使用实战(以Blog为例)
- `我们测试一下下面这些语句执行的sql,需要在settings里加入 LOGGING(验证方式)`
- 1. 我们使用`posts = Blog.objects.all() 或者 posts = Blog.objects.filter()
- 2. queryset是具有cache的
- 3. 简单的使用if语句进行判断也会完全执行整个queryset并且把数据放入cache,为了避免这个,可以用exists()方法来检查是否有数据:
- 4. 当queryset非常巨大时,cache会成为问题
- 3. 总结
- 4. 开启数据库的logging
简言: Django的QuerySet具有
延迟
特性,仅在强制操作下才会被执行,这种行为使得QuerySet更加的高效我们可以连接任意一个过滤器到一个QuerySet上,在QuerySet
计算之前
并不会访问数据库
1. QuerySet被计算的场合
- 首次迭代时
- 当对QuerySet访问时,如Post.objects.all()[:4]
- 当对QuerySet缓存时
- 当在QuerySet上调用repr()或len()时
- 当在QuerySet上显示调用list()时
- 当在某个语句中对QuerySet进行测试时,如bool(), or, and 或 if
2. queryset使用实战(以Blog为例)
我们测试一下下面这些语句执行的sql,需要在settings里加入 LOGGING(验证方式)
1. 我们使用`posts = Blog.objects.all() 或者 posts = Blog.objects.filter()
- 上面的代码并没有运行任何的数据库查询。你可以使用posts,给它加上一些过滤条件,或者将它传给某个函数,这些操作都不会发送给数据库。这是对的,因为数据库查询是显著影响web应用性能的因素之一
2. queryset是具有cache的
-
当你遍历queryset时,所有匹配的记录会从数据库获取,然后转换成Django的model。这被称为执行
(evaluation).这些model会保存在queryset内置的cache中,这样如果你再次遍历这个queryset,
你不需要重复运行通用的查询。我们使用两次for循环,可以看到logging只会打印一次posts = Post.published.all() for post in posts: # 执行sql查询 print(post) print(post.publish) print(post.title) for post in posts: # 不执行sql查询,使用cache print(post)
3. 简单的使用if语句进行判断也会完全执行整个queryset并且把数据放入cache,为了避免这个,可以用exists()方法来检查是否有数据:
posts = Post.published.all()
if posts.exists(): # .exists()判断值是否存在,可以避免数据放入queryset的cache
for post in posts:
print(post)
if posts: # if判断是否存在也会进行数据库查询并将返回结果放入QuerySet的cache
print(2333)
关于是否使用exists()的谏言:
当只是用来判断某个QuerySet是否存在的时候,使用if QuerySet.exists()
,当用来判断某个QuerySet是否存在,并且后面的语句还会用到QuerySet中的数据的时候使用if QuerySet:
,使用.exists反而会造成额外的开销
4. 当queryset非常巨大时,cache会成为问题
处理成千上万的记录时,将它们一次装入内存是很浪费的。更糟糕的是,巨大的queryset可能会锁住系统
进程,让你的程序濒临崩溃。要避免在遍历数据的同时产生queryset cache,可以使用iterator()方法
来获取数据,处理完数据就将其丢弃。
posts = Post.published.all().iterator()
# iterator()可以一次只从数据库获取少量数据,这样可以节省内存
for post in posts:
print(post)
print(post.publish)
# 但是,再次遍历没有打印,因为迭代器已经在上一次遍历(next)到最后一次了,没有可以遍历得了
for post in posts:
print(post)
使用iterator()方法来防止生成cache,意味着遍历同一个queryset时会重复执行查询。所以使
用iterator()的时候要当心,确保你的代码在操作一个大的queryset时没有重复执行查询
posts = Post.published.all()
for post in posts.iterator(): # 此处会进行一次sql查询,但是并没有缓存进queryset cache
print(post)
print(post.publish)
for post in posts.iterator(): # 此处会再次进行一次sql查询,同样没有缓存进queryset cache
print(post)
3. 总结
- queryset的cache是用于减少程序对数据库的查询,在通常的使用下会保证只有在需要的时候才会查询数据库。
- 使用exists()和iterator()方法可以优化程序对内存的使用。不过,由于它们并不会生成queryset cache,可能会造成额外的数据库查询。
4. 开启数据库的logging
在setting中加入
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level': 'DEBUG',
},
}
}