Search code examples
pythondjangoormforeign-keys

Preload unique related object in Django


This works just fine and only performs one DB query:

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}")

However, how can I have Django construct Blog and Post objects out of such a queryset without needing additional queries? Something like the following:

for blog in qs.select_related("post"):
    print(f"The latest post in {blog.title} is about {blog.post.title}")

Assume it’s not possible to reverse the queryset (Post.objects.select_related("blog")), because, for example, there are other related models that need the same treatment as Post.


Solution

  • Surprisingly, this works!

    qs = Blog.objects.annotate(
        latest_post=FilteredRelation(
            "post",
            condition=Q(
                post=Subquery(
                    Post.objects.filter(blog=OuterRef("pk"))
                    .order_by("-seq")
                    .values("pk")[:1]
                ),
            ),
        ),
    ).select_related("latest_post")
    
    for blog in qs:
        print(f"The latest post in {blog.title} is about {blog.latest_post.title}")
    

    This doesn’t actually require the condition to be unique:

    for blog in Blog.objects.annotate(
        some_post=FilteredRelation("post"),
    ).select_related("some_post"):
        do_something(blog, blog.some_post)
        # will happen multiple times for the same blog
        # but blog.some_post will be different each time