Как сделать список страниц из динамического количества кнопок Aiogram?

Нужна помощь с телеграмм ботом. Есть бот у которого есть динамическая клавиатура, количество кнопок постоянно изменяется, может быть, как 2,так и 200. Я вывожу кнопки таким образом.

# список прилетает из бд с n-ым количеством строк
list = ['b1', 'b2'] 

# постоянная кнопка, которая должна быть на каждой странице 
back_to_main = InlineKeyboardButton(text="❌ Вернуться в меню", callback_data="back_to_main_menu")
button_list = [InlineKeyboardButton(text=x, callback_data=f'shop_{x}') for x in list]
default_kb = InlineKeyboardMarkup(row_width=1).add(*button_list, back_to_main)
return default_kb 

Как правильно реализовать систему пролистывания страниц при достижении, например > 10 кнопок. Т.е если в функцию прилетел список на 46 кнопок. Нужно как-то разбить на 5 страниц, где на 4х страницах будет по 10 кнопок и на последней 6. Буду очень благодарен коду и комментариям по его реализации <3


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

Автор решения: pshpth_ sht

Нашёл решение данного вопроса:

async def category_swipe_fp(remover,shop_id):
    get_categories = get_all_categoriesx(shop_id)
    keyboard = InlineKeyboardMarkup()
    if remover >= len(get_categories): remover -= 10

    for count, a in enumerate(range(remover, len(get_categories))):
        if count < 10:
            keyboard.add(InlineKeyboardButton(get_categories[a]['category_name'],
                             callback_data=f"buy_category_open:{get_categories[a]['category_id']}:{shop_id}"))

    if len(get_categories) <= 10:
        pass
    elif len(get_categories) > 10 and remover < 10:
        keyboard.add(
            InlineKeyboardButton(f"? 1/{math.ceil(len(get_categories) / 10)} ?", callback_data="..."),
            InlineKeyboardButton("Далее ?", callback_data=f"buy_category_swipe:{remover + 10}:{shop_id}"),
        )
    elif remover + 10 >= len(get_categories):
        keyboard.add(
            InlineKeyboardButton("? Назад", callback_data=f"buy_category_swipe:{remover - 10}:{shop_id}"),
            InlineKeyboardButton(f"? {str(remover + 10)[:-1]}/{math.ceil(len(get_categories) / 10)} ?", callback_data="..."),
        )
    else:
        keyboard.add(
            InlineKeyboardButton("? Назад", callback_data=f"buy_category_swipe:{remover - 10}:{shop_id}"),
            InlineKeyboardButton(f"? {str(remover + 10)[:-1]}/{math.ceil(len(get_categories) / 10)} ?", callback_data="..."),
            InlineKeyboardButton("Далее ?", callback_data=f"buy_category_swipe:{remover + 10}:{shop_id}"),
        )

    return keyboard

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

В переменной shop_id я записываю id конкретного магазина для дальнейшего извлекания всех категорий с помощью функции get_all_categoriesx. Данная функция должна возвращать СЛОВАРЬ вида:

{'shop_id': 5, 'products_id': 12, 'product_name': 'l', 'position_id': 77564, 'pic': None, 'price': 7.6}

Реализовываю функцию get_all_categoriesx так:

def dict_factory(cursor, row):
    save_dict = {}

    for idx, col in enumerate(cursor.description):
        save_dict[col[0]] = row[idx]

    return save_dict

def get_all_categoriesx(shop_id,**kwargs):
    with sqlite3.connect('database.db') as con:
        con.row_factory = dict_factory
        sql = f"SELECT * FROM shops_category WHERE shop_id = {shop_id}"
        return con.execute(sql).fetchall()

Функция вернет вам как раз словарь, по сути своей клавиатура уже готова. Осталось обработать calldat'у по клику на кнопку "Далее" или "Назад"

@dp.callback_query_handler(text_startswith="buy_category_swipe:", state="*")
async def user_purchase_category_next_page(call: types.CallbackQuery, state: FSMContext):
    remover = int(call.data.split(":")[1])
    shop_id = int(call.data.split(":")[2])
    await call.message.edit_text("<b>Категории</b>",
                                 reply_markup=await category_swipe_fp(remover, shop_id))

Обрабатываем call.data,берем remover, если пользователь нажал на кнопку далее, то remover уже будет 10 и клавиатура отобразит следующие 10 категорий.

→ Ссылка