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

Django Rest API问题:返回全部分类及子分类而非仅选中项

Django API分类层级序列化问题解决方案

问题描述

我实现了带父级字段的Category模型来支持子分类,同时创建了Excursions模型(类似文章)及对应的序列化器。在Django后台中,我添加了分类和子分类,并为Excursions选择了多个分类(包含父分类和子分类)。但调用API接口时,返回的JSON数据里,选中的父分类会附带其下所有子分类,而选中的子分类则单独返回,没有嵌套到对应的父分类中。

相关模型代码如下:

# Category 模型
class Category(models.Model):
    name = models.CharField(max_length=100)
    # 支持子分类的父级关联
    parent = models.ForeignKey(
        'self', null=True, blank=True, related_name='subcategories', on_delete=models.CASCADE
    )
    # 分类用途标记
    for_excursion = models.BooleanField(default=False)
    for_event = models.BooleanField(default=False)
    for_destination = models.BooleanField(default=False)

    def __str__(self):
        return self.name

# Excursions 模型
class Excursions(models.Model):
    title = models.CharField(max_length=100)
    image = models.ImageField(upload_to='excursion_images/', null=True, blank=True)
    description = models.TextField()
    data_added = models.DateField(auto_now_add=True, null=True, blank=True)
    modified = models.DateField(auto_now=True, null=True, blank=True)
    # 与分类多对多关联,仅筛选用于Excursion的分类
    categories = models.ManyToManyField(Category, limit_choices_to={'for_excursion': True}, blank=True, related_name='excursions')
    destinations = models.ManyToManyField('Destination', related_name='excursions')

    def __str__(self):
        return self.title

API视图代码:

class ExcursionListView(APIView):
    def get(self, request):
        excursions = Excursions.objects.all()
        serializer = ExcursionsListPublicSerializer(excursions, many=True)
        return Response(serializer.data)

问题原因

当前序列化逻辑存在两个核心问题:

  1. Category序列化器默认返回subcategories字段,会把父分类下的所有子分类都带出,而非仅与当前Excursion关联的子分类。
  2. 多对多字段categories返回的是所有关联分类的平级列表,不会按父子层级嵌套展示。

解决方案

1. 编写递归嵌套的Category序列化器

创建自定义序列化器,仅序列化与当前Excursion关联的子分类:

from rest_framework import serializers
from .models import Category, Excursions

class NestedCategorySerializer(serializers.ModelSerializer):
    subcategories = serializers.SerializerMethodField()

    class Meta:
        model = Category
        fields = ['id', 'name', 'subcategories']

    def get_subcategories(self, obj):
        # 从上下文获取当前Excursion实例
        excursion = self.context.get('excursion')
        if excursion:
            # 过滤出属于当前Excursion且父分类为obj的子分类
            related_subcats = obj.subcategories.filter(excursions=excursion)
            return NestedCategorySerializer(related_subcats, many=True, context=self.context).data
        return []

2. 调整Excursions序列化器

修改序列化器,过滤出顶级分类(无父分类)并嵌套关联的子分类:

class ExcursionsListPublicSerializer(serializers.ModelSerializer):
    categories = serializers.SerializerMethodField()

    class Meta:
        model = Excursions
        fields = ['id', 'title', 'image', 'description', 'data_added', 'modified', 'categories']

    def get_categories(self, obj):
        # 获取当前Excursion关联的顶级分类(无父分类)
        top_categories = obj.categories.filter(parent__isnull=True)
        # 将当前Excursion传入上下文,供子分类序列化器过滤使用
        return NestedCategorySerializer(top_categories, many=True, context={'excursion': obj}).data

3. 可选:自动补全父分类(未选中父分类但选中子分类时)

如果需要实现「即使未选中父分类,只要选中子分类,就自动显示父分类并嵌套子分类」的效果,修改get_categories方法如下:

def get_categories(self, obj):
    all_related_cats = obj.categories.all()
    # 收集所有关联分类的顶级父分类
    top_categories = set()
    for cat in all_related_cats:
        current = cat
        # 递归找到顶级父分类
        while current.parent:
            current = current.parent
        top_categories.add(current)
    # 转换为QuerySet并序列化
    top_categories_queryset = Category.objects.filter(id__in=[cat.id for cat in top_categories])
    return NestedCategorySerializer(top_categories_queryset, many=True, context={'excursion': obj}).data

效果验证

调整后,API返回的categories字段会呈现层级结构:

{
  "id": 1,
  "title": "示例行程",
  "categories": [
    {
      "id": 2,
      "name": "户外探险",
      "subcategories": [
        {
          "id": 5,
          "name": "徒步",
          "subcategories": []
        }
      ]
    }
  ]
}

不会再出现平级的子分类项,也不会带出父分类下未被选中的子分类。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 06:44:55