Pass variables to all Jinja2 templates with Flask - python

I have a table in the navigation system of my webapp that will be populated with up-to-date information each time a page is rendered. How could I avoid putting the following code in each view?
def myview():
mydict = code_to_generate_dict()
return render_template('main_page.html',mydict=mydict)
mydict is used to populate the table. The table will show up on each page

You can use Flask's Context Processors to inject globals into your jinja templates
Here is an example:
#app.context_processor
def inject_dict_for_all_templates():
return dict(mydict=code_to_generate_dict())
To inject new variables automatically into the context of a template,
context processors exist in Flask. Context processors run before the
template is rendered and have the ability to inject new values into
the template context. A context processor is a function that returns a
dictionary. The keys and values of this dictionary are then merged
with the template context, for all templates in the app:

Write your own render method do keep yourself from repeating that code. Then call it whenever you need to render a template.
def render_with_dict(template):
mydict = code_to_generate_dict()
return render_template(template, mydict=mydict)
def myview():
return render_with_dict('main_page.html')

Related

How to create a global variable for render_template()?

I have a simple task: to pass the same variable for different routes in the render_template() function. This value is in the base template and I need to pass it on every render_template() function. Can I set this value as a global so that I don't have to set it in every function for different routes?
#app.route('/hello')
def hello(name=None):
return render_template('hello.html', value=value)
#app.route('/bye')
def bye(name=None):
return render_template('bye.html', value=value)
To make variables available to templates without having to burden your routes with passing those variables, use Flask context processors. See https://flask.palletsprojects.com/en/2.1.x/templating/#context-processors for details an an example.
Here's one that I use to 'cache bust' CSS so that browsers won't accidentally use stale versions.
style_css_path = os.path.join(os.path.dirname(__file__), 'static', 'style.css')
style_css_mtime = int(os.stat(style_css_path).st_mtime)
#app.context_processor
def cache_busters():
return {
'style_css_mtime': style_css_mtime,
}
The base template can then do
<link rel="stylesheet"
href="{{ url_for('static', filename='style.css') }}?v={{ style_css_mtime }}" />
Any template that uses base.html inherits this behavior without routes that use that template having to pass style_css_time.
You could use a partial from functools like this:
from functools import partial
# Define a function that takes 2 parameters
def someFunc(a,b):
print(f'Called with a:{a} and b:{b}')
# Define a "partial" where the parameters are partially pre-filled in
p1 = partial(someFunc, b="I was added for free")
# Now call the already partially defined function "p1"
p1("Hello")
Result
Called with a:Hello and b:I was added for free
I found the correct solution to my question in the Flask documentation:
Context Processors
To inject new variables automatically into the context of a template, context processors exist in Flask. Context processors run before the template is rendered and have the ability to inject new values into the template context. A context processor is a function that returns a dictionary. The keys and values of this dictionary are then merged with the template context, for all templates in the app:
#app.context_processor
def inject_user():
return dict(user=g.user)
The context processor above makes a variable called user available in the template with the value of g.user. This example is not very interesting because g is available in templates anyways, but it gives an idea how this works.

Can't use .update() function on Django context in views?

I have a function that gets some base information in my views.py file, and I'm trying to update the context of each page using it by having it return a dictionary. However, using .update() on the context dictionary in the render() function doesn't seem to work.
Here's what I'm doing:
def getBaseInfo():
allPages = list(Page.objects.all())
primaryPages = allPages[:5]
secondaryPages = allPages[5:]
return {'p':primaryPages, 'p2':secondaryPages}
def index(request):
return render(request, 'pages/index.html', {}.update(getBaseInfo()))
However, nothing is sent to my templates. Thanks in advance!
Edit: I'm using Python 2.7.11
Firstly, if you wanted to use a base dictionary and add objects to that you should do so explicitly:
def index(request):
context = getBaseInfo()
context.update({'otherkey': 'othervalue'})
# or
context['otherkey'] = 'othervalue'
return(...)
However, there is no need to do this at all. Django already provides you a way of automatically providing shared context, and that is a context processor.
In fact your getBaseInfo() function is already almost a context processor - it just needs to accept the request parameter - so you just need to add it to the context_processors list in your TEMPLATES setting. Then all your templates will automatically get the values from that function.
You should do something like this:
def index(request):
allPages = list(Page.objects.all())
primaryPages = allPages[:5]
secondaryPages = allPages[5:]
return render(request, 'pages/index.html', {'p':primaryPages, 'p2':secondaryPages})
Other option should be to make getBaseInfo a #property for reusability and DRY purposes, or make the view class based template view and define reusable code as mixin. I prefer the latter, but it's entirely matter of personal choice.

Pyramid - Dynamically assign template to view

In Pyramid, templates are statically assigned to view with
#view_config(renderer='templates/foo.pt')
def my_view(request):
return {'foo':1, 'bar':2}
However, in some cases, we want to set different template for each user. For example, user1 use templates from templates/style1/xxx.pt and user2 use templates from templates/style2/xxx.pt. Thus, we need a way to dynamically pass templates to views, instead of configure them statically.
Is there anyway to do the task elegantly?
I just did this a couple of days ago.
Here is example code for an ajax call.
#view_config(name="my_view", renderer="")
def my_view(request):
renderer = choose_renderer() #this is where you would dynamically choose what renderer you want. ex "templates/foo.pt"
data = get_my_data_for_renderer
return render_to_response(renderer, data, request)
Here is an example for a normal route call
#view_config(route_name="my_view", renderer="")
def my_view(request):
renderer = choose_renderer() #this is where you would dynamically choose what renderer you want. ex "templates/foo.pt"
data = get_my_data_for_renderer
return render_to_response(renderer, data, request)
How about something like this. I have not tested it. I'm pulling from memory :)
Add this to your init.py in your config settings.
config.add_route('my_view', 'my_view/{renderer}')
Use this view:
#view_config(route_name='my_view', renderer='')
def my_view(request):
renderer = request.matchdict['renderer']
data = get_my_data_for_renderer()
return render_to_response(renderer, data, request)

Django Variable Template Tags

<p>Hello, my name is {{ name }} ! </p>
Where/how do I set the variable: name = 'mike'? I've tried putting this in the settings.py and models.py file and it does not work.
Any info of proper code to put in a .py file would be great! I've searched through the docs page but didn't see how to set a variable for retrieval.
You need to set your variable value in the view function which normally put in view.py
e.g.
def hello(request):
return render(request, "hello.html", {"name": "mike"})
And you may would like to check https://docs.djangoproject.com/en/dev/topics/http/shortcuts/#render to find more about how to render templates with passed variables.
You need also learn more about how does Django's URL mapping works https://docs.djangoproject.com/en/dev/ref/urls/
Use context processors in django. Django handles this dilemma of making some information available to
all pages with something called context processors.
Some Code is like this,
Create a new file called context_processors.py inside your utils app directory, and add the
following code to it:
from project import settings
def context_data(request):
return {
'name': settings.name,
'request': request
}

Django Find Out if User is Authenticated in Custom Tag

I'm trying to create a custom tag. Inside this custom tag, I want to be able to have some logic that checks if the user is logged in, and then have the tag rendered accordingly. This is what I have:
def user_actions(context):
request = template.Variable('request').resolve(context)
return { 'auth': request['user'].is_athenticated() }
register.inclusion_tag('layout_elements/user_actions.html', takes_context=True)(user_actions)
When I run this, I get this error:
Caught VariableDoesNotExist while rendering: Failed lookup for key [request] in u'[{}]'
The view that renders this ends like this:
return render_to_response('start/home.html', {}, context_instance=RequestContext(request))
Why doesn't the tag get a RequestContext object instead of the Context object? How can I get the tag to receive the RequestContext instead of the Context?
EDIT:
Whether or not it's possible to get a RequestContext inside a custom tag, I'd still be interested to know the "correct" or best way to determine a user's authentication state from within the custom tag. If that's not possible, then perhaps that kind of logic belongs elsewhere? Where?
By default, an instance of django.template.Context is used in rendering templates, but you can override this in your view, either by passing a subclass of django.template.Context as the context_instance keyword argument to render_to_response, or by instantiating the subclass, and passing it directly to Template.render.
For convenience, Django provides a useful, pre-defined Context subclass: django.template.RequestContext. RequestContext looks in your settings file for a setting called TEMPLATE_CONTEXT_PROCESSORS, which should be a tuple of callable objects, each of which should return a dictionary; RequestContext will loop over each callable, call it, and add the key/value pairs from its returned dictionary to the template context as variables. Check out these links for a more detailed explanation.
To add the current user to your template context, you need django.core.context_processors.auth:
Add the django.core.context_processors.auth context
processor to your list of context
processors in settings.py:
TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.auth',
... # Other context processors follow
)
Ensure the view than renders the template which calls
your custom tag uses RequestContext:
from django.template.context import RequestContext
from django.shortcuts import render_to_response
def my_view(request):
# Do some stuff
return render_to_response('templates/view.html',
context_instance=RequestContext(request))
Using RequestContext calls all context processors defined
in settings.TEMPLATE_CONTEXT_PROCESSORS, including the
auth context processor, which adds a context variable 'user'
to your template context.
In your template tag, you can access this variable
via context['user']
#register.inclusion_tag('templates/custom/tag.html', takes_context=True)
def custom_tag(context):
user = context['user']
if user.is_authenticated():
# Some stuff for the logged-in users
else:
# Stuff for anonymous users
i dont see how your view is linked to the template tag, because from what i know its django's template system that renders the tag, so the context is a dictionary, try this, hopefully it helps
user = context['user']
if user.is_authenticated():
do stuff
The "context" of a template node is a different thing to the RequestContext. A Context is also a dictionary, so if it has a user at all it would be accessed via context['user'].
The template nodes context contains information to help it render itself within the template, after doing some reading of the docs, I cannot find any way to access the RequestContext which would be associated with the request via the django.template.Context.
Moreover django.template.RequestContext extends from django.template.Context specifically in order to handle the added request object which would contain the user. So it is no surprise that Context would not have access to it.
See the source code of Context for proof.
How to get the user
After a bit of searching I found the django.template.resolve_variable method which can be used as follows:
from django.template import resolve_variable
user = resolve_variable('user', context)
Where context is a template context.
I ended up just passing the "user" parameter to the tag and using that to decide if the user was auth'd or not.

Categories

Resources