从应用程序cod内部使用Alembic API

2024-05-10 10:11:45 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在使用SQLite作为基于PySide的桌面应用程序的应用程序文件格式(请参见here了解为什么要这样做)。也就是说,当用户使用我的应用程序时,他们的数据将保存在其计算机上的单个数据库文件中。我正在使用SQLAlchemy ORM与数据库通信。

当我发布应用程序的新版本时,我可以修改数据库模式。我不希望用户每次更改模式时都扔掉他们的数据,所以我需要将他们的数据库迁移到最新格式。另外,我创建了很多临时数据库来保存数据子集,以便与一些外部进程一起使用。我想用alembic创建这些数据库,以便用正确的版本标记它们。

我有几个问题:

  • 有没有办法从我的Python代码中调用alembic?我认为必须对纯Python模块使用Popen是很奇怪的,但是文档只是从命令行使用alembic。主要是,我需要将数据库位置更改为用户数据库所在的位置。

  • 如果这不可能,我可以从命令行指定一个新的数据库位置而不编辑.ini文件吗?这将使得通过Popen调用alembic不是什么大事。

  • 我看到alembic将其版本信息保存在一个名为alembic_version的简单表中,其中一列名为version_num,一行指定版本。我可以在我的模式中添加一个alembic_version表,并在创建新数据库时用最新版本填充它,这样就不会有开销了吗?这是个好主意吗?我应该用alembic来创建所有的数据库吗?

我让alembic为我用来在项目目录中开发的单个数据库工作得很好。我想使用alembic在任意位置方便地迁移和创建数据库,最好是通过某种Python API,而不是命令行。此应用程序还将使用cx_Freeze进行冻结,以防造成差异。

谢谢!


Tags: 文件数据命令行用户版本数据库应用程序sqlite
3条回答

下面是一个纯编程示例,演示如何以编程方式配置和调用alembic命令。

目录设置(便于代码阅读)

.                         # root dir
|- alembic/               # directory with migrations
|- tests/diy_alembic.py   # example script
|- alembic.ini            # ini file

这里是diy_alembic.py

import os
import argparse
from alembic.config import Config
from alembic import command
import inspect

def alembic_set_stamp_head(user_parameter):
    # set the paths values
    this_file_directory = os.path.dirname(os.path.abspath(inspect.stack()[0][1]))
    root_directory      = os.path.join(this_file_directory, '..')
    alembic_directory   = os.path.join(root_directory, 'alembic')
    ini_path            = os.path.join(root_directory, 'alembic.ini')

    # create Alembic config and feed it with paths
    config = Config(ini_path)
    config.set_main_option('script_location', alembic_directory)    
    config.cmd_opts = argparse.Namespace()   # arguments stub

    # If it is required to pass -x parameters to alembic
    x_arg = 'user_parameter=' + user_parameter
    if not hasattr(config.cmd_opts, 'x'):
        if x_arg is not None:
            setattr(config.cmd_opts, 'x', [])
            if isinstance(x_arg, list) or isinstance(x_arg, tuple):
                for x in x_arg:
                    config.cmd_opts.x.append(x)
            else:
                config.cmd_opts.x.append(x_arg)
        else:
            setattr(config.cmd_opts, 'x', None)

    #prepare and run the command
    revision = 'head'
    sql = False
    tag = None
    command.stamp(config, revision, sql=sql, tag=tag)

    #upgrade command
    command.upgrade(config, revision, sql=sql, tag=tag)

代码或多或少是从this Flask-Alembic file中截取的。这是查看其他命令用法和详细信息的好地方。

为什么要这样做?-它是为了在运行自动测试时创建alembic标记、升级和降级而编写的。

  • chdir(迁移目录)干扰了一些测试。
  • 我们希望有一个数据库创建和操作的来源。”如果我们用alembic、alembic而不是metadata打包和管理数据库,create_all()shell也可以用于测试”。
  • 即使上面的代码超过4行,alembic显示自己是一个很好的可控野兽,如果这样驱动。

以下是我在将软件连接到alembic后学到的内容:

有没有办法从我的Python代码中调用alembic?

是的。在撰写本文时,alembic的主要入口点是^{},因此您可以导入它并自己调用它,例如:

import alembic.config
alembicArgs = [
    '--raiseerr',
    'upgrade', 'head',
]
alembic.config.main(argv=alembicArgs)

注意,alembic在当前目录(即os.getcwd())中查找迁移。在调用alembic之前,我已经通过使用os.chdir(migration_directory)处理了这个问题,但是可能有更好的解决方案。


我可以在不编辑.ini文件的情况下从命令行指定新的数据库位置吗?

是的。关键在于-x命令行参数。从alembic -h(令人惊讶的是,我在文档中找不到命令行参数引用):

optional arguments:
 -x X                  Additional arguments consumed by custom env.py
                       scripts, e.g. -x setting1=somesetting -x
                       setting2=somesetting

因此,您可以创建自己的参数,例如dbPath,然后在env.py中截取它:

alembic -x dbPath=/path/to/sqlite.db upgrade head

例如在env.py中:

def run_migrations_online():   
    # get the alembic section of the config file
    ini_section = config.get_section(config.config_ini_section)

    # if a database path was provided, override the one in alembic.ini
    db_path = context.get_x_argument(as_dictionary=True).get('dbPath')
    if db_path:
        ini_section['sqlalchemy.url'] = db_path

    # establish a connectable object as normal
    connectable = engine_from_config(
        ini_section,
        prefix='sqlalchemy.',
        poolclass=pool.NullPool)

    # etc

当然,您也可以在alembic.config.main中使用argv来提供-x参数。

我同意@davidism关于使用迁移vsmetadata.create_all():)

这是一个非常宽泛的问题,真正实现你的想法将取决于你,但这是可能的。

您可以从您的Python代码中调用Alembic而不使用命令,因为它也是用Python实现的!你只需要重新创建命令在幕后所做的事情。

不可否认,这些文档的状态不是很好,因为它们仍然是库的相对早期版本,但是稍微挖掘一下,您会发现以下内容:

  1. 创建Config
  2. 使用配置创建ScriptDirectory
  3. 使用Config和script目录创建EnvironmentContext
  4. 使用EnvironmentContext创建MigrationContext
  5. 大多数命令使用Config和MigrationContext中的一些方法组合

我编写了一个扩展来提供对Flask SQLAlchemy数据库的编程Alembic访问。这个实现与Flask和Flask SQLAlchemy绑定在一起,但它应该是一个很好的起点。See Flask-Alembic here.

关于如何创建新数据库的最后一点,可以使用Alembic创建表,也可以使用metadata.create_all()然后alembic stamp head(或等效的python代码)。我建议始终使用迁移路径创建表,并忽略原始的metadata.create_all()

我对cx_freeze没有任何经验,但只要迁移包含在发行版中,并且代码中指向该目录的路径正确,就应该没问题。

相关问题 更多 >