前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Django rest-framework视图家族

Django rest-framework视图家族

作者头像
GH
发布2020-01-14 11:44:52
7260
发布2020-01-14 11:44:52
举报

视图家族

  • 视图类 GenericAPIView:包含两大视图类(APIView、GenericAPIView)
  • 视图工具类 mixins:包含五大工具类,六大工具方法
  • 工具视图类 generics:包含九大工具视图类,一堆mixins工具类与GenericAPIView视图基类组合
  • 视图集 viewsets:可自定义映射关系
from rest_framework import views,generics,mixins,viewsets

视图类 GenericAPIView

两大视图类:APIView、GenericAPIView

APIView

from rest_framework.views import APIView
  • 继承基本View,拥有View所有的功能
  • 重写了as_view()方法,禁用了csrf认证
  • 重写dispatch,封装请求、响应、渲染、异常、解析、三大认证模块
  • 封装一堆属性,可完成视图类的局部配置

GenericAPIView

from rest_framework.generics import GenericAPIView
  • 继承APIView,拥有APIView所有的功能
  • 提供get_queryset方法:配置queryset类属性,群查获取QuerySet对象
  • 提供get_object方法:配置lookup_url_kwarg类属性,单查获取单个对象
  • 提供get_serializer方法:配置serializer_class类属性,提供序列化类并使用自定义的序列化类序列化

总结:GenericAPIView就是在APIView基础上额外提供了三个方法和三个类属性,如果不配合视图工具类,则体现不出来优势所在

使用它的好处:视图中的增删改查逻辑其实大差不差,但操作的资源不一致(操作的资源指的是models模型类和序列化类),将资源形成配置,操作逻辑一致,就可以完成封装

使用GenericAPIView类

  • 继承GenericAPIView类
  • 配置对哪个表进行操作
  • 配置使用哪个序列化类

群查

from rest_framework.generics import GenericAPIView

class ViewGenericAPIView(GenericAPIView):
    # 配置关联表的属性
    # 如果只写models.Car.objects的话那就是manager对象,不是QuerySet对象
    queryset = models.Car.objects.filter(is_delete=False).all()
    # 配置使用的序列化类
    serializer_class = serializer.CarModelSerializer

    # 群查
    def get(self,request,*args,**kwargs):
        # 帮我们去表里面拿数据
        car_query = self.get_queryset()
        # 帮我们去序列化
        car_ser = self.get_serializer(car_query,many=True)
        return APIResponse(results=car_ser.data)

单查

from rest_framework.generics import GenericAPIView

class ViewGenericAPIView(GenericAPIView):
    # 配置关联表的属性
    # 如果只写models.Car.objects的话那就是manager对象,不是QuerySet对象
    queryset = models.Car.objects.filter(is_delete=False).all()
    # 配置使用的序列化类
    serializer_class = serializer.CarModelSerializer
    # 配置查询的条件为pk,单查走pk过滤的条件
    lookup_url_kwarg = 'pk'

    # 单查
    def get(self,request,*args,**kwargs):
        car_obj = self.get_object()
        car_ser = self.get_serializer(car_obj)
        return APIResponse(results=car_ser.data)

视图工具类 mixins

  • 在GenericAPIView的基础上提供了五个类,六个方法六大接口(单查、群查、单增、单整体改、单局部改、单删)
  • 使用的时候需要配合继承GenericAPIView类

五大工具类

  • RetrieveModelMixin:单查类
  • ListModelMixin:群查类
  • CreateModelMixin:单增类
  • UpdateModelMixin:单整体和单局部改类
  • DestroyModelMixin:单删类

六大工具方法

  • retrieve:单查方法
  • list:群查方法
  • create:单增方法
  • update:单整体改方法
  • partial_update:单局部改方法
  • destroy:单删方法

使用mixins的六大工具方法

  • 继承GenericAPIView类
  • 配置对哪个表进行操作
  • 配置使用哪个序列化类
from rest_framework import mixins

class ViewMixinsAPIView(mixins.RetrieveModelMixin,
                        mixins.ListModelMixin,
                        mixins.CreateModelMixin,
                        mixins.UpdateModelMixin,
                        mixins.DestroyModelMixin,
                        GenericAPIView):
    queryset = models.Car.objects.filter(is_delete=False).all()
    serializer_class = serializer.CarModelSerializer
    lookup_url_kwarg = 'pk'

    # 单查群查
    def get(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        if pk:
            return self.retrieve(request, *args, **kwargs)
        return self.list(request, *args, **kwargs)

    # 单增
    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

    # 单整体改
    def put(self,request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    # 单局部改
    def patch(self,request, *args, **kwargs):
        return self.partial_update(request, *args, **kwargs)

    # 单删
    def delete(self,request, *args, **kwargs):
        # django中的删除是真正的删除
        # 删除接口一般是自己实现重写到的,因为真正的业务不需要真正的删除
        pass
        # django源代码中是真的删除
        return self.destroy(request, *args, **kwargs)

工具视图类 generics

工具类加视图类的组合,只要继承工具该类,就有响应的方法,

  • 帮我们将不同的mixins工具类与GenericAPIView视图类进行组合,我们不再需要继承GenericAPIView类
  • 不同的组合封装成一个个的类,实现对应的请求方法(get、post、put、patch、delete)

随后就是用单查就继承单查的接口,用群查就继承群查的接口即可。

使用generics的工具类实现接口

  • 配置对哪个表进行操作
  • 配置使用哪个序列化类
from rest_framework import generics

class ViewGenericsAPIView(generics.RetrieveAPIView,
                         generics.ListAPIView,
                         generics.CreateAPIView,
                         generics.UpdateAPIView,
                         generics.DestroyAPIView):
    # 单查和群查只能使用一个get,具体调用哪个要看继承的顺序
    queryset = models.Car.objects.filter(is_delete=False).all()
    serializer_class = serializer.CarModelSerializer
    lookup_url_kwarg = 'pk'

    # 有删除需求的接口继承DestroyAPIView,重写destroy完成字段的删除
    def destroy(self, request, *args, **kwargs):
        pass

视图集 viewsets

  • 重写as_view方法,增加action参数(可以完成路由层的请求方法映射关系)
  • 可以在路由层中自定义请求方法的映射关系

使用viewsets的视图集类实现接口

  • 配置对哪个表进行操作
  • 配置使用哪个序列化类

可自定义路由层中请求方法的映射关系来实现接口

路由层

url(r'^v5/cars/$', views.ViewViewsetsAPIView.as_view({
        "get":"list",
        "post":"create"
    })),
    url(r'^v5/cars/(?P<pk>\d+)/$', views.ViewViewsetsAPIView.as_view({
        "get":"retrieve",
        "put":"update",
        "patch":"partial_update",
        "delete":"destroy"
    })),

视图层

from rest_framework import viewsets
# 视图集类
class ViewViewsetsAPIView(viewsets.ModelViewSet):
    queryset = models.Car.objects.filter(is_delete=False).all()
    serializer_class = serializer.CarModelSerializer
    lookup_url_kwarg = 'pk'

完善viewsets的视图集类实现接口

以上的步骤我们继承视图集的ModelViewSet类实现了六大接口,但是从实际开发角度分析有很多不合理的点:

  1. 没有群增,群整体改,群局部改,群删四个接口
  2. 删除只做字段的修改
  3. 响应的结果只有数据,没有数据状态码和状态信息

所以针对以上问题,我们解决一下:

路由层配置

url(r'^v5/cars/$', views.ViewViewsetsAPIView.as_view({
        "get":"list",
        "post":"create",
        "put":"many_update",
        "patch":"many_partial_update",
        "delete":"many_destroy"
    })),
    url(r'^v5/cars/(?P<pk>\d+)/$', views.ViewViewsetsAPIView.as_view({
        "get":"retrieve",
        "put":"update",
        "patch":"partial_update",
        "delete":"destroy"
    })),

实现群增,群整体改,群局部改,群删四个接口

视图层配置

    # 群整体改
    def many_update(self,request,*args,**kwargs):
        try:
            pks = []
            for dic in request.data:
                pks.append(dic.pop('pk'))
            car_query = models.Car.objects.filter(is_delete=False, pk__in=pks).all()
            if len(pks) != len(car_query):
                raise Exception('pk对象不存在')
        except Exception as e:
            return Response({'detail': '%s' % e}, status=400)
        car_ser = self.get_serializer(instance=car_query,data=request.data,many=True)
        car_ser.is_valid(raise_exception=True)
        car_obj = car_ser.save()
        return APIResponse(results=self.get_serializer(car_obj,many=True).data)

    
    
    # 群局部改
    def many_partial_update(self,request,*args,**kwargs):
        try:
            pks = []
            for dic in request.data:
                pks.append(dic.pop('pk'))
            car_query = models.Car.objects.filter(is_delete=False, pk__in=pks).all()
            if len(pks) != len(car_query):
                raise Exception('pk对象不存在')
        except Exception as e:
            return Response({'detail': '%s' % e}, status=400)
        car_ser = self.get_serializer(instance=car_query,data=request.data,many=True,partial=True)
        car_ser.is_valid(raise_exception=True)
        car_obj = car_ser.save()
        return APIResponse(results=self.get_serializer(car_obj,many=True).data)

    
    
    # 群删
    def many_destroy(self,request,*args,**kwargs):
        pks = request.data
        try:
            rows = models.Car.objects.filter(is_delete=False, pk__in=pks).update(is_delete=True)
        except:
            return APIResponse(1, '数据有误')
        if rows:
            return APIResponse(msg='删除成功')
        return APIResponse(1, '删除失败')

    
    
    # 群增和单增必须使用同一个接口,都要走create方法,重写create方法,使用逻辑拆分
    def create(self, request, *args, **kwargs):
        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()
        if not car_obj:
            return APIResponse(1,msg="删除失败")
        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)

    # 重写retrieve方法,单查有状态码和状态信息
    def retrieve(self, request, *args, **kwargs):
        response = super().retrieve(request, *args, **kwargs)
        return APIResponse(results=response.data)

路由组件(了解)

使用SimpleRouter结合视图组件进行路由配置

from django.conf.urls import url,include
from rest_framework.routers import SimpleRouter
router = SimpleRouter()

# 将所有路由与ViewSet视图类的都可以注册,会产生'^v5/cars/$' 和 '^v5/cars/(?P<pk>[^/.]+)/$'的url
router.register('v5/cars',views.ViewViewsetsAPIView,basename='car')

urlpatterns = [
    # 第一种添加子列表方式
    url(r'^', include(router.urls)),
]
# 第二种添加子列表方式
# urlpatterns.extend(router.urls)

路由组件源码部分

如果想实现其他映射关系的话,修改源码就行了

routes = [
        # List route.
        # 群增,如果想要在url中奖请求方式映射关系改变的话,可以重写这个
        Route(
            url=r'^{prefix}{trailing_slash}$',
            mapping={
                'get': 'list',
                'post': 'create'
            },
            name='{basename}-list',
            detail=False,
            initkwargs={'suffix': 'List'}
        ),
        # Dynamically generated list routes. Generated using
        # @action(detail=False) decorator on methods of the viewset.
        DynamicRoute(
            url=r'^{prefix}/{url_path}{trailing_slash}$',
            name='{basename}-{url_name}',
            detail=False,
            initkwargs={}
        ),
        # Detail route.
        Route(
            url=r'^{prefix}/{lookup}{trailing_slash}$',
            mapping={
                'get': 'retrieve',
                'put': 'update',
                'patch': 'partial_update',
                'delete': 'destroy'
            },
            name='{basename}-detail',
            detail=True,
            initkwargs={'suffix': 'Instance'}
        ),
        # Dynamically generated detail routes. Generated using
        # @action(detail=True) decorator on methods of the viewset.
        DynamicRoute(
            url=r'^{prefix}/{lookup}/{url_path}{trailing_slash}$',
            name='{basename}-{url_name}',
            detail=True,
            initkwargs={}
        ),
    ]

或者自定义路由对象

from rest_framework.routers import Route, DynamicRoute, SimpleRouter as DRFSimpleRouter

class SimpleRouter(DRFSimpleRouter):
    routes = [
        # List route.  /资源s/
        Route(
            url=r'^{prefix}{trailing_slash}$',
            mapping={
                'get': 'list',  # 群查
                'post': 'create',  # 单增、群增
                'put': 'many_update',  # 群整改
                'patch': 'many_partial_update',  # 群局改
                'delete': 'many_destroy',  # 群删
            },
            name='{basename}-list',
            detail=False,
            initkwargs={'suffix': 'List'}
        ),
        # Dynamically generated list routes. Generated using
        # @action(detail=False) decorator on methods of the viewset.
        DynamicRoute(
            url=r'^{prefix}/{url_path}{trailing_slash}$',
            name='{basename}-{url_name}',
            detail=False,
            initkwargs={}
        ),
        # Detail route.  /资源s/(pk)/
        Route(
            url=r'^{prefix}/{lookup}{trailing_slash}$',
            mapping={
                'get': 'retrieve',  # 单查
                'put': 'update',  # 单整改
                'patch': 'partial_update',  # 单局改
                'delete': 'destroy'  # 单删
            },
            name='{basename}-detail',
            detail=True,
            initkwargs={'suffix': 'Instance'}
        ),
        # Dynamically generated detail routes. Generated using
        # @action(detail=True) decorator on methods of the viewset.
        DynamicRoute(
            url=r'^{prefix}/{lookup}/{url_path}{trailing_slash}$',
            name='{basename}-{url_name}',
            detail=True,
            initkwargs={}
        ),
    ]

# 对外提供router对象
router = SimpleRouter()
# eg: router.register('users', UserModelViewSet, basename='user')
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-01-05 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 视图家族
  • 视图类 GenericAPIView
    • APIView
      • GenericAPIView
        • 使用GenericAPIView类
        • 视图工具类 mixins
          • 五大工具类
            • 六大工具方法
              • 使用mixins的六大工具方法
              • 工具视图类 generics
                • 使用generics的工具类实现接口
                • 视图集 viewsets
                  • 使用viewsets的视图集类实现接口
                    • 完善viewsets的视图集类实现接口
                      • 实现群增,群整体改,群局部改,群删四个接口
                      • 实现删除只做字段的修改
                      • 实现返回信息包含数据状态码和状态信息
                  • 路由组件(了解)
                  相关产品与服务
                  文件存储
                  文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档