路由和认证
路由
1 三种路由的配置
在urls.py里面的配置的常规配置
path('books4/', views.Book4View.as_view()),
re_path('books4/(?P<pk>\d+)', views.Book4DetailView.as_view())
视图类中继承了ViewSetMixin
path('books5/', views.Book5View.as_view(actions={'get':'list','post':'create'})), #当路径匹配,又是get请求,会执行Book5View的list方法
re_path('books5/(?P<pk>\d+)', views.Book5View.as_view(actions={'get':'retrieve','put':'update','delete':'destroy'})),
继承视图类ModelViewSet路由的写法
-
配置路由导入模块
from rest_framework import routers
-
两个类,实例化得到对象
routers.DefaultRouter ···· ^books/$ [name='book-list'] # 根 ^books\.(?P<format>[a-z0-9]+)/?$ [name='book-list']# simple的差不多 ^books/(?P<pk>[^/.]+)/$ [name='book-detail'] ^books/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$ [name='book-detail']# 和simple的差不多 ^$ [name='api-root']# 根路径会显示出所有可以访问的地址 ^\.(?P<format>[a-z0-9]+)/?$ [name='api-root'] ···· 自动生成的6个路由 routers.SimpleRouter """[<RegexURLPattern book-list ^books/$>, <RegexURLPattern book-detail ^books/(?P<pk>[^/.]+)/$>]" """ 自动生成的2个路由
-
注册
router.register('books',views.BookViewSet)# 后面可以加别名
-
自动生成路由,加入到原来的路由中去
urlpatterns+=router.urls
action的使用
是为了给继承ModelViewSet视图类定义的函数也添加路由
class BookViewSet(ModelViewSet): queryset = models.Book.objects.all() serializer_class = BookModelSerializer @action(methods=['GET','post'], detail=True) def get_5(self, request,pk): book = self.get_queryset()[:5] # 从0开始截取 ser = self.get_serializer(book, many=True) return Response(ser.data)
-
methods:第一个参数,传一个列表,列表中放请求方式,如get,post等
-
detail:
^books/get_1/$ [name='book-get-1'] 当向这个地址发送get请求,会执行下面的函数 detail:布尔类型 如果是True ^books/(?P<pk>[^/.]+)/get_1/$ [name='book-get-1'] http://127.0.0.1:8000/books/1/get_5/
认证
1 认证的实现
1 写一个类,继承BaseAuthentication,重写authenticate,认证的逻辑写在里面,认证通过,返回两个值,一个值最终给了Requet对象的user,认证失败,抛异常:APIException或者AuthenticationFailed
2 全局使用,局部使用
2 drf认证的源码分析
入口点
在APIView--> as_view--> dispatch方法--> self.initial(request, *args, **kwargs)
这个方法中有认证,权限,频率
dispatch方法源码
def dispatch(self, request, *args, **kwargs):
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
在try里面 self.initial
这个方法点进去,在APIView里面
initital方法
def initial(self, request, *args, **kwargs):
"""
Runs anything that needs to occur prior to calling the method handler.
"""
self.format_kwarg = self.get_format_suffix(**kwargs)
neg = self.perform_content_negotiation(request)
request.accepted_renderer, request.accepted_media_type = neg
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
self.perform_authentication(request)
只读这个认证的功能 ,传个request过来,这个request是包装后的,
perform_authentication方法的源码
def perform_authentication(self, request):
request.user
因为这个是包装过后的Request,所以我们要到包装过后的request对象里面的user属性
user属性源码
@property
def user(self):
if not hasattr(self, '_user'):
with wrap_attributeerrors():
self._authenticate()
return self._user
判断self里面有没有_user这个属性,一开始是没有的,它就会走下面的上下文管理器, self._authenticate()
点_authenticate这个方法,这个方法还在Request对象里面,
_authenticate源码
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()
这里的for循环的authenticators一定是一个可迭代对象,我们要到Request对象里面去找,
class Request:
def __init__(self, request, parsers=None, authenticators=None,
negotiator=None, parser_context=None):
assert isinstance(request, HttpRequest), (
'The `request` argument must be an instance of '
'`django.http.HttpRequest`, not `{}.{}`.'
.format(request.__class__.__module__, request.__class__.__name__)
)
self._request = request
self.authenticators = authenticators or ()
我们发现是在这个类里面初始化得到的,这个类是dispatch包装request得到的,request = self.initialize_request(request, *args, **kwargs)
点击initialize_request
这个方法
我们发现了authenticators
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
def get_authenticators(self):
return [auth() for auth in self.authentication_classes]
这个是列表生成式,从自己`authentication_classes去取,取出一个加括号执行,这个self是APIView里面的对象的属性,因为自己视图类里面没有
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
列表中是一对对象,是视图类中配置的authentication_classes=[类名]对象
,
所以Request对象中的 authenticators=self.get_authenticators()
是列表[类的对象],被传到了Request对象定义的这个 self.authenticators = authenticators or ()
传给了authenticators
里面,
_authenticate源码里面的for循环,就是self.authenticators 配置的一堆认证产生类对象组成的list,每次循环拿到一个对象
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()
authenticator.authenticate这个就是为什么要重写这个方法,执行这个方法,返回的时候是两个值,解压赋值,认证逻辑通过,返回两个值,一个值给了Request对象的user,认证失败,抛出异常APIException,也可以抛出AuthenticationFailed
它里面也是继承了APIException
3 认证组件的使用
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
class MyAuthentication(BaseAuthentication):
def authenticate(self, request):
# 认证逻辑,通过返回两个值
token = request.GET.get('token') #token = request.META.get('HTTP_TOKEN')这个是放在请求头里面
if token:
from app01 import models
user_token = models.UserToken.objects.filter(token=token).first()
if user_token:
return user_token.user,token
else:
raise AuthenticationFailed('认证失败')
else:
raise AuthenticationFailed('请求地址中需要携带token')
4 全局使用和局部禁用
全局使用在settings里面配置
REST_FRAMEWORK={
"DEFAULT_AUTHENTICATION_CLASSES":["app01.app_auth.MyAuthentication",]
}
局部使用:
authentication_classes=[MyAuthentication]
局部禁用:在登陆的时候就不需要,因为你还没登录呢,如何做认证
authentication_classes=[] 里面什么都不加就可以了