一、首先要先明白Django中CBV的过程,CBV全称为Class Base View,中文名称叫做类视图,简单的说Django中CBV使用方法为:类名.as_view(),在进入as_view()时,首先会先执行里面的dispatch()方法,在Django的view中,dispatch()方法只是对url进行了反射,返回的是HttpRequest对象,而在DRF中返回的是Request对象,DRF中的Request对象继承了HttpRequest对象;
二、Django和DRF中的dispatch()方法
(一)Django:View的dispatch()方法
def dispatch(self, request, *args, **kwargs):
# Try to dispatch to the right method; if a method doesn't exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn't on the approved list.
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
(二)DRF:APIView的dispatch()方法
def dispatch(self, request, *args, **kwargs):
"""
`.dispatch()` is pretty much the same as Django's regular dispatch,
but with extra hooks for startup, finalize, and exception handling.
"""
self.args = args
self.kwargs = kwargs
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
self.initial(request, *args, **kwargs)
# Get the appropriate handler method
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
response = handler(request, *args, **kwargs)
except Exception as exc:
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
(三)区别和原理
可以看出DRF中dispatch()方法只是继承了Django原来的方法再添加了些东西,最主要的区别就是最终返回的request发生了改变,可以看到首先DRF先对request进行初始化
def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
"""
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
)
此时的request已发生变化,返回的是一个Request对象,而且这个对象里包括了Django原来的request,并且多了些其他内容;不难发现,在返回的对象中还包括了认证相关内容authenticators,这个对象包含了什么呢?再继续进入该方法中:
def get_authenticators(self):
"""
Instantiates and returns the list of authenticators that this view can use.
"""
return [auth() for auth in self.authentication_classes]
该方法返回的是一个实例化对象列表,对self.authentication_classes进行遍历,并且进行实例化,其实看到这个就可以知道为什么在引用认证类的时候可以直接将类名放在列表里就可以实现功能了,因为源码中会找到authentication_classses = [xxxx, xxxx],对authentication_classses列表进行实例化,然后就进行返回,那么就得到一个既有request和authenticators列表的对象了:
返回一个Request对象后,这时的request就是Request了,接下来在dispatch()方法中继续看到initial()方法,进入该方法,针对认证这一功能继续可以看到,initial()内部会进行认证执行,
def initial(self, request, *args, **kwargs):
# 为了方便阅读,已删除了其他不相关的源码,需要看的可以自行查看源码
self.perform_authentication(request)
def perform_authentication(self, request):
"""
Perform authentication on the incoming request.
Note that if you override this and simply 'pass', then authentication
will instead be performed lazily, the first time either
`request.user` or `request.auth` is accessed.
"""
request.user
源码认证最后是找到了request的user属性,前面说过这里的request其实是Request对象,所以回去找到Request对象中的user属性即可以知道接下来DRF都执行了些什么
@property
def user(self):
"""
Returns the user associated with the current request, as authenticated
by the authentication classes provided to the request.
"""
if not hasattr(self, '_user'):
with wrap_attributeerrors():
self._authenticate()
return self._user
def _authenticate(self):
"""
Attempt to authenticate the request using each authentication instance
in turn.
"""
for authenticator in self.authenticators:
try:
user_auth_tuple = authenticator.authenticate(self)
except exceptions.APIException:
self._not_authenticated()
raise
最后_authenticate()方法内遍历了实例化对象列表内的对象,并且返回的是一个元祖,所以在定制一个认证类的时候,需要定义一个authenticate()方法才能执行该认证类的方法;
三、自定义一个认证类
class MyAuthenticate(BaseAuthentication):
def authenticate(self, request):
token = request.data['token']
token_obj = UserToken.objects.filter(token=token).first()
if not token_obj:
raise exceptions.AuthenticationFailed('用户认证失败!')
return (token_obj.user, token_obj)
def authenticate_header(self, request):
pass
class APIView(APIView):
authentication_classes = [MyAuthenticate, ]