Flask-migrate: Default value not working correctly.

Created on 8 May 2019  路  10Comments  路  Source: miguelgrinberg/Flask-Migrate

Hi @miguelgrinberg.

I'm trying to add flask-migrate to an existing model with data. My first migration is adding a boolean column called enabled with default True.

class Prediction(db.Model):
    id = db.Column(db.String(32), primary_key=True)
    ...
    enabled = db.Column(db.Boolean, default=True)
    ...

The upgrade method of the migration is generated as following:

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.add_column('prediction', sa.Column('enabled', sa.Boolean(), nullable=False))
    # ### end Alembic commands ###

Applying this migration makes enabled with default as False.
Changing the migration to:
op.add_column('prediction', sa.Column('enabled', sa.Boolean(), nullable=False, default=True))
also makes enabled False by default.
Using server_default makes this error appear on flask db init:

sqlalchemy.exc.ArgumentError: Argument 'arg' is expected to be one of type '<class 'str'>' or '<class 'sqlalchemy.sql.elements.ClauseElement'>' or '<class 'sqlalchemy.sql.elements.TextClause'>', got '<class 'bool'>'

Am i doing something wrong? Thanks

question

Most helpful comment

Depends on the database that you are using. server_default='true' or server_default='1' are likely choices.

All 10 comments

Can you share the code that you used with server_default and the complete error message including the stack trace?

The default argument is not going to work in a migration, because that is a SQLAlchemy level default, not a database default.

Hi @miguelgrinberg, thanks for replying.
I tried server_default just with the value True passed to the column, .

    enabled = db.Column(db.Boolean, nullable=False, server_default=True)

Here's the stack trace:

Traceback (most recent call last):
  File "x/env/lib/python3.6/site-packages/flask/cli.py", line 529, in list_commands
    rv.update(info.load_app().cli.list_commands(ctx))
  File "x/env/lib/python3.6/site-packages/flask/cli.py", line 377, in load_app
    raise_if_not_found=False)
  File "x/env/lib/python3.6/site-packages/flask/cli.py", line 235, in locate_app
    __import__(module_name)
  File "x/app/__init__.py", line 4, in <module>
    from app.blueprints import ACTIVE
  File "x/app/blueprints/__init__.py", line 4, in <module>
    from app.blueprints.predictor import feed, syi, ensemble  # noqa
  File "x/app/blueprints/predictor/feed.py", line 5, in <module>
    from app.blueprints.predictor.utils import (
  File "/Users/cogarcia/dev/pyme-primeraventa-nomaquinable/app/blueprints/predictor/utils.py", line 7, in <module>
    from app.bin.unsubscribe_item import CONTROL_GROUP_PROBABILITY
  File "x/app/bin/unsubscribe_item.py", line 16, in <module>
    from app.models.models import Prediction
  File "x/app/models/models.py", line 5, in <module>
    class Prediction(db.Model):
  File "x/app/models/models.py", line 13, in Prediction
    enabled = db.Column(db.Boolean, nullable=False, server_default=True)
  File "x/env/lib/python3.6/site-packages/sqlalchemy/sql/schema.py", line 1266, in __init__
    args.append(DefaultClause(self.server_default))
  File "x/env/lib/python3.6/site-packages/sqlalchemy/sql/schema.py", line 2456, in __init__
    TextClause), 'arg')
  File "x/env/lib/python3.6/site-packages/sqlalchemy/util/langhelpers.py", line 1104, in assert_arg_type
    (name, ' or '.join("'%s'" % a for a in argtype), type(arg)))
sqlalchemy.exc.ArgumentError: Argument 'arg' is expected to be one of type '<class 'str'>' or '<class 'sqlalchemy.sql.elements.ClauseElement'>' or '<class 'sqlalchemy.sql.elements.TextClause'>', got '<class 'bool'>'

The server_default should be given as text in the native format of the database, not as a Python type. See https://docs.sqlalchemy.org/en/13/core/metadata.html#sqlalchemy.schema.Column.params.server_default.

Should i be using server_default though? I just want every row to have the value of the new column set to True.

you have two options, either set the server default, or else create the column as nullable, then set it to your default using an UPDATE statement, and finally switch it to non-nullable.

I have close to a hundred millions rows, i guess server_default will do.
Thanks so much for the help and your Flask contributions. Your blog got me into Flask.

If it's not too much to ask, how would you describe the server_default as True?

Thanks again

Depends on the database that you are using. server_default='true' or server_default='1' are likely choices.

Thanks, i'll try that. Im closing this issue.

thanks, that鈥檚 help me a lot

thank you

Was this page helpful?
0 / 5 - 0 ratings