Django通过多对多字段调用关联模型方法为子查询添加注解
问题根源
你遇到的报错和注解返回None的问题完全是由__in和OuterRef的错误搭配导致的:
- 当你写
main_authors__in=[OuterRef('pk')]时,Django不会将列表内的OuterRef识别为查询占位符,对于字符串类型的主键,甚至会将返回的主键值当成可迭代对象逐字符拆分匹配,自然查不到符合条件的结果,注解全为None。 - 当你去掉方括号写
main_authors__in=OuterRef('pk')时,__in语法期望接收可迭代的值列表/查询集,而OuterRef('pk')返回的是单个值,导致Django生成的SQL语法错误,直接将外层表字段名拼接在IN关键字后触发ProgrammingError。
正确实现
多对多字段直接使用等值匹配即可关联到外层的单个作者对象,不需要使用__in语法:
from django.db.models import Count, IntegerField, OuterRef, Subquery class AuthorQuerySet(QuerySet): def annotate_with_total_titles_by_genres(self) -> QuerySet: main_titles_by_genre_sci_fi_query = Book.objects.filter( main_authors=OuterRef('pk') ).by_genre_scifi().annotate( cnt=Count('*') ).values('cnt')[:1] return self.annotate( sci_fi_titles_total = Subquery(main_titles_by_genre_sci_fi_query, output_field=IntegerField()) )
可选优化
如果希望没有科幻类作品的作者注解值为0而非None,可以搭配Coalesce处理空值:
from django.db.models import Count, IntegerField, OuterRef, Subquery, Coalesce class AuthorQuerySet(QuerySet): def annotate_with_total_titles_by_genres(self) -> QuerySet: main_titles_by_genre_sci_fi_query = Book.objects.filter( main_authors=OuterRef('pk') ).by_genre_scifi().annotate( cnt=Count('*') ).values('cnt')[:1] return self.annotate( sci_fi_titles_total = Coalesce( Subquery(main_titles_by_genre_sci_fi_query, output_field=IntegerField()), 0 ) )
修改后即可按照预期方式调用:
annotated_authors = Author.objects.filter(<your filter conditions>).annotate_with_total_titles_by_genres()
内容的提问来源于stack exchange,提问作者lys
相关产品推荐
相关产品推荐

