內容概要 request 對象和 response 對象 GenericAPIView 介紹 基於 GenericAPIView 的 5個視圖擴展類 GenericAPIView 的9個視圖子類 視圖集 ModelViewSet 的使用 ViewSetMixin 源碼分析 內容詳細 request ...
內容概要
- request 對象和 response 對象
- GenericAPIView 介紹
- 基於 GenericAPIView 的 5個視圖擴展類
- GenericAPIView 的9個視圖子類
- 視圖集
- ModelViewSet 的使用 ViewSetMixin 源碼分析
內容詳細
request 對象和 response 對象
請求之 request 對象
我們已經知道,drf 對原生的 request 請求數據對象做過封裝處理,原生 request 對象被保存在_request
中,通過 __getattr__
【點攔截】魔法方法可以獲取 原生 request
對象所有方法和屬性,並且 drf 的 request
可以通過 request.data
取出包括 json
格式數據在內的所有編碼格式,使得數據處理更加方便。
導入模塊 Request
from rest_framework.request import Request
request 對象需要記憶:
__getattr__
request.data
request.query_parmas
request.query_parmas
源碼:接受原生 request
對象的GET
請求的數據,也就是過濾參數
@property
def query_params(self):
"""
More semantically correct name for request.GET.
"""
return self._request.GET
限制
request
能接收的數據格式
局部配置:
在視圖層的視圖類中配置,限制 request
對象只能處理json
格式數據,只針對當前視圖類有效
from rest_framework.parsers import JSONParser,FormParser,MultiPartParser
class PublishView(APIView):
# 局部使用,只針對當前視圖類有效,只想處理json格式
parser_classes = [JSONParser]
def get(self, request):
pass
全局配置:
在項目文件夾下的 settings.py
文件中配置
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': [
'rest_framework.parsers.JSONParser',
],
}
解析順序:
視圖層類中的配置 > 項目文件夾下 settings.py
文件配置 > drf 原有的預設配置
drf的預設配置:from rest_framework import settings
響應之 response 對象
from rest_framework.response import Response
形參:
data=None, # 字元串,字典,列表--》給http響應body體中內容-->response對象中取出處理
status=None, # 響應狀態碼:1xx,2xx,3xx,預設是200
from rest_framework.status import HTTP_201_CREATED
Response(ser.data,status=HTTP_201_CREATED)
headers=None, # 響應頭 字典
---瞭解---
template_name=None, # 模板名字(不用),用瀏覽器訪問時,可以改
exception=False, # 異常處理
content_type=None # 響應編碼格式
設置響應的數據格式:
局部設置:
from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer
class BookDetailView(APIView):
renderer_classes = [JSONRenderer,]
全局設置:
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': ( # 預設響應渲染類
'rest_framework.renderers.JSONRenderer', # json渲染器
'rest_framework.renderers.BrowsableAPIRenderer', # 瀏覽API渲染器
)
}
GenericAPIView 介紹
GenericAPIView
繼承的是 APIView
前面我們已經知道 APIView
對原生 request
對象進行了處理,新的request
對象多了 .data
屬性,並且有三大認證和全局異常處理。
APIView
還擁有限制請求和響應數據的屬性 renderer_classes,parser_classes
那麼,GenericAPIView
就是在 APIView
的基礎上,多了更多屬性和方法
from rest_framework.generics import GenericAPIView
更多屬性和方法可以查看 GenericAPIView
源碼
屬性:
class GenericAPIView(views.APIView):
queryset = None
serializer_class = None
lookup_field = 'pk'
lookup_url_kwarg = None
filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
方法:
優先記憶:
屬性
queryset = None
: 獲取模型類對象serializer_class = None
: 獲取序列化類的屬性lookup_field = 'pk'
: 獲取單個對象時需要用到改屬性
方法:
get_queryset
:獲取模型類對象queryset
get_object
: 獲取單個模型類對象get_serializer
:獲取序列化類執行並返回一個序列化後的對象get_serializer_class
: 獲取序列化類
get_queryset
def get_queryset(self):
# 斷言 self.queryset 不為空,否則報錯後面信息,說明 queryset 屬性必須定義
assert self.queryset is not None, (
"'%s' should either include a `queryset` attribute, "
"or override the `get_queryset()` method."
% self.__class__.__name__
)
# 獲取 queryset 屬性並放回
queryset = self.queryset
if isinstance(queryset, QuerySet):
queryset = queryset.all()
return queryset
get_serializer
def get_serializer(self, *args, **kwargs):
serializer_class = self.get_serializer_class()
kwargs.setdefault('context', self.get_serializer_context())
return serializer_class(*args, **kwargs)
get_serializer_class
def get_serializer_class(self):
assert self.serializer_class is not None, (
"'%s' should either include a `serializer_class` attribute, "
"or override the `get_serializer_class()` method."
% self.__class__.__name__
)
return self.serializer_class
get_object
def get_object(self):
queryset = self.filter_queryset(self.get_queryset())
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
assert lookup_url_kwarg in self.kwargs, (
'Expected view %s to be called with a URL keyword argument '
'named "%s". Fix your URL conf, or set the `.lookup_field` '
'attribute on the view correctly.' %
(self.__class__.__name__, lookup_url_kwarg)
)
filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
obj = get_object_or_404(queryset, **filter_kwargs)
self.check_object_permissions(self.request, obj)
return obj
基於 GenericAPIView 的 5個視圖擴展類
不是視圖類,沒有繼承APIView
,需要配合GenericAPIView
使用,這五個類中提供了查找、新增、修改刪除模型類數據的方法,在GenericAPIView類的五個介面中可以直接調用
- CreateModelMixin
- ListModelMixin
- DestroyModelMixin
- RetrieveModelMixin
- UpdateModelMixin
from rest_framework.mixins import CreateModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin
使用 APIView 的視圖層類:
class BookAPIView(APIView):
def get(self, request):
book_list = models.Book.objects.all()
ser = BookModelSerializer(instance=book_list, many=True) # 獲取多個資源用 many=True
print(ser.data)
return Response(ser.data)
def post(self, request):
ser = BookModelSerializer(data=request.data)
if ser.is_valid():
ser.save()
return Response({'code': 100, 'msg': '新增成功', 'data': ser.data})
return Response({'code': 101, 'msg': '新增失敗', 'data': ser.errors})
class BookSetAPIView(APIView):
def get(self, request, pk):
book_obj = models.Book.objects.filter(pk=pk).first()
ser = BookModelSerializer(instance=book_obj)
return Response(ser.data)
def put(self, request, pk):
book_obj = models.Book.objects.filter(pk=pk).first()
ser = BookModelSerializer(instance=book_obj, data=request.data) # 修改是既有instance 也有data才行
if ser.is_valid():
ser.save()
return Response({'status': 100, 'msg': '修改成功', 'data': ser.data})
return Response({'status': 100, 'msg': '修改失敗', 'data': ser.errors})
def delete(self, request, pk):
Book.objects.filter(pk=pk).delete()
return Response({'status': 100, 'msg': '刪除成功'})
使用 GenericAPIView 的視圖層類:
和繼承 APIView
的區別就是,多了queryset
、serializer_class
屬性,還有get_queryset
等方法,而且無論換了什麼模型表(Auth、Publish),只需要改變queryset
、serializer_class
屬性即可,五個介面方法均不需要改變
class BookGeneric(GenericAPIView):
queryset = Book.objects
serializer_class = BookModelSerializer
def get(self, request):
quertset = self.get_queryset()
serializer_class = self.get_serializer(instance=quertset, many=True)
print(serializer_class.data)
return Response(serializer_class.data)
def post(self, request):
serializer_class = self.get_serializer(data=request.data)
if serializer_class.is_valid():
serializer_class.save()
return Response({'code': 100, 'msg': '新增成功', 'data': serializer_class.data})
return Response({'code': 101, 'msg': '新增失敗', 'data': serializer_class.errors})
class BookSetGeneric(GenericAPIView):
queryset = Book.objects
serializer_class = BookModelSerializer
def get(self, request, pk):
queryset = self.get_object()
serializer_class = self.get_serializer(instance=queryset)
return Response(serializer_class.data)
def put(self, request, pk):
queryset = self.get_object()
serializer_class = self.get_serializer(instance=queryset, data=request.data) # 修改是既有instance 也有data才行
if serializer_class.is_valid():
serializer_class.save()
return Response({'status': 100, 'msg': '修改成功', 'data': serializer_class.data})
return Response({'status': 100, 'msg': '修改失敗', 'data': serializer_class.errors})
def delete(self, request, pk):
self.get_object().delete()
return Response({'status': 100, 'msg': '刪除成功'})
繼承 GenericAPIView 和5個視圖擴展類:
五個視圖擴展類封裝了原來 GeneriacAPIView 類所需要書寫的五個介面方法
獲取模型類所有數據對象(get
方法)和修改數據(post
方法),書寫的方法和原來 GeneriacAPIView 類中的五個介面幾乎一致:
class ListModelMixin:
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)
class CreateModelMixin:
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
獲取單個模型類數據對象、修改數據、刪除數據三個需要傳入pk
值的視圖擴展類:
class RetrieveModelMixin:
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response(serializer.data)
class UpdateModelMixin:
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
if getattr(instance, '_prefetched_objects_cache', None):
instance._prefetched_objects_cache = {}
return Response(serializer.data)
class DestroyModelMixin:
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
self.perform_destroy(instance)
return Response(status=status.HTTP_204_NO_CONTENT)
視圖層類:
繼承了這些視圖擴展類,我們就可以使用類中封裝的介面函數,更進一步精簡視圖層類的介面代碼了
class BookGenericMinxin(GenericAPIView, ListModelMixin, CreateModelMixin):
queryset = Book.objects
serializer_class = BookModelSerializer
def get(self, request):
return self.list(request)
def post(self, request):
return self.create(request)
class BookGenericSetMinxin(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
queryset = Book.objects
serializer_class = BookModelSerializer
def get(self, request, pk):
return self.retrieve(request)
def put(self, request, pk):
return self.update(request)
def delete(self, request, pk):
return self.destroy(request)
序列化類:
class BookModelSerializer(ModelSerializer): # 繼承的是 ModelSerializer
class Meta:
model = Book
fields = ['id', 'name', 'price', 'publish', 'authors', 'author_list', 'publish_info'] # 註意別漏寫 fields
extra_kwargs = {
'publish': {'write_only': True},
'authors': {'write_only': True}
}
路由層:
urlpatterns = [
path('admin/', admin.site.urls),
path('book/', views.BookAPIView.as_view()),
path('book/<int:pk>/', views.BookSetAPIView.as_view()),
path('book1/', views.BookGeneric.as_view()),
path('book1/<int:pk>/', views.BookSetGeneric.as_view()),
path('book2/', views.BookGenericMinxin.as_view()),
path('book2/<int:pk>/', views.BookGenericSetMinxin.as_view()),
]
封裝程度越來越高,代碼越來越精簡
GenericAPIView 的9個視圖子類
這九個視圖子類,連 get
、post
、put
、get
、delete
五個介面都幫我們寫完了,我們在書寫GenericAPIView視圖層類時,只需要導入這九個試圖子類來繼承,就可以不用書寫五個方法了,需要什麼介面導入什麼模塊。
單獨的類:
from rest_framework.generics import CreateAPIView, ListAPIView, RetrieveAPIView, UpdateAPIView, DestroyAPIView
組合:
from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView, RetrieveDestroyAPIView, RetrieveUpdateAPIView
-
CreateAPIView
-
ListAPIView
-
RetrieveAPIView
-
UpdateAPIView
-
DestroyAPIView
-
ListCreateAPIView
-
RetrieveUpdateDestroyAPIView
-
RetrieveDestroyAPIView
-
RetrieveUpdateAPIView
其實,它們就是繼承了GenericAPIView
類,並且與五個視圖擴展類進行了組合繼承,並把介面函數也添加上罷了。pk
使用 *args 和 **kwargs 來取締了
class CreateAPIView(mixins.CreateModelMixin,
GenericAPIView):
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
class ListAPIView(mixins.ListModelMixin,
GenericAPIView):
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
class RetrieveAPIView(mixins.RetrieveModelMixin,
GenericAPIView):
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
class UpdateAPIView(mixins.UpdateModelMixin,
GenericAPIView):
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)
class DestroyAPIView(mixins.DestroyModelMixin,
GenericAPIView):
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
# 組合的就不逐一列舉了,原理一樣
class RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
GenericAPIView):
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)
這樣,我們在書寫視圖層類更加精簡了:
class BookView(ListCreateAPIView):
queryset = Book.objects
serializer_class = BookModelSerializer
class BookSetView(RetrieveUpdateDestroyAPIView):
queryset = Book.objects
serializer_class = BookModelSerializer
路由書寫:
urlpatterns = [
path('book3/', views.BookView.as_view()),
path('book3/<int:pk>/', views.BookSetView.as_view()),
]
ViewSetMixin 介紹
由ViewSetMixin
的源碼可以看到,它把as_view
方法進行了重寫,在路由層調用視圖類時,如果類第一個繼承的時ViewSetMixin
,會優先調用它的as_view
方法,需要傳入位置參數 (一個字典)actions
,而且由actions.items()
,actions
是一個字典。
這樣一來我們不使用到 九個視圖子類給我們封裝的五個介面方法(get、post、put、delete、patch),可以直接繼承 ViewSetMixin, ListModelMixin, CreateModelMixin, GenericAPIView
。因為在路由中會傳進來 get 等五個字典key,賦值對象地址後可直接調用。
記住:繼承ViewSetMixin 之後書寫路由時需要傳入 actions 參數
class ViewSetMixin:
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
def view(request, *args, **kwargs):
self = cls(**initkwargs)
# 例子 method = 'get',action='list'
for method, action in actions.items():
# 獲取 list() 方法的對象地址
handler = getattr(self, action)
# 相當於 self.get = list,把lsit方法賦值給了self.get,發送get請求時觸發,相當於觸發了 ListModelMixin 的 list() 方法
setattr(self, method, handler)
return self.dispatch(request, *args, **kwargs)
view.actions = actions
return csrf_exempt(view)
導入
from rest_framework.viewsets import ViewSetMixin
路由層:
as_view()
需要傳入字典:{“請求方式”: “方法名”}
註意字典中分別是五個介面的請求方式和對應的五個視圖擴展類封裝好的方法
urlpatterns = [
path('book4/', views.BookViewSet.as_view(actions={'get': 'list', 'post': 'create'})),
path('book4/<int:pk>/', views.BookViewSetPk.as_view(actions={'get': 'retrieve', 'put': 'update', 'delete': 'delete'}))
]
視圖層類:
class BookViewSet(ViewSetMixin, ListCreateAPIView):
queryset = Book.objects
serializer_class = BookModelSerializer
class BookViewSetPk(ViewSetMixin, RetrieveUpdateDestroyAPIView):
queryset = Book.objects
serializer_class = BookModelSerializer
Viewset
系列
from rest_framework.viewsets import ViewSetMixin, ViewSet, GenericViewSet, ModelViewSet
- ViewSetMixin
- ViewSet
- GenericViewSet
- ModelViewSet
ViewSet
繼承了 ViewSetMixin
和APIView
class ViewSet(ViewSetMixin, views.APIView):
"""
The base ViewSet class does not provide any actions by default.
"""
pass
GenericViewSet
繼承了 ViewSetMixin
和GenericAPIView
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
ModelViewsetMinx
包含了五個視圖擴展類和 GenericViewSet
這樣繼承了 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
class BookViewSet(ModelViewSet):
queryset = Book.objects
serializer_class = BookModelSerializer
繼承了 ViewSetMixin 之後,路由就要都要寫成這樣:
urlpatterns = [
path('book4/', views.BookViewSet.as_view(actions={'get': 'list', 'post': 'create'})),
path('book4/<int:pk>/', views.BookViewSetPk.as_view(actions={'get': 'retrieve', 'put': 'update', 'delete': 'delete'}))
]
drf 提供了自動生成這樣兩條路由與視圖函數對應關係的功能模塊 routers
用法:
# 導入
from rest_framework.routers import DefaultRouter, SimpleRouter
# 實例化對象並註冊路由
router = DefaultRouter()
router.register('book4', views.BookViewSet, 'book4')
添加到路由列表中
1、路由分發
urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/', include(router.urls)),
]
2、直接列表相加
urlpatterns += router.urls