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

Django一对多与多对一序列化器及查询实现问题

优化嵌套关联数据的序列化与查询方案

问题背景

数据关系为:一个城市对应多个建筑,一个建筑可对应零到多个设备。需通过城市主键请求数据时,返回包含下属所有建筑及对应设备的嵌套结构。当前采用嵌套循环获取关联数据,效率低下,需优化序列化器编写与数据库查询方式。

模型定义:

class City(models.Model):
    # 自定义字段,例如名称等
    name = models.CharField(max_length=100)

class Building(models.Model):
    cityId = models.ForeignKey(City, on_delete=models.CASCADE)
    # 自定义字段
    name = models.CharField(max_length=100)

class Device(models.Model):
    buildingId = models.ForeignKey(Building, on_delete=models.CASCADE)
    # 自定义字段
    name = models.CharField(max_length=100)

现有序列化器仅包含单表字段:

class CitySerializer(serializers.ModelSerializer):
    class Meta:
        model = City
        fields = ['id', 'name']

class BuildingSerializer(serializers.ModelSerializer):
    class Meta:
        model = Building
        fields = ['id', 'cityId', 'name']

class DeviceSerializer(serializers.ModelSerializer):
    class Meta:
        model = Device
        fields = ['id', 'buildingId', 'name']

优化方案

1. 嵌套序列化器实现层级数据返回

通过嵌套序列化器自动处理关联关系,无需手动拼接数据:

第一步:给外键添加反向关联名称(related_name)

修改模型外键定义,简化反向查询逻辑:

class Building(models.Model):
    # 添加related_name,通过city.buildings可直接获取该城市的所有建筑
    cityId = models.ForeignKey(City, on_delete=models.CASCADE, related_name='buildings')
    name = models.CharField(max_length=100)

class Device(models.Model):
    # 添加related_name,通过building.devices可直接获取该建筑的所有设备
    buildingId = models.ForeignKey(Building, on_delete=models.CASCADE, related_name='devices')
    name = models.CharField(max_length=100)

第二步:编写嵌套序列化器

class DeviceSerializer(serializers.ModelSerializer):
    class Meta:
        model = Device
        fields = ['id', 'name']  # 无需返回buildingId,已嵌套在建筑数据中

class BuildingSerializer(serializers.ModelSerializer):
    # 嵌套设备序列化器,many=True表示多设备,read_only=True仅用于序列化输出
    devices = DeviceSerializer(many=True, read_only=True)

    class Meta:
        model = Building
        fields = ['id', 'name', 'devices']  # 无需返回cityId,已嵌套在城市数据中

class CitySerializer(serializers.ModelSerializer):
    # 嵌套建筑序列化器
    buildings = BuildingSerializer(many=True, read_only=True)

    class Meta:
        model = City
        fields = ['id', 'name', 'buildings']

2. 预取关联数据解决N+1查询问题

使用prefetch_related链式预取关联数据,避免多次数据库查询,大幅提升性能:

在视图中查询城市数据时,一次性拉取所有关联的建筑和设备:

from rest_framework.views import APIView
from rest_framework.response import Response
from .models import City
from .serializers import CitySerializer

class CityDetailView(APIView):
    def get(self, request, pk):
        # 链式预取城市的所有建筑,以及每个建筑的所有设备
        city = City.objects.prefetch_related('buildings__devices').get(pk=pk)
        
        # 如果需要对设备/建筑做过滤,可使用Prefetch精细化控制
        # from django.db.models import Prefetch
        # city = City.objects.prefetch_related(
        #     Prefetch('buildings', queryset=Building.objects.prefetch_related(
        #         Prefetch('devices', queryset=Device.objects.filter(status='active'))
        #     ))
        # ).get(pk=pk)
        
        serializer = CitySerializer(city)
        return Response(serializer.data)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 17:50:33