From 2912ff1c4976dda7a637bea6c9b1c12ca5ccd1c2 Mon Sep 17 00:00:00 2001 From: nullmatawasoradesu <119107850+wakaree@users.noreply.github.com> Date: Tue, 8 Aug 2023 20:01:47 +0300 Subject: [PATCH] Added a section on Dependency Injection technology in documentation --- docs/dispatcher/dependency_injection.rst | 56 ++++++++++++++++++++++++ examples/context_addition_from_filter.py | 33 ++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 docs/dispatcher/dependency_injection.rst create mode 100644 examples/context_addition_from_filter.py diff --git a/docs/dispatcher/dependency_injection.rst b/docs/dispatcher/dependency_injection.rst new file mode 100644 index 00000000..5f0e4b2f --- /dev/null +++ b/docs/dispatcher/dependency_injection.rst @@ -0,0 +1,56 @@ +###### +Dependency injection +###### + +Dependency injection is a programming technique that makes a class independent of its dependencies. It achieves that by decoupling the usage of an object from its creation. This helps you to follow `SOLID's `_ dependency inversion and single responsibility principles. + + +How it works in aiogram +====== +For each update :class:`Dispatcher` passes handling context data. Filters and middleware can also make changes to the context. + +To access contextual data you should specify corresponding keyword parameter in handler or filter. For example, to get :class:`FSMContext` we do it like that: + +.. code-block:: python + @router.message(ProfileCompletion.add_photo, F.photo) + async def add_photo( + message: types.Message, bot: Bot, state: FSMContext + ) -> Any: + ... # do something with photo + + +Injecting own dependencies +====== + +Aiogram provides several ways to complement / modify contextual data. + +The first and easiest way is to simply specify the named arguments in :class:`Dispatcher` initialization, polling start methods or :class:`SimpleRequestHandler` initialization if you use webhooks. + +.. code-block:: python + async def main() -> None: + dp = Dispatcher(..., foo=42) + return await dp.start_polling( + bot, allowed_updates=dp.resolve_used_update_types(), bar="Bazz" + ) +Analogy for webhook: + +.. code-block:: python + async def main() -> None: + dp = Dispatcher(..., foo=42) + handler = SimpleRequestHandler(dispatcher=dp, bot=bot, bar="Bazz") + ... # starting webhook +:class:`Dispatcher`'s workflow data also can be supplemented by setting values as in a dictionary: + +.. code-block:: python + dp = Dispatcher(...) + dp["eggs"] = Spam() +The middlewares updates the context quite often. +You can read more about them on this page: + +- `Middlewares `__ + +The last way is to return a dictionary from the filter: + +.. literalinclude:: ../../../examples/context_addition_from_filter.py + +...or using MagicFilter with :code:`as_()` method. (`Read more `__) diff --git a/examples/context_addition_from_filter.py b/examples/context_addition_from_filter.py new file mode 100644 index 00000000..debd2bb9 --- /dev/null +++ b/examples/context_addition_from_filter.py @@ -0,0 +1,33 @@ +from typing import Any, Dict, Optional, Union + +from aiogram import Router +from aiogram.filters import Filter +from aiogram.types import Message, User + + +router = Router(name=__name__) + + +class HelloFilter(Filter): + def __init__(self, name: Optional[str] = None) -> None: + self.name = name + + async def __call__( + self, + message: Message, + event_from_user: User + # as I said previously, filters also can accept keyword parameters like in handlers + ) -> Union[bool, Dict[str, Any]]: + if message.text.casefold() == "hello": + # returning a dictionary that will update the context data + return {"name": event_from_user.mention_html(name=self.name)} + return False + + +@router.message(HelloFilter(name=None)) +async def my_handler( + message: Message, name: str # and now we can accept "name" as named parameter +) -> Any: + return message.answer( + "Hello, {name}!".format(name=name) + )