zoukankan      html  css  js  c++  java
  • rest_framework 之视图三部曲

    一、视图三部曲

    1  使用混合(mixins)

    上一节的视图部分:

    from rest_framework.views import APIView
    from rest_framework.response import Response
    from .models import *
    from django.shortcuts import HttpResponse
    from django.core import serializers
    
    from rest_framework import serializers
    
    class BookSerializers(serializers.ModelSerializer):
          class Meta:
              model=Book
              fields="__all__"
              #depth=1
    
    
    class PublshSerializers(serializers.ModelSerializer):
    
          class Meta:
              model=Publish
              fields="__all__"
              depth=1
    
    
    class BookViewSet(APIView):
    
        def get(self,request,*args,**kwargs):
            book_list=Book.objects.all()
            bs=BookSerializers(book_list,many=True,context={'request': request})
            return Response(bs.data)
    
    
        def post(self,request,*args,**kwargs):
            print(request.data)
            bs=BookSerializers(data=request.data,many=False)
            if bs.is_valid():
                print(bs.validated_data)
                bs.save()
                return Response(bs.data)
            else:
                return HttpResponse(bs.errors)
    
    
    class BookDetailViewSet(APIView):
    
        def get(self,request,pk):
            book_obj=Book.objects.filter(pk=pk).first()
            bs=BookSerializers(book_obj,context={'request': request})
            return Response(bs.data)
    
        def put(self,request,pk):
            book_obj=Book.objects.filter(pk=pk).first()
            bs=BookSerializers(book_obj,data=request.data,context={'request': request})
            if bs.is_valid():
                bs.save()
                return Response(bs.data)
            else:
                return HttpResponse(bs.errors)
    
    
    class PublishViewSet(APIView):
    
        def get(self,request,*args,**kwargs):
            publish_list=Publish.objects.all()
            bs=PublshSerializers(publish_list,many=True,context={'request': request})
            return Response(bs.data)
    
    
        def post(self,request,*args,**kwargs):
    
            bs=PublshSerializers(data=request.data,many=False)
            if bs.is_valid():
                # print(bs.validated_data)
                bs.save()
                return Response(bs.data)
            else:
                return HttpResponse(bs.errors)
    
    
    class PublishDetailViewSet(APIView):
    
        def get(self,request,pk):
    
            publish_obj=Publish.objects.filter(pk=pk).first()
            bs=PublshSerializers(publish_obj,context={'request': request})
            return Response(bs.data)
    
        def put(self,request,pk):
            publish_obj=Publish.objects.filter(pk=pk).first()
            bs=PublshSerializers(publish_obj,data=request.data,context={'request': request})
            if bs.is_valid():
                bs.save()
                return Response(bs.data)
            else:
                return HttpResponse(bs.errors)

     mixin类编写视图 :封装逻辑

    urls.py

     url(r'^books/$', views.BookView.as_view(),name="books"),#
     url(r'^books/(d+)/$', views.BookDetailView.as_view(),name="detailbook"),#

    (1)查看 

    from rest_framework import mixins
    from rest_framework import generics
    #
    class BookViewSet(mixins.ListModelMixin,
                      mixins.CreateModelMixin,
                      generics.GenericAPIView): #GenericAPIView继承了APIView,APIView继承了View,用户访问时相当于执行View方法->执行dispatch方法
    ->执行get方法->相当于执行list方法
    queryset = Book.objects.all() #不能改这两个变量的名字 serializer_class = BookSerializers# def get(self, request, *args, **kwargs): #整个获取数据的逻辑被封装到ListModelMixin这个类下的List方法中,                             以后再有关于所有获取数据的逻辑都从这个类下取 return self.list(request, *args, **kwargs) #list->get->dispatch->View# 
    查看mixins.ListModelMixin源码
    class ListModelMixin:
        """
        List a queryset.
        """
        def list(self, request, *args, **kwargs):
            queryset = self.filter_queryset(self.get_queryset()) #get_queryset方法封装在GenericAPView类中,拿到了queryset数据,
                这里的queryset也就是我们的数据
                                                   page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(queryset, many=True) #把数据交给序列化组件,拿到序列化结果 return Response(serializer.data)
    查看generics.GenericAPIView源码
    #
    class GenericAPIView(views.APIView):#
    
        queryset = None
        serializer_class = None
    
        lookup_field = 'pk'
        lookup_url_kwarg = None
    
        # The filter backend classes to use for queryset filtering
        filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
    
        # The style to use for queryset pagination.
        pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
    
        def get_queryset(self):   #
    
            assert self.queryset is not None, (
                "'%s' should either include a `queryset` attribute, "
                "or override the `get_queryset()` method."
                % self.__class__.__name__
            )
            #
            queryset = self.queryset  #从自己当前类(上面是BookViewSet)找queryset,拿到数据交给queryset
            if isinstance(queryset, QuerySet):  #
                # Ensure queryset is re-evaluated on each request.
                queryset = queryset.all()  #
            return queryset

    (2)添加

    class BookViewSet(mixins.ListModelMixin,
                      mixins.CreateModelMixin,
                      generics.GenericAPIView):
    
        queryset = Book.objects.all()
        serializer_class = BookSerializers

      def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs)
     查看mixins.CreateModelMixin源码
    #
    class CreateModelMixin:
        """
        Create a model instance.
        """
        def create(self, request, *args, **kwargs):
            serializer = self.get_serializer(data=request.data)  #把数据交给序列化组件,拿到序列化结果
            serializer.is_valid(raise_exception=True)# 对数据校验证,如果验证成功继续往下走,失败了在这报错
            self.perform_create(serializer) #相当于save操作
            headers = self.get_success_headers(serializer.data)#
            return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)#返回单条数据生成的对象
    
        def perform_create(self, serializer):#
            serializer.save()  #
    
        def get_success_headers(self, data):#
            try:
                return {'Location': str(data[api_settings.URL_FIELD_NAME])}#
            except (TypeError, KeyError):
                return {}

    (3)单条数据的get请求

    class BookDetailViewSet(mixins.RetrieveModelMixin,
                        mixins.UpdateModelMixin,
                        mixins.DestroyModelMixin,
                        generics.GenericAPIView):
        queryset = Book.objects.all()  #
        serializer_class = BookSerializers#
    #
        def get(self, request, *args, **kwargs):#
            return self.retrieve(request, *args, **kwargs)#

    查看mixins.RetrieveModelMixin源码

    #
    class RetrieveModelMixin:
        """
        Retrieve a model instance.
        """
        def retrieve(self, request, *args, **kwargs): #
            instance = self.get_object()  #拿到单条数据,获取单条对象
            serializer = self.get_serializer(instance) #序列化
            return Response(serializer.data)#

    (4)单条数据的delete请求

    class BookDetailViewSet(mixins.RetrieveModelMixin,
                        mixins.UpdateModelMixin,
                        mixins.DestroyModelMixin,
                        generics.GenericAPIView):
        queryset = Book.objects.all()
        serializer_class = BookSerializers
    
        def delete(self, request, *args, **kwargs): #
            return self.destroy(request, *args, **kwargs)#

    查看mixins.DestroyModelMixin源码

    class DestroyModelMixin:#
        """
        Destroy a model instance.
        """
        def destroy(self, request, *args, **kwargs):
            instance = self.get_object()#拿到单条数据对象
            self.perform_destroy(instance)#删除操作
            return Response(status=status.HTTP_204_NO_CONTENT)#返回空
    
        def perform_destroy(self, instance):#
            instance.delete()#

    (4)单条数据的put请求

    class BookDetailViewSet(mixins.RetrieveModelMixin,
                        mixins.UpdateModelMixin,
                        mixins.DestroyModelMixin,
                        generics.GenericAPIView):
        queryset = Book.objects.all()
        serializer_class = BookSerializers
    
        def put(self, request, *args, **kwargs):#
            return self.update(request, *args, **kwargs)#

    查看mixins.UpdateModelMixin源码

    class UpdateModelMixin:
        """
        Update a model instance.
        """
        def update(self, request, *args, **kwargs): #
            partial = kwargs.pop('partial', False)#
            instance = self.get_object()#拿单条数据实例对象
            serializer = self.get_serializer(instance, data=request.data, partial=partial)#用序列化组件实例化出来一个对象
            serializer.is_valid(raise_exception=True)# 数据校验
            self.perform_update(serializer)#save操作,本质上是一个更新操作
    
            if getattr(instance, '_prefetched_objects_cache', None):#
                # If 'prefetch_related' has been applied to a queryset, we need to
                # forcibly invalidate the prefetch cache on the instance.
                instance._prefetched_objects_cache = {}#
    
            return Response(serializer.data)#
    
        def perform_update(self, serializer):#
            serializer.save()#
    
        def partial_update(self, request, *args, **kwargs):#
            kwargs['partial'] = True#
            return self.update(request, *args, **kwargs)#

     以上这种方式代码量和之前相比没少多少,但主要逻辑都帮我们封装起来了

     2 使用通用的基于类的视图

     通过使用mixin类,我们使用更少的代码重写了这些视图,但我们还可以再进一步。REST框架提供了一组已经混合好(mixed-in)的通用视图,我们可以使用它来简化我们的views.py模块。 

    from rest_framework import generics
    
    class BookView(generics.ListCreateAPIView): #
        queryset=Author.objects.all()#
        serializer_class =AuthorModelSerializers#
        
        
    #generics.py
    class ListCreateAPIView(mixins.ListModelMixin, #
                            mixins.CreateModelMixin,#
                            GenericAPIView):
        """
        Concrete view for listing a queryset or creating a model instance.
        """
        def get(self, request, *args, **kwargs):#
            return self.list(request, *args, **kwargs)#
    
        def post(self, request, *args, **kwargs):#
            return self.create(request, *args, **kwargs)#

    单条数据的查看、更新和删除

    from rest_framework import generics
    
    class BookDetailView(generics.RetrieveUpdateDestroyAPIView):#
        queryset = Author.objects.all()#
        serializer_class = AuthorModelSerializers#
    #generics.py class RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin,# mixins.UpdateModelMixin,# mixins.DestroyModelMixin,# GenericAPIView):# """ Concrete view for retrieving, updating or deleting a model instance. """ def get(self, request, *args, **kwargs):# return self.retrieve(request, *args, **kwargs)# def put(self, request, *args, **kwargs):# return self.update(request, *args, **kwargs)# def patch(self, request, *args, **kwargs):#put和patch都是更新 return self.partial_update(request, *args, **kwargs)# def delete(self, request, *args, **kwargs):# return self.destroy(request, *args, **kwargs)#

    3 viewsets.ModelViewSet:

    统一了上面两个视图类,主要在url这里做了文章

    之前的as_view没有传参数,用户访问时找到的as_view是APIView(View)类下面没有处理参数的as_view方法

    现在我们把url进行以下设置:

    urls.py

       #en
       url(r'^books/$', views.BookViewSet.as_view({"get":"list","post":"create"}),name="book_list"),
        url(r'^books/(?P<pk>d+)$', views.BookViewSet.as_view({
                    'get': 'retrieve',#
                    'put': 'update',#
                    'patch': 'partial_update',#
                    'delete': 'destroy'#
                }),name="book_detail"),#

    上面的url中的as_view传了参数:通过一个字典,传入参数,指定什么请求方式用哪一个内部方法来执行

    views.py

    #
    from rest_framework import viewsets
    
    #只需一个类就能实现get、post、put、patch、delete请求
    class BookViewSet(viewsets.ModelViewSet):#
        queryset = Book.objects.all()#
        serializer_class = BookSerializers#

    当用户通过get/post请求访问了books之后: -> views.BookViewSet.as_view

    现在我们来找as_view方法  BookViewSet->ModelViewSet->GenericViewSet->ViewSetMixin 

    class ViewSetMixin:#         (1)
        """
        This is the magic.
    
        Overrides `.as_view()` so that it takes an `actions` keyword that performs
        the binding of HTTP methods to actions on the Resource.
    
        For example, to create a concrete view binding the 'GET' and 'POST' methods
        to the 'list' and 'create' actions...
    
        view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
        """
    
        @classonlymethod
        def as_view(cls, actions=None, **initkwargs):#actions就是我们传的那个字典 {"get":"list","post":"create"}       (2)
    
            def view(request, *args, **kwargs): #覆盖了View类下的view方法  (4)
                self = cls(**initkwargs)#
                self.action_map = actions #actions就是我们传的那个字典  {"get":"list","post":"create"}
    
                for method, action in actions.items(): #method是请求方式,action是实例方法
                    handler = getattr(self, action)  #反射找实例方法  self.list   self.create
                    setattr(self, method, handler)  #为请求方式设置对应的实例方法
    
                    # setattr(self,"get",self.list)  #这两步相当于:self.get=self.list  self.list =self.create
                    # setattr(self,"post",self.create)
    
                #……
                # And continue as usual
                return self.dispatch(request, *args, **kwargs)#  (5)
                #查找dispatch:->BookViewSet->ModelViewSet-> GenericViewSet->ViewSetMixin->
                # GenericAPIView(views.APIView)->APIView最后找到了APIView下面的dispatch方法
        #……
        return csrf_exempt(view) #返回的是当前ViewSetMixins类下的view方法 (3)

    #rest_frameworkview.py class APIView(View):# #…… def dispatch(self, request, *args, **kwargs): (6) #…… try: self.initial(request, *args, **kwargs)# # Get the appropriate handler method if request.method.lower() in self.http_method_names:# handler = getattr(self, request.method.lower(), #handler就是list、create方法 self.http_method_not_allowed)# else: handler = self.http_method_not_allowed# response = handler(request, *args, **kwargs) #返回的就是list、create方法 (8) #如果get请求走的就是ListModelMixin类下的list方法 #如果post请求走的就是CreateModelMixin类的create方法 #最后执行上述方法后把返回值传给用户 #返回值执行过程:response->dispatch->ViewSetMixin类下的view-> views.BookViewSet.as_view return self.response# (7) 
  • 相关阅读:
    分治算法
    【原创】KFold函数 __init__() got an unexpected keyword argument 'n_folds' or 'n_splits'
    【原创】【Mac】创建可以双击执行Shell脚本文件(类似windows批处理脚本)
    【原创】【Python】随机生成中文姓名
    【原创】【word】两步搞定姓名2个字加空格对齐
    数据结构与算法——冒泡排序及其各种优化变形详解
    CobaltStrike去除流量特征
    Fastjson1.2.24RCE漏洞复现
    Redis奇怪的姿势
    Apache Druid 远程代码执行 CVE-2021-25646 漏洞复现
  • 原文地址:https://www.cnblogs.com/zh-xiaoyuan/p/13040923.html
Copyright © 2011-2022 走看看