drf路由与认证

一、路由

三种路由配置

1 没有继承视图集的视图类

# urls.py
path('books4/', views.Book4View.as_view()),
re_path('books4/(?P<pk>d+)', views.Book4DetailView.as_view())

2 继承了视图集的视图类

# urls.py
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'})),
# 这里的方法名可以修改

3 继承自ModelViewSet的路由写法(自动生成)

# 1 
# 导入routers类
from rest_framework import routers

# 2
# 实例化得到routers对象
# 这种比下面simple多几个没用的路由
# router = routers.DefaultRouter()
# 只有两个路由,一个是带pk参数,一个不带参数
# ^student4/$ [name='student-list']   student4/
# ^student4/(?P<pk>[^/.]+)/$ [name='student-detail']  student4/4/
router = routers.SimpleRouter()

# 3
# 注册 router.register('前缀','继承自ModelViewSet视图类','别名')
router.register('student4',views.Students4API)

# 4
urlpatterns+=router.urls

action参数

# 当我们需要自定义方法名也能自动生成路由的时候
from rest_framework.decorators import action
# methods:放一个需要对于的请求列表
# detail=True 带pk参数,False 不带pk参数
@action(methods=['GET'],detail=True)
    def get_xxx(self,request,pk):
        return Response({'pk':pk})

二、认证

1 drf认证的源码分析

# 分析APIView重写的dispatch方法,发现它添加了一个initial方法,这个方法内涵了drf的三大校验:认证,权限,频率
# 点进去这个方法,删掉没用的,发现最后就调用了这三个方法,第一个是认证的方法
    def initial(self, request, *args, **kwargs):
        self.perform_authentication(request)
        self.check_permissions(request)
        self.check_throttles(request)
# perform_authentication内部只有一个代码就是request.user
# 这里要注意,APIView重写了request,所以这里看到的request实际上是drf写的
# 所以我们在找user的时候要去drf的Request类中
# 最后发现user原来是一个被封装成属性的方法
 @property
    def user(self):
        # 如果request没有_user属性,执行self._authenticate()
        # 到Request的__init__方法看,果然没有
        if not hasattr(self, '_user'):
            with wrap_attributeerrors():
                self._authenticate()
        return self._user
# 最后我们点到_authenticate()方法,再展开这个方法前,我们需要找到其中最关键的参数authenticators
# 先去实例化的对象里,也就是dispatch内定义的request找
# 是通过一个initialize_request方法实例化了request
# 在这里发现了我们要找的authenticators
# 是调用了get_authenticators得到的
# 内部返回了一个[auth() for auth in self.authentication_classes]
# 这是一个列表生成式,循环self.authentication_classes,对象内没有这个属性,去Request类中找
# 发现他的来源是api_settings.xxx
# 这里就不再展开了,只要知道这是一个配置的常量,内部是一个列表,存放着我们要认证的类
# 查找顺序是先从视图类(局部),其次项目settings(全局),最后apisettings(默认)
# 只要在一个地方配置了,就会向下覆盖
# 再回到这个列表生成式,是调用了存放在列表中的认证类,在把实例化的对象放入新的列表authenticators
 def _authenticate(self):
    	# 循环拿到的是一个个认证对象
        for authenticator in self.authenticators:
            try:
                # 调用了认证对象的authenticate,把request传进去
                user_auth_tuple = authenticator.authenticate(self)
            except exceptions.APIException:
                # 如果没有这个方法,就会抛异常,所以从这里可以知道,如果要自定义认证类,就必须重写类中的authenticate方法
                # 并按固定格式返回数据
                self._not_authenticated()
                raise
			# 把返回的参数赋值给request,第一个参数默认必须是用户,第二个随便
            if user_auth_tuple is not None:
                self._authenticator = authenticator
                self.user, self.auth = user_auth_tuple
                return
          # 如果返回值user_auth_tuple为空,代表认证通过,但是没有登陆用户与登陆认证信息,代表游客
        self._not_authenticated()

2 自定义认证类的使用

# 自定义认证类
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
class TokenAuthentication(BaseAuthentication):
    def authenticate(self,request):
        token = request.GET.get('token')
        username = request.GET.get('username')
        obj = UserToken.objects.filter(token=token)
        user = User.objects.filter(username=username).first()
        if obj:
            return user,obj
        else:
            raise AuthenticationFailed('认证失败')

view.py

class Students2API(GenericAPIView,ListModelMixin,CreateModelMixin):
    queryset = models.Student.objects
    serializer_class = ser.StudentModelSerializer
    authentication_classes = [TokenAuthentication]
    # 局部配置
    def get(self,request):
        return self.list(request)
    def post(self,request):
        return self.create(request)
class Student2API(GenericAPIView,UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin):
    queryset = models.Student.objects
    serializer_class = ser.StudentModelSerializer
    def put(self,request,pk):
        return self.update(request,pk)
    def get(self,request,pk):
        return self.retrieve(request,pk)
    def delete(self,request,pk):
        return self.destroy(request,pk)
    
class UserAPI(APIView):
    # 登录视图
    def post(self,request):
        username = request.data.get('username')
        password = request.data.get('password')
        user_obj = models.User.objects.filter(username=username,password=password).first()
        back_dic = {'code':200,'msg':'登录成功'}
        if user_obj:
            # 登录成功
            import uuid
            token = uuid.uuid4()
  models.UserToken.objects.update_or_create(user=user_obj,defaults={'token':token})
            back_dic['user'] = username
            back_dic['token'] = token
        else:
            back_dic['msg'] = '账号或密码错误'
        return Response(back_dic)
原文地址:https://www.cnblogs.com/hz2lxt/p/13275564.html