Alembic 为几何列生成任意类型更改

Alembic generates arbitrary type changes for Geometry columns

我正在从事一个使用 SQLite 作为数据库并使用 Alembic 作为数据库迁移工具的项目。它包含空间数据,因此空间扩展和 geoalchemy2 包含在项目中。我正在使用 autogenerate 命令,它检测到几何列中不存在的一些更改。

这里是项目的简化结构:

    # Model
    sqlite_naming_convention = {
    "ix": "ix_%(column_0_label)s",
    "uq": "uq_%(table_name)s_%(column_0_name)s",
    "ck": "ck_%(table_name)s_%(column_0_name)s",
    "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
    "pk": "pk_%(table_name)s",
    }
    Metadata = MetaData(naming_convention=sqlite_naming_convention)
    BaseSpatiaLite = declarative_base(metadata=Metadata)


    class Geometries(BaseSpatiaLite):
        __tablename__ = "Geometries"

        geometry_id = Column(Integer, primary_key=True)
        geometry = Column(
            geoalchemy2.types.Geometry(geometry_type="GEOMETRY", srid=4326, management=True),
            nullable=False,
        )
        name = Column(String(length=150), nullable=False)

Alembic的env.py如下:

    # env.py
    ...
    def run_migrations_online():
        connectable = engine_from_config(
            config.get_section(config.config_ini_section),
            prefix="sqlalchemy.",
            poolclass=pool.NullPool,
        )
        # Enables Spatialite extension
        listen(connectable, "connect", load_spatialite)
        # Creates Spatial tables if they don't exist
        create_spatial_tables_for_sqlite(connectable)
        with connectable.connect() as connection:
            context.configure(
                connection=connection,
                target_metadata=target_metadata,
                render_as_batch=True,
                compare_type=True,
            )
    
            with context.begin_transaction():
                context.run_migrations()

创建几何体的第一个迁移脚本table:

    ...
    def upgrade():
        op.create_table(
            "Geometries",
            sa.Column("geometry_id", sa.Integer(), nullable=False),
            sa.Column("geometry", geoalchemy2.types.Geometry(management=True), nullable=False),
            sa.Column("name", sa.String(length=150), nullable=False),
            sa.PrimaryKeyConstraint("geometry_id"),
        )
    
    
    def downgrade():
        op.drop_table(
            "Geometries",
        )

此迁移脚本运行后,正确创建table:

当我再次运行autogenerate命令时,应该没有发现任何变化。但是,它会生成一个具有任意类型更改的迁移脚本:

    def upgrade():
        with op.batch_alter_table("Geometries", schema=None) as batch_op:
            batch_op.alter_column(
                "geometry",
                existing_type=sa.NUMERIC(),
                type_=geoalchemy2.types.Geometry(srid=4326, management=True),
                nullable=False,
            )
    
    
    def downgrade():
        with op.batch_alter_table("Geometries", schema=None) as batch_op:
            batch_op.alter_column(
                "geometry",
                existing_type=geoalchemy2.types.Geometry(srid=4326, management=True),
                type_=sa.NUMERIC(),
                nullable=True,
            )

我知道我可以将 compare_type 参数设置为 False,但我想自动检测类型更改。有没有办法告诉 Alembic geometry 列的类型是 Geometry 并且根本没有变化?

我找到了解决办法。我在这里分享它以防其他人可能会遇到此错误:(https://alembic.sqlalchemy.org/en/latest/autogenerate.html#comparing-types)

可以实现自定义 compare_type 函数并在 env.py 中使用它。在我的例子中,geometry 列被解释为 sqlalchemy.Integersqalchemy.NUMERIC 类型。这就是为什么我添加了一个 if 子句,其中 returns False if inspected_type is NUMERIC or Integer and metadata_type is geoalchemy2.types.Geometry.

# add it to env.py
def custom_compare_type(
    context, 
    inspected_column, 
    metadata_column, 
    inspected_type, 
    metadata_type
):
# return False if the metadata_type is the same as the inspected_type
# or None to allow the default implementation to compare these
# types. a return value of True means the two types do not
# match and should result in a type change operation.
if (isinstance(inspected_type, NUMERIC) or isinstance(inspected_type, Integer)) and isinstance(
    metadata_type, Geometry
):
    return False

return None

当您将 compare_type=True 更改为 compare_type=custom_compare_type 时,Alembic 应该停止检测 geometry 列的任意类型更改!

注意: Alembic 仍然检测到可空性更改,但它与 compare_type 问题无关。