In my last post, I complained that code like the following is redundant:
Now, without a doubt, writing that custom tag is a pain. The hard part is that I have to parse the contents of the custom tag, resolve variables, etc. It sure would be nice if the templating system did that stuff for me. I personally think it would be much nicer if I could simply create a block that takes arguments. Consider something like:
<tr class="fieldrow">What I really want is:
<th><label for="id_subject">Subject:</label></th>
<td>
{{ form.subject }}<br>
{% if form.subject.errors %}
<div class="error">{{ form.subject.errors|join:", " }}</div>
{% endif %}
</td>
</tr>
<tr class="fieldrow">
<th><label for="id_name">
Poster's Name:
</label></th>
<td>
{{ form.name }}<br>
{% if form.name.errors %}
<div class="error">{{ form.name.errors|join:", " }}</div>
{% endif %}
</td>
</tr>
<tr class="fieldrow">
<th><label for="id_email">
Poster's Email:
</label></th>
<td>
{{ form.email }}<br>
{% if form.email.errors %}
<div class="error">{{ form.email.errors|join:", " }}</div>
{% endif %}
</td>
</tr>
<tr>
<td></td>
<td>
{{ form.body }}<br>
{% if form.body.errors %}
<div class="error">{{ form.body.errors|join:", " }}</div>
{% endif %}
</td>
</tr>
{% load formutils %}This would allow me to create new forms and form fields quickly. Naturally, there could be other custom tags besides just "fieldrow" for different style layouts. This is just the beginning. Well, I started by creating a custom tag:
{% fieldrow form "subject" %}Subject:{% endfieldrow %}
{% fieldrow form "name" %}Poster's Name:{% endfieldrow %}
{% fieldrow form "email" %}Poster's Email:{% endfieldrow %}
{% fieldrow form "body" %}{% endfieldrow %}
"""These are custom tags for creating forms more easily."""This made use of a template:
from django.core import template
from django.core.template import Context, loader
register = template.Library()
@register.tag
def fieldrow(parser, token):
"""This tag creates a table row with a form field.
Example: {% fieldrow form "name" %}Name:{% endfieldrow %}
form -- This is a formfields.FormWrapper object.
name -- This is the name of the field. It may be a variable or a quoted
string. If it is a quoted string, it may not contain spaces.
Name: -- This is the label for the field.
Note, I'll use <tr class="fieldrow">.
"""
try:
tag_name, form, fieldname = token.contents.split()
except ValueError:
raise template.TemplateSyntaxError(
"%s tag requires two arguments" % token.contents[0])
nodelist = parser.parse(('endfieldrow',))
parser.delete_first_token()
return FieldRow(form, fieldname, nodelist)
class FieldRow(template.Node):
def __init__(self, form, fieldname, nodelist):
self.form = form
self.fieldname = fieldname
self.nodelist = nodelist
def render(self, context):
form = template.resolve_variable(self.form, context)
fieldname = resolve_variable_or_string(self.fieldname, context)
field = form[fieldname]
label = self.nodelist.render(context)
t = loader.get_template('bulletin/fieldrow')
c = Context({"form": form, "fieldname": fieldname,
"field": field, "label": label})
return t.render(c)
def resolve_variable_or_string(s, context):
"""s may be a variable or a quoted string.
If s is a quoted string, unquote it and return it. If s is a variable,
resolve it and return it.
"""
if not s[0] in ("'", '"'):
return template.resolve_variable(s, context)
if s[-1] == s[0]:
s = s[:-1] # Strip trailing quote, if any.
s = s[1:] # Strip starting quote.
return s
{% comment %}Viola! I'm done. It works.
This template is used by the fieldrow custom tag to create a table row with
a form field.
{% endcomment %}
<tr class="fieldrow">
<th><label for="id_{{ fieldname }}">
{{ label }}
</label></th>
<td>
{{ field }}<br>
{% if field.errors %}
<div class="error">{{ field.errors|join:", " }}</div>
{% endif %}
</td>
</tr>
Now, without a doubt, writing that custom tag is a pain. The hard part is that I have to parse the contents of the custom tag, resolve variables, etc. It sure would be nice if the templating system did that stuff for me. I personally think it would be much nicer if I could simply create a block that takes arguments. Consider something like:
{% block fieldrow(form, fieldname, field) %}Sure, a template author might not be able to write that "fieldrow" block. The template authors I work with do understand code like this. If nothing else, a programmer could create a separate template containing that block. He'd be able to avoid duplicating code with very little work. Having the templating system handle parsing arguments and resolving variables would make this task much easier than creating a custom tag.
<tr class="fieldrow">
<th><label for="id_{{ fieldname }}">
{{ yield }}
</label></th>
<td>
{{ field }}<br>
{% if field.errors %}
<div class="error">{{ field.errors|join:", " }}</div>
{% endif %}
</td>
</tr>
{% endblock %}
{% fieldrow(form, "name", form.name) %}Subject's Name:{% endfieldrow %}
Comments
Thanks Again!
I often see new Django users confused about the use of template tags versus views. This is a great illustration of how template tags can save a lot of redundant code in an elegant manner.
Thanks.