views:

452

answers:

3

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

+3  A: 

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.

  1. 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 the if 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.

  2. 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

S.Lott
S.Lott, I'm not sure how this even remotely answers my question about reusing my template that is expecting an object named patients.
slypete
Can you please elaborate since I'm still in the dark?
slypete
S.Lott, thanks for the clarification. When coupled with something like jQuery UI's tabbed interface, repeating the same template in a more complex context becomes very usable. The with tag is the correct solution to my question.
slypete
Repeating the same template in a more complex context is bad for users. Creating complex tags to make a more complex UI is bad for users. Adding complexity in any form is bad for users.
S.Lott
S.Lott, I'm afraid you're mistaken. Reusing a template is essential to nearly all web developers. django documentation encourages it. Let me get this straight, you're encouraging duplicating a template and modifying it to expect different parameter names?
slypete
Different parameter names should not occur; your view function prevents this situation. If you think you need variant parameters, you're doing something too complex.
S.Lott
+1  A: 

As way, you can try to use in quality templating language jinja. It is more flexible.

Alexandr
I'm beginning to believe that django's templating engine is so limiting that it should just be thrown away as well.
slypete
+6  A: 

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

Ned Batchelder
Thank you! I kept searching for an "as" tag. I guess "with" works too.
slypete