前言:本文是“《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路由,有多个注册多个即可,使用也很简单。