DRF - Отношения и гиперссылки API
На данный момент отношения в нашем API представлены с использованием первичных ключей. В этой части руководства мы улучшим целостность и обнаруживаемость нашего API, вместо этого используя гиперссылки для отношений.
Создание endpoint для корня нашего API
Сейчас у нас есть endpoint для «сниппетов» и «пользователей», но у нас нет единой точки входа в наш API.
Чтобы создать его, мы будем использовать обычный view на основе функций и декоратор @api_view, который мы представили ранее. В ваши snippets/views.py добавьте:
from rest_framework.decorators import api_view from rest_framework.response import Response from rest_framework.reverse import reverse @api_view(['GET']) def api_root(request, format=None): return Response({ 'users': reverse('user-list', request=request, format=format), 'snippets': reverse('snippet-list', request=request, format=format) })
Здесь следует отметить две вещи. Во-первых, мы используем функцию реверса инфраструктуры REST для возврата полностью определенных URL; во-вторых, шаблоны URL идентифицируются по вспомогательным именам, которые мы объявим позже в нашем snippets/urls.py.
Создание конечной точки для выделенных фрагментов
Другой очевидной вещью, которая все еще отсутствует в нашем API-интерфейсе для вставки, является код, подсвечивающий конечные точки. В отличие от всех других наших конечных точек API, мы не хотим использовать JSON, а просто представляем представление HTML.
Существует два стиля рендеринга HTML, предоставляемых REST: один для работы с HTML с использованием шаблонов, другой для работы с предварительно визуализированным HTML. Второй рендерер - это тот, который мы хотели бы использовать для этой конечной точки.
Еще одна вещь, которую мы должны учитывать при создании представления подсветки кода, - это то, что нет никакого конкретного конкретного универсального представления, которое мы можем использовать. Мы не возвращаем экземпляр объекта, а вместо этого свойство экземпляра объекта. Вместо использования конкретного универсального представления мы будем использовать базовый класс для представления экземпляров и создадим наш собственный метод .get ().
В ваши сниппеты / views.py добавьте:
from rest_framework import renderers from rest_framework.response import Response class SnippetHighlight(generics.GenericAPIView): queryset = Snippet.objects.all() renderer_classes = [renderers.StaticHTMLRenderer] def get(self, request, *args, **kwargs): snippet = self.get_object() return Response(snippet.highlighted)
Как обычно, нам нужно добавить новые представления, которые мы создали, в наш URLconf. Мы добавим шаблон url для нашего нового корня API в snippets/urls.py:
path('', views.api_root),
А затем добавьте шаблон URL для подсветки фрагмента:
path('snippets/<int:pk>/highlight/', views.SnippetHighlight.as_view()),
Гиперссылка нашего API
Работа с отношениями между сущностями является одним из наиболее сложных аспектов проектирования веб-API. Существует несколько различных способов представления отношений:
- Использование первичных ключей.
- Использование гиперссылок между сущностями.
- Использование уникального идентифицирующего поля слага в связанной сущности.
- Использование строкового представления по умолчанию для связанной сущности.
- Вложение связанной сущности в родительское представление.
- Некоторые другие пользовательские представления.
Платформа REST поддерживает все эти стили и может применять их через прямые или обратные отношения или применять их к пользовательским менеджерам, таким как общие внешние ключи.
В этом случае мы хотели бы использовать стиль гиперссылки между сущностями. Для этого мы модифицируем наши сериализаторы, чтобы расширить HyperlinkedModelSerializer вместо существующего ModelSerializer.
HyperlinkedModelSerializer имеет следующие отличия от ModelSerializer:
- Он не включает поле id по умолчанию.
- Он включает в себя поле URL, используя HyperlinkedIdentityField.
- В отношениях используется HyperlinkedRelatedField вместо PrimaryKeyRelatedField.
Мы можем легко переписать наши существующие сериализаторы для использования гиперссылок. В ваши snippets/serializers.py добавьте:
class SnippetSerializer(serializers.HyperlinkedModelSerializer): owner = serializers.ReadOnlyField(source='owner.username') highlight = serializers.HyperlinkedIdentityField(view_name='snippet-highlight', format='html') class Meta: model = Snippet fields = ['url', 'id', 'highlight', 'owner', 'title', 'code', 'linenos', 'language', 'style'] class UserSerializer(serializers.HyperlinkedModelSerializer): snippets = serializers.HyperlinkedRelatedField(many=True, view_name='snippet-detail', read_only=True) class Meta: model = User fields = ['url', 'id', 'username', 'snippets']
Обратите внимание, что мы также добавили новое поле «highlight». Это поле того же типа, что и поле url, за исключением того, что оно указывает на шаблон URL 'snippet-highlight' вместо шаблона URL 'snippet-detail'.
Поскольку мы включили URL-адреса с суффиксами формата, такие как «.json», мы также должны указать в поле выделения, что для всех возвращаемых гиперссылок с суффиксами формата следует использовать суффикс «.html».
Убедитесь, что наши шаблоны URL названы
Если у нас будет API с гиперссылками, нам нужно убедиться, что мы назовем наши шаблоны URL. Давайте посмотрим, какие шаблоны URL нам нужно назвать.
- Корень нашего API ссылается на «список пользователей» и «список фрагментов».
- Наш сериализатор фрагментов содержит поле, которое ссылается на «фрагмент фрагмента».
- Наш пользовательский сериализатор содержит поле, которое ссылается на «фрагмент кода».
- Наш сериализатор и пользовательские сериализаторы содержат поля 'url', которые по умолчанию будут ссылаться на '{model_name} -detail', которые в этом случае будут 'snippet-detail' и 'user-detail'.
После добавления всех этих имен в наш URLconf наш последний файл snippets/urls.py должен выглядеть следующим образом:
from django.urls import path from rest_framework.urlpatterns import format_suffix_patterns from snippets import views # API endpoints urlpatterns = format_suffix_patterns([ path('', views.api_root), path('snippets/', views.SnippetList.as_view(), name='snippet-list'), path('snippets/<int:pk>/', views.SnippetDetail.as_view(), name='snippet-detail'), path('snippets/<int:pk>/highlight/', views.SnippetHighlight.as_view(), name='snippet-highlight'), path('users/', views.UserList.as_view(), name='user-list'), path('users/<int:pk>/', views.UserDetail.as_view(), name='user-detail') ])
Добавление нумерации страниц
Представления списков для пользователей и фрагменты кода могут в конечном итоге возвращать довольно много экземпляров, поэтому на самом деле мы хотели бы убедиться, что разбили результаты на страницы и позволили клиенту API пройти по каждой отдельной странице.
Мы можем изменить стиль списка по умолчанию, чтобы использовать нумерацию страниц, слегка изменив наш файл tutorial/settings.py.
Добавьте следующий параметр:
REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 10 }
Обратите внимание, что все настройки в REST frameworks помещены в одно пространство имен словаря с именем REST_FRAMEWORK, что помогает отделить их от других настроек вашего проекта.
Мы могли бы также настроить стиль нумерации страниц, если нам это нужно, но в этом случае мы просто будем придерживаться значения по умолчанию.
Просмотр API
Если мы откроем браузер и перейдем к API с возможностью просмотра, вы обнаружите, что теперь вы можете обойти API, просто перейдя по ссылкам. Вы также сможете увидеть ссылки «выделение» на экземплярах фрагмента, которые приведут вас к выделенным HTML-представлениям кода.
В шестой части руководства мы рассмотрим, как мы можем использовать ViewSets и Routers, чтобы уменьшить количество кода, необходимого для создания нашего API.