--- description: "Паттерны работы с базой данных, репозитории и модели" globs: ["database/**/*.py", "**/repositories/*.py"] --- # Паттерны работы с базой данных ## Repository Pattern Все операции с БД выполняются через репозитории. Каждый репозиторий: - Наследуется от `DatabaseConnection` из `database/base.py` - Работает с одной сущностью (User, Post, Blacklist, etc.) - Содержит методы для CRUD операций - Использует асинхронные методы `_execute_query()` и `_execute_query_with_result()` ### Структура репозитория ```python from database.base import DatabaseConnection from database.models import User class UserRepository(DatabaseConnection): """Репозиторий для работы с пользователями.""" async def create_tables(self): """Создание таблицы пользователей.""" query = ''' CREATE TABLE IF NOT EXISTS our_users ( user_id INTEGER NOT NULL PRIMARY KEY, ... ) ''' await self._execute_query(query) self.logger.info("Таблица пользователей создана") async def add_user(self, user: User) -> None: """Добавление нового пользователя.""" query = "INSERT OR IGNORE INTO our_users (...) VALUES (...)" params = (...) await self._execute_query(query, params) self.logger.info(f"Пользователь добавлен: {user.user_id}") async def get_user_by_id(self, user_id: int) -> Optional[User]: """Получение пользователя по ID.""" query = "SELECT * FROM our_users WHERE user_id = ?" rows = await self._execute_query_with_result(query, (user_id,)) # Преобразование row в модель User ... ``` ## Модели данных - Модели определены в `database/models.py` - Используются dataclasses или простые классы - Модели передаются между слоями (Repository → Service → Handler) ## Работа с БД ### AsyncBotDB - Основной интерфейс для работы с БД - Использует `RepositoryFactory` для доступа к репозиториям - Методы делегируют вызовы соответствующим репозиториям ### DatabaseConnection - Базовый класс для всех репозиториев - Предоставляет методы: - `_get_connection()` - получение соединения - `_execute_query()` - выполнение запроса без результата - `_execute_query_with_result()` - выполнение запроса с результатом - Автоматически управляет соединениями (открытие/закрытие) - Настраивает PRAGMA для оптимизации SQLite ### Важные правила 1. **Всегда используйте параметризованные запросы** для защиты от SQL injection: ```python # ✅ Правильно query = "SELECT * FROM users WHERE user_id = ?" await self._execute_query_with_result(query, (user_id,)) # ❌ Неправильно query = f"SELECT * FROM users WHERE user_id = {user_id}" ``` 2. **Логируйте важные операции**: ```python self.logger.info(f"Пользователь добавлен: {user_id}") self.logger.error(f"Ошибка при добавлении пользователя: {e}") ``` 3. **Используйте транзакции** для множественных операций (если нужно): ```python async with await self._get_connection() as conn: await conn.execute(...) await conn.execute(...) await conn.commit() ``` 4. **Обрабатывайте None** при получении данных: ```python rows = await self._execute_query_with_result(query, params) if not rows: return None row = rows[0] ``` ## RepositoryFactory - Создает и кэширует экземпляры репозиториев - Доступ через свойства: `factory.users`, `factory.posts`, etc. - Используется в `AsyncBotDB` для доступа к репозиториям ## Миграции - SQL миграции в `database/schema.sql` - Python скрипты для миграций в `scripts/` - Всегда проверяйте существование таблиц перед созданием: `CREATE TABLE IF NOT EXISTS`