Django嵌套序列化器分组聚合报错:int对象无code属性
解决AttributeError:int实例无code属性的问题
问题根源
你遇到的这个错误,本质是查询集返回的country字段是整数ID,而非Country模型实例,但你的序列化器却期望用CountrySerializers去序列化它,自然会报错“int实例没有code属性”。
具体来说,你的get_queryset方法里用了values('country'),这个方法会返回字典格式的查询结果,其中country对应的是关联模型的主键ID(整数类型),而不是完整的Country对象。但TradeAggregateSerializers里的country = CountrySerializers(read_only=True),是要序列化一个Country实例去读取它的code、name字段,两者不匹配就触发了错误。
解决方案:调整查询逻辑与序列化器
我们可以换一种思路,从Country模型出发反向聚合关联的Trade数据,这样既能获取完整的国家信息,又能计算指定字段的平均值。
1. 修改视图类的查询逻辑
把查询集从Trade转向Country,通过反向关联(trade__前缀)来计算平均值,同时做参数校验避免非法输入:
from rest_framework import serializers class TradeAggragateViewSet(viewsets.ModelViewSet): queryset = Country.objects.all() # 改为Country的查询集 serializer_class = TradeAggregateSerializers def get_queryset(self): import_or_export = self.request.GET.get('data_type') # 校验参数合法性 if import_or_export not in ['imports', 'exports']: raise serializers.ValidationError("data_type must be 'imports' or 'exports'") # 为每个国家标注指定字段的平均值 queryset = self.queryset.annotate( value=models.Avg(f'trade__{import_or_export}') ) return queryset
2. 调整序列化器适配新的查询集
现在查询集是Country实例,所以序列化器可以直接继承CountrySerializers,新增value字段即可:
class TradeAggregateSerializers(CountrySerializers): value = serializers.DecimalField(max_digits=10, decimal_places=2) class Meta(CountrySerializers.Meta): fields = CountrySerializers.Meta.fields + ('value',)
为什么这个方案可行?
- 从
Country模型出发查询,每个结果都是完整的Country实例,CountrySerializers可以正常序列化它的code、name字段; - 通过
annotate和反向关联trade__{import_or_export},直接计算出每个国家对应贸易字段的平均值,无需额外处理; - 参数校验能避免传入非法字段导致的后续错误。
另一种可选方案(不修改查询模型)
如果你坚持从Trade模型查询,可以调整values的返回字段,包含国家的所有属性,然后修改序列化器对应这些字段:
# 视图类的get_queryset def get_queryset(self): import_or_export = self.request.GET.get('data_type') if import_or_export not in ['imports', 'exports']: raise serializers.ValidationError("data_type must be 'imports' or 'exports'") queryset = Trade.objects.values( 'country__id', 'country__code', 'country__name' ).annotate(value=models.Avg(import_or_export)) return queryset # 对应的序列化器 class TradeAggregateSerializers(serializers.Serializer): country = serializers.DictField( child=serializers.CharField(), source='*' # 把country__开头的字段映射为字典 ) value = serializers.DecimalField(max_digits=10, decimal_places=2) def to_representation(self, instance): # 手动把country__code、country__name整理为country字典 return { 'country': { 'id': instance['country__id'], 'code': instance['country__code'], 'name': instance['country__name'] }, 'value': instance['value'] }
不过这个方案需要手动处理字段映射,不如第一种方案简洁优雅。
内容的提问来源于stack exchange,提问作者Hassam.C
相关产品推荐
相关产品推荐

