I am creating a flask instance using:
application/init.py:
def create_app(config_name):
app = Flask(__name__)
mongo = PyMongo(app)
...
return app
manage.py
app = create_app(Config.FLASK_CONFIG)
manager = Manager(app)
with application started using:
python manage.py runserver
In views I would like to access the mongo instance created in app. Any idea how I can access the PyMongo wrapper around app?
In __init__.py, initialize app and mongo to None.
app = None
mongo = None
Create a separate setup.py module in application and move create_app factory function there. The reason for this approach is so as to bind Flask application instance and mongo instance to your application package.
Also, refactor the app factory separating the database factory from it since we need to keep the database instance around.
import application
def create_app(config_name):
app = Flask(__name__)
....
return app
def create_mongo(app):
return PyMongo(app)
app = create_app(Config.FLASK_CONFIG)
# bind to application package
application.app = app
application.mongo = create_mongo(app)
In manage.py, rewrite this to use the app instance created in setup.py module.
from application.setup import app
manager = Manager(app)
Mongo instance can be imported directly from the application package because the instance was bound earlier to the package in setup.
from application import mongo
NOTE:
You may care to know that initializing a PyMongo instance registers pymongo as an extension in your flask application.
The connection and database are bound to a config_prefix key whose default value is 'MONGO' in the registered extension. They can both be retrieved like so:
cx, db = app.extensions['pymongo']['MONGO']
Reference
Registering PyMongo extension in Flask application
Related
Suppose I have something like this in app/models.py:
from flask import current_app as app
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.sql import func
db = SQLAlchemy()
class LoginLink(db.Model):
...
expiration_date = db.Column(
db.DateTime(timezone=True), nullable=False,
server_default=func.now() + str(app.config["LOGIN_LINK_EXP_TIME"]) # Error here!!!
)
And this in app/__init__.py:
from flask import Flask
from config import CONFIG_OBJECT
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(CONFIG_OBJECT[config_name])
from app.models import db
db.init_app(app)
db.create_all(app=app)
return app
Finally, this is my config.py:
from datetime import timedelta
CONFIG_OBJECT = {
"dev": "config.DevConfig",
"prod": "config.ProdConfig"
}
class Config:
...
class DevConfig(Config):
LOGIN_LINK_EXP_TIME = timedelta(seconds=30)
class ProdConfig(Config):
LOGIN_LINK_EXP_TIME = timedelta(minutes=30)
I tried to use app.app_context() everywhere (believe me) and I'm still getting this error:
RuntimeError: Working outside of application context.
I'm just trying to do the following: in a development environment I want the login links to expire in 30 seconds (for testing and demonstration purposes), but login links will last 30 minutes in a production environment.
How to accomplish this using different config environments?
Note: this is intended to be a generic question.
I think I had a misconception about application factories. We have the following from the Flask documentation:
The downside is that you cannot use the application object in the blueprints at import time. You can however use it from within a request.
Additionally:
It’s preferable to create your extensions and app factories so that the extension object does not initially get bound to the application.
What is in bold is what I was doing wrong: using the application object outside a request and bounding the extension to the application.
Therefore, I only see two solutions:
Use the app.config object (or dict) only within requests (the best IMHO).
Don't include configs that require to be used outside of requests in the app.config object (although this may complicate testing a bit).
I am trying to integrate SQLAlchemy into my application. I currently use application factories. my app/__init__.py looks like below
from flask import Flask
def create_app(config_filename):
app = Flask(__name__)
app.config.from_pyfile(config_filename)
from app.views.admin import admin
app.register_blueprint(frontend)
return app
Now I want to use sqlalchemy but the sqlalchemy object needs an instance of my app to be properly constructed. This documentation http://flask.pocoo.org/docs/1.0/patterns/appfactories/, recommends that I create this object in a separate file like say model.py
db = SQLAlchemy()
Then inside my factory method create_app in __init__.py, I add this
from app.model import db
db.init_app(app)
Now my question is, if I import db in other files, will I be importing a db that has been initialized with an instance of my app or just what is declared in the model.py file.
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
spinngod.py - flask app starter code
from app import create_app
import sys
run_profile = str(sys.argv[1]) if len(sys.argv) >= 2 else 'development'
app = create_app(run_profile)
print("App Root Path:" + app.root_path)
if __name__ == '__main__':
print sys.path
app.run(debug=True, host='0.0.0.0')
app/init.py - creates flask app
def create_app(profile_name):
print "currently active profile:" + profile_name
app = Flask(__name__)
############# configurations ####################
app.config.from_object(config[profile_name])
configure_app(app)
configure_app_logger(app)
#################### blueprint registration and rest_plus namespace additions ###############
from api_1_0 import api as api_1_0_blueprint
from api_1_0.restplus import api_restplus
# ************************************************** #
api_restplus.init_app(api_1_0_blueprint)
api_restplus.add_namespace(application_namespace)
api_restplus.add_namespace(pipeline_template_namespace)
api_restplus.add_namespace(loadbalancer_namespace)
api_restplus.add_namespace(servergroup_namespace)
api_restplus.add_namespace(task_namespace)
# ************************************************** #
app.register_blueprint(api_1_0_blueprint)
##############################################################
return app
I want to access flask config variables defined in config.py in some other files which are outside application context. The app configuration depends on which profile it is started with (dev,stage or production) which is being passed from command line as an arg.
The only way that I can think of accessing config variables outside app context is to set profile (dev,stage or prod) as an environment variable and
then import directly from config file.
The second way that I tried was to move creation of flask app in app/init.py outside method.
This is how I am trying to access config variables in another class.
import requests
class Client(object):
def __init__(self):
from app import app
print "fjaijflkajsf" + app.config['SPINNAKER_BASE_URL']
pass
Is there a way better of doing this in flask ?
From the docs:
Rather than passing the application around to each function, the current_app and g proxies are accessed instead.
The Flask application object has attributes, such as config, that are useful to access within views and CLI commands. However, importing the app instance within the modules in your project is prone to circular import issues.
Flask solves this issue with the application context. Rather than referring to an app directly, you use the the current_app proxy, which points to the application handling the current activity.
You import current_app like this:
from flask import current_app
and then access the config or other attributes like this:
config = current_app.config
Example:
src/application.py (where config is set in the context)
create_app():
app = Flask('app')
app.config.from_object(some_class)
return app
src/module/another_module.py
from flask import current_app
def function_that_requires_config():
config = current_app.config
Alternative:
src/application.py (where config is set in the context)
APP = create_app(os.environ.get('FLASK_ENV'))
src/module/another_module.py
from src.application import APP
def function_that_requires_config():
config_value = APP.config.get(config_key, default_value)
Not sure if it is good to put it here as it may not respond to the question directly, but here is the cleanest way i've figured to use config values outside of requests, without having to pass config as a param.
The solution is actually pretty simple, juste consider the part of your code as a flask_extension.
my exemple will be the use of external api, with ROOT_URL in the config file, and i don't want to make api call from within my routes, so the api is in its own module.
in my create_app fuction:
from flask import Flask
from .api import api
from .configmodule import Config
from .model import db
def create_app(environment):
app = Flask(__name__)
app.config.from_object(Config.get_config(environment))
db.init_app(app)
api.init_app(app) # here i use api.init_app the same way i do for sqlalchemy
and in api/init.py
class Api:
def init_app(self, app):
self.config = app.config
api = Api()
and in any files in my api modude i can now write
from . import api
def foo():
print(api.config.get("API_ROOT_URL"))
this can even be improved if you feel the need to access some other global app vars from your module.
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.