How to set the Jinja environment variable in Flask? - python

I have a page with the following Code Structure:
Python Code:
from flask import Flask,render_template
app=Flask(__name__)
#app.route('/')
def home():
return render_template("first.html")
#app.route('/second')
def next():
return render_template("second.html")
if __name__=="__main__":
app.run(debug=True)
HTML Code for second.html:
{% extends first.html %}
<dosomething>
</dosomething>
Now I want to set the Environment of Jinja as follows:
Environment:
import jinja2
JINJA_ENV=jinja2.Environment(block_start_string='#%',block_end_string="#%",variable_start_string='{',variable_end_string='}')
I want this changes to be reflected in Flask. Should this variable be initialized with Flask in some way or left as such?

It's not publically documented, but a Flask() object has a .jinja_options dict that will be used to build the Jinja Environment. Just make sure to set it ASAP.
Source:
https://github.com/pallets/flask/blob/bbb273bb761461ab329f03ff2d9002f6cb81e2a4/src/flask/app.py#L272
https://github.com/pallets/flask/blob/bbb273bb761461ab329f03ff2d9002f6cb81e2a4/src/flask/app.py#L652
Example:
from flask import Flask
from jinja2 import ChainableUndefined
app = Flask(__name__)
app.jinja_options["undefined"] = ChainableUndefined

You can also pass in an entire Jinja Environment like this:
from flask import Flask
from jinja2 import Environment
app = Flask(__name__)
app.jinja_env = Environment(...)

Related

Flask Babel RuntimeError: Working outside of request context

I tried to set up multiple Dash Apps inside a Flask App and use Flask Babel.
from flask import Flask, request
from flask_babel import Babel, gettext
from werkzeug.middleware.dispatcher import DispatcherMiddleware
def init_app():
"""Construct core Flask application."""
app = Flask(__name__, instance_relative_config=False)
app.config.from_object("config.Config")
babel = Babel(app)
#babel.localeselector
def get_locale():
return request.accept_languages.best_match(app.config["LANGUAGES"])
with app.app_context():
# Import parts of our core Flask app
from . import routes
from .plotly.sec import init_dashboard_sec
from .plotly.bafin import init_dashboard_bafin
# app = init_dashboard(app)
app = DispatcherMiddleware(app, {
"/s": init_dashboard_sec(app),
"/b": init_dashboard_bafin(app)
})
return app
However, since I added the babel decorated function I get the following error:
RuntimeError: Working outside of request context.
This typically means that you attempted to use functionality that needed
an active HTTP request. Consult the documentation on testing for
information about how to avoid this problem.
I tried to move the function to a different position but the error stayed the same.
I don't know if it is the correct solution but the following is working:
from flask import Flask, request
from flask_babel import Babel, gettext
from werkzeug.middleware.dispatcher import DispatcherMiddleware
def init_app():
"""Construct core Flask application."""
app = Flask(__name__, instance_relative_config=False)
app.config.from_object("config.Config")
babel = Babel(app)
with app.app_context():
# Import parts of our core Flask app
from . import routes
from .plotly.sec import init_dashboard_sec
from .plotly.bafin import init_dashboard_bafin
# app = init_dashboard(app)
app = DispatcherMiddleware(app, {
"/s": init_dashboard_sec(app),
"/b": init_dashboard_bafin(app)
})
#babel.localeselector
def get_locale():
return request.accept_languages.best_match(["de", "en"])
return app
So babel gets initialized outside of the app context but the localselector is inside the context. For me it does not make much sense since the localeselector is still out of a request context but its working.
if you are probably working on the the terminal testing stuff out and you get the error just do this
from your_app import create_app
app = create_app()
with app.test_request_context(
'/make_report/2017', data={'format': 'short'}):
##do something here
for more information you can vist this part of the docs to learn more https://flask.palletsprojects.com/en/2.1.x/reqcontext/

How can I use the correct routing to display an HTML page with Flask?

I am lost with python and flask as I do not understand how the routing structure works to navigate between HTML pages.
Deployed to heroku my structure is:
/my_app_name/
app.py
config.py
drivers.html
Procfile
requirements.txt
the app.py file is:
import os
from flask import Flask, render_template, request, url_for
from flask.ext.sqlalchemy import SQLAlchemy
import json
app = Flask(__name__)
db = SQLAlchemy(app)
app = Flask(__name__)
#app.route('/')
def hello():
return 'Hello World from Python!'
#app.route('/')
def drivers():
drivers = db.drivers.select()
return render_template('drivers.html')
#return HttpResponse('/drivers.html', json.dumps(result), content_type='application/json')
if __name__ == '__main__':
app.run(debug=True)
config.py is:
SQLALCHEMY_DATABASE_URI = 'heroku_database_uri_string) #connection is OK, no problem here
drivers.html is:
{% block body %}
<ul>
{% for driver in drivers %}
<li><h2>{{ driver.driver_name }}</h2>
</ul>
{% endblock %}
When I navigate to myapp.heroku.com/ I get the "hello world from pyhton" fine but when I navigate to myapp.heroku.com/drivers.html
I get a '404' error.
So, 2 questions:
1) Why 404? What is wrong?
2) I assume it is, but is the entire structure flawed?
You surely must have noticed these lines:
#app.route(...)
in your code. They define the routes. You have two handlers pointing at "/", whereas presumably you want the drivers handler pointing at "/drivers": so change the argument you're passing to route.
Also, you shouldn't be thinking in terms of HTML pages, and you shouldn't be trying to give your routes ".html" suffixes. Just use "drivers", and navigate to myapp.heroku.com/drivers.
Finally, there's no need to upload to Heroku to test this: just run the local development server. Please note that all this is well described in the Flask docs; you should read them.

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)

Changing jinja_env from a blueprint

I am trying to register a new jinja global on my blueprint using the Blueprint object. However, it appears that Blueprint objects do not have jinja_env attributes; how can I register a new jinja global attributes?
Here's the __init__.py of the blueprint, this does not work:
from flask import Blueprint, current_app
uploader = Blueprint('uploader', __name__, template_folder='templates')
from . import views
from . import models
current_app.jinja_env.globals['form_token'] = views.generate_form_token
Nor does this:
uploader.jinja_env.globals['form_token'] = views.generate_form_token
Use the Blueprint.app_template_global decorator to register a global function to the jinja env.
uploader.app_template_global(views.generate_form_token)
Or in views.py:
#uploader.app_template_global
def generate_form_token():
pass
I was facing similar problem using Flask with Jinja 2 and having trouble registering global template using app_template_global decorator.
I used add_app_template_global decorator described here Flask API refernce
The following code works for Flask (0.10.1) blueprints with Jinja2(2.8).
Here is the __init__.py file.
from flask import Blueprint
blueprint_name = Blueprint('blueprint_name', __name__)
from . import views
add the decorator to function in main file [in my case views.py] file.
Here is the views.py file.
from . import blueprint_name
#blueprint_name.add_app_template_global
def function_name():
pass
You can then call this function in Jinja2 template as
<h1>
{{ function_name() }}
<h1>

Split Python Flask app into multiple files

I'm having trouble understanding how to split a flask app into multiple files.
I'm creating a web service and I want to split the api's into different files (AccountAPI.py, UploadAPI.py, ...), just so I don't have one huge python file.
I've read that you can do this with Blueprints, but I'm not entirely sure that route is the right one for me.
Ultimately I want to run one Main python file and include other files so that when it runs, they are considered one big file.
For example if I have Main.py and AccountAPI.py I want to be able to do this:
Main.py:
from flask import Flask
import AccountAPI
app = Flask(__name__)
#app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run()
AccountAPI.py:
#app.route("/account")
def accountList():
return "list of accounts"
I know with this example it obviously won't work, but is it possible to do something like that?
Thanks
Yes, Blueprints are the right way to do it. What you are trying to do can be achieved like this:
Main.py
from flask import Flask
from AccountAPI import account_api
app = Flask(__name__)
app.register_blueprint(account_api)
#app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run()
AccountAPI.py
from flask import Blueprint
account_api = Blueprint('account_api', __name__)
#account_api.route("/account")
def accountList():
return "list of accounts"
If this is an option, you might consider using different URL prefixes for the different APIs/Blueprints in order to cleanly separate them. This can be done with a slight modification to the above register_blueprint call:
app.register_blueprint(account_api, url_prefix='/accounts')
For further documentation, you may also have a look at the official docs.
Using Blueprint you can add your routes in the routes directory.
Structure
app.py
routes
__init__.py
index.py
users.py
__init__.py
from flask import Blueprint
routes = Blueprint('routes', __name__)
from .index import *
from .users import *
index.py
from flask import render_template
from . import routes
#routes.route('/')
def index():
return render_template('index.html')
users.py
from flask import render_template
from . import routes
#routes.route('/users')
def users():
return render_template('users.html')
app.py
from routes import *
app.register_blueprint(routes)
If you want to add a new route file, say accounts.py, you just need to create the file accounts.py in the routes directory, just like index.py and users.py, then import it in the routes.__init__.py file
from .accounts import *
If you are using blueprints and want to route / redirect to a url of your blueprint inside a template you are using you need to use the correct url_for statement.
In your case if you would like to open the url account of your blueprint you have to state it like this in your template:
href="{{ url_for('account_api.account') }}"
and for the main app it would look like this:
redirect(url_for('account_api.account'))
Otherwise the werkzeug library will throw an error.
One another way to do this can be with lazy loading, where you would explicitly attach view functions on need basis.

Categories