Access a Flask extension that is defined in the app factory - python

I am using the app factory pattern to set up my Flask application. My app uses the Flask-Babel extension, and that is set up in the factory as well. However, I want to access the extension in a blueprint in order to use it,
The factory is in __init__.py.
def create_app(object_name):
app = Flask(__name__)
app.config.from_object(object_name)
babel = Babel(app)
app.register_blueprint(main_blueprint)
app.register_blueprint(category_blueprint)
app.register_blueprint(item_blueprint)
db.init_app(app)
return app
I want to add the following to main.py:
#babel.localeselector
def get_locale():
if 'locale' in session:
return session['locale']
return request.accept_languages.best_match(LANGUAGES.keys())
#application.route('/locale/<locale>/', methods=['GET'])
def set_locale(locale):
session['locale'] = locale
redirect_to = request.args.get('redirect_to', '/')
return redirect(redirect_to) # Change this to previous url
Unfortunately, main.py doesn't have access to the babel variable from the application factory. How should I go about solving this?

Flask extensions are designed to be instantiated without an app instance for exactly this case. Outside the factory, define your extensions. Inside the factory, call init_app to associate the app with the extension.
babel = Babel()
def create_app():
...
babel.init_app(app)
...
Now the babel name is importable at any time, not just after the app has been created.
You already appear to be doing this correctly with the db (Flask-SQLAlchemy) extension.
In the case of your specific babel.localeselector example, it might make more sense to put that next to babel since it's being defined there.

Related

Flask-Babel localeselector is not being called

Flask-babel doesn't call its localeselector even once. I'm using app factory to init my app. The translations folder is within my app, It was created by babel, according to the docs. I've also tried moving the translations dir to the folder containing the run.py, which calls the factory, but to no effect.
from flask import Flask, session, request
from myapp.text_fields import next_month
from myapp.config import Config
from flask_babel import Babel
babel = Babel()
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(Config)
babel.init_app(app)
from myapp.views import user
from myapp.errors import errors
app.register_blueprint(user)
app.register_blueprint(errors)
#babel.localeselector
def get_locale():
try:
language = session['language']
except KeyError:
language = None
if language is not None:
return language
return request.accept_languages.best_match(app.config['LANGUAGES'])
#app.context_processor
def inject_conf_var():
return dict(
MONTH=next_month(),
AVAILABLE_LANGUAGES=app.config['LANGUAGES'],
CURRENT_LANGUAGE=session.get('language',
request.accept_languages.best_match(app.config['LANGUAGES'])))
return app
Here's the config part concerning babel:
BABEL_DEFAULT_LOCALE = 'pl'
LANGUAGES = ['pl', 'ua', 'ru', 'en']
So far I've complied only EN, tried to change the default, but it doens't do anything either. Seems like babel is not able to locate the translations folder, I'm not sure how to fix this.
Solved it, turned out I had to move localeselector out of the factory. Since most of my text fields are generated on the service side rather than forntend, I also had to use lazy_text on all of them, as they are called by endpoints rather than defined within them.

Configure Email Token with Flask Factory Application create_app

I am getting confused with configurations and imports once I started using the Flask factory application pattern.
I am creating an application with the function create_app in #app/init.py
I have a config file for setting the development/testing/production variables, and an instance folder with another config file.
def create_app(config_name):
app=Flask(__name__, instance_relative_config=True)
app.config.from_object(app_config[config_name])
app.config.from_pyfile('config.py')
etc...
return app
I am using blueprints and have an authentication view in #app/auth/views.py
I am trying to set up email confirmation tokens using URLSafeTimedSerializer...
from itsdangerous import URLSafeTimedSerializer
#auth.route('/register', methods=['GET','POST'])
def register():
ts = URLSafeTimedSerializer(app.config['SECRET_KEY'])
token = ts.dumps(self.email, salt='email-confirm-key')
etc...
Now my problem is, my variable 'ts' needs the app.config['SECRET_KEY'] set. But I am unable to define the app variable (as is shown in all online tutorials). I get an error when I try to import...(in #app/auth/views.py)
from .. import app
and when I try to import like...
from .. import create_app
Can someone shine light on how to initialize modules using 'app' and app.config outside the flask app factory create_app?
Hope you understand my question.
In this scenario you should use Flask.current_app
from flask import current_app
...
ts = URLSafeTimedSerializer(current_app.config['SECRET_KEY'])
From the documentation:
flask.current_app
Points to the application handling the request. This
is useful for extensions that want to support multiple applications
running side by side. This is powered by the application context and
not by the request context, so you can change the value of this proxy
by using the app_context() method.
This link aso explains further details about the Flask application factory methodology, in particular using current_app to access the app configuration.

Access Flask config outside of application factory

I'm currently using the Flask Application Factory pattern with Blueprints. The issue that I'm having is how do I access the app.config object outside of the application factory?
I don't need all the configuration options from the Flask app. I just need 6 keys. So the current way I do this is when the create_app(application factory) is called, I basically create a global_config dictionary object and I just set the global_config dictionary to have the 6 keys that I need.
Then, the other modules that need those configuration options, they just import global_config dictionary.
I'm thinking, there has to be a better way to do this right?
So, on to the code
My current init.py file:
def set_global_config(app_config):
global_config['CUPS_SAFETY'] = app_config['CUPS_SAFETY']
global_config['CUPS_SERVERS'] = app_config['CUPS_SERVERS']
global_config['API_SAFE_MODE'] = app_config['API_SAFE_MODE']
global_config['XSS_SAFETY'] = app_config['XSS_SAFETY']
global_config['ALLOWED_HOSTS'] = app_config['ALLOWED_HOSTS']
global_config['SQLALCHEMY_DATABASE_URI'] = app_config['SQLALCHEMY_DATABASE_URI']
def create_app(config_file):
app = Flask(__name__, instance_relative_config=True)
try:
app.config.from_pyfile(config_file)
except IOError:
app.config.from_pyfile('default.py')
cel.conf.update(app.config)
set_global_config(app.config)
else:
cel.conf.update(app.config)
set_global_config(app.config)
CORS(app, resources=r'/*')
Compress(app)
# Initialize app with SQLAlchemy
db.init_app(app)
with app.app_context():
db.Model.metadata.reflect(db.engine)
db.create_all()
from authenication.auth import auth
from club.view import club
from tms.view import tms
from reports.view import reports
from conveyor.view import conveyor
# Register blueprints
app.register_blueprint(auth)
app.register_blueprint(club)
app.register_blueprint(tms)
app.register_blueprint(reports)
app.register_blueprint(conveyor)
return app
An example of a module that needs access to those global_config options:
from package import global_config as config
club = Blueprint('club', __name__)
#club.route('/get_printers', methods=['GET', 'POST'])
def getListOfPrinters():
dict = {}
for eachPrinter in config['CUPS_SERVERS']:
dict[eachPrinter] = {
'code': eachPrinter,
'name': eachPrinter
}
outDict = {'printers': dict, 'success': True}
return jsonify(outDict)
There has to be a better way then passing a global dictionary around the application correct?
There is no need to use global names here, that defeats the purpose of using an app factory in the first place.
Within views, such as in your example, current_app is bound to the app handling the current app/request context.
from flask import current_app
#bp.route('/')
def example():
servers = current_app.config['CUPS_SERVERS']
...
If you need access to the app while setting up a blueprint, the record decorator marks functions that are called with the state the blueprint is being registered with.
#bp.record
def setup(state):
servers = state.app.config['CUPS_SERVERS']
...

Flask Blueprint Initialization - Initializing some global variables

I'm new to Flask and I'm about to write a larger application. So far I'v divided functionality into blueprints. Now I want to be able to set some global variable from within a blueprint during initialization of it (so outside of a request context). Basically, I want to automatically initialize a navigation list for certain blueprints, so each blueprint needs to tell my base application how it wants to be named and what its default route is.
My goal is that other people can extend my application by just putting their custom blueprint into my application's "plugin" folder. In this scenario, my application doesn't know their routes or names. It needs to automatically learn it while loading the specific blueprint...
To explain it in some other way: I have a main application containing some sub applications implemented as blueprints. The main application should hold a navigation bar referencing to all sub applications (blueprints). How can the blueprint register something in that main menu navigation variable on (e.g. on initialization)?
(I didn't find a way to access something like "self.parent" or the application context from a blueprint. Don't blueprints have something like a constructor?)
so each blueprint needs to tell my base application how it want's to
be named and what it's default route is.
When you create a blueprint you already pass its name in the first parameter:
simple_page = Blueprint('simple_page')
You can pass to the constructor the url_prefix value too
simple_page = Blueprint('simple_page', url_prefix='/pages')
I have a main application containing some sub applications implemented
as blueprints. The main application should hold a navigation bar
referencing to all sub applications (blueprints)
This is an example in one python module, you should split each blueprint in its own module.
from flask import Flask, Blueprint, render_template
# ADMIN
admin = Blueprint('admin', __name__, url_prefix='/admin')
#admin.route('/')
def admin_index():
return 'Admin module'
#admin.route('/settings')
def settings():
return 'Admin Settings'
# USER
user = Blueprint('user', __name__, url_prefix='/user')
#user.route('/')
def user_index():
return 'User module'
#user.route('/profile')
def profile():
return 'User Profile'
app = Flask(__name__)
app.register_blueprint(admin)
app.register_blueprint(user)
#app.route('/')
def index():
return render_template('index.html', blueprints=app.blueprints)
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True, port=7000)
The Jinja2 template. Remember to place this file in the templates folder in you root project, which is where flask search the templates by default.
<ul>
{% for bp_name, bp in blueprints.iteritems() %}
<li>{{ bp.name }}</li>
{% endfor %}
</ul>
From the flask main application object, in this case app, you can access the list of blueprints registered; app.blueprints, which is a python dictionary with the name of the blueprint that you passed in the constructor. So you pass this object to you template and loops on it to render the name and URL in the menu.
Furthermore, what if I want to enable blueprint authors to pass more
data in a standardized way (e.g. in which color to display the link to
his piece, or whatever...)
As this is a blueprint specific data, I think a good solution is to extend the Blueprint class and implement a custom method to save extra data in a standardized way and then access then from the main application object.
custom.py
from flask import Blueprint
class MyBlueprint(Blueprint):
bp_data = {}
def set_data(data):
# here you can make extra task like ensuring if
# a minum group of value were provided for instance
bp_data = data
admin.py
from .custom import MyBlueprint
admin = MyBlueprint('admin', __name__, url_prefix='/admin')
admin.set_data({'color': '#a569bd', 'enabled': true})
# all the blueprint's routes
...
app.py
from admin import admin
app = Flask(__name__)
app.register_blueprint(admin)
#app.route('/')
def index():
for a, b in app.blueprints:
print b.bp_data['color']
print b.bp_data['enabled']
...
Of course, my custom Blueprint class needs more work on it, like validating what type of data its being passed or throwing an error if there isn't a required value, like; title, require_auth, etc. From this point, it's you who must define what is the minimum required data that a blueprint must provide to your main application to work properly.
Here I present you a extensible app pattern that I usually use. It is proved that work fine.
Directory structure:
YourApp
|- plugins (will contain all the user's plugins)
|- __init__.py (empty)
|-plugin1
|- __init__.py (empty)
|- loader.py
|- app.py
app.py
from flask import Flask
import os
def plugin_loader(app):
""" Function for discover new potencial plugins.
After checking the validity (it means, check if it implements
'register_as_plugin', load the user defined blueprint"""
plugins = [x for x in os.listdir('./plugins')
if os.path.isdir('./plugins/' + x)]
for plugin in plugins:
# TODO: Add checking for validation
module = __import__('plugins.' + str(plugin) + '.loader', fromlist=['register_as_plugin'])
app = module.register_as_plugin(app)
return app
# Creates the flask app
app = Flask(__name__)
# Load the plugins as blueprints
app = plugin_loader(app)
print(app.url_map)
#app.route('/')
def root():
return "Web root!"
app.run()
plugins/plugin1/loader.py
from flask import Blueprint
plugin1 = Blueprint('plugin1', __name__)
#plugin1.route('/version')
def version():
return "Plugin 1"
def register_as_plugin(app):
app.register_blueprint(plugin1, url_prefix='/plugin1')
return app
So futures plugins should implement the register_as_plugin function, inside the loader.py file, inside their directory in plugins/.
For more information about directories discovery you can read https://docs.python.org/2/library/os.path.html.
For more information about the dynamic import of modules you can read https://docs.python.org/2/library/functions.html#import.
Proved with Python 2.7.6, Flask 0.10.1 and Unix platform.

Can I keep all Flask blueprints in one file?

I am using an app factory to initialize my app. In it, I import all the blueprints and register them one by one. Is there a way I can move the import and register statements to a different file or inform the factory about them without referencing them all individually?
def create_app(config_filename):
app = Flask(__name__)
app.config.from_object(config_filename)
from app.users.models import db
db.init_app(app)
from app.users.views import users
from app.posts.views import posts
app.register_blueprint(posts, url_prefix='/posts')
app.register_blueprint(users, url_prefix='/users')
return app
In my project I'm actually generating the blueprints with another script, so I'd like to be able to generate the registration too by appending to a file or something, rather than trying to modify code in the factory.
Yes, you can import and register the blueprints in some other module. But there's no practical point to this approach, it just moves the imports and register calls somewhere else.
myapp/blueprints.py:
from app.users.views import users
from app.posts.views import posts
def init_app(app):
app.register_blueprint(users, prefix='/users')
app.register_blueprint(posts, prefix='/posts')
myapp/__init__.py:
def create_app():
app = Flask(__name__)
# ...
from myapp import blueprints
blueprints.init_app(app)
# ...
Something more useful might be to tell the app what packages to import from and have the app expect to find a blueprint in some standard location for each package. Assuming the blueprint variable will always have the same name as the package, is defined in views, and has the same prefix as the name:
from werkzeug.utils import import_string
def create_app():
app = Flask(__name__)
# ...
for name in ('users', 'posts'):
bp = import_string('myapp.{0}.views:{1}'.format(name, name))
app.register_blueprint(bp, prefix='/{0}'.format(name))
# ...

Categories

Resources