Django 如何在 prefetch_related 中注释
Django how to annotate in prefetch_related
我有三个模型:
class User:
screen_name = Charfield
class Post:
author = FK(User)
class Comment:
post = FK(Post, related_name=comment_set)
author = FK(User)
现在我想用下面的方式注释Post
s(原来的注释比较复杂,添加了更简单的例子):
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))
在此之后我想通过 comment_set__comment_author_screen_name_seen_by_user
字段过滤 Post
s,但我收到以下错误:
django.core.exceptions.FieldError: Unsupported lookup 'comment_author_screen_name_seen_by_user' for AutoField or join on the field not permitted
但是可以访问这个字段:
queryset[0].comment_set.all()[0].comment_author_screen_name_seen_by_user == "Foo Bar"
我觉得 Prefetch 有问题,但不能说清楚到底是什么。有什么想法吗?
你不能这样做:prefetch_related
没有出现在查询中,这些是通过第二个查询完成的。
您可以简单地过滤:
Post.objects.filter(
<strong>comment_set__author__screen_name='Foo Bar'</strong>
).distinct()
或者您可以使用逻辑过滤:
Post.objects.alias(
<b>comment_author_screen_name_seen_by_user=Case(</b>
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()
<b>)</b>
).filter(
<strong>comment_author_screen_name_seen_by_user='Foo Bar'</strong>
).distinct()
因此,如果您只想过滤,不需要预取。
我有三个模型:
class User:
screen_name = Charfield
class Post:
author = FK(User)
class Comment:
post = FK(Post, related_name=comment_set)
author = FK(User)
现在我想用下面的方式注释Post
s(原来的注释比较复杂,添加了更简单的例子):
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))
在此之后我想通过 comment_set__comment_author_screen_name_seen_by_user
字段过滤 Post
s,但我收到以下错误:
django.core.exceptions.FieldError: Unsupported lookup 'comment_author_screen_name_seen_by_user' for AutoField or join on the field not permitted
但是可以访问这个字段:
queryset[0].comment_set.all()[0].comment_author_screen_name_seen_by_user == "Foo Bar"
我觉得 Prefetch 有问题,但不能说清楚到底是什么。有什么想法吗?
你不能这样做:prefetch_related
没有出现在查询中,这些是通过第二个查询完成的。
您可以简单地过滤:
Post.objects.filter(
<strong>comment_set__author__screen_name='Foo Bar'</strong>
).distinct()
或者您可以使用逻辑过滤:
Post.objects.alias(
<b>comment_author_screen_name_seen_by_user=Case(</b>
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()
<b>)</b>
).filter(
<strong>comment_author_screen_name_seen_by_user='Foo Bar'</strong>
).distinct()
因此,如果您只想过滤,不需要预取。