• 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

  • 相关阅读:
    网页支付宝接口使用
    @Valid注解的使用springmvc pojo校验
    Git
    面试宝典
    Navicat 破解版(操作非常简单)
    JAVA中List,Map,Set接口的区别
    java 枚举示例
    springcloud和springboot是什么关系?
    java面试经常涉及到的
    asp.net 报错 SAP 报错 试图加载格式不正确的程序。 (异常来自 HRESULT:0x8007000B)
  • 原文地址:https://www.cnblogs.com/freely/p/10315404.html
Copyright © 2020-2023  润新知