"""Архив вместо удаления: deleted_at на источниках/типах/целях/правилах
Revision ID: b7d3e8f04a92
Revises: e2f6a9b3c4d7
Create Date: 2026-10-03
"""
from alembic import op
import sqlalchemy as sa
revision = "b7d3e8f04a92"
down_revision = "e2f6a9b3c4d7"
branch_labels = None
depends_on = None
TABLES = ("sources", "notification_types", "channel_targets", "routing_rules")
# Частичные индексы: живые строки (архив исключён) — уникальность/подчистка
# имен живых записей делает код, индекс ускоряет списки и дабл-чеки.
# Для notification_types — составной (source_id, subject, action) — дабл-чек
# create_type ищет по тройке; subject/action в индексе.
PARTIAL = {
"sources": ("ix_sources_live", ["name"]),
"notification_types": ("ix_ntypes_live", ["source_id", "subject", "action"]),
"channel_targets": ("ix_targets_live", ["channel", "name"]),
"routing_rules": ("ix_rules_live", ["enabled"]),
}
def upgrade() -> None:
for table in TABLES:
op.add_column(
table,
sa.Column("deleted_at", sa.DateTime(timezone=True), nullable=True),
)
for table, (iname, cols) in PARTIAL.items():
op.create_index(
iname,
table,
cols,
unique=False,
postgresql_where=sa.text("deleted_at IS NULL"),
)
def downgrade() -> None:
for table, (iname, _cols) in PARTIAL.items():
op.drop_index(iname, table)
for table in TABLES:
op.drop_column(table, "deleted_at")