Django REST Framework (DRF) 提供了多种方式来定制返回数据的格式,从全局配置到局部视图控制都有灵活的方案。以下是主要的定制方法:
1. 使用自定义渲染器 (Renderer)
全局配置
在 settings.py 中配置默认渲染器:
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
],
# 自定义JSON格式
'JSON_RENDERER_CLASS': 'myapp.renderers.CustomJSONRenderer',
}
创建自定义渲染器
# myapp/renderers.py
from rest_framework.renderers import JSONRenderer
import json
class CustomJSONRenderer(JSONRenderer):
def render(self, data, accepted_media_type=None, renderer_context=None):
"""
统一返回格式:
{
"code": 200,
"message": "success",
"data": { ... }
}
"""
response = renderer_context.get('response', None)
code = response.status_code if response else 200
# 处理错误情况
if code >= 400:
message = data.get('detail', 'error') if isinstance(data, dict) else 'error'
res = {
'code': code,
'message': message,
'data': None
}
else:
res = {
'code': code,
'message': 'success',
'data': data
}
return json.dumps(res, ensure_ascii=False)
2. 使用自定义响应类 (Response)
封装统一响应
# myapp/utils/response.py
from rest_framework.response import Response
class APIResponse(Response):
def __init__(self, data=None, code=200, message='success',
status=None, template_name=None, headers=None,
exception=False, content_type=None):
std_data = {
'code': code,
'message': message,
'data': data
}
super().__init__(std_data, status, template_name,
headers, exception, content_type)
# 使用示例
from rest_framework.decorators import api_view
from .utils.response import APIResponse
@api_view(['GET'])
def user_list(request):
users = User.objects.all()
serializer = UserSerializer(users, many=True)
return APIResponse(data=serializer.data)
高级响应类(支持分页)
class APIResponse(Response):
def __init__(self, data=None, code=200, message='success',
status=None, template_name=None, headers=None,
exception=False, content_type=None, pagination=None):
std_data = {
'code': code,
'message': message,
'data': data
}
# 如果有分页信息,添加到data中
if pagination:
std_data['pagination'] = {
'count': pagination.get('count'),
'next': pagination.get('next'),
'previous': pagination.get('previous'),
'page_size': pagination.get('page_size', 10)
}
super().__init__(std_data, status, template_name,
headers, exception, content_type)
3. 使用序列化器控制输出
动态字段选择
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'username', 'email']
def to_representation(self, instance):
"""自定义输出格式"""
ret = super().to_representation(instance)
# 添加额外字段
ret['full_name'] = f"{instance.first_name} {instance.last_name}"
# 转换字段格式
ret['created_at'] = instance.date_joined.strftime('%Y-%m-%d')
return ret
条件字段显示
class DynamicFieldsSerializer(serializers.ModelSerializer):
"""
根据请求参数动态选择字段
"""
def __init__(self, *args, **kwargs):
fields = kwargs.pop('fields', None)
super().__init__(*args, **kwargs)
if fields is not None:
allowed = set(fields)
existing = set(self.fields)
for field_name in existing - allowed:
self.fields.pop(field_name)
class UserSerializer(DynamicFieldsSerializer):
class Meta:
model = User
fields = ['id', 'username', 'email', 'phone', 'address']
# 视图中使用
serializer = UserSerializer(user, fields=['id', 'username'])
4. 使用分页器定制分页格式
# myapp/pagination.py
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
class CustomPagination(PageNumberPagination):
page_size = 10
page_size_query_param = 'page_size'
max_page_size = 100
def get_paginated_response(self, data):
return Response({
'code': 200,
'message': 'success',
'data': {
'count': self.page.paginator.count,
'next': self.get_next_link(),
'previous': self.get_previous_link(),
'results': data
}
})
# settings.py 配置
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'myapp.pagination.CustomPagination',
'PAGE_SIZE': 10
}
5. 使用异常处理定制错误格式
# myapp/exceptions.py
from rest_framework.views import exception_handler
from rest_framework.response import Response
def custom_exception_handler(exc, context):
# 先调用默认处理
response = exception_handler(exc, context)
if response is not None:
# 自定义错误格式
error_data = {
'code': response.status_code,
'message': '请求失败',
'data': None,
'errors': response.data
}
response.data = error_data
return response
# settings.py
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'myapp.exceptions.custom_exception_handler'
}
6. 视图级别的定制
APIView 中定制
from rest_framework.views import APIView
class UserDetailView(APIView):
def get(self, request, pk):
user = get_object_or_404(User, pk=pk)
serializer = UserSerializer(user)
# 完全自定义返回结构
return Response({
'status': 'ok',
'timestamp': timezone.now().isoformat(),
'result': serializer.data,
'meta': {
'version': 'v1.0',
'request_id': request.META.get('REQUEST_ID')
}
})
ViewSet 中定制
from rest_framework import viewsets
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
def list(self, request, *args, **kwargs):
response = super().list(request, *args, **kwargs)
# 包装原有响应
response.data = {
'code': 200,
'message': '获取列表成功',
'data': response.data
}
return response
7. 使用中间件全局处理
# myapp/middleware.py
import json
from django.utils.deprecation import MiddlewareMixin
class ResponseFormatMiddleware(MiddlewareMixin):
def process_response(self, request, response):
# 只处理API响应
if request.path.startswith('/api/') and 'application/json' in response.get('Content-Type', ''):
try:
data = json.loads(response.content)
if 'code' not in data: # 避免重复包装
wrapped_data = {
'code': response.status_code,
'message': 'success' if response.status_code < 400 else 'error',
'data': data
}
response.content = json.dumps(wrapped_data)
except:
pass
return response
推荐方案总结
| 场景 | 推荐方案 |
|---|---|
| 全局统一格式 | 自定义渲染器 + 自定义响应类 |
| 特定接口特殊格式 | 视图中直接构造 Response |
| 错误统一处理 | 自定义异常处理器 |
| 分页格式定制 | 自定义分页类 |
| 字段级控制 | 序列化器 to_representation
|
最常用的组合是:自定义响应类 (APIResponse) + 自定义异常处理器,这样既能保证成功响应格式统一,又能统一错误返回格式。