Flask factory pattern and Flask CLI commands - python

Ok, i'm a bit new to Flask, so I'm not sure if i should be using Flask Script or Flask's built-in CLI took but I'm going with the latter for now.
However, I'm facing the problem that others have faced when it comes to bootstrapping the app context before running a command and having current_app context and whatnot.
def create_app(test_config=None):
# create and configure the app
app = Flask(__name__, instance_relative_config=True)
app.cli.add_command(seed_db)
return app
#click.command
#with_appcontext
def seed_db():
# Register SqlAlchemy
from .models import db, User, Role
db.init_app(app)
# Setup Flask-Security
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
security = Security(app, user_datastore)
# Some initial models (if they don't exist)
user_datastore.find_or_create_role(name='admin', description='Administrator')
user_datastore.find_or_create_role(name='end-user', description='End user')
return app
So in this case I have a factory method initializing the app. And then I have my custom command that I want to run to seed the database.
I've already been able to run flask db migrate to set up my initial tables, but if I try to run flask seed_db, I get something like this:
File "/usr/local/lib/python3.6/site-packages/click/core.py", line
1150, in add_command
name = name or cmd.name AttributeError: 'function' object has no attribute 'name'
Can someone enlighten me on how to properly bootstrap the app with commands outside the create_app method?

There is an error when you are using #click.command
I think the error message gives you a clear clue that you forget to give a name to your command.
You need to init a name to the command like this #click.command("seed_db") or just default function name like this #click.command()
#click.command("seed_db")
#with_appcontext
def seed_db():
# Register SqlAlchemy
from .models import db, User, Role
db.init_app(app)
Hope it helps.

These two lines are in the wrong place:
app.cli.add_command(seed_db)
return app
Your seed_db() function is adding itself to the app, which is a bit confusing - then returning the app. I think you meant for these two actions to occur your app factory method:
def create_app(test_config=None):
# create and configure the app
app = Flask(__name__, instance_relative_config=True)
app.cli.add_command(seed_db)
return app
Your app factory is the function with the responsibility of setting up the app object and returning it, no other function should be trying to do this.

Related

db.create_all() not creating db [duplicate]

def get_db(self,dbfile):
if hasattr(g, 'sqlite_db'): self.close_db(g.sqlite_db)
try:
g.sqlite_db = self.connect_db('{}/{}'.format(app.root_path, dbfile))
except sqlite3.OperationalError as e:
raise e
return g.sqlite_db
Hi this code is located inside DB class, The error I get is
RuntimeError: working outside of application context
the error occurs on this line
g.sqlite_db = self.connect_db('{}/{}'.format(app.root_path, dbfile))
I think the problem is with g, it is imported like that from flask import g
How this error can be fixed?
Thanks.
Maybe you need to call your function inside an application context:
with app.app_context():
# call your method here
When creating your app, use:
app.app_context().push()
for example like this:
from yourapp import create_app
app = create_app()
app.app_context().push()
for further information
From the Flask source code in flask/globals.py:
_app_ctx_err_msg = '''\
Working outside of application context.
This typically means that you attempted to use functionality that needed
to interface with the current application object in a way. To solve
this set up an application context with app.app_context(). See the
documentation for more information.\
'''
Following the documentation, you can see that you need to make flask.current_app point to your application and it currently doesn't.
You're probably calling your DB function before Flask has initialized. My guess is that your app object has not been created yet with the Flask constructor.
Two possible solution
First method
Instead of calling create_all() in your code, call manually in the flask shell which is CLI
Go to your terminal
type flask shell, then
db.create_all()
Second method
As it says in the runtime error message
This typically means that you attempted to use functionality that needed
the current application. To solve this, set up an application context
with app.app_context().
Open the python terminal in your project directory and manually add a context
from project_name import app, db
app.app_context().push()
db.create_all()
Check out this video for better understanding.
YouTube Video
Simple Example To Avoid This Error
Please check out the Purpose of context
#filename = run.py (inside root directory)
from flaskblog import create_app
app = create_app()
if __name__ == "__main__":
app.run(debug=True)
Inside flaskblog folder
filename = __init __.py (inside flaskblog folder)
app = Flask(__name__)
db = SQLAlchemy()
login_manager = LoginManager()
login_manager.login_view = "users.login"
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(Config)
db.init_app(app)
from flaskblog.user.routes import users
app.register_blueprint(users)
return app
filename = config.py (inside flaskblog folder)
class Config:
SECRET_KEY = 'your secret key'
SQLALCHEMY_DATABASE_URI = 'your db uri'
filename = models.py
#login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
users folder (inside flaskblog)
users folder contain one __init__.py file
Filename = form.py (inside users folder)
class LoginForm(FlaskForm):
# define your field
pass
Filename = routes.py (inside users folder)
users = Blueprint('users',__name__)
#users.route('/login', methods=['GET', 'POST'])
def login():
# do your stuff
pass
Other users have pointed out how to solve the immediate problem, however you might consider modifying how the database connection is created to solve this issue.
Instead of having a method within you DB class instantiate the database connection you could have the connection created in the controller before every request. Then use the teardown_request decorator to close the connection.
Then when within a route you could pass the connection to the DB class as part of instantiating a new DB object.
This would ensure that you never create a database connection unless you need one. And it prevent you from accessing Flask globals out of the app context.
#app.before_request
def before_request():
try:
g.sqlite_db = self.connect_db('{}/{}'.format(app.root_path, dbfile))
except sqlite3.OperationalError as e:
raise e
#app.teardown_request
def teardown_request(e):
if hasattr(g, 'sqlite_db'): self.close_db(g.sqlite_db)
#app.route('/someroute', methods=["GET"]:
def someroute():
db_obj = DB(g.sqlite_db)
.
.
.
Use
pip install flask-sqlalchemy==2.5.1
This might solve the error
To expand on #VadimK's answer. If you want to prevent your code from executing outside of an app_context you can use flask.has_app_context() to see if the code is currently inside an app context:
See also: flask.has_request_context()
This is what fixed it for me. I hope it helps someone else.
if __name__ == "__main__":
with app.app_context():
db.create_all()
app.run(debug=True)
I had the same issue while doing some unit testing.
Adding the following function to my test class solved my issue:
#classmethod
def setUpClass(self):
self.app = create_app("testing")
self.client = self.app.test_client()
When initializing the app I use this block instead :
def create_app():
app = Flask(__name__)
with app.app_context():
init_db()
return app
when creating an app just add the below code.
app = Flask(__name__)
app.app_context().push()
Install this version of flask using
pip install flask-sqlalchemy==2.5.1
then run db.create_all() and it will run.
ERROR:This typically means that you attempted to use functionality that needed
to interface with the current application object in a way. To solve
this set up an application context with app.app_context(). See the
documentation for more information.

How to put decorators on `app` when using Application Factory with Flask?

I'm trying to define some global constants for my app, and have found that this can be done with a function that's decorated as #app.context_processor.
However, the issue is that I don't have an app variable. My application uses an application factory, and I'd like to keep it that way. Is there a different way to register a function as the context_processor for my app?
One option I have seen is to apply the decorator to each Blueprint instead of applying it to the app. That's something I would like to avoid though, since it would lead to a lot of duplicate code.
The issue is that there is no app object in case of factories. You have a create_app function where the app gets created.
So to install the context processors you can use create_app itself
def create_app(config_filename):
app = Flask(__name__)
app.config.from_pyfile(config_filename)
from yourapplication.model import db
db.init_app(app)
from yourapplication.context_processor import myprocessor
app.context_processor(myprocessor)
from yourapplication.views.frontend import frontend
app.register_blueprint(frontend)
return app
You could also have the function in the same app.py file (wherever the create_app() function is written). In such a case, you could simply register the context_processor without importing it.
Another approach is to do it in a blueprint as shown in below
Flask context processors functions
from flask import Blueprint
thingy = Blueprint("thingy", __name__, template_folder='templates')
#thingy.route("/")
def index():
return render_template("thingy_test.html")
#thingy.context_processor
def utility_processor():
def format_price(amount, currency=u'$'):
return u'{1}{0:.2f}'.format(amount, currency)
return dict(format_price=format_price)

Flask-Admin: A blueprint's name collision occurred ... Both share the same name "admin"

I have a Flask 0.12.4 app and am using Flask-Admin.
I get this error intermittently when developing locally, and have just started seeing it in one of our public environments too.
AssertionError: A blueprint's name collision occurred between
<flask.blueprints.Blueprint object at 0x7f5cd31f96d0> and
<flask.blueprints.Blueprint object at 0x7f5cd33b0d90>.
Both share the same name "admin".
Blueprints that are created on the fly need unique names.
The steps to reproduce in my development environment are slightly odd:
Break some of the admin config (e.g. change one of the strings relating to a column name in form_columns so it refers to a non-existent name)
Refresh the browser to see the regular "unknown field" error
Revert the change
Refresh the browser again - you then see the blueprint error above
The line which is causing the error is this one:
# admin.py
admin = flask_admin.Admin(
app, index_view=MyIndexView(), template_mode="bootstrap3"
)
It seemed like the line in question was being called multiple times (although the file it's in is only imported in one place). I tried passing a custom endpoint in the Admin constructor and continued to get the same error but with the new endpoint name, which suggested that was the case.
This answer refers in passing to the same problem, and to the solution of using init_app rather than passing the app to the constructor.
Doing that, my code then looked like this, and the error went away:
# admin.py
# Notice I'm not passing app here any more.
admin = flask_admin.Admin(index_view=MyIndexView(), template_mode="bootstrap3")
# app.py
admin.init_app(app) # This line is new
I don't understand the detail of how the problem occurred or exactly why this solved it, so any further pointers welcome!
to me help some idea from here. Flask-admin 1.5.3
# token.__init__.py <-- token - my app
# endpoint - very important
from app.model import blueprint, Token
from db import db
blueprint = Blueprint("token", __name__, url_prefix='/token')
blueprint.custom_model_views = []
blueprint.custom_model_views += [ModelView(Token, db.session, endpoint='token-admin')]
# admin.__init__.py
def create_module(admin, app, **kwargs):
admin.init_app(app)
with app.app_context() as e:
from app import token
views = token.blueprint.custom_model_views
for v in views:
admin.add_view(v)
# root.__init__.py
flask_admin = Admin()
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
from .token import create_module as token_create_module
from .admin import create_module as admin_create_module
token_create_module(app)
admin_create_module(flask_admin, app)
return app
check http://localhost:5000/admin/token-admin/

Access Flask app context in CLI command

I want to register a CLI command that's defined in a separate file from the Flask app factory. This command needs access to app.config. However, accessing current_app.config from the command raises RuntimeError: Working outside of application context.
app/__init__.py
from flask import Flask
from app.commands import my_command
def create_app():
app = Flask(__name__, instance_relative_config=True)
app.config.from_pyfile("config.py")
app.add_command(my_command)
return app
instance/config.py
TEST_VARIABLE = "TESTVALUE"
app/commands.py
from flask import current_app
#click.command()
def my_command():
click.echo(current_app.config["TEST_VARIABLE"])
I expect running flask my_command to output TESTVALUE. However, I get the following error:
RuntimeError: Working outside of application context.
This typically means that you attempted to use functionality that needed
to interface with the current application object in some way. To solve
this, set up an application context with app.app_context(). See the
documentation for more information.
I need to use with app.app_context(): for current_app to work, but I don't have access to app since it's defined in a factory. If I use #app.cli.command() in the factory, this would be no problem, as there I have access to the app variable, and wouldn't even need to push an app context.
def create_app():
...
#app.cli.command()
def my_command():
click.echo(current_app.config["TEST_VARIABLE"])
...
However, I want to define my commands in other files and have them use values from the app config, while this requires all the commands to be nested in the factory function.
I tried creating an app using the factory inside the command, and that worked, but I don't think doing that is a good idea just to have access to the config variables.
import click
import app
#click.command()
def my_command():
app_config = app.create_app().config
click.echo(app_config["TEST_VARIABLE"])
How can I define a command that has access to the application context when using the application factory pattern?
Flask's docs about the CLI discuss this.
#app.cli.command() automatically ensures there's an app context when the command runs. However, this is less convenient to use when using an app factory because you don't have access to the app when defining the command.
When using the app factory, you use #click.command() and app.cli.add_command() to separate defining and registering the command, but #click.command doesn't know about the app context. Use the #with_appcontext decorator to ensure the command runs in an app context.
#click.command()
#with_appcontext
def my_command():
config = current_app.config
click.echo(config["TEST_VARIABLE"])
def create_app():
app = Flask(__name__)
...
app.cli.add_command(my_command)
...
return app
$ flask my-command
TESTVALUE

working outside of application context - Flask

def get_db(self,dbfile):
if hasattr(g, 'sqlite_db'): self.close_db(g.sqlite_db)
try:
g.sqlite_db = self.connect_db('{}/{}'.format(app.root_path, dbfile))
except sqlite3.OperationalError as e:
raise e
return g.sqlite_db
Hi this code is located inside DB class, The error I get is
RuntimeError: working outside of application context
the error occurs on this line
g.sqlite_db = self.connect_db('{}/{}'.format(app.root_path, dbfile))
I think the problem is with g, it is imported like that from flask import g
How this error can be fixed?
Thanks.
Maybe you need to call your function inside an application context:
with app.app_context():
# call your method here
When creating your app, use:
app.app_context().push()
for example like this:
from yourapp import create_app
app = create_app()
app.app_context().push()
for further information
From the Flask source code in flask/globals.py:
_app_ctx_err_msg = '''\
Working outside of application context.
This typically means that you attempted to use functionality that needed
to interface with the current application object in a way. To solve
this set up an application context with app.app_context(). See the
documentation for more information.\
'''
Following the documentation, you can see that you need to make flask.current_app point to your application and it currently doesn't.
You're probably calling your DB function before Flask has initialized. My guess is that your app object has not been created yet with the Flask constructor.
Two possible solution
First method
Instead of calling create_all() in your code, call manually in the flask shell which is CLI
Go to your terminal
type flask shell, then
db.create_all()
Second method
As it says in the runtime error message
This typically means that you attempted to use functionality that needed
the current application. To solve this, set up an application context
with app.app_context().
Open the python terminal in your project directory and manually add a context
from project_name import app, db
app.app_context().push()
db.create_all()
Check out this video for better understanding.
YouTube Video
Simple Example To Avoid This Error
Please check out the Purpose of context
#filename = run.py (inside root directory)
from flaskblog import create_app
app = create_app()
if __name__ == "__main__":
app.run(debug=True)
Inside flaskblog folder
filename = __init __.py (inside flaskblog folder)
app = Flask(__name__)
db = SQLAlchemy()
login_manager = LoginManager()
login_manager.login_view = "users.login"
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(Config)
db.init_app(app)
from flaskblog.user.routes import users
app.register_blueprint(users)
return app
filename = config.py (inside flaskblog folder)
class Config:
SECRET_KEY = 'your secret key'
SQLALCHEMY_DATABASE_URI = 'your db uri'
filename = models.py
#login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
users folder (inside flaskblog)
users folder contain one __init__.py file
Filename = form.py (inside users folder)
class LoginForm(FlaskForm):
# define your field
pass
Filename = routes.py (inside users folder)
users = Blueprint('users',__name__)
#users.route('/login', methods=['GET', 'POST'])
def login():
# do your stuff
pass
Other users have pointed out how to solve the immediate problem, however you might consider modifying how the database connection is created to solve this issue.
Instead of having a method within you DB class instantiate the database connection you could have the connection created in the controller before every request. Then use the teardown_request decorator to close the connection.
Then when within a route you could pass the connection to the DB class as part of instantiating a new DB object.
This would ensure that you never create a database connection unless you need one. And it prevent you from accessing Flask globals out of the app context.
#app.before_request
def before_request():
try:
g.sqlite_db = self.connect_db('{}/{}'.format(app.root_path, dbfile))
except sqlite3.OperationalError as e:
raise e
#app.teardown_request
def teardown_request(e):
if hasattr(g, 'sqlite_db'): self.close_db(g.sqlite_db)
#app.route('/someroute', methods=["GET"]:
def someroute():
db_obj = DB(g.sqlite_db)
.
.
.
Use
pip install flask-sqlalchemy==2.5.1
This might solve the error
To expand on #VadimK's answer. If you want to prevent your code from executing outside of an app_context you can use flask.has_app_context() to see if the code is currently inside an app context:
See also: flask.has_request_context()
This is what fixed it for me. I hope it helps someone else.
if __name__ == "__main__":
with app.app_context():
db.create_all()
app.run(debug=True)
I had the same issue while doing some unit testing.
Adding the following function to my test class solved my issue:
#classmethod
def setUpClass(self):
self.app = create_app("testing")
self.client = self.app.test_client()
When initializing the app I use this block instead :
def create_app():
app = Flask(__name__)
with app.app_context():
init_db()
return app
when creating an app just add the below code.
app = Flask(__name__)
app.app_context().push()
Install this version of flask using
pip install flask-sqlalchemy==2.5.1
then run db.create_all() and it will run.
ERROR:This typically means that you attempted to use functionality that needed
to interface with the current application object in a way. To solve
this set up an application context with app.app_context(). See the
documentation for more information.

Categories