В прошлом отчете о своей работе над Telegram ботом Work for Everyone я поведал себе и вам о создании основы для работы с данными. В частности, создал таблицу, в которую загрузил данные регионов. Эти данные будут использоваться для генерации клавиатуры, а также для иных целей.
Постановка задачи
Первая inline клавиатура мне нужна в хендлере, который отвечает за обработку нажатий кнопок «Готов!» и «Начать ввод данных».
Вторая inline клавиатура мне понадобится в хендлере, который перехватывает выбранный пользователем федеральный округ и в ответ предлагает выбрать регион, в котором пользователь хотел бы посмотреть доступные вакансии для людей с инвалидностью. Как раз названия регионов и будут выступать в качестве inline кнопок клавиатуры.
У кнопок данных клавиатур много общего. Так, в качестве текста для кнопки используется название федерального округа и региона, а в качестве callback_data – их номера. А если есть у двух сценариев (генерация клавиатур) что-то общее, надо этим общим воспользоваться. Вот я и напишу одну функцию для генерации двух клавиатур.
Реализация
Для создания клавиатур с inline кнопками буду использовать классы InlineKeyboardButton (для создания непосредственно кнопок) и InlineKeyboardBuilder (для построения клавиатуры). Функция должна вернуть объект класса InlineKeyboardBuilder.
И уже в хендлере при передаче клавиатуры пользователю буду использовать метод as_markup(), который преобразует объект билдера в объект класса InlineKeyboardMarkup. Безусловно, метод as_markup() можно было бы использовать непосредственно в функции генерации клавиатур, но мне этот вариант не подходит, так как в клавиатуру с регионами нужно будет добавить кнопку возврата к выбору федеральных округов, на случай, если пользователь неверно выберет округ.
В качестве аргумента функция принимает список кортежей с необходимыми данными.
Таким образом, мне нужно, кроме самой функции генерации клавиатур, правильно подготовить данные.
Подготовка данных для генерации клавиатур
Как я уже писал ранее, данные о федеральных округах у меня хранятся в словаре. У словаря есть метод items(), который возвращает специальный объект dict_items (набор кортежей с парами ключ — значение). Если этот метод применить к словарю с данными федеральных округов, то получим:
dict_items( [ ('Центральный федеральный округ', 30), ('Северо-Западный федеральный округ', 31), ('Приволжский федеральный округ', 33), ('Уральский федеральный округ', 34), ('Северо-Кавказский федеральный округ', 38), ('Южный федеральный округ', 40), ('Сибирский федеральный округ', 41), ('Дальневосточный федеральный округ', 42) ] )
Собственно это практически то, что нужно. Объект dict_items внутри себя содержит список кортежей. Сам объект является итерируемым. На каждой итерации можно получить кортеж. Кстати, можно, используя функцию list(), преобразовать объект dict_items в обычный список кортежей, что собственно я и сделаю, чтобы тип входных данных в функцию генерации был равнозначный во всех случаях её использования.
Данные для клавиатур с названием регионов хранятся в базе. Каждая inline клавиатура будет состоять из кнопок с названиями регионов в выбранном пользователем федеральном округе.
В функцию генерации клавиатур я должен передать список кортежей с названием регионов и их номерами.
Для реализации этого шага мне понадобится функция, которая будет делать запрос в базу данных для получения списка регионов в конкретном федеральном округе.
Назову функцию get_data_regions(). В качестве аргумента функция должна принимать номер федерального округа, который выбрал пользователь. Функция возвращает список кортежей.
def get_data_regions(federal_district_code: int) -> List[tuple[str, int]]: """ Получение списка регионов заданного пользователем федерального округа. """ _database_connection() data: ModelSelect = ( Region.select(Region.region_name, Region.region_code) .where(Region.federal_district_code == federal_district_code) .tuples() ) _database_close() if data: return [region_data for region_data in data] return False
Буквально пару слов о том, что происходит. В методе select() я указываю поля, которые мне нужно получить из таблицы region. Методом where() задаю параметры поиска нужных регионов, а метод tuples() позволяет вернуть данные в виде кортежей.
Возвращаемый на запрос к БД объект ModelSelect является итерируемым, поэтому его также, как и объект dict_items можно сразу передать в функцию генерации клавиатур, но я так делать не буду. Возвращая результат, я, используя списковые включения, сгенерирую список кортежей, который в качестве аргумента и будет передан в функцию генерации клавиатур.
Все данные подготовлены. Собственно вот код самой функции генерации клавиатур.
def generation_kb(data_list: List[tuple[str, int]]) -> InlineKeyboardBuilder: """ Функция генерации клавиатур с inline кнопками для федеральных округов и регионов. """ bt_list = [ InlineKeyboardButton(text=x[0], callback_data=str(x[1])) for x in data_list ] builder = InlineKeyboardBuilder().row(*bt_list, width=1) return builder
Внезапная задачка
Долго пытался решить следующую задачу. Изначально ширину клавиатуры я задал равной 2, как это делал изначально, когда работал со «стандартными» кнопками. Но текст на inline кнопках никак не хотел переноситься и просто срезался, что, конечно, меня не могло устроить.
Гугление не принесло положительных результатов. Документация также не дала ответа (может, плохо искал). В итоге, где-то нашел информацию, что такое поведение как перенос текста inline кнопки на новую строку невозможно. Так я и пришел к решению, что ширина должна быть равна единичке.
Вот такие клавиатуры в итоге получились.
Inline клавиатура выбора федеральных округов
Inline клавиатура выбора региона
На этом на сегодня все, в следующий раз поговорим о машине состояний и фильтрации. Подписывайтесь на мой Telegram канал Touch IT.
Код проекта Telegram bot Work for everyone доступен на GitHub.
Я открыт к любой, даже самой жесткой, но конструктивной критике!