添加非主键Alembic的自动增量列

问题描述 投票:0回答:3

我想添加一个自动增量列,该列不是现有 MySQL 数据库的主键。

此操作所需的在服务器上发出的命令如下:

ALTER TABLE `mytable` ADD `id` INT UNIQUE NOT NULL AUTO_INCREMENT FIRST

我面临的问题是如何通过 Alembic 迁移来复制此表更改。我试过了:

from alembic import op
import sqlalchemy as sa

def upgrade():
    op.add_column('mytable', sa.Colummn('id', sa.INTEGER(), 
                  nullable=False, autoincrement=True)

但是当我尝试使用以下命令插入行时:

INSERT INTO `mytable` (`col1`, `col2`) VALUES (`bar`);

其中

col1
col2
是不可为 null 的列。当我插入这条记录时,我希望表能够自动为我生成 id。

ERROR 1364 (HY000): Field 'id' doesn't have a default value

如果我使用以下命令检查 Alembic 自动生成的 sql:

alembic upgrade 'hash-of-revision' --sql

对于给定的修订版,它会吐出:

ALTER TABLE mytable ADD COLUMN id INTEGER NOT NULL;

这意味着 Alembic 或 SQLAlchemy 在生成迁移的 sql 时忽略

autoincrement
字段。

有什么办法可以解决这个问题吗?或者我可以基于自定义sql命令建立迁移吗?

python mysql python-3.x sqlalchemy alembic
3个回答
3
投票

首先我们添加列并允许其具有空值

op.add_column('abc', Column('id', BIGINT(unsigned=True), comment='This column stores the type of phrase') )

然后我们创建主键

op.create_primary_key( 'abc_pk', table_name='abc', columns=['id'] )

不确定它如何允许我在空列上添加主键,但我想这是因为它位于事务块中。

然后我们将该列更改为自动增量列

op.alter_column( existing_type=BIGINT(unsigned=True), table_name='abc', column_name='id', autoincrement=True, existing_autoincrement=True, nullable=False)


2
投票

从 SQLAlchemy 1.4+ 和 Alembic 1.9 开始,您可以使用 Identity 类型,根据 docs,它取代了 Serial 类型。

这个声明式 ORM:

class ProductOption(Base):
    __tablename__:str = 'product_options'

    id:Mapped[int] = mapped_column(Integer, server_default=Identity(start=1, cycle=True), primary_key=True)
    uuid:Mapped[UUID] = mapped_column(UUID, nullable=False, unique=True)
    name:Mapped[str] = mapped_column(String(50), nullable=False)
    price:Mapped[Decimal] = mapped_column(Numeric(16, 4), nullable=False)
    cost:Mapped[Decimal] = mapped_column(Numeric(16, 4), nullable=False)
    unit:Mapped[str] = mapped_column(String(8), nullable=False)
    duration:Mapped[int] = mapped_column(Integer)

导致以下 Alebic

--autogenerate
迁移:

op.create_table(
    "product_options",
    sa.Column(
        "id",
        sa.Integer(),
        sa.Identity(always=False, start=1, cycle=True),
        nullable=False,
    ),
    sa.Column("uuid", sa.UUID(), nullable=False),
    sa.Column("name", sa.String(length=50), nullable=False),
    sa.Column("price", sa.Numeric(precision=16, scale=4), nullable=False),
    sa.Column("cost", sa.Numeric(precision=16, scale=4), nullable=False),
    sa.Column("unit", sa.String(length=8), nullable=False),
    sa.Column("duration", sa.Integer(), nullable=False),
    sa.PrimaryKeyConstraint("id"),
    sa.UniqueConstraint("uuid"),
)

0
投票

不幸的是,如果表中有数据,@Nelson 的答案将不起作用

不确定它如何允许我在空列上添加主键,但我想这是因为它位于事务块中。

我相信这是因为如果数据库中没有行,则列中没有

NULL
值。

这是我使用 MySQL 8.0.34 的工作:

# add NOT NULL `id` column, for now is not autoincrement
op.add_column('table_name', sa.Column('id', mysql.INTEGER(unsigned=True), nullable=False, autoincrement=False))
# drop current primary key so that can create a new one
op.drop_constraint(None, 'table_name', 'primary')
# add new primary key with the same columns as the old one + the `id` column
op.create_primary_key(None, "table_name", ["id", "previous_pk_1", "previous_pk_2"])
# alter the `id` column to be autoincrement (can't make an autoincrement column that is not part of a primary key)
# this will assign autoincrement values to the column (before this it is all 0s)
op.alter_column('table_name', 'id',
                existing_type=mysql.INTEGER(unsigned=True),
                existing_nullable=False,
                autoincrement=True)
# remove the autoincrement from the column to allow dropping the primary key
op.alter_column('table_name', 'id',
                existing_type=mysql.INTEGER(unsigned=True),
                existing_nullable=False,
                autoincrement=False)
# drop the primary key
op.drop_constraint(None, 'table_name', 'primary')
# recreate the primary key, now with just the `id` column
op.create_primary_key(None, "table_name", ["id"])
# re-add autoincrement to the column
op.alter_column('table_name', 'id',
                existing_type=mysql.INTEGER(unsigned=True),
                existing_nullable=False,
                autoincrement=True)
© www.soinside.com 2019 - 2024. All rights reserved.