Рендереры

Рендереры

Прежде чем экземпляр TemplateResponse будет возвращен клиенту, он должен быть отрендерен. Процесс рендеринга принимает промежуточное представление шаблона и контекста и превращает его в конечный поток байтов, который может быть передан клиенту.

DRF включает ряд встроенных классов Renderer, которые позволяют возвращать ответы с различными типами медиа. Также имеется поддержка определения собственных пользовательских рендереров, что дает вам гибкость в разработке собственных типов медиа.

Как определяется рендерер

Набор допустимых рендерингов для представления всегда определяется как список классов. При входе в представление DRF будет выполнять согласование содержимого входящего запроса и определять наиболее подходящий рендерер для удовлетворения запроса.

Основной процесс согласования содержимого включает в себя изучение заголовка Accept запроса, чтобы определить, какие типы медиа ожидаются в ответе. По желанию, суффиксы формата в URL могут быть использованы для явного запроса определенного представления. Например, URL http://example.com/api/users_count.json может быть конечной точкой, которая всегда возвращает данные в формате JSON.

Для получения дополнительной информации смотрите документацию по согласованию контента.

Установка рендереров

Набор рендеров по умолчанию можно задать глобально, используя параметр DEFAULT_RENDERER_CLASSES. Например, следующие настройки будут использовать JSON в качестве основного типа медиа, а также включать API самоописания.

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

Вы также можете установить рендереры, используемые для отдельного представления или набора представлений, используя представления на основе класса APIView.

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)

Упорядочивание классов рендеринга

Важно при определении классов рендеринга для вашего API подумать о том, какой приоритет вы хотите присвоить каждому типу медиа. Если клиент недоопределяет представления, которые он может принимать, например, посылает заголовок Accept: */* заголовок, или вообще не включает заголовок Accept, то DRF выберет первый рендерер в списке для использования в ответе.

Например, если ваш API обслуживает JSON-ответы и HTML-просмотр, вы можете сделать JSONRenderer рендерером по умолчанию, чтобы отправлять JSON ответы клиентам, которые не указывают заголовок Accept.

Если ваш API включает представления, которые могут обслуживать как обычные веб-страницы, так и ответы API в зависимости от запроса, то вы можете сделать TemplateHTMLRenderer рендерером по умолчанию, чтобы хорошо работать со старыми браузерами, которые отправляют broken accept headers.


API Reference

JSONRenderer

Переводит данные запроса в JSON, используя кодировку utf-8.

Обратите внимание, что стиль по умолчанию включает символы юникода и отображает ответ, используя компактный стиль без лишних пробелов:

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

Клиент может дополнительно включить параметр медиатипа 'indent', в этом случае возвращаемый JSON будет иметь отступ. Например, Accept: application/json; indent=4.

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

Стиль кодировки JSON по умолчанию может быть изменен с помощью ключей настроек UNICODE_JSON и COMPACT_JSON.

.media_type: application/json.

.format: 'json'.

.charset: None.

TemplateHTMLRenderer

Рендерит данные в HTML, используя стандартный шаблонный рендеринг Django. В отличие от других рендерингов, данные, передаваемые в Response, не нужно сериализовать. Также, в отличие от других рендереров, вы можете включить аргумент template_name при создании Response.

TemplateHTMLRenderer создаст RequestContext, используя response.data в качестве диктанта контекста, и определит имя шаблона, который будет использоваться для рендеринга контекста.


Примечание: При использовании с представлением, которое использует сериализатор, Response, отправленный для рендеринга, может не быть словарем и должен быть обернут в dict перед возвратом, чтобы TemplateHTMLRenderer смог его отрендерить. Например:

response.data = {'results': response.data}

Имя шаблона определяется (в порядке предпочтения):

  1. Явный аргумент 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 либо для возврата обычных HTML-страниц с помощью DRF, либо для возврата HTML и API ответов с одной конечной точки.

Если вы создаете сайты, использующие TemplateHTMLRenderer наряду с другими классами рендереров, вам следует рассмотреть возможность включения TemplateHTMLRenderer в качестве первого класса в список renderer_classes, чтобы он был приоритетным даже для браузеров, которые посылают плохо сформированные заголовки ACCEPT:.

Дополнительные примеры использования TemplateHTMLRenderer смотрите в HTML & Forms Topic Page.

.media_type: text/html.

.format: 'html'.

.charset: utf-8

См. также: StaticHTMLRenderer

StaticHTMLRenderer

Простой рендерер, который просто возвращает предварительно отрендеренный 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 либо для возврата обычных HTML-страниц с помощью DRF, либо для возврата HTML- и API-ответов с одной конечной точки.

.media_type: text/html.

.format: 'html'.

.charset: utf-8

См. также: TemplateHTMLRenderer.

BrowsableAPIRenderer

Рендерит данные в HTML для Browsable API:

Этот рендерер определяет, какой другой рендерер имел бы наивысший приоритет, и использует его для отображения ответа в стиле API на HTML-странице.

.media_type: text/html.

.format: 'api'.

.charset: utf-8

.template: 'rest_framework/api.html'.

Настройка BrowsableAPIRenderer

По умолчанию содержимое ответа будет отображаться рендерером с наивысшим приоритетом, кроме BrowsableAPIRenderer. Если вам нужно настроить это поведение, например, использовать HTML в качестве формата возврата по умолчанию, но использовать JSON в Web-интерфейсе API, вы можете сделать это, переопределив метод get_default_renderer(). Например:

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

AdminRenderer

Рендерит данные в HTML для отображения в стиле администратора:

Этот рендерер подходит для веб-интерфейсов в стиле CRUD, которые также должны представлять удобный интерфейс для управления данными.

Обратите внимание, что представления, которые имеют вложенные или списковые сериализаторы для своего ввода, не будут хорошо работать с AdminRenderer, так как HTML-формы не могут должным образом поддерживать их.

Примечание: AdminRenderer способен включать ссылки на детальные страницы только в том случае, если в данных присутствует правильно настроенный атрибут URL_FIELD_NAME (по умолчанию url). Для 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_type: text/html.

.format: 'admin'.

.charset: utf-8

.template: 'rest_framework/admin.html'.

HTMLFormRenderer

Переводит данные, возвращаемые сериализатором, в форму HTML. Вывод этого рендерера не включает заключающие теги <form>, скрытый CSRF-вход или какие-либо кнопки отправки.

Этот рендерер не предназначен для прямого использования, но может быть использован в шаблонах путем передачи экземпляра сериализатора в тег шаблона render_form.


<div data-gb-custom-block data-tag="load"></div>

<form action="/submit-report/" method="post">
    

<div data-gb-custom-block data-tag="csrf_token"></div>

    

<div data-gb-custom-block data-tag="render_form"></div>
    <input type="submit" value="Save" />
</form>

Для получения дополнительной информации смотрите документацию HTML & Forms.

.media_type: text/html.

.format: 'format'.

.charset: utf-8

.template: 'rest_framework/horizontal/form.html'.

MultiPartRenderer

Этот рендерер используется для рендеринга данных многочастной формы HTML. Он не подходит для рендеринга ответов, а используется для создания тестовых запросов, используя тестовый клиент и фабрику тестовых запросов DRF.

.media_type: multipart/form-data; border=BoUnDaRyStRiNg.

.format: 'multipart'.

.charset: utf-8


Пользовательские рендеры

Для реализации пользовательского рендерера необходимо отнаследоваться от BaseRenderer, установить свойства .media_type и .format и реализовать метод .render(self, data, accepted_media_type=None, renderer_context=None).

Метод должен возвращать строку байт, которая будет использоваться в качестве тела ответа HTTP.

Аргументы, передаваемые методу .render(), следующие:

data

Данные запроса, заданные инстанцией Response().

accepted_media_type=None

Дополнительно. Если указано, то это принятый тип носителя, определенный на этапе согласования содержимого.

В зависимости от заголовка Accept: клиента, он может быть более конкретным, чем атрибут media_type рендерера, и может включать параметры типа медиа. Например, 'application/json; nested=true'.

renderer_context=None

Опционально. Если предоставляется, то это словарь контекстной информации, предоставляемой представлением.

По умолчанию сюда входят следующие ключи: view, request, response, args, kwargs.

Пример

Ниже приведен пример рендеринга обычного текста, который вернет ответ с параметром data в качестве содержимого ответа.

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


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

    def render(self, data, accepted_media_type=None, renderer_context=None):
        return smart_str(data, encoding=self.charset)

Установка набора символов

По умолчанию предполагается, что классы рендеринга используют кодировку UTF-8. Чтобы использовать другую кодировку, установите атрибут charset для рендерера.

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

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

Обратите внимание, что если класс рендерера возвращает строку unicode, то содержимое ответа будет преобразовано в строку байт классом Response, при этом атрибут charset, установленный на рендерере, будет использоваться для определения кодировки.

Если рендерер возвращает строку байт, представляющую необработанное двоичное содержимое, вам следует установить значение charset равное None, что обеспечит отсутствие в заголовке Content-Type ответа значения charset.

В некоторых случаях вы также можете установить атрибут render_style на 'binary'. Это также гарантирует, что Web-интерфейс API не будет пытаться отобразить двоичное содержимое в виде строки.

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

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

Расширенное использование рендеринга

Вы можете делать довольно гибкие вещи, используя рендереры DRF. Некоторые примеры...

  • Предоставление плоских или вложенных представлений из одной и той же конечной точки, в зависимости от запрашиваемого типа носителя.

  • Предоставлять как обычные веб-страницы HTML, так и ответы API на основе JSON с одной и той же конечной точки.

  • Указывать несколько типов представления HTML для использования клиентами API.

  • Недоопределять медиатип рендерера, например, используя media_type = 'image/*', и использовать заголовок Accept для изменения кодировки ответа.

Различное поведение в зависимости от типа носителя

В некоторых случаях вы можете захотеть, чтобы ваше представление использовало различные стили сериализации в зависимости от принятого типа носителя. Если вам нужно сделать это, вы можете обратиться к request.accepted_renderer, чтобы определить согласованный рендерер, который будет использоваться для ответа.

Например:

@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_type, такое как image/*, или */*.

Если вы недоопределили медиатип рендерера, вы должны убедиться, что указали медиатип явно, когда возвращаете ответ, используя атрибут content_type. Например:

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

Проектирование типов носителей

Для целей многих Web API может быть достаточно простых ответов JSON с гиперссылками на отношения. Если вы хотите полностью внедрить RESTful дизайн и HATEOAS, вам необходимо более детально продумать дизайн и использование типов медиа.

По словам Роя Филдинга, "REST API должен потратить почти все свои усилия по описанию на определение типа(ов) медиа, используемых для представления ресурсов и управления состоянием приложения, или на определение расширенных имен отношений и/или гипертекстовой разметки для существующих стандартных типов медиа".

Хорошими примерами пользовательских типов медиа являются использование GitHub пользовательского типа медиа application/vnd.github+json и одобренная IANA гипермедиа на основе JSON application/vnd.collection+json Майка Амундсена.

HTML представления ошибок

Обычно рендерер ведет себя одинаково независимо от того, имеет ли он дело с обычным ответом или с ответом, вызванным возникшим исключением, например, исключением Http404 или PermissionDenied, или подклассом APIException.

Если вы используете TemplateHTMLRenderer или StaticHTMLRenderer и при этом возникает исключение, поведение немного отличается и является зеркальным отражением Django's default handling of error views.

Исключения, возникающие и обрабатываемые средством рендеринга HTML, будут пытаться отобразить с помощью одного из следующих методов, в порядке старшинства.

  • Загрузите и отобразите шаблон с именем {status_code}.html.

  • Загрузите и отобразите шаблон с именем api_exception.html.

  • Вывести код статуса HTTP и текст, например, "404 Not Found".

Шаблоны будут отображаться с RequestContext, который включает ключи status_code и details.

Примечание: Если DEBUG=True, то вместо отображения кода статуса HTTP и текста будет отображаться стандартная страница ошибки трассировки Django.


Пакеты сторонних производителей

Также доступны следующие пакеты сторонних производителей.

YAML

REST framework YAML обеспечивает поддержку разбора и рендеринга YAML. Ранее он был включен непосредственно в пакет DRF, но теперь поддерживается как сторонний пакет.

Установка и настройка

Установите с помощью pip.

$ pip install djangorestframework-yaml

Измените настройки DRF.

REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES': [
        'rest_framework_yaml.parsers.YAMLParser',
    ],
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework_yaml.renderers.YAMLRenderer',
    ],
}

XML

REST Framework XML предоставляет простой неформальный формат XML. Ранее он был включен непосредственно в пакет DRF, но теперь поддерживается как сторонний пакет.

Установка и настройка

Установите с помощью pip.

$ pip install djangorestframework-xml

Измените настройки DRF.

REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES': [
        'rest_framework_xml.parsers.XMLParser',
    ],
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework_xml.renderers.XMLRenderer',
    ],
}

JSONP

REST framework JSONP обеспечивает поддержку рендеринга JSONP. Ранее он был включен непосредственно в пакет DRF, но теперь поддерживается как сторонний пакет.


Предупреждение: Если вам требуются междоменные AJAX-запросы, вам следует использовать более современный подход CORS в качестве альтернативы JSONP. Более подробную информацию смотрите в документации CORS.

Подход jsonp по сути является хаком для браузера и подходит только для глобально читаемых конечных точек API, где запросы GET являются неаутентифицированными и не требуют никаких разрешений пользователя.


Установка и настройка

Установите с помощью pip.

$ pip install djangorestframework-jsonp

Измените настройки DRF.

REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework_jsonp.renderers.JSONPRenderer',
    ],
}

MessagePack

MessagePack - это быстрый и эффективный формат двоичной сериализации. Juan Riaza поддерживает пакет djangorestframework-msgpack, который обеспечивает поддержку рендеринга и парсера MessagePack для DRF.

Microsoft Excel: XLSX (Двоичные конечные точки электронных таблиц)

XLSX - это самый популярный в мире формат двоичных электронных таблиц. Тим Аллен из The Wharton School поддерживает drf-excel, который отображает конечную точку в виде электронной таблицы XLSX с помощью OpenPyXL и позволяет клиенту загрузить ее. Электронные таблицы могут быть стилизованы для каждого вида.

Установка и настройка

Установите с помощью pip.

$ pip install drf-excel

Измените настройки DRF.

REST_FRAMEWORK = {
    ...

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

Чтобы избежать потоковой передачи файла без имени (при этом браузер часто принимает по умолчанию имя файла "download" без расширения), нам необходимо использовать миксин для переопределения заголовка Content-Disposition. Если имя файла не указано, то по умолчанию будет задано export.xlsx. Например:

from rest_framework.viewsets import ReadOnlyModelViewSet
from drf_excel.mixins import XLSXFileMixin
from drf_excel.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 = 'my_export.xlsx'

CSV

Значения, разделенные запятыми, - это формат табличных данных в виде обычного текста, который легко импортируется в приложения электронных таблиц. Mjumbe Poe поддерживает пакет djangorestframework-csv, который обеспечивает поддержку CSV-рендеринга для DRF.

UltraJSON

UltraJSON - это оптимизированный кодировщик JSON на языке C, который может значительно ускорить рендеринг JSON. Adam Mertz поддерживает drf_ujson2, форк ныне не поддерживаемого drf-ujson-renderer, который реализует рендеринг JSON с использованием пакета UJSON.

CamelCase JSON

djangorestframework-camel-case предоставляет рендереры и парсеры JSON в верблюжьем регистре для DRF. Это позволяет сериализаторам использовать имена полей в стиле Python с подчеркиванием, но отображать их в API как имена полей в верблюжьем регистре в стиле Javascript. Поддерживается Виталием Бабием.

Pandas (CSV, Excel, PNG)

Django REST Pandas предоставляет сериализатор и рендереры, которые поддерживают дополнительную обработку и вывод данных через Pandas DataFrame API. Django REST Pandas включает рендереры для файлов CSV в стиле Pandas, рабочих книг Excel (как .xls, так и .xlsx) и ряда других форматов. Он поддерживается S. Andrew Sheppard в рамках проекта wq Project.

LaTeX

Rest Framework Latex предоставляет рендерер, который выводит PDF-файлы с использованием Laulatex. Он поддерживается Pebble (S/F Software).

Last updated