refactor(migration): backfill all missing columns in ORM models
This migration updates the database schema by automatically adding all missing columns from the ORM models to the existing tables. It replaces the previous specific addition of 'tts_audio_urls' with a more comprehensive approach that inspects the ORM metadata and synchronizes it with the database schema. The downgrade function has been simplified to a no-op.
This commit is contained in:
@@ -1,51 +1,66 @@
|
||||
"""补建缺失列:segments.tts_audio_urls, conversation_messages.tts_audio_urls
|
||||
"""补建所有 ORM 模型中存在但数据库缺失的列
|
||||
|
||||
0001 用 create_all 建表,对已有表不会 ALTER 追加列。
|
||||
本迁移补齐 ORM 模型中存在但生产库缺失的 tts_audio_urls 列。
|
||||
本迁移自动内省 ORM 元数据与实际数据库 schema 的差异,
|
||||
为每张已有表补齐缺失列(不含 FK 约束,仅补列定义)。
|
||||
|
||||
Revision ID: 0020_add_tts_audio_urls_column
|
||||
Revision ID: 0020_backfill_all_missing_columns
|
||||
Revises: 0019_backfill_missing_columns
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision: str = "0020_add_tts_audio_urls_column"
|
||||
revision: str = "0020_backfill_all_missing_columns"
|
||||
down_revision: Union[str, None] = "0019_backfill_missing_columns"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def _column_names(table_name: str) -> set[str]:
|
||||
bind = op.get_bind()
|
||||
inspector = sa.inspect(bind)
|
||||
return {column["name"] for column in inspector.get_columns(table_name)}
|
||||
def _import_all_models() -> None:
|
||||
from app.features.asset import models as _asset # noqa: F401
|
||||
from app.features.auth import models as _auth # noqa: F401
|
||||
from app.features.conversation import models as _conv # noqa: F401
|
||||
from app.features.evaluation import models as _eval # noqa: F401
|
||||
from app.features.memory import models as _memory # noqa: F401
|
||||
from app.features.memoir import models as _memoir # noqa: F401
|
||||
from app.features.payment import models as _payment # noqa: F401
|
||||
from app.features.story import models as _story # noqa: F401
|
||||
from app.features.user import models as _user # noqa: F401
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
seg_cols = _column_names("segments")
|
||||
if "tts_audio_urls" not in seg_cols:
|
||||
op.add_column(
|
||||
"segments",
|
||||
sa.Column("tts_audio_urls", sa.JSON(), nullable=True),
|
||||
)
|
||||
from app.core.db import Base
|
||||
|
||||
msg_cols = _column_names("conversation_messages")
|
||||
if "tts_audio_urls" not in msg_cols:
|
||||
op.add_column(
|
||||
"conversation_messages",
|
||||
sa.Column("tts_audio_urls", sa.JSON(), nullable=True),
|
||||
)
|
||||
_import_all_models()
|
||||
|
||||
bind = op.get_bind()
|
||||
inspector = sa.inspect(bind)
|
||||
existing_tables = set(inspector.get_table_names())
|
||||
|
||||
for table in Base.metadata.sorted_tables:
|
||||
if table.name not in existing_tables:
|
||||
continue
|
||||
|
||||
db_cols = {c["name"] for c in inspector.get_columns(table.name)}
|
||||
|
||||
for col in table.columns:
|
||||
if col.name in db_cols:
|
||||
continue
|
||||
|
||||
kwargs: dict = {"nullable": True}
|
||||
|
||||
if col.server_default is not None:
|
||||
kwargs["server_default"] = col.server_default.arg
|
||||
|
||||
new_col = sa.Column(col.name, col.type, **kwargs)
|
||||
op.add_column(table.name, new_col)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
msg_cols = _column_names("conversation_messages")
|
||||
if "tts_audio_urls" in msg_cols:
|
||||
op.drop_column("conversation_messages", "tts_audio_urls")
|
||||
|
||||
seg_cols = _column_names("segments")
|
||||
if "tts_audio_urls" in seg_cols:
|
||||
op.drop_column("segments", "tts_audio_urls")
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user