web-dev-qa-db-ja.com

Flask-SQLAlchemyを使用した移行のAlembic自動生成で変更は検出されませんでした

Baseの代わりに_db.Model_(Flask-SQLAlchemy)を使用して、Alembicが変更からクラスへの候補の移行を自動生成するのに問題があります。

_env.py_を変更して、Flaskアプリを作成し、関連するすべてのモデルをインポートし、データベースを初期化してから、移行を実行します。

_...
uri = 'mysql://user:password@Host/dbname?charset=utf8'
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = uri
app.config['SQLALCHEMY_ECHO'] = True
db.init_app(app)
with app.test_request_context():
    target_metadata = db.Model.metadata
    config.set_main_option('sqlalchemy.url', uri)
    if context.is_offline_mode():
        run_migrations_offline()
    else:
        run_migrations_online()
...
_

このアプローチは、drop_all()create_all()に対しては正常に機能しますが(たとえば、単体テスト用にテストデータベースを再作成する場合)、この場合はフラットになります。自動生成されたバージョンスクリプトには、常に空のアップグレードメソッドとダウングレードメソッドがあります

_def upgrade():
    ### commands auto generated by Alembic - please adjust! ###
    pass
    ### end Alembic commands ###


def downgrade():
    ### commands auto generated by Alembic - please adjust! ###
    pass
    ### end Alembic commands ###
_

私の変更には、インデックスや外部キーの変更だけでなく、列の名前変更、列定義の変更、etc。が含まれています。

Flask-SQLAlchemyでAlembicを使用している人はいますか?私がどこで間違っているのか考えていますか?

どうもありがとう!

21
PartialOrder

Alembicは、テーブルまたは列の名前変更を自動的に検出できません。デフォルトでは、列タイプの変更も検索されませんが、これに対して_compare_type_オプションを有効にすることができます。

Alembicドキュメントからの抜粋:

自動生成はデフォルトで以下を検出します。

  • テーブルの追加、削除。
  • 列の追加、削除。
  • 列のNULL可能ステータスの変更。

自動生成は、オプションで以下を検出できます。

  • 列タイプの変更。これは、EnvironmentContext.configure()に_compare_type=True_を設定した場合に発生します。この機能はほとんどの場合うまく機能しますが、デフォルトではオフになっているため、最初にターゲットスキーマでテストできます。ここで呼び出し可能オブジェクトを渡すことによってカスタマイズすることもできます。詳細については、関数のドキュメントを参照してください。
  • サーバーのデフォルトの変更。これは、EnvironmentContext.configure()に_compare_server_default=True_を設定した場合に発生します。この機能は単純なケースではうまく機能しますが、常に正確な結果が得られるとは限りません。 Postgresqlバックエンドは、実際にはデータベースに対して「検出された」値と「メタデータ」値を呼び出して、同等性を判断します。この機能はデフォルトでオフになっているため、最初にターゲットスキーマでテストできます。タイプ比較と同様に、呼び出し可能オブジェクトを渡すことでカスタマイズすることもできます。詳細については、関数のドキュメントを参照してください。

自動生成は検出できません:

  • テーブル名の変更。これらは2つの異なるテーブルの追加/削除として出力され、代わりに名前の変更に手動で編集する必要があります。
  • 列名の変更。テーブル名の変更と同様に、これらは列の追加と削除のペアとして検出されますが、名前の変更とはまったく同じではありません。
  • Enumを直接サポートしないバックエンドで生成された場合のENUMなどの特別なSQLAlchemyタイプ-これは、サポートされていないデータベースでのそのようなタイプの表現、つまり_CHAR+CHECK_制約は、任意の種類の_CHAR+CHECK_である可能性があります。 SQLAlchemyがこれが実際にENUMであると判断するのは推測にすぎず、一般的には悪い考えです。ここで独自の「推測」関数を実装するには、sqlalchemy.events.DDLEvents.column_reflect()イベントを使用して特定の列に渡されるSQLAlchemy型を変更し、場合によってはsqlalchemy.events.DDLEvents.after_parent_attach()を使用して不要なCHECK制約をインターセプトします。

現在、自動生成はできませんが、最終的には以下を検出します。

  • CHECKUNIQUE、_FOREIGN KEY_などの独立した制約の追加、削除-これらはまだ実装されていません。現在、新しいテーブル内の制約、既存のテーブルへの「ダウングレード」のPKおよびFK制約、およびSQLAlchemyの「スキーマ」タイプCHECKで生成されたBoolean制約を取得します。 Enum
  • インデックスの追加、削除-まだ実装されていません。
  • シーケンスの追加、削除-まだ実装されていません。

更新:この最後のリストの一部の項目は、Alembic0.7.xリリースでサポートされています。

43
Miguel

私の間違いは、dbがすでに最終状態にある状態で最初の移行を作成しようとしたことでした。これは、既存のバージョンがないことに気づき、モデルに基づいていると考えていました。データベース内のすべてのテーブルを削除するまで空のバージョンを取得しましたが、その後は正常に機能しました。

3
Brad M

私もこの問題に直面し、この方法を使用してそれを解決しました:

_migrations/env.py_ファイルを開き、def run_migrations_online()関数で_context.configure_を確認します。_Alembic 1.0.8_では次のようになります。

_with connectable.connect() as connection:
    context.configure(
        connection=connection,
        target_metadata=target_metadata,
        process_revision_directives=process_revision_directives,
        **current_app.extensions['migrate'].configure_args,
    )
_

The _process_revision_directives=process_revision_directives_を削除またはコメントしてから、その上に_compare_type=True_を追加するだけです。

このような:

_with connectable.connect() as connection:
    context.configure(
        connection=connection,
        target_metadata=target_metadata,
        # process_revision_directives=process_revision_directives,
        **current_app.extensions['migrate'].configure_args,
        compare_type=True
    )
_
1
Tri

フラスコアレンビックを試してください https://github.com/tobiasandtobias/flask-alembic

昨日やってみました。 drop操作を除いて、私にとっては問題なく動作します。それらはsqliteでは機能しません( https://bitbucket.org/zzzeek/alembic/issue/21/column-renames-not-supported-on-sqlite )。

私がそれを使用した方法。まず、_python manage.py migrate revision --autogenerate_を使用してsqliteデータベースに空のテーブルを作成しました。それはそのような移行を生み出すでしょう

_def upgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.create_table('users_user',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('name', sa.String(length=50), nullable=True),
    sa.Column('email', sa.String(length=120), nullable=True),
    sa.Column('password', sa.String(length=20), nullable=True),
    sa.Column('role', sa.SmallInteger(), nullable=True),
    sa.Column('status', sa.SmallInteger(), nullable=True),
    sa.PrimaryKeyConstraint('id'),
    sa.UniqueConstraint('email'),
    sa.UniqueConstraint('name')
)
### end Alembic commands ###

def downgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.drop_table('users_user')
    ### end Alembic commands ###
_

次に-_python manage.py migrate upgrade head_

次に、新しい列test = db.Column(db.String(20))をユーザーモデルに追加し、このコマンドを実行しました_python manage.py migrate revision --autogenerate -m 'test field at users'_

これにより、次のような移行が発生しました。

_def upgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.add_column('users_user', sa.Column('test', sa.String(length=20), nullable=True))
    ### end Alembic commands ###


def downgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.drop_column('users_user', 'test')
    ### end Alembic commands ###
_
0
gl0om

必要な機能がアレムビックでサポートされていないことがわかった場合

  1. テーブル(または列)を削除してから移行する

  2. テーブル(または列)を追加し直してから、再度移行します

これは列挙型の変更でも機能します

0
tyan