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/' . 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