一. 过滤组件#
1. 步骤#
Copy
1. 安装:pip3 install django-filter
2. 注册: settings.py中注册
INSTALLED_APPS = [
...
'django_filters',
]
3. 全局配置 或者 局部配置
全局配置: 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
局部配置:
指定所有字段: filter_fields = '__all__'
指定固定字段: filter_fields = ['name', ...]
2. 代码实例#
Copy
from rest_framework.response import Response
from rest_framework.generics import ListAPIView
class TextView6(ListAPIView):
authentication_classes = []
permission_classes = []
throttle_classes = []
queryset = models.Book.objects.all()
serializer_class = BookModelSerializer
filter_fields = '__all__'
http://127.0.0.1:8000/books2/?name=egon
3. 总结#
Copy
安装 -> 注册 -> 全局 or 局部
4. 注意#
Copy
django-filter的安装可能会出现django版本最低要求问题, 如果下载最新版本的django-filter
如果使用的是django 1.11版本会自动升级到3.x.
二. 排序组件#
1. 全局配置 局部配置#
Copy
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ('rest_framework.filters.OrderingFilter')
}
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend', 'rest_framework.filters.OrderingFilter')
}
from rest_framework.filters import OrderingFilter
filter_backends = [OrderingFilter]
from rest_framework.filters import OrderingFilter
from django_filters.rest_framework import DjangoFilterBackend
filter_backends = [OrderingFilter, DjangoFilterBackend]
filter_fields = '__all__'
2. 代码实例#
Copy
class TextView7(ListAPIView):
authentication_classes = []
permission_classes = []
throttle_classes = []
filter_backends = [DjangoFilterBackend, OrderingFilter]
queryset = models.Book.objects.all()
serializer_class = BookModelSerializer
filter_fields = ['name', 'price']
3. 总结#
Copy
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import OrderingFilter
它们2个全局配置都是共用一个配置路径, 如果局部指定了就会将全局配置的对应项所有的覆盖
三. 分页组件#
1. 分页的三种方式#
Copy
from rest_framework.pagination import PageNumberPagination
class CoustomPageNumberPagination(PageNumberPagination):
"""
url地址栏目支持的查询格式:
http://api.example.org/accounts/?page=4
http://api.example.org/accounts/?page=4&page_size=100
"""
page_size = 3
page_query_param = 'page'
page_size_query_param = 'size'
max_page_size = 5
from rest_framework.pagination import LimitOffsetPagination
class CustomLimitOffsetPagination(LimitOffsetPagination):
"""
url地址栏目支持的查询格式:
http://api.example.org/accounts/?limit=100
http://api.example.org/accounts/?offset=400&limit=100
"""
default_limit = 3
limit_query_param = 'limit'
offset_query_param = 'offset'
max_limit = 5
from rest_framework.pagination import CursorPagination
class CustomCursorPagination(CursorPagination):
"""
提示: 这种方式实现的方式很复杂, 因此带来的好处就是查询效率极高, 不过也有它的缺陷, 就是通过这种方式制作的分页只有上一页下一页,
如果数据库数据非常大那么使用它将会是非常好的选择.
url地址栏目支持的查询格式:
直接浏览器输入访问即可, 因为不能指定位置.
"""
cursor_query_param = 'cursor'
page_size = 3
ordering = '-id'
from rest_framework.generics import ListAPIView
from . import ser
from . import models
class BookAPIView(ListAPIView):
queryset = models.Book.objects.all()
serializer_class = ser.BookModelSerializer
"""
APIView -> api_settings -> DEFAULTS -> 51行
# Generic view behavior
'DEFAULT_PAGINATION_CLASS': None,
"""
pagination_class = CustomCursorPagination
from rest_framework.pagination import PageNumberPagination
通过page分页, 通过site获取分页条目数
from rest_framework.pagination import LimitOffsetPagination
通过offset找到分页位置, 通过limit获取当前数据之后的条目数
from rest_framework.pagination import CursorPagination
没有参数, 默认只有上一页, 下一页. 通过ordering排序进行分页
优点: 查询速度快. 缺点: 无法选择起始位置
提示: 以上都可以限制最大显示条目数 和 默认获取条目数. 以及筛选的key都可以重定义.
2. 如果规范化分页器的restful返回规范#
Copy
from rest_framework.generics import ListAPIView
from utils.response import CommonResponse
from utils.throttle import CustomSimpleRateThrottle
from . import ser
from . import models
class BookAPIView(ListAPIView):
queryset = models.Book.objects.all()
serializer_class = ser.BookModelSerializer
throttle_classes = [CustomSimpleRateThrottle, ]
def get(self, request, *args, **kwargs):
instance = self.get_queryset()
page_obj = CustomCursorPagination()
instance = page_obj.paginate_queryset(queryset=instance, request=request, view=self)
next_url = page_obj.get_next_link()
previous_url = page_obj.get_previous_link()
serializer = self.get_serializer(instance=instance, many=True)
return CommonResponse(results=serializer.data, next=next_url, previous_url=previous_url)
page_obj = CustomCursorPagination()
next_url = page_obj.get_next_link()
previous_url = page_obj.get_previous_link()
default_limit = 3
limit_query_param = 'limit'
offset_query_param = 'offset'
max_limit = 5
四. 异常处理#
1. 从源码分析到如何实现自定义异常处理#
Copy
try:
self.initial(request, *args, **kwargs)
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
response = handler(request, *args, **kwargs)
except Exception as exc:
response = self.handle_exception(exc)
1. 异常的捕获范围: 注意, 并不是所有位置的异常出可以捕获. 例如: 自定义视图中的类中抛出的异常就不行
self.initial(request, *args, **kwargs)
认证: self.perform_authentication(request)
提示: 不会捕获自定义的认证类. 因此perform_authentication做的事情就是request.user赋值
权限: self.check_permissions(request)
频率: self.check_throttles(request)
自定义视图类中的方法:
response = handler(request, *args, **kwargs)
2. 关键实现 handle_exception方法
def handle_exception(self, exc):
if isinstance(exc, (exceptions.NotAuthenticated,
exceptions.AuthenticationFailed)):
auth_header = self.get_authenticate_header(self.request)
if auth_header:
exc.auth_header = auth_header
else:
exc.status_code = status.HTTP_403_FORBIDDEN
'''
def get_exception_handler(self):
"""
配置文件导入的内容:
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
"""
return self.settings.EXCEPTION_HANDLER
'''
exception_handler = self.get_exception_handler()
'''
def get_exception_handler_context(self):
return {
'view': self,
'args': getattr(self, 'args', ()),
'kwargs': getattr(self, 'kwargs', {}),
'request': getattr(self, 'request', None)
}
'''
context = self.get_exception_handler_context()
'''
exc: 这里的exc是APIView中定义的dispatch中传过来的异常对象
context: 这里的context是对出现异常对象的上下文捕获
'''
response = exception_handler(exc, context)
'''
这里就是通过在drf提供的exception_handler函数处理的返回值结果来判断时候交给django自己处理.
如果response的返回值是None就会交给django处理了, 现在我们要的就是在exception_handler函数执行完毕以后将返回值进行判断,
并且返回的结果不再是None, 而应该是response对象
'''
if response is None:
self.raise_uncaught_exception(exc)
response.exception = True
return response
1. 先新建一个.py文件存放自定义的异常处理函数
2. 在drf提供的默认配置文件中导入exception_handler函数的
3. 在自定义异常处理函数中先将exception_handler传入让drf先处理一番, 根据返回的结果为None是来执行自己的判断.
如果返回不为None也不应该直接将原本的response对象直接返回, 可以自己封装一个符合restful规范的类用来继承Response类
将原本的response对象中的返回结果通过 response.data.get('detail') 方法获取
4. settings.py文件中配置自定义的exception_handler函数的路径
'EXCEPTION_HANDLER': 'app01.app_auth.custom_exception_handler',
2. 代码实例#
Copy
from rest_framework.views import exception_handler
from rest_framework import status
from rest_framework.response import Response
class APIResponse(Response):
def __init__(self, code=1000, messages='成功', results=None, error=None,
status=None,
template_name=None, headers=None,
exception=False, content_type=None, **kwargs):
data = {
'code:': code,
'messages:': messages,
}
print('error:', error)
print('results:', results)
if results:
data['results'] = results
if error:
data['error'] = error
data.update(kwargs)
super().__init__(data=data, status=status,
template_name=template_name, headers=headers,
exception=exception, content_type=content_type)
def custom_exception_handler(exc, context):
"""
:param exc: 这里的exc是APIView中定义的dispatch中传过来的异常对象
try:
...
except Exception as exc:
response = self.handle_exception(exc)
:param context: 这里的context是对出现异常对象的上下文捕获
查找: handle_exception -> get_exception_handler_context
def get_exception_handler_context(self):
return {
'view': self,
'args': getattr(self, 'args', ()),
'kwargs': getattr(self, 'kwargs', {}),
'request': getattr(self, 'request', None)
}
:return: 这里返回Response对象, 本来drf没有处理的的异常会交给django处理, 但是我们捕获这种异常, 规定成统一的处理. 让drf处理.
注意!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
这里可以捕获的异常范围由以下源码得知:
try:
self.initial(request, *args, **kwargs)
# Get the appropriate handler method
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
response = handler(request, *args, **kwargs)
except Exception as exc:
response = self.handle_exception(exc)
捕获范围:
self.initial(request, *args, **kwargs)
认证: self.perform_authentication(request)
提示: 不会捕获自定义的认证类. 因此perform_authentication做的事情就是request.user赋值
权限: self.check_permissions(request)
频率: self.check_throttles(request)
自定义视图类中的方法:
response = handler(request, *args, **kwargs)
"""
obj = None
response = exception_handler(exc, context)
if not response:
if isinstance(exc, AttributeError):
obj = APIResponse(2000, '失败', error=str(exc), results=str(context), status=status.HTTP_403_FORBIDDEN)
elif isinstance(exc, ImportError):
obj = APIResponse(2002, '失败', error=str(exc), results=str(context), status=status.HTTP_403_FORBIDDEN)
elif isinstance(exc, TypeError):
obj = APIResponse(2003, '失败', error=str(exc), results=str(context), status=status.HTTP_403_FORBIDDEN)
elif isinstance(exc, Exception):
obj = APIResponse(2004, '失败', error=str(exc), results=str(context), status=status.HTTP_403_FORBIDDEN)
else:
obj = APIResponse(2005, '失败', error=response.data.get('detail'), results=str(context),
status=status.HTTP_403_FORBIDDEN)
return obj
3. 总结#
Copy
1. 导入需要在drf提供的默认函数的基础之上的函数
from rest_framework.views import exception_handler
2. 自定义异常处理函数2个参数exc, context
3. 先让drf处理一波, 处理它处理不完的, 或者 在他处理完的基础之上拓展, 通过response返回结果来进行区分
提示: 可以通过 response.data.get('detail') 获取drf处理完的对象中返回的响应信息
4. 注意#
Copy
配置文件中配置自定义的异常处理函数时, drf提供的exception_handler的导入会与在同一个文件中自定义的认证类 或者 自定义的权限类的导入起冲突.
自定义的异常的处理代码逻辑最好新建一个纯净的.py文件存放
5. 快速使用#
Copy
from rest_framework.views import exception_handler
from rest_framework.response import Response
class CommonResponse(Response):
def __init__(self, code=1000, messages='ok', results=None,
status=None, template_name=None, headers=None,
exception=False, content_type=None,
**kwargs):
data = {
'code': code,
'messages': messages,
}
data.update(kwargs)
if results:
data['results'] = results
super().__init__(data=data, status=status,
template_name=template_name, headers=headers,
exception=exception, content_type=content_type)
def common_exception_handler(exc, context):
response = exception_handler(exc, context)
if not response:
obj = CommonResponse(code=2000, messages='error', results=str(exc))
else:
obj = CommonResponse(code=2000, messages='error', results=response.data)
return obj