APIView
APIView是rest framework中最常用也是最基本的一個視圖。APIView繼承自Django的View視圖,並對Django的原生request進行了一些封裝,主要封裝了驗證、權限、節流三部分。
先看一下APIView中驗證、權限、節流的流程是怎樣的
驗證
rest framework提供了一個驗證的基類與4個驗證類
class BaseAuthentication:
def authenticate(self, request):
raise NotImplementedError(".authenticate() must be overridden.")
def authenticate_header(self, request):
pass
class BasicAuthentication(BaseAuthentication):
class SessionAuthentication(BaseAuthentication):
class TokenAuthentication(BaseAuthentication):
class RemoteUserAuthentication(BaseAuthentication):
如果想要自己實現驗證方法,可以通過繼承BaseAuthentication類並實現其中的兩個方法來實現。
然後看一下源碼中,rest frameworl是如何知道要使用自己寫的驗證方法還是系統自帶的驗證方法呢?
從上面的流程圖看,rest將驗證方法封裝到了Request這個類中。
[rest_framework\views.py]
def initialize_request(self, request, *args, **kwargs):
parser_context = self.get_parser_context(request)
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
看一下self.get_authenticators(),會發現rest實際上是將self.authentication_classes中的類給實例化了。
[rest_framework\views.py]
def get_authenticators(self):
return [auth() for auth in self.authentication_classes]
根據多繼承的繼承原理,首先會在自己的屬性中查找authentication_classes,如果沒有的話就去父類中查找,所以可以通過給authentication_classes賦值,來確定是使用自己寫的驗證方法還是系統的驗證方法。然後看一下APIView中對authentication_classes的定義
[rest_framework\views.py]
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
APIView中的authentication_classes是通過配置文件獲取的。所以這也是設置驗證方法的另外一種方法, 通過配置文件來配置需要使用哪些驗證方法。
定義自己的驗證方法
from rest_framework.authentication import BaseAuthentication
class MyAuthenticate(BaseAuthentication):
def authenticate(self, request):
return ( , ) or None or raise
def authenticate_header(self, request):
pass
在authenticate方法中有三種返回值,可以返回None表明這次驗證通過了,可以進行下面的驗證了,拋出一個異常,表明驗證失敗;返回一個元組。元組的信息爲(user_obj, auth_str)可以查看這部分源碼
[rest_framework\request.py]
def _authenticate(self):
for authenticator in self.authenticators:
try:
user_auth_tuple = authenticator.authenticate(self)
except exceptions.APIException:
self._not_authenticated()
raise
if user_auth_tuple is not None:
self._authenticator = authenticator
self.user, self.auth = user_auth_tuple
return
self._not_authenticated()
讓驗證失敗時,rest會返回匿名用戶,匿名用戶的信息可以通過配置文件配置。可以參考下面這部分源碼。
[rest_framework\request.py]
def _not_authenticated(self):
self._authenticator = None
if api_settings.UNAUTHENTICATED_USER:
self.user = api_settings.UNAUTHENTICATED_USER()
else:
self.user = None
if api_settings.UNAUTHENTICATED_TOKEN:
self.auth = api_settings.UNAUTHENTICATED_TOKEN()
else:
self.auth = None
權限控制與節流跟驗證的流程大體相似,配置的方式基本相同,可以自己通過源碼查看一下,可以從下面這三個方法開始查看
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
GenericAPIView
GenericAPIView是對APIView進行的又一次封裝
class GenericAPIView(views.APIView):
# 從數據庫中拿到的所有的樹
def get_queryset(self):
# 返回的一個對象
def get_object(self):
# 與序列化有關
def get_serializer(self, *args, **kwargs):
def get_serializer_class(self):
def get_serializer_context(self):
# 條件查詢
def filter_queryset(self, queryset):
# 與分頁有關
@property
def paginator(self):
def paginate_queryset(self, queryset):
def get_paginated_response(self, data):
GenericAPIView共實現了以上幾種方法,它幫我們做了很多事情。具體做了什麼事情可以自己查看這部分源碼。
viewsets中的視圖
class ViewSet(ViewSetMixin, views.APIView):
class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
class ReadOnlyModelViewSet(mixins.RetrieveModelMixin,
mixins.ListModelMixin,
GenericViewSet):
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
在使用這四個視圖的時候,我們的路由就不能像前面那些視圖一樣,get請求調用get方法。
ViewSetMixin這個類重寫了as_view方法,改變了方法的調用。我們需要在寫路由的時候做出一些修改
path('viewset', MyViewset.as_view({'get': ''list})),
我們可以自己配置請求方法對應的處理方法。我們來看一下as_view中的這部分源碼
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
...
def view(request, *args, **kwargs):
...
self.action_map = actions
for method, action in actions.items():
handler = getattr(self, action)
setattr(self, method, handler)
...
as_view根據我們傳入的actions字段來進行方法的調用。
既然有get了,那肯定還有其他的方法,比如post、uptade、delete等。
接下來看一下ModelViewSet,它繼承的類有很多,它所繼承的那些Minix類幫我們做的事情實際上就是實現了請求時所要調用的方法。
以mixins.CreateModelMixin爲例子
class CreateModelMixin:
"""
Create a model instance.
"""
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)
從源碼中可以看出CreateModelMixin實現了create方法,所做的操作就是幫我們把數據添加到數據庫。同樣的其他的類也是事項的相應的方法。
class ListModelMixin: # 獲取列表頁面
def list(self, request, *args, **kwargs):
class RetrieveModelMixin: # 獲取單條數據
def retrieve(self, request, *args, **kwargs):
class UpdateModelMixin: # 更新數據
def update(self, request, *args, **kwargs):
class DestroyModelMixin: # 刪除數據
def destroy(self, request, *args, **kwargs):
如果要繼承ModelViewSet的話,還有一個問題,就是當要查詢單個數據、修改與刪除的時候需要傳入pk,而創建和查詢列表則不需要,這就要求我們在定義路由的時候要做出一些變化。
path('viewset'. MyViewset.as_view({'get': 'list'})),
path('viewset/<int: pk>'. MyViewset.as_view({'post': 'create'})),
將需要參數的url與不需要參數的url分開寫。
如果一個API繼承了所有的Mixin,即增刪改查都有。那麼可以使用route來更優雅的實現路由
from django.urls import path, include
from rest_framework import routers
router = routers.DefaultRouter()
router.register(r'mypath', MyView)
urlpatterns = [
path('test/', include(router.urls)),
]
使用這種方法,rest會自動添加上面兩種路由。
我們可以根據rest提供的這幾種Mixin實現不同的API接口,比如只繼承mixins.RetrieveModelMixin、mixins.ListModelMixin,和GenericViewSet的只讀APIReadOnlyModelViewSet