rest-framework视图组件(views)

一:APIView和View的区别

    a:rest-framework对原Request进行了封装

      --request.query_params 存放的是我们get请求的参数

      --request.data 存放的是我们所有的数据,包括post请求的以及put,patch请求

二:对Views视图进行封装

     a:观察几个请求对应的方法的共同之处

      1:models.Book.objects.all()或者models.Book.objects.filter(**kwargs)

      2:book_serializers = BookSerializers(book_obj,data=request.data,partial=True)      #参数不一样

    def put(self,request,id):
        print(request.data)
        book_obj = Book.objects.filter(id=id).first()
        # partial   True 表示可以进行部分字段跟新,Flase 表示全部跟新,默认False
        book_serializers = BookSerializers(book_obj,data=request.data,partial=True)

        #需要重写 updata
        if book_serializers.is_valid():
            book_serializers.save()
            return Response(book_serializers.validated_data)
        else:
            return Response(book_serializers.errors)


    def  delete(self,request,id):
        book_obj = Book.objects.filter(id=id).first()
        book_obj.delete()
        return  Response("")

    3:进行初次封装

       注意点:
           i:定义序列化和反序列化类 并进行设置 serializer_class = BookSerializers

          II:获取到对应的对象(可以时多个也可以是单个)query_set = models.Book.objects.all()

         III:创建一个类 GenericApIview 将上面两个属性进行通过方法获取

         IV:Mixin类对不同的方法进行封装。并因为里面没有 query_set和serializer_class属性。所以需要和GenericApiView类进行混合继承。

from  SerDemo import models
from  rest_framework.views import APIView     #视图函数继承APIView  不是View
from rest_framework.response import Response  #用于返回值
#BookSerializers  自己定义的序列化规则
from SerDemo.serializers import *

class  GenericAPIView(APIView):
    query_set = None
    serializer_class = None
    def   get_query_set(self):
        return self.query_set

    def   get_serializer_class(self,*args,**kwargs):
        return self.serializer_class(*args,**kwargs)

class ListModelMixin(object):
    def list(self, request, *args, **kwargs):
        book_obj = self.get_query_set()
        # book_obj = models.Book.objects.all()
        # 序列化多组数据 和一个对象,需要many=TRUE  默认一个
        ret = self.get_serializer_class(book_obj, many=True)
        # ret  =BookSerializers(book_obj,many=True)
        # 序列化的数据在data属性中
        return Response(ret.data)

class CreateModelMixin(object):
    def  create(self,request,*args,**kwargs):
        data = self.get_serializer_class(data=request.data)
        # 进行数据验证,并且在BookSerializers中重写create方法
        if data.is_valid():
            data.save()
            return Response(data.data)
        else:
            return Response(data.errors)
class RetrieveModelMixin(object):
    def  retrieve(self,id,request,*args,**kwargs):
        book_obj = self.get_query_set().filter(id=id).first()
        book = self.get_serializer_class(book_obj)
        return Response(book.data)

class UpdateModelMixin(object):
    def update(self, request, id, *args, **kwargs):
        book_obj = self.get_query_set().filter(pk=id).first()
        book_ser = self.get_serializer(book_obj, data=request.data, partial=True)
        if book_ser.is_valid():
            book_ser.save()
            return Response(book_ser.validated_data)
        else:
            return Response(book_ser.errors)


class DestroyModelMixin(object):
    def destroy(self, request, id, *args, **kwargs):
        queryset = self.get_query_set()
        try:
            queryset.get(pk=id).delete()
            return Response("")
        except Exception as e:
            return Response("信息有误")

class BookApiView(GenericAPIView,ListModelMixin,CreateModelMixin):
    query_set=models.Book.objects.all()
    serializer_class=BookSerializers
    def  get(self,request,*args,**kwargs):

        # book_obj =self.get_query_set()
        # book_obj = models.Book.objects.all()
        #序列化多组数据 和一个对象,需要many=TRUE  默认一个
        # ret  =  self.get_serializer_class(book_obj,many=True)
        # ret  =BookSerializers(book_obj,many=True)
        #序列化的数据在data属性中
        return  self.list(request, *args, **kwargs)

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

class   BookEditView(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
    query_set = models.Book.objects.all()
    serializer_class = BookSerializers
    def  get(self, request,id,*args,**kwargs):
        return  self.retrive(id,request,*args,**kwargs)

    def put(self,request,id,*args,**kwargs):
        return self.update(request,id,*args,**kwargs)


    def  delete(self,request,id,*args,**kwargs):
        return self.destroy(request, id, *args, **kwargs)

  4:再次优化封装

        现在有两个问题

          一:view中有两个方法。能不能合成一个

          二:对应的View视图函数中只有对应的方法调用,不存在任何逻辑。能否通过url映射取出对应的方法

       a:修改url

         --观察上面修改的方法,可以看出link/ 路径下  get ---调用类了 self.list  post调用了 self.create方法

         --可以得出结论 如果我们按下面袖该的url分发并执行对应的函数,那么我们就可以进一步简化

         --但是原有的方法as_view方法不能进行传参数

urlpatterns = [
    path('link/', views.BookModelSetView.as_view({"get":"list","post":"create"})),
    path('retrieve/<int:id>',
         views.BookModelSetView.as_view({"get":"retrieve","put":"update","delete":"destroy"}))

]

       b:解决as_view()方法传参

         i:导入ViewSetMixin类

from rest_framework.viewsets import ViewSetMixin

        II:阅读对应的源码:

           --as_view()进行重写,然后看view方法(不建议改动源码,我是为了学习注释。)

@classonlymethod
    def as_view(cls, actions=None, **initkwargs):
        """
        Because of the way class based views create a closure around the
        instantiated view, we need to totally reimplement `.as_view`,
        and slightly modify the view function that is created and returned.
        """
        # The name and description initkwargs may be explicitly overridden for
        # certain route confiugurations. eg, names of extra actions.
        cls.name = None
        cls.description = None

        # The suffix initkwarg is reserved for displaying the viewset type.
        # This initkwarg should have no effect if the name is provided.
        # eg. 'List' or 'Instance'.
        cls.suffix = None

        # The detail initkwarg is reserved for introspecting the viewset type.
        cls.detail = None

        # Setting a basename allows a view to reverse its action urls. This
        # value is provided by the router through the initkwargs.
        cls.basename = None

        # actions must not be empty
        if not actions:
            raise TypeError("The `actions` argument must be provided when "
                            "calling `.as_view()` on a ViewSet. For example "
                            "`.as_view({'get': 'list'})`")

        # sanitize keyword arguments
        for key in initkwargs:
            if key in cls.http_method_names:
                raise TypeError("You tried to pass in the %s method name as a "
                                "keyword argument to %s(). Don't do that."
                                % (key, cls.__name__))
            if not hasattr(cls, key):
                raise TypeError("%s() received an invalid keyword %r" % (
                    cls.__name__, key))

        # name and suffix are mutually exclusive
        if 'name' in initkwargs and 'suffix' in initkwargs:
            raise TypeError("%s() received both `name` and `suffix`, which are "
                            "mutually exclusive arguments." % (cls.__name__))

        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            # We also store the mapping of request methods to actions,
            # so that we can later set the action attribute.
            # eg. `self.action = 'list'` on an incoming GET request.
            self.action_map = actions

            # Bind methods to actions
            # This is the bit that's different to a standard view
            '''
                1:actions={"get":"list","post":"create"}   as_view()方法中传递的参数
                2:循环后结果为
                    methon ="get"      #key
                    action ='list'     #对应的方法字符串
                    
            '''
            for method, action in actions.items():
                '''
                 3:通过getattr将字符串反射对应的方法名
                    handler = list    #循环将每一个字符传,修改对应的放啊
                
                 4:通过setattr(self, method, handler) 得到的结果为
                   set.method =self.handler -->self.get=self.list.
                 5:成功映射对应的方法  
                    
                '''
                handler = getattr(self, action)

                setattr(self, method, handler)

            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get

            self.request = request
            self.args = args
            self.kwargs = kwargs

            # And continue as usual
            '''
              6:重新进行路由分发,并且已经修改成功方法
            '''
            return self.dispatch(request, *args, **kwargs)

        # take name and docstring from class
        update_wrapper(view, cls, updated=())

        # and possible attributes set by decorators
        # like csrf_exempt from dispatch
        update_wrapper(view, cls.dispatch, assigned=())

        # We need to set these on the view function, so that breadcrumb
        # generation can pick out these bits of information from a
        # resolved URL.
        view.cls = cls
        view.initkwargs = initkwargs
        view.actions = actions
        return csrf_exempt(view)

    c:最后进行view编写:

from rest_framework.viewsets import ViewSetMixin

# 定义一个类,用来简化BookModelSetView的继承类的长度,不是很重要
class ModelViewSet(ViewSetMixin,GenericAPIView,RetrieveModelMixin,
                        UpdateModelMixin,DestroyModelMixin,CreateModelMixin,ListModelMixin):
    pass
class  BookModelSetView(ModelViewSet):
    query_set = models.Book.objects.all()
    serializer_class = BookSerializers

  5:使用rest_framework自带的方法实现以上功能

          --导入 from  rest_framework.viewsets import ModelViewSet

          --参数是queryset和serializer_class

          --路由修改id 成pk

          --该方法和上面自修改方法本质上相同。

          --使用自带的方法简便,但是有时候并不需要那么多的方法,所以自定义方法可以由于简化请求方法。

from  rest_framework.viewsets import ModelViewSet
class  BookModelSetView(ModelViewSet):
    queryset = models.Book.objects.all()
    serializer_class = BookSerializers
urlpatterns = [
    path('link/', views.BookModelSetView.as_view({"get":"list","post":"create"})),
    path('retrieve/<int:pk>',
         views.BookModelSetView.as_view({"get":"retrive","put":"update","delect":"destroy"}))

]  

三:小结

      1:以上用到的方法均在以下4个类中,可以查看下源码

        from rest_framework import views,generics,mixins,viewsets

   

四:路由系统

    了解下即可,最好不要用这种方法

from .views import BookView
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r"book", BookView)

urlpatterns = [

]
urlpatterns += router.urls

  

  

       

      

 

posted @ 2019-03-27 01:14  pyjar  阅读(257)  评论(0编辑  收藏  举报