django rest framework ModelViewSet动态传入queryset和serializer_class,树形结构数据重复问题解决

本文介绍如何在Django REST framework中根据前端参数动态调整queryset和serializer_class,包括重写get_queryset()、get_serializer_class()及处理树形结构数据重复问题的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

需求背景:
当继承ModelViewSet,视图层的类定义好之后,router.register注册类即可实现模型的增删改查,
但有些业务逻辑需要根据前端传入参数动态改变 querysetserializer_class, 其原因是model层将部分空值字段拆分为几个相似的表结构,而为了前端方便将相似的API接口整合为一个接口,通过传入不同类型请求。

实现方式
重写 get_queryset()方法和 get_serializer_class()方法
在这里插入图片描述
如创建,更新和删除方法也需要动态调整:
则重写 perform_create()、perform_update()和 perform_destroy()方法即可
或直接完全重写create,update,destroy 方法

还有一些情况是API接口返回树形结构遇到的 bug

Model:在这里插入图片描述
serializers:
在这里插入图片描述
views:
在这里插入图片描述

这样json返回将会出现二级,三级等数据重复出现在与一级平级的结构中,可能这并不是我们想要的结果
解决方式一
serializers中: get_child()方法过滤parent=None的数据

def get_child(self,obj):
        return DepartmentSerializer(obj.children.filter(parent=None),many=True).data

或者是在DepartmentView中的queryset过滤

queryset =  Department.objects.filter(parent=None)

这样虽然解决了list返回中没有重复,但是获取详情时会拿不到parent=None的数据
例 id=5的数据 parent为4
GET /api/department/5
在这里插入图片描述
这样对为一级目录的数据无法进行更新和删除

解决方法二:
在DepartmentView中重写 get_object(self)方法
DepartmentView代码如下:
在这里插入图片描述

可能存在问题:在实际开发过程中,有可能也存在自定义了get_objectget_queryset方法,
这样会导致如果get_queryset方法里的queryset返回的是过滤了parent=None的数据集,则获取非一级目录数据同样返回:
在这里插入图片描述

我们查看get_object源代码得知:
generics.py中的GenericAPIView类
在这里插入图片描述
看源代码知道get_object方法中的数据集来源是**get_queryset()**方法

如上所述,为了满足需求,我们需要继承GenericAPIView类重写get_object方法

最终解决方法:
如下图:
红框为改动地方,其它地方不变
在这里插入图片描述


最终 DepartmentView 代码如下

class DepartmentView(_GenericAPIView,viewsets.ModelViewSet):
    serializer_class = DepartmentSerializer

    def get_queryset(self):
        return Department.objects.filter(parent=None)

    def get_object(self):
        queryset = Department.objects.all()
        return super().get_object(queryset)

注意: 在DepartmentView中继承的类 _GenericAPIView写在viewsets.ModelViewSet前面
这样list列表返回是只返回一级目录,因为二三级目录一级包含嵌套已经在child字段中
获取列表中某个数据详情时,get_object的数据集是所有数据,所以可以返回parent=None的数据。

### Django REST framework视图文档示例 #### 使用函数式视图(FBV) 在Django REST framework中,可以使用基于函数的视图(Function Based Views, FBV),这些视图通过装饰器`@api_view`来定义HTTP方法对应的处理逻辑[^3]。 ```python from rest_framework.decorators import api_view from rest_framework.response import Response from rest_framework import status @api_view(['GET', 'POST']) def course_list(request): """ 列出所有的课程或者创建一个新的课程. """ if request.method == 'GET': courses = Course.objects.all() serializer = CourseSerializer(courses, many=True) return Response(serializer.data) elif request.method == 'POST': serializer = CourseSerializer(data=request.data) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) ``` 此代码片段展示了如何实现一个简单的API端点用于获取课程列表以及添加新课程的功能。它接受两种类型的请求:`GET` `POST`。对于`GET` 请求返回所有课程的数据;而当接收到`POST` 请求时,则尝试根据传入数据创建新的记录并保存到数据库中。 #### 类视图(CBV)介绍 除了FBVs之外,DRF还支持类基视图(Class-Based Views, CBVs), 这种方式提供了更结构化的编程模式,并且更容易重用代码。通用视图(Generic View Classes)进一步简化了常见的CRUD操作流程: - **ListCreateAPIView**: 同时提供对象集合查询(`list`)及新增(`create`)功能; - **RetrieveUpdateDestroyAPIView**: 提供单个实例详情(`retrieve`)、更新(`update`)删除(`destroy`)等功能。 下面是一个利用CBV的例子: ```python from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView from .models import Course from .serializers import CourseSerializer class CoursesListView(ListCreateAPIView): queryset = Course.objects.all() serializer_class = CourseSerializer class CourseDetailView(RetrieveUpdateDestroyAPIView): queryset = Course.objects.all() serializer_class = CourseSerializer ``` 这里分别定义了一个用来展示全部课程信息并且允许提交新数据的接口 (`CoursesListView`) ,还有一个针对特定ID的操作界面 (`CourseDetailView`) 。这两个类都继承自预置好的泛型视图,从而减少了大量重复性的模板代码编写工作量[^1].
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值