To, o co prosisz, to migracja danych , w przeciwieństwie do migracji schematu, która jest najbardziej rozpowszechniona w dokumentach Alembic.
Ta odpowiedź zakłada, że do definiowania modeli używasz deklaratywnego (w przeciwieństwie do klas-mapowania-tabeli lub rdzenia). Dostosowanie tego do innych form powinno być stosunkowo proste.
Zauważ, że Alembic zapewnia kilka podstawowych funkcji danych: op.bulk_insert()
i op.execute()
. Jeśli operacje są dość minimalne, użyj ich. Jeśli migracja wymaga relacji lub innych złożonych interakcji, wolę korzystać z pełnej mocy modeli i sesji opisanych poniżej.
Poniżej znajduje się przykład skryptu migracji, który konfiguruje niektóre deklaratywne modele, które będą używane do manipulowania danymi w sesji. Kluczowe punkty to:
Zdefiniuj podstawowe modele, których potrzebujesz, wraz z potrzebnymi kolumnami. Nie potrzebujesz każdej kolumny, tylko klucz podstawowy i te, których będziesz używać.
W ramach funkcji aktualizacji użyj, op.get_bind()
aby uzyskać bieżące połączenie i zrób z nim sesję.
- Lub użyj
bind.execute()
niższego poziomu SQLAlchemy do bezpośredniego pisania zapytań SQL. Jest to przydatne w przypadku prostych migracji.
Korzystaj z modeli i sesji w normalny sposób w swojej aplikacji.
"""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')
Migracja definiuje oddzielne modele, ponieważ modele w kodzie reprezentują bieżący stan bazy danych, podczas gdy migracje reprezentują kroki po drodze . Twoja baza danych może znajdować się w dowolnym stanie wzdłuż tej ścieżki, więc modele mogą jeszcze nie zostać zsynchronizowane z bazą danych. O ile nie jesteś bardzo ostrożny, używanie rzeczywistych modeli bezpośrednio spowoduje problemy z brakującymi kolumnami, nieprawidłowymi danymi itp. Bardziej przejrzyste jest wyraźne określenie, jakich kolumn i modeli będziesz używać podczas migracji.
op.execute
wupgrade()
, czy istnieje sposób, aby zapewnić domyślny szablon, który będzie używany przezalembic revision
polecenie (domyślnym ciała dla wygenerowanego.py
pliku)?