Django如何追加QuerySet结果并在viewset返回关联外键数据
实现方案
你要的「返回ModelA时附带关联ModelB数据」的需求完全可以在get_queryset函数中实现,不需要手动拼接两个不同模型的QuerySet,核心是用预加载配合嵌套序列化器完成,具体实现如下:
第一步:在get_queryset中预加载关联数据
因为ModelA和ModelB是一对多关系(一个ModelA对应多条ModelB),用prefetch_related可以一次性批量查出所有关联的ModelB数据,避免循环查询产生的N+1性能问题,示例代码:
from rest_framework import viewsets from .models import ModelA class ModelAViewSet(viewsets.ModelViewSet): serializer_class = ModelASerializer # 后续定义的序列化器 def get_queryset(self): # 反向关联默认名为小写模型名+_set,即modelb_set # 如果ForeignKey定义了related_name参数,替换成自定义的名称即可 base_qs = ModelA.objects.all().prefetch_related("modelb_set") # 这里可以追加你需要的过滤、排序、权限控制逻辑 return base_qs
如果需要对关联的ModelB做过滤(比如只返回生效的ModelB记录),可以用
Prefetch对象自定义预加载规则:from django.db.models import Prefetch base_qs = ModelA.objects.all().prefetch_related( Prefetch( "modelb_set", queryset=ModelB.objects.filter(is_active=True), to_attr="active_b_list" ) )配置
to_attr后,关联数据会挂载到ModelA实例的active_b_list属性上,后续序列化器对应字段名要和这个值保持一致。
第二步:配置嵌套序列化器返回关联数据
预加载只是把关联数据查到了内存里,还需要在序列化器里配置对应字段,才能把ModelB的数据包含在接口返回中:
from rest_framework import serializers from .models import ModelA, ModelB # 先写ModelB的序列化器,定义要返回的B的字段 class ModelBSerializer(serializers.ModelSerializer): class Meta: model = ModelB fields = ["id", "其他需要返回的ModelB字段"] class ModelASerializer(serializers.ModelSerializer): # 字段名和prefetch_related里的关联名保持一致,many=True表示是多条数据 modelb_set = ModelBSerializer(many=True, read_only=True) # 如果上面用了to_attr='active_b_list',这里字段名就写active_b_list = ModelBSerializer(many=True, read_only=True) class Meta: model = ModelA fields = ["id", "some_fields", "modelb_set"] # 把关联字段加到返回列表里
注意事项
- 不要尝试用
|运算符直接合并ModelA和ModelB的QuerySet:两个模型表结构不同,强行合并会抛出异常,也不符合「每条A记录挂载对应B记录」的返回结构要求 - 不要在序列化器逻辑里临时查询关联的ModelB:会产生N+1查询问题,数据量上来之后接口响应速度会急剧下降,预加载逻辑放在
get_queryset里是DRF框架推荐的最优实现 - 一对多关系的预加载必须用
prefetch_related,select_related仅支持一对一、多对一的正向外键场景,不能用于反向一对多关联。
内容的提问来源于stack exchange,提问作者dsimond
相关产品推荐
相关产品推荐

