Django REST Framework怎么渲染XML

DRF 默认不启用XMLRenderer,需手动在DEFAULT_RENDERER_CLASSES中添加;XMLRenderer要求数据为字典且顶层键作根元素,列表须包装为字典;自定义根名需子类化XMLRenderer。

XML renderer 在 DRF 中默认不启用

DRF 默认只注册 JSONRendererXMLRenderer 虽然内置但未自动加入 DEFAULT_RENDERER_CLASSES。直接返回 XML 会触发 NotAcceptable (406) 错误,浏览器或 curl -H "Accept: application/xml" 都拿不到 XML 响应。

手动启用 XMLRenderer 的两种方式

推荐在全局设置中添加,避免每个视图重复声明。注意顺序:XML 渲染器需放在 JSON 之后,否则 Accept: */* 可能优先匹配 XML(部分客户端行为不一致)。

  • settings.py 中修改 REST_FRAMEWORK 配置:
REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.XMLRenderer',  # ← 显式加入
    ],
}
  • 若仅对某个视图启用,可在视图类中指定:
from rest_framework.renderers import XMLRenderer

class MyAPIView(APIVie

w): renderer_classes = [XMLRenderer, JSONRenderer] # 顺序影响 content-negotiation def get(self, request): return Response({'name': 'Alice', 'age': 30})

XML 输出结构受 serializer 字段名和嵌套深度直接影响

XMLRenderer 不支持任意嵌套对象或列表顶层渲染——它要求数据是字典,且顶层键将成为根元素名。传入 [{'id': 1}, {'id': 2}] 会报 TypeError: object of type 'list' has no len()

  • 正确写法:用字典包装列表,并指定根标签名(通过 renderer_context 或自定义)
  • 常见翻车点:Response([obj1, obj2]) → 必须改为 Response({'users': [obj1, obj2]})
  • 字段值为 None 时,XML 中该元素仍会出现但为空标签(),不会被跳过

自定义 XML 根元素名或添加命名空间

DRF 的 XMLRenderer 不提供开箱即用的命名空间或根名覆盖接口,但可通过子类轻量扩展:

from rest_framework.renderers import XMLRenderer

class CustomXMLRenderer(XMLRenderer): root_tag_name = 'response' # 可设为类属性或从 renderer_context 动态取

def render(self, data, accepted_media_type=None, renderer_context=None):
    if data and isinstance(data, dict) and 'root_tag_name' in renderer_context:
        self.root_tag_name = renderer_context['root_tag_name']
    return super().render(data, accepted_media_type, renderer_context)
  • 使用时,在视图中传入上下文:return Response(data, renderer_context={'root_tag_name': 'book_list'})
  • 命名空间需手动拼接字符串或改用第三方库(如 djangorestframework-xml 的增强版)
  • 注意:子类化后需在 renderer_classes 中替换原 XMLRenderer

XML 渲染真正麻烦的不是配置,而是数据结构必须严格适配其字典+单根约束;很多开发者卡在 Response([...]) 上,却没意识到错误信息里那句 Expected a dict, but got list 已经说得很清楚了。