CRUD для Yatube

Вы набили руку в тренажёре и теперь можете самостоятельно, с нуля, написать API для реального Django-проекта. Но прежде — необходимая для выполнения задания теория.

Viewsets

Вьюсет — это view-класс, реализующий все операции CRUD: вывод объекта или списка объектов, добавление, правка и удаление объекта. Viewset заменяет два обычных view-класса.
Во вьюсеты встроена обработка разных типов запросов, работа с сериализаторами и моделями, возврат ошибок. Не нужно ничего придумывать и писать повторяющийся код, как вы это делали раньше. Чтобы изменить для каких-то методов поведение по умолчанию, достаточно в классе-наследнике переопределить нужный метод и внести в него необходимые изменения.
Кроме того, вьюсеты предоставляют инструмент для генерации эндпоинтов, так называемые роутеры (routers).
Всё, что нужно сделать — импортировать во views.py класс ModelViewSet из модуля viewsets и создать класс-наследник. Внутри класса нужно описать два обязательных поля — queryset и serializer_class:
Скопировать кодPYTHON
class UserViewSet(viewsets.ModelViewSet): queryset = User.objects.all() serializer_class = UserSerializer
Чтобы создать роутер, который генерирует эндпоинты, нужно в файле urls.py импортировать класс DefaultRouter из модуля routers и создать объект этого класса. Затем для регистрации каждого эндпоинта нужно вызывать у объекта роутера метод register() , который принимает в качестве аргументов для параметра: шаблон URL и класс вьюсета. И включить роутер в список urlpatterns:
Скопировать кодPYTHON
from rest_framework.routers import DefaultRouter router = DefaultRouter() router.register('api/v1/users', UserViewSet) urlpatterns = [ ... path('', include(router.urls)), ]
В случае затруднений обращайтесь к документации: https://www.django-rest-framework.org/api-guide/viewsets/ . Там же можно посмотреть примеры применения.
Есть и неофициальный перевод на русский язык (но лучше привыкать читать документацию на английском): https://github.com/ilyachch/django-rest-framework-rusdoc/blob/master/api-navigation/viewsets.md

Про CORS

При деплое вашего проекта на веб-сервер вы можете столкнуться с ограничениями на отправку запросов с других доменов.
По умолчанию отправка запросов ограничивается пределами одного домена: программа-клиент, отправляющая запрос к серверу, должна находиться в том же домене, что и веб-сервер, к которому направлен запрос. Это называется «политика единого источника».
При взаимодействии программы-клиента с API, находящимся в другом домене (например, клиент размещён на mysite.ru, а API — на yoursite.ru) — потенциально возможны проблемы безопасности.
Вы уже сталкивались с этой проблемой при отправке форм. Для защиты от поддельных запросов вы создавали и отправляли вместе с формой CSRF-токен, по которому сервер определял, что запрос не подделан.
По умолчанию кросс-доменные запросы запрещены в дефолтных настройках веб-сервера: это самое простое решение. Но тут возникает другая проблема: к открытому API надо разрешить запросы с любого домена, иначе какой же он «открытый».
Чтобы к нашему API могли обращаться любые приложения — нужно разрешить фреймворку использовать CORS.
Cross-Origin Resource Sharing (англ. «совместное использование ресурсов между разными источниками») — это разрешение на обработку запросов с другого домена.
Для настройки CORS установите пакет django-cors-headers в виртуальном окружении:
Скопировать кодBASH
pip install django-cors-headers
Подключите его в settings.py как приложение:
Скопировать кодPYTHON
INSTALLED_APPS = [ ... 'rest_framework', 'corsheaders', ]
И в списке MIDDLEWARE зарегистрируйте обработчик (обязательно перед CommonMiddleware)
Скопировать кодPYTHON
MIDDLEWARE = [ ... 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', ... ]
Осталось добавить в конфиг две настройки: CORS_ORIGIN_ALLOW_ALL и CORS_URLS_REGEX:
  • CORS_ORIGIN_ALLOW_ALL: значение True, установленное для этого ключа, разрешит обрабатывать запросы, приходящие с любого хоста, игнорируя политику Some Origin. Если установить False или просто удалить этот ключ из конфига — будут разрешены только запросы с текущего хоста.
  • CORS_URLS_REGEX: значением этого ключа должно быть регулярное выражение, которое определяет URL'ы, к которым можно обращаться с других доменов.
Можно разрешить кросс-доменные запросы к любым адресам домена, но это повысит уязвимость проекта. Потому включим CORS только для путей с префиксом /api. В ключе CORS_URLS_REGEX задаём регулярное выражение, описывающее такие пути:
Скопировать кодPYTHON
CORS_ORIGIN_ALLOW_ALL = True CORS_URLS_REGEX = r'^/api/.*$'
Для ключа CORS_URLS_REGEX можно указать и несколько значений через запятую, каждое должно быть в кавычках.
Префикс r перед строкой определяет r-строку. Такую строку система будет читать как простую последовательность символов, игнорируя escape-последовательности (escape sequence) — комбинации обратного слеша и символа. Например, escape-последовательность \n интерпретируется как перенос строки, но в r-строке это будут просто два текстовых символа безо всякого скрытого смысла.
Настройка доступа к API завершена. Теперь любой клиент с любого домена может отправлять запросы к вашему API.
Важно: если вы делаете закрытый API, то в CORS_ORIGIN_WHITELIST укажите домен, с которого разрешены запросы (например, localhost:3000) и удалите ключ CORS_ORIGIN_ALLOW_ALL (тогда он примет значение по умолчанию: False).
Полное руководство по работе с django-cors-headers находится здесь: https://github.com/adamchainz/django-cors-headers

Задание

Предварительная настройка

У вас в репозитории появился проект api_yatube, клонируйте его в свою рабочую директорию Dev.
В репозитории — знакомый вам Yatube, но не весь, а только его бэкенд: приложения, модели. Мы исключили из проекта фронтенд: он не понадобится в работе.
Активируйте виртуальное окружение и установите все необходимые пакеты. При оформлении проекта принято перечислять все зависимости в файле requirements.txt, чтобы их потом можно было установить одной командой из этого файла (обратите внимание на ключ -r, он означает «рекурсивно»):
Скопировать кодBASH
pip install -r requirements.txt

Задача

В проекте api_yatube есть приложение posts с описанием моделей Yatube. Вам нужно реализовать API для всех моделей.
Выносить логику API в отдельное приложение — хорошая практика. При иной организации кода работать в большом проекте с множеством приложений будет крайне неудобно. Сделайте правильно: создайте приложение api и работайте в нём.
Опишите эндпоинты для взаимодействия с ресурсами:
api/v1/posts/ (GET, POST): получаем список всех постов или создаём новый пост
api/v1/posts/{post_id}/ (GET, PUT(PATCH), DELETE): получаем, редактируем или удаляем пост по id
api/v1/api-token-auth/ (POST): передаём логин и пароль, получаем токен
Пример запроса POST:
Скопировать кодJSON
{ "text":"Вечером собрались в редакции «Русской мысли», чтобы поговорить о народном театре. Проект Шехтеля всем нравится." }
Пример ответа:
Скопировать кодJSON
{ "id": 14, "text": "Вечером собрались в редакции «Русской мысли», чтобы поговорить о народном театре. Проект Шехтеля всем нравится.", "author": "Антон Чехов", "image": null, "pub_date": "2020-04-01T08:47:11.084589Z" }
api/v1/posts/{post_id}/comments/{comment_id}/ (GET, PUT(PATCH), DELETE): получаем, редактируем или удаляем комментарий по id
api/v1/posts/{post_id}/comments/ (GET, POST): получаем список всех комментариев или создаём новый, передав id поста, который хотим прокомментировать
Пример запроса POST с токеном Антона Чехова: отправляем комментарий к посту с id=14:
Скопировать кодJSON
{ "text": "тест тест", }
Пример ответа:
Скопировать кодJSON
{ "id": 4, "author": "Антон Чехов", "post": 14, "text": "тест тест", "created": "2020-04-01T10:14:51.388932Z" }
В ответ на запросы POST и PUT(PATCH) ваш API должен возвращать объект, который был добавлен или изменён.
Начните с описания файла urls.py на уровне проекта. Он должен включать (include) маршруты из приложения api.
Обязательное условие: работайте с моделью Post через Viewsets.
Если вы решите, что Viewsets подойдёт для работы и с остальными моделями — не стесняйтесь, применяйте их везде, где можно.

Ограничения

Не применяйте сторонние библиотеки вроде Nested Routers или её аналогов. Возможно, это упростит выполнение задания, но мы считаем важным, чтобы вы реализовали всё самостоятельно.

Отправка запросов и тестирование

Для отправки запросов лучше всего использовать инструмент Postman, который мы упоминали в прошлых уроках. Скачать его можно здесь: https://www.postman.com/downloads/
Также вы можете использовать встроенный браузерный клиент Django REST Framework или консольный клиент HTTPie: https://httpie.org/
Аккаунт на Github успешно привязан к вашему аккаунту. Теперь
можно отправить задание на проверку.