美文网首页程序员
Rest framework-视图组件(升级版)

Rest framework-视图组件(升级版)

作者: 墨颜丶 | 来源:发表于2018-08-06 01:41 被阅读0次

    大家想一个问题现在只是一个Book,如果出现比如Author,Publisher等等,难道我们一直重复下去么 ?

    比如我现在要写一个Author,从下面我们可以看出这是在重复造文字

    urls.py

    urlpatterns = [
        url(r'^books/$',views.BookView.as_view()),
        # 以pk命名是固定的
        # 执行如下:(不在详细解析,可以跳过)
        # view(request,pk=1)
        # self.kwargs={"pk":1}
        # queryset.get(**{"pk":1})
        # queryset.get(pk=1)
        url(r'^books/(?P<pk>\d+)/$', views.BookDetailView.as_view()),
        url(r'^authors/$',views.AuthorView.as_view()),
        url(r'^authors/(?P<pk>\d+)/$',views.AuthorDetailView.as_view()),
    
    ]
    

    views.py

    from app01.models import Book
    from app01.serializer import BookSerializers
    
    class BookView(APIView):
    
        def get(self,request):
            book_list=Book.objects.all()
            ps=BookSerializers(book_list,many=True)
            # 序列化完成的数据
            return Response(ps.data)
    
        def post(self,request):
            # 添加一条数据
            # request.POST 在发JSON数据的时候是取不到值的,只有发urlecoding,orm-data才会有值
            # REST已经对request再次封装了,发送JSON,urlecoding,form-data等常用等常用的数据都封装到data里了,这样就解决了request.POST 在发JSON数据的时候是取不到值的情况
            print("data",request.data)
            print("POST",request.POST)
            # 生成一条记录
            bs=BookSerializers(data=request.data)
            # 校验成功后生成记录
            if bs.is_valid():
                bs.save()
                # 把添加的数据序列化返回
                return Response(bs.data)
            else:
                # 校验失败返回错误信息
                return Response(bs.errors)
    
    
    class BookDetailView(APIView):
    
        def get(self,request,pk):
            # 根据pk值获取数据库对应的一条对象
            book_obj=Book.objects.filter(pk=pk).first()
            # 序列化一个对象,默认就为False
            bs=BookSerializers(book_obj,many=False)
            return Response(bs.data)
    
        def put(self,request,pk):
            book_obj=Book.objects.filter(pk=pk).first()
            # 更新一定要传两个值
            # 第一个这次更新传进来的数据(data=request.data)
            # 第二个要更新的对象(instance=book_obj)
            bs=BookSerializers(data=request.data,instance=book_obj)
            if bs.is_valid():
                # 如果校验成功就updata
                bs.save()
                # 返回更新后的数据
                return Response(bs.data)
            else:
                # 返回错误信息
                return Response(bs.errors)
    
        def delete(self,request,pk):
            Book.objects.filter(pk=pk).delete()
            return Response("OK")
    # 以下基本都是重复上面的结构
    from app01.models import Author
    from app01.serializer import AuthorSerializers
    class AuthorView(APIView):
    
        def get(self,request):
            author_list=Author.objects.all()
            ser=AuthorSerializers(author_list,many=True)
            return Response(ser.data)
        def post(self,request):
            ser=AuthorSerializers(data=request.data)
            if ser.is_valid():
                ser.save()
                return Response(ser.data)
            else:
                return Response(ser.errors)
    
    class AuthorDetailView(APIView):
        def get(self,request,pk):
            author_obj=Author.objects.filter(pk=pk).first()
            ser=AuthorSerializers(author_obj,many=False)
            return Response(ser.data)
        def put(self,request,pk):
            author_obj=Author.objects.filter(pk=pk).first()
            ser=AuthorSerializers(data=request.data,instance=author_obj)
            if ser.is_valid():
                ser.save()
                return Response(ser.data)
            else:
                return Response(ser.errors)
        def delete(self,request,pk):
            Author.objects.filter(pk=pk).delete()
            return Response("OK")
    

    第一种方法(把不同的请求分为5个类)

    其实我们可以灵活的分成把不同请求分为5个类,类似于下面写的这种,别忘记python支持多继承,灵活继承-只继承他需要的

    class LIST():
        queryset=""
        serialize_class=""
        def list(self,request):
            queryset=self.queryset
            As=self.serialize_class(queryset(),many=True)
            return Response(As.data)
    
    class CREATE():
        serialize_class = ""
        def create(self,request):
            As=self.serialize_class(data=request.data)
            if As.is_valid():
                As.save()
                return Response(As.data)
            else:
                return Response(As.errors)
    # --------------------
    class AuthorView(APIView,LIST,CREATE):
    
        queryset=Author.objects.all
        serialize_class=AuthorSerializers
        def get(self,request):
            # author_list=Author.objects.all()
            # ser=AuthorSerializers(author_list,many=True)
            # return Response(ser.data)
            return self.list(request)
    
        def post(self,request):
            # ser=AuthorSerializers(data=request.data)
            # if ser.is_valid():
            #     ser.save()
            #     return Response(ser.data)
            # else:
            #     return Response(ser.errors)
            return self.create(request)
            
    #  .......... 按照这种方法,在写三个加id参数的,这里就不一一写了,因为REST有跟这个类似的封装............
    

    以下就是REST封装的

    from rest_framework.mixins import CreateModelMixin,ListModelMixin,DestroyModelMixin,RetrieveModelMixin,UpdateModelMixin
    

    Author为例

    from app01.models import Author
    from app01.serializer import AuthorSerializers
    from rest_framework.mixins import CreateModelMixin,ListModelMixin,DestroyModelMixin,RetrieveModelMixin,UpdateModelMixin
    from rest_framework import generics
    
    # generics.GenericAPIView 继承的是APIView,在里边又定义了以下五个方法内需要的参数
    class AuthorView(ListModelMixin,CreateModelMixin,generics.GenericAPIView):
        # 以下这些格式是变量等都是固定的
        queryset=Author.objects.all()
        serializer_class=AuthorSerializers
        def get(self,request):
            return self.list(request)
    
        def post(self,request):
            return self.create(request)
    
    class AuthorDetailView(DestroyModelMixin,RetrieveModelMixin,UpdateModelMixin,generics.GenericAPIView):
        # 以下这些格式是变量等都是固定的
        queryset=Author.objects.all()
        serializer_class=AuthorSerializers
        
        def get(self,request, *args, **kwargs):
            # 看一下对应方法源码里边有return Response就是我们这要return的
            return self.retrieve(request, *args, **kwargs)
    
        def put(self,request, *args, **kwargs):
            # 看一下对应方法源码里边有return Response就是我们这要return的
            return self.update(request, *args, **kwargs)
    
        def delete(self,request, *args, **kwargs):
            # 看一下对应方法源码里边有return Response就是我们这要return的
            return self.destroy(request, *args, **kwargs)
    

    第二种方法(把5个类根据是否有id参数封装成两个类)

    view.py

    from app01.models import Author
    from app01.serializer import AuthorSerializers
    from rest_framework import generics
    
    class AuthorView(generics.ListCreateAPIView):
        # 以下这些格式是变量等都是固定的
        queryset=Author.objects.all()
        serializer_class=AuthorSerializers
    
    class AuthorDetailView(generics.RetrieveUpdateDestroyAPIView):
        # 以下这些格式是变量等都是固定的
        queryset=Author.objects.all()
        serializer_class=AuthorSerializers
    

    继承类源码简单分析

    # generics.ListCreateAPIView) 继承了原先三个类
    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)
    
    # generics.RetrieveUpdateDestroyAPIView 继承了原先四个类
    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):
            return self.partial_update(request, *args, **kwargs)
    
        def delete(self, request, *args, **kwargs):
            return self.destroy(request, *args, **kwargs)
    

    第三种方法(终极)

    上边的view.py可以看出来还有重复的部分,那我们就封装到一个类里面去,那我们的想一个问题了view.py怎么写?

    如:两个get请求一个视图处理怎么区分;加参数与不加参数

    authors为例:

    对应的请求执行对应的方法,仔细看key对应的value,value跟第一种方法要return返回的方法执行的方法变量一致 (第一种方法(把不同的请求分为5个类)),以后用的最多的也是第一种和第三种
    urls.py

    urlpatterns = [
        # 执行get请求执行list方法,post执行create方法
        url(r'^authors/$',views.AuthorModelView.as_view({"get":"list","post":"create"})),
        # 执行get请求执行retrieve方法,put执行update方法,delete请求destroy
        url(r'^authors/(?P<pk>\d+)/$',views.AuthorModelView.as_view({"get":"retrieve","put":"update","delete":"destroy"})),
    ]
    

    serializer.py

    from app01.models import Author
    class AuthorSerializers(serializers.ModelSerializer):
        class Meta:
            model=Author
            field
    

    views.py

    from app01.models import Author
    from app01.serializer import AuthorSerializers
    from rest_framework.viewsets import ModelViewSet
    class AuthorModelView(ModelViewSet):
        # 以下这些格式是变量等都是固定的
        queryset=Author.objects.all()
        serializer_class=AuthorSerializers
    

    源码简单分析

    ModelViewSet

    # ModelViewSet继承了5个不同请求方法类
    # 主要区别在于ModelViewSet继承了GenericViewSet而不是第一种与第二种类继承的GenericAPIView
    class ModelViewSet(mixins.CreateModelMixin,
                       mixins.RetrieveModelMixin,
                       mixins.UpdateModelMixin,
                       mixins.DestroyModelMixin,
                       mixins.ListModelMixin,
                       GenericViewSet):
        """
        A viewset that provides default `create()`, `retrieve()`, `update()`,
        `partial_update()`, `destroy()` and `list()` actions.
        """
        pass
    

    GenericViewSet又干了什么呢 ?

    Django一启动走到了view方法-return csrf_exempt(view),用户一访问执行view()

    # GenericViewSet 他继承了第一种与第二种类继承的GenericAPIView
    # 所以区别就在与ViewSetMixin
    class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
        """
        The GenericViewSet class does not provide any actions by default,
        but does include the base set of generic view behavior, such as
        the `get_object` and `get_queryset` methods.
        """
        pass
    
    # ViewSetMixin 重写了as_view方法,
    class ViewSetMixin(object):
        # 看这神奇的注释,它简单的解释了一切
        """
        这是神奇的。
        覆盖'.as_view()',使其具有执行的' actions '关键字
        将HTTP方法绑定到资源上的操作。
    
        例如,创建绑定“GET”和“POST”方法的具体视图
        到“列表”和“创建”动作……
        
        这就是告诉你怎么用这个类,就是给urls.py,as_view加参数
        get不要在执行get请求执行list方法,post执行create方法
        view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
        """
        """
        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):
            cls.suffix = None
            cls.detail = None
            # ..............................................等
            def view(request, *args, **kwargs):
                self = cls(**initkwargs)
                # 我们还存储请求方法到动作的映射,
                # 以便我们以后可以设置action属性.
                # 如.  对传入的GET(GET=self.action)请求的操作`self.action = 'list'`
                self.action_map = actions
    
                #将方法绑定到操作
                # 这一点与标准视图不同
                # actions相当于urls.py,as_view传进来的参数
                # actions={"get":"list","post":"create"}
                # actions={"get":"retrieve","put":"update","delete":"destroy"}
                for method, action in actions.items():
                    handler = getattr(self, action)
                    setattr(self, method, handler)
                # 上边的for循环相当于
                # self.get=self.list self.post=self.create
                # 另一种URL时:self.get=self.retrieve self.put=self.update self.delete=self.destroy
    
                    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
                return self.dispatch(request, *args, **kwargs)
            # ..............................................等
            view.cls = cls
            view.initkwargs = initkwargs
            view.suffix = initkwargs.get('suffix', None)
            view.actions = actions
            return csrf_exempt(view)
    
    

    总结

    作为数据接口:第三种最合适-写到一个类里面

    作为非数据接口:第一种最合适-写到5个类里面去-如:登陆

    相关文章

      网友评论

        本文标题:Rest framework-视图组件(升级版)

        本文链接:https://www.haomeiwen.com/subject/ikaivftx.html