Django Templates - Best Practice in Avoiding Repeating Code that Require Different For Loops? - django

I'm creating a blog and have numerous pages that will display a list of articles. So, to avoid repeating that code, I'm trying to place it within a parent template that I can extend where needed.
The problem is I'll need a different for loop to display the article lists on each page/view. I figure the easiest approach would be to simply create blocks where I want the loop to start and close within the parent, then alter accordingly within each child.
However, Django doesn't allow you to close blocks that have an open for loop, despite closing the loop later in a different block.
My initial approach, in the parent, article_list.html:
<div class="row">
{% block loop_start %}
{% endblock loop_start %}
<div class="col-xs-12 col-sm-4">
<div class="card">
<a class="img-card">
<img class="img-fluid"src="../../static/{{ post.featured_image }}" />
</a>.... etc
I know I have to fix my src code.
Extends to child as:
{% block loop_start %}
{% for post in recent_articles %}
{% endblock loop_start %}
However, that doesn't work as noted above.
I've also tried wrapping the entire code for the article list in a block, extending it and performing the following within the child:
{% for post in recent_articles %}
{% block.super article_list %}
{% endblock article_list %}
{% endfor %}
That doesn't work either. Again, producing the same error as a block is closing before the loop ends. I've also tried closing the loop in the parent which doesn't work either.
Is there an easier way of going about this that I'm missing? I could pass the same variable to each view then implement the loop in the parent, but that seems janky and limiting.
What's best practice here?

You should take a look at a 'base.html' file. Take a look at this web-page: https://ultimatedjango.com/learn-django/lessons/create-the-project-base-template/
This will allow you to do {% extends 'base.html' %} all of which Django will handle.

Related

Django nested templates

In trying to keep with DRY, I'm setting up my Django project HTML files now. I have successfully extracted most repeated information to my base.html page. On most (but not all) of my pages, my content is displayed within a general 'panel' which is basically just a container set-up with styling, but it's got a few div-tags to it so it looks a bit ugly and I'm having to type out the exact same code out several times on each page.
My idea was to extract this to a 'panel.html' then call it whenever I need it, for example some pages might just have one 'panel' whereas my dashboard (it's an administrative site) will have maybe 15+. So it seemed a better idea and cleaner to not have to type out all this code each time I need to set up a 'panel'.
My ideal page would look something like..
{% extends 'base.html' %}
{% block content %}
{% extends 'panel.html' %}
{% block panel_content %}
Panel content...
{% endblock panel_content %}
{% extends 'panel.html' %}
{% block panel_content %}
Second panel content
{% endblock panel_content %}
{% endblock content %}
I know I can't use extends multiple times but I'm using it just as an example for what it is I'm trying to achieve.
I am going to potentially have hundreds of these identical 'panels' across my site but each containing different content and it would be so much cleaner if I could just have one stored somewhere in a HTML file and call it however many times I need.
Is there a way to do this?
You can use include
{% include "panel.html" %}
I should mention that too many include statements create a performance issue.

Django templating: extend a block in same template?

So here is the set up (simplified for here):
In my template file I have two sections I would like to add content too by looping of a list of dictionaries, e.g.:
<div class="stuff-a"></div>
<div class="stuff-b"></div>
{% for thing in list %}
<!-- goes into div a -->
<!-- goes into div b -->
{% endfor %}
I thought this would work:
<div class="stuff-a">
{% block diva %}{% endblock %}
</div>
<div class="stuff-b">
{% block divb %}{% endblock %}
</div>
{% for thing in list %}
{% block diva %} thing.stuff_for_div_a {% endblock %}
{% block divb %} thing.stuff_for_div_b {% endblock %}
{% endfor %}
and then django complains about me using each block more than once - shame on me.
Now the solution to this (keeping the same schema) is to make an intermediate template file and then extend it.
My question is, is there a way to append to different specific areas in a django template without having to loop through a list twice or an intermediate template file?
This matters if the loop is somewhat complicated and you dont want to have to update two loops whenever you make a change.
When might this occur in practice (outside this example)? If you have a list where each element in a list references another list (e.g. the way bootstrap handles tabnation)
Now the solution to this (keeping the same schema) is to make an intermediate template file and then extend it.
You can't do this either because you can't use {% block %} in a loop for the same reason as you can't have a block more than once in the same template.
My question is, is there a way to append to different specific areas in a django template without having to loop through a list twice or an intermediate template file?
No you can't. And if ever you could do what you are trying to achieve, it would necessarily involve looping twice over the list behind the scenes.
This matters if the loop is somewhat complicated and you dont want to have to update two loops whenever you make a change.
It matters indeed if the loop is a generator where the next element is calculated at each iteration. But in this case you wouldn't be able to loop twice over the iterable anyway, which is necessary.
If it is a list already calculated and stored in memory, it doesn't matter.
When might this occur in practice (outside this example)? If you have a list where each element in a list references another list (e.g. the way bootstrap handles tabnation)
I don't see how this would be complicated.

Can I specify a multiline context variable/parameter when {% include %}ing a template?

I know that it is possible to set context variables when including a Django template from another template using
{% include "default_table.html" with table_header=table_header1 table_data=table_data1 %}
or
{% with "My data" as table_data %}
{% include 'default_table.html' %}
{% endwith %}
My issue with this is that both approaches don't let me define multiline variables (unless they are based on a previous multiline variable of course).
My specific usecase is this
<!-- widget.html -->
<div class="box">
<div class="title">{{ title }}</div>
<div class="title">{{ body }}</div>
</div>
and I'd like to be able to set a longer text for the body context variable. This will make is possible for me to reuse common widget HTML in various places. Can this be done?
I've been searching a bit on http://djangosnippets.org for an über {% with ... %} template tag, but haven't found any so far.
This Django snippet kinda solves my issue: http://djangosnippets.org/snippets/1860/ But I'd love to be able to set context variables instead of defining {% localblock step_ready_js %}{% endlocalblock %} in my widget HTML.

Django template inclusion

The template inheritance page on the django site doesn't really solve my problem (Django 1.2).
My base page looks like:
...
<div class="grid_12" id="content">
{% block content %}{% endblock %}
</div>
...
{% block javascript %}{% endblock %}
I have another template that defines content for these:
{% block content %}
animated sidebar
{% endblock %}
...
{% block javascript %}
alert('hello');
{% endblock %}
This is something like an animated sidebar, so I don't want to extend the base template since it's auxiliary to the main content of the page. If I just use "include", the entire thing is put where the "include" tag is placed - as a result the javascript doesn't run because it's included before one of its dependencies.
What's the best way to solve this?
EDIT
Sorry, I didn't make myself clear.
I have my content pages which render a template that extends "base.html". In "base.html" I want to include a sidebar template that needs to append blocks in "base.html". So I've tried just putting include "sidebar.html" into "base.html", but it just inserts the whole thing where the "include" tag is. What I want it to do is append the blocks in "base.html", which may themselves have been populated by "page.html".
Maybe it's important to say that "sidebar.html" is entirely static - i.e. there's no callable associated with it. So perhaps this question should really be "How can I include a static template into base.html so it will append to blocks in base.html regardless of the output of the actual view that processes the request?"
I think you mean you want to append to a block? You can put {{ block.super }} where you want the inherited content to go. e.g.:
{% block javascript %}
{{ block.super }}
alert('hello');
{% endblock %}
You should only use {% block foo %} tags to extend blocks in a base template, so I'm not clear what you mean when you say you don't want to extend it.
The code, as you've entered it, should render to
...
<div class="grid_12" id="content">
animated sidebar
</div>
...
alert(hello)
Unless you want to append the content (as in Matt's answer) it's not clear what you want to happen.
You shoud be using something like jQuery to trigger execution only after the page is fully loaded. Include jQuery library in the document header and then somewhere:
$(document).ready(function() {
//your code goes here
});

How to test for use of a django template block?

I would like to do the following:
{% if appnav %}
<hr />
<div id="appnav">
<ul class="tabs">
{% block appnav %}{% endblock %}
</ul>
</div>
{% endif %}
...however, testing for the present use of a block by templates further down the inheritance chain does not seem to work.
Is there some other conditional that might do this?
The template language doesn't provide exactly what you're looking for. Child templates can call the parent block with {{ block.super }}, but parent templates can't reference child templates.
Your best bet will probably be to write a custom template tag. There are two sections in the template manual to review.
First, Parsing until another block tag. This will give you the basics of how to parse.
Second, Parsing until another block tag and saving contents. By placing a block tag inside the custom tag, you could detect content and wrap it as appropriate. This should work, because I believe the inner block tag will be parsed first. If that doesn't work, subclass the existing block template tag provided by django to implement your special magic.
If you you are looking for an easy solution. You can hide the element as the default html.
<div id="appnav">
<ul class="tabs">
{% block appnav %}
<script>document.getElementById("appnav").style.display = "none"</script>
{% endblock %}
</ul>
</div>