Home > Mobile >  Django how to annotate in prefetch_related
Django how to annotate in prefetch_related

Time:06-02

I have three models:

class User:
    screen_name = Charfield


class Post:
    author = FK(User)


class Comment:
    post = FK(Post, related_name=comment_set)
    author = FK(User)

Now I want to annotate Posts in following way (original annotation is more complicated, added simpler example):

if is_student:
        comment_qs = Comment.objects.annotate(
            comment_author_screen_name_seen_by_user=Case(
                When(Q(is_anonymous=True) & ~Q(author__id=user.id), then=Value("")),
                default=F("author__screen_name"), output_field=CharField()
            ),
            comment_author_email_seen_by_user=Case(
                When(Q(is_anonymous=True) & ~Q(author__id=user.id), then=Value("")),
                default=F("author__email"), output_field=CharField()
            ),
        )
        queryset = queryset.annotate(
            post_author_screen_name_seen_by_user=Case(
                When(Q(is_anonymous=True) & ~Q(author__id=user.id), then=Value("")),
                default=F("author__screen_name"), output_field=CharField()
            ),
            post_author_email_seen_by_user=Case(
                When(Q(is_anonymous=True) & ~Q(author__id=user.id), then=Value("")),
                default=F("author__email"), output_field=CharField()
            ),
        )
    else:
        comment_qs = Comment.objects.annotate(
            comment_author_screen_name_seen_by_user=F("author__screen_name"),
            comment_author_email_seen_by_user=F("author__email")
        )
        queryset = queryset.annotate(
            post_author_screen_name_seen_by_user=F("author__screen_name"),
            post_author_email_seen_by_user=F("author__email"),
        )
queryset = queryset.prefetch_related(Prefetch("comment_set", queryset=comment_qs))

After this I want to filter Posts by comment_set__comment_author_screen_name_seen_by_user field, but I receive following error:

django.core.exceptions.FieldError: Unsupported lookup 'comment_author_screen_name_seen_by_user' for AutoField or join on the field not permitted

But this field can be accessed:

queryset[0].comment_set.all()[0].comment_author_screen_name_seen_by_user == "Foo Bar"

I thing something wrong with Prefetch, but can't tell what exactly. Any thoughts?

CodePudding user response:

You can't do that: prefetch_relateds do not appear in the query, these are done through a second query.

You can simply filter with:

Post.objects.filter(
    comment_set__author__screen_name='Foo Bar'
).distinct()

or you can filter with logic:

Post.objects.alias(
    comment_author_screen_name_seen_by_user=Case(
        When(Q(comment_set__is_anonymous=True) & ~Q(comment_set__author__id=user.id), then=Value('')),
                default=F('comment_set__author__screen_name'),
        output_field=CharField()
    )
).filter(
    comment_author_screen_name_seen_by_user='Foo Bar'
).distinct()

There is thus no need to prefetch if you only want to filter.

  • Related