79 lines
2.9 KiB
Python
79 lines
2.9 KiB
Python
"""
|
|
Migrazione 0017: Tassonomia di Classificazione Multi-livello (Feature N2).
|
|
|
|
Estende la tabella `labels` con:
|
|
- parent_id: FK self-referenziale (nullable) per struttura ad albero
|
|
- description: testo descrittivo opzionale
|
|
|
|
Struttura tassonomica:
|
|
Livello 0 (radice) = Ambito (Area Aziendale) — parent_id IS NULL
|
|
Livello 1 = Processo — parent_id = ID Ambito
|
|
Livello 2 (foglia) = Classificazione — parent_id = ID Processo
|
|
|
|
I vincoli di unicità vengono sostituiti con indici parziali per supportare
|
|
nomi identici a diversi livelli dell'albero.
|
|
"""
|
|
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
from sqlalchemy.dialects import postgresql
|
|
|
|
revision = "0017"
|
|
down_revision = "0016"
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
# ── Aggiunge parent_id (FK self-referenziale nullable) ─────────────────────
|
|
op.add_column(
|
|
"labels",
|
|
sa.Column(
|
|
"parent_id",
|
|
postgresql.UUID(as_uuid=True),
|
|
nullable=True,
|
|
),
|
|
)
|
|
op.create_foreign_key(
|
|
"fk_labels_parent",
|
|
"labels",
|
|
"labels",
|
|
["parent_id"],
|
|
["id"],
|
|
ondelete="CASCADE",
|
|
)
|
|
|
|
# ── Aggiunge description ────────────────────────────────────────────────────
|
|
op.add_column(
|
|
"labels",
|
|
sa.Column("description", sa.Text(), nullable=True),
|
|
)
|
|
|
|
# ── Indice su parent_id per query gerarchiche ───────────────────────────────
|
|
op.create_index("idx_labels_parent", "labels", ["parent_id"])
|
|
|
|
# ── Sostituisce il vincolo di unicità con indici parziali ───────────────────
|
|
# Il vecchio vincolo (tenant_id, name) non supporta nomi uguali a livelli diversi
|
|
op.drop_constraint("uq_label_name_tenant", "labels")
|
|
|
|
# Nodi radice: nome unico per tenant (parent_id IS NULL)
|
|
op.execute(
|
|
"CREATE UNIQUE INDEX uq_label_name_root "
|
|
"ON labels (tenant_id, name) WHERE parent_id IS NULL"
|
|
)
|
|
# Nodi non-radice: nome unico per (tenant, parent) — stesso nome ammesso sotto parent diversi
|
|
op.execute(
|
|
"CREATE UNIQUE INDEX uq_label_name_parent "
|
|
"ON labels (tenant_id, name, parent_id) WHERE parent_id IS NOT NULL"
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.execute("DROP INDEX IF EXISTS uq_label_name_parent")
|
|
op.execute("DROP INDEX IF EXISTS uq_label_name_root")
|
|
op.create_unique_constraint("uq_label_name_tenant", "labels", ["tenant_id", "name"])
|
|
op.drop_index("idx_labels_parent", table_name="labels")
|
|
op.drop_constraint("fk_labels_parent", "labels", type_="foreignkey")
|
|
op.drop_column("labels", "description")
|
|
op.drop_column("labels", "parent_id")
|