如何在Django中从TableA出发关联TableB和TableC执行内连接查询
从TableA出发关联查询TableB和TableC的Django实现
需求说明
需要实现等价于以下SQL的查询逻辑,从TableA出发关联查询对应的TableB和TableC数据,最终返回结果包含TableA所有属性 + TableB所有属性 + TableC所有属性:
select * from TableA inner join TableB on TableA.id=TableB.table_a_id inner join TableC on TableA.id=TableC.table_a_id where TableA.id=3
现有Django模型与序列化器
from django.db import models from rest_framework import serializers class Users(models.Model): first_name = models.CharField(max_length=255, blank=True, null=True, unique=True) last_name = models.CharField(max_length=255, blank=True, null=True) class TableA(models.Model): user = models.ForeignKey(Users, on_delete=models.CASCADE) atrribute_a_1 = models.CharField(max_length=255, blank=True, null=True, unique=True) atrribute_a_2 = models.CharField(max_length=255, blank=True, null=True) class TableB(models.Model): user = models.ForeignKey(Users, on_delete=models.CASCADE) table_a_id = models.ForeignKey(TableA, on_delete=models.CASCADE) atrribute_b_1 = models.DecimalField(max_digits=100, decimal_places=8, default=0) atrribute_b_2 = models.DecimalField(max_digits=100, decimal_places=8, default=0) atrribute_b_3 = models.DecimalField(max_digits=100, decimal_places=8, default=0) class TableC(models.Model): user = models.ForeignKey(Users, on_delete=models.CASCADE) table_a_id = models.ForeignKey(TableA, on_delete=models.CASCADE) atrribute_c_1 = models.DecimalField(max_digits=100, decimal_places=8, default=0) atrribute_c_2 = models.DecimalField(max_digits=100, decimal_places=8, default=0) class TableBSerializer(serializers.ModelSerializer): class Meta: model = TableB fields = '__all__' class TableCSerializer(serializers.ModelSerializer): class Meta: model = TableC fields = '__all__'
解决方案
1. 调整查询语句
由于TableB和TableC通过外键关联到TableA,从TableA出发查询关联数据属于反向一对多查询,需要用prefetch_related预取关联集合,避免N+1查询问题:
# 查询id=3的TableA对象,并预取对应的TableB、TableC数据 table_a_instance = TableA.objects.prefetch_related('tableb_set', 'tablec_set').get(id=3)
注:
tableb_set和tablec_set是Django自动生成的反向关联名称,格式为模型名小写_set;如果需要自定义名称,可以在ForeignKey中添加related_name参数(比如table_a_id = models.ForeignKey(TableA, on_delete=models.CASCADE, related_name='tablebs'))。
2. 修改TableASerializer以包含关联字段
原序列化器只返回TableA自身字段,需要嵌套TableBSerializer和TableCSerializer来关联输出对应的子表数据:
class TableASerializer(serializers.ModelSerializer): # 嵌套序列化TableB和TableC的集合(many=True表示多个关联对象) tableb_set = TableBSerializer(many=True) tablec_set = TableCSerializer(many=True) class Meta: model = TableA fields = '__all__'
3. 执行序列化
serializer = TableASerializer(table_a_instance) result_data = serializer.data
补充说明
- 如果业务场景中一个
TableA仅对应一个TableB和一个TableC,可将many=True改为many=False,同时给外键添加unique=True约束; - 若需要完全复刻SQL的笛卡尔积扁平结构(每条记录是TableA+TableB+TableC的组合),可使用
values()方法手动拼接字段,但嵌套序列化的REST风格返回结构更符合接口设计规范。
内容的提问来源于stack exchange,提问作者younes
相关产品推荐
相关产品推荐

