Alembic генерирует произвольные изменения типа для столбцов Geometry

Я работаю над проектом, который использует 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)

env.py Alembic выглядит следующим образом:

    # 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()

Первый скрипт миграции, создающий таблицу Geometry:

    ...
    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",
        )

После запуска этого сценария миграции таблица создается правильно:

Созданная таблица геометрии

Когда я снова запускаю команду 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 и вообще никаких изменений нет?


person Baris    schedule 07.07.2020    source источник


Ответы (1)


Я нашел решение. Я делюсь им здесь на случай, если другие люди могут столкнуться с этой ошибкой: (https://alembic.sqlalchemy.org/en/latest/autogenerate.html#comparing-types)

Можно реализовать пользовательскую функцию compare_type и использовать ее в env.py. В моем случае столбцы geometry интерпретировались как типы sqlalchemy.Integer или sqalchemy.NUMERIC. Вот почему я добавил условие if, которое returns False if inspected_type равно NUMERIC или Integer, а metadata_type равно 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 по-прежнему обнаруживает изменение допустимости значений NULL, но это не связано с проблемой compare_type.

person Baris    schedule 12.07.2020