Django templates - Reusable fragment with flexible name - django

I have a reusable HTML fragment that I use to list items. So to list items in a view I just do:
variables = RequestContext(request, {
'items': items,
}
return render_to_response('template_in_question',variables)
and the fragment is:
{% for item in items %}
<p>Item: {{item.name}} </p>
{% endfor %}
So far so good. However, there are views where I want to use the same reusable fragment twice. For example, if I want to list the most sold items and the latest items, I need to create two copies of that reusable fragment:
The view would be like this:
variables = RequestContext(request, {
'most_sold_items': most_sold_items,
'latest_items': latest_items
}
and in the HTML would need to have two reusable HTML templates:
{% for item in most_sold_items %}
<p>Item: {{item.name}}</p>
{% endfor %}
and a second one
{% for item in latest_items %}
<p>Item: {{item.name}}</p>
{% endfor %}
So my question is: How can I use, in the same view, two or more item lists, and use a common HTML template for that? For example, in the view above pass "most_sold_items" and "latest_items" and somehow use just one HTML template to list each separately?

You can do it with the include tag. Basically, you'd end up with:
<h1>Most sold items</h1>
{% include "items.html" with items=most_sold_items only %}
<h1>Latest items</h1>
{% include "items.html" with items=latest_items only %}

Related

How to pass the context to a Django template with include?

I have started building a django project from the tutorial at https://docs.djangoproject.com/en/2.1/intro/tutorial01/
After finishing the base tutorial which creates a project with one app called "polls" I wanted to build a sort of home page that can hold many apps together. For this reason I built an app called "news" and now I'm looking at ways to compose the two apps together.
So far I'm doing so in the main 'news' template, which is called 'news/base.html' and I'm including the different apps in the code.
This is my 'news/base.html' file:
{% include 'news/index.html' %}
{% include polls_template %}
{% include 'news/footer.html' %}
The two templates 'news/index.html' and 'news/footer.html' are just html pages with no arguments, just for testing and they work fine.
The polls_template variable instead is a template variable that I create in the news.views.base function and pass to the template in the context.
This is the view snippet that does this:
def base(request):
t = loader.get_template('polls/index.html')
return render(request, 'news/base.html', {'polls_template': t})
The template is showing just fine but it shows an empty poll since there is no argument. Now my problem is that I cannot find a way to pass a context variable to this template object in order to fill it's fields.
I tried to do something like:
{% include polls_template with context=polls_context %}
But it does not work.
Ideally I would like a way to do all of that in the view because this would allow me to build the apps separately and then just use one view to gather them all and pass them to a template. Thanks in advance for any help!
Possible duplicate of Django - two views, one page (disregard the references to Ajax.) One quick note: I see what you are trying to do, but you should understand that render() is a shortcut that includes both the template loading and the HttpResponse(). You don't need to call loader() if you are using render(). Another problem with your function, you've included the template within the context dict. Please read the linked post b/c there are a number of different approaches but for the sake of completeness, here's one way to approach what you are trying to do. First, typically you'd create a 'base.html' file that would be the container for your content, it would include header, footer and possibly the messaging templates. You could then extend the base.html and include other templates.
'base.html'
<!doctype html>
<html lang="en">
<head>
{% include 'header.html' %}
<body>
{% include 'news.html' %}
{% block content %}
//to be replaced by index/polls content that extends this template//
{% endblock %}
</body>
{% include 'footer.html' %}
</html>
'index.html'
{% extends 'base.html' %}
{% block content %}
<ul>
{% for question in questions%}
<li> {{question}}</li>
{% endfor %}
</ul>
{% endblock %}
'news.html'
<ul>
{% for article in news %}
<li> {{article}}</li>
{% endfor %}
</ul>
And then your function
def index(request):
polls_questions = Question.objects.all()
newest_articles = Articles.objects.filter(post=OuterRef('pk')).order_by('-created_at')
return render(request, 'index.html', {'questions' : polls_questions, 'news': newest_articles})

Optimizing pattern of loading Django template filters in a for loop

In a Django template, I use a for loop to display content.
This content is of 3 types. Each type has its own set of conditions, HTML and CSS.
I felt including all that processing in 1 template would make it tough to maintain the file. So instead I'm using template tags:
{% load get_display_type_1 %}
{% load get_display_type_2 %}
{% load get_display_type_3 %}
{% for item in content %}
{% if item == '1' %}
{% display_type_1 payload='foo' %}
{% elif item == '2' %}
{% display_type_2 payload='bar' %}
{% elif item == '3' %}
{% display_type_3 payload='foo bar' %}
{% endif %}
{% endfor %}
And where, for example, the template tag called display_type_1 has the following code:
from django import template
register = template.Library()
#register.inclusion_tag(file_name='display_type_1.html')
def display_type_1(payload):
return {'payload':payload}
Standard stuff so far.
But now take a look at the HTML template connected to the template tag:
{% load myfilter %}
<div class="type1">{{ payload|myfilter }}</div>
I.e. notice I'm loading a custom filter called myfilter here.
So here's the problem: in essence, I've called {% load myfilter %} within a for loop. How? Because the template tag itself resides in the parent template's for loop.
This has slower performance then if I had written everything in the parent template and loaded the filter once.
I don't ideally want to abandon using template tags in this way; I find it more maintainable. But I don't want to sacrifice performance by loading filters (etc.) in a for loop either.
Can someone help me improve this pattern, making it more optimal? An illustrative example would be great.
Note: this is a simplified example, the actual code is more complex

Passing arguments into partials automatically

The problem is the header.html partial always contains categories dictionary that is kept on database. Including this partial with arguments
{% include "_partials/header.html" with categories %}
Every time on rendering partials I need to pass categories dictionary
render("index.html", {"flowers":flowers, "categories":categories})
render("details.html", {"flower":flower, "categories":categories})
...
Is there any solution, that header.html partials always contains categories dictionary.
Solved it using inclusion tags.
Created custom tag in the templatetags/tags.py file
from django import template
from flowers.models import Category
register = template.Library()
#register.inclusion_tag('_partials/nav.html')
def show_categories():
categories = Category.objects.all()
print categories
return {'categories':categories}
Created template for it in the _partials/nav.html file
<nav>
<ul>
{% for category in categories %}
<li>{{ category.name }}</li>
{% endfor %}
</ul>
</nav>
At the end, used that tag
{% load tags %}
{% show_categories %}
You should use a custom inclusion tag for this.

truncate characters in a HTML element - Django Templates

In my django templates i have a list of objects which is rendered as follows:
<li class="keys">
{% for key in job.key_list|slice:":2" %}
{% if not forloop.last %}
{{ key }},
{% else %}
{{ key }}
{% endif %}
{% endfor %}
</li>
This outputs the list as:
some_key, some_key_two
I want to truncate the number of characters to 20 but on the whole list. So that it renders something like:
some_key, some_key_t...
I am aware about the truncatechars and truncatewords filters available in django but they work on a string variable within the template.
How do i go about implementing this functionality ?
Wow, that's a strange requirement. You would have to implement this as a template tag or custom filter that takes a list and transforms it.
#register.filter(name='truncatinator')
def truncatinator(value, arg):
strings = ",".join(value)
if len(strings) >= arg:
part = strings[0:19] + "..."
return part.split(',')
and use it like this {% for object in job.key_list|truncatinator:"20" %}
What you will notice though is that you will loop over a string list here instead of your objects which will give you this disadvantage: You won't have access to your .id.
This could be modified though as well.
I would do this with Javascript instead but I don't think that Django should be responsible for this overall.

Reusing django templates?

I find django's template language very limiting. Following along with django's DRY principle, I have a template that I'd like to use in many other templates. For example a patient list:
{% for physician in physicians.all %}
{% if physician.service_patients.count %}
<div id="tabs-{{ forloop.counter }}">
{% include "hospitalists/patient_list.html" %}
</div>
{% endif %}
{% endfor %}
The problem is that the patient_list template is expecting a list of patients named patients. How can I rename physician.service_patients to patients before including the template?
Thanks,
Pete
Use the with tag:
{% for physician in physicians.all %}
{% if physician.service_patients.count %}
{% with physician.service_patients as patients %}
<div id="tabs-{{ forloop.counter }}">
{% include "hospitalists/patient_list.html" %}
</div>
{% endwith %}
{% endif %}
{% endfor %}
You might also upgrade to creating a custom tag:
{% for physician in physicians.all %}
{% if physician.service_patients.count %}
{% patient-list physician.service_patients %}
{% endif %}
{% endfor %}
Although custom tags involve writing Python code, there are shortcuts that make it easy to use an existing template file as a tag: Django Inclusion Tags
When you have "functionality" (specifically an if-condition) inside a loop, you have an opportunity to move this into the view function.
First
This construct
{% for physician in physicians.all %}
{% if physician.service_patients.count %}
{% endif %}
{% endfor %}
Is so common that you have several ways to avoid it.
Change your model. Add a patients" method and use it instead of the default query set that you get with a on-to-many relationship. This method of your model has theif service_patients.count` test, removing it from your template.
This eliminates the {% if %} from your template, reducing it to {% for %} and the actual HTML, which cannot easily the eliminated.
Change your view function. Write a few lines of code to create a list of physicians with service_patients instead of a simplistic collection of physicians. This code in your view function has the if service_patients.count test, removing it from your template.
This eliminates the {% if %} from your template, reducing it to a {% for %} and the actual HTML, which cannot easily be eliminated.
The point is to get rid of the {% if %} so that you're simply cutting and pasting the {% for %} and the actual HTML. By keeping your template to just the HTML (which cannot be eliminated), the only overhead is the {% for %}
Second
It appears that you want to reuse an {% include %} construct in slightly different contexts.
It's not at all clear what the problem with this {% include %} file is. It is "expecting a list of patients named patients" seems superficially silly. Fix it, so it expects physician.patients.
Perhaps you want to use this same list twice. Once with a list called 'patients' and once with a list called 'physician.patients'. In this case, consider (a) simplifying or (b) writing a template tag.
It appears that you have a patient list that is sometimes a stand-alone page, and other times is repeated many times on a much more complex page. Repeating a list of details embedded in some longer list is not really the best page design. Django doesn't help you with this because -- frankly -- it's not easy for people to use. Hence option (a) -- consider redesigning this "patient list within a physician" list as too complex.
However, you can always write a template tags to create really complex pages.
Summary
There's a really good reason why the Django template language has limited functionality. All of your functionality should be either an essential feature of your model, or a feature of the current application that uses the model.
Presentation is simply the translation of objects (and querysets) into HTML. Nothing more
As way, you can try to use in quality templating language jinja. It is more flexible.