Django ORM 无法通过 `ForeignKey` 自动关联,而是需要 **根据父模型中的某个字段(比如 ID)去查询子模型**。
Django ORM 无法通过 ForeignKey
自动关联,而是需要 根据父模型中的某个字段(比如 ID)去查询子模型。
在 DRF 里,这种情况可以通过 SerializerMethodField 或 自定义字段 来实现。
示例场景
假设:
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
获取子模型
from rest_framework import serializers
from .models import Parent, Childclass ChildSerializer(serializers.ModelSerializer):class Meta:model = Childfields = ("id", "content")class ParentSerializer(serializers.ModelSerializer):children = serializers.SerializerMethodField()class Meta:model = Parentfields = ("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 中使用
from rest_framework import generics
from .models import Parent
from .serializers import ParentSerializerclass ParentListAPIView(generics.ListAPIView):queryset = Parent.objects.all()serializer_class = ParentSerializerclass ParentDetailAPIView(generics.RetrieveAPIView):queryset = Parent.objects.all()serializer_class = ParentSerializer
3️⃣ 优化性能
如果父模型很多,直接在 get_children
查询会导致 N+1 查询,可以提前把子模型查出来,然后传给 serializer:
class ParentListAPIView(generics.ListAPIView):serializer_class = ParentSerializerdef 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 contextserializer = self.get_serializer(parents, many=True, context={"children_map": children_map})return Response(serializer.data)
然后在 serializer 里:
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,自动生成嵌套返回的数据,完全不依赖外键。你希望我直接写吗?