Wie führe ich Einfügungen und Updates in einem Alembic-Upgrade-Skript aus?

Lesezeit: 5 Minuten

Wie fuhre ich Einfugungen und Updates in einem Alembic Upgrade Skript aus
Arek S

Ich muss während eines Alembic-Upgrades Daten ändern.

Ich habe derzeit eine ‘Spieler’-Tabelle in einer ersten Überarbeitung:

def upgrade():
    op.create_table('player',
        sa.Column('id', sa.Integer(), nullable=False),
        sa.Column('name', sa.Unicode(length=200), nullable=False),
        sa.Column('position', sa.Unicode(length=200), nullable=True),
        sa.Column('team', sa.Unicode(length=100), nullable=True)
        sa.PrimaryKeyConstraint('id')
    )

Ich möchte eine ‘Teams’-Tabelle einführen. Ich habe eine zweite Überarbeitung erstellt:

def upgrade():
    op.create_table('teams',
        sa.Column('id', sa.Integer(), nullable=False),
        sa.Column('name', sa.String(length=80), nullable=False)
    )
    op.add_column('players', sa.Column('team_id', sa.Integer(), nullable=False))

Ich möchte, dass die zweite Migration auch die folgenden Daten hinzufügt:

  1. Teams-Tabelle ausfüllen:

    INSERT INTO teams (name) SELECT DISTINCT team FROM players;
    
  2. Aktualisieren Sie die player.team_id basierend auf dem Namen der player.team:

    UPDATE players AS p JOIN teams AS t SET p.team_id = t.id WHERE p.team = t.name;
    

Wie führe ich Einfügungen und Updates innerhalb des Upgrade-Skripts aus?

Wie fuhre ich Einfugungen und Updates in einem Alembic Upgrade Skript aus
Davidismus

Was du verlangst ist ein Datenmigration, im Gegensatz zu den Schemamigration das ist am weitesten verbreitet in den Alembic-Dokumenten.

Diese Antwort geht davon aus, dass Sie deklarativ (im Gegensatz zu class-Mapper-Table oder core) verwenden, um Ihre Modelle zu definieren. Es sollte relativ einfach sein, dies an die anderen Formen anzupassen.

Beachten Sie, dass Alembic einige grundlegende Datenfunktionen bereitstellt: op.bulk_insert() und op.execute(). Wenn die Operationen relativ minimal sind, verwenden Sie diese. Wenn die Migration Beziehungen oder andere komplexe Interaktionen erfordert, bevorzuge ich es, die volle Leistungsfähigkeit von Modellen und Sitzungen wie unten beschrieben zu nutzen.

Im Folgenden finden Sie ein Beispiel für ein Migrationsskript, das einige deklarative Modelle einrichtet, die zum Bearbeiten von Daten in einer Sitzung verwendet werden. Die wichtigsten Punkte sind:

  1. Definieren Sie die benötigten Basismodelle mit den benötigten Spalten. Sie benötigen nicht jede Spalte, sondern nur den Primärschlüssel und die, die Sie verwenden werden.

  2. Verwenden Sie innerhalb der Upgrade-Funktion op.get_bind() um die aktuelle Verbindung abzurufen und eine Sitzung damit zu machen.

    • Oder verwenden Sie bind.execute() um die untere Ebene von SQLAlchemy zu verwenden, um SQL-Abfragen direkt zu schreiben. Dies ist für einfache Migrationen nützlich.
  3. Verwenden Sie die Modelle und die Sitzung wie gewohnt in Ihrer Anwendung.

"""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')

Die Migration definiert separate Modelle, da die Modelle in Ihrem Code die aktuellen Zustand der Datenbank, während die Migrationen Schritte auf dem Weg. Ihre Datenbank kann sich entlang dieses Pfads in einem beliebigen Status befinden, sodass die Modelle möglicherweise noch nicht mit der Datenbank synchronisiert werden. Wenn Sie nicht sehr vorsichtig sind, führt die direkte Verwendung der echten Modelle zu Problemen mit fehlenden Spalten, ungültigen Daten usw. Es ist klarer, explizit anzugeben, welche Spalten und Modelle Sie bei der Migration verwenden werden.

Sie können auch direktes SQL verwenden, siehe (Alembic-Betriebsreferenz) wie im folgenden Beispiel:

from alembic import op

# revision identifiers, used by Alembic.
revision = '1ce7873ac4ced2'
down_revision = '1cea0ac4ced2'
branch_labels = None
depends_on = None


def upgrade():
    # ### commands made by andrew ###
    op.execute('UPDATE STOCK SET IN_STOCK = -1 WHERE IN_STOCK IS NULL')
    # ### end Alembic commands ###


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

  • Falls ich schon immer mal eine SQL-Anweisung aus einer externen Datei lesen und dann weitergeben wollte op.execute in upgrade(), gibt es eine Möglichkeit, eine Standardvorlage bereitzustellen, die von verwendet werden soll? alembic revision Befehl (ein Standardtext für das generierte .py Datei)?

    – QUentin

    11. Juli ’20 um 9:28


  • Ich kenne @Quentin nicht. Es ist eine interessante Idee.

    – Martlark

    12. Juli ’20 um 2:10

1641661984 80 Wie fuhre ich Einfugungen und Updates in einem Alembic Upgrade Skript aus
cmc

Ich empfehle die Verwendung von SQLAlchemy-Kernanweisungen mithilfe einer Ad-hoc-Tabelle. wie in der offiziellen Dokumentation beschrieben, da es die Verwendung von agnostischem SQL und Python-Schreiben ermöglicht und auch in sich abgeschlossen ist. SQLAlchemy Core ist das Beste aus beiden Welten für Migrationsskripte.

Hier ein Beispiel für das Konzept:

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

bind = op.get_bind()
session = orm.Session(bind=bind)

data = {
    "name": "John",
}
ret = session.execute(insert(account).values(data))
# for use in other insert calls
account_id = ret.lastrowid

.

184790cookie-checkWie führe ich Einfügungen und Updates in einem Alembic-Upgrade-Skript aus?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy