DRF. Библиотеки

Введение

Продолжение блока Django REST framework. В прошлом блоке мы достаточно подробно обсудили возможности DRF поставляемые, так скажем, из 'коробки'. В этом блоке познакомимся с некоторыми наиболее важными сторонними библиотеками. Самое важное, что нам осталось обсудить - аутентификация, на ней остановимся подробно, и дополнительно обсудим еще пару возможностей, реализуемых сторонними библиотеками.

Аутентификация

Первая важная тема - Аутентификация. В прошлом блоке мы коснулись всех базовых возможностей, кроме этой. В базовом варианте DRF поддерживает авторизацию на основе сессий и токенов. Начнем с сессий.

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

Различие в том, что в случае сессий этот идентификатор привязан к устройству, с которого мы авторизуемся, а в случе токенов - нет. Привязка к устройству связана с тем, что информация о сесси хранится в Cookies, параметр Session.

И на самом деле, для авторизации по сессиям нам не нужно писать никаких дополнительных настроек. Она установлена для нас по умолчанию, о чем свидетельствует кнопка Log in. Настройка для такой аутентификации уже написана в словаре REST_FRAMEWORK.

pylibrary/settings.py
...
REST_FRAMEWORK = {

    ...

    'DEFAULT_AUTHENTICATION_CLASSES': [
        # 'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ]

}
...

Записывается она в списке DEFAULT_AUTHENTICATION_CLASSES и называется SessionAuthentication.

Есть еще один вид аутентификации, который в примере стоит в комментарии - BasicAuthentication.

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

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

pylibrary/settings.py
INSTALLED_APPS = [
    ...
    'rest_framework.authtoken',
]

Называется приложение authtoken. После добавления необходимо произвести миграции.

Миграции нужны, потому что authtoken имеет таблицу Tokens.

Теперь мы можем создавать токены для наших пользователей. Для того чтобы посмотреть, как это происходит, перейдем в shell.

shell

>>> from rest_framework.authtoken.models import Token
>>> from django.contrib.auth.models import User

>>> admin3=User.objects.get(username='admin3')
>>> admin3
<User: admin3>

>>> token = Token.objects.create(user=admin3)
>>> token
<Token: 8345b306fe3d9816c25f2bad243af5a21e1d1b86>

Для этого из rest_framework.authtoken.models заберем модель где хранятся токены и из django.contrib.auth.models таблицу пользователей. Заберем какого-нибудь пользователя и создадим новый объект в таблице Token, в которой будет храниться токен этого пользователя.

Заодно посмотрим, что представляет собой этот токен, как видно это просто уникальная последовательность символов.

Токен есть. Что нам теперь с ним делать?

Перейдем в Postman. Как вы, возможно, помните, просмотр списка авторов доступен только авторизованным пользователям. Соответственно, если попробовать отправить GET запрос на адрес для вывода авторов мы получим 403 ответ.

Так и есть. Но postman хорош тем, что он поддерживает отправку запросов с аутентификационными данными.

Но перед этим давайте добавим автоматизацию для генерации токенов.

api/views.py
...
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token


@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
    if created:
        Token.objects.create(user=instance)
...
api/urls.py
from django.urls import path

from api.views import *
from rest_framework import routers
from rest_framework.authtoken import views


router = routers.DefaultRouter()
router.register('authors', AuthorsViewSet, basename='authors')
router.register('books', BookViewSet, basename='book')
urlpatterns = router.urls

urlpatterns += [
    path('api-token-auth/', views.obtain_auth_token)

Сигнал и url для этого функционала возьмем из документации. В случае наличия токена мы будем его возвращать, в случае отсутствия создавать.

Для проверки вернемся в Postman и отправим POST запрос на адрес, который мы выше добавили в urlpatterns. Отправлять запрос мы должны под данными пользователя, чей токен нам необходим. Для этого нужно перейти в Body - form-data и добавить два ключа - логин и пароль. Ответ на запрос строка с токеном. Обратите внимание токен соответствует тому, что мы сгенерировали выше в shell.

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

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

Теперь наконец попробуем авторизоваться по токену.

Попробуем получить список авторов под токеном admin3. Для отправки запроса с токеном нужно добавить дополнительный заголовок с ключом Authorization и значением Token (токен). На скриншоте мы сделали все правильно, но ответа в виде списка авторов мы не получили. Произошло это по тому, что для view явно не указан способ авторизации.

api/views.py
...
from rest_framework.authentication import TokenAuthentication

...

class AuthorsViewSet(viewsets.ModelViewSet):
    authentication_classes = [TokenAuthentication]
    ...

Способ авторизации указывается в переменной authentication_classes. Записью с примера мы говорим, что данное view поддерживает авторизацию токенами для взаимодействия с запросами.

pylibrary/settings.py
...
REST_FRAMEWORK = {

    ...

    'DEFAULT_AUTHENTICATION_CLASSES': [
        # 'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication',
    ]

}
...

Либо вы можете добавить поддержку авторизации токенами глобально в словаре REST_FRAMEWORK.

Вернемся в Postman и проверим.

Как видно теперь все отлично работает.

Давайте также реализуем регистрацию пользователей.

api/serializers.py
...
from django.contrib.auth.models import User


class UserSerializer(ModelSerializer):
    class Meta:
        model = User
        fields = ['username', 'password']
...
api/views.py
...
from rest_framework import viewsets
from rest_framework import views
from django.contrib.auth.models import User
from rest_framework.authtoken.models import Token
...

class RegisterUser(views.APIView):
    def post(self, request):
        serializer = UserSerializer(data=request.data)

        if not serializer.is_valid():
            return Response({'status': 400, 'errors': serializer.errors})
        serializer.save()

        user = User.objects.get(username=serializer.data['username'])
        token = Token.objects.get_or_create(user=user)

        return Response({'status': 200, 'token': str(token)})
...
api/urls.py
...
from django.urls import path

from api.views import *

...
urlpatterns += [
    ...
    path('register/', RegisterUser.as_view())
]

Для этого создадим сериализатор для модели User. Давайте возьмем только поля username и password.

RegisterUser создадим на основе APIView и используем метод post. Сначала будем проверять переданные в сериализатор данные на валидность, в случае успеха сохранять данные, в случае неверных данный будем возвращать 400 статус и текст ошибки. В сценарии, когда данные валидны будем забирать пользователя, ведь к этому моменту он уже создан в таблице User. Исключений быть в такой схеме не может, поэтому проверять ситуацию, при которой в таблице нет такого пользователя не имеет смысла. Токен создадим методом get_or_create(), из блока Django. ORM мы помним, что этот метод возвращает два параметра. Первый - запись из БД, второй - False или True, False, когда мы просто возвратили запись, True, когда создали. И в итоговом результате отдадим токен, который вернуть необходимо в строковом виде и статус 200. Также добавим путь для регистрации в urls.py.

Попробуем зарегистрировать пользователя с помощью этого view. Регистрационные данные передадим в json словаре raw запроса. Как видно регистраиця сработала и метод get_or_create() показал нам, что токен к моменту отрабатывания этой функции уже был в нашей БД. Связано это с сигналом create_auth_token(), который мы писали выше. Если его закомментировать и создать нового пользователя, то в ответ мы получим токен и True.

api/views.py
...
class RegisterUser(views.APIView):
    def post(self, request):

        ...
        token, _ = Token.objects.get_or_create(user=user)
...

Если вы хотите получать только токен, без True и False, то добавьте в RegisterUser в строке создания токена запятую и подчеркивание. Так мы говорим, что второй параметр возвращаемый функцией нужно игнорировать.

С базовыми возможностями аутентификации мы с вами познакомились.

Djoser

Первая библиотека для DRF, о которой мы поговорим - Djoser. Аутентификация механизм, который нужен почти в любом проекте и каждый раз написание его логику кажется занятием, которое нуждается в автоматизации. Библиотека Djoser как раз умеет делать то, что мы писали выше и даже немного больше, но для ее работы не нужно почти ничего кроме установки.

Документация Djoser

Установим Djoser командой

pip install djoser

И добавим в INSTALLED_APPS

INSTALLED_APPS = [
    ...    

    # DRF
    'rest_framework.authtoken',
    'djoser',
]

Помимо djoser, нам нужно оставить таблицу для токенов, предоставляемую drf, всю логику этого механизма мы обсудили выше.

В settings.py у нас уже есть строка поддержки авторизации по токенам.

REST_FRAMEWORK = {
    ...

    'DEFAULT_AUTHENTICATION_CLASSES': [
        ...
        'rest_framework.authentication.TokenAuthentication',
    ]

}

Речь про TokenAuthentication.

И осталось добавить соответствующие пути в urls.py.

api/urls.py
...
from django.urls import path, include
...

urlpatterns += [
    # path('api-token-auth/', views.obtain_auth_token),
    # path('register/', RegisterUser.as_view())
    path('auth/', include('djoser.urls')),
    path('auth/', include('djoser.urls.authtoken')),
]

Пути, оставленные из прошлого раздела мы закомментируем, а вместо них добавим пути для djoser. Первый для авторизации по сессиям, второй для авторизации по токенам.

Давайте перейдем по адресу /auth/.

По адресу у нас открыт список пользователей. Я оставил пагинацию для компактности скриншотов, как вы понимаете это не обязательно. Список пользователей, разумеется без паролей, а снизу post форма для регистрации.

Регистрация djoser уже включает в себя проверку пароля на сложность.

Для обзора возможностей давайте лучше вернемся в Postman.

Все эти возможности описаны в данном разделе.

Адрес /users/ для регистрации пользователей.

Для получения токена используется адрес /token/login, для выхода /token/logout. После выхода через /token/logout токен будет недействителен и потребуется генерация нового токена.

По адресу users/me/ мы можем просматривать данные пользователя и применять к нему методы PUT и PATCH. Таким образом добавим нашему пользователю email, чтобы запрос узнал какого пользователя мы изменяем в Headers нужно добавить токен, как это выглядит мы видели выше. У пользователя теперь есть email.

Но давайте протестируем как работает djoser с настоящими email. Если вы читали блок 'Django. Деплой. Платные хостинги', то вы знаете как подключить почту к проекту, в данном проекте она уже тоже подключена.

pylibrary/settings.py
...
EMAIL_HOST = 'mail.hosting.reg.ru'
EMAIL_HOST_USER = 'pylibrary@pylibrary.site'
EMAIL_HOST_PASSWORD = '(пароль от ящика)'

EMAIL_USE_TLS = True
EMAIL_PORT = 587

DEFAULT_FROM_EMAIL = EMAIL_HOST_USER

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'

...

DJOSER = {
    'PASSWORD_RESET_CONFIRM_URL': '#/password/reset/confirm/{uid}/{token}',
    'USERNAME_RESET_CONFIRM_URL': '#/username/reset/confirm/{uid}/{token}',
    'ACTIVATION_URL': '#/activate/{uid}/{token}',
    'SEND_ACTIVATION_EMAIL': True,
    'SEND_CONFIRMATION_EMAIL': True,
}
...

Для проверки работы почты нам будет достаточно таких настроек. Настройки для djoser взяты из документации, там подробно и понятно написано, что и для чего нужно. Но даже без обращения к документации названия констант говорят сами за себя.

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

Новый пользователь успешно зарегистрирован.

На почту мы получили письмо для подтверждения.

http://127.0.0.1:8000/#/activate/MjE/b9s05u-1587ded25bb04fa1593191071beef06a

В ссылке для нас примечателен
uis - MjE
token - b9s05u-1587ded25bb04fa1593191071beef06a

Воспользуемся ими для активации.

Для этого воспользуемся адресом /users/activation/, куда передадим uid и token из письма, правда в ответ запрос ничего не возвращает. Убедиться в том, что активация прошла успешно можно отправить запрос под данными пользователя на /token/login, в ответ мы должны получить auth_token, который будет отличаться от токена из письма.

Думаю мы с вами теперь хорошо понимаем как работать с djoser адресами и как передавать в них данные. Оставшиеся методы рассматривать не станем, все они описаны в документации, принцип мы разобрали, меняются в них только отправляемые данные и метод запроса.

Simple JWT

Еще одна возможность авторизации - авторизация по JWT (JSON Web Token) токенам. Для того чтобы понять в чем отличие JWT от обычных токенов и где они могут понадобиться перейдем сразу к практике.

Начать стоит с установки библиотеки Simple JWT, будем использовать ее поскольку она наиболее популярная и часто используется в связке с djoser. В документации djoser, как вы возможно заметили, даже есть отдельный раздел посвященный библиотеке Simple JWT.

Документация Simple JWT

Установка командой

pip install djangorestframework-simplejwt

Добавим приложение в установленные

INSTALLED_APPS = [
    ...

    # DRF
    'rest_framework.authtoken',
    'djoser',
    'rest_framework_simplejwt',
]

Добавим способ авторизации по JWT токенам в settings.py.

REST_FRAMEWORK = {
    ...

    'DEFAULT_AUTHENTICATION_CLASSES': [
        ...
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ],

}

И добавим соответствующие пути в urls.py

api/urls.py
...
from django.urls import path
...
from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
)


...
urlpatterns += [
    ...
    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]

Это еще не все предварительные настройки, но на данный момент давайте перейдем по добавленному адресу api/token/.

По адресу api/token/ мы получаем форму для генерации JWT токена. Попробуем это сделать.

Авторизуемся под данными существующего пользователя. В ответ мы получаем два токена. refresh и access.

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

Вспомним Yandex и Google и их сервисы. У каждой из компаний есть множество независимых сервисов и в каждый из них мы можем попасть, предварительно авторизовавшись только в одном. Например, мы авторизовались в Яндекс почте и в Яндекс Директ и РСЯ мы теперь мы можем зайти, не вводя авторизационные данные. Тут как раз и работает идея JWT токенов. Так как это разные сервисы, хоть и от одной компании, обычный токены нам не подойдут, с обычными токенами мы бы все равно должны были каждый раз авторизовываться в каждом из сервисов отдельно. Кратко говоря это именно то, для чего нам и нужны JWT токены (вернее будет сказать самое популярное их применение) - возможность авторизации в независимые сервисы под одними авторизационными данными, без необходимости ввода этих данным в каждом отдельно сервисе.

Теперь что касается токенов. Продолжим имитировать ситуация с яндексом. Когда мы авторизовались в Яндекс почте произошел запрос к БД пользователей, случилась проверка, что такой пользователь действительно есть, и в ответ нам прислали эти самые два токена. Теперь мы идем в Яндекс директ и РСЯ, в обоих случаях вместо данным для входа мы отправляем access токен, в случае валидности токена мы успешно входим. Для защиты данных access токен имеет очень короткий период жизни и для повторного получения используется уже refresh токен, время жизни, которого больше.

С идеей более менее разобрались, но тут возникает вопрос: почему сервисы пускают нас по этому access токену? Для ответа на вопрос нужно разобраться как устроен этот токен.

access токен хранит в себе закодированные данные о пользователе, убедиться в этом можно, если воспользоваться каким-нибудь сервисом для декодирования JWT токенов. Например, JSON Web Tokens.

Перейдем на сервис и вставим в него наш access токен.

Токен разделен точками на 3 части. Первая - алгоритм шифрования и тип токена. Вторая - данные о пользователе, которому принадлежит данный токен. Третья - объединенные первая и вторая часть и зашифрованные алгоритмом из первой части.

Поэтому сервисы, в которых мы зарегистрированы под такими же данными того сервиса, от которого мы получили эти токены, впускают нас без запроса дополнительный информации. Потому что PAYLOAD уже хранит в себе данные о пользователе.

Теперь вернемся в settings.py и напишем остальные настройки.

pylibrary/settings.py
...
from datetime import timedelta

...
SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
    'ROTATE_REFRESH_TOKENS': False,
    'BLACKLIST_AFTER_ROTATION': False,
    'UPDATE_LAST_LOGIN': False,

    'ALGORITHM': 'HS256',
    'SIGNING_KEY': SECRET_KEY,
    'VERIFYING_KEY': None,
    'AUDIENCE': None,
    'ISSUER': None,
    'JWK_URL': None,
    'LEEWAY': 0,

    'AUTH_HEADER_TYPES': ('Bearer',),
    'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
    'USER_ID_FIELD': 'id',
    'USER_ID_CLAIM': 'user_id',
    'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule',

    'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
    'TOKEN_TYPE_CLAIM': 'token_type',
    'TOKEN_USER_CLASS': 'rest_framework_simplejwt.models.TokenUser',

    'JTI_CLAIM': 'jti',

    'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
    'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
    'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
}
...

Настройки полностью взяты из документации, давайте посмотрим, за что они отвечают и что мы можем изменять.

ACCESS_TOKEN_LIFETIME и REFRESH_TOKEN_LIFETIME время жизни access и refresh токена соответственно. Как упоминалось выше время жизни access значительно ниже, чем refresh.

Следующие настройки связаны с блэклистом. Для его работы нужно в INSTALLED_APPS добавить 'rest_framework_simplejwt.token_blacklist',. Кратко говоря refresh токены после отработки будут попадать в черный список, очередная настройка безопасности. ROTATE_REFRESH_TOKENS при этом может работать и без token_blacklist, в значении True для обновления токена запрашивает старый и в случае предоставления старого возвращает новый, тоже настройка безопасности. UPDATE_LAST_LOGIN в значении True обновляет поле last_login в таблице auth_user, разработчики предупреждают, что в True эта настройка может значительно увеличить нагрузку на БД.

Следующие 6 настроек связаны с настройкой 3 части access токена, опять же дополнительная безопасность.

AUTH_HEADER_TYPES - слово, которое мы будем использовать перед токеном при отправке заголовка Authorization. Если привычнее Token, то можно сразу заменить Bearer на Token.
AUTH_HEADER_NAME как раз говорит какое имя заголовка авторизации будет использоваться, по умолчанию именно Authorization. Следующие 2 настройки какое поле будет использоваться для связи с пользователем, по этому полю нас будут узнавать сервисы куда мы входим. USER_AUTHENTICATION_RULE содержит информацию каким пользователям доступно прохождение авторизации.

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

Давайте теперь перейдем в Postman и попробуем получить список авторов, используя JWT токен. Как вы помните доступ к просмотру авторов есть только у авторизованных пользователей.

Сделаем это так. Вместо Token теперь Bearer и через пробел access токен. Но как видно из ответа, по данному access токену данные нам возвращать не хотят. Связано это с временем жизни токена, 5 минут уже прошло.

Для получения нового access токена воспользуемся refresh токеном. Делается по второму добавленному в urls.py адресу api/token/refresh/. С запросом надо передать JSON строку, ключ refresh значение сам токен. В ответ получаем новый access токен.

И по новому access токену мы уже получаем интересующую нас информацию.

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

Это не все существующие библиотеки для авторизации, но скорее всего этих двух вам будет достаточно для всех, или почти всех, ваших задач.

django-filter

В DRF есть встроенная поддержка DjangoFilterBackend, но гибкую настройку данного класса предоставляет дополнительная библиотека - django-filter.

Установим библиотеку командой

pip install django-filter

И добавим ее в INSTALLED_APPS

INSTALLED_APPS = [
    ...

    # DRF
    'rest_framework.authtoken',
    'djoser',
    'rest_framework_simplejwt',
    'django_filters',
]

Теперь мы можем подключить возможность фильтрации глобально через settings.py.

pylibrary/settings.py
...
REST_FRAMEWORK = {
    ...

    'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']

}
...

Делается в переменной DEFAULT_FILTER_BACKENDS словаря REST_FRAMEWORK.

api/views.py
...
from django_filters.rest_framework import DjangoFilterBackend.
...

class BookViewSet(viewsets.ModelViewSet):
    ...
    filter_backends = [DjangoFilterBackend]
...

Либо мы можем добавить фильтрацию выборочно для какого-нибудь view. Для этого существует переменная filter_backends.

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

api/views.py
...
from django_filters.rest_framework import DjangoFilterBackend
...

class BookViewSet(viewsets.ModelViewSet):
    ...
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['category', 'year']
...

Добавим в переменную filterset_fields какие-нибудь поля, по которым хотим фильтровать записи.

Теперь по адресу данного view появилась кнопка Фильтры.

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

Как видно фильтрация в DRF реализуется достаточно просто.

Помимо BaseFilterBackend есть еще два класса фильтрации.

Первый SearchFilter.

api/views.py
...
from rest_framework import filters
...

class BookViewSet(viewsets.ModelViewSet):
    ...
    filter_backends = [filters.BaseFilterBackend]
    search_fields = ['category__name', 'year']
...

Добавляется он из rest_framework.filters, для реализации также используется переменная filter_backends, а вместо filterset_fields используется search_fields. Обратите внимание мы можем настраивать фильтрацию по полям связанным моделей.

Выглядит он так. Кнопка Фильтры никуда не пропадает, но вместо выбора по заданным полям мы получаем строку поиска. Введенные данные ищутся только по полям из переменной search_fields. В ответ на фильтр Программирование мы получили 5 страниц книг, вместо 13.

Второй класс фильтрации - OrderingFilter.

api/views.py
...
from rest_framework import filters
...

class BookViewSet(viewsets.ModelViewSet):
    ...
    filter_backends = [filters.OrderingFilter]
    ordering_fields = ['category__name', 'year']
...

Настраивается также, как и SearchFilter, но вместо search_fields используется ordering_fields.

Выглядит он следующим образом.

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

drf-yasg

Возможно вы обращали внимание, что некоторые сайты с открытым api имеют документацию по работе с данным api. Вы можете писать документацию самостоятельно, а можете воспользоваться помощником в виде библиотеки - drf-yasg .

Давайте посмотрим какую документацию сгенерирует эта библиотека для нашего проекта.

Установка

pip install drf-yasg

INSTALLED_APPS = [
    ...

    # DRF
    'rest_framework.authtoken',
    'djoser',
    'rest_framework_simplejwt',
    'django_filters',
    'drf_yasg',
]
api/urls.py
...
from django.urls import path, include, re_path
...
from rest_framework import permissions
from drf_yasg.views import get_schema_view
from drf_yasg import openapi

schema_view = get_schema_view(
   openapi.Info(
      title="Pylibrary API",
      default_version='v1',
      description="documentation for pylibrary API",
      terms_of_service="https://www.google.com/policies/terms/",
      contact=openapi.Contact(email="contact@snippets.local"),
      license=openapi.License(name="BSD License"),
   ),
   public=True,
   permission_classes=[permissions.AllowAny],
)


...
urlpatterns += [
    ...
    re_path(r'^swagger(?P<format>\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'),
    re_path(r'^swagger/$', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
    re_path(r'^redoc/$', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
]

Содержимое для urls.py заберем из документации. Только немного изменим schema_view. В title напишем название нашего проекта и немного изменим description.

Нам доступны два вида документации. Первая по адресу /redoc/. Здесь мы можем посмотреть доступные api запросы. Оформлено все достаточно приятно.

По адресу /swagger/ мы получаем документацию следующего формата. Тут мы можем протестировать запросы. Для этого сначала нужно разрешить изменения, правый верхний угол, затем передать какой-нибудь параметр, данный адрес для детализации автора и он требует id, поэтому передадим id, кнопка Execute для отправки запроса. В ответ получаем 403 статус, все работает корректно.

Таким образом можно реализовать автодокументирование вашего api.

drf-spectacular

Еще одна библиотека для автодокументирования api - drf-spectacular. Разница в том, что drf-spectacular поддерживает OpenAPI 3.0, в то время как drf-yasg только OpenAPI 3.0.

Документации у этой библиотеки нет, есть репозиторий, там все достаточно понятно.

Установка

pip install drf-spectacular

INSTALLED_APPS = [
    ...

    # DRF
    'rest_framework.authtoken',
    'djoser',
    'rest_framework_simplejwt',
    'django_filters',
    'drf_yasg',
    'drf_spectacular'
]
pylibrary/settings.py
...
REST_FRAMEWORK = {
    ...
    'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',

}

SPECTACULAR_SETTINGS = {
    'TITLE': 'Pylibrary API',
    'DESCRIPTION': 'Pylibrary API documentation',
    'VERSION': '1.0.0',
    'SERVE_INCLUDE_SCHEMA': False,
    # OTHER SETTINGS
}
...

Для работы библиотеки в REST_FRAMEWORK нужно добавить DEFAULT_SCHEMA_CLASS и словарь SPECTACULAR_SETTINGS с описанием документации.

api/urls.py
...
from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView

...
urlpatterns += [
    ...
    # YOUR PATTERNS
    path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
    # Optional UI:
    path('api/schema/swagger-ui/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
    path('api/schema/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'),
]

И пути для того же swagger и redoc. Теперь по соответствующим адресам вы увидите туже документацию, что мы видели при использовании drf-yasg.

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


Материал будет дополняться...
(скорее всего)

Для отправки комментария необходимо авторизоваться



Комментарии

Здесь пока ничего нет...