Conditionally extending a template in Django - python

I have a download page on a Django site that I want to serve for both users who are logged in and who aren't. Instead of having a user_download.html and login_download.html, I want to have a single download.html that conditionally extends the correct base.
However, I get an error when I use the following code.
{% if user.is_authenticated %}
{% extends 'user_base.html' %}
{% else %}
{% extends 'login_base.html' %}
{% endif %}
{% block content %}
<h2>Downloadable content</h2>
...
{% endblock %}
The error I receive is
TemplateSyntaxError at /download/
Invalid block tag: 'else'
What's wrong with the else? I tried
{% if user.is_authenticated %}
{% extends 'user_base.html' %}
{% else %}{% if AnonymousUser.is_authenticated %}
{% extends 'login_base.html' %}
{% endif %}{% endif %}
{% block content %}
<h2>Downloadable content</h2>
...
{% endblock %}
but this didn't work, either.
Thanks,
erip

The {% extends %} tag supports variables. See the doc for reference.
def my_view(request):
if request.user.is_authenicated
base_template_name = 'user_base.html'
else:
base_template_name = 'login_base.html'
# Pass base template name to the renderer
return render_to_response('your_template.html', {'base_template_name':base_template_name})
Template (please note that the value is not quoted):
{% extends base_template_name %}
...

You're getting an error because extends needs to be defined at the top of the template. extends controls template inheritance: you are basically creating a subclass from some parent class, which is why extends needs to be the first thing in the template.
Imagine writing a class, and in the __init__() you said something like
class DoesntKnowWhereToInheritFrom(object):
def __init__():
if something:
self.inherits_from(x)
else
self.inherits_from(y)
The compiler/interpreter would freak out.
The common way to do what you are trying to do here is to check for is_authenticated in the view, and then render the appropriate template.

Related

How to call a Django function from template and save return in a variable

Context:
I have a piece of HTML that I want to dispaly in the template just in case a function return "true".
Details:
My function
def show_avg_kpi():
return config.avg_times_visible
register.filter('show_avg_kpi', show_avg_kpi)
Template ( is the piece of code to display or not):
{% if show_avg_kpi %}
<HTML CODE>
{% endif %}
I want something like this, but I don't know how to save the result of the show_avg_kpi function in a variable to use it with the {% if %} tags
Thank you in advance.
You can use register.simple_tag like this
#register.simple_tag
def show_avg_kpi():
return config.avg_times_visible
and in your template like this
{% show_avg_kpi as your_var %}
{% if your_var %}
your code
{% endif %}
Now show_avg_kpi will be called only once
I think you could use {% with %} tags. i.e:
{% with my_var=show_avg_kpi %}
{% if my_var=condition %}
HTML CODE
{% endif %}
{% endwith %}
But you can only use my_var inside the with statement.
Another approach is to send the variable from the view.

Nest {% block %} in {% for %} Django template inheritance

I am developing a dictionary application using Django. One of the main themes of the app is displaying feeds of definitions.
Specifically, I have:
an "index" feed where new definitions are listed,
a "headword" feed where definitions of a particular headword are listed,
and a "tag" feed where definitions tagged with a particular tag are listed.
index.html:
{% block body %}
<h1>Definitions</h1>
<ul>
{% for definition in definitions %}
// definition HTML
{% endfor %}
</ul>
{% endblock %}
headword.html:
{% block body %}
<h1>{{ headword }}</h1>
<ul>
{% for definition in headword.definitions_headword.all %}
// definition HTML
{% endfor %}
</ul>
tag.html:
{% block body %}
<h1>{{ tag }}</h1>
<ul>
{% for definition in tag.definitions_tag.all %}
// definition HTML
{% endfor %}
</ul>
Clearly, I need a base feed.html template that these three files can share. For the h1 tag no problem. However, I am having problems with the for loop iterable variables.
I tried nesting a {% block iterable %}{% endblock %} in the for loop of feed.html as in {% for definition in {% block iterable %}{% endblock %} %} and then plugging definitions, headword.definitions_headword.all, and tag.definitions_tag.all in {% block iterable %}, but Django does not seem to support that.
I could probably pass to all three templates a "definitions" context variable and use that as the iterable in all three for loops, but is there a way to do it without touching Python code?
Your explanation was a bit fuzzy to me at the end there, but going just based off your first sentence of "Clearly, I need a base feed.html template that these three files can share."
I think {% include %} may be what you are looking for.
https://docs.djangoproject.com/en/3.0/ref/templates/builtins/#include
Create your base template of "feed.html" which includes generic variables. Generic variables below are "header_name" and "definitions".
<h1>{{ header_name }}</h1>
<ul>
{% for definition in definitions %}
// definition HTML
{% endfor %}
</ul>
In your parent templates (index, headword, tag), use the "include" tag. Pass in your parent template variables into the child template. Examples below:
headword.html
{% block body %}
{% include 'pathname/feed.html' with header_name=headword definitions=headword.definitions_headword.all %}
{% endblock %}
tag.html
{% block body %}
{% include 'pathname/feed.html' with header_name=tag definitions=tag.definitions_tag.all %}
{% endblock %}

Passing variables into extends template?

I have a template named base.html. As the name suggests, it is where the header and footer reside. In between these 2 elements, is a {% block content %} where the child template can extend this template and add content inside block content.
However, inside header I want the user's name to be shown. For example, {{ user.username }} but Django can't seem to recognize this when I extend this template to a child template. Is there a way I can pass in objects to the extends template? That way the logged in user's name is shown?
This is a rough example of what I'm trying to do. user.username does not show even when the user is logged in.
base.html
<header>
<h1>Hello, {{ user.username }}</h1>
</header>
{% block content %}{% endblock %}
<footer>
///Some content
</footer>
child.html
{% extends 'base.html' %}
{% block content %}
//Some content
{% endblock %}
views.py for child.html
ChildView(TemplateView):
template_name = 'child.html'
In your child template add this to the top
{% extends 'base.html' %}
This will allow you to "inherit" the context variable.
Alternatively, if you only want to pass say just the user data to the template, you could do the following in your base.html
{% include 'header.html' with my_user=user %}
This answer summarises the differences between extend and include functionality quite well.
Edit:
In response to your comments and updated question, you aren't accessing the user object correctly. To do so you must use {{ request.user }}. This is because there is a context processor that passes the user object to every template.
As an aside, if you were to explicitly send the user from the view, you could then access the user with {{ user }} as you have done. However, this is obviously quite unnecessary.
That is because the content in blocks in child templates are overriden.
base.html
{% block my_block %}
This content is overriden by child templates
{% endblock my_block %}
child.html
{% extends 'base.html' %}
{% block my_block %}
This content is shown
{% endblock my_block %}
If you want some content shown in all templates, you shouldn't put inside a block content, but directly in your base template.
base.html
{{ user.username }}
{% block my_block %}
This content is overriden by child templates
{% endblock my_block %}
So, it all comes down to how the layout of your page is done. If the header is always the same, you should not use the block tag.
If it's almost the same, but changes in details, use blocks to change the details:
header:
<h1>This doesn't change ever
{% block this_changes %}
the child themplate will provide the content
{% endblock this_changes %}</h1>
<b>User: {{ user.username }}</b>

How to include block of code to every page except base.html

I need to create a small side block with form(it contains only one field and button) and I want it to be included to every page except base.html
I thought about making simple view function, but maybe there are better ways to do this?
I'm using Python and Django 1.6
In general, you shouldn't use base.html directly, but because you are and because it would be a huge hassle to change it in every other template, what you can do is, in the view function that returns base.html, you can add a boolean to the context and check the boolean to determine what template you are using.
Something like this:
def view_that_uses_base.html(request):
is_base = True
return render_to_response("base.html", {"is_base":is_base}, RequestContext(request,{}))
And then in the template:
{% block sidebar %}
{% if is_base%}
{% else %}
#Your code here
{% endif %}
{% endblock sidebar %}
You must use templates to do that.
In other words, try creating $DJANGO_ROOT/templates/main.html using the following code:
<html>
<head>
</head>
<body>
{% block one_field_and_a_button %}
<input />
<button>I am everywhere</button>
{% endblock %}
{% block my_custom_content %}
{% endblock %}
</body>
<html>
Then all other templates must extend that main.html template and insert their own data.
Imagine this is $DJANGO_ROOT/templates/login.html. It will only replace "my_custom_content" and will inherit all other blocks including "one_field_and_a_button"
{% extends 'templates/main.html' %}
{% block my_custom_content %}
Hello World! This is the login
{% endblock %}
Finally, if you want to have a base.html that does not have that part of the code containing one field and a button, you can do the following.
Imagine this is $DJANGO_ROOT/templates/base.html. It will replace both "one_field_and_a_button" and "my_custom_content". However, in this case, "one_field_and_a_button" will be replaced with blank space that will not show in your html code.
{% extends 'templates/main.html' %}
{% block one_field_and_a_button %} {% endblock %}
{% block my_custom_content %}
Hello World! This is my base.html template
{% endblock %}
Hope it works for you!
You can use block tag in base.html, i think you are searching foe something like this
base.html
{% block code %}
{% include 'sidebar.html' %}
{% endblock %}
index.html
{% extends base.html %}
{% block code %}
{% endblock %}
and every other templates
just extend base html
{% extends base.html %}

Django: set default behavior in include template

I'm looking to set a default behavior in a template to be included.
I have a problem with Django template system not allowing to set variables in template (I've read about the Django Philosophy, and I understand it).
Here is my example problem:
I want to include a template to render a newsfeed:
template.html:
...
{% include "_newsfeed.html" with slicing=":20" %}
...
I would like to not be forced to enter the slicing argument, and set a default behavior, let's say ":20"
In my _newsfeed.html , I would like to do (pseudo-code, it doesn't work):
_newsfeed.html:
...
{% if not slicing %}{% with slicing=":20" %}{% endif %}
{% for content in newsfeed_content|slice:slicing %}
{# Display content #}
{% endfor %}
{% if not slicing %}{% endwith %}{% endif %}
Instead, I end up doing this below, that doesn't follow the DRY rule (and doesn't satisfy me!):
_newsfeed.html:
...
{% if not slicing %}{% with slicing=":20" %}
{% for content in newsfeed_content|slice:slicing %}
{# Display content #}
{% endfor %}
{% endwith %}{% else %}
{% for content in newsfeed_content|slice:slicing %}
{# Display content #}
{% endfor %}
{% endif %}
How should I do ?
If you want to do this via your template not your views file, you could create your own filter based on slice e.g.
from django.template.defaultfilters import slice_filter
#register.filter("slice_default", is_safe=True)
def slice_filter_20(value, arg=":20"):
return slice_filter(value, arg)

Categories