P1-1 [context_builder] build_context 共享 session,切片函数传 db 参数,
回测 20 场并发连接需求从 100+ 降至每场 1 个
P1-2 [bzzoiro] 预加载改为按 raw_events 日期范围 ±30 天按需加载
P1-3 [understat] 批量查询球队 + 比赛,从 1140 次往返降到 3 次
P1-4 [injuries] 批量幂等检查 + 分批 flush,IntegrityError 逐条回退
P1-5 [predict] 删除 threading.Lock,dict 操作原子无需同步锁
P1-6 [models] 添加 (match_id, provider, model) 唯一约束 + 迁移
P2-1 [unit_of_work] get_uow 返回类型改为 AsyncIterator[AsyncSession]
P2-2 [normalize] _parse_date 失败时记录 warning 避免静默丢数据
P2-3 [repositories] find_by_teams_and_date 改用 match_date_date 等值匹配
P2-4 [migration] 幽灵列 cutoff_at 已在 0006 迁移删除(已有)
P2-5 [migration] injuries 约束命名对齐 ORM,UniqueConstraint → 唯一索引
82 lines
2.5 KiB
Python
82 lines
2.5 KiB
Python
"""修复 injuries 表约束命名与 ORM 声明不一致
|
|
|
|
Revision ID: 0007_injuries_constraint_naming_align
|
|
Revises: 0006_schema_model_drift_cleanup
|
|
Create Date: 2026-09-16
|
|
|
|
背景(见代码审查报告 P2-5):
|
|
0003 迁移使用 sa.UniqueConstraint 创建唯一约束,
|
|
而 ORM models.py 中声明为 Index(..., unique=True)。
|
|
虽然 PostgreSQL 中两者效果相同(都保证唯一性),
|
|
但 pg_catalog 中表示不同,会导致:
|
|
- alembic autogenerate 持续报告漂移
|
|
- 约束命名约定不一致(uc_ 前缀 vs ix_ 前缀)
|
|
|
|
本迁移将 UniqueConstraint 替换为唯一索引,与 ORM 声明对齐。
|
|
"""
|
|
|
|
from typing import Sequence, Union
|
|
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
|
|
# revision identifiers, used by Alembic.
|
|
revision: str = '0007_injuries_constraint_naming_align'
|
|
down_revision: Union[str, None] = '0006_schema_model_drift_cleanup'
|
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
depends_on: Union[str, Sequence[str], None] = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
bind = op.get_bind()
|
|
inspector = sa.inspect(bind)
|
|
|
|
# 检查当前约束类型
|
|
constraints = {
|
|
c["name"]: c
|
|
for c in inspector.get_unique_constraints("injuries")
|
|
}
|
|
indexes = {
|
|
i["name"]: i
|
|
for i in inspector.get_indexes("injuries")
|
|
}
|
|
|
|
# 如果存在 UniqueConstraint 形式的 ix_injuries_player_fixture,替换为唯一索引
|
|
if "ix_injuries_player_fixture" in constraints:
|
|
# 删除唯一约束
|
|
op.drop_constraint("ix_injuries_player_fixture", "injuries", type_="unique")
|
|
|
|
# 如果不存在同名唯一索引,创建它(与 ORM 声明一致)
|
|
if "ix_injuries_player_fixture" not in indexes:
|
|
op.create_index(
|
|
"ix_injuries_player_fixture",
|
|
"injuries",
|
|
["player_id", "fixture_id", "injury_type"],
|
|
unique=True,
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
bind = op.get_bind()
|
|
inspector = sa.inspect(bind)
|
|
|
|
indexes = {
|
|
i["name"]: i
|
|
for i in inspector.get_indexes("injuries")
|
|
}
|
|
constraints = {
|
|
c["name"]: c
|
|
for c in inspector.get_unique_constraints("injuries")
|
|
}
|
|
|
|
# 恢复为 UniqueConstraint 形式
|
|
if "ix_injuries_player_fixture" in indexes:
|
|
op.drop_index("ix_injuries_player_fixture", table_name="injuries")
|
|
|
|
if "ix_injuries_player_fixture" not in constraints:
|
|
op.create_unique_constraint(
|
|
"ix_injuries_player_fixture",
|
|
"injuries",
|
|
["player_id", "fixture_id", "injury_type"],
|
|
)
|