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

XML renderer 在 DRF 中默认不启用
DRF 默认只注册 JSONRenderer,XMLRenderer 虽然内置但未自动加入 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 XMLRendererclass MyAPIView(APIView): 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 XMLRendererclass 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










