Files
ros_control/app/db.py
T
ayurishchevandClaude Opus 5.5 123b5abdfc Ревью кодовой базы и исправления корректности по его итогам
Ревью кодовой базы: docs/reviews/2026-09-27-codebase-review.md.

Корректность и согласованность, пункты 5–7 ревью (docs/changes/018):
- одиночное удаление бэкапа в UI идёт через общий delete_many: пометка
  deleted_at и событие backup.deleted, как у группового удаления и API;
- единая система миграций: ручные ALTER из db._migrate перенесены в
  migrations.run (при user_version < 1, до замены ID);
- групповая смена канала выполняется фоновыми задачами set_channel;
  PUT /api/v1/batch/channel → 202 {"job_ids": [...]} (ломающее изменение
  API), меню «Канал» в UI выводит задачи в панель «Задачи».

Тесты: 22 из 22. Стенд проверен на порту 8001 (8000 занят посторонним
процессом), боевые данные не изменены. Ручная проверка UI пользователем
на момент коммита не подтверждена.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 21:23:34 +03:00

48 lines
1.5 KiB
Python

from contextlib import contextmanager
from pathlib import Path
from sqlalchemy import create_engine
from sqlalchemy.orm import DeclarativeBase, sessionmaker
from app.config import get_settings
class Base(DeclarativeBase):
pass
_engine = None
_SessionLocal = None
def init_db(url: str | None = None) -> None:
"""Создаёт engine и таблицы. Вызывается при старте приложения (и в тестах)."""
global _engine, _SessionLocal
url = url or get_settings().database_url
if url.startswith("sqlite:///") and ":memory:" not in url:
Path(url.removeprefix("sqlite:///")).parent.mkdir(parents=True, exist_ok=True)
_engine = create_engine(url, connect_args={"check_same_thread": False})
_SessionLocal = sessionmaker(_engine, expire_on_commit=False)
from app import models # noqa: F401 (регистрация моделей)
Base.metadata.create_all(_engine) # у новой БД — все таблицы, у старой — только недостающие (events)
from app import migrations
db_file = Path(url.removeprefix("sqlite:///")) if url.startswith("sqlite:///") and ":memory:" not in url else None
migrations.run(_engine, db_file)
@contextmanager
def session_scope():
"""Короткая транзакция: commit при успехе, rollback при ошибке."""
session = _SessionLocal()
try:
yield session
session.commit()
except Exception:
session.rollback()
raise
finally:
session.close()