You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何编写DRF序列化器生成指定输出并规避N+1查询问题

实现方案

首先先修正现有模型定义的两处语法/逻辑问题:一是CharField未指定必填的max_length参数,二是ForeignKey的related_name和字段同名会导致反向关联冲突,修正后的可正常运行的模型如下:

from django.db import models

class User(models.Model):
    name = models.CharField(max_length=100)

class Task(models.Model):
    task_name = models.CharField(max_length=100)

class UserTask(models.Model):
    user = models.ForeignKey(User, related_name='user_tasks', on_delete=models.CASCADE)
    task = models.ForeignKey(Task, related_name='user_tasks', on_delete=models.CASCADE)

规避N+1查询的实现方式

select_related仅适用于正向多对一、一对一关联的连表查询,当前场景是User通过中间表UserTask关联Task的多对多层级关系,需要用prefetch_related配合Prefetch对象做预加载,配合select_related拉取中间表关联的Task数据,全程只会产生2次数据库查询,完全没有额外的单条记录查询:

  • 第一次查询全量User记录
  • 第二次查询这些User关联的所有UserTask记录,同时通过JOIN把关联的Task数据一并取出

视图层使用的查询集写法如下:

from django.db.models import Prefetch
from .models import User, UserTask

# 预加载逻辑直接写在查询集里
user_queryset = User.objects.prefetch_related(
    Prefetch(
        lookup='user_tasks',
        queryset=UserTask.objects.select_related('task')
    )
).all()

DRF序列化器编写

分层定义序列化器,自定义字段匹配要求的输出结构,取值时直接使用预加载好的关联数据,不会触发额外数据库查询:

from rest_framework import serializers
from .models import User, Task

class TaskSerializer(serializers.ModelSerializer):
    class Meta:
        model = Task
        fields = ['id', 'task_name']

class UserSerializer(serializers.ModelSerializer):
    # 匹配输出要求的Task字段名
    Task = serializers.SerializerMethodField()

    class Meta:
        model = User
        fields = ['id', 'name', 'Task']

    def get_Task(self, obj):
        # 直接遍历预加载的中间表记录取关联task,无额外查询
        task_list = [user_task.task for user_task in obj.user_tasks.all()]
        return TaskSerializer(task_list, many=True).data

外层结构包装

如果需要最外层包裹"User"键匹配要求的输出格式,在视图返回时直接包装序列化结果即可:

from rest_framework.views import APIView
from rest_framework.response import Response

class UserTaskListAPI(APIView):
    def get(self, request):
        serializer = UserSerializer(user_queryset, many=True)
        return Response({
            "User": serializer.data
        })

内容的提问来源于stack exchange,提问作者user9914165

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 21:12:25