rest-framework框架——视图三部曲
一、mixins類編寫視圖
1、配置url
urlpatterns = [
...
re_path(r'^authors/$', views.AuthorView.as_view(), name="author"),
re_path(r'^authors/(?P<pk>d+)/$', views.AuthorDetailView.as_view(), name="detail_author")
]
2、編寫Author的序列化類
/app01/serializer.py:
class AuthorModelSerializers(serializers.ModelSerializer):
class Meta:
model = Author
fields = "__all__"
3、編寫Author的視圖
# Author
from rest_framework import mixins, generics
class AuthorView(mixins.ListModelMixin, # 擴展了列出查詢集功能
mixins.CreateModelMixin, # 擴展了創建和保存新模型實例功能
generics.GenericAPIView): # 繼承擴展了REST框架的APIView類,為標準列表和詳細視圖添加了常見的行為
queryset = Author.objects.all() # 配置queryset:告知這個類這次處理的數據
serializer_class = AuthorModelSerializers # 告知處理用到的序列化組件
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
class AuthorDetailView(mixins.RetrieveModelMixin, # 擴展在響應中實現返回現有模型實例功能(獲取單條數據)
mixins.DestroyModelMixin, # 擴展現有模型實例的刪除功能
mixins.UpdateModelMixin, # 擴展更新和保存現有模型實例功能
generics.GenericAPIView):
queryset = Author.objects.all() # 配置queryset:告知這個類這次處理的數據
serializer_class = AuthorModelSerializers # 告知處理用到的序列化組件
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)queryset和serializer_class變量
這兩個變量是必須的。queryset告知這個類這次處理的數據。serializer_class告知這個類數據處理用到的序列化組件。
(2)五類mixins作用和對應的http方法
(3)GenericAPIView
這個類擴展了REST框架的APIView類,為標準列表和詳細視圖添加了常見的行為。
提供的每個具體的通用視圖都是通過把GenericAPIView與一個或多個mixin類進行組合來構建的。
(4)測試驗證
二、基于mixins封裝視圖示例
1、基本視圖
# 方式三:基于rest_framework框架實現序列化(pip install djangorestframework)
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializers import BookSerializer # 自定義序列化類
class BookView(APIView):
query_set = Book.objects.all() # 將query_set抽離
serializer_class = BookSerializer # 拿到序列化器
def get(self, request):
# 第一個圖書對象
# book_obj = Book.objects.first()
# ret = BookSerializer(book_obj)
# book_list = Book.objects.all()
book_list = self.query_set
# ret = BookSerializer(book_list, many=True) # 使用序列化器序列化
ret = self.serializer_class(book_list, many=True)
"""
序列化的數據保存在ret.data中
"""
return Response(ret.data)
"""
得出來的結果會使用Django REST framework模板,在serializers.py中定制好序列化類后,顯示效果如下所示:
HTTP 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
[
{
"id": 1,
"title": "python開發",
"category": "Python",
"pub_time": "2011-08-27",
"publisher": {
"id": 1,
"title": "人民日報社"
},
"author": [
{
"id": 1,
"name": "xxx"
},
{
"id": 2,
"name": "sssxx"
}
]
},
...
]
"""
def post(self, request):
print(request.data)
serializer = BookSerializer(data=request.data) # 序列化器校驗前端傳回來的數據
if serializer.is_valid():
serializer.save() # 驗證成功后保存數據庫
# 因為ModelSerializer的create方法不支持source的用法。因此必須還自定義一個create方法。
return Response(serializer.validated_data) # validated_data存放驗證通過的數據
else:
return Response(serializer.errors) # errors存放錯誤信息
'''
發送post請求接口設計
POST /books/list
{
"title": "nodejs的使用教程",
"w_category": "1",
"pub_time": "2018-10-27",
"publisher_id": 1,
"author_list": [1,2,3]
}
'''
class BookEditView(APIView):
def get(self, request, id):
"""查看單條數據"""
book_obj = Book.objects.filter(id=id).first()
ret = BookSerializer(book_obj)
return Response(ret.data)
'''
GET /books/retrieve/3
{
"id": 3,
"title": "Linux開發",
"category": "Linux",
"pub_time": "2008-08-27",
"publisher": {
"id": 3,
"title": "長江日報社"
},
"author": [
{
"id": 1,
"name": "阿薩德"
},
{
"id": 3,
"name": "阿斯達"
}
]
}
'''
def put(self, request, id):
"""更新操作"""
book_obj = Book.objects.filter(id=id).first()
serializer = BookSerializer(
book_obj, # 待更新對象
data=request.data, # 要更新的數據
partial=True # 重點:進行部分驗證和更新
)
if serializer.is_valid():
serializer.save() # 保存
return Response(serializer.validated_data) # 返回驗證通過的數據
# return Response(serializers.data) # 返回所有數據
else:
return Response(serializer.errors) # 返回驗證錯誤的數據
def delete(self, request, id):
"""刪除操作"""
book_obj = Book.objects.filter(id=id).first()
book_obj.delete()
return Response("")
2、基于mixins封裝視圖
升級:改寫使用通用類,繼承通用方法。
class GenericAPIView(APIView):
# 通用APIView模板類
query_set = None
serializer_class = None
def get_queryset(self):
return self.query_set
def get_serializer(self, *args, **kwargs):
return self.serializer_class(*args, **kwargs) # 實例化,且接收所有的參數
class BookView(GenericAPIView):
# 以方法的形式調用獲取
query_set = Book.objects.all()
serializer_class = BookSerializer
def get(self, request):
book_list = self.get_queryset()
ret = self.get_serializer(book_list, many=True)
return Response(ret.data)
def post(self, request):
serializer = BookSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
else:
return Response(serializer.errors)
3、第一次封裝
class GenericAPIView(APIView):
# 通用APIView模板類
query_set = None
serializer_class = None
def get_queryset(self):
return self.query_set
def get_serializer(self, *args, **kwargs):
return self.serializer_class(*args, **kwargs) # 實例化,且接收所有的參數
class ListModelMixin(object):
def list(self, request):
queryset = self.get_queryset()
ret = self.get_serializer(queryset, many=True)
return Response(ret.data)
class CreateModelMixin(object):
def create(self, request):
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
else:
return Response(serializer.errors)
class RetrieveModelMixin(object):
def retrieve(self, request, id): # 查看單條數據
book_obj = self.get_queryset().filter(id=id).first()
ret = self.get_serializer(book_obj)
return Response(ret.data)
class UpdateModelMixin(object):
def update(self, request, id):
book_obj = self.get_queryset().filter(id=id).first()
serializer = self.get_serializer(
book_obj, # 待更新對象
data=request.data, # 要更新的數據
partial=True # 重點:進行部分驗證和更新
)
if serializer.is_valid():
serializer.save() # 保存
return Response(serializer.validated_data) # 返回驗證通過的數據
# return Response(serializers.data) # 返回所有數據
else:
return Response(serializer.errors) # 返回驗證錯誤的數據
class DestroyModelMixin(object):
def delete(self, request, id):
book_obj = self.get_queryset().filter(id=id).first()
book_obj.delete()
return Response("")
class BookView(GenericAPIView, ListModelMixin, CreateModelMixin): # 一層封裝
query_set = Book.objects.all()
serializer_class = BookSerializer
def get(self, request):
return self.list(request)
def post(self, request):
return self.create(request)
class BookEditView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin): # 一層封裝
query_set = Book.objects.all()
serializer_class = BookSerializer
def get(self, request, id):
"""查看單條數據"""
return self.retrieve(request, id)
def put(self, request, id):
"""更新操作"""
return self.update(request, id)
def delete(self, request, id):
"""刪除操作"""
return self.destroy(request, id)
4、第二次封裝
進一步封裝如下所示:
# 二層封裝
class ListCreateAPIView(GenericAPIView, ListModelMixin, CreateModelMixin):
pass
class RetrieveUpdateDestroyAPIView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
pass
# class BookView(GenericAPIView, ListModelMixin, CreateModelMixin): # 一層封裝
class BookView(ListCreateAPIView): # 使用二層封裝
query_set = Book.objects.all()
serializer_class = BookSerializer
def get(self, request):
return self.list(request)
def post(self, request):
return self.create(request)
# class BookEditView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin): # 一層封裝
class BookEditView(RetrieveUpdateDestroyAPIView): # 使用二層封裝
query_set = Book.objects.all()
serializer_class = BookSerializer
def get(self, request, id):
"""查看單條數據"""
return self.retrieve(request, id)
5、第三次封裝
經過兩次封裝后,剩下兩個視圖,但依然不夠簡潔。由于兩個get請求的返回值不同,寫了兩個視圖來解決。
現在可以通過路由傳參的方式來解決上述問題。
(1)改寫urls.py
from django.urls import path, include
from .views import BookView, BookEditView, BookModelViewSet
urlpatterns = [
# path('list', BookView.as_view()), # 查看所有的圖書
# 注意url中參數命名方式,2.0之前的寫法:'retrieve/(?P<id>d+)'
# 2.0之后的寫法:<>內聲明類型,冒號后面跟著關鍵字參數
# path('retrieve/<int:id>', BookEditView.as_view()) # 單條數據查看
path('list', BookModelViewSet.as_view({"get": "list", "post": "create"})),
path('retrieve/<int:id>', BookModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}))
]
(2)使as_view支持傳參
as_view方法本身是不支持傳參的,因此需要重寫該方法。
class ViewSetMixin(object):
def as_view(self):
"""
按照參數指定的去匹配
get-->list
:return:
"""
pass
class BookModelViewSet(ViewSetMixin, GenericAPIView, ListModelMixin, CreateModelMixin,
RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
pass
但是我們不用自己去定義該方法,可以直接使用框架已經提供好的ViewSetMixin:
from rest_framework.viewsets import ViewSetMixin
它做的主要事情就是重寫as_view方法:
class ViewSetMixin:
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
def view(request, *args, **kwargs):
self = cls(**initkwargs)
# We also store the mapping of request methods to actions,
# so that we can later set the action attribute.
# eg. `self.action = 'list'` on an incoming GET request.
self.action_map = actions
# Bind methods to actions
# This is the bit that's different to a standard view
# 這里actions是前面url中提供的字典參數{"get": "list", "post": "create"}
for method, action in actions.items():
# 循環得到的method是get,action是list
handler = getattr(self, action) # self是自己的視圖類
setattr(self, method, handler) # 在自己的視圖類中找list方法
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
# 做分發:get-->self.get;post-->self.post
return self.dispatch(request, *args, **kwargs)
由于self.get已經等于self.list,分發get對應的是list。
(3)視圖改寫
from rest_framework.viewsets import ViewSetMixin
class BookModelViewSet(ViewSetMixin, GenericAPIView, ListModelMixin, CreateModelMixin,
RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
query_set = Book.objects.all()
serializer_class = BookSerializer
由于get去匹配時已經自動對應到了self.list方法。因此不再自定義視圖方法。
(4)簡化繼承
from rest_framework.viewsets import ViewSetMixin
class ModelViewSet(ViewSetMixin, GenericAPIView, ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
pass
class BookModelViewSet(ModelViewSet):
query_set = Book.objects.all()
serializer_class = BookSerializer
三、Mixins源碼分析
1、CreateModelMixin
class CreateModelMixin(object):
"""Create a model instance ==>創建一個實例"""
def create(self, request, *args, **kwargs):
# 獲取相關serializer
serializer = self.get_serializer(data=request.data)
# 進行serializer的驗證;raise_exception=True,一旦驗證不通過,不再往下執行,直接引發異常
serializer.is_valid(raise_exception=True)
# 調用perform_create()方法,保存實例
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def perform_create(self, serializer):
# 保存實例
serializer.save()
def get_success_headers(self, data):
try:
return {'Location': str(data[api_settings.URL_FIELD_NAME])}
except (TypeError, KeyError):
return {}
注意:
(1)perform_create( )對serializer直接進行save保存,當在一些情境下,需要對perform_create( )進行重寫。
(2)這個類的運行流程如下所示:
2、ListModelMixin
class ListModelMixin(object):
"""List a queryset.==> 列表頁獲取"""
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
# 這是一個分頁功能,如果在viewset中設置了pagination_class,那么這里就會起作用
# 獲取當前頁的queryset,如果不存在分頁,返回None
page = self.paginate_queryset(queryset)
if page is not None:
# 分頁不為空,那么不能簡單的執行Response(serializer.data)
# 還需要將相關的page信息序列化在進行響應
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
ListModelMixin一般用來獲取列表頁,不需要重寫方法。
3、RetriveModelMixin
class RetrieveModelMixin(object):
"""
Retrieve a model instance.==> 獲取某一個對象的具體信息
"""
def retrieve(self, request, *args, **kwargs):
# 一般訪問的url都為/obj/id/這種新式
# get_object()可以獲取到這個id的對象
# 注意在viewset中設置lookup_field獲取重寫get_object()方法可以指定id具體對象是什么~!
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response(serializer.data)
4、DestoryModelMixin
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()
5、總結
這一章簡要分析了源碼的內容以及各個mixins的邏輯,最重要的還是學會重寫它們相關的方法。一般情況下,當我們在操作某一個model的時候,涉及到另外一個model中數據的修改,那么就需要對這個mixins下執行save的邏輯的方法進行重寫。
四、使用通用的基于類的視圖
通過使用mixin類,使用更少的代碼重寫了這些視圖,但我們還可以再進一步。REST框架提供了一組已經混合好(mixed-in)的通用視圖,我們可以使用它來簡化我們的views.py模塊。
from rest_framework import mixins, generics
class AuthorView(generics.ListCreateAPIView):
queryset = Author.objects.all() # 配置queryset:告知這個類這次處理的數據
serializer_class = AuthorModelSerializers # 告知處理用到的序列化組件
class AuthorDetailView(generics.RetrieveUpdateDestroyAPIView):
queryset = Author.objects.all() # 配置queryset:告知這個類這次處理的數據
serializer_class = AuthorModelSerializers # 告知處理用到的序列化組件
1、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)
可以看到源碼中將Mixins混合類和getpost函數都封裝進去了。
2、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)
其他RetrieveDestroyAPIView、RetrieveUpdateAPIView、UpdateAPIView、DestroyAPIView等封裝方式完全類似。
五、運用viewsets.ModelViewSet的視圖
1、改寫urls.py
from django.contrib import admin
from django.urls import path, re_path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
'''代碼省略'''
# re_path(r'^authors/$', views.AuthorView.as_view(), name="author"),
# re_path(r'^authors/(?P<pk>d+)/$', views.AuthorDetailView.as_view(), name="detail_author"),
# as_view參數指定什么請求走什么方法
re_path(r'^authors/$', views.AuthorViewSet.as_view({"get": "list", "post": "create"}), name="author_list"),
re_path(r'^authors/(?P<pk>d+)/$', views.AuthorViewSet.as_view({
'get': 'retrieve',
'put': 'update',
'patch': 'partial_update',
'delete': 'destroy'
}), name="author_detail"),
]
注意:
(1)as_view參數
利用參數來指定什么請求方式由哪一個內部方法來執行。尤其注意兩種不同get請求用不同的方法來處理。
2、引入viewsets改寫視圖
from rest_framework import viewsets
class AuthorViewSet(viewsets.ModelViewSet):
queryset = Author.objects.all() # 配置queryset:告知這個類這次處理的數據
serializer_class = AuthorModelSerializers # 告知處理用到的序列化組件
五、ModelViewSet源碼分析
1、查看viewsets.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繼承了所有五個混合類。還繼承了GenericViewSet類。
2、查看GenericViewSet源碼
class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
pass
可以看到GenericViewSet繼承了前面學的generics.GenericAPIView類,這個類擴展了REST框架的APIView類,但是它并沒有改寫dispatch方法,因此url中可以添加參數與它無關。
GenericViewSet還繼承了ViewSetMixin類。
3、查看分析ViewSetMixin源碼
class ViewSetMixin(object):
"""
Overrides `.as_view()` so that it takes an `actions` keyword that performs
the binding of HTTP methods to actions on the Resource.
覆蓋as_view方法需要接收一個actions參數來實現將HTTP方法綁定到資源。
For example, to create a concrete view binding the 'GET' and 'POST' methods
to the 'list' and 'create' actions...
舉例來說,如果要創建一個具體視圖綁定'GET'和'POST'方法到'list'和'create'操作。可寫為如下格式:
view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
"""
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
"""
Because of the way class based views create a closure around the
instantiated view, we need to totally reimplement `.as_view`,
and slightly modify the view function that is created and returned.
"""
# actions must not be empty actions來接收{'get': 'list', 'post': 'create'}
if not actions: # 為空則報錯
raise TypeError("The `actions` argument must be provided when "
"calling `.as_view()` on a ViewSet. For example "
"`.as_view({'get': 'list'})`")
# sanitize keyword arguments
for key in initkwargs:..... # 這里的值是空的
def view(request, *args, **kwargs):...
return csrf_exempt(view) # 執行as_view 最終返回的還是view函數
注意:
(1)分析函數傳參方式:**字典傳參等同于關鍵字傳參。
def foo(action=None, **kwargs):
print(action)
print(kwargs)
foo({"a": 1}, b=2, c=3)
"""
{'a': 1}
{'b': 2, 'c': 3}
"""
foo(a=2, b=3)
"""
None
{'a': 2, 'b': 3}
"""
foo(**{"a": 1, "b": 3})
"""
None
{'a': 1, 'b': 3}
"""
foo({"a": 1, "b": 3})
"""
{'a': 1, 'b': 3}
{}
"""
由此可見as_view傳遞的參數{"get": "list", "post": "create"}是ViewSetMixin內改寫的as_view方法,由actions參數來接收的。
由于acitons默認值是None,因此not None其實是True,if not actions: 其實是actions為空則報錯的意思。
(2)as_view函數最終返回值是view函數
def view(request, *args, **kwargs):
self = cls(**initkwargs)
# We also store the mapping of request methods to actions,
# so that we can later set the action attribute.
# eg. `self.action = 'list'` on an incoming GET request.
self.action_map = actions # 傳入的字典數據{'get': 'list', 'post': 'create'}
# Bind methods to actions
# This is the bit that's different to a standard view
for method, action in actions.items(): # method:請求方式 action:實例方法
handler = getattr(self, action) # 反射得到self.list self.create方法
setattr(self, method, handler) # 給請求方式設置對應的實例方法
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函數中用self.action_map = actions 來接收傳入的字典數據{'get': 'list', 'post': 'create'}。
循環for method, action in actions.items(): 拿到method:請求方式和action:實例方法。
再通過反射方法:handler = getattr(self, action) 得到self.list self.create方法。
最后通過反射方法setattr(self, method, handler) 給請求方式設置對應的實例方法:以后再找getattr(self, "get")的時候找到是self.list;找getattr(self, "post")的時候找到是self.create;
(3)反射方法詳解
1. getattr()函數是Python自省的核心函數,具體使用大體如下: class A: def __init__(self): self.name = 'zhangjing' #self.age='24' def method(self): print"method print" Instance = A() print getattr(Instance , 'name, 'not find') #如果Instance 對象中有屬性name則打印self.name的值,否則打印'not find' print getattr(Instance , 'age', 'not find') #如果Instance 對象中有屬性age則打印self.age的值,否則打印'not find' print getattr(a, 'method', 'default') #如果有方法method,否則打印其地址,否則打印default print getattr(a, 'method', 'default')() #如果有方法method,運行函數并打印None否則打印default 2. hasattr(object, name) 說明:判斷對象object是否包含名為name的特性(hasattr是通過調用getattr(ojbect, name)是否拋出異常來實現的) 3. setattr(object, name, value) 這是相對應的getattr()。參數是一個對象,一個字符串和一個任意值。字符串可能會列出一個現有的屬性或一個新的屬性。這個函數將值賦給屬性的。該對象允許它提供。例如,setattr(x,“foobar”,123)相當于x.foobar = 123。 4. delattr(object, name) 與setattr()相關的一組函數。參數是由一個對象(記住python中一切皆是對象)和一個字符串組成的。string參數必須是對象屬性名之一。該函數刪除該obj的一個由string指定的屬性。delattr(x, 'foobar')=del x.foobar
4、找到view函數執行完后最終返回dispatch方法 并完成分析
從ModelViewSet——》GenericViewSet——》GenericAPIView——》APIView一路回溯到APIView才找到dispatch方法。
def dispatch(self, request, *args, **kwargs):
self.args = args
self.kwargs = kwargs
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
self.initial(request, *args, **kwargs)
# Get the appropriate handler method
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
response = handler(request, *args, **kwargs)
except Exception as exc:
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
當訪問是走的路由是re_path(r'^authors/$', views.AuthorViewSet.as_view({"get": "list", "post": "create"}), name="author_list"),get請求handler對應的是self.list。
而當訪問走的路由是re_path(r'^authors/(?P<pk>d+)/$', views.AuthorViewSet.as_view({'get': 'retrieve',....
此時get請求handler對應的是self.retrieve。
如此就實現了兩個類合成一個類。
5、找到并執行list方法
查看ModelViewSet源碼:
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
在ListModelMixin找到list方法:
class ListModelMixin(object):
"""
List a queryset.
"""
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
六、視圖組件總結
1、DRF所有視圖文件
所有的視圖類在如下這四個文件內:
from rest_framework import views # APIView from rest_framework import generics # 公共通用視圖類:GenericAPIView,及各種組合視圖類CreateAPIView、ListAPIView、RetrieveAPIView等 from rest_framework import mixins # 混合繼承類:CreateModelMixin、ListModelMixin、RetrieveModelMixin、UpdateModelMixin、DestroyModelMixin from rest_framework import viewsets # 重寫as_view: ViewSetMixin;其他類都是幫助去繼承ViewSetMixin
2、DRF視圖圖譜
視圖類繼承順序,如Django REST Framework視圖圖譜所示:
首先 django是繼承 view的,DRF是從APIView開始繼承起,APIView封裝了request,其中包含了data、query_params等屬性、方法。
然后 GenericAPIView封裝了 get_queryset() 和 get_serializer();ViewSetMixin重寫了 as_view()方法。
隨后 GenericViewSet幫忙繼承GenericAPIView和ViewSetMixin。
最后最高層的封裝是 ModelViewSet。
總結
以上是生活随笔為你收集整理的rest-framework框架——视图三部曲的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SAP Spartacus focus
- 下一篇: k8s ingres 的安装与使用