miguelgrinberg / Flask-Migrate

SQLAlchemy database migrations for Flask applications using Alembic
MIT License
2.37k stars 229 forks source link

flask db init fails: KeyError 'migrate' #196

Closed fieldse closed 6 years ago

fieldse commented 6 years ago

Hi Miguel!

I'm attempting to use your flask-migrate CLI tool as per your tutorial.

Issue: Running flask db init from the terminal fails with error:

    directory = current_app.extensions['migrate'].directory
KeyError: 'migrate' 

Desired result: flask db init should create the database (sqlite) and initialize database tables from all models.

Context:

  1. I've exported FLASK_APP='run.py', where run.py does the import of 'app' and 'db' from (myapp)/__init__.py
  2. Flask app is created with: app = Flask(APP_NAME) in run.py
  3. DB object is created with: db = SQLAlchemy(app) in run.py

Speculative analysis: It seems that current_app.extensions is not somehow being populated with a desired key migrate, which I assume refers to your Flask extension.

Where/how is that supposed to happen?

There may be steps here I've missed or required parameters I'm unaware of. Can you point me in the right direction?

Thanks!

utsushiiro commented 6 years ago

Hi fieldse. Did you create a Migrate object? I get the same error when trying flask db init without that object.

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

app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app)
migrate = Migrate(app, db) # this
miguelgrinberg commented 6 years ago

Yes, I think @utsushiiro is right, you did not initialize the Flask-Migrate extension by creating a Migrate object.

fieldse commented 6 years ago

@utsushiiro You're a wizard. :smile: @miguelgrinberg Correct, that was it.

Yeah, I had abstracted the migrate = Migrate(app, db) to a separate function.

Solution:

Adding migrate = Migrate(app, db) into __init__.py resolved the issue. (I didn't realize this was necessary for the cli functionality and database initialization, assumed it was related only to database migrations afterwards.)

Running flask db init now works correctly:

Thanks guys!

Nerevarishe commented 6 years ago

Get the same error. migrate = Migrate(app, db) in __init__.py. What I'm doing wrong? My code All Traceback: Traceback (most recent call last): File "/home/iliasit/flask_projects/news_site/venv/bin/flask", line 11, in sys.exit(main()) File "/home/iliasit/flask_projects/news_site/venv/lib/python3.6/site-packages/flask/cli.py", line 894, in main cli.main(args=args, prog_name=name) File "/home/iliasit/flask_projects/news_site/venv/lib/python3.6/site-packages/flask/cli.py", line 557, in main return super(FlaskGroup, self).main(args, kwargs) File "/home/iliasit/flask_projects/news_site/venv/lib/python3.6/site-packages/click/core.py", line 697, in main rv = self.invoke(ctx) File "/home/iliasit/flask_projects/news_site/venv/lib/python3.6/site-packages/click/core.py", line 1066, in invoke return _process_result(sub_ctx.command.invoke(sub_ctx)) File "/home/iliasit/flask_projects/news_site/venv/lib/python3.6/site-packages/click/core.py", line 1066, in invoke return _process_result(sub_ctx.command.invoke(sub_ctx)) File "/home/iliasit/flask_projects/news_site/venv/lib/python3.6/site-packages/click/core.py", line 895, in invoke return ctx.invoke(self.callback, ctx.params) File "/home/iliasit/flask_projects/news_site/venv/lib/python3.6/site-packages/click/core.py", line 535, in invoke return callback(args, kwargs) File "/home/iliasit/flask_projects/news_site/venv/lib/python3.6/site-packages/click/decorators.py", line 17, in new_func return f(get_current_context(), *args, *kwargs) File "/home/iliasit/flask_projects/news_site/venv/lib/python3.6/site-packages/flask/cli.py", line 412, in decorator return __ctx.invoke(f, args, kwargs) File "/home/iliasit/flask_projects/news_site/venv/lib/python3.6/site-packages/click/core.py", line 535, in invoke return callback(*args, **kwargs) File "/home/iliasit/flask_projects/news_site/venv/lib/python3.6/site-packages/flask_migrate/cli.py", line 31, in init _init(directory, multidb) File "/home/iliasit/flask_projects/news_site/venv/lib/python3.6/site-packages/flask_migrate/init.py", line 109, in init directory = current_app.extensions['migrate'].directory KeyError: 'migrate'

miguelgrinberg commented 6 years ago

@Nerevarishe does it still do it if you remove the app.run() call in app/__init__.py. If you are going to use the Flask CLI you have to start the app with flask run, don't use app.run().

Nerevarishe commented 6 years ago

Fixed. Problem was in bad pyCharm deployment directory configuration. Because that i use default pyCharm flask project file that don't have migrate = Migrate(app, db). So, need to be more attentive. And app.run() does not affect on db init process.

malay95 commented 6 years ago

I am getting the same error when I us e CLI in flask db init. I have created the Migrate object in the __init__.py file. I also dont use app.run(). Thank you for your help

Nerevarishe commented 6 years ago

Please show your code

borross commented 6 years ago

@malay95 if you project on Win Machine try to go with PowerShell to \venv foulder of Project, (install again all packadges (now we installing not for venv enviroment only) WTF and etc.). then type: $env:FLASK_APP = "<main_file_where_app.run()>.py" then use flask commands: flask db init

waketzheng commented 5 years ago

I have a manage.py in the project base directory, and init the migrations by run

python manage.py db init
phstrauss commented 4 years ago

Hello Miguel, Hello world! :-) I'm following step by step the flask mega tutorial (great work btw.!), but I'm stuck on this exact same issue, despite having everything in place pertaining the migrate object in app/init.py it seems. I need a second pair of eyes on my code :) I've looked everywhere already without success. my code & stacktrace: https://www.strauss-engineering.ch/tmp/

miguelgrinberg commented 4 years ago

@phstrauss this line in app/init.py:

migrate = Migrate(db, app)

should be:

migrate = Migrate(app, db)
Brightadekunle commented 4 years ago

Hello Miguel, Hello Everyone, I'm following step by step the flask mega tutorial (great work btw.!), Flask Migrate once worked for me, but I'm stuck on this exact same issue,

Brightadekunle commented 4 years ago

This is my code

init.py from flask_migrate import Migrate, MigrateCommand from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_bcrypt import Bcrypt from flask_login import LoginManager from flask_script import Manager

app=Flask(name) app.config['TESTING'] = True app.config['SECRET_KEY'] = '3915c865a309c2d8c9892e4f5095f270' app.config['SQLAlCHEMY_DATABASE_URI'] ='sqlite:///site.db' db = SQLAlchemy(app) migrate = Migrate(app, db) manager=Manager(app) manager.add_command('db', MigrateCommand) bcrypt = Bcrypt(app) login_manager=LoginManager(app)

from flaskblog import routes, models

if name == "main": manager.run()

run.py

from flaskblog import app, db from flaskblog.models import User, Post if name == 'main': app.run(debug=True)

routes.py from flask import redirect, render_template, flash, url_for, request from flaskblog import app, bcrypt, db from flaskblog.forms import RegistrationForm, LoginForm from flaskblog.models import User, Post from flask_login import login_user

posts = [ {'author': 'Corey Schafer', 'Title': 'Blog post 1', 'Content': 'First Post Comment', 'date_posted':'31 March, 2020' }, { 'author': 'Jane Doe', 'Title': 'Blog post 2', 'Content': 'Second Post Comment', 'date_posted': '31 March, 2020' } ] @app.route('/') @app.route('/home') def home(): return render_template('home.html', posts=posts)

@app.route('/about') def about(): return render_template('about.html', title=about)

@app.route('/user/') def user(name): return '

Hi, %s!

' %name

@app.route('/register', methods=['GET', 'POST']) def register(): form = RegistrationForm() if form.validate_on_submit(): hashed_password = bcrypt.generate_password_hash(form.password.data).decode('utf-8') user = User(username=form.username.data, email=form.email.data, password=hashed_password) db.session.add(user) db.session.commit() print(form.username.data, form.email.data) flash('Your account has been created, you can now log in', 'success') return redirect(url_for('login')) return render_template('register.html', title='Register', form=form)

@app.route('/login', methods=['GET', 'POST']) def login(): form = LoginForm() if form.validate_on_submit(): user = User.query.filter_by(email=form.email.data).first() if user and bcrypt.check_password_hash(user.password, form.password.data): login_user(user, remember=form.remember.data) return redirect(url_for('home')) else: flash('Login Unsuccessful, check email and password', 'danger') return render_template('login.html', title='Login', form=form)

model.py from datetime import datetime from flaskblog import db, login_manager from flask_login import UserMixin

@login_manager.user_loader def load_user(user_id): return User.query.get(int(user_id))

class User(db.Model): tablename = 'User' id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(20), unique=True, nullable=False, index=True) email = db.Column(db.String(20), unique=True, nullable=False) image_file = db.Column(db.String(120), nullable=False, default='default.jpg') password = db.Column(db.String(70), nullable=False) posts = db.relationship('Post', backref='author', lazy=True)

def __repr__(self):
    return f"User('{self.username}', '{self.email}', '{self.image_file}')"

class Post(db.Model): tablename = 'Post' id = db.Column(db.Integer, primary_key=True) title = db.Column(db.String(120), unique=True, nullable=False) date_posted = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) content = db.Column(db.Text, nullable=False) user_id = db.Column(db.Integer, db.ForeignKey('User.id'), nullable=False)

def __repr__(self):
    return f"Post('{self.title}', '{self.date_posted}')"

forms.py from flask_wtf import FlaskForm from wtforms import StringField, PasswordField, SubmitField, BooleanField, ValidationError from wtforms.validators import DataRequired, length, Email, EqualTo from flaskblog.models import User, Post

class RegistrationForm(FlaskForm): username = StringField('Username', validators=[DataRequired(), length(min=2, max=20)]) email = StringField('Email', validators=[DataRequired(), Email()]) password = PasswordField('Password', validators=[DataRequired()]) confirm_password = PasswordField('Confirm_password', validators=[DataRequired(), EqualTo('password')]) submit = SubmitField('Sign Up')

def validate_username(self, username):
    user = User.query.filter_by(username=username.data).first()
    if user:
        raise ValidationError('The username is taken, please choose another one')

def validate_email(self, email):
    email = User.query.filter_by(email=email.data).first()
    if email:
        raise ValidationError('The email is taken, please choose another one')

class LoginForm(FlaskForm): email = StringField('Email', validators=[DataRequired(), Email()]) password = PasswordField('Password', validators=[DataRequired()]) remember = BooleanField('Remember me') submit = SubmitField('Login')

miguelgrinberg commented 4 years ago

@Brightadekunle stack trace of the error?

pascale64 commented 3 years ago

Hi I have the same error when I "flask db init", How ever my setup is as the tutorial but on chapter 15 and I am using apache2 with mod_wsgi, so where here you are adding "migrate = Migrate(app, db)' my init.py code is this import os from flask import Flask, request, current_app from config import Config from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate from flask_login import LoginManager from flask_mail import Mail from flask_wtf import FlaskForm from datetime import timedelta from flask_uploads import UploadSet, IMAGES, configure_uploads

db = SQLAlchemy() migrate = Migrate() login = LoginManager() login.login_view = 'auth.login' login.login_message = 'Please log in to access this page.' mail = Mail() images = UploadSet('images', IMAGES)

def create_app(test_config=None):

create and configure the app

app = Flask(__name__, instance_relative_config=True)
app.config.from_object(Config)
app.config['UPLOAD_FOLDER'] = '/var/www/accounts/static/img'

db.init_app(app)
migrate.init_app(app, db)
login.init_app(app)
mail.init_app(app)

I tried to add 'app, db' in the part above where migrate=Migrate(), but I got an app error. my app.py codeis: from app import create_app from app.models import db, Book

app = create_app() migrate = Migrate(app, db)

@app.shell_context_processor def make_shell_context(): """Create a shell context so that can use REPL.""" return dict(app=app, db=db, Book=Book)

Any ideas?

miguelgrinberg commented 3 years ago

@pascale64 you have two migrate objects? You should have one one.

pascale64 commented 3 years ago

@pascale64 you have two migrate objects? You should have one one.

Hi Miguel, Thank you for coming back so quickly, I have removed the "migrate = Migrate(app, db)" from app.py, I still get the same error, do I need to remove one of the migrate objects from init.py as well? edit I removed both of the other migrate objects one at a time I still got the same error. My original code works perfectly with a nginx server but here I must use apache2.

miguelgrinberg commented 3 years ago

@pascale64 I can't really give you specific advice when you don't show the code. As I said above, there should be one migrate object for the entire application. You can't have multiple ones.

pascale64 commented 3 years ago

@miguelgrinberg I Have reduced init.py to this: import os from flask import Flask, request, current_app from config import Config from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate

db = SQLAlchemy() migrate = Migrate()

def create_app(config_class=Config): app = Flask(name) app.config.from_object(Config_class)

db.init_app(app)
migrate.init_app(app, db)

I have removed the migrate object from all other code but I am still getting the same error message,

pascale64 commented 3 years ago

@miguelgrinberg I Have changed the init code to this: import os from flask import Flask, request, current_app from config import Config from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate

db = SQLAlchemy() migrate = Migrate()

def create_app(test_config=None): app = Flask(name, instance_relative_config=True) app.config.from_object(Config)

db.init_app(app)
migrate.init_app(app, db)

And I no longer have the error! Many thanks for your help and most of all for your excellent tutorials and hard work with python.

YogeshBisht2307 commented 3 years ago

Hi fieldse. Did you create a Migrate object? I get the same error when trying flask db init without that object.

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

app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app)
migrate = Migrate(app, db) # this

I have mentioned this thing even though I m having error

Rishi-Heera commented 3 years ago

Hello Miguel,

Greetings for the day!!!!!

I am new to flask and learning Flask thru your book "The New And Improved Flask Mega-Tutorial ". In the chapter4 when i am firing command flask db init it gives "directory = current_app.extensions['migrate'].directory Key Error: 'migrate' " . I am able to resolve the issue, this is possible only when i commented below line of code.
############ init.py ###############

from flask import Flask
# from config import Config
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate

app = Flask(__name__)
# app.config.from_object(Config)
var = app.config["SECRET_KEY"]
db = SQLAlchemy(app)
migrate = Migrate(app, db)
# from app import routes, models

Used below command for the migration:


PS D:\Result\MicroBlog> set FLASK_APP=app/_init_.py
PS D:\Result\MicroBlog> $env:FLASK_APP = "app/_init_.py"
PS D:\Result\MicroBlog> flask db init

Case 1: If i do not comment the above 3 lines of code, I get below error ######################################################

  File "D:\Result\MicroBlog\app\_init_.py", line 2, in <module>
    from config import Config
ModuleNotFoundError: No module named 'config'

I do understand the above error is because i am giving path to init.py (app/init.py) and firing flask db on it and it is not able to find config.py, is this a correct way as this process generates the migrate directory.

Case 2: If i use below commands, it throws Key Error: 'migrate' " ######################################################

PS D:\Result\MicroBlog> set FLASK_APP=microblog.py
PS D:\Result\MicroBlog> $env:FLASK_APP = "microblog.py"
PS D:\Result\MicroBlog> flask db init

Please suggest for migration we need to set the FLASK_APP to app/init.py and fire flask db init, if any changes is done on the schema on the database ?????

miguelgrinberg commented 3 years ago

@Rishi-Heera the code shown in the tutorial is 100% correct. You do not need to comment any lines of code for things to work. I suggest you download the official code from my GitHub repository and compare against yours to find your mistake. There is a download link in the introduction section of the chapter in question.

Rishi-Heera commented 3 years ago

@Rishi-Heera the code shown in the tutorial is 100% correct. You do not need to comment any lines of code for things to work. I suggest you download the official code from my GitHub repository and compare against yours to find your mistake. There is a download link in the introduction section of the chapter in question.

Thanks Miguel, as suggested i corrected the code and it worked finally.