文章目錄
一、Generics:工具視圖
generics 是工具視圖,可以實現極簡化接口編寫操作。
工具視圖都是 GenericAPIView 的子類,不同的子類繼承不同工具類,重寫請求方法。
1.羣查與單增:ListCreateAPIView
查看源碼
# 繼承了視圖基類 GenericAPIView,工具類 ListModelMixin,CreateModelMixin實現羣查和單增
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)
代碼實現
urls.py
from django.conf.urls import url, include
from api import views
urlpatterns = [
url(r'^v4/books/$', views.BookListCreateView.as_view()),
url(r'^v4/books/(?P<pk>.*)/$', views.BookListCreateView.as_view()),
]
工具視圖的功能如果滿足需求,只需要繼承工具視圖,添加 queryset,serializer_class
views.py
class BookListCreateView(ListCreateAPIView):
queryset = models.Book.objects.filter(is_delete=False)
serializer_class = serializers.BookModelSerializer
測試接口
羣查
單增
入庫成功
其他方法
根據上述源碼分析與示例和下面的圖示,我們可以很容易知道這些類的功能和用法
2.添加其他接口
代碼實現
# 需要什麼接口,直接繼承就行
# 比如我們在羣查,單增的基礎上,添加單改接口
class BookListCreateView(ListCreateAPIView, UpdateAPIView):
queryset = models.Book.objects.filter(is_delete=False)
serializer_class = serializers.BookModelSerializer
接口測試
修改成功
3.後續問題
但是上述雖然可以實現簡單接口,但是有時候需求會很複雜。
比如,來自前端用戶的數據格式並不是和我們規定的一樣,有可能傳來空值,錯誤字符等等。這就需要對 request.data 進行過濾,尤其是在入庫的時候。
另外,如果數據有誤,DRF 並不知道你的字段是哪出的問題,所以拋異常是隻會是數據錯誤。我們需要對每個字段的每種錯誤類型給出對應的返回值。
可以在 UpdateModelMixin 源碼中看到,request.data 並沒有進行過濾。
class UpdateModelMixin:
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
# request.data 並沒有進行過濾
serializer = self.get_serializer(instance, data=request.data, partial=partial)
# ...
二、Viewsets:視圖集
1.簡單使用
DRF 提供了 Viewsets.py 視圖集,再次封裝之前的操作。最主要的是,可以通過設置 請求-函數
映射關係,來將請求方式與原有方法或自定義方法對應執行。
查看源碼
發現沒有提供實際的方法
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,GenericAPIView
查看 ViewSetMixin 類的 as_view 方法
GenericViewSet 和 ViewSet 都繼承了 ViewSetMixin,as_view 可以配置 請求-函數
映射
比如view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
class ViewSetMixin:
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
#...
def view(request, *args, **kwargs):
# 這裏 cls 去解析前邊例子中的 {'get': 'list', 'post': 'create'}
self = cls(**initkwargs)
self.action_map = actions
# methods拿到請求方法,比如get
for method, action in actions.items():
handler = getattr(self, action)
# 映射method get到執行函數handler list
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
# 繼承 APIView 中的 dispatch 進行分發
return self.dispatch(request, *args, **kwargs)
# ...
代碼實現
這樣的好處是,各種需求的接口的請求方式都可以用不同函數定義返回值。比如十大接口對應十個函數,分別碼代碼。
views.py
class BookGenericViewSet(RetrieveModelMixin, ListModelMixin, GenericViewSet):
queryset = models.Book.objects.filter(is_delete=False)
serializer_class = serializers.BookModelSerializer
def get_list(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def get_obj(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
urls.py
from django.contrib import admin
from django.urls import path
from django.conf.urls import url, include
from django.views.static import serve
from django.conf import settings
from api import views
urlpatterns = [
# ...
url(r'^v5/books/$', views.BookGenericViewSet.as_view({'get':'get_list'})),
url(r'^v5/books/(?P<pk>.*)/$', views.BookGenericViewSet.as_view({'get':'get_obj'})),
]
2.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
代碼實現
views.py
# 繼承 ModelViewset 會直接擁有六大接口:單查,羣查,單增,單刪,單整體改,單局部改
# 需要注意:Destroy 需要重寫
class BookModelViewSet(ModelViewSet):
queryset = models.Book.objects.filter(is_delete=False)
serializer_class = serializers.BookModelSerializer
# 刪除操作
def destroy(self, request, *args, **kwargs):
instance = self.get_object() # type: models.Book
if not instance:
return APIResponse(1, "Delete fail")
instance.is_delete = True
instance.save()
return APIResponse(1, "Delete successful")
urls.py
from django.conf.urls import url
from api import views
urlpatterns = [
url(r'^v6/books/$', views.BookModelViewSet.as_view({'get':'list', 'post':'create'})),
url(r'^v6/books/(?P<pk>.*)/$', views.BookModelViewSet.as_view({'get': 'retrieve', 'post':'create', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'})),
]
接口測試
羣查
單查
單刪
查看數據庫
單增
入庫成功
等等
總結
GenericAPIView 與 APIView 的區別與適用場景
1. GenericAPIView 視圖類
GenericView 繼承 GenericAPIView 視圖類
適用於標準的接口請求,或實現標準的 Model 類操作接口。
案例: 用戶查詢時,發送 GET 請求,返回數據。
2. APIView 視圖類
ViewSet 繼承 APIView 視圖類
實現不需要 Model 類操作,或非標準的 Model 類操作接口。比如,POST請求在標準的 Model 類操作用於新增接口,但以下案例並不符合這個標準。
案例 1: 請求手機驗證碼時,發送 POST 請求,不需要 Model 類的參與。
案例 2: 用戶登錄時,發送的 POST 請求,並不是完成數據的新增。POST 只是用於提交數據,返回值也不是登錄用戶信息,而是登錄的認證信息。