Найти в Дзене
Computer Pro

FasAPI + Postgres + Alembic + линтеры и тесты. GitHub actions (CI). Часть 2

Дисклеймер. Это не руководство к действию, не список рецептов, а что-то типа лайв-коддинга. Тут могут быть ошибки! Я просто пытаюсь упорядочить свои действия и размышления. Так что - "ПОНЯТЬ И ПРОСТИТЬ" Продолжаем разработку приложения по управлению кулинарными рецептами. Начало публикации тут: Казалось бы, базовую модель можно было создать написанием двух-трех строчек кода типа: Но программист должен упростить себе работу в будущем, чтобы писать меньше строчек кода потом, мы напишем больше кода сейчас. Вот моя базовая модель: Вот код конвертера из CamelCase в snake_case: Ну ладно, базовая модель написана, можно приступать к остальным. В данном приложении за основу берется ингредиент, из которых составляются рецепты. То есть одна таблица - ingredients, вторая - recipes и третья - "ингредиенты в рецепте" (ingredients_in_recipes). Все модели расписаны по отдельным файлам, чтобы alembic смог увидеть эти файлы их нужно объявить в файле src/core/models/__init__.py следующим кодом: Но
Оглавление
Фоточка для карточки публикации, не несет никакой смысловой нагрузки. Фото моё.
Фоточка для карточки публикации, не несет никакой смысловой нагрузки. Фото моё.

Дисклеймер. Это не руководство к действию, не список рецептов, а что-то типа лайв-коддинга. Тут могут быть ошибки! Я просто пытаюсь упорядочить свои действия и размышления.
Так что - "ПОНЯТЬ И ПРОСТИТЬ"

Продолжаем разработку приложения по управлению кулинарными рецептами. Начало публикации тут:

Базовая модель (class Base)

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

-2

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

-3

Вот код конвертера из CamelCase в snake_case:

Строчки 2 - 11 можно не писать... Это что-то типа документации
Строчки 2 - 11 можно не писать... Это что-то типа документации

Ну ладно, базовая модель написана, можно приступать к остальным. В данном приложении за основу берется ингредиент, из которых составляются рецепты. То есть одна таблица - ingredients, вторая - recipes и третья - "ингредиенты в рецепте" (ingredients_in_recipes).

class Ingredient

-5

class Recipe

-6

class IngredientsInRecipe

-7

Все модели расписаны по отдельным файлам, чтобы alembic смог увидеть эти файлы их нужно объявить в файле src/core/models/__init__.py следующим кодом:

-8

Но тут есть один нюанс - объявляя здесь все эти классы, у нас получается циклический импорт (ОШИБКА) и мы не можем импортировать при аннотации типов (Mapped[List["Ingredient"]]). Чтобы решить эту проблему - вставляется вот такая конструкция, которая проверяет является ли вызываемый импорт аннотацией.

-9

Создаем миграцию:

alembic revision --autogenerate --message="создание таблиц"

Первую миграцию я немного подправил, т.к. поля id таблиц ingredients и recipes располагались не в начале таблицы, а где-то посерёдке... Некрасиво!

-10

И после чего накатил миграцию:

alembic upgrade head

И проверить создание таблиц в DBEaver. Таблицы созданы!

-11

После создания таблиц можно смело приступать к обработке эндпойнтов. В моём проекте будет три эндпойнта:

  • / Main page (просто заглушка с сообщением "Main page")
  • /ingredients (GET, POST) вывести все ингредиенты, добавить новые ингредиенты
  • /recipes (GET, POST) показать все рецепты, добавить новые рецепты, показать детальную информацию рецепта, по его {recipe_id}

В Swagger это выглядит вот так (скриншот уже работающего приложения):

-12

Все события будут происходить в файле main.py и директории src/api. Объявлю роуты в main.py:

-13

После чего в директории api создаём два файла ingredient.py и recipe.py:

-14
-15
-16

В этих файлах происходит получение асинхронной сессии из db_helper, а в случаях и с POST запросами - получение данных из формы в формате JSON и передача этих данных и сессии в асинхронные функции обработки данных, в директории src/api/crud.

Да, здесь было использованы pydatic schemas для правильного отображения полей в документации. Схемы расположены в src/core/schemas/

-17
-18

Я долгое время не понимал как работает валидация в pydantic, да и сейчас не уверенно им пользуюсь. Но когда сделаешь схему валидации, то в документации оно начинает выглядеть вот так:

-19

Чтобы фронтенд-разработчику было понятно, какие данные ожидать, при каждом запросе.

Отправляемся в директорию api/crud и создадим там два файла (recipe.py и ingredient.py) по обработке запросов к базе данных:

recipe.py тут будет три функции (получение всех рецептов, получение детальной информации по рецепту по его recipe_id и добавление нового рецепта)

-20
-21
-22

ingredient.py Тут будет две функции - получение списка ингредиентов и создание нового ингредиента:

-23

И вот, если я не ошибаюсь, приложение "Кулинарные рецепты" минимально готово и можно делать GET и POST запросы и получать ответы.

Начну с того, что открою http://127.0.0.1:800/docs и добавлю несколько ингредиентов для создания рецепта (честно стырен с дзена, канал - Анастасия Вишняк):

-24
-25
-26

Сформируется вот такой запрос:

-27

На что будет получен ответ:

-28
Как это будет выглядеть в DBEaver
Как это будет выглядеть в DBEaver

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

-30

Ну и теперь самое время получить детальное описание данного рецепта, со всеми ингредиентами и их количеством:

-31

И получаем ответ:

-32

Ну вот, очередной кусочек проекта подошел к концу. Теперь нужно пройтись по коду линтерами по порядку: (mypy, black, isort, flake8). Но это уже в следующей части публикации.

Кто прочитал - молодец, лайкосик не забудьте ткнуть! В комментах отвечу на вопросы, если они будут.

UPD: Продолжение