过滤、排序与分页
DRF 为列表接口提供了完整的过滤、排序与分页支持,通过配置 filter_backends 和 pagination_class 即可启用。本文还介绍异常处理机制,以及如何统一处理 DRF 未覆盖的业务异常。
过滤(Filtering)
django-filter 集成
对字段进行精确或范围过滤,推荐使用 django-filter 扩展:
pip install django-filter注册应用并配置全局过滤后端:
# settings.py
INSTALLED_APPS = [
...
'django_filters',
]
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': [
'django_filters.rest_framework.DjangoFilterBackend',
]
}在视图中指定可过滤字段:
from rest_framework.generics import ListAPIView
from django_filters.rest_framework import DjangoFilterBackend
class StudentListView(ListAPIView):
queryset = Student.objects.all()
serializer_class = StudentSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['age', 'sex', 'class_null']
# 客户端请求:GET /students/?age=20&sex=true精细化过滤条件
通过自定义 FilterSet 类支持范围、包含、模糊匹配等过滤方式:
import django_filters
from .models import Student
class StudentFilter(django_filters.FilterSet):
age_min = django_filters.NumberFilter(field_name="age", lookup_expr="gte")
age_max = django_filters.NumberFilter(field_name="age", lookup_expr="lte")
name = django_filters.CharFilter(field_name="name", lookup_expr="icontains")
class Meta:
model = Student
fields = ["age_min", "age_max", "name", "sex"]
class StudentListView(ListAPIView):
queryset = Student.objects.all()
serializer_class = StudentSerializer
filter_backends = [DjangoFilterBackend]
filterset_class = StudentFilter
# 客户端请求:GET /students/?age_min=18&age_max=30&name=张搜索过滤
DRF 内置 SearchFilter,支持跨字段模糊搜索:
from rest_framework.filters import SearchFilter
class StudentListView(ListAPIView):
queryset = Student.objects.all()
serializer_class = StudentSerializer
filter_backends = [SearchFilter]
search_fields = ['name', 'description']
# 客户端请求:GET /students/?search=张三search_fields 字段前缀含义:
| 前缀 | 说明 | 示例 |
|---|---|---|
| 无 | 模糊(icontains) | 'name' |
^ | 前缀匹配 | '^name' |
= | 精确匹配 | '=name' |
@ | 全文检索(需数据库支持) | '@description' |
排序(Ordering)
DRF 内置 OrderingFilter 允许客户端通过 URL 参数控制排序:
from rest_framework.filters import OrderingFilter
class StudentListView(ListAPIView):
queryset = Student.objects.all()
serializer_class = StudentSerializer
filter_backends = [OrderingFilter]
ordering_fields = ['id', 'age', 'name'] # 允许排序的字段
ordering = ['-id'] # 默认排序(可选)
# 升序:GET /students/?ordering=age
# 降序:GET /students/?ordering=-age
# 多字段:GET /students/?ordering=class_null,-age过滤 + 排序组合
同时使用多个 filter_backends 时,需要全部列出(局部配置会覆盖全局配置):
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter, OrderingFilter
class StudentListView(ListAPIView):
queryset = Student.objects.all()
serializer_class = StudentSerializer
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
filterset_fields = ['sex', 'class_null']
search_fields = ['name']
ordering_fields = ['id', 'age']
ordering = ['-id']分页(Pagination)
PageNumberPagination(页码分页)
最常用的分页方式,客户端通过页码 + 每页大小请求数据:
from rest_framework.pagination import PageNumberPagination
class StandardPagination(PageNumberPagination):
page_size = 10 # 默认每页条数
page_size_query_param = 'size' # 允许客户端指定每页大小的参数名
max_page_size = 100 # 每页最大条数
page_query_param = 'page' # 页码参数名(默认已是 page)
class StudentListView(ListAPIView):
queryset = Student.objects.all()
serializer_class = StudentSerializer
pagination_class = StandardPagination
# 请求示例:GET /students/?page=2&size=20分页响应格式:
{
"count": 100,
"next": "http://127.0.0.1:8000/students/?page=3&size=20",
"previous": "http://127.0.0.1:8000/students/?page=1&size=20",
"results": [...]
}LimitOffsetPagination(偏移分页)
按偏移量取数据,适合无限滚动场景:
from rest_framework.pagination import LimitOffsetPagination
class StandardLimitOffsetPagination(LimitOffsetPagination):
default_limit = 10 # 默认取多少条
max_limit = 100 # 最多取多少条
limit_query_param = 'limit' # 取数量参数名
offset_query_param = 'offset' # 起始偏移参数名
class StudentListView(ListAPIView):
pagination_class = StandardLimitOffsetPagination
# 请求示例:GET /students/?limit=20&offset=40
# 含义:从第 40 条开始,取 20 条全局分页配置
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'myapp.pagination.StandardPagination',
'PAGE_SIZE': 10,
}全局分页配置会影响所有使用
list() 方法的视图。对于不需要分页的接口(如下拉列表选项),在视图中设置 pagination_class = None 关闭分页。异常处理
DRF 内置异常
DRF 自动处理以下异常,并返回适当的 HTTP 响应:
| 异常类 | 触发场景 | HTTP 状态码 |
|---|---|---|
ParseError | 请求体解析失败 | 400 |
AuthenticationFailed | 认证失败 | 401 |
NotAuthenticated | 未认证 | 401 |
PermissionDenied | 权限拒绝 | 403 |
NotFound | 对象不存在 | 404 |
MethodNotAllowed | HTTP 方法不被允许 | 405 |
Throttled | 超出限流频率 | 429 |
ValidationError | 序列化器校验失败 | 400 |
自定义异常处理
对于 DRF 未处理的异常(如数据库错误、业务逻辑异常),可注册自定义处理函数:
# myapp/utils/exceptions.py
from rest_framework.views import exception_handler
from rest_framework.response import Response
from rest_framework import status
from django.db import DatabaseError
import logging
logger = logging.getLogger(__name__)
def custom_exception_handler(exc, context):
# 先交给 DRF 默认处理器
response = exception_handler(exc, context)
if response is not None:
# DRF 处理了的异常,统一添加状态码字段
response.data['status_code'] = response.status_code
return response
# DRF 未处理的异常,按类型分类处理
view = context.get('view')
if isinstance(exc, DatabaseError):
logger.error("[%s] 数据库错误: %s", view.__class__.__name__, exc)
return Response(
{'detail': '数据库错误,请稍后重试'},
status=status.HTTP_503_SERVICE_UNAVAILABLE,
)
# 其他未知异常记录日志,返回 500
logger.exception("[%s] 未预期的异常", view.__class__.__name__)
return Response(
{'detail': '服务器内部错误'},
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
)注册到 settings:
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'myapp.utils.exceptions.custom_exception_handler',
}在视图中主动抛出 DRF 异常
from rest_framework.exceptions import NotFound, ValidationError, PermissionDenied
class StudentAPIView(APIView):
def get(self, request, pk):
try:
student = Student.objects.get(pk=pk)
except Student.DoesNotExist:
raise NotFound(f"学生 id={pk} 不存在")
return Response(StudentSerializer(student).data)
def post(self, request):
if not request.data.get("name"):
raise ValidationError({"name": "姓名不能为空"})
...接口文档
DRF 支持通过 coreapi 或 drf-spectacular 自动生成接口文档。
推荐使用 drf-spectacular(OpenAPI 3.0):
pip install drf-spectacular# settings.py
INSTALLED_APPS = [..., 'drf_spectacular']
REST_FRAMEWORK = {
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
}# urls.py
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
urlpatterns = [
path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
path('api/docs/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
]访问 http://127.0.0.1:8000/api/docs/ 即可查看 Swagger UI 风格的接口文档,支持在线测试。
最后更新于