私のPySideベースのデスクトップアプリケーションでは、SQLiteをアプリケーションファイル形式として使用しています(これを行う理由については here を参照)。つまり、ユーザーが私のアプリを使用すると、ユーザーのデータはマシン上の単一のデータベースファイルに保存されます。 SQLAlchemy ORMを使用してデータベースと通信しています。
アプリケーションの新しいバージョンをリリースするときに、データベーススキーマを変更する場合があります。スキーマを変更するたびにユーザーがデータを破棄する必要がないようにしたいので、データベースを最新の形式に移行する必要があります。また、いくつかの外部プロセスで使用するためにデータのサブセットを保存するために、一時データベースをたくさん作成しています。これらのデータベースをalembicで作成して、適切なバージョンのタグが付けられるようにしたいと考えています。
少し質問があります:
私のPythonコード内からalembicを呼び出す方法はありますか?純粋なPythonモジュールにPopen
を使用する必要があるのは奇妙だと思います、しかし、ドキュメントはコマンドラインからalembicを使用するだけです。主に、ユーザーのデータベースがある場所にデータベースの場所を変更する必要があります。
それが不可能な場合、.iniファイルを編集せずにコマンドラインから新しいデータベースの場所を指定できますか?これはPopen
を介してalembicを呼び出すことを大したことにはしません。
Alembicはバージョン情報をalembic_version
という単純なテーブルの下に保持し、version_num
という1つの列とバージョンを指定する1つの行があることがわかります。新しいデータベースを作成するときにalembic_version
テーブルをスキーマに追加し、最新バージョンを設定してオーバーヘッドをなくすことはできますか?それも良い考えです。 alembicを使用してすべてのデータベースを作成する必要がありますか?
私はプロジェクトのディレクトリで開発に使用する単一のデータベースに対して優れた作業をしています。私はalembicを使用して、データベースを任意の場所に便利に移行および作成します。できれば、コマンドラインではなく、ある種のPython APIを使用して行います。このアプリケーションもcx_Freezeでフリーズされています。違い。
ありがとう!
ソフトウェアをalembic
に接続した後に私が学んだことは次のとおりです。
はい。これを書いている時点では、alembicのメインエントリポイントは _alembic.config.main
_ なので、インポートして自分で呼び出すことができます。次に例を示します。
_import alembic.config
alembicArgs = [
'--raiseerr',
'upgrade', 'head',
]
alembic.config.main(argv=alembicArgs)
_
Alembicは現在のディレクトリ(つまり、os.getcwd())でマイグレーションを探すことに注意してください。 alembicを呼び出す前にos.chdir(migration_directory)
を使用してこれを処理しましたが、もっと良い解決策があるかもしれません。
はい。重要なのは、_-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 に対抗してマイグレーションの使用について同意しますmetadata.create_all()
:)
これは非常に広範な質問であり、実際にアイデアを実装するかどうかはあなた次第ですが、それは可能です。
Pythonにも実装されているため、コマンドを使用せずにPythonコードからAlembicを呼び出すことができます!コマンドの背後で実行されているコマンドを再作成する必要があるだけです。シーン。
確かに、これらはまだライブラリの比較的初期のリリースであるため、ドキュメントはあまり良い状態ではありませんが、少し掘り下げると、次のことがわかります。
このプログラムによるAlembicによるFlask-SQLAlchemyデータベースへのアクセスを提供する拡張機能を作成しました。実装はFlaskとFlask-SQLAlchemyに関連付けられていますが、開始するには適切な場所であるはずです。 こちらのFlask-Alembicを参照してください。
新しいデータベースの作成方法に関する最後のポイントについては、Alembicを使用してテーブルを作成するか、metadata.create_all()
を使用して_alembic stamp head
_(または同等のpython)を使用することができます。 =コード)テーブルの作成には常に移行パスを使用し、生のmetadata.create_all()
は無視することをお勧めします。
私はcx_freezeの経験はありませんが、マイグレーションがディストリビューションに含まれていて、コード内のそのディレクトリへのパスが正しい限り、問題ありません。
Alembic docsの commands API ページを見ると、Pythonアプリケーションから直接CLIコマンドを実行する方法の例が表示されます。CLIを経由せずにコード。
alembic.config.main
を実行することには、env.py
スクリプトが実行されるという欠点があります。たとえば、ロギング構成を変更します。
別の非常に簡単な方法は、上記の「コマンドAPI」を使用することです。たとえば、ここに私が書いた小さなヘルパー関数があります:
from alembic.config import Config
from alembic import command
def run_migrations(script_location: str, dsn: str) -> None:
LOG.info('Running DB migrations in %r on %r', script_location, dsn)
alembic_cfg = Config()
alembic_cfg.set_main_option('script_location', script_location)
alembic_cfg.set_main_option('sqlalchemy.url', dsn)
command.upgrade(alembic_cfg, 'head')
ここではset_main_option
メソッドを使用して、必要に応じて別のDBで移行を実行できるようにしています。だから私はこれを次のように呼び出すだけです:
run_migrations('/path/to/migrations', 'postgresql:///my_database')
これらの2つの値(パスとDSN)をどこから取得するかはユーザー次第です。しかし、これはあなたが達成したいことに非常に近いようです。コマンドAPIには stamp() メソッドもあり、特定のDBを特定のバージョンとしてマークできます。上記の例は、これを呼び出すように簡単に調整できます。
これは、プログラムで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)
コードは多かれ少なかれ このFlask-Alembicファイル からのカットです。他のコマンドの使用法と詳細を確認するのに良い場所です。
なぜこのソリューションですか?-自動テストの実行時に、アレンビックスタンプ、アップグレード、およびダウングレードを作成する必要があるために作成されました。
Flaskを使用していないので、既に推奨されているFlask-Alembicライブラリを使用できませんでした。代わりに、かなりいじくり回した後、すべてを実行する次の短い関数をコード化しました。移行と呼ばれるサブモジュール(フォルダー)の下にすべてのalembic関連ファイルを保管します。実際には、alembic.ini
をenv.py
と一緒に保管します。そのために調整するalembic.ini
ファイルのスニペット:
[alembic]
script_location = .
次に、同じディレクトリに次のファイルを追加し、run.py
という名前を付けました。しかし、スクリプトをどこに置いても、正しいパスを指すように以下のコードを変更するだけです。
from alembic.command import upgrade
from alembic.config import Config
import os
def run_sql_migrations():
# retrieves the directory that *this* file is in
migrations_dir = os.path.dirname(os.path.realpath(__file__))
# this assumes the alembic.ini is also contained in this same directory
config_file = os.path.join(migrations_dir, "alembic.ini")
config = Config(file_=config_file)
config.set_main_option("script_location", migrations_dir)
# upgrade the database to the latest revision
upgrade(config, "head")
次に、そのrun.py
ファイルを配置すると、メインコードでこれを行うことができます。
from mymodule.migrations.run import run_sql_migrations
run_sql_migrations()
SQLAlchemyでフライウェイ風の結果を達成しようとしている他の誰にとっても、これは私にとってうまくいきました:
プロジェクトにmigration.pyを追加します。
from flask_alembic import Alembic
def migrate(app):
alembic = Alembic()
alembic.init_app(app)
with app.app_context():
alembic.upgrade()
データベースが初期化された後、アプリケーションの起動時にそれを呼び出します
application = Flask(__name__)
db = SQLAlchemy()
db.init_app(application)
migration.migrate(application)
次に、残りの標準的なalembicステップを実行するだけです。
プロジェクトをalembicとして初期化する
alembic init alembic
Env.pyを更新します。
from models import MyModel
target_metadata = [MyModel.Base.metadata]
Alembic.iniを更新する
sqlalchemy.url = postgresql://postgres:postgres@localhost:5432/my_db
SQLAlchemyモデルがすでに定義されていると仮定すると、今すぐスクリプトを自動生成できます。
alembic revision --autogenerate -m "descriptive migration message"
Env.pyにモデルをインポートできないというエラーが発生した場合は、修正プログラムのターミナルで次を実行できます
export PYTHONPATH=/path/to/your/project
最後に、私の移行スクリプトはalembic/versionsディレクトリに生成されていたので、alembicがそれらを取得するには、それらをmigrationsディレクトリにコピーする必要がありました。
├── alembic
│ ├── env.py
│ ├── README
│ ├── script.py.mako
│ └── versions
│ ├── a5402f383da8_01_init.py # generated here...
│ └── __pycache__
├── alembic.ini
├── migrations
│ ├── a5402f383da8_01_init.py # manually copied here
│ └── script.py.mako
何か設定が間違っている可能性がありますが、現在は機能しています。
Alembic.operations.base.Operationsのドキュメントをご覧ください:
from alembic.runtime.migration import MigrationContext
from alembic.operations import Operations
conn = myengine.connect()
ctx = MigrationContext.configure(conn)
op = Operations(ctx)
op.alter_column("t", "c", nullable=True)