Reusing django templates? - django

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.

Related

Handcoding forms vs django forms

Are there any performance differences between handcoding forms in django (as well as all the validations in the views.py) and using django's form library? If they are roughly the same, in which scenarios would one handcode a form over using the built-in ones?
Also, What about handcoding the HTML templates and using the django block tags, etc. to re-use certain areas?
Do you have insane, zero-tolerance performance requirements? As in: will people actually die or come to harm or you be fired if a page takes an extra few milliseconds to render?
I doubt it, so just let the framework do the lifting up to the point where you need more control over the HTML output -- that's actually far more likely a scenario than you needing to avoid executing some Python to save (at an utter guess) 15ms.
When you do need more control, that's when it's best to splice in some handmade HTML, or - even better - create an include/partial for form fields that you can reuse everywhere, to save you the time of manually writing more than you need to, but still giving you a lot more flexibility than myform.as_p etc
Here's a rough snippet I use and adapt a lot, to give me lots of control over form fields and also let me leverage the Django templating framework to save me time:
In my template:
{% for form_field in myform %}
{% include "path/to/partials/form_field_as_p.html" %}
{% endfor %}
And in that form_field_as_p.html, something like:
{% if not form_field.is_hidden %}
<p>
{% if form_field.errors %}
{% for error in form_field.errors %}
<span class="errorlist">{{error}}</span>
{% endfor %}
{% endif %}
{{ form_field.label_tag }}
{% if form_field.field.required %}
<span class="required">*</span>
{% endif %}
{{ form_field }}
{% if form_field.help_text %}
<span class="form-help-text">{{ form_field.help_text }}</span>
{% endif %}
</p>
{% else %}
<div>{{ form_field }}</div> {# hidden field #}
{% endif %}

Do you know a custom tag to increment a value in a Django template?

One very annoying thing with strict MVC is that you can make the smallest processing in template. While it's usually a good practice, in this case it gets in the way:
I make a for loop on a queryset of a lot of objects and display some attributes;
Attributes are properties, making heavy processing (I don't want to trigger them twice).
At the end I need to display a total of the value of these attributes.
I now I have to do the sum once in the view, then reloop (and call the properties again) to display. I can improve that by loopring once in the view and caching the results, but then I get more code just to bypass this limitations, and I use more memory.
At this stage of the project, it's not THAT a big deal. Performance is not the issue yet at all. But I really like to know is there is a solution for this.
My best bet is to write a template tag that does the job, but it's such a chore :-) Do you know somebody that would have done that already?
E.G:
Ideal, it sould be something like:
{% for object in object_list %}
- {% sum_in_and_print object.property total %}
{% endfor %}
total {{ total }}
that would display:
- 2
- 3
- 1
total 6
I don't see how it's a chore. Check if the destination variable is in the context, and if not, initialize it with an empty-constructed object of the class of the first element in the list, then accumulate.
As an alternative to calculating the sum in a view or template, what about QuerySet.Annotate(total_x=Sum('x')), to have the database take care of the calculations?
Aside: I believe trying to solve the actual problem sometimes warrants not answering the direct question when there is not an obvious answer.
I'ts quite hard tag to write nicely. You will have to take care if evaluation order is ok (sum may be displayed above data). That you can have many sumup sections (maybe data too). You will need probably following structure:
{% raport %}
{% sumup %}
...
{% endsumup %}
{% data %}
...
{% enddata %}
{% sumup %}
...
{% endsumup %}
{% endraport %}
EDIT:
Also you will have hard times, to parse multilevel structure:
{% raport %}
{% sumup %}
...
{% endsumup %}
{% data %}
{% raport %}
{% sumup %}
...
{% endsumup %}
{% data %}
...
{% enddata %}
{% sumup %}
...
{% endsumup %}
{% endraport %}
{% enddata %}
{% sumup %}
...
{% endsumup %}
{% endraport %}
I don't know what project your're developing, but if you already require users to have JavaScript, you may check jQuery.calculation plugin. This is a great tool to shift all dumb calculations to the client side.
Look into a different templating system like Mako. Mako especially allows full python code to run within the template.
Is there a compelling reason you're not doing this in the view?
What could be easier than:
total = sum([o.property for o in object_list])
Even if your goal is to use generics, you can always wrap it in a view function.
Let me add that I think the functionality you specifically want: to output a variable and add it to some arbitrary variable -- is such a unique one that absolutely you'll want to write your own template tag for it.
Fortunately, this is very easy.
Update
As I said in the question the property
trigger HEAVY processing. I don't want
to trigger them twice...
So you could try this instead:
rendered_object_list = []
for o in object_list:
rendered_object_list.append({
'field1': o.field1,
'field2': o.field2,
'property': o.property
})
total = sum(o['property'] for o in rendered_object_list)
You process it once, in the view, then when you output it in the template, it's a dictionary, with everything in the dictionary already processed.

Django - Is there a way to create a variable in the template?

I want to do this:
{% for egg in eggs %}
<p>{{ egg.spam }}</p>
{% if egg.is_cool %}
{% myvariable = egg %} // Possible in any way?
{% endif %}
{% endfor %}
Pardon the JavaScript-style comment (it shows up as a comment on SO)
I think the closest you'll get is the with tag: http://docs.djangoproject.com/en/dev/ref/templates/builtins/#with.
If you're say trying to feature an item in a template, I can imagine doing something like:
<div class="special">
{% with some_list.first as special_item %}
{{ specialitem }}
{% endwith %}
</div>
<div class="everything">
{% for item in some_list %}
{{ item }}
{% endfor %}
</div>
If you want some special logic to determine which one is the special item, I'd add a method to the object (so you end up with: {% with some_collection.my_method as special_item %} above) or determine the special item before passing it to the view. Hope that helps.
Welcome to Django Templates.
This problem is easily solved with one of the earliest snippets posted to DjangoSnippets.com: the Expr tag.
People can argue all day about the separation of logic from the templates, but that ignores that there is business logic, which belongs in the models or views, and presentation logic which belongs only in the templates. If you have a lot of presentation logic you may want to consider using Jinja2 for some or all of your templates. WARNING: although Jinja2 looks a lot like Django's template language, there are incompatibilities with things like Custom Template Tags.
Yes, you can use the with construct:
{% with myvariable as egg %}
do stuf
{% endwith %}
I think it's probably best to do this kind of test-and-set behaviour in the view, not the template. If anything, it'll give you better control over cacheing if/when you need it.

Why were the original authors of Django against include tags?

In this excellent Google Tech Talk by Jacob Kaplan-Moss, Jacob says that they added support for the include template tag despite previous dogmatic objections, and says that people shouldn't use it.
Does anyone know why? A quick search didn't show anything that would explain why. There's nothing relevant in the now-fixed ticket where support for an include tag was added. I use include tags liberally to avoid repeating myself, which seems like a good idea, but maybe I'm missing some core reason why those who know think it's bad.
I suppose he wants to encourage template reuse by inheritance (using extends) rather than by composition. Perhaps the implication is that if you can't organise your templates this way, the dogmatic opinion is that your site is organised poorly. (For example, if you are reusing a navigation menu, shouldn't it always be in the same place in the page structure? Why should each individual page decide where to put it?)
By the way, using include doesn't do much to help you stay DRY, because any context that the included template requires must be passed from all the views that use it.
By contrast, using a custom inclusion template tag allows you to execute arbitrary Python code at the point where the tag is included, rather than in the view (or by shoving it into a model just to make it easier to access in the template).
As a trivial example, I wanted to show a list of users' avatars. Using include, it looks like this:
{% for user in users %}
{% with user.gravatar_url as avatar_url %}
{% include "foo/bar/avatar.html" %}
{% endwith %}
{% endfor %}
With a custom tag:
{% for user in users %}
{% gravatar user.email %}
{% endfor %}
Using the custom inclusion tag meant that the Gravatar hashing logic no longer had to be a concern of the User model, nor of the view function.
This said, I think are are some situations where you inevitably have similar data in the context of multiple templates, you don't need to do anything fancy with it, you just want to display some of its attributes so you don't want to write a function to make it work.
For example, I wrote a blog application (who hasn't?) which had two types of archive view: a basic sequential, X-posts-per-page one, and a monthly archive view. Both templates obviously had a list of posts in their context, both used exactly the same summary template fragment to show a title and excerpt from each post, but each template presented them in a slightly different context. So I used:
{# in archive_index.html #}
{% extends "base.html" %}
{# some stuff specific to sequential archives here #}
{% for post in posts %}
{% include "post_summary.html" %}
{% endfor %}
{# probably more stuff specific to sequential archives #}
And...
{# in archive_monthly.html #}
{% extends "base.html" %}
{# some stuff specific to monthly archives here #}
{% for post in posts %}
{% include "post_summary.html" %}
{% endfor %}
{# probably more stuff specific to monthly archives #}
It really seems that in this case, composition makes more sense than inheritance. In fact it was difficult at first to imagine how inheritance would work here at all. Well, it's still possible:
{# in base_archive.html #}
{% extends "base.html" %}
{% block archive_header %}{% endblock %}
{% for post in posts %}
{% include "post_summary.html" %}
{% endfor %}
{% block archive_pagination %}{% endblock %}
Now, the two different archives extend this and just inject their unique stuff into the blocks:
{# in archive_monthly.html #}
{% extends "base_archive.html" %}
{% block archive_header %}
<h1>Archive for {{ month }}</h1>
{% endblock %}
{% block archive_pagination %}
{# previous/next month links here #}
{% endblock %}
I'll leave imagining what archive_index.html looks like as an exercise for the (no doubt bored) reader.
Phew! It feels smart to have come up with a way of doing the same thing using both composition and inheritance, but is the latter just bending over backwards to conform to the dogma that Jacob Kaplan-Moss mentioned?
Having just watched the video (yes, all 1 hour 5 minutes of it, just so I could finish answering this question), I don't think Jacob would be enormously bothered. It sounded like an off-the-cuff comment, maybe a reference to which technique you ought to consider first.

Django templates - Regrouping by a string parameter

I have the following code in one of my Django templates that I want to refactor:
{% ifequal sort_type "set" %}
{% regroup cards by set as grouped %}
{% endifequal %}
{% ifequal sort_type "rarity" %}
{% regroup cards by rarity as grouped %}
{% endifequal %}
It does work, but it's really ugly and I want to make it more like this:
{% regroup cards by sort_type as groupedcards %}
But this doesn't work (it just puts them all in a single group called None.) From the documentation, I think it might be trying a dictionary lookup (i.e., calling card["set"] instead of card.set).
Is there a good way to do this in the template, or should I move the regrouping out into the Python code using itertools?
Ticked in Django bugtracker related to this problem.