Quello che stai chiedendo è una migrazione dei dati , al contrario della migrazione dello schema che è più diffusa nei documenti di Alembic.
Questa risposta presuppone che tu stia usando dichiarativo (al contrario di class-Mapper-Table o core) per definire i tuoi modelli. Dovrebbe essere relativamente semplice adattarlo alle altre forme.
Nota che Alembic fornisce alcune funzioni di dati di base: op.bulk_insert()
e op.execute()
. Se le operazioni sono abbastanza minime, usa quelle. Se la migrazione richiede relazioni o altre interazioni complesse, preferisco utilizzare tutta la potenza dei modelli e delle sessioni come descritto di seguito.
Di seguito è riportato uno script di migrazione di esempio che configura alcuni modelli dichiarativi che verranno utilizzati per manipolare i dati in una sessione. I punti chiave sono:
Definisci i modelli di base di cui hai bisogno, con le colonne di cui avrai bisogno. Non hai bisogno di tutte le colonne, solo della chiave primaria e di quelle che utilizzerai.
All'interno della funzione di aggiornamento, utilizzare op.get_bind()
per ottenere la connessione corrente e fare una sessione con essa.
- Oppure utilizzare
bind.execute()
per utilizzare il livello inferiore di SQLAlchemy per scrivere direttamente query SQL. Questo è utile per semplici migrazioni.
Usa i modelli e la sessione come faresti normalmente nella tua applicazione.
"""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)
# create the teams table and the players.team_id column
Team.__table__.create(bind)
op.add_column('players', sa.Column('team_id', sa.ForeignKey('teams.id'), nullable=False)
# create teams for each team name
teams = {name: Team(name=name) for name in session.query(Player.team).distinct()}
session.add_all(teams.values())
# set player team based on team name
for player in session.query(Player):
player.team = teams[player.team_name]
session.commit()
# don't need team name now that team relationship is set
op.drop_column('players', 'team')
def downgrade():
bind = op.get_bind()
session = orm.Session(bind=bind)
# re-add the players.team column
op.add_column('players', sa.Column('team', sa.String, nullable=False)
# set players.team based on team relationship
for player in session.query(Player):
player.team_name = player.team.name
session.commit()
op.drop_column('players', 'team_id')
op.drop_table('teams')
La migrazione definisce modelli separati perché i modelli nel codice rappresentano lo stato corrente del database, mentre le migrazioni rappresentano passaggi lungo il percorso . Il tuo database potrebbe trovarsi in qualsiasi stato lungo quel percorso, quindi i modelli potrebbero non essere ancora sincronizzati con il database. A meno che tu non stia molto attento, l'utilizzo diretto dei modelli reali causerà problemi con colonne mancanti, dati non validi, ecc. È più chiaro indicare in modo esplicito esattamente quali colonne e modelli utilizzerai nella migrazione.
op.execute
inupgrade()
, c'è un modo per fornire un modello predefinito da utilizzare tramitealembic revision
comando (un corpo predefinito per il.py
file generato )?