Обновлены пути к библиотекам в Dockerfile для соответствия новой версии Python. Исправлены все тесты, теперь все проходят
118 lines
4.8 KiB
Markdown
118 lines
4.8 KiB
Markdown
---
|
||
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`
|