Pass Flask app properties to Flask blueprint with Jinja template - python

I am having bigger Flask app and I want to utilize blueprints for cleaner design. Current code looks like this:
main_app.py:
app = Flask(__name__)
# ... some config passing to app ...
app.register_blueprint(user_management, url_prefix="/user")
and user_management.py
user_management = Blueprint("user_management", __name__, static_folder="static", template_folder="templates")
#user_management.route("/register")
def register_user():
register_form = UserRegistrationForm()
return render_template('user_registration.html', title="...", form=register_form)
The problem I am currently trying to solve is that I have base Jinja template layout.html, which is used in blueprints Jinja template user_registration.html like this:
{# user_registration.html #}
{% extends "layout.html" %}
But the layout.html is using some global variables of Flask app via flask.g and also config of app. When I try to load page with user_registration.html template (/user/register), it prints out error:
jinja2.exceptions.UndefinedError: 'ac' is undefined
The ac variable is defined inside of another route in main_app.py like g.ac = "...". I managed to solve this by adding parameter to template rendering inside of register_user() method as render_template(..., ac=g.ac), but it is not really elegant way of solving this issue, because there are also some other variables like config of app (also used in layout.html), which brings another error:
jinja2.exceptions.UndefinedError: 'flask.config.Config object' has no attribute 'login'
There must be some better way of handling this issue and passing base app attributes and global variables from flask.g inside of blueprint Jinja template. I've read multiple threads but cannot find similar issue.

Related

Flask - Forms - Blueprint

In most simple cases I would make the following form in a html file like this:
<form action="/do_this" method ="POST">
and the action would carry out whatever that function did within the app.py file.
I'm now deep within a blueprint and I just want to set that form action in a simple way like the above but I can't figure out how to reference a certain .py file and then a function within it.
Any pointers?
You should use url_for().
In this case you would do:
<form action="{{ url_for('your_blueprint.do_this') }}" method="POST">
The step which is of interest for me in the blueprint setup is the final line in this example:
from flask import Flask
from yourapplication.your_blueprint import your_blueprint
app = Flask(__name__)
app.register_blueprint(your_blueprint) # hook your blueprint to your app
where Flask is instructed where to find your blueprint's functions.
Not relevant to this particular question, but something I just now learned is that if you're in the blueprint to which the action will go you can prefix the URL with . as a shortcut, e.g.:
<form action="{{ url_for('.do_this') }}" method="POST">

How to get the name of the Flask blueprint inside a template?

I have a site built with Flask. It has several sections, each with a dedicated blueprint. The templates in each blueprint extend a base template, which has the basic layout for each page, including a navigation bar with tabs for each section.
I'd like to have the base template highlight the current tab, so it needs to know which blueprint's view is handling the request.
One way I came up with to do this is to add a before_request handler to each blueprint and store the name in g:
bp = Blueprint('blog', __name__, url_prefix='/blog')
#bp.before_request
def set_bp_name():
g.section = 'blog'
But Flask must know which blueprint is handling a request (if any) and the blueprint, of course, knows its own name. Is there a way for the template to fetch the current blueprint's name that doesn't require modifying each blueprint?
Yes. Quite simply: request.blueprint.
The request object is one of a few (along with g) that Flask makes available to all templates. It contains the blueprint property, which is the name of the blueprint handling the current request.

how to set the environment variables in python flask and jinja2

i am unable to set the environment variables from jinja2
<body>
<h1>hi {{session['user']}}<h1>
{{session['user']='Mad'}}
</body>
<body>
<h1>
hi {{session['user']}}
<h1>
{{session['user']='Mad'}}
</body>
I have already declared session['user'] in python code in flask but i am unable to set it in jinja2 that is the with the following html code
As described above, you need to define your variables within your backend and pass them to your template. For example:
run.py
from flask import Flask, render_template, session
app = Flask(__name__)
app.secret_key = 'your-secret-key'
#app.route("/<user>")
def user_page(user):
session['user'] = user
return render_template("user_page.html", user=session['user'])
if __name__ == '__main__':
app.run()
user_page.html
<body>
<h1>hi {{ user }}<h1>
</body>
Doing this doesn't make sense. Jinja2 is an HTML templating system, used server-side to render a page; it is not meant to update global variable. The logic should be implemented in your python code, before calling the template. The repsonsibility is thus:
python method: does server-side logic and prepare the variables needed by Jinja2 to render the response page;
jinja2 template: simply render the page using the variables prepared in 1.
Hence, editing session content should never appear in Jinja2 (it wouldn't make any sense anyway), so move your line to the python code. If you need two different names, simply save the old session value to a new variable that you pass to jinja2 alongside the updated session.

Flask - How to properly set global values

I'm currently working on a web app where I'm using Flask and the Flask-User extension to handle authorization. At the moment I have something like this:
#application.route('/dummy')
#roles_required('Admin')
#login_required
def dummy():
in my views.py file and something like this:
{% if current_user.is_authenticated and current_user.has_roles('Admin') %}
in my templates.
Now, there's been a few times where I've changed my mind in terms of role names and then had to search through the whole project to find and replace those usages. I wanted to define something like a ADMIN_ROLE global that can be accessed inside my views.py or any templates. I'd then just change a config file whenever I wanted to change a role's name.
I tried using my config.py setting ADMIN_ROLE = "Admin" and writing #roles_required(current_app.config['ADMIN_ROLE']) but that gives me the following error:
RuntimeError: Working outside of application context.
This typically means that you attempted to use functionality that needed
to interface with the current application object in a way. To solve
this set up an application context with app.app_context(). See the
documentation for more information.
so that should be wrong. What would be the most correct way to accomplish what I'm trying?
As it says, you can only access config from within the app context. Normally, you do this:
with current_app.app_context():
admin_role = current_app.config['ADMIN_ROLE']
Because you're doing this in a decorator, I don't believe there's anyway to do that. I would suggest just using a separate module. Now, it doesn't appear that you can use python import statements in jinja, so we'll still use app.config for that. Basically we'll create a module with the desired roles and then import that into the config file and your routes file.
roles.py
ADMIN = "Admin"
USER = "User"
config.py
from roles import ADMIN, USER
ADMIN_ROLE = ADMIN
USER_ROLE = USER
routes.py
from roles import ADMIN, USER
#application.route('/dummy')
#roles_required(ADMIN)
#login_required
def dummy():
views.py
{% if current_user.is_authenticated and current_user.has_roles(app.config['ADMIN_ROLE']) %}

How to show widgets on pages in Flask?

How can I create a widget on the site, such as login forms, dynamic menu (items taken from the database), site statistics?
I know that you can render a template that will extend out of a base template. And in the base template you can create these widgets.
But I do not know how to move the logic from the base template to my code. For example, the selection data for the block. Such actions certainly can be done in the template, but it would be a poor method in my opinion.
Sorry for my bad English. If you can not understand, I'll try to rephrase.
You would use a python library called WTForms. It helps you write code for creating forms and other widgets backed by database which you can render using jinja2 templates.
class YourForm(Form):
your_field1 = TextField()
....
your_fieldn = SubmitField()
#app.route('/')
def view():
form=YourForm()
return render_template('your.html', form=form)
In your.html
<form >
{{ form.your_field1 }}
....
{{ form.your_fieldn }}
</form>
Check out this flask pattern for form validation and rendering to know more about it.
Edit: To create global variables available to all templates,there are two ways:
You can use global dict of jinja environment.
This is the code:
app.jinja_env.globals.update({'variable':1})
You can use ContextProcessor. Code:
#app.context_processor
def inject_variable():
return dict(variable=1)
Now you can access variable in any template of your app.

Categories