Django Rest Framework(3)-----APIView与Viewsets

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

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

 

mixins

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

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

Mixin 类能够从 rest_framework.mixins导入。json

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

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

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

 

 

 

 

 

 

 

 

使用详解

1.APIView

APIView对django自己的View进行封装session

APIView类和通常的View类有如下不一样:框架

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

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

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

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

                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

相关文章
相关标签/搜索