Django无直接外键关联时,如何在ModelA查询中获取ModelC的val_e?
解决方法
1. 修改ViewSet的QuerySet
因为ModelA关联ModelB,ModelC和ModelB是一对一关系,我们可以通过跨表关联查询一次性获取所有需要的数据,避免N+1查询:
方式一:利用关联路径直接查询
class ModelAViewSet(ListDataResponseMixin, APIKeyViewSet): endpoint_permissions = [APILicenseKeyPermission.Codes.A] # 通过select_related关联ModelB和对应的ModelC,一次性拉取数据 queryset = ModelA.objects.select_related('modelB__modelc') serializer_class = ModelASerializer filterset_class = ModelAFilter filter_backends = [DjangoFilterBackend]
这里modelB__modelc的路径逻辑:modelB是ModelA里的外键字段名,modelc是ModelB对ModelC的默认反向关联名(Django会把一对一关系的反向关联设为小写模型名)。
方式二:用Subquery注解字段(更灵活)
如果不确定反向关联名,或者需要更复杂的过滤逻辑,可以用子查询注解字段:
from django.db.models import OuterRef, Subquery class ModelAViewSet(ListDataResponseMixin, APIKeyViewSet): endpoint_permissions = [APILicenseKeyPermission.Codes.A] queryset = ModelA.objects.annotate( val_e=Subquery( # 通过ModelB的关联关系,取对应ModelC的val_e字段 ModelC.objects.filter(modelB=OuterRef('modelB')).values('val_e')[:1] ) ) serializer_class = ModelASerializer filterset_class = ModelAFilter filter_backends = [DjangoFilterBackend]
2. 修改Serializer添加val_e字段
对应上面两种QuerySet的修改方式,Serializer有两种写法:
对应方式一的Serializer写法
直接通过source指定跨表路径:
class ModelASerializer(serializers.ModelSerializer): # 从关联的ModelB->ModelC中读取val_e val_e = serializers.CharField(source='modelB.modelc.val_e', read_only=True) class Meta: model = ModelA fields = ( "id", "val_a", "val_b", "val_e" # 将新字段加入返回字段列表 )
对应方式二的Serializer写法
因为QuerySet已经把val_e注解到ModelA实例上,直接添加字段即可:
class ModelASerializer(serializers.ModelSerializer): val_e = serializers.CharField(read_only=True) class Meta: model = ModelA fields = ( "id", "val_a", "val_b", "val_e" )
注意点
- 由于
ModelC的modelB字段设置了null=False,每个ModelB都对应一个ModelC,不会出现val_e为空的情况;如果后续这个约束变化,记得给字段加上allow_null=True处理空值。 - 两种方法都是数据库层面的一次性查询,不会产生额外的数据库请求,性能表现一致。
内容的提问来源于stack exchange,提问作者user3533755
相关产品推荐
相关产品推荐

