• DRF之路由和认证


    一、路由Routers

    在 Rest Framework 中提供了两个 router , 可以帮助我们快速的实现路的自动生成。

    必须是继承 ModelViewSet 的视图类才能自动生成路由

    SimpleRouter

    使用方法:

    urls.py

    # 第一步:导入routers模块
    from rest_framework import routers
    
    # 第二步:实例化得到对象
    router = routers.SimpleRouter()  
    
    # 第三步:注册( register('前缀', viewset视图集, 路由的别名) )
    router.register('books', views.BooksViewset)
    
    # 第四步:生成路由加入到原路由中
    # 方式一:
    urlpatterns = [
        ...
    ]
    urlpatterns += router.urls
    
    # 方式二:
    urlpatterns = [
        ...
        url(r'^', include(router.urls))
    ]
    
    
    # 形成路由如下
    <URLPattern '^books/$' [name='books-list']>
    <URLPattern '^books/(?P<pk>[^/.]+)/$' [name='books-detail']>
    

    DefaultRouter

    DefaultRouter与SimpleRouter的区别是,DefaultRouter会多附带一个默认的API根视图,返回一个包含所有列表视图的超链接响应数据。

    # 前两条和SimpleRouter一样
    <URLPattern '^books/$' [name='books-list']>
    <URLPattern '^books/(?P<pk>[^/.]+)/$' [name='books-detail']>
    
    # 效果也和前两条类似,
    # 如:http://127.0.0.1:8000/books.json
    <URLPattern '^books.(?P<format>[a-z0-9]+)/?$' [name='books-list']>
    # http://127.0.0.1:8000/books/1.json
    <URLPattern '^books/(?P<pk>[^/.]+).(?P<format>[a-z0-9]+)/?$' [name='books-detail']>
    
    # 多了个根路由http://127.0.0.1:8000/
    <URLPattern '^$' [name='api-root']>, <URLPattern '^.(?P<format>[a-z0-9]+)/?$' [name='api-root']>
    

    action的使用

    action是为了给继承自 ModelViewSet 的视图类中自定义的函数也添加路由

    例如下面这样:

    from rest_framework.viewsets import ModelViewSet
    from rest_framework.response import Response
    from app01.ser import BooksSerializers
    from app01.models import Books
    
    
    class BooksViewSet(ModelViewSet):
        queryset = Books.objects.all()
        serializer_class = BooksSerializers
    
        # 这种方法不会自动生成,需要用action配置
        def get_num(self, request, pk):
            book = self.get_queryset()[:int(pk)]
            ser = self.get_serializer(book, many=True)
            return Response(ser.data)
    

    使用示例:

    action是一个装饰器,放在被装饰的函数上方,

    method:请求方式

    detail:是否带pk ——>True 表示路径格式是xxx/<pk>/action方法名/——False 表示路径格式是xxx/action方法名/

    from rest_framework.viewsets import ModelViewSet
    from rest_framework.response import Response
    from rest_framework.decorators import action
    from app01.ser import BooksSerializers
    from app01.models import Books
    
    
    class BooksViewSet(ModelViewSet):
        queryset = Books.objects.all()
        serializer_class = BooksSerializers
    
        @action(methods=['GET', 'POST'], detail=True)
        def get_num(self, request, pk):
            book = self.get_queryset()[:int(pk)]	# 获取前几条数据
            ser = self.get_serializer(book, many=True)
            return Response(ser.data)
        
        
    # 生成路由如下
    http://127.0.0.1:8000/books/2/get_num/
    <URLPattern '^books/(?P<pk>[^/.]+)/get_num/$' [name='books-get-num']>
    

    二、认证

    认证的写法

    1. 写一个认证类,继承 BaseAuthentication,重写 authenticate, 认证的逻辑写在里面,认证通过,返回两个值,一个值给Request对象的user, 认证失败,抛异常:APIException或者AuthenticationFailed
    2. 将认证类添加到需要认证视图类的authentication_classes = [认证类1]
    3. 全局使用,还是局部使用
    # 全局使用,在setting.py中配置
    REST_FRAMEWORK={
        "DEFAULT_AUTHENTICATION_CLASSES":["app01.app_auth.MyAuthentication",]
    }
    
    # 局部使用,在视图类上写
    authentication_classes=[MyAuthentication]
    # 局部禁用
    authentication_classes=[]
    

    认证源码分析

    1、APIView重写as_view方法使之没有csrf认证——>但还是正常执行 dispatch 方法,但是 dispatch方法被 APIView重写了——>dispatch 中执行了 self.initial 认证方法——>有认证,权限,频率

    2、现在只是看认证源码self.perform_authentication(request)

    3、但是self.perform_authentication(request)就一句话:request.user,那么就需要去 drf 的 Request 对象中找 user 属性(方法)

    @property
    def user(self):
        # 先去判断当前对象中有没有'_user'这个属性,一开始肯定是没有的,因为用户是没有登录的
        if not hasattr(self, '_user'):
            with wrap_attributeerrors():
                # 没有用户,认证出用户
                self._authenticate()
        # 有用户,直接返回用户
        return self._user
    

    4、Request 类中的 user 方法,刚开始来,没有_user,走 self._authenticate()

    5、核心,就是Request类中的 _authenticate(self)

    def _authenticate(self):
        
        # 遍历拿到一个认证器,进行认证
        # self.authenticators 配置的一堆认证类产生的认证类对象组成的 list
        # self.authenticators 就是在视图类中配置的:authentication_classes = [认证类1,认证类2] 的一个个认证类的对象:
        
        ————>self.authenticators ==》 [认证类1对象,认证类2对象]
        for authenticator in self.authenticators:
            try:
                
                # 认证器调用认证方法authenticate(认证类对象self,request对象)
                """
                def authenticate(self, request):
            		return (self.force_user, self.force_token)
                """
                # 返回值:登录的用户与认证的信息组成的 tuple
                # 并且该方法被try包裹,就代表该方法会抛异常,抛异常就代表认证失败
                user_auth_tuple = authenticator.authenticate(self) # self是request对象
            except exceptions.APIException:
                self._not_authenticated()
                raise
    		
            # 返回值的处理
            if user_auth_tuple is not None:
                self._authenticator = authenticator
                # 如果有返回值,就将 "登录用户" 与 "登录认证" 分别保存到 request.user / request.auth
                self.user, self.auth = user_auth_tuple
                return
    	# 如果返回值user_auth_tuple为空,代表认证通过,但是没有 "登录用户" 与 "登录认证信息",代表游客
        self._not_authenticated()
    

    认证组件的使用

    1、写一个认证类,继承 BaseAuthentication,重写 authenticate

    # app01_auth.py
    from rest_framework.authentication import BaseAuthentication
    from rest_framework.exceptions import AuthenticationFailed
    from app01.models import UserToken
    
    
    class TokenAuthentication(BaseAuthentication):
        def authenticate(self, request):
            # 认证逻辑,如果认证通过,返回两个值
            # 如果认证失败,抛出AuthenticationFailed异常
            token = request.data.get('token')
            if token:
                user_token = UserToken.objects.filter(token=token).first()
    
                # 认证通过
                if user_token:
                    return user_token.user, token
                else:
                    raise AuthenticationFailed('认证失败')
            else:
                raise AuthenticationFailed('请求地址中需要带token')
    

    2、将认证类添加到需要认证视图类的authentication_classes = [认证类1]

    # views.py
    from rest_framework.viewsets import ModelViewSet
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from app01.models import Books, UserInfo, UserToken
    from app01.ser import BooksSerializer
    from app01.app01_auth import TokenAuthentication
    import uuid
    
    
    # 查看Books需要经过认证才能查看
    class BooksView(ModelViewSet):
        authentication_classes = [TokenAuthentication]
    
        queryset = Books.objects.all()
        serializer_class = BooksSerializer
    
    
    # 登录视图,登录后获得token,后续用token认证
    class LoginView(APIView):
    
        def post(self, request):
            response_msg = {'status': 200, 'msg': ''}
            username = request.data.get('username')
            password = request.data.get('password')
            user_obj = UserInfo.objects.filter(username=username, password=password).first()
    
            if user_obj:
                # 登录成功生成一个随机字符串
                token = uuid.uuid4()
                
                # 存到UserToken表中,update_or_create有就更新,没有就新增
                UserToken.objects.update_or_create(defaults={'token': token}, user=user_obj)
                response_msg['msg'] = '登录成功'
                response_msg['token'] = token
            else:
                response_msg['msg'] = '账户或密码错误'
                response_msg['status'] = 204
            return Response(response_msg)
    
    学习之旅
  • 相关阅读:
    这样的UI UX设计师描述你满意吗?
    原型设计建议规范(二)
    优质产品需求文档(PRD)写作三大原则
    一流用户体验设计师的6个共同点
    在原型设计上,UI和UX设计师有哪三个区别?
    UI设计:掌握这6点,轻松0到1
    优秀UX设计师的八条黄金法则
    hdu1686Oulipo(KMP)
    hdu1711Number Sequence(KMP)
    hdu2612Find a way
  • 原文地址:https://www.cnblogs.com/XiaoYang-sir/p/14993045.html
Copyright © 2020-2023  润新知