Django rest framework之路由组件、认证Authentication及权限Permissions

1、路由Routers

对于视图集ViewSet,我们除了可以自己手动指明请求方式与动作action之间的对应关系外,还可以使用Routers来帮助我们快速实现路由信息

REST framework提供了两个router

  • SimpleRouter
  • DefaultRouter
1.1、使用方法
1.1.1、创建router对象,并注册视图集
from rest_framework import routers

router = routers.SimpleRouter()
router.register(r'router_book', BookModelViewSet, base_name='book')

register(prefix, viewset, base_name)

  • prefix 该视图集的路由前缀
  • viewset 视图集
  • base_name 路由别名的前缀

如上述代码会形成的路由如下

^router_book/$ [name='book-list']
^router_book/(?P<pk>[^/.]+)/$ [name='book-detail']
1.1.2、添加路由数据
1.1.2.1、方式一
urlpatterns = [
    ...
]
urlpatterns += router.urls
1.1.2.2、方式二
urlpatterns = [
    ...
    url(r'^', include(router.urls))
]
1.2、示例

使用路由类给视图集生成了路由地址

# 必须是继承ModelViewSet的视图类才能自动生成路由
from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet
class StudentModelViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
		# 这种方法不会自动生成,需要用action配置
    def login(self,request):
        """学生登录功能"""
        print(self.action)
        return Response({"message":"登录成功"})

路由配置

from django.urls import path, re_path
from . import views
urlpatterns = [
    ...
]

"""使用drf提供路由类router给视图集生成路由列表"""
# 实例化路由类
# drf提供一共提供了两个路由类给我们使用,他们用法一致,功能几乎一样
from rest_framework.routers import DefaultRouter
router = DefaultRouter()

# 注册视图集
# router.register("路由前缀",视图集类)
router.register("router_stu",views.StudentModelViewSet)

# 把生成的路由列表追加到urlpatterns
print( router.urls )
urlpatterns += router.urls

上面的代码就成功生成了路由地址[增/删/改/查一条/查多条的功能],但是不会自动我们在视图集自定义方法的路由

所以我们如果也要给自定义方法生成路由,则需要进行action动作的声明

1.3、视图集中附加action的声明

在视图集中,如果想要让Router自动帮助我们为自定义的动作生成路由信息,需要使用rest_framework.decorators.action装饰器。

以action装饰器装饰的方法名会作为action动作名,与list、retrieve等同

action装饰器可以接收两个参数:

  • methods::声明该action对应的请求方式,列表传递
  • detail:声明该action的路径是否与单一资源对应
    xxx/<pk>/action方法名/
    True 表示路径格式是xxx/<pk>/action方法名/
    False 表示路径格式是xxx/action方法名/

示例

from rest_framework.viewsets import ModelViewSet
from rest_framework.decorators import action

class StudentModelViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

    # methods 设置当前方法允许哪些http请求访问当前视图方法
    # detail 设置当前视图方法是否是操作一个数据
    # detail为True,表示路径名格式应该为 router_stu/{pk}/login/
    @action(methods=['get'], detail=False)
    def login(self, request):
        return Response({'msg': '登陆成功'})

    @action(methods=['put'], detail=True)
    def get_new_5(self, request,pk):
        return Response({'msg': '获取5条数据成功'})

由路由器自动为此视图集自定义action方法形成的路由会是如下内容:

^router_stu/get_new_5/$    name: router_stu-get_new_5
^router_stu/{pk}/login/$   name: router_stu-login

在这里插入图片描述

1.4、路由router形成URL的方式
1.4.1、SimpleRouter

在这里插入图片描述
在这里插入图片描述

1.4.2、DefaultRouter

在这里插入图片描述
在这里插入图片描述
DefaultRouter与SimpleRouter的区别是,DefaultRouter会多附带一个默认的API根视图,返回一个包含所有列表视图的超链接响应数据

2、认证Authentication

2.1、自定义认证方案
2.1.1、models
class User(models.Model):
    username=models.CharField(max_length=32)
    password=models.CharField(max_length=32)
    user_type=models.IntegerField(choices=((1,'超级用户'),(2,'普通用户'),(3,'初级用户')))

class UserToken(models.Model):
    user=models.OneToOneField(to='User')
    token=models.CharField(max_length=64)
2.1.2、新建认证类
2.1.2.1、方式一
from rest_framework.authentication import BaseAuthentication
class TokenAuth():
    def authenticate(self, request):
        token = request.GET.get('token')
        token_obj = models.UserToken.objects.filter(token=token).first()
        if token_obj:
            return
        else:
            raise AuthenticationFailed('认证失败')
            
    def authenticate_header(self,request):
        pass
2.1.2.2、方式二
from rest_framework.authentication import BaseAuthentication
class TokenAuth(BaseAuthentication):
    def authenticate(self, request):
        token = request.GET.get('token')
        token_obj = models.UserToken.objects.filter(token=token).first()
        if token_obj:
            return
        else:
            raise AuthenticationFailed('认证失败')
2.1.3、视图
def get_random(name):
    import hashlib
    import time
    md=hashlib.md5()
    md.update(bytes(str(time.time()),encoding='utf-8'))
    md.update(bytes(name,encoding='utf-8'))
    return md.hexdigest()
    
class Login(APIView):
    def post(self,reuquest):
        back_msg={'status':1001,'msg':None}
        try:
            name=reuquest.data.get('name')
            pwd=reuquest.data.get('pwd')
            user=models.User.objects.filter(username=name,password=pwd).first()
            if user:
                token=get_random(name)
                models.UserToken.objects.update_or_create(user=user,defaults={'token':token})
                back_msg['status']='1000'
                back_msg['msg']='登录成功'
                back_msg['token']=token
            else:
                back_msg['msg'] = '用户名或密码错误'
        except Exception as e:
            back_msg['msg']=str(e)
        return Response(back_msg)

class Course(APIView):
    authentication_classes = [TokenAuth, ]

    def get(self, request):
        return HttpResponse('get')

    def post(self, request):
        return HttpResponse('post')
2.1.4、全局使用
REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":["TokenAuth的名称空间",]
}
2.1.5、局部使用
# 局部使用,只需要在视图类里加入:
authentication_classes = [TokenAuth, ]  # TokenAuth需要导入
2.2、内置认证方案(需要配合权限使用)

可以在配置文件中配置全局默认的认证方案

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.SessionAuthentication',  # session认证
        'rest_framework.authentication.BasicAuthentication',   # 基本认证
    )
}

也可以在每个视图中通过设置authentication_classess属性来设置

from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.views import APIView

class ExampleView(APIView):
    # 类属性
    authentication_classes = [SessionAuthentication, BasicAuthentication]
    ...

认证失败会有两种可能的返回值:

  • 401 Unauthorized 未认证
  • 403 Permission Denied 权限被禁止
2.3、认证功能源码分析

APIView —> dispatch —> self.initial(request, *args, **kwargs) —> self.perform_authentication(request) —> Request.user —> self._authenticate(self):Request类的方法 —> self.authenticators:Request类的属性 —> 在Request对象实例化的时候传入的 —> Request的实例化是在dispatch的时候 —> APIView:self.get_authenticators() —> return [auth() for auth in self.authentication_classes] —> 如果在自己定义的视图类中写了authentication_classes=[类1,类2] —> Request的self.authenticators就变成了我们配置的一个个类的对象

3、权限Permissions

权限控制可以限制用户对于视图的访问和对于具体数据对象的访问

  • 在执行视图的dispatch()方法前,会先进行视图访问权限的判断
  • 在通过get_object()获取具体对象时,会进行模型对象访问权限的判断
3.1、自定义权限
3.1.1、编写权限类
# 限制只有超级用户能访问
from rest_framework.permissions import BasePermission
class UserPermission(BasePermission):
    message = '不是超级用户,查看不了'
    def has_permission(self, request, view):
        # user_type = request.user.get_user_type_display()
        # if user_type == '超级用户':
        # 权限在认证之后,所以能取到user
        user_type = request.user.user_type
        print(user_type)
        if user_type == 1:
            return True
        else:
            return False
3.1.2、全局使用
REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":["TokenAuth的名称空间",]
    "DEFAULT_PERMISSION_CLASSES":["UserPermission的名称空间",]
}
3.1.3、局部使用
# 局部使用只需要在视图类里加入:
permission_classes = [UserPermission,]
3.1.4、说明
如需自定义权限,需继承rest_framework.permissions.BasePermission父类,并实现以下两个任何一个方法或全部
has_permission(self, request, view): 是否可以访问视图, view表示当前视图对象
has_object_permission(self, request, view, obj): 是否可以访问数据对象, view表示当前视图, obj为数据对象
3.2、内置权限
3.2.1、内置权限类
from rest_framework.permissions import AllowAny,IsAuthenticated,IsAdminUser,IsAuthenticatedOrReadOnly
AllowAny 允许所有用户
IsAuthenticated 仅通过认证的用户
IsAdminUser 仅管理员用户
IsAuthenticatedOrReadOnly 已经登陆认证的用户可以对数据进行增删改操作,没有登陆认证的只能查看数据。
3.2.2、全局使用

可以在配置文件中全局设置默认的权限管理类

REST_FRAMEWORK = {
    ....
    
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    )
}

如果未指明,则采用如下默认配置

'DEFAULT_PERMISSION_CLASSES': (
   'rest_framework.permissions.AllowAny',
)
3.2.3、局部使用

也可以在具体的视图中通过permission_classes属性来设置

from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView

class ExampleView(APIView):
    permission_classes = (IsAuthenticated,)
    ...
3.2.4、实际操作
# 创建超级用户,登陆到admin,创建普通用户(注意设置职员状态,也就是能登陆)
# 全局配置IsAuthenticated
# setting.py
'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    )
# urls.py
 path('test/', views.TestView.as_view()),
# views.py
class TestView(APIView):
    def get(self,request):
        return Response({'msg':'个人中心'})
# 登陆到admin后台后,直接访问可以,如果没登陆,不能访问

##注意:如果全局配置了
rest_framework.permissions.IsAdminUser
# 就只有管理员能访问,普通用户访问不了
3.3、权限源码分析
APIView的dispatch ---> APIView的initial ---> APIView的check_permissions(request)

for permission in self.get_permissions():  # 权限类对象放到列表中
    if not permission.has_permission(request, self):
        self.permission_denied(
             request,
             message=getattr(permission, 'message', None),
             code=getattr(permission, 'code', None)
            )
            
通过阅读源码, 我们知道错误信息的中文显示
在权限类中加一个 message=字符串
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值