flask-migrate 處理sqlite數據庫報錯Constraint must have a name 的解決方案

一、問題描述
最近在使用 flask migrate命令生成自動遷移腳本時,每次使用flask upgrade命令進行數據庫更新,會出現這樣一個報錯:ValueError: Constraint must have a name,找了很久才找到問題解決方案。實測解決,內容如下

下圖爲錯誤的的內容

Traceback (most recent call last):
  File "/home/openlab/flasky/venv/bin/flask", line 11, in <module>
    sys.exit(main())
  File "/home/openlab/flasky/venv/lib/python3.6/site-packages/flask/cli.py", line 894, in main
    cli.main(args=args, prog_name=name)
  File "/home/openlab/flasky/venv/lib/python3.6/site-packages/flask/cli.py", line 557, in main
    return super(FlaskGroup, self).main(*args, **kwargs)
  File "/home/openlab/flasky/venv/lib/python3.6/site-packages/click/core.py", line 697, in main
    rv = self.invoke(ctx)
  File "/home/openlab/flasky/venv/lib/python3.6/site-packages/click/core.py", line 1066, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/openlab/flasky/venv/lib/python3.6/site-packages/click/core.py", line 1066, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/openlab/flasky/venv/lib/python3.6/site-packages/click/core.py", line 895, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/openlab/flasky/venv/lib/python3.6/site-packages/click/core.py", line 535, in invoke
    return callback(*args, **kwargs)
  File "/home/openlab/flasky/venv/lib/python3.6/site-packages/click/decorators.py", line 17, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/home/openlab/flasky/venv/lib/python3.6/site-packages/flask/cli.py", line 412, in decorator
    return __ctx.invoke(f, *args, **kwargs)
  File "/home/openlab/flasky/venv/lib/python3.6/site-packages/click/core.py", line 535, in invoke
    return callback(*args, **kwargs)
  File "/home/openlab/flasky/venv/lib/python3.6/site-packages/flask_migrate/cli.py", line 134, in upgrade
    _upgrade(directory, revision, sql, tag, x_arg)
  File "/home/openlab/flasky/venv/lib/python3.6/site-packages/flask_migrate/__init__.py", line 95, in wrapped
    f(*args, **kwargs)
  File "/home/openlab/flasky/venv/lib/python3.6/site-packages/flask_migrate/__init__.py", line 280, in upgrade
    command.upgrade(config, revision, sql=sql, tag=tag)
  File "/home/openlab/flasky/venv/lib/python3.6/site-packages/alembic/command.py", line 254, in upgrade
    script.run_env()
  File "/home/openlab/flasky/venv/lib/python3.6/site-packages/alembic/script/base.py", line 427, in run_env
    util.load_python_file(self.dir, 'env.py')
  File "/home/openlab/flasky/venv/lib/python3.6/site-packages/alembic/util/pyfiles.py", line 81, in load_python_file
    module = load_module_py(module_id, path)
  File "/home/openlab/flasky/venv/lib/python3.6/site-packages/alembic/util/compat.py", line 82, in load_module_py
    spec.loader.exec_module(module)
  File "<frozen importlib._bootstrap_external>", line 678, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "migrations/env.py", line 88, in <module>
    run_migrations_online()
  File "migrations/env.py", line 81, in run_migrations_online
    context.run_migrations()
  File "<string>", line 8, in run_migrations
  File "/home/openlab/flasky/venv/lib/python3.6/site-packages/alembic/runtime/environment.py", line 836, in run_migrations
    self.get_context().run_migrations(**kw)
  File "/home/openlab/flasky/venv/lib/python3.6/site-packages/alembic/runtime/migration.py", line 330, in run_migrations
    step.migration_fn(**kw)
  File "/home/openlab/flasky/flasky/migrations/versions/bb9a3eb8a5fb_second_migrate.py", line 30, in upgrade
    batch_op.create_unique_constraint(None, ['name'])
  File "/usr/lib/python3.6/contextlib.py", line 88, in __exit__
    next(self.gen)
  File "/home/openlab/flasky/venv/lib/python3.6/site-packages/alembic/operations/base.py", line 300, in batch_alter_table
    impl.flush()
  File "/home/openlab/flasky/venv/lib/python3.6/site-packages/alembic/operations/batch.py", line 76, in flush
    fn(*arg, **kw)
  File "/home/openlab/flasky/venv/lib/python3.6/site-packages/alembic/operations/batch.py", line 343, in add_constraint
    raise ValueError("Constraint must have a name")
ValueError: Constraint must have a name


二、解決方案
步驟1:新增插件flask-migrate以及flask_sqlalchemy的初始化實參

from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import MetaData
from flask_migrate import Migrate

###初始化插件
#定義命名慣例,不需要改

naming_convention = {
    "ix": 'ix_%(column_0_label)s',
    "uq": "uq_%(table_name)s_%(column_0_name)s",
    "ck": "ck_%(table_name)s_%(column_0_name)s",
    "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
    "pk": "pk_%(table_name)s"
}


#初始化db,將命名慣例naming_convention傳給SQL_Alchemy,解決“ValueError: Constraint must have a name"的問題

db = SQLAlchemy(app=app,metadata=MetaData(naming_convention=naming_convention))


#使用batch操作替換普通操作,因爲普通操作不支持表名,列名的改變!

migrate = Migrate(app,db,render_as_batch=True)

工廠函數初始化的方式與上面類似,就是把app去掉直接在工廠函數中初始化
下列操作必須已經安裝了flask-migrate;

步驟2:生成自動化遷移腳本

flask db init
flask db migrate -m 'first migrate'

步驟3:檢查生成的腳本,打開(./migrations/versions/XXXX.py)文件查看內容

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    with op.batch_alter_table('roles', schema=None) as batch_op:
        batch_op.add_column(sa.Column('name2', sa.String(length=64), nullable=True))
        batch_op.create_unique_constraint(batch_op.f('uq_roles_name'), ['name'])
        batch_op.create_unique_constraint(batch_op.f('uq_roles_name1'), ['name1'])
        batch_op.create_unique_constraint(batch_op.f('uq_roles_name2'), ['name2'])
        batch_op.drop_constraint('uq_roles_id', type_='unique')

    with op.batch_alter_table('users', schema=None) as batch_op:
        batch_op.create_unique_constraint(batch_op.f('uq_users_name555'), ['name555'])

    # ### end Alembic commands ###


def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    with op.batch_alter_table('users', schema=None) as batch_op:
        batch_op.drop_constraint(batch_op.f('uq_users_name555'), type_='unique')

    with op.batch_alter_table('roles', schema=None) as batch_op:
        batch_op.create_unique_constraint('uq_roles_id', ['name1'])
        batch_op.drop_constraint(batch_op.f('uq_roles_name2'), type_='unique')
        batch_op.drop_constraint(batch_op.f('uq_roles_name1'), type_='unique')
        batch_op.drop_constraint(batch_op.f('uq_roles_name'), type_='unique')
        batch_op.drop_column('name2')

自動生成的腳本可能有錯,要人工檢查。

‘uq_roles_id’就是按照naming_convention生成的constraint的name,之前該位置的值爲None,這是報錯 Constraint must have a name的關鍵。

步驟4:執行更新操作

flask db upgrade

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章