zoukankan      html  css  js  c++  java
  • Django Rest Framework(3)-----APIView与Viewsets

    REST framework提供了一个APIView类,它是Django的View类的子类。

    REST framework主要的几种view以及他们之间的关系:

    mixins

         到目前为止,我们使用的创建/获取/更新/删除操作和我们创建的任何基于模型的API视图非常相似。这些常见的行为是在REST框架的mixin类中实现的

    Mixin 类提供用于提供基本视图行为的操作。注意mixin类提供动作方法,而不是直接定义处理程序方法,例如 .get() 和 .post(), 这允许更灵活的行为组成。

    Mixin 类可以从 rest_framework.mixins导入。

    mixins 作用 对应HTTP的请求方法
    mixins.ListModelMixin

    定义list方法,返回一个queryset的列表

    GET
    mixins.CreateModelMixin 定义create方法,创建一个实例 POST
    mixins.RetrieveModelMixin 定义retrieve方法,返回一个具体的实例 GET
    mixins.UpdateModelMixin 定义update方法,对某个实例进行更新 PUT/PATCH
    mixins.DestroyModelMixin 定义delete方法,删除某个实例 DELETE

    使用详解

    1.APIView

    APIView对django本身的View进行封装

    APIView类和一般的View类有以下不同:

    • 被传入到处理方法的请求不会是Django的HttpRequest类的实例,而是REST framework的Request类的实例。

    • 处理方法可以返回REST framework的Response,而不是Django的HttpRequest。视图会管理内容协议,给响应设置正确的渲染器。

    • 任何APIException异常都会被捕获,并且传递给合适的响应。

    • 进入的请求将会经过认证,合适的权限和(或)节流检查会在请求被派发到处理方法之前   

                    authentication_classes:用户登录认证方式,session或者token等等。

                    permission_classes:权限设置,是否需要登录等。

                    throttle_classes:限速设置,对用户进行一定的访问次数限制等等

    使用APIView类和使用一般的View类非常相似,通常,进入的请求会被分发到合适处理方法比如.get(),或者.post。另外,很多属性会被设定在控制API策略的各种切面的类上。

    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework import authentication, permissions
    
    class ListUsers(APIView):
        """
        列出系统中的所有用户的视图。
    
        * 需要token认证
        * 只有管理员用户可以访问这个视图。
        """
        authentication_classes = (authentication.TokenAuthentication,)
        permission_classes = (permissions.IsAdminUser,)
    
        def get(self, request, format=None):
            """
            Return a list of all users.
            """
            usernames = [user.username for user in User.objects.all()]
            return Response(usernames)

    2.GenericAPIView

      该类对APIView进行更高层次的封装,扩展了REST框架的 APIView 类,为标准list和detail view 添加了通常需要的行为。

      提供的每个具体通用视图是通过将 GenericAPIView 与一个或多个mixin类组合来构建的。

    from rest_framework import mixins
    from rest_framework import generics
    class CourseListView(mixins.ListModelMixin, generics.GenericAPIView):
        """
        课程列表页
        """
        queryset = Course.objects.all()
        serialize_class = CourseSerializer
        def get(self, request, *args, **kwargs):
        # list方法是存在于mixins中的,同理,create等等也是
        # GenericAPIView没有这些方法!
            return self.list(request, *args, **kwargs)

    上述中,继承了mixins中的ListModelMixin,就是对应把HTTP的get方法转换调用list方法,list方法会返回queryset的json数据

    GenericAPIView对APIView再次封装,实现了强大功能

    • 加入queryset属性,可以直接设置这个属性,不必再将实例化的data,再次传给seriliazer,系统会自动检测到。除此之外,可以重载get_queryset(),这样就不必设置’queryset=*’,这样就变得更加灵活,可以进行完全的自定义。
    • 加入serializer_class属性与实现get_serializer_class()方法。两者的存在一个即可,通过这个,在返回时,不必去指定某个serializer。
    • 设置过滤器模板:filter_backends。
    • 设置分页模板:pagination_class。
    • 加入 lookup_field=”pk”,以及实现了get_object方法,这个用得场景不多,但十分重要。它们两者的关系同1,要么设置属性,要么重载方法。它们的功能在于获取某一个实例时,指定传进来的后缀是什么。

          例如·,获取具体的某个课程,假设传进来的URL为:http://127.0.0.1:8000/course/1/,系统会默认这个1指的是course的id。那么,现在面临一个问题,假设我定义了一个用户收藏的Model,我想要知道我id为1的主机是否收藏了,我传进来的URL为:http://127.0.0.1:8000/userfav/1/,系统会默认获取userfav的id=1的实例,这个逻辑明显是错的,我们需要获取course的id=1的收藏记录,所以我们就需要用到这个属性或者重载lookup_field=”course_id”这个方法。

    GenericAPIView的不足之处:

      既然GenericAPIView以及它相关的View已经完成了许许多多的功能,那么还要ViewSet干嘛!
    首先,我们思考一个问题,同样上面的例子,我们在功能上,要获取课程的列表,也要获取某个课程的具体信息。那么怎么实现,按照GenericAPIView,我们可以这样实现:

    class CourseView(ListAPIView,RetrieveAPIView):
        # 只需要在上面的基础上,再继承RetrieveAPIView就ok了。
        queryset = Course.objects.all()
        serialize_class = CourseSerializer
      但这样实现有一个问题,关于serialize_class,显然,当获取课程列表时,只需要传回去所有课程的简要信息,如课程名字,老师,封面等等,但当获取课程的具体信息,我们还要将他们的章节以及相关下载资料(很明显,章节是另外一个model,有一个外键指向course),这些信息会很多,在获取课程列表,将这些传回去显然是不理智的。那么,还需要再定义一个CourseDetailSerializer,在get /courses/的时候,使用CourseSerializer,在get /courses/id/的时候,使用CourseDetailSerializer。
      那么,问题来了,我们怎么获取到是哪个action方法?这个时候,viewset就出场了!

    3.Viewset

    GenericViewSet继承了GenericAPIView,依然有get_queryset,get_serialize_class相关属性与方法,GenericViewSet重写了as_view方法,可以获取到HTTP的请求方法。 

    使用ViewSet类比使用View类有两个主要优点。

    • 重复逻辑可以组合成一个类。在上面的示例中,我们只需要指定queryset一次,它将在多个视图中使用。
    • 通过使用路由器,我们不再需要处理自己的URL连接。

    如:上述问题中我们可以这样解决

    法一:

    from rest_framework import viewsets
    import...
    class CourseViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
        queryset = Course.objects.all()
        
        def get_serializer_class(self):
        # 重写get_serializer_class方法
            if self.action == 'list':
                return CourseSerializer
            return CourseDetailSerializer

    http请求方法与mixins的方法进行绑定

      但GenericViewSet本身依然不存在list, create方法,需要我们与mixins一起混合使用,那么新问题来了?我们依然需要自己写get、post方法,然后再return list或者create等方法吗?当然不!重写as_view的方法为我们提供了绑定的功能,我们在设置url的时候:
    # 进行绑定
    courses = CourseViewSet.as_view({
        'get': 'list',
        'post': 'create'
    })
    urlpatterns = [
        ...
        # 常规加入url匹配项
        url(r'courses/', CourseViewSet.as_view(), name='courses')]

    这样,我们就将http请求方法与mixins方法进行了关联。那么还有更简洁的方法吗?很明显,当然有,这个时候,route就登场了!

    法二:route方法注册与绑定

      因为我们使用ViewSet类而不是View类,实际上不用自己设计URL conf及绑定HTTP方法。连接resources到views和urls的约定可以使用Router类自动处理。我们需要做的仅仅是正确的注册View到Router中,然后让它执行其余操作。新的urls.py代码如下:
    from rest_framework.routers import DefaultRouter
    router = DefaultRouter() # 只需要实现一次
    router.register(r'courses', CourseViewSet, base_name='courses')
    urlpatterns = [
        ...
        # 只需要加入一次
        url(r'^', include(router.urls)),]

      route中使用的一定要是ViewSet,用router.register的方法注册url不仅可以很好的管理url,不会导致url过多而混乱,而且还能实现http方法与mixins中的相关方法进行连接。

    ModelViewSet:

      在viewset中,还提供了两个以及与mixins绑定好的ViewSet。当然,这两个ViewSet完全可以自己实现,它只是把各类mixins与GenericViewSet继承在一起了:
    class ReadOnlyModelViewSet(mixins.RetrieveModelMixin,
                               mixins.ListModelMixin,
                               GenericViewSet):
        # 满足只有GET方法请求的情景
        pass
        
    class ModelViewSet(mixins.CreateModelMixin,
                       mixins.RetrieveModelMixin,
                       mixins.UpdateModelMixin,
                       mixins.DestroyModelMixin,
                       mixins.ListModelMixin,
                       GenericViewSet):
        # 满足所有请求都有的情景
        pass

          在开发的时候,直接继承 viewsets.ModelViewSet 就可以了,这样就不用写list、create、update等方法了,当然具体问题具体分析,如果你的需求与DRF提供的不一致,那么你就可以重写相应的方法即可

    假如有这样一个需求,你可能需要过滤查询集,以确保只返回与当前通过身份验证的用户发出的请求相关的结果。

    class CourserViewSet(ModelViewSet):
        """
        每个用户只可以查看owner属于自己的条目,可以创建条目
        """
    
        def list(self, request, *args, **kwargs):
            self.queryset = Course.objects.filter(owner=request.user.id)
            self.serializer_class = CourseSerializer
            return super(CourserViewSet, self).list(request, *args, **kwargs)
    
        def create(self, request, format=None):
            serializer = CourseSerializer(data=request.data)
            if serializer.is_valid():
                # .save()是调用CourseSerializer中的create()方法
                serializer.save(owner=self.request.user)
                return Response(serializer.data, status=status.HTTP_201_CREATED)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

         这里定义了两个方法,list方式中我们我们重写了queryset(就是对返回结果做了一次过滤),然后对于serializer_class指定了一个序列化类。并且我们使用super方法,继续载入viewset中的list方法,这里只会覆盖原有list的queryset和serializer_class。而对于create方法,我们则是完全重写了原viewset中的create方法。这里只是演示一下再ViewSet模式下如何来做工作。原则就是能用ViewSet内置的就是用内置的,内置的不满足需求就可以重写部分或全部重写。

    另外,如果只是过滤查询集,最简单方法是重写.get_queryset()方法即可。重写此方法允许你以多种不同方式自定义视图返回的查询集。

    class CourseViewSet(ModelViewSet):
        """
        每个用户只可以查看owner属于自己的条目,可以创建条目
        """
        
        serializer_class = CourseSerializer
    
        def list(self, request, *args, **kwargs):
            return Course.objects.filter(owner=request.user.id)

     

    自定义ViewSet基类

    要创建基础视图集类,提供createlistretrieve操作,继承GenericViewSet和混入所需的操作:
    class CreateListRetrieveViewSet(mixins.CreateModelMixin,
                                    mixins.ListModelMixin,
                                    mixins.RetrieveModelMixin,
                                    viewsets.GenericViewSet):
        """
        A viewset that provides `retrieve`, `create`, and `list` actions.
    
        To use it, override the class and set the `.queryset` and
        `.serializer_class` attributes.
        """
        pass

    ps:在开发时是使用ViewSet与mixins方法结合进行可以为我们节省很多功夫

    参考:

    https://q1mi.github.io/Django-REST-framework-documentation/api-guide/generic-views_zh/

    https://www.django-rest-framework.org/api-guide/viewsets/

    http://www.ywnds.com/?p=14104

  • 相关阅读:
    《ASP.NET Core跨平台开发从入门到实战》Web API自定义格式化protobuf
    .NET Core中文分词组件jieba.NET Core
    .NET Core 2.0及.NET Standard 2.0
    Visual Studio 2017 通过SSH 调试Linux 上.NET Core
    Visual Studio 2017 ASP.NET Core开发
    Visual Studio 2017正式版离线安装及介绍
    在.NET Core 上运行的 WordPress
    IT人员如何开好站立会议
    puppeteer(二)操作实例——新Web自动化工具更轻巧更简单
    puppeteer(一)环境搭建——新Web自动化工具(同selenium)
  • 原文地址:https://www.cnblogs.com/freely/p/10315404.html
Copyright © 2011-2022 走看看