Nu när jag nästan var klar med min nya hemsida, bara lite puts kvar så började plötsligt mina databasmigreringar att strula.
Please edit configuration/connection/logging settings in 'C:\\Users\\maria\\Documents\\PyCharmProjects\\MittProjekt\\migrations\\alembic.ini' before proceeding.
Detta är vad jag fann för att rätta till problemet:
Varför visas felet ”Please edit configuration/connection/logging settings…”?
Det meddelande som visas (”Please edit configuration/connection/logging settings in ’…/migrations/alembic.ini’ before proceeding.”) är Alembics generella uppmaning efter att man initierat en ny migrationsmiljö. Alembic påminner om att man måste ange vissa inställningar (framförallt databasens URL) i konfigurationsfilen alembic.ini innan man går vidare (alembic.sqlalchemy.org). Om man inte har justerat denna fil (eller på annat sätt försett Alembic med databasens anslutningssträng) kommer Alembic att varna om att standardinställningarna fortfarande är ogiltiga.
I en Flask-applikation som följer application factory-mönstret (där appen skapas via en funktion create_app), definieras databasens URL oftast i app-konfigurationen (app.config['SQLALCHEMY_DATABASE_URI']). När man kör flask db init skapas en alembic.ini med en platshållar-URL (t.ex. driver://user:pass@localhost/dbname). Om denna inte ersätts eller överskrivs får man ovanstående meddelande. Felet orsakas alltså av att Alembic ännu inte vet vilken databas den ska koppla upp mot. I praktiken är det ingen kraschande bugg, utan en uppmaning att konfigurera Alembic korrekt. Om Flask-Migrate används korrekt kan man ofta ignorera meddelandet och fortsätta med t.ex. flask db migrate – Flask-Migrate kommer då att hämta databas-URL:en från Flask-appen automatiskt (stackoverflow.com). Men för att undvika förvirring bör man se till att Alembic-konfigurationen ställs in rätt.
Anpassa alembic.ini och env.py för att fungera med create_app
För att Alembic ska fungera ihop med en Flask-app som skapas dynamiskt via create_app behöver man justera både alembic.ini och migrations/env.py:
- alembic.ini: Här kan man ange databasens URL direkt under sektionen
[alembic]med nyckelnsqlalchemy.url. Till exempel för MySQL:
sqlalchemy.url = mysql+pymysql://<user>:<password>@<host>/<databasename>
Detta hårdkodar anslutningssträngen. Alternativt kan man låta denna rad vara en dummy (eller använda en miljövariabelsreferens) och istället sätta URL:en via env.py – vilket är vanligare när man använder Flask-Migrate för att hämta appens konfiguration i stället för att duplicera den i alembic.ini.
Övriga viktiga rader i alembic.ini är normalt: script_location (som ska peka på migrationskatalogen, t.ex. migrations) och eventuellt inställningar för loggning. Standardvärdena som genererats av flask db init brukar redan vara korrekta för dessa, men bekräfta att script_location = migrations matchar din projektstruktur. För MySQL kan man även försäkra sig om att uppsättningar som encoding (t.ex. utf8mb4) är korrekta om de förekommer.
- migrations/env.py: Denna fil körs vid varje migrationskommando och här behöver vi koppla in Flask-applikationen så att Alembic får tillgång till appens konfiguration (databas-URL) och modeller. I en application factory-arkitektur måste
env.pyantingen:
Använda Flask CLI:s app-kontekst (current_app): Om man kör kommandona via flask db ... och har talat om för Flask hur appen ska skapas (t.ex. genom att sätta miljövariabeln FLASK_APP till "app:create_app"), då kommer Flask att skapa appen och pusha en app context åt Alembic. I env.py kan man då nå appen via flask.current_app. Exempelvis:
from flask import current_app
config.set_main_option('sqlalchemy.url', current_app.config.get('SQLALCHEMY_DATABASE_URI'))
target_metadata = current_app.extensions['migrate'].db.metadata
Här hämtar vi databasens URL från appens konfiguration och sätter den i Alembics config, samt tar ut target_metadata (d.v.s. metadata för alla modeller) från SQLAlchemy-objektet som Flask-Migrate registreratstackoverflow.com. Flask-Migrate extension ser till att current_app.extensions['migrate'].db hänvisar till din SQLAlchemy-instans. (Notera: om din databas-URL innehåller procenttecken % kan du behöva ersätta dem med %% för att configparser inte ska misstolka dem (stackoverflow.com, stackoverflow.com.)
Skapa appen manuellt: Om current_app inte är tillgänglig (t.ex. om man kör Alembic direkt utan Flask CLI), kan man importera och initiera applikationen inom env.py. Då anropar man create_app() själv, och pushar en app context manuellt. På så vis får Alembic samma konfiguration som din applikation. Du måste också importera dina modeller så att de registreras i metadata. Exempel på de relevanta delarna i ett env.py anpassat för application factory:
from alembic import context
from logging.config import fileConfig
import logging
# Konfiguration från alembic.ini
config = context.config
fileConfig(config.config_file_name)
logger = logging.getLogger('alembic.env')
# Importera appen och dess databas
from app import create_app, db
flask_app = create_app() # skapa applikationen
flask_app.app_context().push() # pusha app context för att få current_app
# Importera modelldefinitionerna så att de registreras i metadata
import app.models
# Sätt databaskoppling (URL) dynamiskt från appens config
db_url = flask_app.config['SQLALCHEMY_DATABASE_URI']
config.set_main_option('sqlalchemy.url', db_url)
# Hämta SQLAlchemy-metadata som beskriver modellerna
target_metadata = db.Model.metadata # alternativt db.metadata
def run_migrations_offline():
"""Kör migrationer utan att koppla upp (offline-läge)."""
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True
)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online():
"""Kör migrationer i online-läge, med databaskoppling."""
connectable = db.engine
with connectable.connect() as connection:
context.configure(
connection=connection,
target_metadata=target_metadata,
process_revision_directives=... # ev. utelämnat eller anpassat
)
with context.begin_transaction():
context.run_migrations()
# Avgör läge
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()
I koden ovan skapas appen via factory-funktionen och appens konfiguration (inklusive databas-URL) läses in i Alembic. Vi hämtar target_metadata från vår SQLAlchemy-instans (här antas att db = SQLAlchemy() i app/init.py, och att modellerna är definierade som subklasser av db.Model i app/models.py). Det är viktigt att importera modulpaketet app.models efter att app-context har skapats – detta säkerställer att alla modelldefinitioner faktiskt laddas och registreras. Om man missar att importera modellerna kan Alembic tro att det inte finns några ändringar, eftersom target_metadata då är tomt (innehåller inga tabeller) trots att de finns definierade i koden.
Säkerställ att rätt databas-URL och modeller används
För att verifiera att Alembic/Flask-Migrate verkligen använder korrekt databas och inkluderar modellerna kan man göra följande:
- Kontrollera databas-URL: Kör en migrationskommando (t.ex.
flask db migrateelleralembic revision --autogenerate) med debug/info-loggning på. I terminalens output bör du se att Alembic kopplar upp mot MySQL och inte t.ex. SQLite. Alembic loggar vilken ”Context impl” som används. För MySQL skulle det stå något i stil med ”Context impl MySQLImpl” (för SQLite står det SQLiteImplblog.miguelgrinberg.com). Om du ser MySQL nämnas vet du att den använder rätt driver/databas. Dessutom, om anslutningen misslyckas eller URL:en är felaktig, kommer Alembic att kasta ett fel direkt – då behöver du dubbelkollaSQLALCHEMY_DATABASE_URIi appen eller i alembic.ini. - Kontrollera att modellerna laddas: Efter att ha kört
flask db migrate(autogenerering), öppna den genererade revisionsfilen undermigrations/versions/. Där bör du se skapandet av tabeller och kolumner som matchar dina modellklasser. Om Alembic rapporterar ”No changes in schema detected.” trots att du har oaplicerade modeller, tyder det på attenv.pyinte lyckades ladda in modelldefinitionerna eller att den jämför mot fel databasblog.miguelgrinberg.com. Säkerställ då att du importerat rätt moduler och atttarget_metadatafaktiskt refererar till all metadata (t.ex. viadb.Model.metadata). I vårt exempel importerar viapp.modelsmanuellt i env.py för att undvika detta problem. - Testa migrationerna: Kör
flask db upgradeför att applicera migrationerna på databasen. Om allt är rätt konfigurerat ska dina tabeller nu skapas/uppdateras i MySQL utan fel. Du kan även köraflask db currentför att se aktuell migrationsversion i databasen, vilket bekräftar att Alembic kan kommunicera med databasen.
Rekommenderat arbetssätt med Flask-Migrate och Alembic
- Initiera migrationsrepositoryt: För en ny app gör man normalt
flask db initen gång (det verkar användaren redan ha gjort, vilket skapade mappen migrations/ i projektroten och en alembic.ini). Se till att FLASK_APP är korrekt satt till din factory (t.ex.export FLASK_APP="app:create_app"i Unix, eller motsvarande i Windows) innan du kör init, så att Flask vet hur appen ska skapas vid migrationskommandon. - Skapa migrations (autogenerera): När du har definierat eller ändrat dina modeller, kör
flask db migrate -m "Beskrivning av ändring"för att låta Alembic jämföratarget_metadata(dina modeller) med databasen och generera en ny migrationsfil. Tack vare konfigurationen ovan kommer Alembic att använda appens MySQL-URL och inkludera alla modeller i jämförelsen. (Observera att första gången i ett projekt med befintlig databas kan Alembic tro att inget behöver göras om databasen redan har dessa tabeller. I så fall kan man behöva användaflask db stamp headför att markera databasen som uppdaterad, eller köra migrationskommandot mot en tom databaskopiablog.miguelgrinberg.comblog.miguelgrinberg.com.) - Applicera migrationer: Använd
flask db upgradeför att köra migrationerna och uppdatera databasschemat till senaste version. Detta kommando kommer under huven att anropa Alembic och köraenv.pymed online-läget. Återigen användscreate_app-funktionen i vår konfiguration för att koppla upp mot rätt databas. - Använd Flask-Migrate i utvecklingsflödet: I och med att du har integrerat Alembic med Flask via Flask-Migrate, bör du fortsättningsvis använda
flask db ...-kommandona (snarare än att köra alembic direkt) för att hantera migrationerna. Flask-Migrate sköter kopplingen till appen och kontexten åt dig. Som påpekats av utvecklare är Flask-Migrate enklare att använda i Flask-projekt eftersom det automatiserar mycket av konfigurationenstackoverflow.com. Se till att extensions initieras korrekt icreate_app(d.v.s.db.init_app(app)ochMigrate(app, db)om du inte redan gjort det).




