Обновить
8K+
6
Максим Горбань@max31ru12

Python Developer

5
Рейтинг
12
Подписчики
Хабр КарьераХабр Карьера
Отправить сообщение

Спасибо за комментарий!

В теории, можно сделать repository_registry внутри TransactionManager, и вызывать метод get_repository("users_repository"), но я намеренно избегаю этого, пока позволяют размеры проекта, так как это лишит меня удобной типизации в угоду архитектурной правильности, которая сейчас никак не раскрывается.

У меня в функционале юз кейса используются сервисы, сервисы использую репозитории, а открытие транзакции перед выполнением юз кейса считаю не всегда удачным решением, потому что:

  1. Тогда логику открытия транзакции придется писать в эндпоинте, middleware или декораторе

  2. Если выносить открытие из эндпоинта, то это лишний слой абстракции, без которого вполне можно обойтись

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

Скорее всего, вы в первый раз вы видите такую реализацию, так как я ее не тупо скопировал у кого-то другого. Я пришел к этому почти самостоятельно. Комментарии позволяют мне посмотреть на свои решения с разных сторон, это важно для меня)

Нарушение OCP все же есть, вы правы. Но TransactionManager выступает еще и как такой composition object, который собирает репозитории, поэтому тут это не особо критично. Можно сделать код лучше с точки зрения OCP, но хуже с точки зрения типизации и явных контрактов. Наверное этот тот случай, когда я нарушаю его намеренно ради простоты и удобства, а то получится как в Django с его непонятными строками, обозначающими поля моделей:

class UserSerializer(serializers.ModelSerializer):
    full_name = serializers.CharField(
        source="profile.personal_data.full_name",
        read_only=True,
    )

    company_name = serializers.CharField(
        source="profile.company.name",
        read_only=True,
    )

    manager_email = serializers.EmailField(
        source="profile.company.manager.email",
        read_only=True,
    )

    class Meta:
        model = User
        fields = [
            "id",
            "email",
            "username",
            "full_name",
            "company_name",
            "manager_email",
        ]

PS. С DI контейнерами я все же поразбираюсь, вдруг поможет сделать мой подход лучше)

Спасибо за комментарий!

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

Нарушения OCP не вижу. Добавил табличку - добавил новый репозиторий и новый property. Было бы здорово аргументировать вашу позицию чуть более подробно.

Насчет DI контейнера очень крутая мысль, обязательно посмотрю. Может быть есть какие-то примеры/статьи?

С некоторыми вещами это действительно криво работает, например, с FastAPI. Однако, я уже около года пользуюсь удаленным интерпретатором, работая с джанго. Да это медленно, да это кривовато, но на тяжеловесном приложении, где есть 8-10 контейнеров, легче запустить джангу через удаленный интерпретатор, чем танцевать с миллионом переменных окружения, которые нужно прокинуть в контейнер, чтобы там где надо подставилось название сервиса.

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

Насчет jetbrains согласен, их решения не помогают. Например, python3.13 + удаленный интерпретатор + Pycharm версии 22.1.3 вообще не стакаются вместе. Но если обновить pycharm, то, скорее всего, все взлетит (я не пробовал).

Спасибо за очень конструктивный и полезный комментарий!

Ну вот когда понадобится, тогда и разберусь, и 15 минут выделю. Но за последние 4 года еще ни разу не появилось необходимости :)

Нет, вам достаточно прокинуть папку с кодом в волюмы и использовать везде одно и тоже. Вероятно, единственное, что будет отличаться адрес подключения к БД. Если запускаете приложение через докер, то надо вместо localhost писать имя сервиса. Это можно гибко настроить, но сейчас не смогу привести пример.

Приложения можно и нужно запускать локально, если так быстрее и легче дебажить. Однако Pycharm предоставляет возможность использовать удаленный интерпретатор, что в определённых ситуациях бывает даже удобнее (но вряд ли быстрее). Насчет менеджеров сред точно сказать не могу, я просто ими не пользуюсь.

Не сравнивал, да и вообще не знал, что проблемы с производительностью musl libc. Спасибо за информацию, теперь стало интересно разобраться)

loguru - потому что простой и все идет из коробки, не надо больше писать кучу бойлерплейт кода как в logging
uv буду обязательно пробовать в будущем, пока что времени совсем мало(

Если автор собирается писать еще статьи, то хочу попросить использовать подсветку кода, хабр это позволяет. Так код легче воспринимается, особенно когда он представлен в таком количестве

Чтобы эндпоинты показывались как защищенные, можно заменить get_access_token на

cookie_scheme = APIKeyCookie(name="users_access_token", auto_error=True)

А затем прокинуть как зависимость в get_current_user:

token: str = Depends(cookie_scheme),

Очень крутая статья по аутентификации. Было бы славно подробнее раскрыть различные способы передачи токена, например, Bearer. Есть ли простой способ сделать эндпоинты защищенными в swagger'е с использованием вашего метода аутентификации?

Потому волюм в docker-compose и так прокидывает эту директорию в контейнер. Docker compose не всегда нужен только для локальной разработки. Например, с его помощью можно накатывать ПО для заказчиков и не возиться с особенностями установленной у него операционки

Спасибо за конструктивный комментарий, постараюсь так же конструктивно ответить.

Я не упоминал о существующих шаблонах, по нескольким причинам:
1) Я с ними слабо знаком и не использую их (хотя я понимаю, что стоит ознакомиться)
2) Они часто имеют избыточный функционал (как пример, cookiecutter содержит в себе фронт на Vue, а зачем он мне нужен, если я пишу простые CRUD'ы просто чтобы попрактиковаться)

Насчет гибкости и масштабируемости хотел бы узнать более подробно. Почему моя структура плоха? Не могли бы еще привести пример проблем моего подхода с разрастанием кодовой базы? Это позволило бы мне пересмотреть свой подход и улучшить его.

Спасибо, посмотрю!

Потому что это самый простой вариант. Его всегда можно легко и просто заменить на pyproject.toml, при желании

Пока не пробовал. Наверное, стоит попробовать. Есть либа, которая позволяет маппить модели sqlalchemy в модели pydantic. Называется pydantic-marshals, тоже довольно удобная вещь

В зависимости от того, что вам нужно. Шаблон Cookiecutter для fastapi содержит в себе очень много всего, не всегда столько нужно для простого api. Да и кажется его больше не поддерживают (последнее обновление 4 года назад)

Да, вы абсолютно правы, эту строчку можно вообще убрать из докер-файла

1

Информация

В рейтинге
1 336-й
Откуда
Москва, Москва и Московская обл., Россия
Дата рождения
Зарегистрирован
Активность

Специализация

Фулстек разработчик, Веб-разработчик
Средний
От 200 000 ₽
Python
Git
PostgreSQL
Базы данных
Django
SQLalchemy
Linux
FastAPI