flask_migrate结合geoalchemy2迁移postgis

2024-04-01 00:48

本文主要是介绍flask_migrate结合geoalchemy2迁移postgis,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

代码示例

创建app.py

from flask import Flask
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
from geoalchemy2 import Geometryapp = Flask(__name__)app.config['SQLALCHEMY_DATABASE_URI'] = "postgresql://postgres:111111@localhost:5432/postgres"
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = Falsedb = SQLAlchemy(app)
Migrate(app, db)class Point(db.Model):id = db.Column(db.Integer, primary_key=True, autoincrement=True)geom = db.Column(Geometry("POINT", srid=4326, spatial_index=False))# 正确创建空间索引的方式:指定spatial_index=False,并使用以下方式创建空间索引,否则使用flask_migrate会重复创建索引导致报错
# 参考链接:https://github.com/geoalchemy/geoalchemy2/issues/137#issuecomment-1022413828
db.Index("idx_point_geom",Target.__table__.c.geom,postgresql_using='gist',
)@app.route('/')
def index():return "<h1>hello</h1>"if __name__ == '__main__':app.run(debug=True)

迁移

flask db init

输出:

Creating directory /test/migrations ...  done
Creating directory /test/migrations/versions ...  done
Generating /test/migrations/alembic.ini ...  done
Generating /test/migrations/env.py ...  done
Generating /test/migrations/README ...  done
Generating /test/migrations/script.py.mako ...  done
Please edit configuration/connection/logging settings in '/test/migrations/alembic.ini' before proceeding.
flask db migrate

输出:

INFO  [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO  [alembic.runtime.migration] Will assume transactional DDL.
INFO  [alembic.autogenerate.compare] Detected added table 'point'
INFO  [alembic.autogenerate.compare] Detected removed table 'spatial_ref_sys'
Generating /test/migrations/versions/419c1d992227_.py ...  done
flask db upgrade

输出:

INFO  [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO  [alembic.runtime.migration] Will assume transactional DDL.
INFO  [alembic.runtime.migration] Running upgrade  -> 419c1d992227, empty message...File "/test/migrations/versions/419c1d992227_.py", line 23, in upgradesa.Column('geom', geoalchemy2.types.Geometry(geometry_type='POINT', srid=4326, from_text='ST_GeomFromEWKT', name='geometry'), nullable=True),
NameError: name 'geoalchemy2' is not defined

错误分析

发现/test/migrations/versions/419c1d992227_.py中的geoalchemy2没有导入,并且spatial_ref_sys表也不应该被删除,否则将无法使用空间扩展
在这里插入图片描述

解决方案

方法一(推荐):使用geoalchemy2中的alembic_helpers

...
from geoalchemy2.alembic_helpers import include_object, render_itemfrom app import create_app
from app.model import dbapp = create_app()# compare_server_default=True
migrate = Migrate(app, db, compare_type=True, include_object=include_object, render_item=render_item)

方法二:手动编辑

每次迁移完后修改迁移脚本,如下:
在这里插入图片描述
再执行flask db upgrade,输出:

INFO  [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO  [alembic.runtime.migration] Will assume transactional DDL.
INFO  [alembic.runtime.migration] Running upgrade  -> 419c1d992227, empty message

迁移成功

修改模板

script.py.mako中添加import geoalchemy2,如下所示:

"""${message}Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}"""
from alembic import op
import sqlalchemy as sa
import geoalchemy2
${imports if imports else ""}# revision identifiers, used by Alembic.
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
branch_labels = ${repr(branch_labels)}
depends_on = ${repr(depends_on)}def upgrade():${upgrades if upgrades else "pass"}def downgrade():${downgrades if downgrades else "pass"}

env.py中添加include_object函数,忽略spatial_ref_sys表,并在context.configure中添加include_object=include_object,如下所示:

...def include_object(object, name, type_, reflected, compare_to):if type_ == 'table' and name in ('spatial_ref_sys'):return Falsereturn Truedef run_migrations_online():"""Run migrations in 'online' mode.In this scenario we need to create an Engineand associate a connection with the context."""# this callback is used to prevent an auto-migration from being generated# when there are no changes to the schema# reference: http://alembic.zzzcomputing.com/en/latest/cookbook.htmldef process_revision_directives(context, revision, directives):if getattr(config.cmd_opts, 'autogenerate', False):script = directives[0]if script.upgrade_ops.is_empty():directives[:] = []logger.info('No changes in schema detected.')connectable = current_app.extensions['migrate'].db.enginewith connectable.connect() as connection:context.configure(connection=connection,target_metadata=target_metadata,process_revision_directives=process_revision_directives,include_object=include_object,**current_app.extensions['migrate'].configure_args)with context.begin_transaction():context.run_migrations()...       

此方法只在数据库迁移前修改一次,随后可直接进行数据库迁移。

升级

接下来我们来添加一个Polygon

class Polygon(db.Model):id = db.Column(db.Integer, primary_key=True, autoincrement=True)geom = db.Column(Geometry("POLYGON", srid=4326))

迁移后脚本如下:

def upgrade():# ### commands auto generated by Alembic - please adjust! ###op.create_table('polygon',sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),sa.Column('geom', geoalchemy2.types.Geometry(geometry_type='POLYGON', srid=4326, from_text='ST_GeomFromEWKT', name='geometry'), nullable=True),sa.PrimaryKeyConstraint('id'))op.drop_index('idx_point_geom', table_name='point')# ### end Alembic commands ###

其中op.drop_index('idx_point_geom', table_name='point')显然是不对的,因为此操作将会删除空间索引,这不是我们想要的。
回到env.py,添加index判断:

def include_object(object, name, type_, reflected, compare_to):if type_ == 'table' and name in ('spatial_ref_sys'):return Falseif type_ == 'index' and name.endswith("_geom"):return Falsereturn True

总结

  • 在生产环境中,migrations目录应该作为持久化数据永久存储!
  • 对于永久不变的数据库迁移推荐使用db.create_all(),免去了复杂的配置
  • 对于需要添加字段或者新增表的数据库迁移推荐使用修改模板方式,一次配置永久有效

参考链接:

  • https://alembic.sqlalchemy.org/en/latest/autogenerate.html#omitting-table-names-from-the-autogenerate-process
  • https://geoalchemy-2.readthedocs.io/en/latest/alembic.html

这篇关于flask_migrate结合geoalchemy2迁移postgis的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/865810

相关文章

Spring 中的切面与事务结合使用完整示例

《Spring中的切面与事务结合使用完整示例》本文给大家介绍Spring中的切面与事务结合使用完整示例,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考... 目录 一、前置知识:Spring AOP 与 事务的关系 事务本质上就是一个“切面”二、核心组件三、完

Python Flask实现定时任务的不同方法详解

《PythonFlask实现定时任务的不同方法详解》在Flask中实现定时任务,最常用的方法是使用APScheduler库,本文将提供一个完整的解决方案,有需要的小伙伴可以跟随小编一起学习一下... 目录完js整实现方案代码解释1. 依赖安装2. 核心组件3. 任务类型4. 任务管理5. 持久化存储生产环境

Python用Flask封装API及调用详解

《Python用Flask封装API及调用详解》本文介绍Flask的优势(轻量、灵活、易扩展),对比GET/POST表单/JSON请求方式,涵盖错误处理、开发建议及生产环境部署注意事项... 目录一、Flask的优势一、基础设置二、GET请求方式服务端代码客户端调用三、POST表单方式服务端代码客户端调用四

SpringBoot结合Knife4j进行API分组授权管理配置详解

《SpringBoot结合Knife4j进行API分组授权管理配置详解》在现代的微服务架构中,API文档和授权管理是不可或缺的一部分,本文将介绍如何在SpringBoot应用中集成Knife4j,并进... 目录环境准备配置 Swagger配置 Swagger OpenAPI自定义 Swagger UI 底

Oracle迁移PostgreSQL隐式类型转换配置指南

《Oracle迁移PostgreSQL隐式类型转换配置指南》Oracle迁移PostgreSQL时因类型差异易引发错误,需通过显式/隐式类型转换、转换关系管理及冲突处理解决,并配合验证测试确保数据一致... 目录一、问题背景二、解决方案1. 显式类型转换2. 隐式转换配置三、维护操作1. 转换关系管理2.

flask库中sessions.py的使用小结

《flask库中sessions.py的使用小结》在Flask中Session是一种用于在不同请求之间存储用户数据的机制,Session默认是基于客户端Cookie的,但数据会经过加密签名,防止篡改,... 目录1. Flask Session 的基本使用(1) 启用 Session(2) 存储和读取 Se

Python Web框架Flask、Streamlit、FastAPI示例详解

《PythonWeb框架Flask、Streamlit、FastAPI示例详解》本文对比分析了Flask、Streamlit和FastAPI三大PythonWeb框架:Flask轻量灵活适合传统应用... 目录概述Flask详解Flask简介安装和基础配置核心概念路由和视图模板系统数据库集成实际示例Stre

MySQL 迁移至 Doris 最佳实践方案(最新整理)

《MySQL迁移至Doris最佳实践方案(最新整理)》本文将深入剖析三种经过实践验证的MySQL迁移至Doris的最佳方案,涵盖全量迁移、增量同步、混合迁移以及基于CDC(ChangeData... 目录一、China编程JDBC Catalog 联邦查询方案(适合跨库实时查询)1. 方案概述2. 环境要求3.

Spring Boot 结合 WxJava 实现文章上传微信公众号草稿箱与群发

《SpringBoot结合WxJava实现文章上传微信公众号草稿箱与群发》本文将详细介绍如何使用SpringBoot框架结合WxJava开发工具包,实现文章上传到微信公众号草稿箱以及群发功能,... 目录一、项目环境准备1.1 开发环境1.2 微信公众号准备二、Spring Boot 项目搭建2.1 创建

nginx -t、nginx -s stop 和 nginx -s reload 命令的详细解析(结合应用场景)

《nginx-t、nginx-sstop和nginx-sreload命令的详细解析(结合应用场景)》本文解析Nginx的-t、-sstop、-sreload命令,分别用于配置语法检... 以下是关于 nginx -t、nginx -s stop 和 nginx -s reload 命令的详细解析,结合实际应