How to render a Jinja2 macro from a view callable? - python

I have some macros defined that are called from several templates.
For example, the Product page has a Review section that uses the macros defined in 'helpers/review.jinja2' to print each review. The 'helpers/review.jinja2' file has this two macros:
{% macro render_review(request,review) -%}
{% macro render_review_comment(request,comment) -%}
When someone submits a new review, via ajax, I want to return the rendered review in order to append the content to the Review section.
Right now, I have an intermediate template 'review/review.jinja2' that looks like this:
{% import 'helpers/review.jinja2' as review_helper %}
{{ review_helper.render_review(request,review) }}
This template is rendered from the view:
#view_config(route_name='review.add_review', renderer='review/review.jinja2')
def add_review(request):
return dict(review=my_new_review)
But I hope there is a better way to do this. So, is it possible to render a macro defined in a template?
Thanks

The solution can actually be found in another one of Armin's projects - Flask implements a get_template_attribute method (see here for the source of the method). This points us at Jinja2's Template class, more specifically, the Template class' module attribute.
I don't know if Pyramid's default renderer for Jinja2 exposes that functionality for you, but it should be possible to create and register a custom renderer if the default one does not let you do something like this (entirely theoretical):
#view_config(route_name='review.add_review',
renderer='helpers/review.jinja2:render_review')
def add_review(request):
return dict(review=my_new_review)

This method was suggested elsewhere, it helped me:
#app.route("/test")
def test_view():
t = app.jinja_env.get_template('macros.html')
mod = t.make_module({'request': request})
return mod.my_macro()
Render Jinja2 macro without bothering what's in the rest of the template

Related

Slicing paginator.page_range inside a template

I have page_obj in a template which was returned from a ListView view. Now, I wanted to create links to several pages before and after the current page. Therefore, I wanted to slice page_obj.paginator.page_range this way: page_obj.paginator.page_range[page_obj.number-3:page_obj.number+4]. This works in django shell but for some reason when I did it a template, there is a Template Syntax Error, Could not parse the remainder: '[page_obj.number-3:page_obj.number+4]' from 'page_obj.paginator.page_range[page_obj.number-3:page_obj.number+4]'. Is there a workaround for this case?
P.S. I know I can do it using the whole page_obj.paginator.page_range and then using if statements to check if a page is in the required range, but I thought it's a bit inefficient.
As stated in my comment Django Template Language does not include normal python syntax. The reason for this is Django aims to separate the logic and design of the website. If there is need to perform somewhat complicated logic you either need to use template tags or filters.
For your need either an inclusion tag would work or a simple filter that would take the page_range and return a sliced version of it. A template filter here would not be very useful considering we can only pass one argument to it, meaning it would not be very customizable. Let's assume that your pagination would look very similar or perhaps you would pass the template you use to the tag.
Firstly you need to create a templatetags sub-package in your app and then in that you would add files (e.g. pagination_tags.py) which would contain your tags. The layout would be something like:
your_app/
__init__.py
models.py
templatetags/
__init__.py
pagination_tags.py
views.py
Now in your file pagination_tags.py you want to write your tags. As a reference you may read the howto on Custom template tags and filters in the documentation.
Firstly we declare register which is an instance of template.Library. After which we would write our template tags / filters. We will use an inclusion_tag:
from django import template
register = template.Library()
#register.inclusion_tag('pagination_tag.html')
def show_pagination(page_obj, **kwargs):
left = kwargs.get('left', 3)
right = kwargs.get('right', 4)
pages_iter = page_obj.paginator.page_range[page_obj.number - left:page_obj.number + right]
template = kwargs.get('template', 'default_pagination_template.html')
return {**kwargs, 'page_obj': page_obj, 'pages_iter': pages_iter, 'template': template}
Now we will have a simple template named pagination_tag.html that will simply extend the template name either passed as a keyword argument or default_pagination_template.html:
{% extends template %}
Now in default_pagination_template.html or any other template we can use all the variables in the dictionary that our function show_pagination returns:
{% for page_num in pages_iter %}
Display page links here, etc.
{% endfor %}
You can modify this implementation as per your needs. I will also leave the design and implementation of default_pagination_template.html upto you.
Now in your template where you want to use this, first we will load these tags. Then we will use them:
{% load pagination_tags %}
...
{% show_pagination page_obj left=5 right=6 template="some_custom_template.html" %}

How to access functions from ModelAdmin in template?

Lets assume a ConactAdmin for showing a ConactModel:
class ContactAdmin(admin.ModelAdmin):
change_form_template = "admin/contact_change.html"
def my_custom_fnc():
return "Test"
Now I want to access my_custom_fnc() in an view html my contact_change template like <p>{{my_custom_fnc()}}</p>
Is there a list of properties which I can access in my own template? I couldn't find it in the documentation.
The current modeladmin instance is available in the context as adminform.model_admin so this should work (nb : no parens - this is the Django template language, not Python) :
<p>{{ adminform.model_admin.my_custom_fnc }}</p>
This being said, depending on what my_custom_func is supposed to do, there might be better solutions...
NB : to find out what you have in a template context, you can use the {% debug %} template tag. Also, Django is OSS so you can just read the source code (which is what I did here).

How do I pass variables to all templates in django? [duplicate]

This question already has an answer here:
Django - How to make a variable available to all templates?
(1 answer)
Closed 6 years ago.
I am trying to pass variables (browser variable) to all my templates in my app. Any advice on how to get it to work?
View:
def browser(request):
primary_cat_list = Categories.objects.order_by("category")
subcat_list = SubCategories.objects.order_by("sub_category")
product = Productbackup.objects.order_by("website")
browser = list(chain(primary_cat_list, subcat_list, product))
return render_to_response('reserve/templates/base.html', locals(), context_instance=RequestContext(request))
Template:
{% for prod in browser %} {{ prod }}, {% endfor %}
You, my friend, are in the market for Context Processors.
From a blog entry written by a far nimbler and erudite technical writer than I:
What are template context processors?
Django’s context processors are a facility that allows you to provide data and callbacks to your templates.
You can do so in one of two ways:
On an individual request basis: by passing a custom Context value to your render_to_response() call
Globally: by creating a context processor method that accepts a HttpRequest object as input, and returns a payload or callback, then
registering the context processor in your settings.py, then providing your render_to_response() call with the built-in RequestContext attribute
instead of your own (you can always extend RequestContext to add more data on an individual request basis of course).
If that approach for passing data to templates sounded absurd and obfuscated to you, you’re not alone. The complexity involved in such a simple operation is unwarranted and counter-productive, but every system has its shortcomings.
The official documentation is here:
https://docs.djangoproject.com/en/dev/ref/templates/api/
So but yeah, I have been programming with Django for a while, and one of the reasons I really like solving problems w/ it is because it is almost Byzantine in its complexity, but not in a domineering sort of way. It has a ton of geegaws and doodads that may not immediately appear useful; each of these either comes in extremely handy when you need it, and it will stay out of your way if not.
The upshot here for you is: context processors are a fine example of those. Yes.
Currently you're passing locals() as the variable scope which should include browser aswell, but I find the use of locals() very ugly.
Personally I always prefer a pattern like this instead:
def browser(request):
context = RequestContext(request)
primary_cat_list = Categories.objects.order_by("category")
subcat_list = SubCategories.objects.order_by("sub_category")
product = Productbackup.objects.order_by("website")
browser = list(chain(primary_cat_list, subcat_list, product))
context['browser'] = browser
return render_to_response('reserve/templates/base.html', context_instance=context)
I can give you an example of my code, that works fine. Here is the file named context_processors.py:
context_processors.py
def base(request):
user = request.user
#======================
#Login form
#=====================
# here is the code for login user or check if he is logged in already
return {
'user': user,
}
and that's, part of my base.html (a template that I use wor all my pages)
base.html
{% if user.username %}
<h3>
Welcome {{ user.username }}
</h3>

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.

Automatically add a variable into context on per-application basis in Django?

I want to add a context variable in Django, so that I could define its value on per-application basis, or leave it empty.
Example:
apps/someapp/views.py:
def_context_var('app_name', 'Calendar')
templates/base.html:
{% if app_name %}You are in {{ app_name }} app.{% endif %}
....
{% if app_name %}Subsections of {{ app_name }}: ...{% endif %}
I considered the following:
Declare a variable in the app (in a view, or in URLs), and make a context processor. But I can't understang how to extract that var given the request object.
Put decorators on views. Hm, I don't like the idea: too much boilerplate or duplicated code.
#1 but nicer: make methods (like in the example above) that are executed on server restart, write the data into a dict, then a context processor somehow (how?) gets the application name and extracts the data from the dict. Where do I put the method, the dict, how does the context processor know where the view object is in?
You can call resolve(request.path) in a context processor to resolve the current url. See the django documentation on resolve for its return values, especially app_name.

Categories