Обучение Python+aiogram+MySQL
September 26, 2023

Обучение #3

Хай, я получил от людей проблему с установкой программы для базы данных, из 1 урока, так вот, если у вас после установки нет программы как на скриншоте:

Её можно скачать по ссылке - *КЛИКАБЕЛЬНО*

При установке, выбираем Modify

Далее нажимаем везде "Next"

Вроде разобрались (если не помогло прошу написать мне в личные сообщения: @Xacker_Name_new

-------------------------------------------------------------------------

Давайте начнём работать с базой данных, с помощью вспомогательного класса work_db из файла class_db.py мы будем записывать все анкеты в базу данных.

Возьмём код бота из Урока #2

from aiogram.dispatcher.filters.state import State, StatesGroup
from aiogram import Bot, Dispatcher, executor, types
from aiogram.dispatcher import FSMContext
from aiogram.types import ReplyKeyboardRemove
from class_db import work_db #Импорируем класс для работы с базой данных из файла class_db.py
import test as bb #Импортируем файл test.py в переменную bb (мне так удобнее работать с ним)
import config as c #Импортируем файл config.py в переменную с (опять же мне так удобнее)



#Создаём класс бота и дистпетчера
bot = Bot(token=c.token) #Берём токен бота из файла config.py который импортировали ранее в переменную c
dp = Dispatcher(bot)
db_work = work_db(c.host, c.port, c.user, c.password, c.database) #Создаём экземпляр класса для работы с базой данных, передав все нужные параметры из конфига


class anketa(StatesGroup):
	name = State()
	floor = State()
	age = State()




@dp.message_handler(content_types=['text']) #Обрабатываем каждое сообщение от пользователей, добавляем content_types для обработки текста, в будущих уроках будет фото и видео
async def text(message: types.Message): #Асинхронная функция с название "text" которая принимает в переменную message, текст пользователя отправевшего сообщение
	if message.text == '/start':
		await bot.send_message(message.chat.id, 'Привет, заполни анкету, или посмотри информацйию о мне', reply_markup=bb.mark_menu) #Обрабатываем команду /start и выводим клавиатуру из файлы test.py
	elif message.text == 'Информация':
		await bot.send_message(message.chat.id, f'Сюда пишем какую либо информацию, например я хочу вывести ID пользователя\nТвой ID: {message.chat.id}')
	elif message.text == 'Заполнить анкету':
		await bot.send_message(message.chat.id, 'Введите своё имя:')
		await anketa.name.set()


@dp.message_handler(state=anketa.name)
async def anketa_name(message: types.Message, state: FSMContext): #Как видите у нас добавился FSMContext, он нам нужен как раз для обработки машины состояния
	#Записываем имя в кеш
	await state.update_data(name=name)
	#Далее просим ввести пол, с выбором пола из кнопок
	await bot.send_message(message.chat.id, 'Выберите свой пол:', reply_markup=bb.main_floor) #добавляем вывод кнопок с выбором пола
	await anketa.floor.set()


@dp.message_handler(state=anketa.floor)
async def anketa_name(message: types.Message, state: FSMContext):
	#Записываем пол в кеш
	await state.update_data(floor=floor)
	#Далее просим ввести свой возраст
	await bot.send_message(message.chat.id, 'Выберите свой пол:', reply_markup=ReplyKeyboardRemove()) #убираем текстовую клавиатуру
	await anketa.age.set()


@dp.message_handler(state=anketa.age)
async def anketa_name(message: types.Message, state: FSMContext):
	#Получим все данные записанные в кеш
	data = await state.get_data()
	#Сортируем их по переменным
	name = data['name'] #В ковычках пишем name, потому что сами записывали в такую переменную, там может быть любая другая
	floor = data['floor'] #В ковычках пишем floor, потому что сами записывали в такую переменную, там может быть любая другая
	age = message.text #записываем ввод пользователя в переменную age
	await state.finish() #Завершаем работу с машиной состояний
	#Выведем анкету пользователю
	await bot.send_message(message.chat.id, f'Ваша анкета:\n\nИмя: {name}\nПол: {floor}\nВозраст: {age}\n\nСпасибо за уделение времени', reply_markup=bb.mark_menu) #Отправляем анкету, благодарим, и выдаём ему текстовоем меню
	#Отправим анкету администратору по его ID из config.py
	#Так же выведем ID пользователя и его юзернейм
	await bot.send_message(c.admin, f'Пользователь: {message.chat.id}\n@{message.from_user.username} заполнил анкету\nЕго данные:\n\nИмя: {name}\nПол: {floor}\nВозраст: {age}')




if __name__ == "__main__": #Если скрипт запущен с этого файла, запускаем executor
    executor.start_polling(dp, skip_updates=True) #skip_updates=True - нужен для того чтобы не обрабатывать сообщения которые были присланы пользователем в тот момент когда бот был выключен

Для начала давайте создадим функцию которая будет создавать таблицы:

def creat_table(): #Данную функцию будем запускать при каждом запуске кода, параметры функция не принимает
	#Для начала конектимся к базе данных:
	connection = db_work.connect_db()
	if connection: #Если класс вернул конект, а не False
		#Напишем сам запрос
		creat_table_request = 'CREATE TABLE IF NOT EXISTS `ankets`(id int AUTO_INCREMENT, name varchar(32), floor varchar(10), age int, PRIMARY KEY (id));' #И так мы написали запрос, но для чего он нам?
		#Он нам для того что-бы создать таблицу для анкет где будут следующие данные: id - это будет id анкеты (число), оно будет прописываться само (из-за AUTO_INCREMENT)
		#name типа сроки длинной не более 32 символов, floor типа строки не более 10 символов и age типа числа без ограничения
		#Первичный ключ (PRIMARY KEY) — особенное поле в таблице, которое позволяет однозначно идентифицировать каждую запись в ней
		#Запрос написан, давайте его выполним:
		status = db_work.create_table(connection, creat_table_request) #Предаём необходимые данные (Конект и сам запрос)
		#Проверяем статус создания базы данных:
		if status: #Если вернуло True значит всё хорошо (выведем в консоль что запрос выполнен)
			print('Таблица успешно создана')
			#Закрываем конект
			if db_work.connect_close(connection):
				print('Конект закрыт!')
			else: #Если нам вернуло False, значит произошла ошибка, в вспомогательном классе мы прописали вывод ошибки
				print('Ошибка указана выше')
		else: #Если вернуло False, значит не удалось создать таблицу, ошибку мы так же вывели в классе
			print('Ошибка указана выше')
	else: #Если мы не смогли подключиться к базе данных, то опять же ошибка была выведена в классе
		print('Ошибка указана выше')

И так давайте допишем запуск этой функции:

if __name__ == "__main__": #Если скрипт запущен с этого файла, запускаем executor
	creat_table() #Запускаем функцию создания таблицы
	executor.start_polling(dp, skip_updates=True) #skip_updates=True - нужен для того чтобы не обрабатывать сообщения которые были присланы пользователем в тот момент когда бот был выключен

Запускаем:

У меня возникла ошибка с вспомогательным классом для работы с базой данных, текст ошибки:

Traceback (most recent call last):
  File "C:\Users\ffff1\OneDrive\Рабочий стол\bot_ankets\main.py", line 14, in <module>
    db_work = work_db(c.host, c.port, c.user, c.password, c.database) #Создаём экземпляр класса для работы с базой данных, передав все нужные параметры из конфига
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: work_db.__init__() takes 1 positional argument but 6 were given

Это из-за того что я не написал в функции "__init__" принятие нескольких аргументов, а передаю аж 6, поэтому давайте перепишем функцию "__init__":

def __init__(self, host, port, user, password, database): #Функция для иницилизирования переменных в класс, запускать её не надо
		self.host = host
		self.port = port
		self.user = user
		self.password = password
		self.database = database

Запускаем:

...Пишет что базы нет, в таком случае в программе Workbench 8.0 CE заходим в базу которые создали в первом уроке

В строке (указанной прямоугольником пишем следующие:

CREATE DATABASE usersdb;

Должно получиться так:

Далее нажимаем на молнию (указана стрелочкой)

Красная стрелочка: нам пишет что база данных создана!
Черная стрелочка: На том месте нажимаем правой кнопкой и Refresh (У нас появляется база данных)

Далее покажу как смотреть данные из неё...

Запускаем бота:

Таблица успешно создана, и конект успешно закрыт, я считаю это успех)

Давайте напишем функцию которая будет записывать данные в таблицу:

def insert_table(name_, floor_, age_): #Данная функция принимает имя таблицы в которую надо записать данные (Сразу скажу т.к. мы записывать будет в ankets ропишем получение данных)
	#Для начала конектимся к базе данных:
	connection = db_work.connect_db()
	if connection: #Если класс вернул конект, а не False
		#Напишем сам запрос
		insert_table_request = 'INSERT INTO ankets (name, floor, age) VALUES (%s, %s, %s);' #И так в этом запросе мы создаём запись данных
		with connection.cursor() as cursor:
			cursor.execute(insert_table_request, (name_, floor_, age_)) #Записываем данные полученные в функцию
		connection.commit() #Сохраняем изменения
		print('Данные записаны!')
		if db_work.connect_close(connection):
			print('Конект закрыт!')
		else: #Если нам вернуло False, значит произошла ошибка, в вспомогательном классе мы прописали вывод ошибки
			print('Ошибка указана выше')
	else: #Если мы не смогли подключиться к базе данных, то опять же ошибка была выведена в классе
		print('Ошибка указана выше')

Функция записана, давайте её добавим вызов функции из обработчика возраста:

@dp.message_handler(state=anketa.age)
async def anketa_name(message: types.Message, state: FSMContext):
	#Получим все данные записанные в кеш
	data = await state.get_data()
	#Сортируем их по переменным
	name = data['name'] #В ковычках пишем name, потому что сами записывали в такую переменную, там может быть любая другая
	floor = data['floor'] #В ковычках пишем floor, потому что сами записывали в такую переменную, там может быть любая другая
	age = message.text #записываем ввод пользователя в переменную age
	await state.finish() #Завершаем работу с машиной состояний
	#Выведем анкету пользователю
	await bot.send_message(message.chat.id, f'Ваша анкета:\n\nИмя: {name}\nПол: {floor}\nВозраст: {age}\n\nСпасибо за уделение времени', reply_markup=bb.mark_menu) #Отправляем анкету, благодарим, и выдаём ему текстовоем меню
	#Отправим анкету администратору по его ID из config.py
	#Так же выведем ID пользователя и его юзернейм
	await bot.send_message(c.admin, f'Пользователь: {message.chat.id}\n@{message.from_user.username} заполнил анкету\nЕго данные:\n\nИмя: {name}\nПол: {floor}\nВозраст: {age}')
	insert_table(name, floor, age) #Передаём необходимые данные

Перезапускаем бота, и заполняем анкету:

Выдало ошибку:

FSMStorageWarning: You haven’t set any storage yet so no states and no data will be saved.
You can connect MemoryStorage for debug purposes or non-essential data.

Нужно добавить ещё 1 импорт в файл main.py:

from aiogram.contrib.fsm_storage.memory import MemoryStorage

Так же поменяем класс бота и диспетчера:

bot = Bot(token=c.token, parse_mode=types.ParseMode.HTML)
dp = Dispatcher(bot, storage=MemoryStorage())

Ещё я допустил ошибку в 2х обработчиках:

@dp.message_handler(state=anketa.name)
async def anketa_name(message: types.Message, state: FSMContext): #Как видите у нас добавился FSMContext, он нам нужен как раз для обработки машины состояния
	#Записываем имя в кеш
	await state.update_data(name=message.text)
	#Далее просим ввести пол, с выбором пола из кнопок
	await bot.send_message(message.chat.id, 'Выберите свой пол:', reply_markup=bb.main_floor) #добавляем вывод кнопок с выбором пола
	await anketa.floor.set()


@dp.message_handler(state=anketa.floor)
async def anketa_name(message: types.Message, state: FSMContext):
	#Записываем пол в кеш
	await state.update_data(floor=message.text)
	#Далее просим ввести свой возраст
	await bot.send_message(message.chat.id, 'Выберите свой пол:', reply_markup=ReplyKeyboardRemove()) #убираем текстовую клавиатуру
	await anketa.age.set()

Не думайте что я не мастер своего дела, по мимо уроков я ещё и работаю, учусь, и иногда бывают такие ошибки...

Давайте продолжим, запускаем бота и заполняем анкету:

И так мы успешно записали данные, давайте посмотрим их

Открываем программу (скрин)

Заходим двойным щелчком ЛКМ по базе "test_basa"

Раскрываем базу:

Разворачиваем таблицы

При наведении на таблице ankets у нас появляются кнопочки, нам нужна эта:

Нажимаем и снизу у нас есть все данные записанные в таблицу:

Молодцы, теперь давайте по команду в бота "/info_table"

Давайте перепишем функцию в вспомогательном классе:

	def info_table(self, connection, info, values): #Функция для получения данных из таблицы, в info будем передавать текст запроса
		try:
			with connection.cursor() as cursor:
				cursor.execute(info, values)
			return cursor.fetchall()[0] #Возвращаем всё что нашли
		except Exception as eror:
			print(eror) #Если произошла ошибка, выводим её в консоль
			return False #Если произошла ошибка, возвращаем False

Выведем все данные по ID 1
Напишем обработчик команды "/info_table":

@dp.message_handler(content_types=['text']) #Обрабатываем каждое сообщение от пользователей, добавляем content_types для обработки текста, в будущих уроках будет фото и видео
async def text(message: types.Message): #Асинхронная функция с название "text" которая принимает в переменную message, текст пользователя отправевшего сообщение
	if message.text == '/start':
		await bot.send_message(message.chat.id, 'Привет, заполни анкету, или посмотри информацйию о мне', reply_markup=bb.mark_menu) #Обрабатываем команду /start и выводим клавиатуру из файлы test.py
	elif message.text == 'Информация':
		await bot.send_message(message.chat.id, f'Сюда пишем какую либо информацию, например я хочу вывести ID пользователя\nТвой ID: {message.chat.id}')
	elif message.text == 'Заполнить анкету':
		await bot.send_message(message.chat.id, 'Введите своё имя:')
		await anketa.name.set()
	elif message.text == '/info_table':
		info_request = 'SELECT * FROM ankets WHERE id = %s'
		values = (1,)
		connection = db_work.connect_db() #Конектимся к базе данных
		if connection: #Если конект успешный
			info = db_work.info_table(connection, info_request, values)
			if info: #Если получили данные по id = 1
				await bot.send_message(message.chat.id, f'Полученные данные: {info}')
			else: #Если не смогли получить данные
				await bot.send_message(message.chat.id, 'Не удалось получить данные')
		else: #Если конект не удался
			await bot.send_message(message.chat.id, 'Не удалось подключиться к базе данных')

У нас получился такой код, перезапускаем бота, и проверяем работоспособность команды:

Работает отлично, но выводит данные не особо красиво, давайте исправим:

@dp.message_handler(content_types=['text']) #Обрабатываем каждое сообщение от пользователей, добавляем content_types для обработки текста, в будущих уроках будет фото и видео
async def text(message: types.Message): #Асинхронная функция с название "text" которая принимает в переменную message, текст пользователя отправевшего сообщение
	if message.text == '/start':
		await bot.send_message(message.chat.id, 'Привет, заполни анкету, или посмотри информацйию о мне', reply_markup=bb.mark_menu) #Обрабатываем команду /start и выводим клавиатуру из файлы test.py
	elif message.text == 'Информация':
		await bot.send_message(message.chat.id, f'Сюда пишем какую либо информацию, например я хочу вывести ID пользователя\nТвой ID: {message.chat.id}')
	elif message.text == 'Заполнить анкету':
		await bot.send_message(message.chat.id, 'Введите своё имя:')
		await anketa.name.set()
	elif message.text == '/info_table':
		info_request = 'SELECT * FROM ankets WHERE id = %s'
		values = (1,)
		connection = db_work.connect_db() #Конектимся к базе данных
		if connection: #Если конект успешный
			info = db_work.info_table(connection, info_request, values)
			if info: #Если получили данные по id = 1
				await bot.send_message(message.chat.id, f'Полученные данные:\nID: {info["id"]}\nИмя: {info["name"]}\nПол: {info["floor"]}\nВозраст: {info["age"]}')
			else: #Если не смогли получить данные
				await bot.send_message(message.chat.id, 'Не удалось получить данные')
		else: #Если конект не удался
			await bot.send_message(message.chat.id, 'Не удалось подключиться к базе данных')

Проверяем вывод, и наслаждаемся красотой:

Так то лучше не так ли?

Давайте обновим возраст с помощью инлайн кнопок?

Создаём обработчик команды "/new_value"

Но перед этим напишем класс для ввода ID записи в базе данных:

class new_values(StatesGroup):
	new_value = State()

И так продолжим писать обработчик:

@dp.message_handler(content_types=['text']) #Обрабатываем каждое сообщение от пользователей, добавляем content_types для обработки текста, в будущих уроках будет фото и видео
async def text(message: types.Message): #Асинхронная функция с название "text" которая принимает в переменную message, текст пользователя отправевшего сообщение
	if message.text == '/start':
		await bot.send_message(message.chat.id, 'Привет, заполни анкету, или посмотри информацйию о мне', reply_markup=bb.mark_menu) #Обрабатываем команду /start и выводим клавиатуру из файлы test.py
	elif message.text == 'Информация':
		await bot.send_message(message.chat.id, f'Сюда пишем какую либо информацию, например я хочу вывести ID пользователя\nТвой ID: {message.chat.id}')
	elif message.text == 'Заполнить анкету':
		await bot.send_message(message.chat.id, 'Введите своё имя:')
		await anketa.name.set()
	elif message.text == '/info_table':
		info_request = 'SELECT * FROM ankets WHERE id = %s'
		values = (1,)
		connection = db_work.connect_db() #Конектимся к базе данных
		if connection: #Если конект успешный
			info = db_work.info_table(connection, info_request, values)
			if info: #Если получили данные по id = 1
				await bot.send_message(message.chat.id, f'Полученные данные:\nID: {info["id"]}\nИмя: {info["name"]}\nПол: {info["floor"]}\nВозраст: {info["age"]}')
			else: #Если не смогли получить данные
				await bot.send_message(message.chat.id, 'Не удалось получить данные')
		else: #Если конект не удался
			await bot.send_message(message.chat.id, 'Не удалось подключиться к базе данных')
	elif message.text == '/new_value':
		await bot.send_message(message.chat.id, 'Введите ID для изменения возраста:')
		await new_values.new_value.set()

Пишем обработчик ввода пользователя:

@dp.message_handler(state=new_values.new_value)
async def anketa_name(message: types.Message, state: FSMContext): #Как видите у нас добавился FSMContext, он нам нужен как раз для обработки машины состояния
	#Записываем ID в кеш
	await state.update_data(id_value=message.text)
	#Выводим инлайн кнопки с числами
	#Пока напишу прямо тут, в следующих уроках будем создавать функции
	await bot.send_message(message.chat.id, 'Выберите новое значение:', reply_markup=InlineKeyboardMarkup(row_width=3).add(
		InlineKeyboardButton(text='1', callback_data='new_value_1'),InlineKeyboardButton(text='2', callback_data='new_value_2'), InlineKeyboardButton(text='10', callback_data='new_value_10'),
		InlineKeyboardButton(text='25', callback_data='new_value_25')))
	#Этого достаточно

Давайте допишем ипорт:

from aiogram.types import ReplyKeyboardRemove, InlineKeyboardMarkup, InlineKeyboardButton

Давайте ещё перепишем функцию в вспомогательном классе:

	def edit_table(self, connection, edit, values): #Функция для редактирования данных в таблице, в edit будем передавать текст запроса
		try:
			with connection.cursor() as cursor:
				cursor.execute(edit, values)
			connection.commit()
			return True
		except Exception as eror:
			print(eror) #Если произошла ошибка, выводим её в консоль
			return False #Если произошла ошибка, возвращаем False

Теперь перейдём к обработчику нажатий на инлайн кнопки:

@dp.callback_query_handler(lambda call: True, state='*') #Обрабатываем нажатия всех инлайн кнопок, даже когда активно ожидание ввода от пользователя
async def callback_inline(call, state: FSMContext): #В переменную call принимаем данные о нажатой кнопке
	try: #Обрабатываем ошибки, если всё успешно то мы получим переменную id_value на всю функцию
		data = await state.get_data()
		id_value = data['id_value']
		await state.finish()
	except Exception as eror: #Если будут ошибки мы их пришлём пользователю
		await bot.send_message(call.message.chat.id, f'Произошла ошибка: {eror}')
		return #Останавливаем выполнение функции
	#Приступим к обработке нажатия кнопок, т.к. у нас записано новое чило в callback_data, но начало одно и тоже
	#Нам нужно проверять есть ли в нажатой кнопке начало:
	if 'new_value_' in call.data: #В call.data содержиться callback_data
		new_value = int(call.data.replace('new_value_', '')) #С момощью этого кода мы получим число типа int, удалив из call.data не нужное нам
		#Грубыми словами с помощью replace мы заменяем new_value_ на ничего
		#Теперь давайте заменим значение в таблице по ID которое вписали изначально
		#Конектимся к базе данных:
		connection = db_work.connect_db()
		if connection: #Если конект успешен
			#Возпользуемся функцией edit_table из вспомогательного класса:
			new_value_request = 'UPDATE ankets SET age = %s WHERE id = %s'
			values = (new_value, int(id_value))
			status = db_work.edit_table(connection, new_value_request, values)
			if status: #Если успешно обновили данные
				await bot.send_message(call.message.chat.id, f'Данные для строки с ID: {id_value}\nУспешно обновлены на: {new_value}')
			else: #Если произошла ошибка
				await bot.send_message(call.message.chat.id, 'Произошла ошибка, она отобразилась в консоли!')
		else: #Если не удалось подключиться к базе данных
			await bot.send_message(call.message.chat.id, 'Не удалось подключиться к базе данных!')

Запустим бота и проверим работоспособность:

Всё работает, кайф, давайте проверим данные через программу

Для обновления, нажимаем на кнопку:

И так, данные успешно обновлены:

На этом я предлагаю закончить наш урок)

Итоговый код который у нас полчиться:

from aiogram.dispatcher.filters.state import State, StatesGroup
from aiogram.contrib.fsm_storage.memory import MemoryStorage
from aiogram import Bot, Dispatcher, executor, types
from aiogram.dispatcher import FSMContext
from aiogram.types import ReplyKeyboardRemove, InlineKeyboardMarkup, InlineKeyboardButton
from class_db import work_db #Импорируем класс для работы с базой данных из файла class_db.py
import test as bb #Импортируем файл test.py в переменную bb (мне так удобнее работать с ним)
import config as c #Импортируем файл config.py в переменную с (опять же мне так удобнее)



#Создаём класс бота и дистпетчера
bot = Bot(token=c.token, parse_mode=types.ParseMode.HTML) #Берём токен бота из файла config.py который импортировали ранее в переменную c
dp = Dispatcher(bot, storage=MemoryStorage())
db_work = work_db(c.host, c.port, c.user, c.password, c.database) #Создаём экземпляр класса для работы с базой данных, передав все нужные параметры из конфига


class anketa(StatesGroup):
	name = State()
	floor = State()
	age = State()


class new_values(StatesGroup):
	new_value = State()


def creat_table(): #Данную функцию будем запускать при каждом запуске кода, параметры функция не принимает
	#Для начала конектимся к базе данных:
	connection = db_work.connect_db()
	if connection: #Если класс вернул конект, а не False
		#Напишем сам запрос
		creat_table_request = 'CREATE TABLE IF NOT EXISTS `ankets`(id int AUTO_INCREMENT, name varchar(32), floor varchar(10), age int, PRIMARY KEY (id));' #И так мы написали запрос, но для чего он нам?
		#Он нам для того что-бы создать таблицу для анкет где будут следующие данные: id - это будет id анкеты (число), оно будет прописываться само (из-за AUTO_INCREMENT)
		#name типа сроки длинной не более 32 символов, floor типа строки не более 10 символов и age типа числа без ограничения
		#Первичный ключ (PRIMARY KEY) — особенное поле в таблице, которое позволяет однозначно идентифицировать каждую запись в ней
		#Запрос написан, давайте его выполним:
		status = db_work.create_table(connection, creat_table_request) #Предаём необходимые данные (Конект и сам запрос)
		#Проверяем статус создания базы данных:
		if status: #Если вернуло True значит всё хорошо (выведем в консоль что запрос выполнен)
			print('Таблица успешно создана')
			#Закрываем конект
			if db_work.connect_close(connection):
				print('Конект закрыт!')
			else: #Если нам вернуло False, значит произошла ошибка, в вспомогательном классе мы прописали вывод ошибки
				print('Ошибка указана выше')
		else: #Если вернуло False, значит не удалось создать таблицу, ошибку мы так же вывели в классе
			print('Ошибка указана выше')
	else: #Если мы не смогли подключиться к базе данных, то опять же ошибка была выведена в классе
		print('Ошибка указана выше')


def insert_table(name_, floor_, age_): #Данная функция принимает имя таблицы в которую надо записать данные (Сразу скажу т.к. мы записывать будет в ankets ропишем получение данных)
	#Для начала конектимся к базе данных:
	connection = db_work.connect_db()
	if connection: #Если класс вернул конект, а не False
		#Напишем сам запрос
		insert_table_request = 'INSERT INTO ankets (name, floor, age) VALUES (%s, %s, %s);' #И так в этом запросе мы создаём запись данных
		with connection.cursor() as cursor:
			cursor.execute(insert_table_request, (name_, floor_, age_)) #Записываем данные полученные в функцию
		connection.commit() #Сохраняем изменения
		print('Данные записаны!')
		if db_work.connect_close(connection):
			print('Конект закрыт!')
		else: #Если нам вернуло False, значит произошла ошибка, в вспомогательном классе мы прописали вывод ошибки
			print('Ошибка указана выше')
	else: #Если мы не смогли подключиться к базе данных, то опять же ошибка была выведена в классе
		print('Ошибка указана выше')


@dp.message_handler(content_types=['text']) #Обрабатываем каждое сообщение от пользователей, добавляем content_types для обработки текста, в будущих уроках будет фото и видео
async def text(message: types.Message): #Асинхронная функция с название "text" которая принимает в переменную message, текст пользователя отправевшего сообщение
	if message.text == '/start':
		await bot.send_message(message.chat.id, 'Привет, заполни анкету, или посмотри информацйию о мне', reply_markup=bb.mark_menu) #Обрабатываем команду /start и выводим клавиатуру из файлы test.py
	elif message.text == 'Информация':
		await bot.send_message(message.chat.id, f'Сюда пишем какую либо информацию, например я хочу вывести ID пользователя\nТвой ID: {message.chat.id}')
	elif message.text == 'Заполнить анкету':
		await bot.send_message(message.chat.id, 'Введите своё имя:')
		await anketa.name.set()
	elif message.text == '/info_table':
		info_request = 'SELECT * FROM ankets WHERE id = %s'
		values = (1,)
		connection = db_work.connect_db() #Конектимся к базе данных
		if connection: #Если конект успешный
			info = db_work.info_table(connection, info_request, values)
			if info: #Если получили данные по id = 1
				await bot.send_message(message.chat.id, f'Полученные данные:\nID: {info["id"]}\nИмя: {info["name"]}\nПол: {info["floor"]}\nВозраст: {info["age"]}')
			else: #Если не смогли получить данные
				await bot.send_message(message.chat.id, 'Не удалось получить данные')
		else: #Если конект не удался
			await bot.send_message(message.chat.id, 'Не удалось подключиться к базе данных')
	elif message.text == '/new_value':
		await bot.send_message(message.chat.id, 'Введите ID для изменения возраста:')
		await new_values.new_value.set()


@dp.message_handler(state=new_values.new_value)
async def anketa_name(message: types.Message, state: FSMContext): #Как видите у нас добавился FSMContext, он нам нужен как раз для обработки машины состояния
	#Записываем ID в кеш
	await state.update_data(id_value=message.text)
	#Выводим инлайн кнопки с числами
	#Пока напишу прямо тут, в следующих уроках будем создавать функции
	await bot.send_message(message.chat.id, 'Выберите новое значение:', reply_markup=InlineKeyboardMarkup(row_width=3).add(
		InlineKeyboardButton(text='1', callback_data='new_value_1'),InlineKeyboardButton(text='2', callback_data='new_value_2'), InlineKeyboardButton(text='10', callback_data='new_value_10'),
		InlineKeyboardButton(text='25', callback_data='new_value_25')))
	#Этого достаточно



@dp.message_handler(state=anketa.name)
async def anketa_name(message: types.Message, state: FSMContext): #Как видите у нас добавился FSMContext, он нам нужен как раз для обработки машины состояния
	#Записываем имя в кеш
	await state.update_data(name=message.text)
	#Далее просим ввести пол, с выбором пола из кнопок
	await bot.send_message(message.chat.id, 'Выберите свой пол:', reply_markup=bb.menu_floor) #добавляем вывод кнопок с выбором пола
	await anketa.floor.set()


@dp.message_handler(state=anketa.floor)
async def anketa_name(message: types.Message, state: FSMContext):
	#Записываем пол в кеш
	await state.update_data(floor=message.text)
	#Далее просим ввести свой возраст
	await bot.send_message(message.chat.id, 'Введите свой возраст:', reply_markup=ReplyKeyboardRemove()) #убираем текстовую клавиатуру
	await anketa.age.set()


@dp.message_handler(state=anketa.age)
async def anketa_name(message: types.Message, state: FSMContext):
	#Получим все данные записанные в кеш
	data = await state.get_data()
	#Сортируем их по переменным
	name = data['name'] #В ковычках пишем name, потому что сами записывали в такую переменную, там может быть любая другая
	floor = data['floor'] #В ковычках пишем floor, потому что сами записывали в такую переменную, там может быть любая другая
	age = message.text #записываем ввод пользователя в переменную age
	await state.finish() #Завершаем работу с машиной состояний
	#Выведем анкету пользователю
	await bot.send_message(message.chat.id, f'Ваша анкета:\n\nИмя: {name}\nПол: {floor}\nВозраст: {age}\n\nСпасибо за уделение времени', reply_markup=bb.mark_menu) #Отправляем анкету, благодарим, и выдаём ему текстовоем меню
	#Отправим анкету администратору по его ID из config.py
	#Так же выведем ID пользователя и его юзернейм
	await bot.send_message(c.admin, f'Пользователь: {message.chat.id}\n@{message.from_user.username} заполнил анкету\nЕго данные:\n\nИмя: {name}\nПол: {floor}\nВозраст: {age}')
	insert_table(name, floor, age) #Передаём необходимые данные



@dp.callback_query_handler(lambda call: True, state='*') #Обрабатываем нажатия всех инлайн кнопок, даже когда активно ожидание ввода от пользователя
async def callback_inline(call, state: FSMContext): #В переменную call принимаем данные о нажатой кнопке
	try: #Обрабатываем ошибки, если всё успешно то мы получим переменную id_value на всю функцию
		data = await state.get_data()
		id_value = data['id_value']
		await state.finish()
	except Exception as eror: #Если будут ошибки мы их пришлём пользователю
		await bot.send_message(call.message.chat.id, f'Произошла ошибка: {eror}')
		return #Останавливаем выполнение функции
	#Приступим к обработке нажатия кнопок, т.к. у нас записано новое чило в callback_data, но начало одно и тоже
	#Нам нужно проверять есть ли в нажатой кнопке начало:
	if 'new_value_' in call.data: #В call.data содержиться callback_data
		new_value = int(call.data.replace('new_value_', '')) #С момощью этого кода мы получим число типа int, удалив из call.data не нужное нам
		#Грубыми словами с помощью replace мы заменяем new_value_ на ничего
		#Теперь давайте заменим значение в таблице по ID которое вписали изначально
		#Конектимся к базе данных:
		connection = db_work.connect_db()
		if connection: #Если конект успешен
			#Возпользуемся функцией edit_table из вспомогательного класса:
			new_value_request = 'UPDATE ankets SET age = %s WHERE id = %s'
			values = (new_value, int(id_value))
			status = db_work.edit_table(connection, new_value_request, values)
			if status: #Если успешно обновили данные
				await bot.send_message(call.message.chat.id, f'Данные для строки с ID: {id_value}\nУспешно обновлены на: {new_value}')
			else: #Если произошла ошибка
				await bot.send_message(call.message.chat.id, 'Произошла ошибка, она отобразилась в консоли!')
		else: #Если не удалось подключиться к базе данных
			await bot.send_message(call.message.chat.id, 'Не удалось подключиться к базе данных!')


if __name__ == "__main__": #Если скрипт запущен с этого файла, запускаем executor
	creat_table() #Запускаем функцию создания таблицы
	executor.start_polling(dp, skip_updates=True) #skip_updates=True - нужен для того чтобы не обрабатывать сообщения которые были присланы пользователем в тот момент когда бот был выключен

Я помню что не научил удалять данные с базы данных, но давайте будем честны, вы уже устали делать всё что выше, в следующем задании начнём с удаления данных!

Домашнее задание:
1. Добавить больше кнопок для изменения возраста
2. Создать функцию "creat_table_2" и создать в ней таблицу "users" в которую добавить столбцы:
1. id - как в функции "creat_table"
2. name - типа varchar длинной не более 32 символов
3. password - типа varchar длинной не более 20 символов
4. Возраст - типа int
Записывать в таблицу ничего не надо, просто создать её при запуске бота, как мы это сделали с "creat_table"

В обратной связи жду:
1. Скриншот кода с больший кол-вом кнопок
2. Скриншот кода функции "creat_table_2"
3. Скриншот с программы где видна новая таблица "users" и все столбца в ней

Выполнить ДЗ до 28.09.2023

Спасибо за внимание

С вами был @Xacker_Name_new

Поддержка: @Bsc_Black_Secret_Club_bot