2014-12-13 18 views
6

Flask, SQLAlchemy, Alembic ve Flask (Flask-SQLAlchemy ve Flask-Migrate) için ambalajları üzerinde bir proje üzerinde çalışıyorum. Dört göçler vardır: Bir yeni ve temiz bir veritabanı başlayıp bir hata alıyorum göçler çalıştırmayıFlask-Migrate neden 2 adımlı geçişi yapıyor?

1c5f54d4aa34 -> 4250dfa822a4 (head), Feed: Countries 
312c1d408043 -> 1c5f54d4aa34, Feed: Continents 
41984a51dbb2 -> 312c1d408043, Basic Structure 
<base> -> 41984a51dbb2, Init Alembic 

:

[email protected]:/vagrant$ python manage.py db upgrade 
... 
sqlalchemy.exc.ProgrammingError: (ProgrammingError) relation "continent" does not exist 
... 

Ben Flask-geçirme Tüm taşıma işlemlerini çalıştırmak için sorarsanız ancak Son, işe yarıyor. Ondan sonra tekrar yükseltme komutunu çalıştırırsanız, çalışır - yani, tam kodda tek değişiklik olmadan benim veritabanı yükseltir:

[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

son göç (Besleme: Ülkeler), bir öncekiyle beslenen tabloda sorgulama yapar (Feed: Continents). Kıtalar tablosunu oluşturup beslediğimde, betikler çalışmalıdır. Ama öyle değil. Neden başka bir komutta yeniden başlatmak için geçiş işlemini durdurmam gerekiyor? Bunu gerçekten anlamadım. Alembic, bir dizi göçten sonra yürüten bir komut mu? Herhangi bir fikir?

Benim modeller olarak tanımlanır Her ihtimale karşı

aşağıdaki gibidir:

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) 

çok teşekkür ederiz,

UPDATE 1: son iki göçlerden geçiş yöntemini

@Miguel bir yorumda sordu, burada yükseltme m var Son iki göçler edilmesinin yöntemlerine:

Feed: Kıtalar

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:

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 (son göç beslenen masaya bağlıdır) Ülkeler kullanıyorum temelde şu kalıpları izliyor:

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 
... 

Yani Feed: Kıtalar kıta tablosunu yayınları ve Feed: Ülkeler ülke tablosunu besler. Ancak, kıta ile ülke arasında doğru bağlantıyı kurmak için kıta tablosunu sorgulamak zorunda.

GÜNCELLEME 2: Reddit bazı biri zaten bir açıklama ve

Ben the same question on Reddit sorulan bir çözüm sundu ve themathemagician söyledi: reddit'te @themathemagician tarafından belirtildiği gibi

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.

+1

Son iki geçişin 'upgrade()' yöntemlerini ekleyebilir misiniz? – Miguel

+0

@Miguel, teşekkürler, sadece yazıyı düzenledim ve yöntemleri ekledim. BTW, [başka bir yerde] (https://github.com/cuducos/whiskyton#thanks) Sana “teşekkür ederim” dedim ama belki de görmedin - yani, bir kez daha haha… – cuducos

cevap

8

, Alembic varsayılan olarak Tüm geçişleri tek bir işlemde çalıştırır, bu nedenle veritabanı motoruna ve geçiş komut dosyalarınızda yaptığınız işe bağlı olarak, önceki bir geçişte eklenenlere bağlı olan bazı işlemler başarısız olabilir.

Bunu kendim denemedim, ancak Alembic 0.6.5 bu sorunu ele alan bir transaction_per_migration seçeneğini tanıttı. Bu, env.py numaralı telefondan configure() numaralı aramanın bir seçenektir.

def run_migrations_online(): 
    """Run migrations in 'online' mode. 

    # ... 
    context.configure(
       connection=connection, 
       target_metadata=target_metadata, 
       transaction_per_migration=True  # <-- add this 
       ) 
    # ... 

Ayrıca, aynı zamanda çevrimdışı taşıma işlemini planlıyorsanız size configure() çağrıyı düzeltmek gerekir unutmayın: Flask-geçirme onları yaratan olarak varsayılan yapılandırma dosyalarını kullanıyorsanız, migrations/env.py düzelteceğiz nerede, o zaman bu olduğunu run_migrations_offline() da aynı şekilde.

Bunu deneyin ve sorunu giderirse bildirin.

+0

Yay ! Bu çalıştı Miguel! Çok teşekkürler, – cuducos

İlgili konular