Как реализовать пагинацию с использованием inline кнопок в библиотеке aiogram?

Как правильно реализовать пагинацию с функцией перелистывания страниц в своем проекте? Я уже успешно разделил информацию на страницы и помещаю данные в список, но сталкиваюсь с трудностями при реализации функционала перехода между страницами. Пробовал использовать callback для передачи данных, но не могу корректно вызвать функцию для отправки сообщения и обновления кнопок. При изменении переменной `current_page` вручную всё работает, но как правильно организовать обработку нажатий стрелок для перехода между страницами? Также, вот код, с которым я работаю: <br/> <pre><code class="python">r_json = reqtest.get_schools(449) # &lt;-- JSON in List
    buttons = InlineKeyboardMarkup()

    chunk_size = 15
    chunks = [r_json[i:i + chunk_size] for i in range(0, len(r_json), chunk_size)] # Делим все школы на группы (страницы) по 2
    max_page = len(chunks)
    current_page = int(db.get_current_page(message.from_user.id))


    for a in chunks[current_page]:
        buttons.add(InlineKeyboardButton(text=a['shortName'], callback_data=f"school-{a['id']}"))

    buttons.add(InlineKeyboardButton(text='&lt;', callback_data=f'to-left-{max_page}'), InlineKeyboardButton(text=f'{current_page + 1}/{max_page}', callback_data='count'), InlineKeyboardButton(text='&gt;', callback_data=f'to-right-{max_page}'))
    buttons.add(InlineKeyboardButton(text='Поменять город', callback_data='choose_city'))


    await bot.send_message(message.from_user.id, f'Выберите школу:', reply_markup=buttons)</code></pre> <br/> <br/> Я также пытался обрабатывать нажатия на стрелки: <br/> <pre><code class="python">@dp.callback_query_handler(lambda c: re.search('to-', c.data))
async def to_page(call: CallbackQuery):
    chat_id = call.message.chat.id
    await call.answer()
    side_page = call.data.split("-")[1]
    max_page = call.data.split("-")[2]
    current_page = int(db.get_current_page(chat_id))
  • 18 сентября 2024 г. 15:13
Ответы на вопрос 2
Для реализации пагинации с использованием inline кнопок в библиотеке aiogram, вам нужно правильно настроить обработку нажатий на кнопки "влево" и "вправо" для перехода между страницами. Вы уже на верном пути, но давайте создадим полноценный пример для понимания.

Вот как вы можете организовать пагинацию с использованием `InlineKeyboardMarkup` и обработчиков callback-кнопок:

1. **Сохраните текущее состояние страницы в базе данных или в памяти.**
2. **Создайте обработчик для кнопок "влево" и "вправо", который будет изменять текущую страницу и обновлять сообщение с кнопками.**

Ниже пример реализации:

```python
from aiogram import Bot, Dispatcher, types
from aiogram.contrib.fsm_storage.memory import MemoryStorage
from aiogram.dispatcher import FSMContext
from aiogram.dispatcher import filters
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
from aiogram.utils import executor
import requests

API_TOKEN = 'YOUR_API_TOKEN'
bot = Bot(token=API_TOKEN)
dp = Dispatcher(bot, storage=MemoryStorage())

# Получаем данные
def get_schools():
    # Здесь у вас будет ваш код для получения данных
    r_json = requests.get('https://example.com/api/schools').json()  # Замените на ваш реальный API
    return r_json

# Храним состояния для текущей страницы
class States:
    current_page = {}

@dp.message_handler(commands=['start'])
async def start_command(message: types.Message):
    r_json = get_schools()  # Получаем данные
    chunk_size = 15
    chunks = [r_json[i:i + chunk_size] for i in range(0, len(r_json), chunk_size)]
    
    States.current_page[message.from_user.id] = {'page': 0, 'chunks': chunks}
    await send_page(message.from_user.id)

async def send_page(chat_id):
    current_state = States.current_page[chat_id]
    current_page = current_state['page']
    chunks = current_state['chunks']

    buttons = InlineKeyboardMarkup()

    # Отправляем кнопки со школами
    for school in chunks[current_page]:
        buttons.add(InlineKeyboardButton(text=school['shortName'], callback_data=f"school-{school['id']}"))

    # Добавляем кнопки навигации
    max_page = len(chunks)
    buttons.add(InlineKeyboardButton(text='◀️', callback_data=f'to-left-{max_page}'),
                 InlineKeyboardButton(text=f'{current_page + 1}/{max_page}', callback_data='count'),
                 InlineKeyboardButton(text='▶️', callback_data=f'to-right-{max_page}'))
    
    buttons.add(InlineKeyboardButton(text='Поменять город', callback_data='choose_city'))

    if chat_id in States.current_page:  # Проверяем, существует ли состояние
        await bot.send_message(chat_id, 'Выберите школу:', reply_markup=buttons)
    else:
        del States.current_page[chat_id]  # Если состояние не существует, удалить пользователя

@dp.callback_query_handler(filters.Regexp('to-'))
async def change_page(call: types.CallbackQuery):
    chat_id = call.message.chat.id
    current_state = States.current_page[chat_id]
    
    # Обработка нажатия на кнопку
    if call.data.startswith('to-left'):
        if current_state['page'] > 0:
            current_state['page'] -= 1  # Уменьшаем номер страницы
    elif call.data.startswith('to-right'):
        if current_state['page'] < len(current_state['chunks']) - 1:
            current_state['page'] += 1  # Увеличиваем номер страницы
            
    # Обновляем сообщение с кнопками на новой странице
    await call.answer()
    await call.message.delete()  # Удаляем старое сообщение
    await send_page(chat_id)  # Отправляем новое сообщение с обновленными кнопками

if __name__ == '__main__':
    executor.start_polling(dp, skip_updates=True)
```

### Примечания:
- В этом примере состояние текущей страницы хранится в классе `States`, организуя его по идентификатору пользователя.
- Код обрабатывает нажатия на кнопки навигации и обновляет сообщение с новыми данными.
- Кнопки "влево" и "вправо" настраиваются так, чтобы не выходить за пределы доступных страниц.
- Не забудьте заменить `YOUR_API_TOKEN` и URL API на свои.

Теперь, когда пользователь нажимает кнопки для перехода между страницами, будет вызываться функция
Сделай просто обновление текущего сообщения вместо отправки нового при нажатии на кнопки "влево" и "вправо", а также сохраняй и извлекай текущую страницу для пользователя. 

Как-то так:

from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
from aiogram import types

def create_pagination_buttons(current_page, max_page, chunks):
    buttons = InlineKeyboardMarkup()
    
    for a in chunks[current_page]:
        buttons.add(InlineKeyboardButton(text=a['shortName'], callback_data=f"school-{a['id']}"))
    
    buttons.add(InlineKeyboardButton(text='<', callback_data=f'to-left-{max_page}'),
                InlineKeyboardButton(text=f'{current_page + 1}/{max_page}', callback_data='count'),
                InlineKeyboardButton(text='>', callback_data=f'to-right-{max_page}'))
    
    buttons.add(InlineKeyboardButton(text='Поменять город', callback_data='choose_city'))
    
    return buttons

async def send_school_list(message: types.Message, r_json, current_page=0):
    chunk_size = 15
    chunks = [r_json[i:i + chunk_size] for i in range(0, len(r_json), chunk_size)]
    max_page = len(chunks)

    buttons = create_pagination_buttons(current_page, max_page, chunks)
    await message.answer('Выберите школу:', reply_markup=buttons)

@dp.callback_query_handler(lambda c: re.search('to-', c.data))
async def to_page(call: types.CallbackQuery):
    chat_id = call.message.chat.id
    await call.answer()

    direction = call.data.split("-")[1]
    max_page = int(call.data.split("-")[2])
    
    current_page = int(db.get_current_page(chat_id))

    if direction == 'left':
        current_page = (current_page - 1) % max_page
    elif direction == 'right':
        current_page = (current_page + 1) % max_page

    db.set_current_page(chat_id, current_page)

    r_json = reqtest.get_schools(449)
    chunk_size = 15
    chunks = [r_json[i:i + chunk_size] for i in range(0, len(r_json), chunk_size)]

    buttons = create_pagination_buttons(current_page, max_page, chunks)
    await call.message.edit_reply_markup(reply_markup=buttons)
Похожие вопросы