flask-migrate生成无法解释的TypeErrors

时间:2017-10-12 01:46:15

标签: flask sqlalchemy flask-sqlalchemy alembic flask-migrate

Flask-Migrate产生了一个非常奇怪的错误,我无法破译。运行flask db initflask db migrate运行正常,但是当我运行flask db upgrade时,Flask-Migrate在sa.PrimaryKeyConstraint('id')行上的TypeError上停止了。它留下了这么长的堆栈跟踪:

INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.runtime.migration] Running upgrade  -> cb774072558f, empty message
Traceback (most recent call last):
  File "/obfuscateddir/flask/bin/flask", line 11, in <module>
    sys.exit(main())
  File "/obfuscateddir/flask/lib/python3.5/site-packages/flask/cli.py", line 513, in main
    cli.main(args=args, prog_name=name)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/flask/cli.py", line 380, in main
    return AppGroup.main(self, *args, **kwargs)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/click/core.py", line 697, in main
    rv = self.invoke(ctx)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/click/core.py", line 1066, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/obfuscateddir/flask/lib/python3.5/site-packages/click/core.py", line 1066, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/obfuscateddir/flask/lib/python3.5/site-packages/click/core.py", line 895, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/click/core.py", line 535, in invoke
    return callback(*args, **kwargs)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/click/decorators.py", line 17, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/flask/cli.py", line 257, in decorator
    return __ctx.invoke(f, *args, **kwargs)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/click/core.py", line 535, in invoke
    return callback(*args, **kwargs)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/flask_migrate/cli.py", line 134, in upgrade
    _upgrade(directory, revision, sql, tag, x_arg)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/flask_migrate/__init__.py", line 259, in upgrade
    command.upgrade(config, revision, sql=sql, tag=tag)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/alembic/command.py", line 254, in upgrade
    script.run_env()
  File "/obfuscateddir/flask/lib/python3.5/site-packages/alembic/script/base.py", line 425, in run_env
    util.load_python_file(self.dir, 'env.py')
  File "/obfuscateddir/flask/lib/python3.5/site-packages/alembic/util/pyfiles.py", line 93, in load_python_file
    module = load_module_py(module_id, path)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/alembic/util/compat.py", line 64, in load_module_py
    module_id, path).load_module(module_id)
  File "<frozen importlib._bootstrap_external>", line 388, in _check_name_wrapper
  File "<frozen importlib._bootstrap_external>", line 809, in load_module
  File "<frozen importlib._bootstrap_external>", line 668, in load_module
  File "<frozen importlib._bootstrap>", line 268, in _load_module_shim
  File "<frozen importlib._bootstrap>", line 693, in _load
  File "<frozen importlib._bootstrap>", line 673, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 665, in exec_module
  File "<frozen importlib._bootstrap>", line 222, in _call_with_frames_removed
  File "migrations/env.py", line 87, in <module>
    run_migrations_online()
  File "migrations/env.py", line 80, in run_migrations_online
    context.run_migrations()
  File "<string>", line 8, in run_migrations
  File "/obfuscateddir/flask/lib/python3.5/site-packages/alembic/runtime/environment.py", line 836, in run_migrations
    self.get_context().run_migrations(**kw)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/alembic/runtime/migration.py", line 330, in run_migrations
    step.migration_fn(**kw)
  File "/obfuscateddir/migrations/versions/cb774072558f_.py", line 59, in upgrade
    sa.PrimaryKeyConstraint('id')
  File "<string>", line 8, in create_table
  File "<string>", line 3, in create_table
  File "/obfuscateddir/flask/lib/python3.5/site-packages/alembic/operations/ops.py", line 1120, in create_table
    return operations.invoke(op)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/alembic/operations/base.py", line 318, in invoke
    return fn(self, operation)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/alembic/operations/toimpl.py", line 101, in create_table
    operations.impl.create_table(table)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/alembic/ddl/impl.py", line 194, in create_table
    self._exec(schema.CreateTable(table))
  File "/obfuscateddir/flask/lib/python3.5/site-packages/alembic/ddl/impl.py", line 118, in _exec
    return conn.execute(construct, *multiparams, **params)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/sqlalchemy/engine/base.py", line 945, in execute
    return meth(self, multiparams, params)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/sqlalchemy/sql/ddl.py", line 68, in _execute_on_connection
    return connection._execute_ddl(self, multiparams, params)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/sqlalchemy/engine/base.py", line 996, in _execute_ddl
    if not self.schema_for_object.is_default else None)
  File "<string>", line 1, in <lambda>
  File "/obfuscateddir/flask/lib/python3.5/site-packages/sqlalchemy/sql/elements.py", line 436, in compile
    return self._compiler(dialect, bind=bind, **kw)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/sqlalchemy/sql/ddl.py", line 26, in _compiler
    return dialect.ddl_compiler(dialect, self, **kw)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/sqlalchemy/sql/compiler.py", line 216, in __init__
    self.string = self.process(self.statement, **compile_kwargs)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/sqlalchemy/sql/compiler.py", line 242, in process
    return obj._compiler_dispatch(self, **kwargs)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/sqlalchemy/sql/visitors.py", line 81, in _compiler_dispatch
    return meth(self, **kw)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/sqlalchemy/sql/compiler.py", line 2338, in visit_create_table
    and not first_pk)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/sqlalchemy/sql/compiler.py", line 242, in process
    return obj._compiler_dispatch(self, **kwargs)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/sqlalchemy/sql/visitors.py", line 81, in _compiler_dispatch
    return meth(self, **kw)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/sqlalchemy/sql/compiler.py", line 2369, in visit_create_column
    first_pk=first_pk
  File "/obfuscateddir/flask/lib/python3.5/site-packages/sqlalchemy/dialects/sqlite/base.py", line 863, in get_column_specification
    column.type, type_expression=column)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/sqlalchemy/sql/compiler.py", line 290, in process
    return type_._compiler_dispatch(self, **kw)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/sqlalchemy/sql/visitors.py", line 81, in _compiler_dispatch
    return meth(self, **kw)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/sqlalchemy/sql/compiler.py", line 2783, in visit_string
    return self.visit_VARCHAR(type_, **kw)
  File "/obfuscateddir/flask/lib/python3.5/site-packages/sqlalchemy/sql/compiler.py", line 2729, in visit_VARCHAR
    return self._render_string_type(type_, "VARCHAR")
  File "/obfuscateddir/flask/lib/python3.5/site-packages/sqlalchemy/sql/compiler.py", line 2717, in _render_string_type
    text += "(%d)" % type_.length
TypeError: %d format: a number is required, not str

有人可以更有经验解释这个错误吗?如有必要,我很乐意提供有关我的数据库架构的任何信息。另外,我在sqlalchemy_migrate中遇到了同样的错误。

更新:这是我的迁移。

"""empty message

Revision ID: cb774072558f
Revises: 
Create Date: 2017-10-11 18:32:12.235047

"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = 'cb774072558f'
down_revision = None
branch_labels = None
depends_on = None


def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('game',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('turn', sa.Integer(), nullable=True),
    sa.Column('phase', sa.String(length=20), nullable=True),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('user',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('username', sa.String(length=64), nullable=True),
    sa.Column('password', sa.String(length=64), nullable=True),
    sa.Column('email', sa.String(length=120), nullable=True),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_user_email'), 'user', ['email'], unique=True)
    op.create_index(op.f('ix_user_username'), 'user', ['username'], unique=True)
    op.create_table('player',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('type', sa.Integer(), nullable=True),
    sa.Column('user_id', sa.Integer(), nullable=True),
    sa.Column('game_id', sa.Integer(), nullable=True),
    sa.Column('attackpower', sa.Integer(), nullable=True),
    sa.Column('defensepower', sa.Integer(), nullable=True),
    sa.Column('destruction', sa.Integer(), nullable=True),
    sa.ForeignKeyConstraint(['game_id'], ['game.id'], ),
    sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('action',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('type', sa.String(), nullable=True),
    sa.Column('origin', sa.Integer(), nullable=True),
    sa.Column('dest', sa.Integer(), nullable=True),
    sa.Column('start_turn', sa.Integer(), nullable=True),
    sa.Column('end_turn', sa.Integer(), nullable=True),
    sa.Column('count', sa.Integer(), nullable=True),
    sa.Column('special', sa.String(length='500'), nullable=True),
    sa.ForeignKeyConstraint(['dest'], ['player.id'], ),
    sa.ForeignKeyConstraint(['origin'], ['player.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    # ### end Alembic commands ###


def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_table('action')
    op.drop_table('player')
    op.drop_index(op.f('ix_user_username'), table_name='user')
    op.drop_index(op.f('ix_user_email'), table_name='user')
    op.drop_table('user')
    op.drop_table('game')
    # ### end Alembic commands ###

我制作了一个最小的文件来重现错误:

from flask import Flask
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'
db = SQLAlchemy(app)
migrate = Migrate(app, db)

@app.route('/')
def index():
    return "this isn't really part of the example"


class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), index=True, unique=True)
    password = db.Column(db.String(64))
    email = db.Column(db.String(120), index=True, unique=True)
    players = db.relationship('Player', backref='user', lazy=True)
    #games = db.relationship('Game', backref='user', lazy='dynamic')

    def __repr__(self):
        return '<User %r>' % (self.username)

    #is the user allowed to authenticate?
    @property
    def is_authenticated(self):
        return True

    #is the user active and unbanned?
    @property
    def is_active(self):
        return True

    #for fake users that can't login
    @property
    def is_anonymous(self):
        return False

    #return an id
    def get_id(self):
        try:
            return unicode(self.id) #i need this for py2
        except NameError:
            return str(self.id)

class Game(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(20))
    players = db.relationship('Player', backref='game', lazy=True)
    turn = db.Column(db.Integer)
    phase = db.Column(db.String(20))
    actions = db.relationship('Action', backref='game', lazy=True)

    def __repr__(self):
        return '<Game %r>' % (self.id)

class Player(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    type = db.Column(db.Integer)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    game_id = db.Column(db.Integer, db.ForeignKey('game.id'))
    attackpower = db.Column(db.Integer)
    defensepower = db.Column(db.Integer)
    destruction = db.Column(db.Integer) #maybe constrain to >= 100?
    def __repr__(self):
        return '<Player %r>' % (self.id)

#is this the best way to do this?
class Action(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    type = db.Column(db.String)
    origin = db.Column(db.Integer, db.ForeignKey('player.id'))
    dest = db.Column(db.Integer, db.ForeignKey('player.id'))
    start_turn = db.Column(db.Integer)
    end_turn = db.Column(db.Integer)
    count = db.Column(db.Integer)
    special = db.Column(db.String('500')) #contains json-dumped dicts

重现:

- 将上述文本复制到名为app.py的文件中并保存在空目录中

- 使用终端,cd进入目录

-run以下(对于* nix / mac,windows可能有点不同)

virtualenv flask
source flask/bin/activate
pip install flask flask-sqlalchemy flask-migrate
export FLASK_APP=app.py
flask db init
flask db migrate
flask db upgrade

抛出错误。

1 个答案:

答案 0 :(得分:1)

原来,最后一行:

    special = db.Column(db.String('500')) #contains json-dumped dicts

抛出错误。删除500左右的引号使一切正常。