Django REST如何将annotate注解值添加到JSON响应中
问题原因
- 报错是因为
annotate是Django ORM查询集(QuerySet)的专属方法,只能通过模型管理器(如Book.objects)调用,你在序列化器实例(代码里的self)上调用该方法,自然会报属性不存在的错误。 - 你用
SerializerMethodField的思路完全没必要,还会产生N+1查询问题,大幅降低接口性能。 - 不需要额外在Book模型里定义
@property方法,注解(annotate)已经可以直接给查询返回的模型实例动态添加统计属性。
正确实现步骤
1. 确认关联关系正确
首先确认你缺失的Chapter模型定义中,存在指向Book的外键,反向关联名不要和Book已有的chapters整数字段冲突,示例:
class Chapter(models.Model): # 反向关联名用chapter_list,避免和Book已有的chapters字段冲突 book = models.ForeignKey(Book, on_delete=models.CASCADE, related_name="chapter_list") # 其余字段省略
如果没有指定related_name,默认反向关联名为chapter_set,后续注解的路径要对应修改。
2. 修改视图查询集,提前完成统计注解
在视图查询Book的时候直接加上注解,一次SQL就可以算出所有书籍的Verse总数,给每个Book实例动态添加total_num_verses属性:
class BookAPIView(APIView): """ List all books """ def get(self, request, format=None): # 直接在查询时添加注解,不需要后续再重复计算 books = Book.objects.annotate( total_num_verses=Count('chapter_list__verse', distinct=True) ) serializer = BookSerializer(books, many=True) return Response(serializer.data)
如果需要同时统计章节数,也可以保留你原来的Count('chapter_list', distinct=True),按需添加序列化字段即可。
3. 修改序列化器,直接映射注解属性
不需要用SerializerMethodField,直接新增只读字段映射到注解出来的属性即可:
class BookSerializer(serializers.ModelSerializer): # 直接映射注解生成的total_num_verses属性 total_verse_count = serializers.IntegerField(source="total_num_verses", read_only=True) class Meta: model = Book # 手动列出所有要返回的字段,包含新增的统计字段 fields = ["id", "author", "chapters", "name", "total_verse_count"]
可选:SerializerMethodField的正确写法(不推荐)
如果你一定要用SerializerMethodField实现,只需要直接读取实例上的注解属性即可,不需要重复查询:
class BookSerializer(serializers.ModelSerializer): total_verse_count = serializers.SerializerMethodField() class Meta: model = Book fields = ["id", "author", "chapters", "name", "total_verse_count"] def get_total_verse_count(self, obj): # 直接取视图查询时注解生成的属性,不要在这里再做查询 return obj.total_num_verses
内容的提问来源于stack exchange,提问作者erikvm
相关产品推荐
相关产品推荐

