如何使用 sqlalchemy 有效地管理频繁的模式更改?

Posted

技术标签:

【中文标题】如何使用 sqlalchemy 有效地管理频繁的模式更改?【英文标题】:How to efficiently manage frequent schema changes using sqlalchemy? 【发布时间】:2011-05-09 02:30:13 【问题描述】:

我正在使用 sqlalchemy 编写一个 Web 应用程序。在网站未投入生产的第一阶段开发过程中,一切都很顺利。我可以通过简单地删除旧的 sqlite 数据库并从头开始创建一个新数据库来轻松更改数据库架构。

现在网站正在生产中,我需要保留数据,但我仍然希望通过轻松地将数据库转换为新架构来保持我原来的开发速度。

假设我有版本 50 的 model.py 和版本 75 的 model.py,描述了数据库的架构。在这两个模式之间,大多数更改都是微不足道的,例如,使用默认值声明了一个新列,我只想将此默认值添加到旧记录中。

最终,一些更改可能并非微不足道,需要进行一些预先计算。

您如何(或将)如何处理快速变化的 Web 应用程序,例如每天使用一两个新版本的生产代码?

顺便说一句,如果这有什么不同的话,该网站是用 Pylons 编写的。

【问题讨论】:

"那么值得使用 migrate 吗?"应该是一个单独的问题。你有一个关于如何迁移的答案。询问 sqlalchemy-migrate 的用例和您的具体用例比这个一般问题更具体。 好的,所以我需要提出另一个关于迁移的问题,以便知道接受哪个答案。 @ascobol:“关于迁移的另一个问题,以便知道接受哪个答案”。错误的。你在这里有答案。 “工具[X]值得吗?”与“我如何迁移?”无关。你有“如何?”的答案。询问一种特定工具的价值与“如何?”无关。 @ascobol:也许您不相关的问题是“为什么不迁移工作?”不是“迁移值得吗?”这 - 也 - 与“如何?”无关 @ascobol:“我需要知道一个工具是否合适”。你被告知这是合适的。本题结束。开始另一个问题,说明您在使用该工具时遇到的具体问题。不相关。 【参考方案1】:

使用sqlalchemy-migrate。

它旨在支持数据库设计的敏捷方法,并在需要更改架构时更容易保持开发和生产数据库的同步。它使模式版本控制变得容易。

将其视为数据库架构的版本控制。您向它提交每个架构更改,它将能够在架构版本上前进/后退。这样您就可以升级客户端,并且它会确切知道要在该客户端的数据库上应用哪组更改。

它会自动为您执行 S.Lott 在他的回答中提出的建议。让困难的事情变得容易。

【讨论】:

【参考方案2】:

Alembic 是一个新的数据库迁移工具,由 SQLAlchemy 的作者编写。我发现它比 sqlalchemy-migrate 更容易使用。它还可以与 Flask-SQLAlchemy 无缝协作。

从您的 SQLAlchemy 模型中自动生成架构迁移脚本:

alembic revision --autogenerate -m "description of changes"

然后将新的架构更改应用到您的数据库:

alembic upgrade head

更多信息在这里:http://readthedocs.org/docs/alembic/

【讨论】:

我同意,但让它与你的烧瓶应用程序一起工作有点困难。我认为新代码“flask-alembic”是我需要测试的。检查这个问题:***.com/questions/14682466/… 在运行 alembic init alembic 并在 alembic.ini 中更改我的数据库用户名和密码后,我将 alembic/env.py 更改为:gist.github.com/alanhamlett/4721073 这就是使用 alembic 所需的全部内容我的烧瓶应用程序。希望对您有所帮助。 我的问题是我无法在您的 gist 代码中执行第 10 行。我无法导入我的模型或我的“db”变量。它不会让我在 env.py 中这样做。 在让 alembic 工作时遇到了类似的问题。 Alan 的要点中的第 8 行和第 9 行非常重要,文档中并未真正提及。但是您需要将项目添加到系统路径以允许 alembic 访问您的模型。【参考方案3】:

我们做什么。

    使用“主要版本”。“次要版本”标识您的应用程序。主要版本是架构版本号。主要数字不是一些随机的“足够新功能”之类的东西。这是与数据库模式兼容的正式声明。

    2.3 版和 2.4 版都使用架构版本 2。

    版本 3.1 使用版本 3 架构。

    使架构版本非常非常明显。对于 SQLite,这意味着将架构版本号保留在数据库文件名中。对于 mysql,使用数据库名称。

    编写迁移脚本。 2to3.py,3to4.py。这些脚本分两个阶段工作。 (1) 将旧数据查询到新结构中,创建简单的 CSV 或 JSON 文件。 (2) 从简单的 CSV 或 JSON 文件加载新结构,无需进一步处理。这些提取文件——因为它们的结构正确,加载速度快,并且可以很容易地用作单元测试装置。此外,您永远不会同时打开两个数据库。这使得脚本稍微简单一些。最后,加载文件可用于将数据移动到另一个数据库服务器。

“自动化”架构迁移非常非常困难。对数据库进行如此深入的操作很容易(也很常见),以至于自动化脚本无法轻松地将数据从旧架构映射到新架构。

【讨论】:

如何定义主要的架构更改?即使兼容性没有改变,添加表/新模型是否被视为主要模式更改?您在版本号上与 Firefox 竞争吗? (笑话)【参考方案4】:

处理您的问题的最佳方法是反映您的架构,而不是以声明的方式进行。我在这里写了一篇关于反射方法的文章: http://petrushev.wordpress.com/2010/06/16/reflective-approach-on-sqlalchemy-usage/ 但也有其他资源。通过这种方式,每次对架构进行更改时,您只需重新启动应用程序,反射就会为表中的更改获取新的元数据。这是相当快的,sqlalchemy 每个进程只执行一次。当然,您必须管理自己做出的关系更改。

【讨论】:

只是评论一下,这在现实世界中是一种可怕的做法。它迫使我检查数据库与代码的各种设置,例如字符串允许的长度等。我没有从我的 IDE 中获得任何自动完成或源代码检查。我真的不鼓励人们在任何项目中都进行反思。这只是一团糟。 “各种设置,如字符串允许的长度” - 你能详细说明一下吗?什么样的设置?而且,仅仅因为您没有在 ide 中获得任何自动完成功能并不意味着该方法是错误的。毕竟,我们处理的是 python - 而不是 java/c++。 另外,我在“现实世界”中拥有与 db 反射一起使用的应用程序。具有复杂的 ER。对我来说没那么乱。 如果您在模型上有一个字段名称并且我正在处理代码并看到 person.name = 'really long string' 并且我想查看名称的最大长度是多少,我必须加载数据库才能查看,因为它不会出现在我的模型中,因为 person = Column(Unicode(100)) 您不必“加载数据库”-(我不知道这到底是什么意思,但您不必这样做)。在架构反射之后,您需要的所有信息都存储在 sqlalchemy 反射的元数据中。具体来说,要获取列的长度,您需要:length = metadata.tables['tablename']._columns._data[u'columnname'].type.length。元数据当然是以前反映的。请注意,这不会再次调用对 db 的内省 - 它已经存储了。

以上是关于如何使用 sqlalchemy 有效地管理频繁的模式更改?的主要内容,如果未能解决你的问题,请参考以下文章

如何有效地比较列表并获得最频繁的对?

如何在 SQLAlchemy 中使用空集合进行分页查询?

spark有效地找到一组列的最频繁值

由于变量被更频繁地释放,使较小的函数通常在内存方面更有效吗? [关闭]

如何使用异步 GRPC 调用有效地管理内存

SQLAlchemy:如何根据其后端有条件地选择列的类型