我正在与Flask,SQLAlchemy,Alembic以及Flask(Flask-SQLAlchemy和Flask-Migrate)的包装工作。我有四次迁移:
1c5f54d4aa34 -> 4250dfa822a4 (head), Feed: Countries
312c1d408043 -> 1c5f54d4aa34, Feed: Continents
41984a51dbb2 -> 312c1d408043, Basic Structure
<base> -> 41984a51dbb2, Init Alembic
当我启动一个新的干净数据库并尝试运行迁移时,我收到一个错误:
vagrant@precise32:/vagrant$ python manage.py db upgrade
...
sqlalchemy.exc.ProgrammingError: (ProgrammingError) relation "continent" does not exist
...
如果我要求Flask-Migrate运行所有迁移但是最后一次迁移,它会起作用。如果在那之后我再次运行升级命令,它就可以工作 - 也就是说,它完全升级了我的数据库而没有一次代码更改:
vagrant@precise32:/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
vagrant@precise32:/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
上次迁移(Feed:Countries)对前一个(Feed:Continents)提供的表运行查询。如果我有大陆表创建和馈送,脚本应该工作。但它并没有。 为什么我必须在此之间停止迁移过程以在另一个命令中重新启动它?我真的不明白。是否有一些命令Alembic在一系列迁移后执行?有什么想法吗?
以防万一
我的模型定义如下:
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)
非常感谢,
更新1:最近两次迁移的升级方法
正如@Miguel在评论中所说,这里有最后两次迁移的升级方法:
Feed:各大洲
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基本上遵循以下模式:
continents.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
...
所以 Feed:Continents 提供大陆表, Feed:Countries 提供国家/地区表格。但它必须查询大陆表,以便在国家和大陆之间建立适当的联系。
更新2:Reddit中的某人已经提供了解释和解决方法
我问the same question on Reddit,themathemagician说:
我之前遇到过这种情况,问题是迁移不会发生 单独执行,但是alembic批处理所有这些(或全部 需要运行的那些)然后执行SQL。这意味着 在最后一次迁移试图运行时,表格没有 实际上已经存在,所以你实际上无法进行查询。做
from alembic import op def upgrade(): #migration stuff op.execute('COMMIT') #run queries
这不是最优雅的解决方案(那是Postgres,也就是 命令可能与其他dbs不同),但它对我有用。也, 对于Flask-Migrate而言,这实际上并不是一个问题 使用alembic,所以如果你想谷歌获取更多信息,请搜索 蒸馏器。 Flask-Migrate只是一个有效的alembic包装器 很容易使用Flask-Script。
答案 0 :(得分:9)
正如@themathemagician在reddit上指出的那样,Alembic默认在单个事务中运行所有迁移,因此根据数据库引擎和迁移脚本中的操作,某些操作依赖于先前迁移中添加的内容,失败。
我自己没有尝试过,但是Alembic 0.6.5引入了transaction_per_migration
选项,可能会解决这个问题。这是configure()
中env.py
来电的选项。如果您使用默认配置文件,因为Flask-Migrate会创建它们,那么您可以在migrations/env.py
中解决此问题:
def run_migrations_online():
"""Run migrations in 'online' mode.
# ...
context.configure(
connection=connection,
target_metadata=target_metadata,
transaction_per_migration=True # <-- add this
)
# ...
另请注意,如果您还计划同时运行离线迁移,则需要以相同方式修复configure()
中的run_migrations_offline()
来电。
尝试一下,让我知道它是否解决了这个问题。