Sto lavorando a un progetto con Flask, SQLAlchemy, Alembic e i loro wrapper per Flask (Flask-SQLAlchemy e Flask-Migrate). Ho quattro migrazioni:Perché Flask-Migrate mi sta facendo fare una migrazione in 2 passaggi?
1c5f54d4aa34 -> 4250dfa822a4 (head), Feed: Countries
312c1d408043 -> 1c5f54d4aa34, Feed: Continents
41984a51dbb2 -> 312c1d408043, Basic Structure
<base> -> 41984a51dbb2, Init Alembic
Quando inizio un nuovo e pulito database e si tenta di eseguire le migrazioni ottengo un errore:
[email protected]:/vagrant$ python manage.py db upgrade
...
sqlalchemy.exc.ProgrammingError: (ProgrammingError) relation "continent" does not exist
...
Se chiedo Flask-migrazione per eseguire tutte le migrazioni, ma la per ultimo, funziona. Se dopo che ho eseguito di nuovo il comando di aggiornamento, funziona - cioè, di aggiornare completamente il mio database senza un singolo cambiamento nel codice:
[email protected]:/vagrant$ python manage.py db upgrade 312c1d408043
INFO [alembic.migration] Context impl PostgresqlImpl.
INFO [alembic.migration] Will assume transactional DDL.
INFO [alembic.migration] Running upgrade -> 41984a51dbb2, Init Alembic
INFO [alembic.migration] Running upgrade 41984a51dbb2 -> 312c1d408043, Basic Structure
[email protected]:/vagrant$ python manage.py db upgrade
INFO [alembic.migration] Context impl PostgresqlImpl.
INFO [alembic.migration] Will assume transactional DDL.
INFO [alembic.migration] Running upgrade 312c1d408043 -> 1c5f54d4aa34, Feed: Continents
INFO [alembic.migration] Running upgrade 1c5f54d4aa34 -> 4250dfa822a4, Feed: Countries
TL; DR
L'ultima migrazione (Feed: Paesi) esegue query sulla tabella alimentata dalla precedente (Feed: Continents). Se ho creato e alimentato la tabella dei continenti, gli script dovrebbero funzionare. Ma non è così. Perché devo interrompere il processo di migrazione da allora per riavviarlo in un altro comando? Davvero non capisco. È un comando che Alembic esegue dopo una serie di migrazioni? Qualche idea?
Solo nel caso
I miei modelli sono definiti come segue:
class Country(db.Model):
__tablename__ = 'country'
id = db.Column(db.Integer, primary_key=True)
alpha2 = db.Column(db.String(2), index=True, unique=True)
title = db.Column(db.String(140))
continent_id = db.Column(db.Integer, db.ForeignKey('continent.id'))
continent = db.relationship('Continent', backref='countries')
def __repr__(self):
return '<Country #{}: {}>'.format(self.id, self.title)
class Continent(db.Model):
__tablename__ = 'continent'
id = db.Column(db.Integer, primary_key=True)
alpha2 = db.Column(db.String(2), index=True, unique=True)
title = db.Column(db.String(140))
def __repr__(self):
return '<Continent #{}: {}>'.format(self.id, self.title)
Molte grazie,
UPDATE 1: Il metodo di aggiornamento degli ultimi due migrazioni
Come @Miguel ha chiesto in un commento, qui ci sono l'aggiornamento m ETODI degli ultimi due migrazioni:
Feed: Continenti
def upgrade():
csv_path = app.config['BASEDIR'].child('migrations', 'csv', 'en')
csv_file = csv_path.child('continents.csv')
with open(csv_file) as file_handler:
csv = list(reader(file_handler))
csv.pop(0)
data = [{'alpha2': c[0].lower(), 'title': c[1]} for c in csv]
op.bulk_insert(Continent.__table__, data)
Feed: Paesi (che dipende tavolo alimentato sull'ultima migrazione)
def upgrade():
# load countries iso3166.csv and build a dictionary
csv_path = app.config['BASEDIR'].child('migrations', 'csv', 'en')
csv_file = csv_path.child('iso3166.csv')
countries = dict()
with open(csv_file) as file_handler:
csv = list(reader(file_handler))
for c in csv:
countries[c[0]] = c[1]
# load countries-continents from country_continent.csv
csv_file = csv_path.child('country_continent.csv')
with open(csv_file) as file_handler:
csv = list(reader(file_handler))
country_continent = [{'country': c[0], 'continent': c[1]} for c in csv]
# loop
data = list()
for item in country_continent:
# get continent id
continent_guess = item['continent'].lower()
continent = Continent.query.filter_by(alpha2=continent_guess).first()
# include country
if continent is not None:
country_name = countries.get(item['country'], False)
if country_name:
data.append({'alpha2': item['country'].lower(),
'title': country_name,
'continent_id': continent.id})
CSV I sto usando fondamentalmente seguendo questi modelli:
con tinents.csv
...
AS, "Asia"
EU, "Europe"
NA, "North America"
...
iso3166.csv
...
CL,"Chile"
CM,"Cameroon"
CN,"China"
...
_country_continent.csv_
...
US,NA
UY,SA
UZ,AS
...
Così Feed: Continenti alimenta il tavolo continente e feed: Paesi alimenta la tabella del paese. Ma deve interrogare la tabella dei continenti per creare il collegamento corretto tra il paese e il continente.
UPDATE 2: Qualcuno da Reddit già offerto una spiegazione e una soluzione alternativa
ho chiesto the same question on Reddit e themathemagician detto:
I've run into this before, and the issue is that the migrations don't execute individually, but instead alembic batches all of them (or all of them that need to be run) and then executes the SQL. This means that by the time the last migration is trying to run, the tables don't actually exist yet so you can't actually make queries. Doing
from alembic import op def upgrade(): #migration stuff op.execute('COMMIT') #run queries
This isn't the most elegant solution (and that was for Postgres, the command may be different for other dbs), but it worked for me. Also, this isn't actually an issue with Flask-Migrate as much as an issue with alembic, so if you want to Google for more info, search for alembic. Flask-Migrate is just a wrapper around alembic that works with Flask-Script easily.
È possibile aggiungere i metodi 'upgrade()' delle ultime due migrazioni? – Miguel
@Muel, grazie, ho appena modificato il post e aggiunto i metodi. BTW, [altrove] (https://github.com/cuducos/whiskyton#thanks) Ho detto "grazie" a voi, ma forse non l'avete visto - quindi, condividete ancora una volta haha ... – cuducos