目录
一, DRF 的视图家族
1. DRF 中视图家族成员和配套路由
2. DRF 的视图家族的作用和注意点
(1)视图家族的作用
(2)视图家族的不足之处和注意点
3. 视图家族的使用方法
二, 视图家族的使用实例
1. 自定义视图类 GenericAPIView 和视图工具的组合
2. 直接使用工具视图类
3. 使用视图集
三, 视图家族的使用优化
一, DRF 的视图家族
1. DRF 中视图家族成员和配套路由
视图类: views
# 包括: APIView,GenericAPIView(generics 中)两个视图类, 也是视图基类
APIView 类:
1. 继承 View, 所以拥有 View 的所有
2. 重写 as_view 方法
3. 重写 dispatch 方法
4. 一系列类属性
GenericAPIView 类:
1. 继承 APIView, 所以拥有 APIView 的所有
2. 有 get_queryset 方法, 来配置 queryset 类属性, 提供了要处理的资源对象列表
3. 在第二条基础上, 通过 get_object 方法, 提供了处理的具体资源对象. 配置 lookup_url_kwarg 类属性是因为配置的 url 中传的有名分组的名字可能不是 pk, 这使就可以通过 lookup_url_kwarg 来替代 pk
4. get_serializer 方法, 配置 serializer_class 类属性, 提供视图类相关的序列化对象
*** 配置的 queryset 属性和 serializer_class 属性为视图工具准备数据 ***
(GenericAPIView 类中:
- queryset = None
- serializer_class = None
- lookup_field = 'pk'
- lookup_url_kwarg = None
- )
总结: GenericAPIView 就是在 APIView 基础上额外提供了三个方法(get_queryset,get_object,get_serializer), 三个类属性(queryset,lookup_url_kwarg,serializer_class), 如果不配合视图工具类, 体现不出优势.
GenericAPIView 内部调用视图工具中的 6 种操作资源的对应实现体方法
目的: 视图中的增删改查逻辑相似, 但操作的资源不一致. 操作资源就是操作: 资源对象, 资源对象们 以及 资源相关的序列化类, 将这三者形成类属性的配置, 由程序员自己从外部根据不同资源传入不同类属性, 这样一来, 不同资源的操作逻辑就都一致了, 就可以进行封装
视图工具类: mixins
视图工具类的作用就是完成对资源的操作, 并返回数据的序列化结果给前端
视图工具含有的 6 种类的实现体方法, 调用这 6 个实现体方法的方法就是 GenericAPIView 提供的, 所以要配合 GenericAPIView 类使用
- # 包括:
- CreateModelMixin # 单增工具
含有 create 方法, 实现增逻辑和返回响应结果
RetrieveModelMixin # 单查工具
含有 retrieve 方法, 实现单查逻辑和返回响应结果
ListModelMixin # 群查工具
含有 list 方法, 实现群查逻辑和返回响应结果
UpdateModelMixin # 单改工具(包含整体单改和局部单改)
含有 update 方法和 partial_update 方法, 分别实现整体单增和局部单增逻辑和返回响应结果
- (update 方法内部的含有 partial 参数, 默认是 False.partial_update 方法其实就是将 partial 参数置为 True, 再用 update 方法)
- DestroyModelMixin # 单删工具
含有 destroy 方法, 实现单删逻辑和返回响应结果
工具视图类: generics
工具视图类是对视图工具和视图类两者的继承, 内部包含了对资源的操作和处理后的响应
- # 包括: 以下等共九种工具视图(其实就是对视图工具和视图的组合)
- CreateAPIView # 单增工具视图
- RetrieveAPIView # 单查工具视图
- ListAPIView # 群查工具视图
- RetrieveUpdateDestroyAPIView # 单查单改 (包括整体改和局部改) 单删工具视图
视图集: viewsets
实现: 在一个请求方式只有一条 url 路由对应时, 对应的单操作和群操作可以区分开.
视图集的核心是继承了 ViewSetMixin 类和两个视图基类中的一个视图基类
视图集中的
1. ViewSetMixin 类
ViewSetMixin 类重写了 as_view 方法, 相比 APIView 的 as_view 方法, 额外多出了一个参数 actions
重写的 as_view 方法可以传字典形式的参数. 通过传入的参数将不同的请求方式对应到各自的处理函数
例子: as_view({'get': 'list'}) 传入的 {'get': 'list'} 就被 actions 接收, 原理是将 get 请求映射给视图类的 list 函数进行处理
2. 两个视图集基类: GenericViewSet 和 ViewSet 的区别
GenericViewSet(ViewSetMixin, GenericAPIView), 该分支严格满足资源接口
# ViewSet(ViewSetMixin, APIView), 该分支满足的接口与资源 Model 类关系不是特别密切: 登录接口, 短信验证码接口
配套路由: 与视图家族对应的路由层 SimpleRouter
实现对同资源的不同操作对应 url 的简化, 同资源只用书写一个 url
用法见下面: 二的 3 中的代码中的.
2. DRF 的视图家族的作用和注意点
(1)视图家族的作用
为我们封装了资源操作中的六大接口:
单查, 群查
单增
整体单改
局部单改
单删
(2)视图家族的不足之处和注意点
封装的 6 大接口内部的逻辑都已经完成了. 但另外的 4 大接口没有提供, 需要我们自己书写方法和逻辑
视图家族封装的 6 大接口的响应数据中只包含资源数据, 没有数据状态码和状态信息, 需要我们重写响应方法
在 6 大接口中, 删除接口直接删除的是资源本身, 因此需要我们重写单删和群删接口
在自己配置类属性 queryset 时, 最后要加上 all 方法
例子: models.Car.objects.filter(is_delete=False).all()
3. 视图家族的使用方法
共有 4 种使用方式
1. 直接使用视图类 APIView, 自己书写操作资源的十大接口和响应方法
2. 自定义视图类 GenericAPIView 和视图工具的组合再使用
3. 直接使用工具视图类(其实和第二种方式类似, 只是第二种是我们自己手动组合, 自己联合使用; 第三种是工具视图类已经分别组合好了的 5 种独立接口和 4 个组合接口, 其中 update 独立接口包含整体改和局部改)
4. 使用视图集中的 ModelViewSet 类, 该类中包含 6 大接口(实际开发中最常用的使用方式)
# ******************* 使用 2,3,4 方式时的相同点:(重点)********************
他们都必须要配置 queryset 类属性和 serializer_class 类属性, 有时也要配置 lookup_url_kwarg 类属性.
二, 视图家族的使用实例
1. 自定义视图类 GenericAPIView 和视图工具的组合
- from rest_framework.mixins import RetrieveModelMixin, ListModelMixin, CreateModelMixin
- from rest_framework.generics import GenericAPIView
- class CarReadCreateGenericAPIView(ListModelMixin, RetrieveModelMixin, CreateModelMixin, GenericAPIView):
- queryset = models.Car.objects.filter(is_delete=False).all()
- serializer_class = serializers.CarModelSerializer
- lookup_url_kwarg = 'pk'
- # 群查
- def get(self, request, *args, **kwargs):
- return self.list(request, *args, **kwargs)
- # 单增
- def post(self, request, *args, **kwargs):
- return self.create(request, *args, **kwargs)
2. 直接使用工具视图类
- # 独立单查接口
- from rest_framework.generics import RetrieveAPIView
- class CarRetrieveAPIView(RetrieveAPIView):
- queryset = models.Car.objects.filter(is_delete=False).all()
- serializer_class = serializers.CarModelSerializer
- lookup_url_kwarg = 'pk'
- # 独立群查接口
- from rest_framework.generics import ListAPIView
- class CarListAPIView(ListAPIView):
- queryset = models.Car.objects.filter(is_delete=False).all()
- serializer_class = serializers.CarModelSerializer
- # 组合接口: 单查(get), 单整体改(put), 单局部改(patch), 单删接口(delete)
- from rest_framework.generics import RetrieveUpdateDestroyAPIView
- class CarRetrieveUpdateDestroyAPIView(RetrieveUpdateDestroyAPIView):
- queryset = models.Car.objects.filter(is_delete=False).all()
- serializer_class = serializers.CarModelSerializer
3. 使用视图集
- # 组合接口的使用
- from rest_framework.viewsets import ViewSetMixin, GenericViewSet, ViewSet, ModelViewSet
- # 完成资源操作的 6 大接口
- class CarModelViewSet(ModelViewSet):
- queryset = models.Car.objects.filter(is_delete=False).all()
- serializer_class = serializers.CarModelSerializer
- # 视图集的 urls 文件中:
- # 写法一:
- from django.conf.urls import url, include
- from . import views
- urlpatterns = [
- url(r'^cars/$', views.CarModelViewSet.as_view({
- 'get': 'list',
- 'post': 'create',
- 'put': 'many_update',
- 'patch': 'many_partial_update',
- 'delete': 'many_destroy',
- })),
- url(r'^cars/(?P<pk>\d+)/$', views.CarModelViewSet.as_view({
- 'get': 'retrieve',
- 'put': 'update',
- 'patch': 'partial_update',
- 'delete': 'destroy',
- })),
- ]
- # 写法二:
- # 使用与视图家族对应的路由类 SimpleRouter 来写路由
- # 路由层: 外面会遇到这种写法, 看到了要认识
- from django.conf.urls import url, include
- from . import views
- from rest_framework.routers import SimpleRouter
- router = SimpleRouter()
- router.register('v7/cars', views.CarModelViewSet, basename='car') # cars 后面没有 /
- urlpatterns = [
- url(r'', include(router.urls))
- ]
三, 视图家族的使用优化
因为视图家族有他的不足之处, 如下
# 分析: 从实际开发角度分析不合理点
1)没有群增, 群整体改, 群局部改, 群删四个接口
2)删除操作视图集默认走的 destroy 方法是将资源从数据库中删除, 实际通常删除时通常是对字段 is_delete 做修改, 表示删除
3)响应的结果只有数据, 没有数据状态码和状态信息
解决方法
- # 自定义响应模块(包含数据状态码, 状态信息和数据)
- from rest_framework.response import Response
- class APIResponse(Response):
- def __init__(self, status=0, msg='ok', results=None, http_status=None,
- headers=None, exception=False, content_type=None, **kwargs):
- # 将 status,msg,results,kwargs 格式化成 data
- data = {
- 'status': status,
- 'msg': msg,
- }
- # results 只要不为空都是数据: False,0,'' 都是数据 => 条件不能写 if results
- if results is not None:
- data['results'] = results
- # 将 kwargs 中额外的 k-v 数据添加到 data 中
- data.update(**kwargs)
- super().__init__(data=data, status=http_status, headers=headers, exception=exception, content_type=content_type)
解决 1(自己写 4 个群操作接口)
- # 群整体改, 群局部改, 全删三个接口可以独立成三个方法
- def many_update(self, request, *args, **kwargs):
- return APIResponse(msg='这个地方是群整体改, 你会写!')
- def many_partial_update(self, request, *args, **kwargs):
- return APIResponse(msg='这个地方是群局部改, 你会写!')
- def many_destroy(self, request, *args, **kwargs):
- return APIResponse(msg='这个地方是群删, 你会写!')
- # 群增与单增必须公用一个接口, 都要走 create 方法, 所以重写 create 方法, 用逻辑进行拆分
- def create(self, request, *args, **kwargs):
- request_data = request.data
- if isinstance(request_data, list):
- car_ser = self.get_serializer(data=request_data, many=True)
- car_ser.is_valid(raise_exception=True)
- car_obj = car_ser.save()
- return APIResponse(msg='群增成功', results=self.get_serializer(car_obj, many=True).data)
- return super().create(request, *args, **kwargs)
- # 解决 2(重写单删的 destroy 方法, 自定义实现体)
- def destroy(self, request, *args, **kwargs):
- car_obj = self.get_object()
- car_obj.is_delete = True
- car_obj.save()
- return APIResponse(msg='删除成功')
- # 解决 3(让群查有状态码和状态信息 - 例如重写 list 方法)
- def list(self, request, *args, **kwargs):
- response = super().list(request, *args, **kwargs)
- return APIResponse(results=response.data)
来源: http://www.bubuko.com/infodetail-3358257.html