Django - Templatetags, include, and class hierachy

426 views Asked by At

Im searching for an elegant solution for my template design in django. I try to make a minimalistic example:

On a page are different functional areas, for example a news field and a poll, represented by own apps in django. The number of this areas are quite different, so there can be for example three polls and two news fields, in a specific order (not sorted by type).
So here's an example implementation, how i would solve this:

# in main app:
class Field(models.Model):
...

# in polls app:
class Poll(main.models.Field):
...

And for different kind of Polls:

class PrivatePoll(Poll):
...

The template system can then iterate over objects of type Field.

{% for field in fields %}
    {% show_field field %}
{% endfor %}

But my problem is, that every subclass of Field or subclass of Poll need their own way to display the content. I know of the templatetag inclusion function in Django, but the templatetag then have to differentiate between the subclasses and how to render it... I would like to see the "template code" of each app inside the app itself, but i dont get a nice solution.

What about the include tag since Django 1.7, which can represent a method with render()? So that all of the classes and subclasses have a render() method? I didnt understand how to use this new functionality.

Any ideas how i can solve this? The Models dont have to be subclasses, maybe its better to have OneToOneRelationships or something else?

edit:
Can i use the include tag like this? Is this a good django style?

{% for field in fields %}
    {% include field.render %}
{% endfor %}

edit2:
I wrote my own Template Tag like discribed in the documentation. But is this the correct way to do this?

from django import template
from django.template.loader import render_to_string

register = template.Library()

@register.tag
def display_object(parser, token):
    try:
        tag_name, tile_object, template_to_render = token.split_contents()
    except ValueError:
        raise template.TemplateSyntaxError("%r tag requires exactly two arguments" % token.contents.split()[0])
    return MyNode(tile_object, template_to_render)


class MyNode(template.Node):
    def __init__(self, tile_object, template_to_render):
        self.tile_object = template.Variable(tile_object)
        self.template_to_render = template_to_render

    def render(self, context):
        try:
            tile_object = self.tile_object.resolve(context)
            return render_to_string(self.template_to_render, tile_object)
        except template.VariableDoesNotExist:
            return ''

Now theoretically i can use it in my template:

{% for field in fields %}
    {% display_object field field.template_to_use %}
{% endfor %}

But it feels not correct, especially display_object(..), where the variables are passed by strings..!? And doesn't clash this string if i call display_object with field multiple times, because it's always the same string "field" that is passed..!?

All i want is to render templates specified from the object instance, is such a difficult way the only way to solve this? Or should i rebuild my hierarchy on a different way?

edit3:
Okay after a lot more research, this is exactly what the include tag does (source code). I don't understand why so much people (in forums/on stackoverflow) say that 'include' should not be used...

So finally this will be my solution:

{% for field in fields %}
    {% include field.template_to_render with context=field.context %}
{% endfor %}

Any suggestions?

1

There are 1 answers

4
bruno desthuilliers On

What about the include tag since Django 1.7, which can represent a method with render()? So that all of the classes and subclasses have a render() method?

From a theoretical POV that would be a violation of the layer's responsabilities, as models are for domain logic, not presentation. Unless the "domain logic" for your models is only (or mostly) about presentation, of course (think cms pages or such)

But my problem is, that every subclass of Field or subclass of Poll need their own way to display the content.

Which means one template for each subclass. The simplest solution imho would be to specify the template path as a class attribute of the models, then rewrite your show_field template as to use this template for rendering...

Can i use the include tag like this? Is this a good django style?

{% for field in fields %} {% include field.render %} {% endfor %}

Assuming field.render is method that takes context as argument, the (technically) correct use would be:

{% for field in fields %}
   {% include field %} 
{% endfor %}