Django ORM 无法通过 ForeignKey
自动关联,而是需要 根据父模型中的某个字段(比如 ID)去查询子模型。
在 DRF 里,这种情况可以通过 SerializerMethodField 或 自定义字段 来实现。
示例场景
假设:
python
class Parent(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100)
# 没有外键,只有 parent_id 用来查询子模型
class Child(models.Model):
id = models.AutoField(primary_key=True)
parent_id = models.IntegerField() # 用 parent.id 关联
content = models.CharField(max_length=200)
1️⃣ 使用 SerializerMethodField
获取子模型
python
from rest_framework import serializers
from .models import Parent, Child
class ChildSerializer(serializers.ModelSerializer):
class Meta:
model = Child
fields = ("id", "content")
class ParentSerializer(serializers.ModelSerializer):
children = serializers.SerializerMethodField()
class Meta:
model = Parent
fields = ("id", "name", "children")
def get_children(self, obj):
# 根据父模型的 id 查询子模型
qs = Child.objects.filter(parent_id=obj.id)
return ChildSerializer(qs, many=True).data
2️⃣ 在 View 中使用
python
from rest_framework import generics
from .models import Parent
from .serializers import ParentSerializer
class ParentListAPIView(generics.ListAPIView):
queryset = Parent.objects.all()
serializer_class = ParentSerializer
class ParentDetailAPIView(generics.RetrieveAPIView):
queryset = Parent.objects.all()
serializer_class = ParentSerializer
3️⃣ 优化性能
如果父模型很多,直接在 get_children
查询会导致 N+1 查询,可以提前把子模型查出来,然后传给 serializer:
python
class ParentListAPIView(generics.ListAPIView):
serializer_class = ParentSerializer
def get_queryset(self):
return Parent.objects.all()
def list(self, request, *args, **kwargs):
parents = self.get_queryset()
parent_ids = [p.id for p in parents]
children_qs = Child.objects.filter(parent_id__in=parent_ids)
# 按 parent_id 分组
children_map = {}
for c in children_qs:
children_map.setdefault(c.parent_id, []).append(c)
# 把 children_map 传给 serializer context
serializer = self.get_serializer(parents, many=True, context={"children_map": children_map})
return Response(serializer.data)
然后在 serializer 里:
python
def get_children(self, obj):
children_map = self.context.get("children_map", {})
qs = children_map.get(obj.id, [])
return ChildSerializer(qs, many=True).data
这样只会做 两条 SQL 查询,避免 N+1。
✅ 总结
- 没有外键关系 → 用
SerializerMethodField
或自定义字段查询 - N+1 查询问题 → 可以提前批量查询子模型并用
context
传递 - 适用于一对多、多对多但没有数据库约束的场景
如果你需要,我可以帮你写一个 通用方法,输入父模型 queryset 和子模型 queryset,自动生成嵌套返回的数据,完全不依赖外键。你希望我直接写吗?