[Django] Django REST Framework(三):APIView、GenericAPIView、Mixins總結
概述
APIView
是DRF的檢視層中最基本的類,它相當於Django中的View類,其他檢視類都是通過繼承APIView實現的。
GenericAPIView
繼承於APIView
,在其父類的基礎上為列表檢視和詳情檢視添加了常用的行為。
Mixins
提供了一些基本檢視行為的操作方法,如提供的list()
方法將返回查詢集,等等。因為Python多繼承的特性,因此,在定義檢視時可以將GenericAPIView
和Mixins
進行靈活的組合。而DRF中就提供了許多這樣的類可以供我們完成所有所需的View。
1.APIView
在DRF中提供了APIView
,繼承於Django的View
- 1.傳遞給處理方法的請求是DRF的
Request
例項,而不是Django的HttpRequest
例項; - 2.響應並返回的是DRF的
Response
物件,而不是Django的HttpResponse
物件; - 3.任何APIException異常都會被捕獲並調製到適當的響應中;
- 4.會對接收的請求進行身份認證和許可權的檢查。
和使用View類似,使用APIView時,接收的請求會被dispatch到對應的方法中,如get()
、post()
……此外,還可以設定許多屬性來控制API各個方面的策略。
使用APIView
from rest_framework.views import APIView
基本的使用APIView方式舉例如下:
class SnippetList(APIView):
# 處理GET請求
def get(self, request, format=None):
snippet = Snippet.objects.all()
serializer = SnippetSerializer(snippet, many=True)
return Response(serializer.data)
# 處理POST請求
def post(self, request, format=None):
serializer = SnippetSerializer(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_400_BAD_REQUEST)
1.1.常用屬性
.authentication_classes
該屬性用於指定當前View endpoint的身份驗證類,如:
from rest_framework.authentication import TokenAuthentication
class TestView(APIView):
authentication_classes = (TokenAuthentication,)
# ......
使用authentication_classes
設定的身份驗證類將僅僅對該View驗證有效,如果需要對整個專案有效,則需要在settings.py配置檔案中進行設定:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
# ......
'rest_framework.authentication.authentication.TokenAuthentication',
)
}
DEFAULT_AUTHENTICATION_CLASSES
也是身份驗證類列表的預設值。
.permission_classes
該屬性用於指定當前View endpoint的許可權類列表,如:
from rest_framework import permissions
class SnippetList(APIView):
# ...
permission_classes = (permissions.IsAuthenticatedOrReadOnly,permissions.IsAuthenticated,permissions.IsAdminUser)
# ...
如果不指定該屬性,則預設使用settings.DEFAULT_PERMISSION_CLASSES
中指定的許可權列表。
APIView
是DRF中所有View的基類,也是DRF中檢視層的基礎,但APIView
並不是重點,重點是下面的GenericAPIView
和Mixins
以及兩個混合組成的許多類。
2.GenericAPIView
GenericAPIView
繼承於APIView,為常用的列表檢視和詳細檢視提供了一些操作屬性方法。
使用GenericAPIView
時需要匯入對應模組:
from rest_framework import generics
其基本使用方式如下:
from .serializers import SnippetSerializer
from .models import Snippet
# Create your views here.
class Show(generics.GenericAPIView):
# 指定序列化類
serializer_class = SnippetSerializer
# 指定QuerySet物件
queryset = Snippet.objects.all()
def get(self, request):
return Response("hello world...")
def post(self, request):
return Response(request.data)
2.1.常用屬性和方法
.queryset
指定要顯示的Model物件的QuerySet物件(查詢集),用於列表檢視中。在GenericAPIView中必須指定該屬性或者重寫get_queryset()
方法。
.serializer_class
指定要顯示的Model物件的Serializer物件,在GenericAPIView中必須指定該屬性或者重寫get_serializer()
方法。
.lookup_field
指定用於查詢單個Model例項的Model欄位,預設為pk
.
.lookup_url_kwarg
指定用於Model查詢的url關鍵字引數,如果未設定,則預設使用與lookup_field
的值。
.pagination_class
指定列表檢視中用於分頁的分頁類列表,可以對響應列表進行分頁。預設使用配置檔案中DEFAULT_PAGINATION_CLASS
的值。
.filter_backends
指定用於過濾查詢集的過濾器後端類列表。 預設使用為DEFAULT_FILTER_BACKENDS
設定的值。
.get_queryset(self)
返回用於列表檢視的查詢集,預設返回queryset
屬性指定的查詢集。
NOTE:
應始終使用此方法而不是直接訪問self.queryset,因為self.queryset
僅被評估一次,然後將結果進行快取用於所有後續請求。
.get_object(self)
返回用於詳情檢視中的Model物件例項,預設使用lookup_field
欄位過濾QuerySet中的資料。
.get_serializer()
返回用於Model序列化的Serializer類,預設返回serializer_class
屬性的值,該方法可以用來動態指定一個序列化類。
3.Mixin
通常GenericAPIView不會單獨使用,因為它相比APIView,僅僅提供了一些公共的用於列表檢視和詳情檢視的屬性和方法,GenericAPIView需要和Mixins組合使用。
Mixin類中提供了許多用於對檢視進行操作的方法,這些操作方法無須我們自己實現就可以使用了。比如,ListModelMixin
提供了一個list()
方法用於將Model的查詢集響應給客戶端,因此可以用於列表檢視的get()
中:
class show(generics.GenericAPIView,mixins.ListModelMixin):
serializer_class = SnippetSerializer
queryset = Snippet.objects.all()
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
相比使用APIView,簡化了非常多的操作。
使用Mixins需要匯入其所在模組:
from rest_framework import mixins
下面對常用Mixins類及其功能進行下總結。
3.1.ListModelMixin
該類提供了一個.list(request, *args, **kwargs)
方法,用於列出查詢集QuerySet。如果queryset被填充,則響應碼為200,並將查詢集序列化作為響應體響應給客戶端。
3.2.CreateModelMixin
該類提供一個.create(request, *args, **kwargs)
方法,用於建立並儲存一個新的Model例項,因此用在POST請求中,和post()
方法組合使用。如果Model例項建立成功,則響應碼為201 Created ,並將物件序列化後作為響應體響應給客戶端;如果Model例項建立失敗,則響應碼為401(Bad Request),並將錯誤資訊作為響應體。
3.3.RetrieveModelMixin
該類提供一個retrieve(request, *args, **kwargs)
方法,用於檢索並返回一個現有的Model例項。如果能夠檢索到物件,則返回200 OK響應,並將物件序列化後作為響應體輸出,否則返回404 Not Found響應。
3.4.UpdateModelMixin
該類提供一個update(request, *args, **kwargs)
方法,用於更新並儲存現有的Model物件。因此用在PUT請求中,和put()
方法組合使用。如果更新成功,則返回200 OK響應,並將物件序列化後作為響應體輸出,否則返回400 Bad Request響應。
3.5.DestroyModelMixin
該類提供一個.destroy(request, *args, **kwargs)
方法,用於刪除一個已存在的Model例項。如果刪除成功返回204 No Content響應,否則返回404 Not Found響應。
以上方法無須我們再進行實現,直接使用即可,如以下示例:
class SnippetDetail(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.CreateModelMixin,
generics.GenericAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
# get,put,delete方法是GenericAPIView中提供
# retrieve,update,destory方法由Mixin類檢視提供
# 二者可以靈活的結合
def get(self, request, *args, **kwargs):
return self.list(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)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
4.常用具體通用檢視
在DRF中,除了提供Mixin
類之外,還提供了GenericAPIView
和Mixins
的組合View類,這些類稱為具體通用檢視。因此我們不需要只需要具體通用檢視就可以實現我們所需的功能,如:
from rest_framework import generics
class show(generics.ListCreateAPIView):
serializer_class = SnippetSerializer
queryset = Snippet.objects.all()
在這個示例中,ListCreateAPIView
繼承自GenericAPIView, ListModelMixin, CreateModelMixin這三個類,並且內部已經將對應請求方法和操作方法進行了實現,我們只需要指定一些屬性即可,無須再寫get()
,post()
請求方法邏輯。
下面是一些常用的具體通用檢視。
使用這些類時需要匯入所在模組:
from rest_framework import generics
4.1.ListCreateAPIView
父類:GenericAPIView,
ListModelMixin
,CreateModelMixin
特點:已提供了get()
、put()
方法處理。
4.2.RetrieveUpdateDestroyAPIView
父類:GenericAPIView
, RetrieveModelMixin
, UpdateModelMixin
, DestroyModelMixin
特點:已提供了get()
、put()
、patch()
、destory()
方法處理。
其他具體通用檢視,還請參考官方文件.
5.重構View示例
現在,利用以上View內容以及Django中的函式檢視等內容,從函式檢視開始,一步步對檢視進行重構,看看其簡化的過程。
Step1.最初的函式檢視
# 列表檢視
@api_view(['GET', 'POST'])
def student_list(request):
# 處理GET請求
if request.method == 'GET':
student = Student.objects.all()
serializers = StudentSerializer(student, many=True)
return Response(serializers.data)
# 處理POST請求
if request.method == 'POST':
serializer = StudentSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(status=status.HTTP_400_BAD_REQUEST)
# 詳情檢視
@api_view(['GET', 'POST'])
def student_detail(request, pk):
if request.method == 'GET':
student = Student.objects.get(pk=pk)
serializer = StudentSerializer(student)
return Response(serializer.data)
if request.method == 'POST':
serializer = StudentSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(status=status.HTTP_400_BAD_REQUEST)
Step2.將函式檢視用APIView替換
現在通過APIView進行重構,可以定義請求對應的get()
方法和post()
方法,重構後如下所示:
from rest_framework.response import Response
from hello.models import Student
from hello.serializers import StudentSerializer
from rest_framework import status
from rest_framework import views
# 列表檢視
class StudentList(views.APIView):
# GET請求
def get(self,request):
student = Student.objects.all()
serializer = StudentSerializer(student,many=True)
return Response(serializer.data)
# POST請求
def post(self, request):
serializer = StudentSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.data, status=status.HTTP_400_BAD_REQUEST)
# 詳情檢視
class StudentDetail(views.APIView):
def get(self, request, pk):
student = Student.objects.get(pk=pk)
serializer = StudentSerializer(student)
return Response(serializer.data)
def post(self, request):
serializer = StudentSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.data, status=status.HTTP_400_BAD_REQUEST)
Step3.GenericAPIView+Mixins替換APIView
GenericAPIView相比APIView,提供了部分屬性;Mixins提供了許多用於對檢視進行操作的方法:
from rest_framework.response import Response
from hello.models import Student
from hello.serializers import StudentSerializer
from rest_framework import status
from rest_framework import generics
from rest_framework import mixins
from django.http import Http404
class StudentList(generics.GenericAPIView, minxins.ListModelMixin, mixins.CreateModelMixin):
# 指定序列化類
serializer_class = StudentSerializer
# 執行Model物件查詢集
queryset = Student.objects.all()
# 如果不對query_set的值進行修改,沒有必要重寫該方法
def get_queryset(self):
return Student.objects.all()
def get(self, request):
return self.list(request)
def post(self, request):
return self.create(request)
# 詳情檢視
class StudentDetail(generics.GenericAPIView,
mixins.RetrieveModelMixin):
def get(self, request, *args, **kwargs):
return self.retrieve(self, request, *args, **kwargs)
Step4.使用具體通用檢視替換GenericAPIView+Mixins
from hello.models import Student
from hello.serializers import StudentSerializer
from rest_framework import generics
# 列表檢視
class StudentList(generics.ListCreateAPIView):
serializer_class = StudentSerializer
queryset = Student.objects.all()
# 詳情檢視
class StudentDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Student.objects.all()
serializer_class = StudentSerializer
通過具體通用檢視,兩行程式碼就搞定。
總結
DRF中提供了這麼多View,但無非就是從views.View這個基類,通過繼承、mixin實現而已。因此,在學習這些類時,不必死記硬背,而是通過其繼承結構、實現原始碼,瞭解明白它的作用和使用方式,以及和mixin的混合使用,這樣才可以更好的使用DRF中的各種View。