Inheriting configuration from parent app in Flask - python

I have an application that is constructed as follows:
app = Flask(__name__)
app.wsgi_app = DispatcherMiddleware(frontend.create_app(), {
'/api': api.create_app()
})
app.config['DATABASE'] = db
I want to access the same database in both the frontend and api apps, but when I run something like current_app.config['DATABASE'] in Blueprints registered to api, that raises KeyError: 'DATABASE'. Is it possible to inherit configurations so that tests, etc. need only modify the top-level abstraction? Thanks.

Simply change your create_app methods on frontend and api to take a configuration dictionary and use flask.Config to create a configuration with all the properties of app.config (loadable from environmental variables, etc.):
from flask import Config
from werkzeug.wsgi import DispatcherMiddlware
config = Config()
config.from_envvar("YOUR_PROGRAM_CONFIG")
app = DispatcherMiddlware(frontend.create_app(config), {
'/api': api.create_app(config)
})
Then you can merge the provided config with each of your app's configurations:
def create_app(config):
app = Flask(__name__)
app.config.update(config)
# Potentially, load another config
app.config.from_envvar("YOUR_PROGRAM_CONFIG_FRONTEND", silent=True)
return app
Note that there is no need to create an app to route to other apps - the dispatcher middleware can already do that for you.
Also note that if you are already using blueprints it may make more sense to simply ship the blueprints and compose them on a single application, rather than using dispatcher middleware (depending, of course on how complex your apps are).

Related

Best methods access to app from __init__.py create_app() using Blueprints and Flask 1.0+

I'm a bit confused by the changes to Flask 1.0.
The docs suggest creating a factory for the application in __init__.py where app=create_app(config_file) which is called from run.py.
Now in prior versions of Flask I had could from application import app in say a blueprint controller because app was not returned from create_app(). I can't call create_app() again, yet I don't have access to #app.route, app.config etc, etc.
I've tried and had little success pushing the context. Now there are various not-so-nice ways of doing this but what is the "canonical" way?
The concept of an application factory is not new to Flask 1.0. You don't have to use a factory if your app doesn't need one.
If you do use an app factory, then you usually do not register any routes directly on the app. You'd create a blueprint instead, register your routes to the blueprint, and in the app factory, attach the blueprint to the app.
Anywhere you need to access the app configuration, you'd use the current_app proxy.
For example, in the views.py module in your application you could create a blueprint just for the views; I included an example use of current_app to access configuration:
from flask import Blueprint, current_app
bp = BluePrint('main', __name__)
#bp.route(...)
def some_route_name(...):
# ...
if current_app.config['SOME_CONFIGURATION_SETTING']:
# ...
and you'd register the blueprint with
def create_app(config_filename=None):
app = Flask(__name__)
# configuration setup, use the confuration module as a default
from . import configuration
app.config.from_object(config)
app.config.from_envvar("PROJECTNAME_SETTINGS", silent=True)
if config_filename is not None:
app.config.from_pyfile(config_filename)
from . import views
app.register_blueprint(views.bp)
# other registrations
return app

Blueprints in Flask

I am taking over a Flask application that has a user module, but does not have a landing page. The structure of the project is:
|-application.py
|-manage.py
|-settings.py
|-/templates
|----base.html
|----index.html
|----navbar.html
|----/user
|--------views.py
application.py:
from flask import Flask
....
def create_app(**config_overrides):
app = Flask(__name__)
app.config.from_pyfile('settings.py')
app.config.update(config_overrides)
db.init_app(app)
from user.views import user_app
app.register_blueprint(user_app)
return app
user/views.py:
from flask import Blueprint, render_template, request, redirect, session, url_for, abort
...
user_app = Blueprint('user_app', __name__)
#user_app.route('login', methods = ('GET','POST'))
def login():
....
I placed index.html in the templates folder.
Should I place a view.py in the root directory where I would put a route to an index.html?
You can add additional routes anywhere you want.
However, since the package uses a create_app() app factory, you can't register those routes with an #app.route() decorator; the app is not created in a way you can just import.
So yes, creating a views.py module is a good organisational idea, but do create a Blueprint() there too, and register that blueprint in the create_app() function with the Flask() app instance.
In views.py:
from flask import Blueprint
bp = Blueprint('main', __name__)
#main.route('/')
def index():
# produce a landing page
and in create_app() in application.py, add
import views
app.register_blueprint(views.bp)
(I use a convention of using the bp variable name for all my blueprints, and then just importing the module and registering the module.bp attribute).
It's the explicit import and app.register_blueprint() call that ties any of the blueprints used in a Flask project into the final app's routes. Blueprints can share the same prefix, including having no prefix.
One thing to note: here the views module is now a top-level module next to application.py. It'd be better if everything was moved into a package, giving you more freedom over what names you use. All modules in a single package are namespaced, kept separate from other top-level modules such as json and time and flask, so there won't be clashes when you want to use the same name for one of the additional modules in your project.
You'd move everything but manage.py into a subdirectory, with a project-appropriate name, and move application.py to __init__.py. Imports can then use from . import ... to import from the current package, etc.

How to import the app using flask foundation?

I am using Flask foundation to begin with my new flask app.
The init.py file has a method:
def create_app(object_name, env="prod"):
"""
An flask application factory, as explained here:
http://flask.pocoo.org/docs/patterns/appfactories/
Arguments:
object_name: the python path of the config object,
e.g. sservice.settings.ProdConfig
env: The name of the current environment, e.g. prod or dev
"""
app = Flask(__name__)
app.config.from_object(object_name)
app.config['ENV'] = env
#init the cache
cache.init_app(app)
debug_toolbar.init_app(app)
#init SQLAlchemy
db.init_app(app)
login_manager.init_app(app)
# Import and register the different asset bundles
assets_env.init_app(app)
assets_loader = PythonAssetsLoader(assets)
for name, bundle in assets_loader.load_bundles().iteritems():
assets_env.register(name, bundle)
# register our blueprints
from controllers.main import main
app.register_blueprint(main)
return app
that is imported in manage.py.
But what If I need to use this app variable to access the application configuration in modules within the application? I can't use current_app outside request contexts. Creating one is ugly.
I need to have a app variable in my models.py file:
In models.py
# how to import? Below doesn't work
from appname import app
# access config
print(app.config)
I can't call this method create_app, because it should be only called once to create the application. Not anytime you need to import the app. How should I solve this?
I only want to use create_app() exactly once outside the package to give it the wsgi server, but I don't want to use it within the package.
What is working now for me:
Wherever there is no current_app (outside of request contexts I guess) just use something like this
app = Flask(__name__)
app.config.from_object('appname.settings_file.DevConfig')
print(app.config)

Running webapp2 app in a multiple WSGI apps set up with Werkzeug

I am trying to run a django app and a webapp2 app together in one python interpreter. I'm using werkzeug for that as described here.
Here's my sample code.
from werkzeug.wsgi import DispatcherMiddleware
from django_app import application as djangoapp
from webapp2_app import application as webapp2app
application = DispatcherMiddleware(djangoapp, {
'/backend': webapp2app
})
After doing this, I would expect all requests to /backend should be treated by the webapp2 app as /. But it treats the requests as /backend. This work fines with other WSGI apps using django or flask. The problem only appears with webapp2 apps. Does anyone have any suggestions how to overcome this? Is there any other way I can achieve my purpose without using werkzeug for serving multiple WSGI apps under one domain?
DispatcherMiddleware fabricates environments for your apps and especially SCRIPT_NAME. Django can deal with it with configuration varibale FORCE_SCRIPT_NAME = '' (docs).
With Webapp2 it's slightly more complicated. You can create subclass of webapp2.WSGIApplication and override __call__() method and force SCRIPT_NAME to desired value. So in your webapp2_app.py it could be like this
import webapp2
class WSGIApp(webapp2.WSGIApplication):
def __call__(self, environ, start_response):
environ['SCRIPT_NAME'] = ''
return super(WSGIApp, self).__call__(environ, start_response)
# app = WSGIApp(...)

Making urls.py file for Flask like in Django

Maybe someone can help/explain me, how to create urls.py file for Flask like in Django?
main.py - main project file. It includes only app runner (app.run()).
urls.py is situated in the same directory and need to provide views
from views.py.
You can do this as is described in the Flask documentation, basically by calling app.add_url_rule to set your routes rather than using the decorator.
In addition to the Flask documentation, this can be solved like this:
When creating the Flask app load your 'urls.py' file
app.register_blueprint(apps.someapp.urls.mod)
Then structure urls.py as following:
from flask import Blueprint
from apps.someapp.views import SomeView
# set method as endpoint
view = SomeView.as_view('someview')
# Create the blueprint for this app
mod = Blueprint("payment_methods", __name__, url_prefix="/someapp/", template_folder="templates")
# Add the view as route; methods like GET, POST, PUT will automatically route to class methods with parameters
mod.add_url_rule('<int:id>/', view_func=view)

Categories