fix: Auto-Migrate fuer fehlende DB-Spalten beim App-Start
Behebt 500 Internal Server Error beim Erstellen von Share-Links auf bestehenden Datenbanken, denen die neue 'permission'-Spalte fehlt. Problem: db.create_all() erstellt nur neue Tabellen, fuegt aber keine Spalten zu bestehenden Tabellen hinzu. Wenn das Model neue Spalten bekommt, crasht die App auf alten Datenbanken. Loesung: _auto_migrate() vergleicht beim Start jede Model-Definition mit dem tatsaechlichen DB-Schema (via SQLAlchemy Inspector) und fuegt fehlende Spalten per ALTER TABLE ADD COLUMN hinzu. Funktioniert fuer alle Datentypen mit korrekten Defaults (VARCHAR, INTEGER, BOOLEAN, etc.). Output im Log: [Auto-Migrate] Added column share_links.permission (VARCHAR(20)) Das macht die App update-sicher - neue Spalten in Models werden automatisch zur bestehenden DB hinzugefuegt, ohne manuelles Migrieren. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+56
-1
@@ -8,6 +8,58 @@ from app.config import Config
|
|||||||
from app.extensions import db, bcrypt, migrate
|
from app.extensions import db, bcrypt, migrate
|
||||||
|
|
||||||
|
|
||||||
|
def _auto_migrate(db):
|
||||||
|
"""Add missing columns to existing tables by comparing model definitions
|
||||||
|
with actual database schema. This handles the case where new columns are
|
||||||
|
added to models but db.create_all() only creates new tables."""
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
|
with db.engine.connect() as conn:
|
||||||
|
inspector = sqlalchemy.inspect(db.engine)
|
||||||
|
|
||||||
|
for table_name, table in db.metadata.tables.items():
|
||||||
|
if not inspector.has_table(table_name):
|
||||||
|
continue
|
||||||
|
|
||||||
|
existing_cols = {col['name'] for col in inspector.get_columns(table_name)}
|
||||||
|
|
||||||
|
for column in table.columns:
|
||||||
|
if column.name not in existing_cols:
|
||||||
|
# Determine SQL type
|
||||||
|
col_type = column.type.compile(db.engine.dialect)
|
||||||
|
|
||||||
|
# Determine default value
|
||||||
|
default = ''
|
||||||
|
if column.default is not None:
|
||||||
|
if hasattr(column.default, 'arg'):
|
||||||
|
val = column.default.arg
|
||||||
|
if callable(val):
|
||||||
|
default = ''
|
||||||
|
elif isinstance(val, str):
|
||||||
|
default = f" DEFAULT '{val}'"
|
||||||
|
elif isinstance(val, bool):
|
||||||
|
default = f" DEFAULT {1 if val else 0}"
|
||||||
|
elif isinstance(val, (int, float)):
|
||||||
|
default = f" DEFAULT {val}"
|
||||||
|
elif column.nullable:
|
||||||
|
default = ' DEFAULT NULL'
|
||||||
|
elif col_type.upper().startswith(('VARCHAR', 'TEXT')):
|
||||||
|
default = " DEFAULT ''"
|
||||||
|
elif col_type.upper().startswith(('INTEGER', 'BIGINT', 'FLOAT')):
|
||||||
|
default = " DEFAULT 0"
|
||||||
|
elif col_type.upper() == 'BOOLEAN':
|
||||||
|
default = " DEFAULT 0"
|
||||||
|
|
||||||
|
sql = f'ALTER TABLE "{table_name}" ADD COLUMN "{column.name}" {col_type}{default}'
|
||||||
|
try:
|
||||||
|
conn.execute(db.text(sql))
|
||||||
|
print(f'[Auto-Migrate] Added column {table_name}.{column.name} ({col_type})')
|
||||||
|
except Exception as e:
|
||||||
|
print(f'[Auto-Migrate] Failed to add {table_name}.{column.name}: {e}')
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
|
||||||
def create_app(config_class=Config):
|
def create_app(config_class=Config):
|
||||||
# Check if static frontend build exists (Docker production mode)
|
# Check if static frontend build exists (Docker production mode)
|
||||||
static_dir = Path(__file__).resolve().parent.parent / 'static'
|
static_dir = Path(__file__).resolve().parent.parent / 'static'
|
||||||
@@ -61,11 +113,14 @@ def create_app(config_class=Config):
|
|||||||
def serve_spa(e):
|
def serve_spa(e):
|
||||||
return send_from_directory(str(static_dir), 'index.html')
|
return send_from_directory(str(static_dir), 'index.html')
|
||||||
|
|
||||||
# Create tables
|
# Create tables + auto-migrate missing columns
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
from app import models # noqa: F401
|
from app import models # noqa: F401
|
||||||
db.create_all()
|
db.create_all()
|
||||||
|
|
||||||
|
# Auto-migrate: add missing columns to existing tables
|
||||||
|
_auto_migrate(db)
|
||||||
|
|
||||||
# Enable WAL mode for SQLite
|
# Enable WAL mode for SQLite
|
||||||
with db.engine.connect() as conn:
|
with db.engine.connect() as conn:
|
||||||
conn.execute(db.text('PRAGMA journal_mode=WAL'))
|
conn.execute(db.text('PRAGMA journal_mode=WAL'))
|
||||||
|
|||||||
Reference in New Issue
Block a user