Home » Beitrag verschlagwortet mit 'DB Migration'

Schlagwort-Archive: DB Migration

Flask DB Migrations

Links

Flask-Migrate ist die Library um DB-Modell-Änderungen, die mit SQLAlchemy ausgedrückt sind zu kontrollieren. Es benutzt dazu die Library Alembic.

Schritte

Aufsetzen Flask Migrate Tool

Steps:

pip3 install Flask-Migrate

Projekt bezüglich Migrations-Handling aufsetzen

flask db init

Das setzt verschiedene Migrations-Script-Folders und Tools im SW-Projekt bereit.


Output:

C:\tmp\Python_Workspace1\task-app>flask db init
Creating directory C:\tmp\Python_Workspace1\task-app\migrations ...  done
Creating directory C:\tmp\Python_Workspace1\task-app\migrations\versions ...  done
Generating C:\tmp\Python_Workspace1\task-app\migrations\alembic.ini ...  done
Generating C:\tmp\Python_Workspace1\task-app\migrations\env.py ...  done
Generating C:\tmp\Python_Workspace1\task-app\migrations\README ...  done
Generating C:\tmp\Python_Workspace1\task-app\migrations\script.py.mako ...  done
Please edit configuration/connection/logging settings in 'C:\\tmp\\Python_Workspace1\\task-app\\migrations\\alembic.ini' before proceeding.

Funktioniert das Kommando nicht, weil die DB noch nicht gestartet ist, dann etwa folgendes absetzen:

pg_ctl -D "C:\Program Files\PostgreSQL\13\data" start

DB-Migrationen durchführen

flask db migrate

Erkennt die Anforderungen an das DB-Modell aus den Python-Scripts (aus den SQLAlchemy Model-Objekt-Definitionen) heraus und erstellt eine Migration (DDL Statements um das erforderliche DB-Model zu erstellen).
Ich gehe schwer davon aus, das dazu FLASK_APP auf das Python-Script mit den Entitätsdefinitionen zeigen muss.

Bsp. einer SQLAlchemy Model-Objekt-Definition im *.py:

class Todo(db.Model):
    __tablename__ = 'todos'
    id = db.Column(db.Integer, primary_key=True)
    description = db.Column(db.String(), nullable=False)

Falls das erzeugte Script das ganze DB-Modell von Grund auf neu erzeugen soll, dann darf die DB diese Entitäten nicht schon enthalten. Das kann man zum Bsp. erreichen durch:

dropdb --username=postgres todoDB
createdb --username=postgres todoDB

!Stelle sicher, dass das Pyton-Script, nicht schon selbst die DB-Objekte erstellt (Z.B. Kommando db.create_all() darf nicht auch schon im Python-Code enthalten sein).

Durchführung:

C:\tmp\Python_Workspace1\task-app>flask db migrate

INFO  [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO  [alembic.runtime.migration] Will assume transactional DDL.
INFO  [alembic.autogenerate.compare] Detected added table 'todos'
Generating C:\tmp\Python_Workspace1\task-app\migrations\versions\70ee48c313e4_.py ...  done

C:\tmp\Python_Workspace1\task-app>

Ein Migrations-File wurde erstellt: task-app/migrations/versions/70ee48c313e4_.py:

"""empty message

Revision ID: 70ee48c313e4
Revises: 
Create Date: 2021-07-22 15:15:43.733091

"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '70ee48c313e4'
down_revision = None
branch_labels = None
depends_on = None


def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('todos',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('description', sa.String(), nullable=False),
    sa.PrimaryKeyConstraint('id')
    )
    # ### end Alembic commands ###


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

Migration auf die DB spielen:

C:\tmp\Python_Workspace1\task-app>flask db upgrade
INFO  [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO  [alembic.runtime.migration] Will assume transactional DDL.
INFO  [alembic.runtime.migration] Running upgrade  -> 70ee48c313e4, empty message

Migration rückgängig machen wäre:

flask db downgrade

Prüfung, ob DB-Migrationen auf DB angekommen sind:

C:\tmp\Python_Workspace1\task-app>psql --username=postgres todoDB
Passwort für Benutzer postgres:
todoDB=# \dt
              Liste der Relationen
 Schema |      Name       |   Typ   | Eigent³mer
--------+-----------------+---------+------------
 public | alembic_version | Tabelle | postgres
 public | todos           | Tabelle | postgres
(2 Zeilen)


todoDB=#

Beachte die alembic_version Tabelle! Beinhaltet die Migrationsinfo. Prinzipiell nicht durch den Programmierer zu verändern.

Wenn nun Änderungen am DB-Modell innerhalb des Python-Scripts gemacht werden (z.B. wird hier das boolean Feld ‚completed‘ neu hinzu gefügt),

class Todo(db.Model):
    __tablename__ = 'todos'
    id = db.Column(db.Integer, primary_key=True)
    description = db.Column(db.String(), nullable=False)
    completed = db.Column(db.Boolean(), nullable=False, default=False)

…, dann wird mit flask db migrate ein neues Update-Script erstellt und mit flask db update dieser Update auf die DB gespielt.

Beheben der Problematik des Hinzufügens von NOT-NULL Feldern:
Das Migrations-Script folgendermassen anpassen:

from alembic import op

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.add_column('todos', sa.Column('completed', sa.Boolean(), nullable=True))
    op.execute('update todos set completed=False where completed is null')
    op.alter_column('todos', 'completed', nullable=False)
    # ### end Alembic commands ###