В статье разберём хранение данных для бота: пользователи, заказы, теги для рассылок. Примеры на SQLite (простой старт) и PostgreSQL (масштаб и надёжность).
Какие таблицы нужны
Типичный минимум: users (user_id, username, дата первого визита, при необходимости тег или сегмент), orders (id, user_id, сумма, статус, дата), при рассылках — таблица тегов (user_id, tag). Состояния FSM aiogram можно хранить в памяти или в Redis/БД — в примерах ниже только пользователи и заказы.
SQLite
Один файл на диске, отдельный сервер БД не нужен. Удобно для прототипов и небольших ботов. Подключение через встроенный sqlite3 или aiosqlite для async.
Пример: создание таблицы пользователей и сохранение при /start. Для конкурентной записи в async-боте лучше использовать aiosqlite или вызывать save_user в отдельном потоке.
import sqlite3
conn = sqlite3.connect("bot.db")
conn.execute("""
CREATE TABLE IF NOT EXISTS users (
user_id INTEGER PRIMARY KEY,
username TEXT,
first_seen TEXT DEFAULT CURRENT_TIMESTAMP
)
""")
conn.commit()
def save_user(user_id: int, username: str = None):
conn.execute(
"INSERT OR IGNORE INTO users (user_id, username) VALUES (?, ?)",
(user_id, username),
)
conn.commit()
# В хендлере /start:
# save_user(message.from_user.id, message.from_user.username)
PostgreSQL (asyncpg)
Для большого числа пользователей и сложных запросов лучше PostgreSQL: транзакции, бэкапы, надёжность. Подключение через пул asyncpg. Пример пула и двух функций — получить пользователя и добавить заказ:
import asyncpg
# При старте бота: pool = await asyncpg.create_pool("postgresql://user:pass@localhost/botdb", min_size=1, max_size=10)
async def get_user(user_id: int):
async with pool.acquire() as conn:
row = await conn.fetchrow("SELECT * FROM users WHERE user_id = $1", user_id)
return dict(row) if row else None
async def add_order(user_id: int, amount: int, status: str = "new"):
async with pool.acquire() as conn:
await conn.execute(
"INSERT INTO orders (user_id, amount, status) VALUES ($1, $2, $3)",
user_id, amount, status,
)
Таблицы создаются отдельно (миграциями или вручную). Пример схемы для orders: CREATE TABLE orders (id SERIAL PRIMARY KEY, user_id BIGINT NOT NULL, amount INTEGER NOT NULL, status VARCHAR(50), created_at TIMESTAMPTZ DEFAULT NOW());
Миграции
При изменении схемы (новые поля, таблицы) не меняйте БД «вручную» на проде без плана. Фиксируйте изменения в скриптах миграций (например, папка migrations/, файлы 001_create_users.sql, 002_add_orders.sql) и применяйте их по порядку. Так проще откатываться и держать dev/prod в одном состоянии.
Итоги
SQLite — для старта и небольших ботов; PostgreSQL — для масштаба и продакшена. Продумайте схему (users, orders, теги) и ведите миграции при любых изменениях структуры.