如何在Django类视图中查询通过ModelC关联的ModelA与ModelB数据
问题分析与解决方案
原代码存在的问题
- Django QuerySet 没有
join方法,关联查询需通过select_related/prefetch_related或跨模型过滤、annotate实现 - 模型引用错误:代码中
Oas_Spend应为ModelB的笔误 - 过滤条件语法错误:
filter('band__brand_id')不符合 Django 过滤规则,需传入关键字参数 - F表达式使用错误:
ModelC不存在brand_id字段,关联字段应为band__id - 冗余重写
get方法:ListAPIView已内置分页、序列化逻辑,无需重复实现
修正后的实现
1. 调整 QuerySet 逻辑
根据需求,我们从 ModelA 出发,关联 ModelC(乐队信息)和 ModelB(支出数据),通过 annotate 聚合乐队总支出,再用 values 提取所需字段:
from django.db.models import Sum, F from rest_framework.generics import ListAPIView from .models import ModelA, ModelB, ModelC from .serializers import MySerializer from .pagination import DataPagination class MyView(ListAPIView): serializer_class = MySerializer pagination_class = DataPagination # 直接指定分页类 def get_queryset(self): # 关联乐队信息,聚合该乐队的总支出,提取所需字段 queryset = ModelA.objects.select_related('band').annotate( spending=Sum('band__bandspends__spending') ).values( 'band_id', band_label=F('band__label'), 'spending', 'id', 'date' ) return queryset
如果需要获取每个支出记录对应关联的乐队活动数据(而非总支出),可改用以下 QuerySet:
def get_queryset(self): queryset = ModelA.objects.select_related('band').filter( band__bandspends__isnull=False ).values( 'band_id', band_label=F('band__label'), spending=F('band__bandspends__spending'), 'id', 'date' ) return queryset
2. 序列化器定义
对应返回字段,编写序列化器:
from rest_framework import serializers class MySerializer(serializers.Serializer): band_id = serializers.IntegerField() band_label = serializers.CharField() spending = serializers.FloatField() id = serializers.IntegerField() date = serializers.DateTimeField()
3. 说明
select_related('band'):提前加载关联的ModelC数据,避免N+1查询annotate(Sum('band__bandspends__spending')):通过反向关联(ModelC的bandspends对应ModelB集合)计算乐队总支出values中的F表达式:将ModelC的label字段重命名为band_label,匹配期望的JSON格式
最终返回格式
执行后将返回符合需求的JSON结构:
[ { "band_id": 100, "band_label": "MLTR", "spending": 900.000, "id": 2212121, "date": "2018-02-01T00:00:00Z" } ]
内容的提问来源于stack exchange,提问作者85nd
相关产品推荐
相关产品推荐

