Django数据库请求优化:如何将3次查询降至1次?
优化Django数据库查询:将3次查询降至1次
首先,我们可以利用Django的Prefetch对象精确控制预取的Post数据,这样只需要1次数据库查询就能获取目标Blog和所有符合条件的Post,之后在内存中拆分nav和posts即可。
最优实现代码
from django.db.models import Prefetch # 先定义要预取的Post查询集:筛选已发布、按发布时间倒序 filtered_posts = Post.objects.filter(publish=True).order_by('-published_date') # 获取Blog的同时,预取符合条件的Post并存储到自定义属性中 blog = get_object_or_404( Blog.objects.prefetch_related( Prefetch('post', queryset=filtered_posts, to_attr='filtered_posts') ), subdomain=extracted.subdomain ) # 在内存中拆分导航页和普通文章(无额外数据库查询) nav = [post for post in blog.filtered_posts if post.is_page] posts = [post for post in blog.filtered_posts if not post.is_page]
为什么你的prefetch_related尝试没生效?
你之前的代码blog.post_set.filter(publish=True)...会触发新查询,原因有两点:
- 你的Post模型定义了
related_name='post',所以Blog关联的Post集合应该是blog.post.all(),而不是默认的post_set; - 即使正确使用了
prefetch_related,后续的filter/order_by操作默认会触发新的数据库查询,而不是使用预取到的内存数据。用Prefetch对象指定to_attr,就能把过滤后的预取数据固定在Blog实例的自定义属性里,后续操作完全在内存中完成。
对比你的现有方案
你已经把查询次数从3次降到了2次(1次查Blog,1次查符合条件的Post),上面的方案进一步压缩到1次。在数据量较小时两者差异不大,但当Post数量较多时,1次查询的性能优势会更明显。
额外优化提示
如果is_page和publish字段的组合筛选频率很高,可以给这两个字段建立联合索引,进一步提升查询效率:
class Post(models.Model): # ... 其他字段定义 class Meta: indexes = [ models.Index(fields=['publish', 'is_page']), ]
内容的提问来源于stack exchange,提问作者Herman Martinus
相关产品推荐
相关产品推荐

