Apa yang Anda minta adalah migrasi data , bukan migrasi skema yang paling umum di dokumen Alembic.
Jawaban ini mengasumsikan Anda menggunakan deklaratif (sebagai lawan dari class-Mapper-Table atau core) untuk mendefinisikan model Anda. Harus relatif mudah untuk menyesuaikannya dengan bentuk lain.
Perhatikan bahwa Alembic menyediakan beberapa fungsi data dasar: op.bulk_insert()
dan op.execute()
. Jika operasinya cukup minimal, gunakan itu. Jika migrasi memerlukan hubungan atau interaksi kompleks lainnya, saya lebih suka menggunakan kekuatan penuh model dan sesi seperti yang dijelaskan di bawah ini.
Berikut ini adalah contoh skrip migrasi yang menyiapkan beberapa model deklaratif yang akan digunakan untuk memanipulasi data dalam sebuah sesi. Poin utamanya adalah:
Tentukan model dasar yang Anda butuhkan, dengan kolom yang Anda perlukan. Anda tidak membutuhkan setiap kolom, cukup kunci utama dan yang akan Anda gunakan.
Dalam fungsi upgrade, gunakan op.get_bind()
untuk mendapatkan koneksi saat ini, dan buat sesi dengannya.
- Atau gunakan
bind.execute()
untuk menggunakan level SQLAlchemy yang lebih rendah untuk menulis kueri SQL secara langsung. Ini berguna untuk migrasi sederhana.
Gunakan model dan sesi seperti biasa dalam aplikasi Anda.
"""create teams table
Revision ID: 169ad57156f0
Revises: 29b4c2bfce6d
Create Date: 2014-06-25 09:00:06.784170
"""
revision = '169ad57156f0'
down_revision = '29b4c2bfce6d'
from alembic import op
import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Player(Base):
__tablename__ = 'players'
id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.String, nullable=False)
team_name = sa.Column('team', sa.String, nullable=False)
team_id = sa.Column(sa.Integer, sa.ForeignKey('teams.id'), nullable=False)
team = orm.relationship('Team', backref='players')
class Team(Base):
__tablename__ = 'teams'
id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.String, nullable=False, unique=True)
def upgrade():
bind = op.get_bind()
session = orm.Session(bind=bind)
Team.__table__.create(bind)
op.add_column('players', sa.Column('team_id', sa.ForeignKey('teams.id'), nullable=False)
teams = {name: Team(name=name) for name in session.query(Player.team).distinct()}
session.add_all(teams.values())
for player in session.query(Player):
player.team = teams[player.team_name]
session.commit()
op.drop_column('players', 'team')
def downgrade():
bind = op.get_bind()
session = orm.Session(bind=bind)
op.add_column('players', sa.Column('team', sa.String, nullable=False)
for player in session.query(Player):
player.team_name = player.team.name
session.commit()
op.drop_column('players', 'team_id')
op.drop_table('teams')
Migrasi menentukan model terpisah karena model dalam kode Anda mewakili status database saat ini, sedangkan migrasi mewakili langkah-langkah selama proses . Database Anda mungkin berada dalam status apa pun di sepanjang jalur tersebut, sehingga model mungkin belum disinkronkan dengan database. Kecuali jika Anda sangat berhati-hati, menggunakan model nyata secara langsung akan menyebabkan masalah dengan kolom yang hilang, data tidak valid, dll. Lebih jelas menyatakan secara eksplisit kolom dan model apa yang akan Anda gunakan dalam migrasi.
op.execute
dalamupgrade()
, apakah ada cara untuk memberikan template default yang akan digunakan olehalembic revision
perintah (badan standar untuk dihasilkan.py
file)?Saya merekomendasikan penggunaan pernyataan inti SQLAlchemy menggunakan tabel ad-hoc, seperti yang dijelaskan dalam dokumentasi resmi , karena memungkinkan penggunaan SQL agnostik dan penulisan pythonic dan juga mandiri. SQLAlchemy Core adalah yang terbaik dari kedua dunia untuk skrip migrasi.
Berikut adalah contoh konsepnya:
from sqlalchemy.sql import table, column from sqlalchemy import String from alembic import op account = table('account', column('name', String) ) op.execute( account.update().\\ where(account.c.name==op.inline_literal('account 1')).\\ values({'name':op.inline_literal('account 2')}) ) # If insert is required from sqlalchemy.sql import insert from sqlalchemy import orm session = orm.Session(bind=bind) bind = op.get_bind() data = { "name": "John", } ret = session.execute(insert(account).values(data)) # for use in other insert calls account_id = ret.lastrowid
sumber