Django restframework怎样定制返回数据格式?

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) + 自定义异常处理器,这样既能保证成功响应格式统一,又能统一错误返回格式。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容