RESTful Backend с информацией о прохождении Metal Gear Solid V: Phantom Pain.
Проект содержит интересные твики (миксины, декораторы, ...), поддержку локализации и т.д.
Для проверки кода на соответствие PEP-8
используется модуль pycodestyle
, для форматирования модуль black
, таким образом весь код автоматически форматируется в соответствии со стандартами и рекомендациями при сохранении файла.
- Склонируйте репозиторий
- Создайте и активируйте виртуальное окружение Python
- Установите зависимости из
requirements.txt
pip install -r requirements.txt
- Проведите миграции
python manage.py makemigrations
python manage.py migrate
- Создайте суперпользователя
python manage.py createsuperuser
Для запуска Production версии проекта необходимо в переменной окружения DJANGO_SETTINGS_MODULE
прописать путь к файлу production_settings.py
, а в перменной окружения SECRET_KEY
секретный ключ для приложения:
Windows (CMD):
set DJANGO_SETTINGS_MODULE=mgsv_backend.production_settings
set SECRET_KEY="SOME-SUPER-SECRET-K3Y"
Windows (PowerShell):
$env:DJANGO_SETTINGS_MODULE="mgsv_backend.production_settings"
$env:SECRET_KEY="SOME-SUPER-SECRET-K3Y"
Unix:
export DJANGO_SETTINGS_MODULE=mgsv_backend.production_settings
export SECRET_KEY="SOME-SUPER-SECRET-K3Y"
Но желательно это делать при помощи Docker, собирая образы с предустановленными переменными окружения (можно брать из секретных ключей GitHub).
Не забываем с помощью collectstatic
собирать необходимые статические файлы и хостить их через nginx
.
Круто бы использовать CI/CD на GitHub для процесса валидации, сборки и развертывания всего приложения.
Основным приложением является missions
, оно содержит в себе следующие модели и соотвтствующие сериализаторы для DRF:
Модель | Сериализатор | Описание |
---|---|---|
Character |
CharacterSerializer |
Персонаж, в основном используется для диалогов |
Mission |
MissionSerializer |
Задание (или же Эпизод), имеет внешние связи с Objective , Fact и Dialog . |
Objective |
ObjectiveSerializer |
Цель задания, имеет внешние связи с ObjectiveImage |
ObjectiveImage |
ObjectiveImageSerializer |
Изображение, изображающее цель задания (Objective ) |
Fact |
FactSerializer |
Факт из задания, де-юре и де-факто просто текст с интересной информацией |
Dialog |
DialogSerializer |
Диалог, происходящий в задании. Описывает реплики, произносимые различными персонажами в ходе выполнения задания. |
Из-за определённой цепочки зависимостей одних сериализаторов от других, в serializers.py
они записаны в противоположном от Моделей в models.py
порядке.
Сериализаторы с постфиксом Extended
в данном проекте по-сути выступают развёрнутой версией PrimaryKeyRelatedField
. В то время, как последний отображает только главный ключ pk
(читайте id
в 99% случаев), КакойЛибоСериализаторExtended
отображает развернутую информацию о модели.
Пример:
class ObjectiveSerializer(serializers.ModelSerializer):
images = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = Objective
fields = "__all__"
Из-за `PrimaryKeyRelatedField` сериализатор будет отображать в `images` лишь поле `id` изображений.
{
"id": 5,
"images": [
4,
5,
6
],
"title": "Эвакуирован пленник, неспособный говорить.",
"text": "Пленника, неспособного говорить зовут Хамид. ...",
"type": 1,
"mission": 1
}
class ObjectiveImageSerializer(serializers.ModelSerializer):
class Meta:
model = ObjectiveImage
fields = "__all__"
class ObjectiveSerializerExtended(ObjectiveSerializer):
images = ObjectiveImageSerializer(many=True, read_only=True)
В то время, как `ObjectiveSerializerExtended`, унаследованный от `ObjectiveSerializer` (просто чтобы заново не прописывать класс `Meta`), в качестве поля `images` использует `ObjectiveImageSerializer`, поведение которого возвращает всю информацию об изображении цели, которая и будет выводиться при сериализации.
{
"id": 5,
"images": [
{
"id": 4,
"image": "http://127.0.0.1:8000/media/objectives/Mission_6_punkt_4a.jpg",
"objective": 5
},
{
"id": 5,
"image": "http://127.0.0.1:8000/media/objectives/Mission_6_punkt_4b.jpg",
"objective": 5
},
{
"id": 6,
"image": "http://127.0.0.1:8000/media/objectives/Mission_6_punkt_4c.jpg",
"objective": 5
}
],
"title": "Эвакуирован пленник, неспособный говорить.",
"text": "Пленника, неспособного говорить зовут Хамид. ...",
"type": 1,
"mission": 1
}
Данный подход используется во
ViewSet
для использования параметраextended=1
в URL. При отсутствииextended
илиextended=0
, отображается краткая информация о моделях, приextended=1
и установленномextended_serializer_class
в нужномViewSet
, отображается развернутая информация.
Пример:
# ExtendedViewSetMixin - миксин, отвечающий за работу `extended`, добавляется первым в нужные ViewSet'ы (подробнее см. по ссылке ниже)
class ObjectiveViewSet(ExtendedViewSetMixin, viewsets.ModelViewSet):
serializer_class = ObjectiveSerializer # Данный сериализатор будет использоваться по умолчанию (без extended или extended=0)
extended_serializer_class = ObjectiveSerializerExtended # Данный сериализатор будет использоваться для extended=1
queryset = Objective.objects.all()
Ссылка: viewsets.py
По умолчанию DRF позволяет производить выборку во ViewSet
лишь по одному полю (по умолчанию это pk
), миксин MultipleFieldLookupMixin
предзанзачен для расширения данного поведения и позволяет использовать сразу несколько полей в выборке.
Пример:
class MissionViewSet(
ExtendedViewSetMixin, MultipleFieldLookupMixin, viewsets.ModelViewSet
):
serializer_class = MissionSerializer
extended_serializer_class = MissionSerializerExtended
queryset = Mission.objects.all()
lookup_fields = ("pk", "slug")
Это позволит производить следующий поиск:
- http://127.0.0.1:8000/api/v1/missions/1/ - поиск по
pk
- http://127.0.0.1:8000/api/v1/missions/0-6-gde-prjachetsja-zhalo/ - поиск по
slug
Подробнее: viewsets.py
Проект поддерживает локализацию на различные языки. Обернув необходимый текст в gettext_lazy
(или pgettext_lazy
для задания контекста), можно сгенерировать файл для локализации:
- Переходим в папку с нужным приложением (не рекомендуется это делать в корне всего проекта, иначе захватит локализацию со всех модулей, что будет крайне сложно контролироваьт)
cd missions
- Собираем все строки и генерируем файл локализации, в данном примере для русского языка
django-admin makemessages -l ru
- После обнаруживаем в папке
missions
следующую структуру файлов:locale/ru/LC_MESSAGES/django.po
, в папкеlocale
хранятся все локализации, в данном случае дочерняя папкаru
отвечает за локализацию на русский язык. В файлеdjango.po
можно обнаружить подобные строки:
#: .\models.py:11
msgid "Subsistence"
msgstr ""
#: .\models.py:12
msgid "Stealth"
msgstr ""
В msgid
указывается текс, обёрнутый ранее в gettext_lazy
(или иную функцию), это и текст, который будет отображаться при отсутствии перевода и текст, который будет переводиться при наличии перевода в msgstr
.
Пример:
msgid "Stealth"
msgstr "Скрытно"
Все вхождения gettext_lazy("Stealth")
в данном приложении будут заменены на Скрытно
при активации русской локали в проекте или у пользователя.
После перевода нужных строк их необходимо скомпилировать из формата .po
в .mo
, после чего перевод может быть использован в приложении и проекте:
django-admin compilemessages
Теперь, при смене LANGUAGE_CODE
в настройках проекта на "ru"
или "ru-ru"
, при наличии соответствующих скомпилированных файлов локализации, указанные строки будут переведены на русский язык, остальные же строки просто остануться на английском.