Django REST Framework嵌套模型如何按菜单分组聚合用户数据
解决方案
你的需求是按菜单维度聚合对应用户,原有实现是按订单维度返回,调整核心是把序列化主体从订单改为菜单,再自定义用户的返回格式即可,修改如下:
第一步:修改序列化器(serializers.py)
新增自定义的菜单序列化器,新增users字段实现你要的字典格式返回:
from rest_framework import serializers from .models import Menu, Order # 保留你原有UserSerializer不变 class UserSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = User fields = ['id', 'username'] class MenuWithUsersSerializer(serializers.HyperlinkedModelSerializer): # 自定义users字段,用方法生成你要的字典结构 users = serializers.SerializerMethodField() class Meta: model = Menu fields = ['id', 'food_name', 'users'] def get_users(self, obj): # obj是当前菜单对象,获取所有关联订单的用户 user_qs = User.objects.filter(id__in=obj.order_set.values_list('user_id', flat=True)) user_list = UserSerializer(user_qs, many=True).data # 转成序号为键的字典,序号从1开始 return {str(index+1): user for index, user in enumerate(user_list)}
如果你的Menu模型本身有原有Serializer,直接在原有基础上新增users字段和对应的get_users方法即可。
第二步:修改视图集(views.py)
把查询主体从订单改为菜单,使用新的序列化器:
from rest_framework import viewsets, response from .models import Menu from .serializers import MenuWithUsersSerializer class MenuOrderViewSet(viewsets.ReadOnlyModelViewSet): # 预加载关联的订单和用户,避免N+1查询,提升性能 queryset = Menu.objects.prefetch_related('order_set__user').all() serializer_class = MenuWithUsersSerializer # 给返回结果加一层menu包裹,匹配你要求的结构 def list(self, request, *args, **kwargs): res = super().list(request, *args, **kwargs) res.data = [{"menu": item} for item in res.data] return res
如果你需要保留原有的订单增删改能力,可以不用修改原有OrderViewSet,新增上面这个视图集单独处理聚合查询的GET请求,单独配置路由即可。
可选方案:如果一定要保留OrderViewSet作为入口
可以重写OrderViewSet的list方法手动聚合数据,实现效果一致但性能略低于上面的方案:
from collections import defaultdict from rest_framework import response class OrderViewSet(viewsets.ModelViewSet): queryset = Order.objects.select_related('user', 'menu').all() serializer_class = OrderSerializer def list(self, request, *args, **kwargs): queryset = self.get_queryset() menu_map = defaultdict(list) # 先按菜单分组所有用户 for order in queryset: menu_map[order.menu].append(order.user) # 构造返回数据 result = [] for menu, users in menu_map.items(): menu_data = MenuSerializer(menu).data menu_data['users'] = {str(index+1): UserSerializer(user).data for index, user in enumerate(users)} result.append({'menu': menu_data}) return response.Response(result)
内容的提问来源于stack exchange,提问作者Mahdi
相关产品推荐
相关产品推荐

