Flask-Babel localeselector is not being called - python

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.

Related

Flask App not calling Babel localeselector

I am adding localisation to my Flask app with Flask-Babel, and I cannot seem to change the language. I followed all the Babel instructions for creating the po/mo files - I don't believe my issue is related to the translation data... its that I don't know how to change languages... which seems like it would/should be obvious.
My debugging shows that babel.localeselector is not being called. My implementation calls refresh(), which I think should call babel.localeselector (somehow... because I don't see how it works, as the refresh() command seems to just delete some keys from the app context... I don't know how that triggers the call to babel.localeselector)
app = Flask(__name__)
babel = Babel(app)
#babel.localeselector
def get_locale():
# if a user is logged in, use the locale from the user settings
user = User.get_by_id(session['user_id'])
if user is not None: return user.lang
return 'en'
and, when the user logs in, this function is called,
from flask_babel import refresh
def login(user_id):
# Gets called when user successfully logs in
refresh()
I have confirmed refresh() is being called.
But the language is not changed.
UPDATED:
Also tried this,
from flask import current_app
from common.models import User
from flask_babel import refresh
def login(user_id):
# Gets called when user successfully logs in
user = User.get_by_id(user_id)
current_app.config['BABEL_DEFAULT_LOCALE'] = user.lang
refresh()
Figured out the problem. I was using,
import gettext
_ = gettext.gettext
instead of,
from flask_babel import gettext as _

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 a Flask extension that is defined in the app factory

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.

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']
...

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