views:

70

answers:

1

Hi,

I'm working on a site that will have a bunch of pages with an indeterminate amount of "apps" on each page. Something like a calendar app, and a random picture app, or whatever, each in a neat little box. While it's possible to write a template with a bunch of if tags that include other templates, this is a bit of a hassle. I'd like to pass in some variables and have forms on some of these apps, so it would get out of hand quickly. Writing custom inclusion tags will be better than {% include x %}, but it would still be a lot of if statements and writing out every possible app for each page.

Is there any way to loop over something like inclusion tags and include only those that are relevant? Any other completely different solution that I'm missing?

What I'm trying to avoid, whether I use {% include %} or inclusion tags, is this:

{% if apps.calendar %}
{% include "calendar.html" %}
{% endif %}

{% if apps.pictures %}
{% include "pictures.html" %}
{% endif %}

This would mean we'd have to update templates any time a new app was added. What would be nice is something like:

{% for app in apps %}
{% call appropriate include or inclusion tag %}
{% endfor %}
+1  A: 

With very few exceptions we use our custom tags all over the place, so we deal with this by simply placing the following in the app/__init__.py file.

from django import template
template.add_to_builtins('content.templatetags.local_tags')
template.add_to_builtins('utils.cachetemplate')

So all of the pages have them available by default. Doesn't seem to impact performance and we use tag names that are unlikely to interfere with other stuff we might include. It's lazy but it works.

Update: OK, I think I better understand what you want. One way of doing this (although I don't really recommend it) is to put the check for the variables inside the included templates. This means you will always have the overhead of including the template, but it will make your other pages marginally less cluttered.

So instead of this in your main file:

{% if apps.calendar %}
{% include "calendar.html" %}
{% endif %}

you simply have:

{% include "calendar.html" %}

and in "calendar.html" you have:

{% if apps.calendar %}
 whatever you do for your calendar app
{% endif %}

Update 2:

The last bit of code you show could be done as follows. This takes advantage of the the fact that {% include arg %} "resolves" its argument. This means it can be a variable or a method reference that returns a usable string value that is a template name.

{% for app in apps %}
   {% include app %}  <!-- or perhaps {% include app.template %} -->
{% endfor %}

Note: Django's template code does not handle top level callables correctly. This means your context cannot pass a reference to a function and expect it to be called and it's output inserted when referenced in a template. Ie. in the example above your list of apps may not be simple functions which you wish to be called. To work as expected a function reference must be either a member of a list or dict, or it must be a method of an object that is passed. We MonkeyPatched this problem long ago because we use curried functions in cached template fragments to defer some heavy DB work. This ticket has been open for over 2 years.

Peter Rowell
Hmm... I don't think I explained my problem very well. I'll add to the main description.
CaptainThrowup
That still involves hard coding includes and will require us to update the template every time a new app/widget is added. I think what I'll end up doing is making a render_widget method for each widget and just loop through and call that.
CaptainThrowup