drf-視圖
一、基於APIView的五個介面
首先,APIView是Rest Framework提供的所有視圖的基類,繼承了Django的View
其次,APIView和View的不同在於:
- 他們傳入視圖方法中的request對象不是同一個,APIView的是Request對象,View的是HttpResponse對象。
- APIView返回的是Response對象,是符合前端Accept要求的格式
- 在dispatch分發前會進行身份認證,許可權檢查,流量控制
五個介面實例:
from rest_framework.views import APIView
from rest_framework.response import Response
from app01.ser import BooksSerializers
from app01.models import Books
# 基於APIView
class BooksView(APIView):
# 查所有
def get(self, request):
books = Books.objects.all()
books_ser = BooksSerializers(instance=books, many=True)
return Response(books_ser.data)
# 增加
def post(self, request):
books_ser = BooksSerializers(data=request.data)
if books_ser.is_valid():
books_ser.save()
return Response(books_ser.data)
class BookView(APIView):
# 查單個
def get(self, request, pk):
print(pk, type(pk))
book = Books.objects.filter(pk=pk).first()
book_ser = BooksSerializers(instance=book)
return Response(book_ser.data)
# 修改
def put(self, request, pk):
book = Books.objects.filter(pk=pk).first()
book_ser = BooksSerializers(instance=book, data=request.data)
if book_ser.is_valid():
book_ser.save()
return Response(book_ser.data)
else:
return Response({'message': book_ser.errors})
# 刪除
def delete(self, request, pk):
Books.objects.filter(pk=pk).delete()
return Response({'message': '刪除成功'})
# urls.py
path('books/', views.BooksView.as_view()),
re_path(r'^book/(?P<pk>\d+)/', views.BookView.as_view()),
二、基於GenericAPIView的五個介面
首先,GenericAPIView繼承了APIView,並增加了操作序列化器和資料庫查詢方法
並提供了兩個屬性和兩個方法
queryset:指要傳的queryset對象
get_queryset():返回queryset對象
serializer_class:指明視圖使用的序列化器,也就是哪個序列化類來序列化這堆數據
get_serializer():返回序列化器類
get_object():返回視圖所需的模型類數據對象
源碼如下:
# from rest_framework.generics import GenericAPIView
class GenericAPIView(views.APIView):
queryset = None # 指要傳的queryset對象
serializer_class = None # 指明視圖使用的序列化器,也就是哪個序列化類來序列化這堆數據
......
def get_queryset(self):
......
queryset = self.queryset
# 判斷傳入的queryset是不是QuerySet的對象
if isinstance(queryset, QuerySet):
queryset = queryset.all() # 是就加all()——>Book.objects.all()
return queryset # 返回queryset對象
def get_serializer(self, *args, **kwargs):
serializer_class = self.get_serializer_class()
# 向序列化器對象的context屬性補充三個數據,
# request,當前視圖的請求對象
# format,當前請求的類視圖對象
# view,當前請求期望返回的數據格式
kwargs.setdefault('context', self.get_serializer_context())
return serializer_class(*args, **kwargs) # 返回序列化器類
五個介面實例:
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from app01.ser import BooksSerializers
from app01.models import Books
# 基於GenericAPIView
class BooksView(GenericAPIView):
queryset = Books.objects
serializer_class = BooksSerializers
# 查所有
def get(self, request):
books = self.get_queryset() # 相當於Books.objects.all()
books_ser = self.get_serializer(instance=books, many=True)
return Response(books_ser.data)
# 新增
def post(self, request):
books_ser = self.get_serializer(data=request.data)
if books_ser.is_valid():
books_ser.save()
return Response(books_ser.data)
class BookView(GenericAPIView):
queryset = Books.objects
serializer_class = BooksSerializers
# 查單個
def get(self, request, pk):
book = self.get_object()
book_ser = self.get_serializer(instance=book)
return Response(book_ser.data)
# 修改
def put(self, request, pk):
book = self.get_object()
book_ser = self.get_serializer(instance=book, data=request.data)
if book_ser.is_valid():
book_ser.save()
return Response(book_ser.data)
else:
return Response({'message': book_ser.errors})
# 刪除
def delete(self, request, pk):
self.get_object().delete()
return Response({'message': '刪除成功'})
三、GenericAPIView的五個視圖括展類
因為之前程式碼的處理流程重複程式碼太多,所以可以使用相應的擴展類來減少編寫的程式碼量。
而這五個擴展類需要搭配GenericAPIView父類,並且還要調用GenericAPIView提供的序列化器和數據查詢方法。
ListModeMixin
列表視圖擴展類(類似於查詢所有),提供了快速獲取所有列表的視圖,並且該方法會對數據進行過濾和分頁,成功返回200
源程式碼:
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) # 返回序列化後的數據
CreateModeMixin
創建視圖擴展類(創建數據),提供了快速創建資源的視圖,成功返回201,失敗400
原程式碼:
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)
# 保存方法
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 {}
RetrieveModelMixin
詳情視圖擴展類(查詢單個),返回一個存在的數據對象,成功返回201,失敗404
原程式碼:
class RetrieveModelMixin:
def retrieve(self, request, *args, **kwargs):
# 獲取對象,並檢查對象的許可權
instance = self.get_object()
# 序列化
serializer = self.get_serializer(instance)
return Response(serializer.data)
UpdateModelMixin
更新視圖擴展類(更新數據),快速更新一個存在的數據對象,也可實現局部更新 partial_update() 成功返回201,失敗400
源程式碼
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)
# 保存
def perform_update(self, serializer):
serializer.save()
# 局部更新
def partial_update(self, request, *args, **kwargs):
kwargs['partial'] = True
return self.update(request, *args, **kwargs)
DestroyModelMixin
刪除視圖擴展類(刪除數據),快速刪除一個存在的數據對象,成功返回204,不存在返回404。
源程式碼
class DestroyModelMixin:
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()
五個視圖擴展類的使用實例
from rest_framework.generics import GenericAPIView
from app01.ser import BooksSerializers
from app01.models import Books
from rest_framework.mixins import ListModelMixin, CreateModelMixin
from rest_framework.mixins import UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin
# 基於GenericAPIView的五個視圖擴展類
class BooksView(GenericAPIView, ListModelMixin, CreateModelMixin):
queryset = Books.objects
serializer_class = BooksSerializers
def get(self, request):
return self.list(request)
def post(self, request):
return self.create(request)
class BookView(GenericAPIView, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin):
queryset = Books.objects
serializer_class = BooksSerializers
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)
四、GenericAPIView的視圖子類
二話不說先來看其中一個源碼!!!其他的子類都是一樣的原理
源碼
# 首先它繼承了CreateModelMixin創建視圖擴展類
class CreateAPIView(mixins.CreateModelMixin, GenericAPIView):
# 並且它提供了post方法,那我們就不用在視圖中寫post方法了,這裡直接寫好了直接調用self.create
# 只需要傳入對象和序列化器就可以了
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
實例:
from rest_framework.generics import ListAPIView, UpdateAPIView
from rest_framework.generics import CreateAPIView, DestroyAPIView, RetrieveAPIView
from app01.ser import BooksSerializers
from app01.models import Books
# 基於GenericAPIView的五個視圖子類
class BooksView(ListAPIView, UpdateAPIView): # 獲取所有,新增一個
queryset = Books.objects
serializer_class = BooksSerializers
# 獲取一個,修改一個,刪除一個
class BookView(CreateAPIView, DestroyAPIView, RetrieveAPIView):
queryset = Books.objects
serializer_class = BooksSerializers
GenericAPIView的視圖子類除了這五個還有四個,也就是這五個方法的其他組合:
- ListCreateAPIView
- 提供get、post方法,是 ListAPIView 和 UpdateAPIView 的組合
- RetrieveDestroyAPIView
- 提供get、delete方法,是 RetrieveAPIView 和 DestroyAPIView 的組合
- RetrieveUpdateAPIView
- 提供get、update方法,是 RetrieveAPIView 和 UpdateAPIView 的組合
- RetrieveUpdateDestroyAPIView
- 提供get、update、delete方法,是 RetrieveAPIView 、 UpdateAPIView 和 DestroyAPIView 的組合
五、ModelViewSet
首先它繼承了 GenericViewSet,並且還有 ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。
ModelViewSet五個介面
先來看一下它的使用:
# views.py
from rest_framework.viewsets import ModelViewSet
from app01.ser import BooksSerializers
from app01.models import Books
class BooksView(ModelViewSet): # 五個介面都有
queryset = Books.objects
serializer_class = BooksSerializers
# urls.py
from django.contrib import admin
from django.urls import path, re_path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
# 當路徑匹配,又是get請求,會執行BooksView的list方法
path('books/', views.BooksView.as_view({'get': 'list', 'post': 'create'})),
re_path(r'^book/(?P<pk>\d+)/', views.BooksView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'}))
]
源碼分析
由於 ModelViewSet 繼承了 GenericViewSet ,並且 GenericViewSet 又繼承了 ViewSetMixin,ViewSetMixin中又重寫了 as_view 方法, 也就是說當路徑匹配上了會執行這裡重寫的 as_view 方法。
分析: as_view 方法
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
...
def view(request, *args, **kwargs):
self = cls(**initkwargs)
# 核心程式碼,所有路由中只要配置了對應關係,比如{'get': 'list'},當get請求來,就會執行list方法
for method, action in actions.items():
# 遍歷我們傳入的對應關係
# method = get
# action = list
# 並且通過反射獲取list方法的記憶體地址
handler = getattr(self, action) # handler已經是list的記憶體地址
# 點攔截(對象.get = list)
setattr(self, method, handler)
# 當循環完畢之後,對象.get:對應list方法,對象.post:對應create方法
...
# 後面照常繼續
return self.dispatch(request, *args, **kwargs)
繼承ViewSetMixin的視圖類
由上面的分析可以得出,我們也可以直接繼承 ViewSetMixin 寫視圖,如下:
from rest_framework.viewsets import ViewSetMixin
from rest_framework.response import Response
from app01.ser import BooksSerializers
from app01.models import Books
class BooksView(ViewSetMixin, APIView):
def get_all_books(self, request):
book_list = Books.objects.all()
book_ser = BooksSerializers(book_list, many=True)
return Response(book_ser.data)
# urls.py
# 當匹配上了路由,又是get請求,就調用get_all_books方法
path('books/', views.BooksView.as_view({'get': 'get_all_books'})),

