二 权限Permissions
权限控制可以限制用户对于视图的访问和对于具体数据对象的访问。
- 在执行视图的dispatch()方法前,会先进行视图访问权限的判断
- 在通过get_object()获取具体对象时,会进行模型对象访问权限的判断
2.1 自定义权限
2.1.1 编写权限类
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
from rest_framework.permissions import BasePermission class UserPermission(BasePermission): message = '不是超级用户,查看不了' def has_permission(self, request, view): user_type = request.user.user_type print(user_type) if user_type == 1: return True else: return False
|
2.1.2 全局使用
1 2 3 4
|
REST_FRAMEWORK={ "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",], "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",] }
|
2.1.3 局部使用
1 2
|
permission_classes = [UserPermission,]
|
2.1.4 说明
1 2 3 4 5 6 7
|
如需自定义权限,需继承rest_framework.permissions.BasePermission父类,并实现以下两个任何一个方法或全部 - `.has_permission(self, request, view)` 是否可以访问视图, view表示当前视图对象
- `.has_object_permission(self, request, view, obj)` 是否可以访问数据对象, view表示当前视图, obj为数据对象
|
2.2 内置权限
2.2.1 内置权限类
1 2 3 4 5
|
from rest_framework.permissions import AllowAny,IsAuthenticated,IsAdminUser,IsAuthenticatedOrReadOnly - AllowAny 允许所有用户 - IsAuthenticated 仅通过认证的用户 - IsAdminUser 仅管理员用户 - IsAuthenticatedOrReadOnly 已经登陆认证的用户可以对数据进行增删改操作,没有登陆认证的只能查看数据。
|
2.2.2 全局使用
可以在配置文件中全局设置默认的权限管理类,如
1 2 3 4 5 6 7
|
REST_FRAMEWORK = { ....
'DEFAULT_PERMISSION_CLASSES': ( 'rest_framework.permissions.IsAuthenticated', ) }
|
如果未指明,则采用如下默认配置
1 2 3
|
'DEFAULT_PERMISSION_CLASSES': ( 'rest_framework.permissions.AllowAny', )
|
2.2.3 局部使用
也可以在具体的视图中通过permission_classes属性来设置,如
1 2 3 4 5 6
|
from rest_framework.permissions import IsAuthenticated from rest_framework.views import APIView
class ExampleView(APIView): permission_classes = (IsAuthenticated,) ...
|
2.2.4 实际操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
'DEFAULT_PERMISSION_CLASSES': ( 'rest_framework.permissions.IsAuthenticated', )
path('test/', views.TestView.as_view()),
class TestView(APIView): def get(self,request): return Response({'msg':'个人中心'})
rest_framework.permissions.IsAdminUser
|
三 限流Throttling
可以对接口访问的频次进行限制,以减轻服务器压力。
一般用于付费购买次数,投票等场景使用.
3.1 自定义频率类
3.1.1 编写频率类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
class MyThrottles(): VISIT_RECORD = {} def __init__(self): self.history=None def allow_request(self,request, view): ip=request.META.get('REMOTE_ADDR') import time ctime=time.time() if ip not in self.VISIT_RECORD: self.VISIT_RECORD[ip]=[ctime,] return True self.history=self.VISIT_RECORD.get(ip) while self.history and ctime-self.history[-1]>60: self.history.pop() if len(self.history)<3: self.history.insert(0,ctime) return True else: return False def wait(self): import time ctime=time.time() return 60-(ctime-self.history[-1])
|
3.1.2 全局使用
1 2 3
|
REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES':['app01.utils.MyThrottles',], }
|
3.1.3 局部使用
1 2
|
throttle_classes = [MyThrottles,]
|
3.2 内置频率类
3.2.1 根据用户ip限制
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
from rest_framework.throttling import SimpleRateThrottle class VisitThrottle(SimpleRateThrottle): scope = 'luffy' def get_cache_key(self, request, view): return self.get_ident(request)
REST_FRAMEWORK = { 'DEFAULT_THROTTLE_RATES':{ 'luffy':'3/m' } }
|
了解:错误信息中文显示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
class Course(APIView): authentication_classes = [TokenAuth, ] permission_classes = [UserPermission, ] throttle_classes = [MyThrottles,]
def get(self, request): return HttpResponse('get')
def post(self, request): return HttpResponse('post') def throttled(self, request, wait): from rest_framework.exceptions import Throttled class MyThrottled(Throttled): default_detail = '傻逼啊' extra_detail_singular = '还有 {wait} second.' extra_detail_plural = '出了 {wait} seconds.' raise MyThrottled(wait)
|
3.2.2 限制匿名用户每分钟访问3次
1 2 3 4 5 6 7 8 9 10
|
REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': ( 'rest_framework.throttling.AnonRateThrottle', ), 'DEFAULT_THROTTLE_RATES': { 'anon': '3/m', } }
|
3.2.3 限制登陆用户每分钟访问10次
1 2 3 4 5 6 7 8 9
|
REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': ( 'rest_framework.throttling.UserRateThrottle' ), 'DEFAULT_THROTTLE_RATES': { 'user': '10/m' } }
|
3.2.4 其他
1) AnonRateThrottle
限制所有匿名未认证用户,使用IP区分用户。
使用DEFAULT_THROTTLE_RATES['anon'] 来设置频次
2)UserRateThrottle
限制认证用户,使用User id 来区分。
使用DEFAULT_THROTTLE_RATES['user'] 来设置频次
3)ScopedRateThrottle
限制用户对于每个视图的访问频次,使用ip或user id。
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
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' } }
|
实例
全局配置中设置访问频率
1 2 3 4
|
'DEFAULT_THROTTLE_RATES': { 'anon': '3/minute', 'user': '10/minute' }
|
1 2 3 4 5 6 7 8 9 10 11
|
from rest_framework.authentication import SessionAuthentication from rest_framework.permissions import IsAuthenticated from rest_framework.generics import RetrieveAPIView from rest_framework.throttling import UserRateThrottle
class StudentAPIView(RetrieveAPIView): queryset = Student.objects.all() serializer_class = StudentSerializer authentication_classes = [SessionAuthentication] permission_classes = [IsAuthenticated] throttle_classes = (UserRateThrottle,)
|
一 过滤Filtering
对于列表数据可能需要根据字段进行过滤,我们可以通过添加django-fitlter扩展来增强支持。
1
|
pip install django-filter
|
在配置文件中增加过滤后端的设置:
1 2 3 4 5 6 7 8 9
|
INSTALLED_APPS = [ ... 'django_filters', ]
REST_FRAMEWORK = { ... 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',) }
|
在视图中添加filter_fields属性,指定可以过滤的字段
1 2 3 4 5 6
|
class StudentListView(ListAPIView): queryset = Student.objects.all() serializer_class = StudentSerializer filter_fields = ('age', 'sex')
|
二 排序
对于列表数据,REST framework提供了OrderingFilter过滤器来帮助我们快速指明数据按照指定字段进行排序。
使用方法:
在类视图中设置filter_backends,使用rest_framework.filters.OrderingFilter过滤器,REST framework会在请求的查询字符串参数中检查是否包含了ordering参数,如果包含了ordering参数,则按照ordering参数指明的排序字段对数据集进行排序。
前端可以传递的ordering参数的可选字段值需要在ordering_fields中指明。
示例:
1 2 3 4 5 6 7 8 9
|
class StudentListView(ListAPIView): queryset = Student.objects.all() serializer_class = StudentModelSerializer filter_backends = [OrderingFilter] ordering_fields = ('id', 'age')
|
如果需要在过滤以后再次进行排序,则需要两者结合!
1 2 3 4 5 6 7 8 9 10 11 12
|
from rest_framework.generics import ListAPIView from students.models import Student from .serializers import StudentModelSerializer from django_filters.rest_framework import DjangoFilterBackend class Student3ListView(ListAPIView): queryset = Student.objects.all() serializer_class = StudentModelSerializer filter_fields = ('age', 'sex') filter_backends = [OrderingFilter,DjangoFilterBackend] ordering_fields = ('id', 'age')
|
REST framework提供了分页的支持。
我们可以在配置文件中设置全局的分页方式,如:
1 2 3 4
|
REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 100 }
|
也可通过自定义Pagination类,来为视图添加不同分页行为。在视图中通过pagination_clas属性来指明。
1 2 3 4 5 6 7 8
|
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
|
注意:如果在视图内关闭分页功能,只需在视图内设置
1
|
pagination_class = None
|
可选分页器
1) PageNumberPagination
前端访问网址形式:
1
|
GET http://127.0.0.1:8000/students/?page=4
|
可以在子类中定义的属性:
- page_size 每页数目
- page_query_param 前端发送的页数关键字名,默认为”page”
- page_size_query_param 前端发送的每页数目关键字名,默认为None
- max_page_size 前端最多能设置的每页数量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
|
from rest_framework.pagination import PageNumberPagination
class Pager(APIView): def get(self,request,*args,**kwargs): ret=models.Book.objects.all() page=PageNumberPagination() page_list=page.paginate_queryset(ret,request,view=self) ser=BookSerializer1(instance=page_list,many=True) return Response(ser.data)
class Mypage(PageNumberPagination): page_size = 2 page_query_param = 'page' page_size_query_param = 'size' max_page_size = 5 class Pager(APIView): def get(self,request,*args,**kwargs): ret=models.Book.objects.all() page=Mypage() page_list=page.paginate_queryset(ret,request,view=self) ser=BookSerializer1(instance=page_list,many=True) return page.get_paginated_response(ser.data)
from rest_framework.pagination import PageNumberPagination class StandardPageNumberPagination(PageNumberPagination): page_size = 2 page_size_query_param = "size" max_page_size = 10 page_query_param = "p"
class StudentAPIView(ListAPIView): queryset = Student.objects.all() serializer_class = StudentModelSerializer pagination_class = StandardPageNumberPagination
|
2)LimitOffsetPagination
前端访问网址形式:
1
|
GET http://127.0.0.1/four/students/?limit=100&offset=400
|
可以在子类中定义的属性:
- default_limit 默认限制,默认值与
PAGE_SIZE设置一直
- limit_query_param limit参数名,默认’limit’
- offset_query_param offset参数名,默认’offset’
- max_limit 最大limit限制,默认None
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
from rest_framework.pagination import LimitOffsetPagination
class Pager(APIView): def get(self,request,*args,**kwargs): ret=models.Book.objects.all() page=LimitOffsetPagination() page_list=page.paginate_queryset(ret,request,view=self) ser=BookSerializer1(instance=page_list,many=True) return Response(ser.data)
from rest_framework.pagination import LimitOffsetPagination class StandardLimitOffsetPagination(LimitOffsetPagination): default_limit = 2 limit_query_param = "size" offset_query_param = "start"
class StudentAPIView(ListAPIView): queryset = Student.objects.all() serializer_class = StudentModelSerializer pagination_class = StandardLimitOffsetPagination
|
3)CursorPagination
前端访问网址形式:
1
|
GET http://127.0.0.1/four/students/?cursor=cD0xNQ%3D%3D
|
可以在子类中定义的属性:
- cursor_query_param:默认查询字段,不需要修改
- page_size:每页数目
- ordering:按什么排序,需要指定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
from rest_framework.pagination import CursorPagination
class Pager(APIView): def get(self,request,*args,**kwargs): ret=models.Book.objects.all() page=CursorPagination() page.ordering='nid' page_list=page.paginate_queryset(ret,request,view=self) ser=BookSerializer1(instance=page_list,many=True) return page.get_paginated_response(ser.data)
class MyCursorPagination(CursorPagination): page_size=2 ordering='-id' from rest_framework.generics import ListAPIView class AuthorListView(ListAPIView): serializer_class = serializers.AuthorModelSerializer queryset = models.Author.objects.filter(is_delete=False) pagination_class =MyCursorPagination
|
应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination,CursorPagination class MyPageNumberPagination(PageNumberPagination): page_size = 2 page_query_param = 'page' page_size_query_param = 'size' max_page_size = 5
class MyLimitOffsetPagination(LimitOffsetPagination): default_limit = 2 max_limit = 5 class MyCursorPagination(CursorPagination): page_size=2 ordering='-id' from rest_framework.generics import ListAPIView class AuthorListView(ListAPIView): serializer_class = serializers.AuthorModelSerializer queryset = models.Author.objects.filter(is_delete=False) pagination_class =MyCursorPagination
|
四 异常处理 Exceptions
REST framework提供了异常处理,我们可以自定义异常处理函数。
4.1 使用方式
1 2 3 4 5 6 7 8 9 10 11
|
from rest_framework.views import exception_handler
def custom_exception_handler(exc, context): response = exception_handler(exc, context)
if response is None: response.data['status_code'] = response.status_code
return response
|
在配置文件中声明自定义的异常处理
1 2 3
|
REST_FRAMEWORK = { 'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler' }
|
如果未声明,会采用默认的方式,如下
rest_frame/settings.py
1 2 3
|
REST_FRAMEWORK = { 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler' }
|
4.2 案例
补充上处理关于数据库的异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
from rest_framework.views import exception_handler from rest_framework.response import Response from rest_framework.views import exception_handler as drf_exception_handler from rest_framework import status from django.db import DatabaseError
def exception_handler(exc, context): response = drf_exception_handler(exc, context)
if response is None: view = context['view'] print('[%s]: %s' % (view, exc)) if isinstance(exc, DatabaseError): response = Response({'detail': '服务器内部错误'}, status=status.HTTP_507_INSUFFICIENT_STORAGE) else: response = Response({'detail': '未知错误'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
return response
REST_FRAMEWORK = { 'EXCEPTION_HANDLER': 'app01.ser.exception_handler' }
|
4.3 REST framework定义的异常
- APIException 所有异常的父类
- ParseError 解析错误
- AuthenticationFailed 认证失败
- NotAuthenticated 尚未认证
- PermissionDenied 权限决绝
- NotFound 未找到
- MethodNotAllowed 请求方式不支持
- NotAcceptable 要获取的数据格式不支持
- Throttled 超过限流次数
- ValidationError 校验失败
也就是说,很多的没有在上面列出来的异常,就需要我们在自定义异常中自己处理了。