Changing jinja_env from a blueprint - python

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>

Related

Why is flask rendering the wrong template [duplicate]

I have a Flask app with blueprints. Each blueprint provides some templates. When I try to render the index.html template from the second blueprint, the first blueprint's template is rendered instead. Why is blueprint2 overriding blueprint1's templates? How can I render each blueprint's templates?
app/
__init__.py
blueprint1/
__init__.py
views.py
templates/
index.html
blueprint2/
__init__.py
views.py
templates/
index.html
blueprint2/__init__.py:
from flask import Blueprint
bp1 = Blueprint('bp1', __name__, template_folder='templates', url_prefix='/bp1')
from . import views
blueprint2/views.py:
from flask import render_template
from . import bp1
#bp1.route('/')
def index():
return render_template('index.html')
app/__init__.py:
from flask import Flask
from blueprint1 import bp1
from blueprint2 import bp2
application = Flask(__name__)
application.register_blueprint(bp1)
application.register_blueprint(bp2)
If I change the order the blueprints are registered, then blueprint2's templates override blueprint1's.
application.register_blueprint(bp2)
application.register_blueprint(bp1)
This is working exactly as intended, although not as you expect.
Defining a template folder for a blueprint only adds the folder to the template search path. It does not mean that a call to render_template from a blueprint's view will only check that folder.
Templates are looked up first at the application level, then in the order blueprints were registered. This is so that an extension can provide templates that can be overridden by an application.
The solution is to use separate folders within the template folder for templates related to specific blueprints. It's still possible to override them, but much harder to do so accidentally.
app/
blueprint1/
templates/
blueprint1/
index.html
blueprint2/
templates/
blueprint2/
index.html
Point each blueprint at its templates folder.
bp = Blueprint('bp1', __name__, template_folder='templates')
When rendering, point at the specific template under the templates folder.
render_template('blueprint1/index.html')
See Flask issue #1361 for more discussion.
I vaguely remember having trouble with something like this early on. You haven't posted all of your code, but I have four suggestions based on what you've written. Try the first, test it, and then if it still is not working, try the next ones, but independently test them to see if they work:
First, I cant't see your views.py file, so be sure you're importing the appropriate blueprint in your views.py files:
from . import bp1 # in blueprint1/views.py
from . import bp2 # in blueprint2/views.py
Second, you may need to fix the relative import statements in __init__.py as follows (note the period preceding the subfolders):
from .blueprint1 import blueprint1 as bp1
from .blueprint2 import blueprint2 as bp2
Third, since you're hardcoding the path to your templates in your render_template function, try removing template_folder='templates' from your blueprint definition.
Fourth, it looks like you named the url_prefix for your blueprint as "/bp1" when you registered it. Therefore, if the hard coded link to your file system still does not work:
render_template('blueprint1/index.html')
then also try this and see what happens:
render_template('bp1/index.html')
Again, I can't see your full code, but I hope this helps.

How to set the Jinja environment variable in Flask?

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(...)

How to add views to a blueprint in separate files

I have a huge application that was getting hard to update its views. To 'fix' this, I had separate the views into a few files, using blueprints. The problem is that the blueprints are also getting very big, because the long documentation that each view has and the different verifications that each view requires.
I had tried to do an import like this:
Where I have a main file that contains the Flask application (which imports the blueprint), a file that contains the blueprint and a file the imports the blueprint and configure the views in it. The problem is that with this approach the views are not rendered, because flow reasons.
The main file, in the root of a folder:
from flask import Flask
from source import test
application = Flask(__name__)
application.register_blueprint(test)
application.run()
The blueprint file, inside a subfolder in the root folder:
from flask import Blueprint
test = Blueprint('test', __name__)
The view file, inside the same subfolder as the blueprint file:
from .test import test
#test.route('/home', methods=['GET', 'POST'])
def home():
return 'home'
I had also tried to add the blueprint decorator to a declared function, this way the views are add to the blueprint in the blueprint file, but I don't think this is a good approach or a scalable approach - and it didn't work ^ - ^.
I expect to create a blueprint in a file, import the blueprint in other files and add views to the blueprint and then import the blueprint and add it to the Flask application.
You need to import the views content in blueprint file.
I have created the scenario and able to get the view. Additionally, I have updated the naming convention.
Folder structure:
.
├── app.py
└── blueprints
├── example_blueprint.py
├── example_views.py
└── __init__.py
app.py:
from flask import Flask
from blueprints.example_blueprint import bp
app = Flask(__name__)
app.register_blueprint(bp)
blueprints/example_blueprint.py:
from flask import Blueprint
bp = Blueprint('bp', __name__,
template_folder='templates')
from .example_views import *
blueprints/example_views.py:
from .example_blueprint import bp
#bp.route('/home', methods=['GET', 'POST'])
def home():
return 'home'
blueprints/__init__.py: Blank file
Output:
Running the application:
export FLASK_APP=app.py
export FLASK_ENV=development
flask run
requirements.txt:
Click==7.0
Flask==1.0.3
itsdangerous==1.1.0
Jinja2==2.10.1
MarkupSafe==1.1.1
pkg-resources==0.0.0
Werkzeug==0.15.4
Reference:
Flask documentation on Blueprints
In root folder change main file:
from flask import Flask
from source.listener import test
application = Flask(__name__)
application.register_blueprint(test)
application.run()
The blueprint file, inside a subfolder in the root folder:
listener.py
from flask import Blueprint
from source.view import home
test = Blueprint('test', __name__)
test.add_url_rule('/home', view_func=home,methods=['GET', 'POST'])
The view file, inside the same subfolder as the blueprint file:
from flask import request
def home():
if request.method == 'POST':
user = request.form['name']
return "Welcome "+user
else:
return 'home'
Get Request O/P:
Home
Post Request O/P:
Welcome username
The view module isn't discovered since only the Blueprint object is imported.
From the organization of your Blueprint and particularly the import that you have shared in your main file, I can deduct the existence of an __init__.py in the blueprint folder that exports the blueprint object.
Importing the views in that file should have the app discover the views registered in the blueprint.
i.e.
blueprint/__init__.py:
from .test import test
from . import views

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.

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