Flask-migrate: "option values must be strings" error on db migrate

Created on 22 Jun 2015  路  14Comments  路  Source: miguelgrinberg/Flask-Migrate

I run

$ python run.py db init

And that runs without issue. Then,

$ python run.py db migrate -m 'First migration'
Traceback (most recent call last):
  File "run.py", line 3, in <module>
    myapp.manager.run()
  File "/Users/bfh/.virtualenvs/myapp/lib/python2.7/site-packages/flask_script/__init__.py", line 412, in run
    result = self.handle(sys.argv[0], sys.argv[1:])
  File "/Users/bfh/.virtualenvs/myapp/lib/python2.7/site-packages/flask_script/__init__.py", line 383, in handle
    res = handle(*args, **config)
  File "/Users/bfh/.virtualenvs/myapp/lib/python2.7/site-packages/flask_script/commands.py", line 216, in __call__
    return self.run(*args, **kwargs)
  File "/Users/bfh/.virtualenvs/myapp/lib/python2.7/site-packages/flask_migrate/__init__.py", line 136, in migrate
    version_path=version_path, rev_id=rev_id)
  File "/Users/bfh/.virtualenvs/myapp/lib/python2.7/site-packages/alembic/command.py", line 113, in revision
    script.run_env()
  File "/Users/bfh/.virtualenvs/myapp/lib/python2.7/site-packages/alembic/script.py", line 390, in run_env
    util.load_python_file(self.dir, 'env.py')
  File "/Users/bfh/.virtualenvs/myapp/lib/python2.7/site-packages/alembic/util.py", line 243, in load_python_file
    module = load_module_py(module_id, path)
  File "/Users/bfh/.virtualenvs/myapp/lib/python2.7/site-packages/alembic/compat.py", line 79, in load_module_py
    mod = imp.load_source(module_id, path, fp)
  File "migrations/env.py", line 19, in <module>
    config.set_main_option('sqlalchemy.url', current_app.config.get('SQLALCHEMY_DATABASE_URI'))
  File "/Users/bfh/.virtualenvs/myapp/lib/python2.7/site-packages/alembic/config.py", line 198, in set_main_option
    self.file_config.set(self.config_ini_section, name, value)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/ConfigParser.py", line 743, in set
    raise TypeError("option values must be strings")
TypeError: <flask_script.commands.Command object at 0x107aba950>: option values must be strings

Any advice for a fella?

Most helpful comment

Do you have SQLALCHEMY_DATABASE_URI set in your project's configuration?

All 14 comments

Do you have SQLALCHEMY_DATABASE_URI set in your project's configuration?

Oh geez, Miguel, my apologies. I made a bunch of configuration changes. This is a direct result of that refactoring. Thanks very much for the quick reply.

@miguelgrinberg Thanks that reply saved me big time

Running into this old problem, but in my case my Config file looks correct. Here's the trace:

flask db migrate -m "test"
Traceback (most recent call last):
  File "/home/haroon/PycharmProjects/federations/venv/bin/flask", line 11, in <module>
    sys.exit(main())
  File "/home/haroon/PycharmProjects/federations/venv/lib/python3.5/site-packages/flask/cli.py", line 894, in main
    cli.main(args=args, prog_name=name)
  File "/home/haroon/PycharmProjects/federations/venv/lib/python3.5/site-packages/flask/cli.py", line 557, in main
    return super(FlaskGroup, self).main(*args, **kwargs)
  File "/home/haroon/PycharmProjects/federations/venv/lib/python3.5/site-packages/click/core.py", line 697, in main
    rv = self.invoke(ctx)
  File "/home/haroon/PycharmProjects/federations/venv/lib/python3.5/site-packages/click/core.py", line 1066, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/haroon/PycharmProjects/federations/venv/lib/python3.5/site-packages/click/core.py", line 1066, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/haroon/PycharmProjects/federations/venv/lib/python3.5/site-packages/click/core.py", line 895, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/haroon/PycharmProjects/federations/venv/lib/python3.5/site-packages/click/core.py", line 535, in invoke
    return callback(*args, **kwargs)
  File "/home/haroon/PycharmProjects/federations/venv/lib/python3.5/site-packages/click/decorators.py", line 17, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/home/haroon/PycharmProjects/federations/venv/lib/python3.5/site-packages/flask/cli.py", line 412, in decorator
    return __ctx.invoke(f, *args, **kwargs)
  File "/home/haroon/PycharmProjects/federations/venv/lib/python3.5/site-packages/click/core.py", line 535, in invoke
    return callback(*args, **kwargs)
  File "/home/haroon/PycharmProjects/federations/venv/lib/python3.5/site-packages/flask_migrate/cli.py", line 90, in migrate
    rev_id, x_arg)
  File "/home/haroon/PycharmProjects/federations/venv/lib/python3.5/site-packages/flask_migrate/__init__.py", line 197, in migrate
    version_path=version_path, rev_id=rev_id)
  File "/home/haroon/PycharmProjects/federations/venv/lib/python3.5/site-packages/alembic/command.py", line 176, in revision
    script_directory.run_env()
  File "/home/haroon/PycharmProjects/federations/venv/lib/python3.5/site-packages/alembic/script/base.py", line 427, in run_env
    util.load_python_file(self.dir, 'env.py')
  File "/home/haroon/PycharmProjects/federations/venv/lib/python3.5/site-packages/alembic/util/pyfiles.py", line 81, in load_python_file
    module = load_module_py(module_id, path)
  File "/home/haroon/PycharmProjects/federations/venv/lib/python3.5/site-packages/alembic/util/compat.py", line 83, in load_module_py
    spec.loader.exec_module(module)
  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 22, in <module>
    current_app.config.get)
  File "/home/haroon/PycharmProjects/federations/venv/lib/python3.5/site-packages/alembic/config.py", line 218, in set_main_option
    self.set_section_option(self.config_ini_section, name, value)
  File "/home/haroon/PycharmProjects/federations/venv/lib/python3.5/site-packages/alembic/config.py", line 245, in set_section_option
    self.file_config.set(section, name, value)
  File "/usr/lib/python3.5/configparser.py", line 1189, in set
    self._validate_value_types(option=option, value=value)
  File "/usr/lib/python3.5/configparser.py", line 1174, in _validate_value_types
    raise TypeError("option values must be strings")
TypeError: option values must be strings

any help would be much appreciated

my config file:

import os
basedir = os.path.abspath(os.path.dirname(__file__))


class Config(object):
    SECRET_KEY = os.environ.get('SECRET_KEY') or "you'll never guess"
    SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or 'sqlite:///' + os.path.join(basedir, 'app.db')
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    UPLOADED_PHOTOS_DEST = 'app/static/photos/'
    UPLOADED_PHOTOS_URL = 'static/photos/'

Are you sure you are importing the configuration into your Flask application instance?

pretty sure..here is my __init__.py file...I am trying the factory pattern that you go over in your blog. Thanks much for the help.

from flask import Flask, g
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager
from config import Config
from flask_restful import Api
from flask_httpauth import HTTPBasicAuth
from flask_uploads import UploadSet, configure_uploads, IMAGES

api = Api()
migrate = Migrate()
login = LoginManager()
db = SQLAlchemy()
auth = HTTPBasicAuth()

from .resources.token import Token
from .resources.therapists import Availability, AccountInfo, Profile, Register, Therapists, SingleTherapist

api.add_resource(Token, '/token')
api.add_resource(Availability, '/therapist/availability')
api.add_resource(AccountInfo, '/therapist/account')
api.add_resource(Profile, '/therapist/profile')
api.add_resource(Therapists, '/therapists')
api.add_resource(SingleTherapist, '/therapist')
api.add_resource(Register, '/therapist/register')


def create_app(config_class=Config):
    app = Flask(__name__)
    app.config.from_object(config_class)
    db.init_app(app)
    migrate.init_app(app, db)
    login.init_app(app)
    api.init_app(app)
    photos = UploadSet('photos', IMAGES)
    configure_uploads(app=app, upload_sets=photos)
    return app


from app import models


@auth.verify_password
def verify_password(username_or_token, password):
    therapist = models.Therapist.verify_token(username_or_token)
    if not therapist:
        therapist = models.Therapist.query.filter_by(username=username_or_token).first()
        if not therapist or not therapist.check_password(password):
            return False
    g.therapist = therapist
    return True

in case this helps, here are my models:

from flask import current_app
from flask_validator import ValidateEmail, ValidateError
from flask_login import UserMixin
from app import db
from werkzeug.security import check_password_hash, generate_password_hash
from itsdangerous import (TimedJSONWebSignatureSerializer as Serializer, BadSignature, SignatureExpired)
import re
from datetime import datetime
from dateutil import parser


class Member(UserMixin, db.Model):
    """
    username, email, password

    """
    __tablename__ = 'member'
    __table_args__ = {'extend_existing': True}

    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(60), unique=True, nullable=False)
    email = db.Column(db.String(60), unique=True, nullable=False)
    passwordHash = db.Column(db.String(120))
    type = db.Column(db.String(10))

    __mapper_args__ = {
        'polymorphic_on': type,
        'polymorphic_identity': 'member'
    }

    def set_password(self, password):
        if self.valid(password):
            self.passwordHash = generate_password_hash(password)
        else:
            raise ValidateError('Password Invalid')

    def check_password(self, password):
        return check_password_hash(self.passwordHash, password)

    @staticmethod
    def valid(password):
        """
        checks if a string is a valid email format
        :param password: the candidate string
        :return: True if conditions met, else false
        """
        lowers = bool(re.search('[a-z]', password))
        uppers = bool(re.search('[A-Z]', password))
        numbers = bool(re.search(r'\d', password))
        long_enough = len(password) >= 8
        return lowers and uppers and numbers and long_enough

    @classmethod
    def __declare_last__(self):
        ValidateEmail(Member.email, throw_exception=True, message='Invalid Email')

    def generate_token(self, expiration=3600):
        s = Serializer(current_app.config['SECRET_KEY'], expires_in=expiration)
        return s.dumps(self.id)

    @staticmethod
    def verify_token(token):
        s = Serializer(current_app.config['SECRET_KEY'])
        try:
            therapist_id = s.loads(token)
        except BadSignature:
            return None
        except SignatureExpired:
            return None
        therapist = Therapist.query.get(therapist_id)
        return therapist


class Therapist(Member):
    __mapper_args__ = {
        'polymorphic_identity': 'therapist'
    }


class Customer(Member):
    __mapper_args__ = {
        'polymorphic_identity': 'customer'
    }


class Event(db.Model):
    """
    member, time, type, location
    """
    id = db.Column(db.Integer, primary_key=True)
    member = db.Column(db.Integer, db.ForeignKey('member.id'))
    time = db.Column(db.DateTime)
    type = db.Column(db.String(60), nullable=False)
    location = db.Column(db.String(60))

    @staticmethod
    def set_time(raw):
        try:
            dt = parser.parse(raw)
            if dt > datetime.now():
                return parser.parse(raw)
            else:
                raise ValidateError('You can only book future events')
        except Exception:
            raise ValidateError('Cannot recognize date format')


# TODO: Tests
class Appointment(db.Model):
    """
    event, customer, description
    """
    id = db.Column(db.Integer, primary_key=True)
    event = db.Column(db.Integer, db.ForeignKey('event.id'))
    customer = db.Column(db.Integer, db.ForeignKey('member.id'))
    description = db.Column(db.String(140))


# TODO: Tests
class Account(db.Model):
    """
    therapist, account_number, sort_code
    """
    id = db.Column(db.Integer, primary_key=True)
    therapist = db.Column(db.Integer, db.ForeignKey('member.id'), unique=True)
    account_number = db.Column(db.Integer, nullable=False)
    sort_code = db.Column(db.Integer, nullable=False)


# TODO: Tests
class Profile(db.Model):
    """
    therapist, firstname, lastname, photo, description
    """
    id = db.Column(db.Integer, primary_key=True)
    therapist = db.Column(db.Integer, db.ForeignKey('member.id'), unique=True)
    firstName = db.Column(db.String(60), nullable=False)
    lastName = db.Column(db.String(60), nullable=False)
    photo = db.Column(db.String(60))
    description = db.Column(db.String(140))


# TODO:Tests
class Therapy(db.Model):
    """
    profile, therapy
    """
    id = db.Column(db.Integer, primary_key=True)
    profile = db.Column(db.Integer, db.ForeignKey('profile.id'))
    therapy = db.Column(db.String(60), nullable=False)


# TODO:Tests
class Transaction(db.Model):
    """
    appointment, tokens, tokenValue, totalCash, cashShare
    """
    DEFAULT_TOKEN_VALUE = 1.0
    DEFAULT_CASHSHARE = 0.2

    id = db.Column(db.Integer, primary_key=True)
    appointment = db.Column(db.Integer, db.ForeignKey('appointment.id'))
    tokens = db.Column(db.Integer, nullable=False)
    tokenValue = db.Column(db.Float, default=DEFAULT_TOKEN_VALUE, nullable=False)
    totalCash = db.Column(db.Float, nullable=False)
    cashShare = db.Column(db.Float, default=DEFAULT_CASHSHARE, nullable=False)

@haroonhassan well, the stack trace does not lie, if I'm not mistaken line 22 in migrations/env.py is the line where Alembic tries to get the database URL. You may want to add a print statement above that line to confirm that the configuration is actually available in current_app.config.

@miguelgrinberg Not sure how, but the arguments to config.get() somehow got deleted. So after line 22 the function object was being passed. Thanks very much for the guidance.

I am also getting this error. I am connecting to mysql in config, quite sure it is pasted correctly. Any advice what could be the issue?

Response for _flask db migrate -m "users table"_

> Traceback (most recent call last):
>   File "/usr/local/bin/flask", line 11, in <module>
>     sys.exit(main())
>   File "/usr/local/lib/python3.5/dist-packages/flask/cli.py", line 894, in main
>     cli.main(args=args, prog_name=name)
>   File "/usr/local/lib/python3.5/dist-packages/flask/cli.py", line 557, in main
>     return super(FlaskGroup, self).main(*args, **kwargs)
>   File "/usr/local/lib/python3.5/dist-packages/click/core.py", line 697, in main
>     rv = self.invoke(ctx)
>   File "/usr/local/lib/python3.5/dist-packages/click/core.py", line 1066, in invoke
>     return _process_result(sub_ctx.command.invoke(sub_ctx))
>   File "/usr/local/lib/python3.5/dist-packages/click/core.py", line 1066, in invoke    
> return _process_result(sub_ctx.command.invoke(sub_ctx))
>   File "/usr/local/lib/python3.5/dist-packages/click/core.py", line 895, in invoke
>     return ctx.invoke(self.callback, **ctx.params)
>   File "/usr/local/lib/python3.5/dist-packages/click/core.py", line 535, in invoke
>     return callback(*args, **kwargs)
>   File "/usr/local/lib/python3.5/dist-packages/click/decorators.py", line 17, in new_func
>     return f(get_current_context(), *args, **kwargs)
>   File "/usr/local/lib/python3.5/dist-packages/flask/cli.py", line 412, in decorator
>     return __ctx.invoke(f, *args, **kwargs)
>   File "/usr/local/lib/python3.5/dist-packages/click/core.py", line 535, in invoke
>     return callback(*args, **kwargs)
>   File "/usr/local/lib/python3.5/dist-packages/flask_migrate/cli.py", line 90, in migrate
>     rev_id, x_arg)
>   File "/usr/local/lib/python3.5/dist-packages/flask_migrate/__init__.py", line 95, in wrapped
>     f(*args, **kwargs)
>   File "/usr/local/lib/python3.5/dist-packages/flask_migrate/__init__.py", line 215, in migrate
>     version_path=version_path, rev_id=rev_id)
>   File "/usr/local/lib/python3.5/dist-packages/alembic/command.py", line 176, in revision
>     script_directory.run_env()
>   File "/usr/local/lib/python3.5/dist-packages/alembic/script/base.py", line 427, in run_env
>     util.load_python_file(self.dir, 'env.py')
>   File "/usr/local/lib/python3.5/dist-packages/alembic/util/pyfiles.py", line 81, in load_python_file
>     module = load_module_py(module_id, path)
>   File "/usr/local/lib/python3.5/dist-packages/alembic/util/compat.py", line 82, in load_module_py
>     spec.loader.exec_module(module)
>   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 22, in <module>
>     current_app.config.get('SQLALCHEMY_DATABASE_URI'))
>   File "/usr/local/lib/python3.5/dist-packages/alembic/config.py", line 218, in set_main_option
>     self.set_section_option(self.config_ini_section, name, value)
>   File "/usr/local/lib/python3.5/dist-packages/alembic/config.py", line 245, in set_section_option
>     self.file_config.set(section, name, value)
>   File "/usr/lib/python3.5/configparser.py", line 1189, in set
>     self._validate_value_types(option=option, value=value)
>   File "/usr/lib/python3.5/configparser.py", line 1174, in _validate_value_types
>     raise TypeError("option values must be strings")
> TypeError: option values must be strings

config.py is defined as:

import os

class Config(object):
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'you-will-never-guess'

class Config(object):
    SQLALCHEMY_DATABASE_URI = os.environ.get('mysql://root:mypassword@localhost/mydatabase')
    SQLALCHEMY_TRACK_MODIFICATIONS = False

@sim0nsl Do this instead:

class Config(object):
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'you-will-never-guess'
    SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL')
    SQLALCHEMY_TRACK_MODIFICATIONS = False

And then set the following in your environment before running the flask db migrate command:

export DATABASE_URL=mysql://root:mypassword@localhost/mydatabase

@miguelgrinberg That is neat! I confirm it is working. Thank you.

@haroonhassan well, the stack trace does not lie, if I'm not mistaken line 22 in migrations/env.py is the line where Alembic tries to get the database URL. You may want to add a print statement above that line to confirm that the configuration is actually available in current_app.config.

Thank you @miguelgrinberg! I was having a similar issue and resolved it by troubleshooted using the migrations/env.py file.

Was this page helpful?
0 / 5 - 0 ratings