Django中如何预加载唯一关联对象且避免额外查询?
问题描述
现有一段Django代码,通过子查询筛选出每个Blog的最新Post,使用values_list可以仅执行一次数据库查询就拿到所需数据:
qs = Blog.objects.filter(post=Subquery( Post.objects.filter(blog=OuterRef("pk")) .order_by("-date").values("pk")[:1] )) for bt, pt in qs.values_list("title", "post__title"): print(f"The latest post in {bt} is about {pt}")
现在需要调整这段代码,让Django直接构建Blog和对应的最新Post对象,能像下面这样直接访问关联对象,同时保证仅执行一次数据库查询:
for blog in qs.select_related("post"): print(f"The latest post in {blog.title} is about {blog.post.title}")
注意:不能反转查询集(比如用Post.objects.select_related("blog")),因为还有其他关联模型需要按相同逻辑处理。
解决方案
方案1:利用注解+属性封装(一次查询)
如果Blog模型没有直接指向Post的ForeignKey字段,可以通过annotate把最新Post的关键字段注解到Blog查询集中,再通过模型属性封装实现直接访问Post对象:
- 定义子查询获取每个Blog的最新Post:
from django.db.models import Subquery, OuterRef, CharField, DateTimeField # 子查询:锁定当前Blog的最新Post latest_post_subquery = Post.objects.filter(blog=OuterRef("pk")).order_by("-date")[:1]
- 给Blog查询集注解最新Post的核心字段,并过滤掉无Post的Blog:
qs = Blog.objects.annotate( _latest_post_id=Subquery(latest_post_subquery.values("pk")), _latest_post_title=Subquery(latest_post_subquery.values("title"), output_field=CharField()), _latest_post_date=Subquery(latest_post_subquery.values("date"), output_field=DateTimeField()) ).filter(_latest_post_id__isnull=False)
- 在Blog模型中添加属性,自动构建Post对象:
class Blog(models.Model): title = models.CharField(max_length=100) @property def latest_post(self): if hasattr(self, "_latest_post_id"): return Post( id=self._latest_post_id, title=self._latest_post_title, date=self._latest_post_date, blog=self ) return None
- 直接按需求访问即可:
for blog in qs: print(f"The latest post in {blog.title} is about {blog.latest_post.title}")
方案2:若Blog已有Post外键(一次查询)
如果你的Blog模型本身就有post ForeignKey字段指向Post,只需在原查询集后追加select_related("post"),Django会自动关联JOIN两张表,一次性获取所有数据并构建对象:
qs = Blog.objects.filter(post=Subquery( Post.objects.filter(blog=OuterRef("pk")) .order_by("-date").values("pk")[:1] )).select_related("post") for blog in qs: print(f"The latest post in {blog.title} is about {blog.post.title}")
内容的提问来源于stack exchange,提问作者Roman Odaisky
相关产品推荐
相关产品推荐

