【一】三大认证
# 认证
self.perform_authentication(request)
# 权限
self.check_permissions(request)
# 频率
self.check_throttles(request)
【二】认证
1)使用
-
新建一个py文件(authentication.py)
-
写一个类继承
BaseAuthentication
-
重写
authenticate
函数from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed from App.models import Token class LoginAuthentication(BaseAuthentication): def authenticate(self, request): # 取出请求头中的token数据 token = request.META.get('HTTP_TOKEN') # 校验登录token是否有效 user_token = Token.objects.filter(token=token).first() if user_token: # 返回用户,token return user_token.user, token else: # 主动抛异常 raise AuthenticationFailed('请先登录')
-
将认证类配置到视图类内
# 局部配置 authentication_classes = [LoginAuthentication] # 全局配置 REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'App.base.authentication.LoginAuthentication' ] }
【三】权限
1)使用
-
新建一个py文件(
permissions.py
) -
写一个类继承
BasePermission
-
重写
has_permission
函数from rest_framework.permissions import BasePermission class CommonPermission(BasePermission): def has_permission(self, request, view): # view.action:请求状态 if view.action == 'list' or view.action == 'retrieve': return True else: limit = request.user.limit if limit == 1: return True else: self.message = f'当前为【{request.user.get_limit_display()}】登录,权限不足' return False
-
将认证类配置到视图类内
# 局部配置 permission_classes = [CommonPermission] # 全局配置 REST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSES': [ 'App.base.permissions.CommonPermission' ] }
【四】频率
1)使用
-
新建一个py文件(
throttles.py
) -
写一个类继承
SimpleRateThrottle
-
设置频率
# 支持的时间:s,m,h,d # 每分钟3次 rate = '3/m'
-
重写
get_cache_key
函数# SimpleRateThrottle 内继承了 BaseThrottle from rest_framework.throttling import SimpleRateThrottle class CommonThrottle(SimpleRateThrottle): # 设置频率(每分钟3次)(s,m,h,d) rate = '3/m' def get_cache_key(self, request, view): print(request.META) # 以返回的内容作为限制 ip = request.META.get('REMOTE_ADDR') return ip
-
将认证类配置到视图类内
# 局部配置 throttle_classes = [CommonThrottle] # 全局配置 REST_FRAMEWORK = { 'DEFAULT_THROTTLE_RATES':[ 'App.base.throttling.CommonThrottle' ] }
【五】排序,过滤
1)排序
-
引入
from rest_framework.filters import OrderingFilter
-
添加到视图类内
# 排序 # 需注册排序依据,才能使用 # http://localhost:8000/api/v1/book/?ordering=price,-id # 哪个依据在前,就先按照那个排序 filter_backends = [OrderingFilter] ordering_fields = ['price', 'id']
2)过滤
1.方式一:drf内置方法,只能使用search进行模糊查询
-
引入
from rest_framework.filters import SearchFilter
-
添加到视图类内
# 需注册排序依据,才能使用 # http://localhost:8000/api/v1/book/?search=书 filter_backends = [SearchFilter] search_fields = ['name']
2.方式二:第三方django_filter
,只能精确查询
-
引入
from django_filters.rest_framework.backends import DjangoFilterBackend
-
添加到视图类内
# 需注册排序依据,才能使用 # http://localhost:8000/api/v1/book/?name=书1&price=80 filter_backends = [ DjangoFilterBackend] filterset_fields = ['name','price']
3.方式三:自定义过滤
-
新建一个py文件(
filters.py
) -
写一个类继承
BaseFilterBackend
-
重写
filter_queryset
函数__contains:模糊查询 __gt:大于查询 __lt:小于查询 __gte:大于等于查询 __lte:小于等于查询
from django.db.models import Q from rest_framework.filters import BaseFilterBackend class CommonFilterBackend(BaseFilterBackend): def filter_queryset(self, request, queryset, view): name = request.query_params.get('name') price = request.query_params.get('price') price_gt = request.query_params.get('price_gt') price_lt = request.query_params.get('price_lt') if name and price_gt and price_lt: queryset = queryset.filter(name__contains=name, price__gt=price_gt, price__lt=price_lt) elif price_gt and price_lt: queryset = queryset.filter(price__gt=price_gt, price__lt=price_lt) elif name and price: queryset = queryset.filter(Q(name__contains=name) | Q(price=price)) # 或关系 elif name: # 模糊查询 queryset = queryset.filter(name__contains=name) elif price: # 精确查询 queryset = queryset.filter(price=price) return queryset
-
将认证类配置到视图类内
# 局部配置 filter_backends = [CommonFilterBackend] # 全局配置 REST_FRAMEWORK = { 'DEFAULT_FILTER_BACKENDS': [ 'App.base.filters.CommonFilterBackend' ] }
-
使用
http://localhost:8000/api/v1/book/?name=书&price=30
【六】分页
-
新建一个py文件(
pagination.py
) -
写一个类继承
PageNumberPagination
-
定义函数
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination # 基本分页 class CommonPagination(PageNumberPagination): # 默认一页显示的数量 page_size = 2 # 指定页数的key # http://localhost:8000/api/v1/book/ # http://localhost:8000/api/v1/book/?p=2 page_query_param = 'page' # 指定显示的数量的key # http://localhost:8000/api/v1/book/?s=3 page_size_query_param = 'size' # 每页最大显示数量 max_page_size = 5 # 可指定每页的数据 class CommonLimitOffsetPagination(LimitOffsetPagination): # 默认一页显示的数量 default_limit = 2 # 指定页数的key limit_query_param = 'limit' # 偏移量的key offset_query_param = 'offset' # 每页最大显示数量 max_limit = 5 # http://localhost:8000/api/v1/book/ # http://localhost:8000/api/v1/book/?limit=2&offset=2 # 无法预测每页的参数, class CommonCursorPagination(CursorPagination): # 默认一页显示的数量 page_size = 2 # 指定页数的key cursor_query_param = 'cursor' # 排序依据的字段 ordering = 'id'
-
将认证类配置到视图类内
# 局部配置 pagination_class = CommonPagination
【七】全局异常
1)3大类异常
- drf的异常
- django的异常
- python的异常
2)使用
-
新建一个py文件(
abnormity.py
) -
引入
from rest_framework.views import exception_handler
-
定义函数
def common_exception_handler(exc, context): # 日志记录 request = context.get('request') view = context.get('view') print(f'时间:{time.time()},' f'用户:{request.user.username or "未知用户"},' f'访问地址:{request.get_full_path()},' f'请求方式:{request.method},' f'视图类:{str(view)}') # ----------------- # 异常错误处理,统一返回格式 response = exception_handler(exc, context) # drf的异常返回对象,其余返回None if response: if isinstance(response.data, dict): msg = response.data.get("detail") or '未知错误' elif isinstance(response.data, list): msg = response.data[0] return Response({'code': 100, 'msg': msg}) else: return Response({'code': 100, 'msg': str(exc)})
-
将认证类配置到视图类内
# 使用全局配置 REST_FRAMEWORK = { 'EXCEPTION_HANDLER': 'App.base.exception.common_exception_handler' }