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': '阅读量'
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY