DRF-Django rest framework
1. 自定制过滤器
# 自定制过滤器 (源码流程)
查询所有才会有过滤
==> list才需要过滤
==> queryset = self.filter_queryset(self.get_queryset())
==> GenericAPIView
==> filter_queryset
# filter_queryset方法
def filter_queryset(self, queryset):
for backend in list(self.filter_backends):
queryset = backend().filter_queryset(self.request, queryset, self)
return queryset
# 使用方法
1. 写一个类MyFilter,继承BaseFilterBackend
2. 重写filter_queryset方法,在该方法内部进行过滤(自己设置的过滤条件)
3. 返回queryset对象(过滤后的queryset对象)
4. 配置在视图类中
filter_backends = [MyFilter,]
2. 分页器Pagination
1. 分页介绍
1. 内置了三种分页器
1. PageNumberPagination 普通分页
2. LimitOffsetPagination 偏移分页
3. CursorPagination 游标分页
2. 分页模式
1. APIView
2. GenericAPIView + ListModelMixin
3. 分页器使用
1. 一个视图函数只能使用一个分页器
2. 如果在视图内关闭分页功能,只需在视图内设置 pagination_class = None
2. 可选分页器
1. PageNumberPagination
# 前端访问网址形式
GET http://127.0.0.1:8000/students/?page=4&size=3
# 可以在子类中定义的属性
page_size 每页数目
page_query_param 前端发送的页数关键字名,默认为”page”
page_size_query_param 前端发送的每页数目关键字名,默认为None
max_page_size 前端最多能设置的每页数量
page_size = api_settings.PAGE_SIZE # 每页显示多少条
page_query_param = 'page' # 查询参数
page_size_query_param = size # 查询的时候指定每页显示多少条
max_page_size = 10 # 每页最多显示多少条
# 使用方式
1. 定义一个类,继承PageNumberPagination
2. 重写四个属性
3. 在继承了GenericAPIView + ListModelMixin视图类中配置
pagination_class = MyPageNumberPagination
4. 查询
http://127.0.0.1:8000/students/?page=1&size=5
# 示例
class MyPageNumberPagination(PageNumberPagination):
page_size = 3
page_query_param = 'page'
page_size_query_param = 'size'
max_page_size = 5
# GenericAPIView + ListModelMixin的分页模式
# class StudentView(GenericViewSet,ListAPIView):
class StudentView(GenericViewSet,ListModelMixin):
queryset = Student.objects.all()
serializer_class = StudentSerializer
# 普通分页
pagination_class = MyPageNumberPagination
2. LimitOffsetPagination
# 前端访问网址形式
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
default_limit = api_settings.PAGE_SIZE # 默认条数
limit_query_param = 'limit' # 查询时,指定查询多少条
offset_query_param = 'offset' # 查询时,指定的起始位置是哪
max_limit = None # 查询时,最多返回多少条
# 使用方式
1. 定义一个类,继承LimitOffsetPagination
2. 重写四个属性
3. 在继承了GenericAPIView + ListModelMixin视图类中配置
pagination_class = MyLimitOffsetPagination
4. 查询
http://127.0.0.1:8000/students/?limit=100&offset=1
# 示例
class MyLimitOffsetPagination(LimitOffsetPagination):
default_limit = 2 # 默认条数
limit_query_param = 'limit' # 查询时,指定查询多少条
offset_query_param = 'offset' # 查询时,指定的起始位置是哪
max_limit = 5 # 查询时,最多返回多少条
# GenericAPIView + ListModelMixin的分页模式
# class StudentView(GenericViewSet,ListAPIView):
class StudentView(GenericViewSet,ListModelMixin):
queryset = Student.objects.all()
serializer_class = StudentSerializer
# 偏移分页
pagination_class = MyLimitOffsetPagination
3. CursorPagination
# 前端访问网址形式
GET http://127.0.0.1/four/students/?cursor=cD0xNQ%3D%3D
# 可以在子类中定义的属性
cursor_query_param 默认查询字段,不需要修改
page_size 每页数目
ordering 按什么排序,需要指定
cursor_query_param = 'cursor' # 查询的时候,指定的查询方式
page_size = api_settings.PAGE_SIZE # 每页显示多少条
ordering = '-created' # 排序方式 一定要指定排序方式 model中的可以排序的字段
page_size_query_param = size # 查询的时候指定每页显示多少条
max_page_size = None # 每页最多显示多少条
# 使用方式
1. 定义一个类,继承CursorPagination
2. 重写四个属性
3. 在继承了GenericAPIView + ListModelMixin视图类中配置
pagination_class = MyCursorPagination
4. 查询
http://127.0.0.1:8000/students/
# 示例
class MyCursorPagination(CursorPagination):
cursor_query_param = 'cursor' # 查询的时候,指定的查询方式
page_size = 2 # 每页显示多少条
ordering = 'id' # 排序方式 就是model中的可以排序的字段
page_size_query_param = 'size' # 查询的时候指定每页显示多少条
max_page_size = 5 # 每页最多显示多少条
# GenericAPIView + ListModelMixin的分页模式
# class StudentView(GenericViewSet,ListAPIView):
class StudentView(GenericViewSet,ListModelMixin):
queryset = Student.objects.all()
serializer_class = StudentSerializer
# 游标分页
pagination_class = MyCursorPagination
4. APIView分页模式
1. 新建一个类,继承普通分页,重写四个属性
class MyPageNumberPagination(PageNumberPagination):
page_size = 3
page_query_param = 'page'
page_size_query_param = 'size'
max_page_size = 5
class MyLimitOffsetPagination(LimitOffsetPagination):
default_limit = 2 # 默认条数
limit_query_param = 'limit' # 查询时,指定查询多少条
offset_query_param = 'offset' # 查询时,指定的起始位置是哪
max_limit = 5 # 查询时,最多返回多少条
class MyCursorPagination(CursorPagination):
cursor_query_param = 'cursor' # 查询的时候,指定的查询方式
page_size = 2 # 每页显示多少条
ordering = 'id' # 排序方式 model: id
page_size_query_param = 'size' # 查询的时候指定每页显示多少条
max_page_size = 5 # 每页最多显示多少条
2. 视图类写法如下
class StudentApiView(APIView):
def get(self,request):
# 获取所有数据
student_list=Student.objects.all()
# 进行分页 实例化得到分页对象
page = MyPageNumberPagination() # 普通分页
page = MyLimitOffsetPagination() # 偏移分页
page = MyCursorPagination() # 游标分页
# 在数据库中获取分页的数据
res=page.paginate_queryset(student_list,request,self)
# 对分页进行序列化
ser=StudentSerializer(res,many=True)
# 方式一: 把分页后的数据放到响应中
return Response(ser.data)
# 方式二: 这个也是返回Response对象,但是比基本的多了上一页,下一页,和总数据条数
return page.get_paginated_response(ser.data)
3. 异常处理 Exceptions
1. 使用方式
from rest_framework.views import exception_handler
def custom_exception_handler(exc, context):
# 先调用REST framework默认的异常处理方法获得标准错误响应对象
response = exception_handler(exc, context)
# 在此处补充自定义的异常处理
if response is None:
response.data['status_code'] = response.status_code
return response
# 在配置文件中声明自定义的异常处理
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'app01.utils.common_exception_handler'
}
# 如果未声明,会采用默认的方式
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler'
}
2. 示例
1. 统一接口的返回方式,即便视图函数执行出错
2. 使用方式
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 common_exception_handler(exc, context):
response = 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
# 统一接口的返回方式
def common_exception_handler(exc, context):
response = exception_handler(exc, context)
if response is None:
print(context['view']) # 哪个视图函数出错
view = context['view']
request = context['request']
print(context['request']) # 当此请求的request对象
print(str(view))
print(request.path)
print(request.method)
print(request.META.get('REMOTE_ADDR'))
response = Response({'code':999,'detail': '未知错误'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
return response
# 在setting中配置
REST_FRAMEWORK = {
'EXCEPTION_HANDLER':'app01.utils.common_exception_handler'
}
3. DRF定义的异常
- APIException 所有异常的父类
- ParseError 解析错误
- AuthenticationFailed 认证失败
- NotAuthenticated 尚未认证
- PermissionDenied 权限拒绝
- NotFound 未找到
- MethodNotAllowed 请求方式不支持
- NotAcceptable 要获取的数据格式不支持
- Throttled 超过限流次数
- ValidationError 校验失败
4. 封装Response对象
1. 自己封装的response
class APIResponse(Response):
def __init__(self, code=100, msg='成功', data=None, status=None, headers=None, content_type=None, **kwargs):
dic = {'code': code, 'msg': msg}
if data:
dic['data'] = data
dic.update(kwargs) # 这里使用update
super().__init__(data=dic, status=status,
template_name=None, headers=headers,
exception=False, content_type=content_type)
2. 使用
from app01.utils import APIResponse
class StudentApiView(APIView):
# 不封装的方式
def get(self,request):
res={'code':100,'msg':'成功','data':None}
student_list=Student.objects.all()
ser=StudentSerializer(student_list,many=True)
res['data']=ser.data
return Response(res)
# 封装的方式
def get(self, request):
student_list = Student.objects.all()
ser = StudentSerializer(student_list, many=True)
return APIResponse(code=100,msg='查询成功',data=ser.data,count=200,next='http://wwwa.asdfas')
5. 自动生成接口文档
1. 安装依赖
# 借助于第三方 (swagger 也可以)
pip install coreapi
2. 设置接口文档访问路径
1. 在总路由中添加接口文档路径
2. 文档路由对应的视图配置为rest_framework.documentation.include_docs_urls
3. 参数title为接口文档网站的标题
# 示例
from rest_framework.documentation import include_docs_urls
urlpatterns = [
...
path('docs/', include_docs_urls(title='站点页面标题'))
]
3. 文档描述说明的定义位置
# 写视图类(需要加注释)
1. 单一方法的视图,可直接使用类视图的文档字符串
class BookListView(ListAPIView):
"""
返回所有图书信息.
"""
2. 包含多个方法的视图,在类视图的文档字符串中,分开方法定义
class BookListCreateView(ListCreateAPIView):
"""
get:
返回所有图书信息.
post:
新建图书.
"""
3. 对于视图集ViewSet,仍在类视图的文档字符串中封开定义,但是应使用action名称区分
class BookInfoViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
"""
list:
返回图书列表数据
retrieve:
返回图书详情数据
latest:
返回最新的图书数据
read:
修改图书的阅读量
"""
4. Settings配置
# 没有配置的报错信息
AttributeError: 'AutoSchema' object has no attribute 'get_link'
REST_FRAMEWORK = {
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
}
5. 访问接口文档网页
浏览器访问 127.0.0.1:8000/docs/,即可看到自动生成的接口文档
6. 注意点
1. 视图集ViewSet中的retrieve名称,在接口文档网站中叫做read
2. 参数的Description需要在模型类或序列化器类的字段中以help_text选项定义
# 模型类
class Student(models.Model):
...
age = models.IntegerField(default=0, verbose_name='年龄', help_text='年龄')
...
# 序列化器类
class StudentSerializer(serializers.ModelSerializer):
class Meta:
model = Student
fields = "__all__"
extra_kwargs = {
'age': {
'required': True,
'help_text': '年龄'
}
}