drf之视图集ViewSet
## 1.3 视图集ViewSet
使用视图集ViewSet,可以将一系列视图相关的代码逻辑和相关的http请求动作封装到一个类中: - list() 提供一组数据 - retrieve() 提供单个数据 - create() 创建数据 - update() 保存数据 - destory() 删除数据 ViewSet视图集类不再限制视图方法名只允许get()、post()等这种情况了,而是实现允许开发者根据自己的需要定义自定义方法名,例如 list() 、create() 等,然后经过路由中使用http和这些视图方法名进行绑定调用。
视图集只在使用as_view()方法的时候,才会将**action**动作与具体请求方式对应上。如:
针对视图子类这种写法写法虽然已经省略了http请求,但是在开发通用5个api接口时,还是会出现需要2个类来实现5个接口的情况。 这主要的原因是2点: 1. 获取多条数据与获取一条数据的http请求重复了。在django中依赖于请求方法来响应不同的http请求 2. 部分接口需要pk值作为url地址。 drf为了解决上面的2个问题,提供了视图集和路由集。 视图集就可以帮我们实现一个视图类响应多种重复的http请求 路由集就可以帮我们实现自动根据不同的视图方法来生成不同参数的路由地址。 from rest_framework.viewsets import ViewSet # ViewSet是APIView的子类,是所有drf中的视图集的父类 """ from rest_framework.viewsets import ViewSet class StudentViewSet(ViewSet): def get_student_list(self, request): """获取所有学生信息""" # 1. 从数据库中读取学生列表信息 student_list = Student.objects.all() # 2. 实例化序列化器,获取序列化对象 serializer = StudentModelSerializer(instance=student_list, many=True) # 3. 转换数据并返回给客户端 return Response(serializer.data) def post(self, request): """添加一条数据""" # 1. 获取客户端提交的数据,实例化序列化器,获取序列化对象 serializer = StudentModelSerializer(data=request.data) # 2. 反序列化[验证数据、保存数据到数据库] serializer.is_valid(raise_exception=True) serializer.save() # 3. 返回新增的模型数据给客户单 return Response(serializer.data, status=status.HTTP_201_CREATED) def get_student_info(self,request, pk): """获取一条数据""" # 1. 使用pk作为条件获取模型对象 try: student = Student.objects.get(pk=pk) except Student.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) # 2. 序列化 serializer = StudentModelSerializer(instance=student) # 3. 返回结果 return Response(serializer.data) def update(self, request, pk): """更新数据""" # 1. 使用pk作为条件获取模型对象 try: student = Student.objects.get(pk=pk) except Student.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) # 2. 获取客户端提交的数据 serializer = StudentModelSerializer(instance=student, data=request.data) # 3. 反序列化[验证数据和数据保存] serializer.is_valid(raise_exception=True) serializer.save() # 4. 返回结果 return Response(serializer.data, status=status.HTTP_201_CREATED) def delete(self,request,pk): """删除数据""" # 1. 根据PK值获取要删除的数据并删除 try: Student.objects.get(pk=pk).delete() except Student.DoesNotExist: pass # 2. 返回结果 return Response(status=status.HTTP_204_NO_CONTENT)
在设置路由时,我们可以如下操作
from django.urls import path, re_path from . import views urlpatterns = [ # APIView path("students/", views.StudentAPIView.as_view()), re_path("^students/(?P<pk>\d+)/$", views.StudentInfoAPIView.as_view()), # GenericAPIView path("students2/", views.StudentGenericAPIView.as_view()), re_path("^students2/(?P<pk>\d+)/$", views.StudentInfoGenericAPIView.as_view()), # GenericAPIView + mixins path("students3/", views.StudentMixinView.as_view()), re_path("^students3/(?P<pk>\d+)/$", views.StudentInfoMixinView.as_view()), # 视图子类 path("students4/", views.StudentView.as_view()), re_path("^students4/(?P<pk>\d+)/$", views.StudentInfoView.as_view()), # 视图集: ViewSet path("students5/", views.StudentViewSet.as_view({ "get": "get_student_list", # 视图类方法,可以是原来的http请求动作,也可以是自定义的方法名 "post": "post", })), re_path("^students5/(?P<pk>\d+)/$", views.StudentViewSet.as_view({ "get": "get_student_info", # 视图类方法,可以是原来的http请求动作,也可以是自定义的方法名 "put": "update", "delete": "delete", })), ]
### 1.3.1 常用视图集父类
#### 1) ViewSet
继承自`APIView`与`ViewSetMixin`,作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。 **ViewSet主要通过继承ViewSetMixin来实现在调用as_view()时传入字典{“http请求”:“视图方法”}的映射处理工作,如{'get':'list'},** 在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。
#### 2)GenericViewSet
继承自GenericAPIView和ViewSetMixin,作用让视图集的视图代码变得更加通用,抽离独特代码作为视图类的属性。 使用ViewSet通常并不方便,因为list、retrieve、create、update、destory等方法都需要自己编写,而这些方法与前面讲过的Mixin扩展类提供的方法同名,所以我们可以通过继承Mixin扩展类来复用这些方法而无需自己编写。
但是Mixin扩展类依赖与`GenericAPIView`,所以还需要继承`GenericAPIView`。 **GenericViewSet**就帮助我们完成了这样的继承工作,继承自`GenericAPIView`与`ViewSetMixin`,在实现了调用as_view()时传入字典(如`{'get':'list'}`)的映射处理工作的同时,还提供了`GenericAPIView`提供的基础方法,可以直接搭配Mixin扩展类使用。
视图代码:
"""GenericViewSet 通用视图集""" from rest_framework.viewsets import GenericViewSet class StudentGenericViewSet(GenericViewSet): queryset = Student.objects.all() serializer_class = StudentModelSerializer def list(self, request): """获取所有数据""" # 1. 从数据库中读取模型列表信息 queryset = self.get_queryset() # GenericAPIView提供的get_queryset # 2. 序列化 serializer = self.get_serializer(instance=queryset, many=True) # 3. 转换数据并返回给客户端 return Response(serializer.data) def create(self,request): """添加一个数据""" # 1. 获取客户端提交的数据,实例化序列化器,获取序列化对象 serializer = self.get_serializer(data=request.data) # 2. 反序列化[验证数据、保存数据到数据库] serializer.is_valid(raise_exception=True) serializer.save() # 3. 返回新增的模型数据给客户单 return Response(serializer.data, status=status.HTTP_201_CREATED) def retrieve(self, request, pk): """获取一个数据""" # 1. 使用pk作为条件获取模型对象 instance = self.get_object() # 2.序列化 serializer = self.get_serializer(instance=instance) # 3. 返回结果 return Response(serializer.data) def update(self,request, pk): """更新一个数据""" # 1. 使用pk作为条件获取模型对象 instance = self.get_object() # 2. 获取客户端提交的数据 serializer = self.get_serializer(instance=instance, data=request.data) # 3. 反序列化[验证数据和数据保存] serializer.is_valid(raise_exception=True) serializer.save() # 4. 返回结果 return Response(serializer.data, status=status.HTTP_201_CREATED) def destory(self, request, pk): """删除一个数据""" # 1. 根据PK值获取要删除的数据并删除 self.get_object().delete() # 2. 返回结果 return Response(status=status.HTTP_204_NO_CONTENT)
路由代码:
from django.urls import path, re_path from . import views urlpatterns = [ # APIView path("students/", views.StudentAPIView.as_view()), re_path("^students/(?P<pk>\d+)/$", views.StudentInfoAPIView.as_view()), # GenericAPIView path("students2/", views.StudentGenericAPIView.as_view()), re_path("^students2/(?P<pk>\d+)/$", views.StudentInfoGenericAPIView.as_view()), # GenericAPIView + mixins path("students3/", views.StudentMixinView.as_view()), re_path("^students3/(?P<pk>\d+)/$", views.StudentInfoMixinView.as_view()), # 视图子类 path("students4/", views.StudentView.as_view()), re_path("^students4/(?P<pk>\d+)/$", views.StudentInfoView.as_view()), # 视图集: ViewSet path("students5/", views.StudentViewSet.as_view({ "get": "get_student_list", # 视图类方法,可以是原来的http请求动作,也可以是自定义的方法名 "post": "post", })), re_path("^students5/(?P<pk>\d+)/$", views.StudentViewSet.as_view({ "get": "get_student_info", # 视图类方法,可以是原来的http请求动作,也可以是自定义的方法名 "put": "update", "delete": "delete", })), # 视图集: GenericViewSet path("students6/", views.StudentGenericViewSet.as_view({ "get": "list", "post": "create", })), re_path("^students6/(?P<pk>\d+)/$", views.StudentGenericViewSet.as_view({ "get": "retrieve", "put": "update", "delete": "destroy", })), ]
集合我们上面学习的模型扩展类,实现简写操作,视图,代码:
"""
GenericViewSet结合Mixins的混入类,直接视图接口,这次连视图子类都不需要了。 ViewSet GenericViewSet ModelViewSet = GenericViewSet + ListModelMixin + CreateModelMixin + UpdateModelMixin + RetrieveModelMixin + DestroyModelMixin ReadOnlyModelViewSet = GenericViewSet + ListModelMixin + RetrieveModelMixin """ class StudentMixinViewSet(GenericViewSet, ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin): queryset = Student.objects.all() serializer_class = StudentModelSerializer
路由,代码:
from django.urls import path, re_path from . import views urlpatterns = [ # APIView path("students/", views.StudentAPIView.as_view()), re_path("^students/(?P<pk>\d+)/$", views.StudentInfoAPIView.as_view()), # GenericAPIView path("students2/", views.StudentGenericAPIView.as_view()), re_path("^students2/(?P<pk>\d+)/$", views.StudentInfoGenericAPIView.as_view()), # GenericAPIView + mixins path("students3/", views.StudentMixinView.as_view()), re_path("^students3/(?P<pk>\d+)/$", views.StudentInfoMixinView.as_view()), # 视图子类 path("students4/", views.StudentView.as_view()), re_path("^students4/(?P<pk>\d+)/$", views.StudentInfoView.as_view()), # 视图集: ViewSet path("students5/", views.StudentViewSet.as_view({ "get": "get_student_list", # 视图类方法,可以是原来的http请求动作,也可以是自定义的方法名 "post": "post", })), re_path("^students5/(?P<pk>\d+)/$", views.StudentViewSet.as_view({ "get": "get_student_info", # 视图类方法,可以是原来的http请求动作,也可以是自定义的方法名 "put": "update", "delete": "delete", })), # 视图集: GenericViewSet path("students6/", views.StudentGenericViewSet.as_view({ "get": "list", "post": "create", })), re_path("^students6/(?P<pk>\d+)/$", views.StudentGenericViewSet.as_view({ "get": "retrieve", "put": "update", "delete": "destroy", })), # 视图集: GenericViewSet + Mixins path("students7/", views.StudentMixinViewSet.as_view({ "get": "list", "post": "create", })), re_path("^students7/(?P<pk>\d+)/$", views.StudentMixinViewSet.as_view({ "get": "retrieve", "put": "update", "delete": "destroy", })), ]
#### 3)ModelViewSet
继承自`GenericViewSet`,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。
#### 4)ReadOnlyModelViewSet
继承自`GenericViewSet`,同时包括了ListModelMixin、RetrieveModelMixin。
视图代码:
""" GenericViewSet结合Mixins的混入类,直接视图接口,这次连视图子类都不需要了。 ViewSet GenericViewSet ModelViewSet = GenericViewSet + ListModelMixin + CreateModelMixin + UpdateModelMixin + RetrieveModelMixin + DestroyModelMixin ReadOnlyModelViewSet = GenericViewSet + ListModelMixin + RetrieveModelMixin """ from rest_framework.viewsets import ReadOnlyModelViewSet class StudentReadOnlyMixinViewSet(ReadOnlyModelViewSet, CreateModelMixin, UpdateModelMixin, DestroyModelMixin): queryset = Student.objects.all() serializer_class = StudentModelSerializer from rest_framework.viewsets import ModelViewSet class StudentModelViewSet(ModelViewSet): # 万能视图集 queryset = Student.objects.all() serializer_class = StudentModelSerializer
路由代码:
from django.urls import path, re_path from . import views urlpatterns = [ # APIView path("students/", views.StudentAPIView.as_view()), re_path("^students/(?P<pk>\d+)/$", views.StudentInfoAPIView.as_view()), # GenericAPIView path("students2/", views.StudentGenericAPIView.as_view()), re_path("^students2/(?P<pk>\d+)/$", views.StudentInfoGenericAPIView.as_view()), # GenericAPIView + mixins path("students3/", views.StudentMixinView.as_view()), re_path("^students3/(?P<pk>\d+)/$", views.StudentInfoMixinView.as_view()), # 视图子类 path("students4/", views.StudentView.as_view()), re_path("^students4/(?P<pk>\d+)/$", views.StudentInfoView.as_view()), # 视图集: ViewSet path("students5/", views.StudentViewSet.as_view({ "get": "get_student_list", # 视图类方法,可以是原来的http请求动作,也可以是自定义的方法名 "post": "post", })), re_path("^students5/(?P<pk>\d+)/$", views.StudentViewSet.as_view({ "get": "get_student_info", # 视图类方法,可以是原来的http请求动作,也可以是自定义的方法名 "put": "update", "delete": "delete", })), # 视图集: GenericViewSet path("students6/", views.StudentGenericViewSet.as_view({ "get": "list", "post": "create", })), re_path("^students6/(?P<pk>\d+)/$", views.StudentGenericViewSet.as_view({ "get": "retrieve", "put": "update", "delete": "destroy", })), # 视图集: GenericViewSet + Mixins path("students7/", views.StudentMixinViewSet.as_view({ "get": "list", "post": "create", })), re_path("^students7/(?P<pk>\d+)/$", views.StudentMixinViewSet.as_view({ "get": "retrieve", "put": "update", "delete": "destroy", })), # 视图集: ReadOnlyModelViewSet + ModelViewSet path("students8/", views.StudentReadOnlyMixinViewSet.as_view({ "get": "list", "post": "create", })), re_path("^students8/(?P<pk>\d+)/$", views.StudentReadOnlyMixinViewSet.as_view({ "get": "retrieve", "put": "update", "delete": "destroy", })), ]
幻想毫无价值,计划渺如尘埃,目标不可能达到。这一切的一切毫无意义——除非我们付诸行动。