mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-01-20 03:20:25 -06:00
fix: make migration 075 idempotent for existing link_templates
Skip creating link_templates when it already exists and only create missing indexes, so partially-migrated customer databases can continue upgrading.
This commit is contained in:
@@ -27,6 +27,22 @@ def _has_column(inspector, table_name: str, column_name: str) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def _has_table(inspector, table_name: str) -> bool:
|
||||
"""Check if a table exists"""
|
||||
try:
|
||||
return table_name in inspector.get_table_names()
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def _has_index(inspector, table_name: str, index_name: str) -> bool:
|
||||
"""Check if an index exists on a table"""
|
||||
try:
|
||||
return any((idx.get("name") or "") == index_name for idx in inspector.get_indexes(table_name))
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def upgrade():
|
||||
"""Add custom_fields to clients and create link_templates table"""
|
||||
bind = op.get_bind()
|
||||
@@ -35,30 +51,44 @@ def upgrade():
|
||||
bool_true_default = '1' if dialect_name == 'sqlite' else ('true' if dialect_name == 'postgresql' else '1')
|
||||
|
||||
# Add custom_fields column to clients table if it doesn't exist
|
||||
if 'clients' in inspector.get_table_names():
|
||||
if _has_table(inspector, 'clients'):
|
||||
if not _has_column(inspector, 'clients', 'custom_fields'):
|
||||
# Use portable JSON type for cross-db compatibility (SQLite + PostgreSQL).
|
||||
op.add_column('clients', sa.Column('custom_fields', sa.JSON(), nullable=True))
|
||||
|
||||
# Create link_templates table
|
||||
op.create_table(
|
||||
'link_templates',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=200), nullable=False),
|
||||
sa.Column('description', sa.Text(), nullable=True),
|
||||
sa.Column('url_template', sa.String(length=1000), nullable=False),
|
||||
sa.Column('icon', sa.String(length=50), nullable=True),
|
||||
sa.Column('field_key', sa.String(length=100), nullable=False),
|
||||
sa.Column('is_active', sa.Boolean(), nullable=False, server_default=sa.text(bool_true_default)),
|
||||
sa.Column('order', sa.Integer(), nullable=False, server_default='0'),
|
||||
sa.Column('created_by', sa.Integer(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['created_by'], ['users.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index('idx_link_templates_is_active', 'link_templates', ['is_active'])
|
||||
op.create_index('idx_link_templates_field_key', 'link_templates', ['field_key'])
|
||||
# Create link_templates table (idempotent; some installs may already have it)
|
||||
if not _has_table(inspector, 'link_templates'):
|
||||
op.create_table(
|
||||
'link_templates',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=200), nullable=False),
|
||||
sa.Column('description', sa.Text(), nullable=True),
|
||||
sa.Column('url_template', sa.String(length=1000), nullable=False),
|
||||
sa.Column('icon', sa.String(length=50), nullable=True),
|
||||
sa.Column('field_key', sa.String(length=100), nullable=False),
|
||||
sa.Column('is_active', sa.Boolean(), nullable=False, server_default=sa.text(bool_true_default)),
|
||||
sa.Column('order', sa.Integer(), nullable=False, server_default='0'),
|
||||
sa.Column('created_by', sa.Integer(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['created_by'], ['users.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
else:
|
||||
print("[Migration 075] ℹ Table link_templates already exists, skipping creation")
|
||||
|
||||
# Ensure indexes exist (best-effort / idempotent)
|
||||
if _has_table(inspector, 'link_templates'):
|
||||
if not _has_index(inspector, 'link_templates', 'idx_link_templates_is_active'):
|
||||
try:
|
||||
op.create_index('idx_link_templates_is_active', 'link_templates', ['is_active'])
|
||||
except Exception:
|
||||
pass
|
||||
if not _has_index(inspector, 'link_templates', 'idx_link_templates_field_key'):
|
||||
try:
|
||||
op.create_index('idx_link_templates_field_key', 'link_templates', ['field_key'])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def downgrade():
|
||||
@@ -67,13 +97,13 @@ def downgrade():
|
||||
inspector = sa.inspect(bind)
|
||||
|
||||
# Drop link_templates table
|
||||
if 'link_templates' in inspector.get_table_names():
|
||||
if _has_table(inspector, 'link_templates'):
|
||||
op.drop_index('idx_link_templates_field_key', table_name='link_templates')
|
||||
op.drop_index('idx_link_templates_is_active', table_name='link_templates')
|
||||
op.drop_table('link_templates')
|
||||
|
||||
# Remove custom_fields column from clients table
|
||||
if 'clients' in inspector.get_table_names():
|
||||
if _has_table(inspector, 'clients'):
|
||||
if _has_column(inspector, 'clients', 'custom_fields'):
|
||||
op.drop_column('clients', 'custom_fields')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user