fix(migrations): F19 (Astra P1) — deterministische vollstaendige Model-Discovery fuer Alembic

Vorher: alembic/env.py importierte nur from app.models import * — das
laedt im frischen Prozess nur die 48 CORE-Modelle. Contact und ~80
weitere Tabellen liegen physikalisch in Plugins (lazy __getattr__ feuert
bei Wildcard-Import nie). Metadatensortierung scheiterte an
contact_merge_history -> contacts (NoReferencedTableError, Astra-Repro);
alembic check haette gegen ein unvollstaendiges Schema verglichen.

Fix: deterministische Plugin-Model-Discovery in env.py — gleiches
Muster wie tests/conftest.py: Registry discover_builtins, dann pro
Plugin das models-Modul importieren (ImportError = kein models-Modul,
bewusst uebersprungen). Side-effect-frei (nur Modell-Registrierung,
kein DB-Zugriff).

Beweis: frischer Prozess laedt jetzt 129 Tabellen, Sortierung OK
(Vorher: 48 + NoReferencedTableError). Bekannt und separat offen: der
contacts/contactpersons-FK-Zyklus (SAWarning, dokumentiert) und der
entity_attachments.dms_file_id-FK auf die DMS-Tabelle (R3).

Verifikation: Syntax OK, ruff clean.
This commit is contained in:
Agent Zero
2026-09-18 12:58:31 +02:00
parent 2f5af6e192
commit 49a9493ca0
+18
View File
@@ -3,6 +3,15 @@
from __future__ import annotations
import asyncio
# F19 (Astra P1): deterministic full-model discovery for Alembic.
# `from app.models import *` only loads CORE models (48 tables in a fresh
# process). Contact and ~80 other tables physically live in plugins
# (e.g. app.plugins.builtins.contacts.models) — the lazy package
# __getattr__ never fires for wildcard imports. Without the plugin models
# the metadata sort fails (contact_merge_history → contacts FK) and
# `alembic check` compares against an incomplete schema.
import importlib
from logging.config import fileConfig
from sqlalchemy import pool
@@ -13,6 +22,15 @@ from alembic import context
from app.config import get_settings
from app.core.db import Base
from app.models import * # noqa: F401,F403
from app.plugins.registry import get_registry
_registry = get_registry()
_registry.discover_builtins()
for _plugin_name in _registry.list_discovered():
try:
importlib.import_module(f"app.plugins.builtins.{_plugin_name}.models")
except ImportError:
pass # plugin has no models module
config = context.config
if config.config_file_name is not None: