Search
Write a publication
Pull to refresh

Comments 13

Дишка прикольная, еще бы для джанги сделать адаптеры (не ругайте :))

Сейчас у нас концепция такая, что новые интеграции будут побликоваться сообществом как отдельные проекты. Это позволят отвязать релизные циклы.

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

Спасибо за пример, можно взять на вооружение, но в dependency-injector реализованы достаточно упрощенные сценарии. Например, тут юзается напрямую класс контейнера. Мы такое не хотим - мы хотим больше гибкости. Дальше, container.wire манкипатчит модули, это тоже не круто и регулярно стреляет. Вариант с инициализацией контейнера в ready мы рассматривали, наверно это хороший вариант. Ещё вопрос в какой момент происходит финализация ресурсов, созданных на скоуп запроса, имеем ли мы к ним доступ из middleware или внутри скоупа только сама вьюха?

Можно сделать AppConfig для скоупа приложения и в нем запустить враппер для контерера, в при остановке процесса - закрыть контейнер

# app.py
import atexit

from django.apps import AppConfig
from .container import dj_container

class DjDishkaConfig(AppConfig):
    name = 'dj_dishka'

    def ready(self):
        dishka_container = dj_container.make()
        atexit.register(dishka_container.close)
# container.py

class DjContainer:
    def __init__(self):
        self.__providers: list[Provider] = []
        self.__created = False

    def add_provider(self, provider: Provider):
        if self.__created:
            raise RuntimeError('Container already created')
        self.__providers.append(provider)

    def make(self):
        return make_container(*self.__providers)

dj_container = DjContainer()

dj_container можно наполнять провайдерами из других приложений (в терминах джанги) типа:

from dj_dishka.container import dj_container

service_provider = Provider(scope=Scope.REQUEST)
service_provider.provide(Service)

dj_container.add_provider(service_provider)

А дальше можно сделать как в у вас в FastApi (на этапе рефлексии и замены параметров как раз использовать dj_container):

@inject
def index(request: HttpRequest, service: FromDishka[Service]) -> HttpResponse:
  ....

Пока грязно, но как-то так...


И наконец-то джангисты заживут настоящим энтерпрайзом!

Привет, я автор dishka, aioinject выглядит достаточно интересно, много знакомых штук реализовано. Но если dishka проектировался с учетом потенциального бесконечного масштабирования приложения (несколько контейнеров для модульного монолита, вложенные скоупы для вебсокетов, изолированные компоненты для переиспользования подграфа зависимостей в разных местах), то использование в autoinject ContextVar выглядит неоправданным и может помешать этому.

Может туплю, но не совсем понял как в dishka сделать разные конфиги одного и того же instance в разные provide. У меня вот есть функция get_task_client, и вот не совсем понимаю как мне передать в `get_task_storage` таск клиент с одной бд, а в `get_skill_storage` с другой.
@Tishka17

class SkillRouterProvider(Provider):
    scope = Scope.APP

    @provide
    async def get_task_client(self) -> StorageClient:
        if settings.redis_host and settings.redis_port:
            return RedisStorageClient(
                host=settings.redis_host,
                port=settings.redis_port,
                db=settings.redis_db
            )
        logger.warning(msg="Redis storage is not configured, in-memory cache storage will be used.")
        return DefaultStorageClient()

    @provide
    async def get_task_storage(self, client: StorageClient) -> TaskStorage:  # type: ignore[return-value]
        return TaskStorage(client=client) # здесь хочу db=settings.redis_task_db

    @provide
    async def get_skill_storage(self, client: StorageClient) -> SkillStorage:  # type: ignore[return-value]
        return SkillStorage(client=client) # здесь хочу db=settings.redis_skill_db


Насколько я понял вопрос нужно создать два инстанса StorageClient.

Дишка позволяет это делать с помощью NewType

Такие типы дишка будет считать разными и как следствие выдавать разные инстансы

TaskStorage = typing.NewType("TaskStorage", StorageClient)

SkillStorage = typing.NewType("SkillStorage", StorageClient)

И дальше уже использовать эти типы в провайдерах в качестве получаемых аргументов и возвращаемых значений

Дишка различает объекты либо по типу либо по компоненту. Пока это один тип и юзается в одном месте, проще сделать NewType. Если же это целый набор типов, которые нужны разных местах - тут есть компоненты https://dishka.readthedocs.io/en/stable/advanced/components.html

Привет, это команда GitVerse! Рады видеть тебя в числе участников сезона open source! Ставим лайк твоей статье :)

Sign up to leave a comment.

Articles