Dynamic template "Includes" with django - python

I am building a Django website and my side bar can have different elements for different users. So my main sidebar template has a div for every plugin to be included and the specific HTML for every one of these plugins is included in their own template file.
example:
<div id="plugins">
<div id="plugin1">
{% include 'plugin1.html' %}
</div>
<div id="plugin2">
{% include 'plugin2.html' %}
</div>
</div>
Now I want to build this list dynamically how could I do it? As the template is only parsed once so I could not send it a '{% include 'plugin1.html'}' string in the context
Any ideas?

You can use a variable inside the include tag:
{% include my_user_html %}

You can generate a variable in the view as above containing your template or you can use a template tag to generate the template path for you based on another variable, i.e. a phase. Register the following tag, customize it to your needs:
#register.filter
def get_template_phase(template_string, phase):
template_string = template_string.replace('<', '{').replace('>', '}')
return template_string.format(phase=phase)
Place the above in your templatetags and register it.
Usage:
{% include 'includes/home__<phase>.html'|get_template_phase:'nomination' %}

Related

Django TemplateSyntaxError: Error during template rendering

I am working on a Python/ Django project, having not really used either much before. I am currently getting a TemplateSyntaxError when clicking a link on one of the pages on my website.
The URL that this link takes you to (the URL for the broken page) is: costing/id/payment-report/overview & the exception value says:
Invalid block tag on line 87: 'date_to_display', expected 'elif', 'else' or 'endif'. Did you forget to register or load this tag?
The template (HTML file) itself for this page, doesn't actually have this variable anywhere in it. The location of the template in the folder structure is: costing/templates/costing/reports_post_deposit.html, however, there is another template file at costing/pdf2_base.html, which does contain this variable within the structure:
<body>
...
{% block content_payment_schedule %}
{% if not webview %}
<div>
<table>
...
<tr>
...
<td> {% date_to_display %} </td>
</tr>
</table>
</div>
{% endif %}
...
{% endblock %}
...
</body>
So I can see that the variable it's complaining about is used within this template. The page at costing/id/payment-report/overview is used to generate a PDF file from information stored in the database, and information provided by the user. The structure of this PDF file is defined in the costing/pdf2_base.html file- and date_to_display is a a variable whose value I wanted to add to the PDF generated...
What do I need to do to register or load this tag, as the error message says I need to do?
when displaying a variable in django templates you use
{{variable}}
so to fix your issue change
{% date_to_display %}
into
{{date_to_display}}
You can check how to display variables in django template in the following link;
https://docs.djangoproject.com/en/1.10/topics/templates/#variables

Extending a template thats already extended in Django

I'm trying to figure out if there is a way to extend a partial view into a view that already extends base.html.
Here is an example of what I'm trying to do:
my-template.html
{% extends 'base.html '%}
<div class="row">
<div class="col-xs-12">
<ul class="nav nav-tabs">
<li role="presentation" class="active">Tab1</li>
<li role="presentation">Tab2</li>
</ul>
</div>
</div>
<div>
{% block tab_content %}
{% endblock %}
</div>
partial1.html
{% extends 'my-template.html' %}
{% block tab_content %}
<h1>I'm partial 1</h1>
{% endblock %}
The my-template.html view has a url that is constructed like so:
url(r'^my-template/(?P<id>[0-9]+)/$', views.my_template_view, name='my-template')
in addition a context dict is passed into the my_template_view providing the id for the url.
I would like the for the user to click on a tab and for its corresponding partial to be rendered with a url like so:
url(r'^my-template/(?P<id>[0-9]+)/tab1/$', views.tab1_view, name='tab1-view')
but right now I'm getting a NoReverseMatch at /my-template/97/tab1/ which I'm assuming means that my tab1_view doesn't have access to the same context as the my_template_view and thus can't get the id to build the reverse of my url.
In template /partial1.html, error at line 0
Reverse for 'tab1_view' with arguments '('',)' and keyword arguments '{}' not found. 1 pattern(s) tried: ['/my-template/(?P<id>[0-9]+)/tab1/$']
So, is there a way for me, at the very least, to pass along the context or the id so this works, or am i going about this in the entirely wrong way?
The typical way to solve this is by using the include template tag, not by extending with a new template.
Here is the Django doc describing this.
You can even use a variable to define a dynamic template name that will be included based on logic in your view.
Little more clarification here:
You can also have the URL route direct to the same view and have the "tab" optionally passed in as a second parameter as so:
url(r'^my-template/(?P<id>[0-9]+)/(?P<tab_name>\w+)/$', views.my_template_view, name='my-template')
url(r'^my-template/(?P<id>[0-9]+)/$', views.my_template_view, name='my-template')
And your view would look something like:
def my_template_view(request, id, tab_name=None):
if not tab_name:
tab_name = "tab1"
if tab_name == "tab1":
partial = "tab1.django.html"
elif tab_name == "tab2":
partial = "tab2.django.html"
return render("my-template.html", { 'partial': partial })
And on your template you would have:
{% include partial %}
Because the included template will have the same context, you will have access to any variables that were available in the original context as well.
EDIT 2020/09/04: As noted in a comment below, the above page no longer exists. I've updated the above link to the latest in the 1.11 branch, but for the latest version of Django (3.1 as of this edit) you can refer to the same doc here.

jinja2: Can I render template tags from an expression?

How would I return text with template markup from an expression and have the tags rendered by Jinja? It looks like Jinja only makes one pass, and just escapes and dumps the text in without further processing it as part of the template (which would be the right thing 99% of the time). Is there a way to make two passes with the renderer, or render the result of my expression first and pass it to the template?
Simplified Problem
I have included further details below in case there is more to this than I think, but this should be all the information needed for the problem.
If do_render() returns <p>Hello there {{ current_user.name }}</p>, how could I do the following in a template, so that I obtain the value of name?
<div>
{{ do_render() }}
</div>
This renders as <div><p>Hello there {{ current_user.name }}</p></div>, when I want <div><p>Hello there Sam</p></div>.
Complete Problem
I'm using Flask, Flask-Bootstrap, and Flask-Nav with Python 2.7. I could just create the navigation bar myself and none of this would matter, but "autogenerated" sounded so much simpler...
Flask-Bootstrap provides a Flask-Nav compatible renderer; I have subclassed it to modify my navigation bar. I'm trying to add a logon form in the navigation bar, right-aligned. Because the BootstrapRenderer generates the complete navbar, I have to inject my form into it prior to the closing tags (alternatively, I could skip super() and do it all myself).
class MyRenderer(BootstrapRenderer):
def visit_Navbar(self, node):
""" Returns the html for a Bootstrap navigation bar. """
root = super(MyRenderer, self).visit_Navbar(node)
# Replace the navbar style with my custom css
root['class'] = 'navbar navbar-mystyle'
# Here I try injecting a login form. This is the correct position,
# and it inserts properly; it just treats {{, }}, {%, %}
# as nothing special.
elem = root[0][1] # div class="navbar navbar-collapse"
elem.add(
dominate.util.include(
os.path.join(
config.app_path_root, app.template_folder, 'inc/login_form.jinja')))
# I have also tried
# elem.add('{% block nav_right %}{% endblock %}')
# thinking I would use inheritance later (still my preference).
return root
I then register the renderer with Flask-Nav, and render it by inserting {{ nav.main_nav.render() }} into my base template, which my .html files inherit from. All of this works.
My problem is that I only want the login form when the person is not logged in.
The login_form is:
{% if not current_user.is_authenticated() %}
<form class="navbar-form navbar-right" role="search" action="login" method="post">
<div class="form-group"><input type="text" name="username" /></div>
<div class="form-group"><input type="password" name="password" /></div>
</form>
{% else %}
<div class="navbar-right">
Welcome {{ current_user.name }} | Logout
</div>
{% endif %}
My HTML output is identical to the template; neither statements, expressions, nor comments are treated as such.
Other attempts: I have generated the navbar first, then passed it to the template via render_template('index.html', navbar=navbar) but I have the same problem. I have also tried macros. I'm about ready to write my navigation menu in the base template myself and be done with it, but now it feels like that would be giving up.
Other than {% include ... %} and {% extends ... %}, you're not going to be able to have the template system automatically render something that's added to a template during runtime without a bit of customization.
The beautiful part about Jinja 2 is that its API is very powerful and you can do many things without having to feel like your "hacking" the system. To do what your first example is implying, you just need to have the function render the included template snippet snd return a rendered string. If you're expecting the template to be rendered with the context of the parent template, that's not gonna happen automatically, but that's not a problem since you can pass in whatever you need directly in your function call in the template.

Creating a "Recent Posts" list in a sidebar.

I'm working on a simple blog app in Django, and i'm having trouble figuring out how to dynamically generate the five most recent posts in a side bar. Each of my views are class based and they extend a generic template, each view maps to one template which I believe is the correct way to do it. I've looked for a way to do this using template tags, but it seems Django doesn't like you to put any logic inside of your templates.
The problem I believe is that I want this to exist within my base.html because I want the recent posts to be displayed site-wide, is a view even supposed to map to your base.html or does that cause problems, i'm pretty new with this. I don't know how to approach this, whether i'm supposed to create a new view for base.html or if I should use my template tags, or if I should extend an existing view(but if I do that it won't be site wide?).
I essentially want the following(they're ordered in reverse chronological order)
{% for post in post_list[:4] %}
{{ post.title }}
{% endfor %}
You can use a template tag. More specifically, an inclusion tag is what you need. This allows you to insert a rendered snippet anywhere inside your template via a small view-like piece of code.
For example, create a templatetags/blog_tags.py file (it's important that you create the templatetags folder within your app; Django searches for them here by default) in your blog app and add the following:
from django import template
register = template.Library()
#register.inclusion_tag('blog/snippets/recent_posts.html')
def render_recent_blogposts():
return {
# This is just an example query, your actual models may vary
'post_list': BlogPost.objects.all().order_by("published_on")[:4]
}
now create a blog/snippets/recent_posts.html template (it can be anywhere as long as it mathecs the #register.inclusion_tag(...) above.):
<ul>
{% for post in post_list %}
<li> {{ post.title }}</li>
...
{% endfor %}
</ul>
finally, in your original template, you can now render your template tags:
<aside>
{% load blog_tags %}
{% render_recent_blogposts %}
</aside>

Is it possible to have Django custom template tags load other templates in package

What I'm looking to do is package templates in a Django package which can be inserted on a developers page by simply using
{% load app_tags %}
this works find for custom methods which take a value and return a value. What I would like to do is simply have a method which returns a template packaged with the app.
{{ custom_template }}
So the question boils down to how do I have a project which installs my app load my apps' tags and call a tag method which includes a template from the app.
thank you for any responses.
yup! make an inclusion tag in app_tags.py, and then call it!
they're great for code reuse (along with Django blocks and the {% include ... %} template tag, of course)
reference: https://docs.djangoproject.com/en/dev/howto/custom-template-tags/#inclusion-tags
# app_tags.py
from django import template
register = template.Library()
#register.inclusion_tag("templates/myappname/greeting.html")
def greet(name, end="!!!"):
return { 'name': name, 'end': end }
and
{# templates/myappname/greeting.html #}
<h1> What's up {{ name }}{{ end }} </h1>
then to call this, you'd use {% and %}, the double bracket notation e.g. {{ custom_template }} is really only for showing the value of a single variable
{% load app_tags %}
{% for person in people_to_greet %}
{% greet person %}
{% end %}
<h3> cool greetings above ^ </h3>

Categories