Django-DRF-视图的演变(二)

前言:本文是“《Django-DRF-序列化模型实战(一)》”的下篇,其中示例所用的内容也继承了它,这篇文章是比较高阶的内容,适合有一定基础的同学查阅。

视图的演变

版本一(底层方法)

这种方法只需要看得懂即可,因为太底层了,后面写代码不会用这种方式

1、撰写视图:

写一个视图,支持:GET all、GET one、POST、PUT、DELETE这五个操作。

$ vim idcs/views.py

from django.http import HttpResponse
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser

from .models import Idc
from .serializers import IdcSerializer


class JSONResponse(HttpResponse):
    def __init__(self, data, **kwargs):
        kwargs.setdefault('content_type', 'application/json')
        content = JSONRenderer().render(data)
        super(JSONResponse, self).__init__(content=content, **kwargs)

def idc_list(request, *args, **kwargs):
    if request.method == 'GET':
        quertset = Idc.objects.all()
        serializer = IdcSerializer(quertset,many=True)
        return JSONResponse(serializer.data)
        # content = JSONRenderer().render(serializer.data)
        # return HttpResponse(content=content,content_type="application/json")

    elif request.method == 'POST':
        content = JSONParser().parse(request)
        serializer = IdcSerializer(data=content)
        if serializer.is_valid():
            serializer.save()
            return JSONResponse(serializer.data)
    return HttpResponse('')

def idc_detail(request, pk, *args, **kwargs):
    try:
        idc = Idc.objects.get(pk=pk)
    except Idc.DoesNotExist:
        return HttpResponse(status=404)

    if request.method == 'GET':
        serializer = IdcSerializer(idc)
        return JSONResponse(serializer.data)
    elif request.method == 'PUT':
        content = JSONParser().parse(request)
        serializer = IdcSerializer(idc, data=content)
        if serializer.is_valid():
            serializer.save()
            return JSONResponse(serializer.data)
        return JSONResponse(serializer.errors,status=400)
    elif request.method == 'DELETE':
        idc.delete()
        return HttpResponse(status=204)

# 代码说明:
1. JSONResponse改写了Django里默认的,目的是处理POST提交的数据,将数据转换成JSON后方便处理。
2. idc_list可以通过HttpResponse和改写的JSONResponse两种方法来返回给前端。
3. idc_detail函数设计接受一个pk,这个pk是一个id,通过查询到ID后,进行GET、PUT、DELETE操作。

2、路由规则:

$ vim idc/urls.py

from django.conf.urls import url
from idcs.views import idc_list,idc_detail

###########################  版本一  ##############################
urlpatterns = [
    url('^idcs/$', idc_list, name='idc_list'),
    url('^idcs/(?P[0-9]+)/$', idc_detail, name='idc_detail')
]

3、请求(获取所有数据,创建一个数据,使用pk指定获取一个资源信息,更新一个资源的信息,删除一个资源):

可以使用Postman工具来进行Http请求的提交操作,每一次提交请求后,该工具都会有Status字段显示请求的状态码,比较方便。

版本二(基于函数视图的@api_view装饰器)

1、撰写视图

$ vim idcs/views.py

###########################  版本二  ##############################

from rest_framework.decorators import api_view   #导入api_view
from rest_framework import status   # 导入status,返回状态使用这个模块
from rest_framework.response import Response # DRF封装好的方法Response

@api_view(["GET","POST"])
def idc_list_v2(request, *args, **kwargs):
    if request.method == 'GET':
        queryset = Idc.objects.all()
        serializer = IdcSerializer(queryset, many=True)
        return Response(serializer.data)
    elif request.method == 'POST':
        serializer = IdcSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.data, status=status.HTTP_404_NOT_FOUND)

@api_view(["GET", "PUT", "DELETE"])
def idc_detail_v2(request, pk, *args, **kwargs):
    try:
        idc = Idc.objects.get(pk=pk)
    except Idc.DoesNotExist:
        return HttpResponse(status=status.HTTP_404_NOT_FOUND)

    if request.method == 'GET':
        serializer = IdcSerializer(idc)
        return Response(serializer.data)

    elif request.method == 'PUT':
        serializer = IdcSerializer(idc, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_404_NOT_FOUND)
    elif request.method == 'DELETE':
        idc.delete()
        return HttpResponse(status=status.HTTP_204_NO_CONTENT)

# 代码说明:
1. @api_view(),传入一个列表作为参数,这个列表里写入具体的方法,如["GET", "PUT", "DELETE"]。api_view之会允许你写入的Http方法进行交互。没有写的就会禁止交互。
2. status.HTTP_404_NOT_FOUND,status模块可以返回你想要给前端的状态。
3. Response,这是drf给我们封装好的方法,它会将模板和数据一并返回给前端,所以你在前端能看见drf的页面了。

2、路由规则:

$ vim idc/urls.py

from django.conf.urls import url
from idcs.views import idc_list,idc_detail

from . import views  

###########################  版本二  ##############################
urlpatterns = [
    url('^idcs/$', views.idc_list_v2, name='idc_list'), 
    url('^idcs/(?P[0-9]+)/$', views.idc_detail_v2)
]

直接通过浏览器访问的话,就能看到Response渲染给前端的页面。

Api Root

截至目前,你在访问跟站点的时候,应该是会出错的,因为我们没有定义访问跟站点需要显示哪些资源。

因此,我们这里介绍下Api Root,它是干什么的呢?先简单理解下:在访问跟站点的时候,为我们列出当前有哪些资源。如果你还是不理解,那且看下面的操作。

1、撰写视图:

$ vim idc/views.py

from rest_framework.reverse import reverse

@api_view(["GET"])
def api_root(request, format=None, *args, **kwargs):
    return Response({
        "idcs": reverse("idc_list", request=request, format=format)
    })


代码说明:
1. reverse,drf封装好的方法,跟Django里的reverse功能一样;第一个参数:"idc_list"是路由里的Namespace名称,使用它的好处我想不用再说了。
2. 通过Response返回一个字典类型。

2、路由规则:

$ vim idc/urls.py

###########################  版本二  ##############################
from . import views
from rest_framework.urlpatterns import format_suffix_patterns
urlpatterns = [
    url("^$",views.api_root),
    url('^idcs/$', views.idc_list_v2, name='idc_list'),
    url('^idcs/(?P[0-9]+)/$', views.idc_detail_v2, name='idc_detail')
]
urlpatterns = format_suffix_patterns(urlpatterns)

代码说明:
1. 所有的urlpatterns通过drf里的方法format_suffix_patterns实例化后,再付给urlpatterns,达到能渲染跟站点路由的效果。

3、首页访问显示:

那有个问题,如果这个平台的app非常多,项目非常大,导致url也会非常多,这时候这个列表该如何维护呢?

只能一条条增加,且后期维护成本较大,这算是一个缺点。

版本三(基于类视图APIView类)

1、撰写视图:

$ vim idcs/views.py

###########################  版本三  ##############################
from rest_framework.views import APIView
from django.http import Http404

class IdcList(APIView):
    def get(self,request, format=None):
        queryset = Idc.objects.all()
        serializer = IdcSerializer(queryset, many=True)
        return Response(serializer.data)

    def post(self,request, format=None):
        serializer = IdcSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.data, status=status.HTTP_404_NOT_FOUND)


class IdcDetail(APIView):
    def get_object(self, pk):
        try:
            return Idc.objects.get(pk=pk)
        except Idc.DoesNotExist:
            raise Http404

    def get(self, request, pk, format=None):
        idc = self.get_object(pk)
        serializer = IdcSerializer(idc)
        return Response(serializer.data)

    def put(self,request, pk, format=None):
        idc = self.get_object(pk)
        serializer = IdcSerializer(idc, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_404_NOT_FOUND)

    def delete(self, request, pk, format=None):
        idc = self.get_object(pk)
        idc.delete()
        return HttpResponse(status=status.HTTP_204_NO_CONTENT)

代码说明:
1. 使用类视图,定义两个类并继承APIView类视图;
2. 在类视图里编写:增删改查方法,通过HttpResponse返回状态。

通过源码能看出,APIView是继承的Django View视图的。

2、路由规则:

$ vim idc/urls.py

###########################  版本三  ##############################
from . import views
from rest_framework.urlpatterns import format_suffix_patterns
urlpatterns = [
    url("^$",views.api_root),
    url('^idcs/$', views.IdcList.as_view(), name='idc_list'),
    url('^idcs/(?P[0-9]+)/$', views.IdcDetail.as_view(), name='idc_detail') #调用类视图
]
urlpatterns = format_suffix_patterns(urlpatterns)

版本四(使用混合 mixins)

这一版本的功能更为高级,使用mixins来实现,往下看!

1、撰写视图:

$ vim idcs/views.py

###########################  版本四(混合:mixins)  ##############################
from rest_framework import mixins, generics  # 导入相应模块

class IdcList_V4(generics.GenericAPIView,
                mixins.ListModelMixin,
                mixins.CreateModelMixin):   # 继承generics和mixins里的方法,称之为“混合”
    queryset = Idc.objects.all()                # 继承了generics,通过通过成员属性的形式传入参数
    serializer_class = IdcSerializer         # 继承了generics,通过通过成员属性的形式传入参数

    def get(self, request, *args, **kwargs):  # 第四版的 get和post方法还得自己写
        return self.list(request, *args, **kwargs)

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

class IdcDetail_V4(generics.GenericAPIView,
                  mixins.RetrieveModelMixin,
                  mixins.UpdateModelMixin,
                  mixins.DestroyModelMixin):   # detail类,继承mixins的检索、更新、删除等类方法
    queryset = Idc.objects.all()
    serializer_class = IdcSerializer

    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 delete(self, request, *args, **kwargs):  # 写好调用的方法
        return self.destroy(request, *args, **kwargs)


代码说明:
1. 为什么要继承mixins.RetrieveModelMixin,mixins.UpdateModelMixin,mixins.DestroyModelMixin等这一堆方法呢?
答:比如我们自定义写了delete方法,而这个方法会自动、只能的去调用mixins.DestroyModelMixin这个类里面的动作,这里给你看看DestroyModelMixin的源码就明白了。

class DestroyModelMixin(object):
    """
    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()        <==在这里,DestroyModelMixin指定执行了一个delete动作,这也就达到了,前端调用我们自定义写的delete方法,就相当于在调用这里。

在版本三中,queryset和serializer属性都是通过自己声明去使用的;版本四继承使用混合继承了generics,通过查看generics源码发现如下:

class GenericAPIView(views.APIView):
···
    queryset = None
    serializer_class = None
···

由上可看出,generics将queryset和serializer通过成员属性的形式抽离出来了,那我们只需要将这两个成员属性声明即可。

2、路由规则:

$ vim idc/urls.py

###########################  版本四  ##############################
from . import views
from rest_framework.urlpatterns import format_suffix_patterns
urlpatterns = [
    url("^$",views.api_root),
    url('^idcs/$', views.IdcList_V4.as_view(), name='idc_list'),
    url('^idcs/(?P[0-9]+)/$', views.IdcDetail_V4.as_view(), name='idc_detail')
]
urlpatterns = format_suffix_patterns(urlpatterns)

版本五(使用混合高级版)

1、撰写视图:

$ vim idcs/views.py

###########################  版本五(使用混合高级版)  ##############################

class IdcList_V5(generics.ListCreateAPIView):
    queryset = Idc.objects.all()
    serializer_class = IdcSerializer

class IdcDetail_V5(generics.RetrieveUpdateDestroyAPIView):
    queryset = Idc.objects.all()
    serializer_class = IdcSerializer

这几行代码搞定上面所有功能,只需要传入queryset和serializer即可,这什么原理呢?且看我来解释:

1、第四版本中,我们继承了generics.GenericAPIView类视图,自己写了两个get和post方法对吧,那generics的另一个方法把这两个事情也干了,我们只需要继承即可。

2、对的,这个方法就是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)

在上面源码里我们看到,ListCreateAPIView直接继承了mixins.ListModelMixin,mixins.CreateModelMixin和GenericAPIView视图,那我们直接用它就好了呀,哈哈,这就是第五版本的进化点。

3、继承generics.RetrieveUpdateDestroyAPIView的方法类似,因为它也封装好了get、put、patch(更新)和delete操作。

真是简单便捷,卧凑,下面还有更简单的,咱们一步步往下看。

2、路由规则:

$ vim idc/urls.py

###########################  版本五  ##############################
urlpatterns = [
    url("^$",views.api_root),
    url('^idcs/$', views.IdcList_V5.as_view(), name='idc_list'),
    url('^idcs/(?P[0-9]+)/$', views.IdcDetail_V5.as_view(), name='idc_detail')
]
urlpatterns = format_suffix_patterns(urlpatterns)

版本六(视图集 ViewSet)

1、撰写视图:

$ vim idcs/views.py

###########################  版本六(视图集 ViewSet)  ##############################
from rest_framework import viewsets

class IdcListViewset(viewsets.GenericViewSet,
                    mixins.ListModelMixin,
                    mixins.CreateModelMixin,
                    mixins.RetrieveModelMixin,
                    mixins.DestroyModelMixin,
                    mixins.UpdateModelMixin,):
    queryset = Idc.objects.all()
    serializer_class = IdcSerializer

第六版的视图进化点很明显就能看出来吧,在views里就写了一个内视图,之前的所有版本都是写一个List和一个Detail视图的。

IdcListViewset继承了viewsets.GenericViewSet,其他的方法都是mixins的,跟第五版本一样,这些方法又封装好了相对应的如更新、删除、查询操作。真是便捷呀!

值得一说的是viewsets.GenericViewSet继承了ViewSetMixin方法,从ViewSetMixin的源码里能看到可以改写as_view的信息,来达到定制路由的效果

ViewSetMixin的源码部分如下:

class ViewSetMixin(object):
    @classonlymethod
    def as_view(cls, actions=None, **initkwargs):
      ······
        if not actions:
            raise TypeError("The `actions` argument must be provided when "
                            "calling `.as_view()` on a ViewSet. For example "
                            "`.as_view({'get': 'list'})`")
      ······

能看到在actions字段可以设置{‘get’: ‘list’}这样的路由规则,那且看路由规则的写法

2、路由规则:
$ vim idc/urls.py

###########################  版本六  ##############################
# 定义了idc_list处理get和post两个路由请求
idc_list = views.IdcListViewset.as_view({
    "get": "list",   # 这里的“list”对应IdcListViewset里继承的mixins.ListModelMixin,而且post和下面的put等方法,也是需要一一对应
    "post": "create"
})

# 定义了idc_detail处理get和put和delete请求
idc_detail = views.IdcListViewset.as_view({
    "get": "retrieve",
    "put": "update",
    "delete": "destroy"
})

urlpatterns = [
    url("^$",views.api_root),
    url('^idcs/$', idc_list, name='idc_list'),  #<==这里使用上面的定义即可
    url('^idcs/(?P[0-9]+)/$', idc_detail, name='idc_detail')
]
urlpatterns = format_suffix_patterns(urlpatterns)

版本七 (终极大法:写项目选用此法)

1、撰写视图:
$ vim idcs/views.py

###########################  版本七  ##############################
from rest_framework import viewsets

class IdcViewset_V7(viewsets.ModelViewSet):
    queryset = Idc.objects.all()
    serializer_class = IdcSerializer

根据版本五和版本六的继承套路,版本七直接继承了一个巨无霸(ModelViewSet),这个巨无霸将所有的功能都封装到一块。相当于把我们从第一版到第六版写的所有事情都干了,按照老规矩,我们来看看它的源码:

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

从源码中能看出,ModelViewSet所继承的视图类,我们在前面几个版本中都重点继承并介绍过。所以,你知道它的原理了吧!

2、路由规则:

$ vim idc/urls.py

###########################  版本七  ##############################
from rest_framework.routers import DefaultRouter

route = DefaultRouter()
route.register("idcs", views.IdcViewset_V7)
urlpatterns = [
    url(r'^', include(route.urls))
]

最终版的规则使用了drf的DefaultRouter函数,通过实例化DefaultRouter得到route对象,使用route.register()你的app路由,有多个注册多个即可,使用也很简单。

你可能感兴趣的:(Django-DRF-视图的演变(二))