【问题标题】:Flask-Migrate creating foreign key with None for no apparent reasonFlask-Migrate 无缘无故地使用 None 创建外键
【发布时间】:2020-09-03 22:19:28
【问题描述】:

我正在尝试升级数据库,但迁移脚本不断创建无外键约束。我试图从用户表中删除一个名为 kiosk 的布尔值,同时添加与 backref 的 kiosk 的 kiosk_id 表的多对一关系。如果我执行一个脚本来删除 kiosk 布尔值,它会很好地删除它,但它拒绝建立从用户表到 kiosk_id 表的多对一关系。

这里是迁移脚本

"""
Revision ID: f76e23eb53f4
Revises: 3354e9f7076a
Create Date: 2020-09-02 20:13:22.217435

"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = 'f76e23eb53f4'
down_revision = '3354e9f7076a'
branch_labels = None
depends_on = None


def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('kiosk_id',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('name', sa.String(length=128), nullable=False),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('kiosk_response_id',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('kiosk_question',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('question', sa.String(length=256), nullable=True),
    sa.Column('kiosk_id', sa.Integer(), nullable=True),
    sa.ForeignKeyConstraint(['kiosk_id'], ['kiosk_id.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('kiosk_response',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('response', sa.String(length=256), nullable=False),
    sa.Column('response_id', sa.Integer(), nullable=True),
    sa.Column('question_id', sa.Integer(), nullable=True),
    sa.ForeignKeyConstraint(['question_id'], ['kiosk_question.id'], ),
    sa.ForeignKeyConstraint(['response_id'], ['kiosk_response_id.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    with op.batch_alter_table('user', schema=None) as batch_op:
        batch_op.add_column(sa.Column('kiosk_id', sa.Integer(), nullable=True))
        #THIS IS THE PROBLEM
        batch_op.create_foreign_key(None, 'kiosk_id', ['kiosk_id'], ['id'])
        #HERE IS THE ISSUE
        batch_op.drop_column('kiosk')

    # ### end Alembic commands ###


def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    with op.batch_alter_table('user', schema=None) as batch_op:
        batch_op.add_column(sa.Column('kiosk', sa.BOOLEAN(), nullable=True))
        batch_op.drop_constraint(None, type_='foreignkey')
        batch_op.drop_column('kiosk_id')

    op.drop_table('kiosk_response')
    op.drop_table('kiosk_question')
    op.drop_table('kiosk_response_id')
    op.drop_table('kiosk_id')
    # ### end Alembic commands ###

我正在尝试添加以下关系。

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(32), index=True, unique=True, nullable=False)
    password_hash = db.Column(db.String(128), nullable=False)
    #Dropping this boolean and replacing with a backref from Kiosk_ID
    #kiosk=db.Column(db.Boolean, default=False)
    #Adding this foriegn key for a 1 to many from kiosk_id
    kiosk_id=db.Column(db.Integer, db.ForeignKey('kiosk_id.id'))
    def __repr__(self):  # pragma: no cover
        return '<user> %r' % (self.username)
class Kiosk_ID(db.Model):
    __tablename__='kiosk_id'

    id=db.Column(db.Integer, primary_key=True)
    name=db.Column(db.String(128), nullable=False)

    users=db.relationship('User', backref='kiosk')
    questions=db.relationship('Kiosk_Question', backref='kiosk')

    def __repr__(self):  # pragma: no cover
        return '<Kiosk_ID: > %r' % (self.name)

class Kiosk_Question(db.Model):
    __tablename__='kiosk_question'

    id=db.Column(db.Integer, primary_key=True)
    question=db.Column(db.String(256))

    kiosk_id=db.Column(db.Integer, db.ForeignKey('kiosk_id.id'))
    responses=db.relationship('Kiosk_Response', backref='question')
    
    def __repr__(self):  # pragma: no cover
        return '<Question: > %r' % (self.question)

class Kiosk_Response_ID(db.Model):
    __tablename__='kiosk_response_id'

    id=db.Column(db.Integer, primary_key=True)

    responses=db.relationship('Kiosk_Response', backref='kiosk_response')

    def __repr__(self):  # pragma: no cover
        return '<Response ID: > %r' % (self.id)

class Kiosk_Response(db.Model):
    __tablename__='kiosk_response'

    id=db.Column(db.Integer, primary_key=True)
    response=db.Column(db.String(256), nullable=False)

    response_id=db.Column(db.Integer, db.ForeignKey('kiosk_response_id.id'))
    question_id=db.Column(db.Integer, db.ForeignKey('kiosk_question.id'))

    def __repr__(self):  # pragma: no cover
        return '<Response: > %r' % (self.response)

我在删除 kiosk 布尔列时遇到了问题,但通过在我的 init 中添加“rend_as_batch=True”来修复它。我认为它不相关,但我无法弄清楚为什么这个错误不断出现,而我大约在同一时间进行了更改。

def create_app(config_class=Config):
    ...
    #Fixes flask db upgrade to allow deleting columns
    with app.app_context():
        if db.engine.url.drivername == 'sqlite':
            migrate.init_app(app, db, render_as_batch=True)
        else:
            migrate.init_app(app, db)
    ...

这是我在运行 flask db migrate 后运行 flask db upgrade 时发生的情况。

INFO  [alembic.runtime.migration] Running upgrade 3354e9f7076a -> 3433e257dcb1, empty message
Traceback (most recent call last):
  File "/home/bob/cat/env/bin/flask", line 8, in <module>
    sys.exit(main())
  File "/home/bob/cat/env/lib/python3.6/site-packages/flask/cli.py", line 967, in main
    cli.main(args=sys.argv[1:], prog_name="python -m flask" if as_module else None)
  File "/home/bob/cat/env/lib/python3.6/site-packages/flask/cli.py", line 586, in main
    return super(FlaskGroup, self).main(*args, **kwargs)
  File "/home/bob/cat/env/lib/python3.6/site-packages/click/core.py", line 782, in main
    rv = self.invoke(ctx)
  File "/home/bob/cat/env/lib/python3.6/site-packages/click/core.py", line 1259, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/bob/cat/env/lib/python3.6/site-packages/click/core.py", line 1259, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/bob/cat/env/lib/python3.6/site-packages/click/core.py", line 1066, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/bob/cat/env/lib/python3.6/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "/home/bob/cat/env/lib/python3.6/site-packages/click/decorators.py", line 21, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/home/bob/cat/env/lib/python3.6/site-packages/flask/cli.py", line 426, in decorator
    return __ctx.invoke(f, *args, **kwargs)
  File "/home/bob/cat/env/lib/python3.6/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "/home/bob/cat/env/lib/python3.6/site-packages/flask_migrate/cli.py", line 134, in upgrade
    _upgrade(directory, revision, sql, tag, x_arg)
  File "/home/bob/cat/env/lib/python3.6/site-packages/flask_migrate/__init__.py", line 96, in wrapped
    f(*args, **kwargs)
  File "/home/bob/cat/env/lib/python3.6/site-packages/flask_migrate/__init__.py", line 271, in upgrade
    command.upgrade(config, revision, sql=sql, tag=tag)
  File "/home/bob/cat/env/lib/python3.6/site-packages/alembic/command.py", line 298, in upgrade
    script.run_env()
  File "/home/bob/cat/env/lib/python3.6/site-packages/alembic/script/base.py", line 489, in run_env
    util.load_python_file(self.dir, "env.py")
  File "/home/bob/cat/env/lib/python3.6/site-packages/alembic/util/pyfiles.py", line 98, in load_python_file
    module = load_module_py(module_id, path)
  File "/home/bob/cat/env/lib/python3.6/site-packages/alembic/util/compat.py", line 184, 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 96, in <module>
    run_migrations_online()
  File "migrations/env.py", line 90, in run_migrations_online
    context.run_migrations()
  File "<string>", line 8, in run_migrations
  File "/home/bob/cat/env/lib/python3.6/site-packages/alembic/runtime/environment.py", line 846, in run_migrations
    self.get_context().run_migrations(**kw)
  File "/home/bob/cat/env/lib/python3.6/site-packages/alembic/runtime/migration.py", line 520, in run_migrations
    step.migration_fn(**kw)
  File "/home/bob/cat/migrations/versions/3433e257dcb1_.py", line 49, in upgrade
    batch_op.drop_column('kiosk')
  File "/usr/lib/python3.6/contextlib.py", line 88, in __exit__
    next(self.gen)
  File "/home/bob/cat/env/lib/python3.6/site-packages/alembic/operations/base.py", line 354, in batch_alter_table
    impl.flush()
  File "/home/bob/cat/env/lib/python3.6/site-packages/alembic/operations/batch.py", line 114, in flush
    fn(*arg, **kw)
  File "/home/bob/cat/env/lib/python3.6/site-packages/alembic/operations/batch.py", line 497, in add_constraint
    raise ValueError("Constraint must have a name")
ValueError: Constraint must have a name

我修好了,有点。我更改了以下蒸馏线:

batch_op.create_foreign_key(None, 'kiosk_id', ['kiosk_id'], ['id'])

到这里

batch_op.create_foreign_key('None', 'kiosk_id', ['kiosk_id'], ['id'])

我给它取什么名字有关系吗? ('None') 我想以后可能会导致问题,但我不确定为什么它首先要求我命名关系。

【问题讨论】:

  • create_foreign_key() 的第一个参数是约束名称。见alembic.sqlalchemy.org/en/latest/…。您是否尝试过在那里设置名称而不是传递None
  • 我尝试命名它,但它不起作用。如果我核对 app.db 和迁移脚本并重新运行 flask db init -> flask db migrate -> flask db upgrade 那么它工作正常。我只是希望我可以让它从这里迁移而不会丢失数据。有没有可能因为我做了一个烧瓶数据库戳头来修复另一个错误,而它做了一些奇怪的事情?
  • 您需要更具体。设置名称后哪个命令不起作用?
  • 当我运行 flask db migrate 时,它​​执行得很好,但它会将 user.kiosk_id 的外键名称设置为 None。当我运行flask db upgrade时,它会抛出一个错误,提示用户.kiosk_id不允许使用None。如果我手动将脚本中的名称更改为某个名称,它仍然会给我一个错误。
  • 抱歉,如果您不提供详细信息,我将无能为力。请问是什么错误?

标签: python flask flask-sqlalchemy flask-migrate


【解决方案1】:

我从这里更改了 alembic 迁移脚本:

batch_op.create_foreign_key(None, 'kiosk_id', ['kiosk_id'], ['id'])

到这里:

batch_op.create_foreign_key('None', 'kiosk_id', ['kiosk_id'], ['id'])

我想我应该选择一个更好的名字,但这暂时解决了它。

【讨论】:

  • 如果您添加其他外键,这可能会导致一些令人讨厌的冲突。我建议将其命名为 'fk_kiosk_id_id' 或类似的名称,以指定此键中使用的表/列。我真的不明白为什么 Flask-Migrate(或者可能是潜在的 alembic)不为此做一些自动命名。
猜你喜欢
  • 2022-08-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-12
  • 1970-01-01
  • 1970-01-01
  • 2020-03-06
  • 1970-01-01
相关资源
最近更新 更多