Двухфазный загрузчик: валидация yaml целиком до записи (SeedError), затем upsert по натуральному ключу без удалений; run_seed(session, *, dev, seed_dir) без commit, CLI коммитит. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
70 lines
2.1 KiB
Python
70 lines
2.1 KiB
Python
"""Модель проверенного набора seed и отчёта о применении (без доступа к БД)."""
|
||
|
||
from dataclasses import dataclass, field
|
||
|
||
from src.models.enums import PermissionAction, PermissionObject
|
||
|
||
|
||
class SeedError(Exception):
|
||
"""Ошибка файлов seed (отсутствие, формат, ссылочная целостность); сообщение — по-русски, с файлом и записью."""
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class RegionalOfficeSeed:
|
||
name: str
|
||
is_head_office: bool
|
||
is_active: bool
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class RoleSeed:
|
||
code: str
|
||
name: str
|
||
description: str | None
|
||
is_system: bool
|
||
is_active: bool
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class PermissionSeed:
|
||
code: str
|
||
object_type: PermissionObject
|
||
action: PermissionAction
|
||
name: str
|
||
description: str | None
|
||
|
||
|
||
@dataclass
|
||
class SeedData:
|
||
"""Проверенный набор seed; ничего не знает о БД."""
|
||
|
||
regional_offices: list[RegionalOfficeSeed] = field(default_factory=list)
|
||
roles: list[RoleSeed] = field(default_factory=list)
|
||
permissions: list[PermissionSeed] = field(default_factory=list)
|
||
#: код роли -> коды прав (порядок как в файле)
|
||
role_permissions: dict[str, list[str]] = field(default_factory=dict)
|
||
|
||
|
||
@dataclass
|
||
class TableStat:
|
||
inserted: int = 0
|
||
updated: int = 0
|
||
unchanged: int = 0
|
||
|
||
|
||
@dataclass
|
||
class SeedReport:
|
||
"""Итог применения: по таблицам — сколько строк вставлено / обновлено / не изменилось."""
|
||
|
||
tables: dict[str, TableStat] = field(default_factory=dict)
|
||
|
||
def stat(self, table: str) -> TableStat:
|
||
return self.tables.setdefault(table, TableStat())
|
||
|
||
def format(self) -> str:
|
||
lines = ["Seed применён (вставлено / обновлено / без изменений):"]
|
||
lines += [
|
||
f" {name}: {stat.inserted} / {stat.updated} / {stat.unchanged}" for name, stat in self.tables.items()
|
||
]
|
||
return "\n".join(lines)
|