美文网首页程序员
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