Remembering that Django template code is not quite your Django Python code
Recently I was doing some work with our Django web application where I wanted to do something special
for one particular field of a form. As is usual in Django forms,
we iterate through the form fields with the '{% for %}
' template
tag.
Stripped down, this is something like:
{% for field in form %} {{ field.label_tag }} <br> {{ field }} {% endfor %}
I had to make one field generate extra HTML, which meant that I had
to recognize it somehow. At first I used a '{% if %}
' on the
label, but that felt wrong; the label is text that's shown to the
user and we might want to change it. So I did some Internet searches
and found 'field.name
' (perhaps from here),
which is the actual name of each field. I put it in, everything worked,
I went on, and then a few days later I was struck by a Python question,
namely how did the form field know its name?
Form fields are defined as what looks like class attributes:
class RequestForm(forms.Form): login = forms.CharField([...]) [...]
However, Python objects don't know their names (for good reasons), including attributes on classes or class
instances. At first I thought that Django was using metaclass magic
so that RequestForm.login.name
was set up when the RequestForm
class was defined, but form classes turn out to be more magic
than that and once you extract the relevant
objects, they don't have .name
attributes.
At this point I realized (and remembered) that the pseudo-Python
code you write in Django templates is not the same as the Python
code you write in your app. You can't assume that what works for
one of them (here, referring to 'field.name
') will work for the
other, and it goes both ways. It's possible to rip the covers off
the magic and find an explanation for what Django is doing, but it
will lead you several levels deep and your Django template code is
still not the same as what you'll write in your Python view code.
PS: One bit of magic that is showing already in my example is that
field.label_tag
is a method, not an attribute. The Django
template language automatically calls it and uses the result.
Sidebar: What is going on here
The 'field
' template variable being used here winds up holding a
real Python object, but it is not from the class you think it is.
As covered in Looping over the form's fields,
the 'field
' object is a BoundField
instance. The actual Field
instance, what your view code deals with, is hiding in field.field
.
BoundField
does have a .name
attribute, which is set up when
it's initialized by the overall form code. The overall form code does
know the name of each field (and has to).
(Somewhat to my surprise, .field
is a plain ordinary instance
attribute, not any sort of property or fancy thing. You can see
it being set up in boundfield.py.)
|
|