aiogram и python. Подскажите пожалуйста как правильнее написать код

Всем привет, подскажите как мне лучше написать код. Я пишу телеграм бот магазин, который в основном состоит из InlineKeyboard`ов и вот я подумал как мне проще написать клавиатуры и подумал, что занесу данные которые нужны для написания функции я занесу в словарь и создам цикл в котором будет вызываться эта функция с данными о продукте, и таким образом создам keyboard'ы. Но очевидно, что я не гуру и не знаю как это правильно реализовать функция не должна вызываться в цикле ведь она даже применять только когда callback_data будет соответствовать фильтру.Если возможно сделать что то приближенно к тому как я хочу, то подскажите пожалуйста как или мне не стоит это делать и написать по обычному на каждый callback_data. Ниже сырой код который я написал:

from aiogram.types import CallbackQuery, Message, InputMediaPhoto, FSInputFile
from app.keyboards_bot.keyboard_builders.keyboard_builder import inline_builder

buy_keyboard = inline_builder(
    ["Купить", "Назад"],
    ["buy", "shop_menu"],
    [1, 1]
)

products = {
    "30 Gems": {
        "callback_data": "F.data == '30g'",
        "price": 260,
        "photo": "",
        "description": "",
        "inline_keyboard": buy_keyboard,       
    },
    "80 Gems": {
        "callback_data": "F.data == '80g'",
        "price": 600,
        "photo": "",
        "description": "",
        "inline_keyboard": buy_keyboard,
    },
    "170 Gems": {
        "callback_data": "F.data == '170g'",
        "price": 1150,
        "photo": "",
        "description": "",
        "inline_keyboard": buy_keyboard,
    },
    "360 Gems": {
        "callback_data": "F.data == '360g'",
        "price": 2150,
        "photo": "",
        "description": "",
        "inline_keyboard": buy_keyboard,
    },
    "950 Gems": {
        "callback_data": "F.data == '950g'",
        "price": 5000,
        "photo": "",
        "description": "",
        "inline_keyboard": buy_keyboard,
    },
    "2000 Gems": {
        "callback_data": "F.data == '2000g'",
        "price": 10000,
        "photo": "",
        "description": f'The price of this product is {dict.get("price")}',
        "inline_keyboard": buy_keyboard,
    }
}


router = Router()

def product_form(callback_data, photo, description, i_keyboard):
    @router.callback_query(callback_data)
    async def product_make_cmd(call: CallbackQuery):
        await call.message.edit_media(
                                    media=InputMediaPhoto(
                                        media=FSInputFile(photo),
                                        caption=description,
                                        parse_mode="HTML"
                                    ),
                                        reply_markup=i_keyboard
                                )



for product in products:
    product_form(callback_data=product['callback_data'], photo=product['photo'], description=product['description'], i_keyboard=product['inline_keyboard'])```
    

Ответы (1 шт):

Автор решения: Paladin4ick

Идея использовать словарь мне нравится. Вот пример моего решения твоей задачки на aiogram 3.5x (последней, на момент написания ответа, версии).

Я решил использовать InlineBuilder и фабрику по созданию CallbackData которую привязал к InlineBuilder. Чуть более подробно расписал в докстрингах к функциям, а если хочешь узнать как работает CallbackData советую почитать документацию.

buy_menu_kb - создает клавиатуру из словаря handle_buy - обрабатывает нажатие созданных кнопок. В этой функции ты можешь получить сумму гемов (count) и цену (price) и работать уже с ними как захочешь в рамках телеграмма.

from aiogram import Router, types
from aiogram.filters import Command
from aiogram.filters.callback_data import CallbackData
from aiogram.utils.keyboard import InlineKeyboardBuilder

router = Router(name=__name__)


class BuyCallbackFactory(CallbackData, prefix="buy"):
    count: int
    price: int


products = {
    "Product 1": {"count": 100, "price": 10, "image": "image_1.jpg"},
    "Product 2": {"count": 200, "price": 18, "image": "image_2.jpg"},
    "Product 3": {"count": 300, "price": 25, "image": "image_3.jpg"},
}


async def buy_menu_kb(products_dict: dict) -> types.InlineKeyboardMarkup:
    """
    Создает клавиатуру для покупки товара из словаря.

    Args:
        products_dict: Словарь с товарами.

    Returns:
        types.InlineKeyboardMarkup: Клавиатура с кнопками для покупки.
    """
    builder = InlineKeyboardBuilder()

    for product, details in products_dict.items():
        count = details['count']
        price = details['price']
        callback_data = BuyCallbackFactory(count=count, price=price).pack()
        builder.button(text=f"{product} - {count} гемов за {price} руб.", callback_data=callback_data)

    builder.adjust(1)
    return builder.as_markup()


@router.callback_query(BuyCallbackFactory.filter())
async def handle_buy(callback_query: types.CallbackQuery, callback_data: BuyCallbackFactory):
    """
    Обрабатывает нажатие кнопки покупки.

    Args:
        callback_query: CallbackQuery объект.
        callback_data: Данные callback.
    """
    count = callback_data.count
    price = callback_data.price

    image_path = None
    product_name = None
    for product, details in products.items():
        if details['count'] == count and details['price'] == price:
            product_name = product
            image_path = details['image']
            break

    if product_name:
        image_file = types.FSInputFile(image_path)
        await callback_query.message.answer_photo(photo=image_file, caption=f"Вы выбрали {count} гемов за {price} руб.")

    await callback_query.answer()


@router.message(Command(commands=["buy_menu"]))
async def send_buy_menu(message: types.Message):
    """
    Отправляет меню с кнопками для покупки товаров.

    Args:
        message: Объект сообщения.
    """
    keyboard = await buy_menu_kb(products)
    await message.answer("Выберите товар:", reply_markup=keyboard)

P.s: код далеко не идеален и может иметь свои недостатки. Совет: разделить этот код на отдельные файлы у себя в проекте, к примеру на utils.py, keyboards.py, callbacks.py, commands.py и т.д.

→ Ссылка