前言:本文是“《Django-DRF-序列化模型實(shí)戰(zhàn)(一)》”的下篇没炒,其中示例所用的內(nèi)容也繼承了它背捌,這篇文章是比較高階的內(nèi)容,適合有一定基礎(chǔ)的同學(xué)查閱构韵。
視圖的演變
版本一(底層方法)
這種方法只需要看得懂即可莱衩,因?yàn)樘讓恿司粜幔竺鎸懘a不會(huì)用這種方式
1、撰寫視圖:
寫一個(gè)視圖笨蚁,支持:GET all睹晒、GET one、POST括细、PUT伪很、DELETE這五個(gè)操作。
$ 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)
# 代碼說(shuō)明:
1. JSONResponse改寫了Django里默認(rèn)的,目的是處理POST提交的數(shù)據(jù),將數(shù)據(jù)轉(zhuǎn)換成JSON后方便處理玻募。
2. idc_list可以通過(guò)HttpResponse和改寫的JSONResponse兩種方法來(lái)返回給前端。
3. idc_detail函數(shù)設(shè)計(jì)接受一個(gè)pk呆盖,這個(gè)pk是一個(gè)id,通過(guò)查詢到ID后贷笛,進(jìn)行GET应又、PUT、DELETE操作乏苦。
2株扛、路由規(guī)則:
$ 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<pk>[0-9]+)/$', idc_detail, name='idc_detail')
]
3、請(qǐng)求(獲取所有數(shù)據(jù),創(chuàng)建一個(gè)數(shù)據(jù),使用pk指定獲取一個(gè)資源信息,更新一個(gè)資源的信息,刪除一個(gè)資源):
可以使用Postman工具來(lái)進(jìn)行Http請(qǐng)求的提交操作汇荐,每一次提交請(qǐng)求后席里,該工具都會(huì)有Status字段顯示請(qǐng)求的狀態(tài)碼,比較方便拢驾。
版本二(基于函數(shù)視圖的@api_view裝飾器)
1、撰寫視圖:
$ vim idcs/views.py
########################### 版本二 ##############################
from rest_framework.decorators import api_view #導(dǎo)入api_view
from rest_framework import status # 導(dǎo)入status改基,返回狀態(tài)使用這個(gè)模塊
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)
# 代碼說(shuō)明:
1. @api_view()繁疤,傳入一個(gè)列表作為參數(shù),這個(gè)列表里寫入具體的方法秕狰,如["GET", "PUT", "DELETE"]稠腊。api_view之會(huì)允許你寫入的Http方法進(jìn)行交互。沒(méi)有寫的就會(huì)禁止交互鸣哀。
2. status.HTTP_404_NOT_FOUND架忌,status模塊可以返回你想要給前端的狀態(tài)。
3. Response我衬,這是drf給我們封裝好的方法叹放,它會(huì)將模板和數(shù)據(jù)一并返回給前端饰恕,所以你在前端能看見drf的頁(yè)面了。
2井仰、路由規(guī)則:
$ 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<pk>[0-9]+)/$', views.idc_detail_v2)
]
直接通過(guò)瀏覽器訪問(wèn)的話埋嵌,就能看到Response渲染給前端的頁(yè)面。
Api Root
截至目前俱恶,你在訪問(wèn)跟站點(diǎn)的時(shí)候雹嗦,應(yīng)該是會(huì)出錯(cuò)的,因?yàn)槲覀儧](méi)有定義訪問(wèn)跟站點(diǎn)需要顯示哪些資源合是。
因此了罪,我們這里介紹下Api Root,它是干什么的呢聪全?先簡(jiǎn)單理解下:在訪問(wèn)跟站點(diǎn)的時(shí)候泊藕,為我們列出當(dāng)前有哪些資源。如果你還是不理解荔烧,那且看下面的操作吱七。
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)
})
代碼說(shuō)明:
1. reverse鹤竭,drf封裝好的方法踊餐,跟Django里的reverse功能一樣;第一個(gè)參數(shù):"idc_list"是路由里的Namespace名稱臀稚,使用它的好處我想不用再說(shuō)了吝岭。
2. 通過(guò)Response返回一個(gè)字典類型。
2吧寺、路由規(guī)則:
$ 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<pk>[0-9]+)/$', views.idc_detail_v2, name='idc_detail')
]
urlpatterns = format_suffix_patterns(urlpatterns)
代碼說(shuō)明:
1. 所有的urlpatterns通過(guò)drf里的方法format_suffix_patterns實(shí)例化后窜管,再付給urlpatterns,達(dá)到能渲染跟站點(diǎn)路由的效果稚机。
3幕帆、首頁(yè)訪問(wèn)顯示:
那有個(gè)問(wèn)題,如果這個(gè)平臺(tái)的app非常多赖条,項(xiàng)目非常大失乾,導(dǎo)致url也會(huì)非常多,這時(shí)候這個(gè)列表該如何維護(hù)呢纬乍?
只能一條條增加碱茁,且后期維護(hù)成本較大,這算是一個(gè)缺點(diǎn)仿贬。
版本三(基于類視圖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)
代碼說(shuō)明:
1. 使用類視圖,定義兩個(gè)類并繼承APIView類視圖;
2. 在類視圖里編寫:增刪改查方法蜓氨,通過(guò)HttpResponse返回狀態(tài)聋袋。
通過(guò)源碼能看出,APIView是繼承的Django View視圖的语盈。
2舱馅、路由規(guī)則:
$ 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<pk>[0-9]+)/$', views.IdcDetail.as_view(), name='idc_detail') #調(diào)用類視圖
]
urlpatterns = format_suffix_patterns(urlpatterns)
版本四(使用混合 mixins)
這一版本的功能更為高級(jí),使用mixins來(lái)實(shí)現(xiàn)刀荒,往下看代嗤!
1、撰寫視圖:
$ vim idcs/views.py
########################### 版本四(混合:mixins) ##############################
from rest_framework import mixins, generics # 導(dǎo)入相應(yīng)模塊
class IdcList_V4(generics.GenericAPIView,
mixins.ListModelMixin,
mixins.CreateModelMixin): # 繼承g(shù)enerics和mixins里的方法缠借,稱之為“混合”
queryset = Idc.objects.all() # 繼承了generics干毅,通過(guò)通過(guò)成員屬性的形式傳入?yún)?shù)
serializer_class = IdcSerializer # 繼承了generics,通過(guò)通過(guò)成員屬性的形式傳入?yún)?shù)
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): # 寫好調(diào)用的方法
return self.destroy(request, *args, **kwargs)
代碼說(shuō)明:
1. 為什么要繼承mixins.RetrieveModelMixin,mixins.UpdateModelMixin,mixins.DestroyModelMixin等這一堆方法呢绅喉?
答:比如我們自定義寫了delete方法渠鸽,而這個(gè)方法會(huì)自動(dòng)、只能的去調(diào)用mixins.DestroyModelMixin這個(gè)類里面的動(dòng)作柴罐,這里給你看看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指定執(zhí)行了一個(gè)delete動(dòng)作革屠,這也就達(dá)到了凿试,前端調(diào)用我們自定義寫的delete方法,就相當(dāng)于在調(diào)用這里似芝。
在版本三中那婉,queryset和serializer屬性都是通過(guò)自己聲明去使用的;版本四繼承使用混合繼承了generics党瓮,通過(guò)查看generics源碼發(fā)現(xiàn)如下:
class GenericAPIView(views.APIView):
···
queryset = None
serializer_class = None
···
由上可看出详炬,generics將queryset和serializer通過(guò)成員屬性的形式抽離出來(lái)了,那我們只需要將這兩個(gè)成員屬性聲明即可寞奸。
2痕寓、路由規(guī)則:
$ 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<pk>[0-9]+)/$', views.IdcDetail_V4.as_view(), name='idc_detail')
]
urlpatterns = format_suffix_patterns(urlpatterns)
版本五(使用混合高級(jí)版)
1、撰寫視圖:
$ vim idcs/views.py
########################### 版本五(使用混合高級(jí)版) ##############################
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即可,這什么原理呢硬毕?且看我來(lái)解釋:
1呻引、第四版本中,我們繼承了generics.GenericAPIView類視圖吐咳,自己寫了兩個(gè)get和post方法對(duì)吧逻悠,那generics的另一個(gè)方法把這兩個(gè)事情也干了元践,我們只需要繼承即可。
2童谒、對(duì)的单旁,這個(gè)方法就是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視圖,那我們直接用它就好了呀琅豆,哈哈愉豺,這就是第五版本的進(jìn)化點(diǎn)。
3茫因、繼承g(shù)enerics.RetrieveUpdateDestroyAPIView的方法類似蚪拦,因?yàn)樗卜庋b好了get、put冻押、patch(更新)和delete操作驰贷。
真是簡(jiǎn)單便捷,臥湊洛巢,下面還有更簡(jiǎn)單的括袒,咱們一步步往下看。
2狼渊、路由規(guī)則:
$ vim idc/urls.py
########################### 版本五 ##############################
urlpatterns = [
url("^$",views.api_root),
url('^idcs/$', views.IdcList_V5.as_view(), name='idc_list'),
url('^idcs/(?P<pk>[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
第六版的視圖進(jìn)化點(diǎn)很明顯就能看出來(lái)吧,在views里就寫了一個(gè)內(nèi)視圖狈邑,之前的所有版本都是寫一個(gè)List和一個(gè)Detail視圖的城须。
IdcListViewset繼承了viewsets.GenericViewSet,其他的方法都是mixins的米苹,跟第五版本一樣糕伐,這些方法又封裝好了相對(duì)應(yīng)的如更新、刪除蘸嘶、查詢操作良瞧。真是便捷呀!
值得一說(shuō)的是viewsets.GenericViewSet繼承了ViewSetMixin方法训唱,從ViewSetMixin的源碼里能看到可以改寫as_view的信息褥蚯,來(lái)達(dá)到定制路由的效果
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字段可以設(shè)置{‘get’: ‘list’}這樣的路由規(guī)則,那且看路由規(guī)則的寫法
2况增、路由規(guī)則:
$ vim idc/urls.py
########################### 版本六 ##############################
# 定義了idc_list處理get和post兩個(gè)路由請(qǐng)求
idc_list = views.IdcListViewset.as_view({
"get": "list", # 這里的“l(fā)ist”對(duì)應(yīng)IdcListViewset里繼承的mixins.ListModelMixin赞庶,而且post和下面的put等方法,也是需要一一對(duì)應(yīng)
"post": "create"
})
# 定義了idc_detail處理get和put和delete請(qǐng)求
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<pk>[0-9]+)/$', idc_detail, name='idc_detail')
]
urlpatterns = format_suffix_patterns(urlpatterns)
版本七 (終極大法:寫項(xiàng)目選用此法)
1、撰寫視圖:
$ vim idcs/views.py
########################### 版本七 ##############################
from rest_framework import viewsets
class IdcViewset_V7(viewsets.ModelViewSet):
queryset = Idc.objects.all()
serializer_class = IdcSerializer
根據(jù)版本五和版本六的繼承套路歧强,版本七直接繼承了一個(gè)巨無(wú)霸(ModelViewSet)澜薄,這個(gè)巨無(wú)霸將所有的功能都封裝到一塊。相當(dāng)于把我們從第一版到第六版寫的所有事情都干了摊册,按照老規(guī)矩肤京,我們來(lái)看看它的源碼:
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所繼承的視圖類茅特,我們?cè)谇懊鎺讉€(gè)版本中都重點(diǎn)繼承并介紹過(guò)忘分。所以,你知道它的原理了吧温治!
2饭庞、路由規(guī)則:
$ vim idc/urls.py
########################### 版本七 ##############################
from rest_framework.routers import DefaultRouter
route = DefaultRouter()
route.register("idcs", views.IdcViewset_V7)
urlpatterns = [
url(r'^', include(route.urls))
]
最終版的規(guī)則使用了drf的DefaultRouter函數(shù),通過(guò)實(shí)例化DefaultRouter得到route對(duì)象熬荆,使用route.register()你的app路由舟山,有多個(gè)注冊(cè)多個(gè)即可,使用也很簡(jiǎn)單卤恳。