跳至主要內容

Rest-framework专栏讲解(十):Renderers

Mr.暴走の海鸽约 4250 字大约 14 分钟

Rest-framework专栏讲解(十):Renderers

目录


官方原文链接open in new window

渲染open in new window

REST framework 包含许多内置的渲染器类,允许您使用各种 media type 返回响应。同时也支持自定义渲染器。

如何确定使用哪个渲染器open in new window

视图的渲染器集合始终被定义为类列表。当调用视图时,REST framework 将对请求内容进行分析,并确定最合适的渲染器以满足请求。内容分析的基本过程包括检查请求的 Accept header,以确定它在响应中期望的 media type。或者,用 URL 上的格式后缀明确表示。例如,URL http://example.com/api/users_count.json 可能始终返回 JSON 数据。

设置渲染器open in new window

可以使用 DEFAULT_RENDERER_CLASSES 设置全局的默认渲染器集。例如,以下设置将使用JSON作为主要 media type,并且还包含自描述 API。

REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
    ]
}

包含了返回 JSON 数据以外, 还包括了自述 API 的返回。 当然, 如果你不想在全局中设置渲染的方式, 还可以使用基于 API​​View 的视图类来设置单个视图或视图集的渲染器:

from django.contrib.auth.models import User
from rest_framework.renderers import JSONRenderer
from rest_framework.response import Response
from rest_framework.views import APIView

class UserCountView(APIView):
    """
    A view that returns the count of active users in JSON.
    """
    renderer_classes = [JSONRenderer]

    def get(self, request, format=None):
        user_count = User.objects.filter(active=True).count()
        content = {'user_count': user_count}
        return Response(content)

再或者是在基于 @api_view 装饰器的函数视图上设置:

@api_view(['GET'])
@renderer_classes([JSONRenderer])
def user_count_view(request, format=None):
    """
    A view that returns the count of active users in JSON.
    """
    user_count = User.objects.filter(active=True).count()
    content = {'user_count': user_count}
    return Response(content)

在使用多种渲染器的时候, 默认使用的是下标为 0 的首选渲染器, 当然你可以在你的请求头中指定 Accept

渲染器类的优先级open in new window

在为 API 指定渲染器类时,需要考虑它们处理每种媒体类型时的优先级,这点很重要。如果客户端没有指定接受数据的表现形式,例如发送 Accept:*/* header,或者根本不包含 Accept header,则 REST framework 将选择列表中的第一个渲染器用于响应。

例如,如果你的 API 提供 JSON 响应和可浏览的 HTML API,则可能需要将 JSONRenderer 作为默认渲染器,以便将 JSON 响应发送给未指定 Accept header 的客户端。

如果你的 API 包含可根据请求同时处理常规网页和 API 响应的视图,那么你可以考虑将 TemplateHTMLRenderer 设置为默认渲染器,以便与发送 broken accept headers 的老式浏览器很好地配合使用。

API 参考open in new window

渲染器罗列

渲染器类渲染类型说明
JSONRendererapplication/json将返回数据渲染成 JSON 数据样式, 并且你可以使用 indent 媒体类型参数制定你的缩进方式, 例如 Accept: application/json; indent=4
TemplateHTMLRenderertext/html使用 Django 模板数据返回时, 将返回 HTML 类型数据, 并与其他返回数据不一样的是, 使用此渲染器返回数据不需要序列化, 但是在你创建并返回 Response 实例对象时需要制定 template_name 关键字参数
StaticHTMLRenderertext/html一个将渲染的 HTML 作为字符渲染的方式传递的渲染器
BrowsableAPIRenderertext/html将数据呈现为 Browsable API 的 HTML
AdminRenderertext/html该渲染器适用于 CRUD 样式的 WebAPI, 这些 API 也应提供用户友好的界面来管理数据
HTMLFormRenderertext/html将序列化数据呈现为 HTML, 次渲染器的输出不包含封闭的 <from> 标签以及隐藏的 CSRF 输入或任何提交按钮
MultiPartRenderermultipart/form-data; boundary=BoUnDaRyStRiNg该渲染器用于渲染 HTML 多部分表单数据, 它不适合用作响应渲染器, 而是用于使用 REST 框架的测试客户端和测试请求工厂创建测试请求

JSONRendereropen in new window

使用 utf-8 编码将请求数据呈现为 JSON

请注意,默认风格包含 unicode 字符,并使用紧凑风格呈现(没有多余的空白)响应:

{"unicode black star":"★","value":999}

客户端可能还会包含 “缩进” media type 参数,在这种情况下,返回的 JSON 将会缩进。

比如: Accept: application/json; indent=4。

{
    "unicode black star": "★",
    "value": 999
}

使用 UNICODE_JSONCOMPACT_JSON 设置键可以更改默认的 JSON 编码风格。

.media_typeapplication/json

.format'.json'

.charsetNone

TemplateHTMLRendereropen in new window

使用 Django 的标准模板将数据呈现为 HTML。与其他渲染器不同,传递给 Response 的数据不需要序列化。另外,创建 Response 时可能需要包含 template_name 参数。

TemplateHTMLRenderer 将创建一个 RequestContext,使用 response.data 作为上下文字典,并确定用于呈现上下文的模板名称。

模板名称由(按优先顺序)确定:

  1. 传递给 response 的显式 template_name 参数。
  2. 在此类上设置明确的 .template_name 属性。
  3. 调用 view.get_template_names() 的返回结果。

使用 TemplateHTMLRenderer 的视图示例:

class UserDetail(generics.RetrieveAPIView):
    """
    A view that returns a templated HTML representation of a given user.
    """
    queryset = User.objects.all()
    renderer_classes = (TemplateHTMLRenderer,)

    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        return Response({'user': self.object}, template_name='user_detail.html')

你可以使用 TemplateHTMLRenderer 来使 REST framework 返回常规 HTML 页面,或者从单个端点(a single endpoint)返回 HTML 和 API 响应。

如果你正在构建使用 TemplateHTMLRenderer 以及其他渲染器类的网站,则应考虑将 TemplateHTMLRenderer 列为 renderer_classes 列表中的第一个类,以便即使对于发送格式错误的 ACCEPT: header 的浏览器,也会优先考虑它。

.media_typetext/html

.format'.html'

.charsetutf-8

StaticHTMLRendereropen in new window

一个简单的渲染器,它只是返回预渲染的 HTML。与其他渲染器不同,传递给响应对象的数据应该是表示要返回的内容的字符串。

使用 StaticHTMLRenderer 的视图示例:

@api_view(['GET'])
@renderer_classes([StaticHTMLRenderer])
def simple_html_view(request):
    data = '<html><body><h1>Hello, world</h1></body></html>'
    return Response(data)

你可以使用 StaticHTMLRenderer 来使 REST framework 返回常规 HTML 页面,或者从单个端点(a single endpoint)返回 HTML 和 API 响应。

.media_typetext/html

.format'.html'

.charsetutf-8

BrowsableAPIRendereropen in new window

将数据呈现为可浏览的 HTML API:

该渲染器将确定哪个其他渲染器被赋予最高优先级,并使用该渲染器在 HTML 页面中显示 API 风格响应。

.media_typetext/html

.format'.api'

.charsetutf-8

.template'rest_framework/api.html'

自定义 BrowsableAPIRenderer

默认情况下,除 BrowsableAPIRenderer 之外,响应内容将使用最高优先级的渲染器渲染。如果你需要自定义此行为,例如,将 HTML 用作默认返回格式,但在可浏览的 API 中使用 JSON,则可以通过覆盖 get_default_renderer() 方法来实现。

例如:

class CustomBrowsableAPIRenderer(BrowsableAPIRenderer):
    def get_default_renderer(self, view):
        return JSONRenderer()

AdminRendereropen in new window

将数据呈现为 HTML,以显示类似管理员的内容:

该渲染器适用于 CRUD 风格的 Web API,该 API 还应提供用于管理数据的用户友好界面。

请注意, AdminRenderer 对于嵌套或列出序列化输入的视图不起作用,因为 HTML 表单无法正确支持它们。

注意:当数据中存在正确配置的 URL_FIELD_NAME (默认为 url)属性时, AdminRenderer 仅能够包含指向详细页面的链接。对于 HyperlinkedModelSerializer ,情况就是这样,但对于 ModelSerializer 类或普通 Serializer 类,你需要确保明确包含该字段。例如,在这里我们使用模型 get_absolute_url 方法:

class AccountSerializer(serializers.ModelSerializer):
    url = serializers.CharField(source='get_absolute_url', read_only=True)

    class Meta:
        model = Account

.media_typetext/html

.format'.admin'

.charsetutf-8

.template'rest_framework/admin.html'

HTMLFormRendereropen in new window

将序列化返回的数据呈现为 HTML 表单。该渲染器的输出不包含封闭的 <form> 标签,隐藏的 CSRF 输入或任何提交按钮。

这个渲染器不是直接使用,而是可以通过将一个序列化器实例传递给 render_form 模板标签来在模板中使用。

render_form 模板标签在模板中使用, 参考 HTML&Forms 文档open in new window

{% load rest_framework %}

<form action="/submit-report/" method="post">
    {% csrf_token %}
    {% render_form serializer %}
    <input type="submit" value="Save" />
</form>

.media_typetext/html

.format'.form'

.charsetutf-8

.template'rest_framework/horizontal/form.html'

MultiPartRendereropen in new window

该渲染器用于呈现 HTML multipart form 数据。它不适合作为响应渲染器,而是用于创建测试请求,使用REST framework 的测试客户端和测试请求工厂。

.media_typemultipart/form-data; boundary=BoUnDaRyStRiNg

.format'.multipart'

.charsetutf-8

自定义渲染器open in new window

要实现自定义渲染器,您应该继承 BaseRenderer ,设置 .media_type.format 属性,并实现 .render(self, data, media_type=None, renderer_context=None) 方法。

该方法应返回一个字符串,它将用作 HTTP 响应的主体。

传递给 .render() 方法的参数是:

dataopen in new window

请求数据,由 Response() 实例化时设置。

media_type=Noneopen in new window

可选的。如果提供,这是接受的媒体类型,由内容协商(content negotiation)阶段确定。

依赖于客户端的 Accept: header,它可以比渲染器的 media_type 属性更具体,并且可能包含媒体类型参数。比如 "application/json; nested=true"

renderer_context=Noneopen in new window

可选的。如果提供,它是视图提供的上下文信息字典。

默认情况下,这将包括以下键:view , request , response , args , kwargs

举个栗子open in new window

以下是一个示例纯文本渲染器,它将返回带有数据参数的响应作为响应的内容。

from django.utils.encoding import smart_unicode
from rest_framework import renderers


class PlainTextRenderer(renderers.BaseRenderer):
    media_type = 'text/plain'
    format = 'txt'

    def render(self, data, media_type=None, renderer_context=None):
        return data.encode(self.charset)

设置 charsetopen in new window

默认情况下,渲染器类被假定为使用 UTF-8 编码。要使用不同的编码,请在渲染器上设置 charset 属性。

class PlainTextRenderer(renderers.BaseRenderer):
    media_type = 'text/plain'
    format = 'txt'
    charset = 'iso-8859-1'

    def render(self, data, media_type=None, renderer_context=None):
        return data.encode(self.charset)

请注意,如果渲染器类返回一个 unicode 字符串,则响应内容将被 Response 类强制为一个 bytestring,请在渲染器上设置 charset 属性用于确定编码。

如果渲染器返回代表原始二进制内容的字符串,则应将 charset 值设置为 None,这将确保响应的 Content-Type header 不会设置 charset 值。

在某些情况下,你可能还想将 render_style 属性设置为 'binary'。这样做也将确保可浏览的 API 不会尝试将二进制内容显示为字符串。

class JPEGRenderer(renderers.BaseRenderer):
    media_type = 'image/jpeg'
    format = 'jpg'
    charset = None
    render_style = 'binary'

    def render(self, data, media_type=None, renderer_context=None):
        return data

渲染器的高级用法open in new window

你可以使用 REST framework 的渲染器来做一些非常灵活的事情。例如...

  • 根据请求的 media type,提供来自同一端点的平面或嵌套(flat or nested)表示。
  • 同时处理常规 HTML 网页和来自相同端点的基于 JSON 的 API 响应。
  • 指定 API 客户端使用的多种 HTML 表示形式。
  • 不用明确指定渲染器的 media type,例如使用 media_type ='image/*',并使用 Accept header 改变响应的编码。

media type 的变化open in new window

在某些情况下,可能希望视图根据接受的 media type 使用不同的序列化风格。如果需要这样做,可以访问 request.accepted_renderer 以确定将用于响应的协商(negotiate)渲染器。

例如:

@api_view(('GET',))
@renderer_classes((TemplateHTMLRenderer, JSONRenderer))
def list_users(request):
    """
    A view that can return JSON or HTML representations
    of the users in the system.
    """
    queryset = Users.objects.filter(active=True)

    if request.accepted_renderer.format == 'html':
        # TemplateHTMLRenderer takes a context dict,
        # and additionally requires a 'template_name'.
        # It does not require serialization.
        data = {'users': queryset}
        return Response(data, template_name='list_users.html')

    # JSONRenderer requires serialized data as normal.
    serializer = UserSerializer(instance=queryset)
    data = serializer.data
    return Response(data)

不明确的 media typeopen in new window

在某些情况下,可能需要渲染器来提供一系列 media type 。在这种情况下,可以通过使用 media_type 值(如 image/**/*)来指定它应该响应的 media type 。

如果没有明确指定渲染器的 media type ,则应确保在返回响应时使用 content_type 属性明确指定 media type 。比如:

return Response(data, content_type='image/png')

HTML error 视图open in new window

通常情况下,无论处理常规响应还是引发异常的响应(例如 Http404PermissionDenied 异常)或 APIException 的子类引起的响应,渲染器都会有相同的表现。

如果您使用的是 TemplateHTMLRendererStaticHTMLRenderer,并且引发异常,则行为会稍有不同,反映了 Django 对错误视图的默认处理。

由 HTML 渲染器引发和处理的异常将尝试使用以下方法之一(按优先顺序)进行渲染。

  • 加载并渲染模板 {status_code}.html
  • 加载并渲染模板 api_exception.html
  • 渲染 HTTP 状态码和文本,例如 "404 Not Found"。

模板将使用 RequestContext 进行渲染,其中包含 status_codedetails 键。

注意:如果 DEBUG = True,则会显示 Django 的标准错误回溯页面,而不是显示 HTTP 状态码和文本。

第三方组件

YAML

python3 -m pip install djangorestframework-yaml
REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES': [
        'rest_framework_yaml.parsers.YAMLParser',
    ],
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework_yaml.renderers.YAMLRenderer',
    ],
}

XML

python3 -m pip install djangorestframework-xml
REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES': [
        'rest_framework_xml.parsers.XMLParser',
    ],
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework_xml.renderers.XMLRenderer',
    ],
}

JSONP

警告:
如果你需要跨域 AJAX 请求, 通常应该使用 CORS 的更现代方法作为的替代方法 JSONP, 而该方法本质上是一种浏览器黑客, 并且仅适用于全局可读的 API 端点, 在该端点中, GET 请求未经身份验证并且不需要任何用户权限。

python3 -m pip install djangorestframework-jsonp
REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework_jsonp.renderers.JSONPRenderer',
    ],
}

MessagePack

MessagePack 是一种快速高效的二进制序列化格式, Juan Riazaopen in new window 维护 djangorestframework-msgpackopen in new window 软件包, 该软件包为 REST 框架提供 MessagePack 渲染器和解析器支持。

XLSX

XLSX 是世界上最受欢迎的二进制电子表格格式, The Wharton Schoolopen in new windowTim Allenopen in new window 维护着 drf-renderer-xlsxopen in new window, 这使得端点使用 OpenPyXL 作为一个 XLSX 的电子表格, 并允许客户端下载。可以基于每个视图设置电子表格的样式。

python3 -m pip install drf-renderer-xlsx
REST_FRAMEWORK = {
    ...

    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
        'drf_renderer_xlsx.renderers.XLSXRenderer',
    ],
}

为了避免流传输的文件没有文件名(浏览器通常将其默认为没有扩展名的 download), 我们需要使用 mixin 覆盖 Content-Disposition标头, 如果未提供文件名则默认为 medusa.xlsx, 例如:

from rest_framework.viewsets import ReadOnlyModelViewSet
from drf_renderer_xlsx.mixins import XLSXFileMixin
from drf_renderer_xlsx.renderers import XLSXRenderer

from .models import MyExampleModel
from .serializers import MyExampleSerializer

class MyExampleViewSet(XLSXFileMixin, ReadOnlyModelViewSet):
    queryset = MyExampleModel.objects.all()
    serializer_class = MyExampleSerializer
    renderer_classes = [XLSXRenderer]
    filename = 'medusa.xlsx'

CSV

逗号分隔的值是纯文本格式的表格数据格式, 可以轻松地导入电子表格应用程序中, Mjumbe Poeopen in new window 维护 djangorestframework-csvopen in new window 软件包, 该软件包为 REST 框架提供 CSV 渲染器支持。

UltraJSON

UltraJSON 是经过优化的 C JSON 编码器, 可以显着加快 JSON 渲染速度, Jacob Haslehurstopen in new window 维护了 drf-ujson-rendereropen in new window 软件包, 该软件包使用 UJSON 软件包实现 JSON 呈现。

Pandas (CSV, Excel, PNG)

Django REST Pandasopen in new window 提供了序列化器和渲染器, 它们支持通过 Pandas DataFrame APIopen in new window 进行其他数据处理和输出, Django REST Pandas 包括用于 Pandas 样式 CSV 文件、Excel工作簿(包括 .xls.xlsx)以及许多其他格式的渲染器, 它由 S. Andrew Sheppardopen in new window 作为 wq Projectopen in new window 的一部分进行维护。

LaTeX

Rest Framework Latexopen in new window 提供了一个渲染器, 该渲染器使用 Laulatex 输出 PDF, 它由 Pebble (S/F Software)open in new window 维护。