From f87deea4fb0363d7d2106d17a51d33ea609eb93c Mon Sep 17 00:00:00 2001 From: nullmatawasoradesu <119107850+wakaree@users.noreply.github.com> Date: Sun, 13 Aug 2023 17:59:38 +0300 Subject: [PATCH] Added a section on Dependency Injection technology in documentation (#1253) * Added a section on Dependency Injection technology in documentation * Added changelog * Edited comments & titles --- CHANGES/1253.doc.rst | 1 + docs/dispatcher/dependency_injection.rst | 56 ++++++++++++++++++++++++ examples/context_addition_from_filter.py | 33 ++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 CHANGES/1253.doc.rst create mode 100644 docs/dispatcher/dependency_injection.rst create mode 100644 examples/context_addition_from_filter.py diff --git a/CHANGES/1253.doc.rst b/CHANGES/1253.doc.rst new file mode 100644 index 00000000..d8100687 --- /dev/null +++ b/CHANGES/1253.doc.rst @@ -0,0 +1 @@ +Added a section on Dependency Injection technology diff --git a/docs/dispatcher/dependency_injection.rst b/docs/dispatcher/dependency_injection.rst new file mode 100644 index 00000000..15b7c908 --- /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..e4eca517 --- /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 + # 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()) +async def my_handler( + message: Message, name: str # Now we can accept "name" as named parameter +) -> Any: + return message.answer( + "Hello, {name}!".format(name=name) + )