Django ORM 无法通过 `ForeignKey` 自动关联,而是需要 **根据父模型中的某个字段(比如 ID)去查询子模型**。

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,自动生成嵌套返回的数据,完全不依赖外键。你希望我直接写吗?

相关推荐
code2cat6 小时前
【随笔】连续批处理:长短请求怎样在迭代之间补位
java·人工智能·后端·大语言模型·vllm
最强小杰6 小时前
DeepSeek V4 Flash + Codex CLI 接入教程:原生 Responses API 适配,从配置到跑通收藏这篇就够了(2026)
java·前端·python·ai
DeepVisionary7 小时前
为拿并网许可,OpenAI在佐治亚新园区同意高峰期断电三成
python·自动化
掘金者阿豪7 小时前
SQL Server数据迁移完成后,凌晨的数据库告警该怎么查?
前端·后端
鶴哥只手遮天7 小时前
AFSIM 2.9.0 深度解析·进阶卷第 1 章 插件源码精读:16 个 WSF 插件的架构与实现
后端
愚农搬码7 小时前
低代码表单与在线问卷不是一回事:产品定位和技术边界
后端
武子康7 小时前
固定时间步已经开了,为什么游戏回放还会走出不同位置?
人工智能·后端·agent
70后写java的老码农7 小时前
换模型只改三行配置:LangChain4j + Spring Boot 3.5 实战,一次分别接入 9 家模型服务
后端·ai编程
VIP_CQCRE7 小时前
内容自动化越跑越乱?先补上状态、去重和失败恢复
python·ai·工作流·幂等设计·内容自动化