Alembic – 复合主键导致MySQL的表定义不正确

huangapple go评论90阅读模式
英文:

Alembic - composite primary key results in incorrect table definition for MySQL

问题

我有多个“有版本记录”的数据库SQLAlchemy模型,它们使用复合主键,通过组合自增整数字段("id")和日期时间字段("record_valid_from")来实现。我试图在本地的Docker容器中运行针对MySQL数据库的这个模型定义。

模型定义大致如下:

from sqlalchemy.orm import (DeclarativeBase, Mapped)

class classA(DeclarativeBase):
    id: Mapped[int] = mapped_column(primary_key=True, index=True, autoincrement=True)
    record_valid_from: Mapped[datetime] = mapped_column(DateTime,
        primary_key=True,
        default=get_current_timestamp # 这是一个返回datetime.now()的Python方法
    )
    active: Mapped[bool] = mapped_column(Boolean, default=True,
        comment="如果是最新版本则为TRUE,否则为FALSE"
    )
    ... # 其他一些字段和逻辑

其他模型类似,它们之间有各种不同的关系。

当使用Alembic自动生成迁移脚本(alembic revision --autogenerate -m "init database")时,生成的Python代码似乎会产生无效的SQL。

更具体地说,我遇到了以下问题:

(pymysql.err.OperationalError) (1075, '不正确的表定义;只能有一个自动列,并且它必须定义为键')

这是迁移代码(注意:我稍微简化了它):

def upgrade() -> None:
    op.create_table('classA',
        sa.Column('name', sa.String(length=100), nullable=False),
        sa.Column('record_valid_from', sa.DateTime(), nullable=False),
        sa.Column('active', sa.Boolean(), nullable=False),
        sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
        sa.PrimaryKeyConstraint('record_valid_from', 'id')
    )
    op.create_index(op.f('ix_classA_id'), 'classA', ['id'], unique=False)

有没有人遇到类似的情况,或者知道如何解决这个问题?

我尝试过的方法:

  • 在创建表之后调用op.create_primary_key(参见:https://alembic.sqlalchemy.org/en/latest/ops.html#alembic.operations.Operations.create_primary_key)。结果:sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (1068, '多个主键已定义')
  • 删除sa.PrimaryKeyConstraint,然后直接调用op.create_primary_key。结果:
    • 迁移正常工作
    • 尝试创建新的ORM模型会导致:sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (1364, "字段'id'没有默认值")
英文:

I have multiple "versioned" database SQLALchemy models that use a composite primary key by combining an auto-increment int field ("id") and a datetime field ("record_valid_from"). I'm trying to run this against a MySQL database setup locally in a docker container.

The model definition looks something like this:

from sqlalchemy.orm import (DeclarativeBase, Mapped)

class classA(DeclarativeBase):
    id: Mapped[int] = mapped_column(primary_key=True, index=True, autoincrement=True)
    record_valid_from: Mapped[datetime] = mapped_column(DateTime,
        primary_key=True,
        default=get_current_timestamp # this is a python method returning datetime.now()
    )
    active: Mapped[bool] = mapped_column(Boolean, default=True,
        comment="TRUE if latest version, FALSE otherwise"
    )
    ... # some more fields and logic

The other models look similar with various different relations between them.

When auto-generating a migration script using alembic (alembic revision --autogenerate -m "init database") the resulting python code seems to be producing invalid SQL.

More specifically, I'm running into:

(pymysql.err.OperationalError) (1075, 'Incorrect table definition; there can be only one auto column and it must be defined as a key')

Here is the migration code (Note: I simplified it a little bit):

def upgrade() -> None:
    op.create_table('classA',
        sa.Column('name', sa.String(length=100), nullable=False),
        sa.Column('record_valid_from', sa.DateTime(), nullable=False),
        sa.Column('active', sa.Boolean(), nullable=False),
        sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
        sa.PrimaryKeyConstraint('record_valid_from', 'id')
    )
    op.create_index(op.f('ix_classA_id'), 'classA', ['id'], unique=False)

Anyone experienced something similar and/or knows how to fix this?

Things I tried:

  • call op.create_primary_key after the table is created (see: https://alembic.sqlalchemy.org/en/latest/ops.html#alembic.operations.Operations.create_primary_key). Result: sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (1068, 'Multiple primary key defined')
  • removing sa.PrimaryKeyConstraint and then call op.create_primary_key directly. Result:
    • migration works correctly
    • trying to create a new ORM model resulted in: sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (1364, "Field 'id' doesn't have a default value")

答案1

得分: 1

我花了几个小时解决了这个问题,然后自己修复了它。对于有类似问题的人,这里是答案:

实际上,主键字段在PrimaryKeyConstraint中包含的顺序很重要。我的问题通过调整顺序得以解决,而不是sa.PrimaryKeyConstraint('record_valid_from', 'id'),我将它更改为sa.PrimaryKeyConstraint("id", "record_valid_from")

希望这能帮助。

英文:

I spend a couple of hours on this issue and fixed it myself. For anyone with a similar problem here's the answer:

The order in which the primary key fields are included in the PrimaryKeyConstraint matters in fact. My problem was solved by reverting the order, instead of sa.PrimaryKeyConstraint('record_valid_from', 'id') I changed it to sa.PrimaryKeyConstraint("id", "record_valid_from")

I hope this helps.

huangapple
  • 本文由 发表于 2023年7月18日 00:32:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/76706462.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定