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?
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)
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...Assuming
field.render
is method that takes context as argument, the (technically) correct use would be: