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

如何在DRF中序列化含GameDetail的多对多模型并生成指定API响应

问题描述

模型结构

game表

idnamedescription

platform表

idname

game_detail表

idrelease_date

game_platform关联表(game_detail_id为game_detail表外键)

idgame_idplatform_idgame_detail_id

对应models.py代码

class Game(models.Model):
    name = models.CharField(max_length=50)
    description = models.TextField(blank=True, null=True)
    genre = models.ForeignKey(Genre, related_name="games", on_delete=models.CASCADE)
    platforms = models.ManyToManyField(
        "Platform", related_name="games", through="GamePlatform"
    )

class Platform(models.Model):
    name = models.CharField(max_length=50)

class GameDetail(models.Model):
    release_date = models.DateField()

class GamePlatform(models.Model):
    game = models.ForeignKey(Game, on_delete=models.CASCADE)
    platform = models.ForeignKey(Platform, on_delete=models.CASCADE)
    game_detail = models.ForeignKey(GameDetail, on_delete=models.CASCADE)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=["game", "platform"], name="unique_game_platform"
            )
        ]

需要编写serializers.py的序列化逻辑和ModelViewSet代码,使游戏API返回如下格式的响应:

[
    {
        "id": 33,
        "name": "Crysis Remastered",
        "description": "",
        "genre": {
            "id": 1,
            "name": "Fps"
        },
        "platforms": [
            {
             "id": 1,
             "name": "PS5",
             "game_detail": {
              "release_date": "2020-11-03"
             }
            },
            {
             "id": 2,
             "name": "PC",
             "game_detail": {
              "release_date": "2020-11-13"
             }
            }
        ]
    }
]

解决方案

1. 编写序列化器(serializers.py)

通过嵌套序列化器处理关联数据,具体代码如下:

Genre序列化器

from rest_framework import serializers
from .models import Game, Platform, GameDetail, GamePlatform, Genre

class GenreSerializer(serializers.ModelSerializer):
    class Meta:
        model = Genre
        fields = ["id", "name"]

GameDetail序列化器

class GameDetailSerializer(serializers.ModelSerializer):
    class Meta:
        model = GameDetail
        fields = ["release_date"]

带GameDetail的Platform序列化器

通过SerializerMethodField从关联表中获取对应游戏的详情数据:

class PlatformWithDetailSerializer(serializers.ModelSerializer):
    game_detail = serializers.SerializerMethodField()

    class Meta:
        model = Platform
        fields = ["id", "name", "game_detail"]

    def get_game_detail(self, obj):
        # 从上下文获取当前游戏实例
        game = self.context.get("game")
        if game:
            # 查询当前游戏与平台对应的关联记录
            game_platform = GamePlatform.objects.filter(game=game, platform=obj).first()
            if game_platform:
                return GameDetailSerializer(game_platform.game_detail).data
        return None

最终Game序列化器

class GameSerializer(serializers.ModelSerializer):
    genre = GenreSerializer()
    platforms = serializers.SerializerMethodField()

    class Meta:
        model = Game
        fields = ["id", "name", "description", "genre", "platforms"]

    def get_platforms(self, obj):
        # 传入当前游戏实例到子序列化器的上下文
        queryset = obj.platforms.all()
        return PlatformWithDetailSerializer(queryset, many=True, context={"game": obj}).data

2. 编写ModelViewSet(views.py)

指定序列化器并优化查询性能:

from rest_framework import viewsets
from .models import Game
from .serializers import GameSerializer

class GameViewSet(viewsets.ModelViewSet):
    queryset = Game.objects.all().prefetch_related(
        "genre",
        "gameplatform_set__platform",
        "gameplatform_set__game_detail"
    )
    serializer_class = GameSerializer

关键说明

  • 用SerializerMethodField处理多对多关联表中的额外字段(game_detail),默认多对多序列化器无法直接读取through表数据。
  • prefetch_related提前加载所有关联数据,避免N+1查询问题,提升API响应速度。
  • 通过上下文传递当前游戏实例,让子序列化器能准确找到对应平台的游戏详情记录。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 03:25:22