1

Flask-Migrate generated a very strange error that I can't decipher. Running flask db init and flask db migrate worked fine, but when I ran flask db upgrade Flask-Migrate stopped on a TypeError on the line sa.PrimaryKeyConstraint('id'). It left this very long stacktrace:

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

Can someone more experienced that me explain this error? I'm happy to provide any info about my database schema if necessary. Also, I was getting the same error with sqlalchemy_migrate.

UPDATE: Here's my migration.

"""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 ###

I made a minimal file to reproduce the error:

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

To reproduce:

-copy the above text into a file called app.py and save in an empty directory

-using a terminal, cd into the directory

-run the following (for *nix/mac, windows is probably a little different)

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

to throw the error.

2
  • update: my computer crashed and now when I try it flask-migrate complains that the table is already there. If the problem somehow fixed itself, will close question. Commented Oct 12, 2017 at 5:27
  • 1
    Please provide a minimal reproducible example, including especially, but not limited to, the contents of your cb774072558f_.py migration. Commented Oct 12, 2017 at 5:46

1 Answer 1

2

Turns out, the very last line:

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

was throwing the error. Removing the quotes around 500 made everything work fine.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.