fix(crm): DB-Migrationen idempotent machen für Neuinstallationen
Bei Neuinstallation erzeugt create_all bereits das gesamte Schema,
so dass nachfolgende ALTER-Table-Migrationen fehlschlagen ("duplicate
column name"). V01_04 prüft nun die Spalten-Existenz per PRAGMA
table_info. checkupdates läuft wieder sequentiell ohne Überspringen.
Konvention in AGENTS.md dokumentiert.
Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
1 parent
efef212544
commit
44c411e5d7
2 files changed
+22
-3
No files matched your search
@@ -74,3 +74,8 @@ Bei neuen projektbezogenen Entitäten muss:
|
|||||||
|
|
||||||
## Generierte Dateien nicht beachten
|
## Generierte Dateien nicht beachten
|
||||||
Das Verzeichnis `Client/tsout/` enthält die JavaScript-Ausgabe der TypeScript-Kompilation und wird durch den Build-Prozess (tsc + rollup) erzeugt. Diese Dateien sind nicht manuell zu bearbeiten und brauchen bei der Codeanalyse nicht berücksichtigt zu werden. Ebenso sind die JavaScript-Dateien in `NGINX_Server/html/static/` (z.B. `kbp_crm_00.00.01.js`) die Ausgabe des Rollup-Builds und nicht zu untersuchen.
|
Das Verzeichnis `Client/tsout/` enthält die JavaScript-Ausgabe der TypeScript-Kompilation und wird durch den Build-Prozess (tsc + rollup) erzeugt. Diese Dateien sind nicht manuell zu bearbeiten und brauchen bei der Codeanalyse nicht berücksichtigt zu werden. Ebenso sind die JavaScript-Dateien in `NGINX_Server/html/static/` (z.B. `kbp_crm_00.00.01.js`) die Ausgabe des Rollup-Builds und nicht zu untersuchen.
|
||||||
|
|
||||||
|
## Vorgaben für DB-Migrationen (checkdb.py)
|
||||||
|
Die DB-Versionierung erfolgt über `_db_Vxx_xx`-Funktionen in `auth/model/checkdb.py` und `crm/model/checkdb.py`. Beim Start wird `checkupdates()` aufgerufen und führt alle Funktionen aus, deren Versionsnummer größer ist als die in der Systeminfo-Tabelle vermerkte. Bei einer Neuinstallation (leere Datenbank) erzeugt `_db_V01_00` über `create_all` bereits das **gesamte aktuelle Schema** aus den Models — inklusive aller Spalten, Tabellen und Indizes, die in späteren Migrationen erst nachträglich ergänzt wurden. Die inkrementellen Migrationen laufen danach dennoch sequentiell nach.
|
||||||
|
|
||||||
|
Daher gilt die Konvention: **Jede `_db_Vxx_xx`-Migration muss idempotent sein** — sie muss fehlerfrei durchlaufen, auch wenn das von ihr geänderte Schema-Element bereits durch `create_all` existiert. Insbesondere müssen `ALTER TABLE ADD COLUMN`-Anweisungen vorab prüfen, ob die Spalte bereits existiert (z.B. via `PRAGMA table_info`). `CREATE INDEX` verwendet `IF NOT EXISTS`. Daten-UPDATEs auf leeren Tabellen matchen nichts und sind somit unbedenklich. Migrationen, die Seed-Daten einfügen, müssen vorab prüfen, ob diese bereits vorhanden sind.
|
||||||
@@ -83,10 +83,19 @@ def _db_V01_04():
|
|||||||
Fügt die Spalte 'position' zur Tabelle 'kontaktadressen' hinzu.
|
Fügt die Spalte 'position' zur Tabelle 'kontaktadressen' hinzu.
|
||||||
Bei bestehenden Datenbanken, die mit Version 01.03 initialisiert wurden,
|
Bei bestehenden Datenbanken, die mit Version 01.03 initialisiert wurden,
|
||||||
fehlt diese Spalte, da das Feld erst nachträglich ergänzt wurde.
|
fehlt diese Spalte, da das Feld erst nachträglich ergänzt wurde.
|
||||||
|
|
||||||
|
Idempotent: Bei Neuinstallation hat create_all die Spalte bereits
|
||||||
|
angelegt — in diesem Fall wird das ALTER TABLE übersprungen.
|
||||||
"""
|
"""
|
||||||
from sqlalchemy import text
|
from sqlalchemy import text
|
||||||
|
|
||||||
session.execute(text("ALTER TABLE kontaktadressen ADD COLUMN position VARCHAR(200)"))
|
# Prüfen, ob die Spalte bereits existiert (Neuinstallation via create_all)
|
||||||
|
result = session.execute(text("PRAGMA table_info(kontaktadressen)"))
|
||||||
|
spalten = {row[1] for row in result}
|
||||||
|
if 'position' not in spalten:
|
||||||
|
session.execute(text(
|
||||||
|
"ALTER TABLE kontaktadressen ADD COLUMN position VARCHAR(200)"
|
||||||
|
))
|
||||||
_updatesysteminfo('01.04')
|
_updatesysteminfo('01.04')
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
@@ -217,7 +226,14 @@ def checkupdates():
|
|||||||
Ermittelt die aktuelle DB-Version aus der Systeminfo-Tabelle und führt alle
|
Ermittelt die aktuelle DB-Version aus der Systeminfo-Tabelle und führt alle
|
||||||
_db_Vxx_xx-Funktionen aus, deren Versionsnummer größer ist. Ist die
|
_db_Vxx_xx-Funktionen aus, deren Versionsnummer größer ist. Ist die
|
||||||
Datenbank noch leer (Tabelle fehlt), wird die Ersteinrichtung ausgeführt.
|
Datenbank noch leer (Tabelle fehlt), wird die Ersteinrichtung ausgeführt.
|
||||||
|
|
||||||
|
Bei einer Neuinstallation erzeugt _db_V01_00 über create_all bereits alle
|
||||||
|
Tabellen mit dem aktuellen Schema (inklusive aller später ergänzten Spalten,
|
||||||
|
Tabellen und Indizes). Die inkrementellen Migrationen laufen trotzdem
|
||||||
|
sequentiell nach — jede einzelne muss idempotent sein, damit sie auf der
|
||||||
|
frisch erzeugten DB fehlerfrei durchläuft (siehe Konvention in AGENTS.md).
|
||||||
"""
|
"""
|
||||||
|
versionen = sorted(filter(lambda f: f.startswith("_db_V"), globals()))
|
||||||
curr_dbversion = '_db_V01_00'
|
curr_dbversion = '_db_V01_00'
|
||||||
try:
|
try:
|
||||||
systeminfo: CrmSysteminfo = CrmSysteminfo.query.get(1)
|
systeminfo: CrmSysteminfo = CrmSysteminfo.query.get(1)
|
||||||
@@ -225,8 +241,6 @@ def checkupdates():
|
|||||||
except (OperationalError, AttributeError):
|
except (OperationalError, AttributeError):
|
||||||
_db_V01_00()
|
_db_V01_00()
|
||||||
|
|
||||||
versionen = sorted(filter(lambda f: f.startswith("_db_V"), globals()))
|
|
||||||
|
|
||||||
for v in versionen:
|
for v in versionen:
|
||||||
if v > curr_dbversion:
|
if v > curr_dbversion:
|
||||||
globals()[v]()
|
globals()[v]()
|
||||||
|
|||||||
Reference in new issue
Block a user