c、认证、权限、限制、分页、过滤和文档

认证、权限、限制、分页、过滤和文档

简单来说就是:

认证确定了你是谁

权限确定你能不能访问某个接⼝

限制确定你访问某个接⼝的频率

⼀、认证

身份验证是将传⼊请求与⼀组标识凭据(例如请求来⾃的⽤户或其签名的令牌)相关联的机制。然后权限和限制组件决定是否拒绝这个请求。认证本身不会允许或拒绝传⼊的请求,它只是简单识别请求携带的凭证。

REST framework 提供了⼀些开箱即⽤的身份验证⽅案,并且还允许你实现⾃定义
⽅案。

1.⾃定义认证

要实现⾃定义的认证⽅案,要继承BaseAuthentication类并且重写.authenticate(self, request) ⽅法。如果认证成功,该⽅法应返回(user, auth)的⼆元元组,否则返回None。

在某些情况下,你可能不想返回None,⽽是希望从.authenticate()⽅法抛出AuthenticationFailed异常。

在App下,创建authentications.py,然后⾃定义认证类

from django.core.cache import cache
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed # ⽤于抛出错误信息

from App.models import User

class MyAuthentication(BaseAuthentication):
    def authenticate(self, request):
        token = request.query_params.get('token')
        if not token:
            raise AuthenticationFailed("没有token")
        uid = cache.get(token)
        if not uid:
            raise AuthenticationFailed("token过期")
        try:
            user = User.objects.get(pk=uid)
            if user:
                return user,None
            else:
                raise AuthenticationFailed("token不存在")
        except Exception as e:
            raise AuthenticationFailed("token不合法")

2.全局级别认证配置

在settings中配置

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'App.authentications.MyAuthentication',   
    )
}

3.视图级别认证配置

from App.authentications import MyAuthentication
from rest_framework.generics import import GenericAPIView

class ExampleView(GenericAPIView):
    authentication_classes = (MyAuthentication, )
    ...

4.实现⼀个⾃定义认证⽅案

  • 定义⼀个⽤户模型,访问⽤户信息
class User(models.Model):
    username = models.CharField(unique=True, max_length=150)
    password = models.CharField(max_length=128)
    usertype = models.IntegerField(choices=((1,'超管'),(2,'管理员'),(3,'普通⽤户')),default=3)
    email = models.CharField(max_length=254,null=True)
    date_joined = models.DateTimeField(default=datetime.now,null=True)

	class Meta:
	    db_table = 'user'
  • ⽤户序列化模型
class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = "__all__"
  • 实现⽤户注册
from rest_framework.generics import CreateAPIView,GenericAPIView
from rest_framework.response import Response

class RegisterView(CreateAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    def post(self, request, *args, **kwargs):
        request.data._mutable = True  # 修改data中的数据
        request.data["password"] = make_password(request.data["password"])
        return self.create(request, *args, **kwargs)
  • 实现登录
import uuid

from django.contrib.auth.hashers import make_password, check_password
from django.core.cache import cache
from rest_framework.generics import 
ListAPIView,CreateAPIView,GenericAPIView
from rest_framework.response import Response
class UserLoginView(GenericAPIView):
    authentication_classes = (MyAuthentication,)
    def post(self, request, *args, **kwargs):
        username = request.data.get('username')
        password = request.data.get('password','')
        user = User.objects.filter(username=username).first()
        if user and check_password(password,user.password):
            # 产⽣token
            token = uuid.uuid4().hex

            # 写⼊缓存
            cache.set(token,user.id,3600)
            return Response({'code':1,'msg':'登录成功','token':token})
        else:
            return Response({'code': -1, 'msg': '⽤户名或密码错误'})

⼆、权限控制

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

  • 在执⾏视图的dispatch()⽅法前,会先进⾏视图访问权限的判断
  • 在通过get_object()获取具体对象时,会进⾏对象访问权限的判断

rest_framework也给我提供了相应的⽀持,接下来看下他的原码:

class BasePermission(object):
    """
    A base class from which all permission classes should inherit.
    """
    #判断是否对视图有权限
    def has_permission(self, request, view):
        """
        Return `True` if permission is granted, `False` otherwise.
        """
        return True
    #判断是否对某个模型有权限
    def has_object_permission(self, request, view, obj):
        """
        Return `True` if permission is granted, `False` otherwise.
        """
        return True

系统内置的权限

  • AllowAny 允许所有⽤户
  • IsAuthenticated 仅通过认证的⽤户
  • IsAdminUser 仅管理员⽤户
  • IsAuthenticatedOrReadOnly 认证的⽤户可以完全操作,否则只能get读取

代码实现

如果我们想编写⼀个权限的控制

  • ⾸先,我们要在app⽬录下新建⼀个 ⽂件⽐如是permission.py,然后⾃定义⼀个权限类
from rest_framework.permissions import BasePermission
#写⼀个继承⾃BasePermission
class SuperPerssion(BasePermission):
    #复写⾥⾯的has_permission函数
    def has_permission(self, request, view):
        #判断当前⽤户是不是超级管理员
        return request.user.is_superuser

class StaffPerssion(BasePermission):
	def has_permission(self, request, view):
    	return request.user.is_staff
  • 对应的Serializer
from django.contrib.auth.models import User
from rest_framework import serializers
from .models import Book

class UsersSerializer(serializers.ModelSerializer):

class Meta:
    model = User
    fields = ("id", "username", "email")
  • 视图级别权限认证
class UsersAPI(ListAPIView):
    queryset = User.objects.all()
    #指定使⽤的序列化器
    serializer_class = UsersSerializer
    authentication_classes = (MyAuthentication, )
    #权限控制
    permission_classes = (SuperPerssion, StaffPerssion)
    def list(self, request, *args, **kwargs):
        user = request.user
        if not isinstance(user, User):
            return Response({
                "code": LOGIN_FAIL_1_CODE,
                "msg": "⽤户名或密码错误"
            })
            queryset = self.filter_queryset(self.get_queryset())

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)
  • 全局级别的权限认证

在settings中配置

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'App.authentications.MyAuthentication',   
    ),
    "DEFAULT_PERMISSION_CLASSES": 
["app.permission.SuperPerssion","app.permission.StaffPerssion" ]
}

三、节流器

节流类似于权限,⽤于控制客户端可以对API发出的请求的速率。

  • ⾃定义类继承内置类
#在app下⾃定义mythrottle.py
from rest_framework.throttling import  SimpleRateThrottle
class VisitThrottle(SimpleRateThrottle):
    #转换频率每分钟5次,转换频率 = num/duration,其中duration可以是s(秒)、m(分)、h(⼩时)、d(天)
    rate = '5/m'  
    score='vistor'
    #返回⼀个唯⼀标示⽤以区分不同的⽤户
    def get_cache_key(self, request, view):
        return self.get_ident(request) #返回⽤户ip
  • 局部限制
#视图类
# 获取当前⽤户创建的书籍 要包括⽤户信息和他所有相关书籍的数据
class xxxAPIView(ListAPIView):
    throttle_classes = (VisitThrottle,)
    ...
  • 全局限制
#在settings中设置
'DEFAULT_THROTTLE_CLASSES': ['apps.mythrottle.VisitThrottle'],

 'DEFAULT_THROTTLE_RATES': {
      'vistor': '3/m',
       'anon': '10/day',# 匿名⽤户
    },

DEFAULT_THROTTLE_RATES 可以使⽤second , minute , hour 或 day 来指明周期。

可选限流类

1) AnonRateThrottle

限制所有匿名未认证⽤户,使⽤IP区分⽤户。

使⽤ DEFAULT_THROTTLE_RATES['anon'] 来设置频次

2)UserRateThrottle

限制认证⽤户,使⽤User id 来区分。

使⽤ DEFAULT_THROTTLE_RATES['user'] 来设置频次

3)ScopedRateThrottle

限制⽤户对于每个视图的访问频次,使⽤ip或user id。

例如:

class ContactListView(APIView):
    throttle_scope = 'contacts'
    ...

class ContactDetailView(APIView):
    throttle_scope = 'contacts'
    ...

class UploadView(APIView):
    throttle_scope = 'uploads'
    ...
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': (
        'rest_framework.throttling.ScopedRateThrottle',
    ),
    'DEFAULT_THROTTLE_RATES': {
        'contacts': '1000/day',
        'uploads': '20/day'
    }
}

四、分⻚Pagination

REST framework提供了分⻚的⽀持。

我们可以在配置⽂件中设置全局的分⻚⽅式,如:

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 
 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 10  # 每⻚数⽬
}

也可通过⾃定义Pagination类,来为视图添加不同分⻚⾏为。在视图中通过pagination_clas 属性来指明。

class LargeResultsSetPagination(PageNumberPagination):
    page_size = 1000
    page_size_query_param = 'page_size'
    max_page_size = 10000
class BookDetailView(RetrieveAPIView):
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer
    pagination_class = LargeResultsSetPagination

注意:如果在视图内关闭分⻚功能,只需在视图内设置

pagination_class = None

可选分⻚器

1) PageNumberPagination

前端访问⽹址形式:

GET  http://api.example.org/books/?page=4

可以在⼦类中定义的属性:

  • page_size 每⻚数⽬

  • page_query_param 前端发送的⻚数关键字名,默认为"page"

  • page_size_query_param 前端发送的每⻚数⽬关键字名,默认为None

  • max_page_size 前端最多能设置的每⻚数量

    from rest_framework.pagination import PageNumberPagination
    
    class StandardPageNumberPagination(PageNumberPagination):
        page_size_query_param = 'page_size'
        max_page_size = 10
    
    class BookListView(ListAPIView):
        queryset = BookInfo.objects.all().order_by('id')
        serializer_class = BookInfoSerializer
        pagination_class = StandardPageNumberPagination
    
    # 127.0.0.1/books/?page=1&page_size=2
    

2)LimitOffsetPagination

前端访问⽹址形式:

GET http://api.example.org/books/?limit=100&offset=400

可以在⼦类中定义的属性:

  • default_limit 默认限制,默认值与PAGE_SIZE 设置⼀致

  • limit_query_param limit参数名,默认'limit'

  • offset_query_param offset参数名,默认'offset'

  • max_limit 最⼤limit限制,默认None

    from rest_framework.pagination import LimitOffsetPagination
    
    class BookListView(ListAPIView):
        queryset = BookInfo.objects.all().order_by('id')
        serializer_class = BookInfoSerializer
        pagination_class = LimitOffsetPagination
    
    # 127.0.0.1:8000/books/?offset=3&limit=2
    

五、过滤

对于列表数据可能需要根据字段进⾏过滤,我们可以通过添加django-fitlter扩展来增强⽀持。官⽹地址:https://django-filter.readthedocs.io/en/master/index.html

1.安装

pip insall django-filter

django-filters⽀持的pytho和django版本:

  • Python: 3.5, 3.6, 3.7, 3.8
  • Django: 1.11, 2.0, 2.1, 2.2, 3.0
  • DRF: 3.10+

2.在配置⽂件中增加过滤后端的设置:

INSTALLED_APPS = [
    ...
    'django_filters',  # 需要注册应⽤,
]

REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': 
('django_filters.rest_framework.DjangoFilterBackend',)
}

3.在视图中添加filter_fields属性,指定可以过滤的字段

class BookListView(ListAPIView):
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer
    filter_fields = ('btitle', 'bread')

# 127.0.0.1:8000/books/?btitle=⻄游记

4.⾃定义过滤类

# customfilter.py
import django_filters
from django_filters import rest_framework as filters
from App.models import BookInfo
class BookFilter(django_filters.FilterSet):
    class Meta: 
        # field_name="bread" 模型中的字段名;lookup_expr是运算,gte表示 >=  
        min_read = filters.NumberFilter(field_name="bread", lookup_expr='gte')
        max_read = filters.NumberFilter(field_name="bread", lookup_expr='lte')
        model = BookInfo  # 模型
        fields = {
            'btitle':['icontains'],  # 键是字段名,列表⾥是查询进⾏运算
        }
# 127.0.0.1:8000/books/?btitle__icontains=笑
# 127.0.0.1:8000/books/?min_read=30&max_read=80

#IndexView.py
class IndexView(ListAPIView):
    """⾸⻚"""
    serializer_class = BookInfoSerializer
    queryset = BookInfo.objects.all()
    filter_class = BookFilter   #指定过滤类
  • field_name 数据库中字段名

  • lookup_expr 操作(和django的orm中运算符⼀样)

    • 关系运算符:gte(>=) 、gt(>)、 lte(<=)、 lt(<)
    • 字符串: icontains(忽略⼤⼩写,包含)
  • model指定模型

  • fields可以指定过滤字段

六、⾃动⽣成接⼝⽂档

REST framework可以⾃动帮助我们⽣成接⼝⽂档。

接⼝⽂档以⽹⻚的⽅式呈现。

⾃动接⼝⽂档能⽣成的是继承⾃APIView 及其⼦类的视图。

1、安装依赖

REST framewrok⽣成接⼝⽂档需要 coreapi 库的⽀持。

pip install coreapi

2、配置

在settings中配置

REST_FRAMEWORK = {
    'DEFAULT_SCHEMA_CLASS': 
'rest_framework.schemas.coreapi.AutoSchema'
}

3、设置接⼝⽂档访问路径

在总路由中添加接⼝⽂档路径。

⽂档路由对应的视图配置为rest_framework.documentation.include_docs_urls ,参数title 为接⼝⽂档⽹站的标题。

from rest_framework.documentation import include_docs_urls

urlpatterns = [
    ...
    url(r'^docs/', include_docs_urls(title='My API title'))
]

4、⽂档描述说明的定义位置千锋python人工智能学院

1) 单⼀⽅法的视图,可直接使⽤类视图的⽂档字符串,如

class BookListView(generics.ListAPIView):
    """
    返回所有图书信息.
    """

2)包含多个⽅法的视图,在类视图的⽂档字符串中,分开⽅法定义,如

class BookListCreateView(generics.ListCreateAPIView):
     """
     get:
     返回所有图书信息.
	 post:
	 新建图书.
	 """

3)对于视图集ViewSet,仍在类视图的⽂档字符串中分开定义,但是应使⽤action名称区分,如

class BookInfoViewSet(mixins.ListModelMixin, 
mixins.RetrieveModelMixin, GenericViewSet):
    """
    list:
    返回图书列表数据

    retrieve:
    返回图书详情数据

    latest:
    返回最新的图书数据

    read:
    修改图书的阅读量
    """

5、访问接⼝⽂档⽹⻚

浏览器访问 127.0.0.1:8000/docs/,即可看到⾃动⽣成的接⼝⽂档。

两点说明:

1) 视图集ViewSet中的retrieve名称,在接⼝⽂档⽹站中叫做read

2)参数的Description需要在模型类或序列化器类的字段中以help_text选项定义,如:

class BookInfo(models.Model):
    ...
    bread = models.IntegerField(default=0, verbose_name='阅读量', help_text='阅读量')
    ...

class BookReadSerializer(serializers.ModelSerializer):
    class Meta:
        model = BookInfo
        fields = ('bread', )
        extra_kwargs = {
            'bread': {
                'required': True,
                'help_text': '阅读量'
            }
        }
posted @   昵称已经被使用  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示