如何在Django REST Framework的Post序列化器中展示分类与子分类
解决方案:嵌套分类-子分类-文章的序列化与分页实现
需求背景
需要在Post视图中实现分页(避免在分类视图分页导致文章数量不一致),同时返回嵌套结构的JSON,包含分类、子分类及对应文章,期望结构如下:
[ { "id": 1, "title": "Taomlar", "subcat": [ { "id": 2, "title": "Milliy", "post": [ { "id": 1, "title": "Palov", "summa": 300000 }, { "id": 2, "title": "Palov", "summa": 300000 } ] } ] } ]
现有代码
models.py
class Category(Base): title = models.CharField(max_length=200) parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children') def __str__(self): return self.title class Post(Base): title = models.CharField(max_length=225) category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='post')
serializers.py
class CategorySerializer(serializers.ModelSerializer): class Meta: model = Category fields = '__all__' class PostSerializer(serializers.ModelSerializer): class Meta: model = Post fields = '__all__'
解决方案
1. 编写嵌套序列化器
从最内层的Post开始,逐层构建嵌套结构,修改serializers.py如下:
from rest_framework import serializers from .models import Category, Post class PostSerializer(serializers.ModelSerializer): class Meta: model = Post fields = ['id', 'title', 'summa'] # 按需添加其他需要返回的字段 class SubCategorySerializer(serializers.ModelSerializer): post = PostSerializer(many=True, read_only=True) # 关联子分类下的文章列表 class Meta: model = Category fields = ['id', 'title', 'post'] class RootCategorySerializer(serializers.ModelSerializer): # 将模型中的children关联名映射为subcat字段,返回子分类列表 subcat = SubCategorySerializer(source='children', many=True, read_only=True) class Meta: model = Category fields = ['id', 'title', 'subcat']
2. 实现视图分页
根据需求选择合适的分页逻辑:
方式一:对根分类分页(适合分类数量较多的场景)
from rest_framework.pagination import PageNumberPagination from rest_framework.views import APIView from rest_framework.response import Response from .models import Category from .serializers import RootCategorySerializer class CategoryPostPagination(PageNumberPagination): page_size = 10 # 每页显示的根分类数量 page_size_query_param = 'page_size' max_page_size = 100 class RootCategoryPostView(APIView): def get(self, request): # 查询所有根分类,预加载子分类和文章避免N+1查询 root_categories = Category.objects.filter(parent=None).prefetch_related( 'children__post' ) paginator = CategoryPostPagination() paginated_categories = paginator.paginate_queryset(root_categories, request) serializer = RootCategorySerializer(paginated_categories, many=True) return paginator.get_paginated_response(serializer.data)
方式二:对文章分页(适合文章数量多,需要按文章数量分页的场景)
如果需要按文章数量分页,同时保持分类嵌套结构,可自定义逻辑:
from rest_framework.pagination import PageNumberPagination from rest_framework.views import APIView from rest_framework.response import Response from .models import Category, Post from .serializers import RootCategorySerializer class PostPagination(PageNumberPagination): page_size = 2 # 每页显示的文章数量 page_size_query_param = 'page_size' max_page_size = 100 class CategoryPostView(APIView): def get(self, request): paginator = PostPagination() # 获取分页后的文章集合 paginated_posts = paginator.paginate_queryset(Post.objects.all(), request) # 提取这些文章所属的子分类ID和根分类ID sub_cat_ids = [post.category.id for post in paginated_posts] root_cat_ids = [cat.parent.id for cat in Category.objects.filter(id__in=sub_cat_ids) if cat.parent] # 查询根分类并预加载关联数据 root_categories = Category.objects.filter(id__in=root_cat_ids).prefetch_related( 'children__post' ) serializer = RootCategorySerializer(root_categories, many=True) return paginator.get_paginated_response(serializer.data)
3. 关键注意点
- 使用
prefetch_related预加载关联数据,避免数据库N+1查询,提升接口性能 - 通过
source参数实现模型关联名到序列化器字段名的映射 - 根据业务场景选择分页方式:根分类分页或文章分页
内容的提问来源于stack exchange,提问作者Xursandbek Ibadullayev
相关产品推荐
相关产品推荐

