We love Django here at Isotoma, and we love using Django’s awesome form classes to generate self-generating, self-validating, [X]HTML forms.
However, in practically every new Django project I find myself doing the same thing over and over again (and I know others do too): breaking the display of a Django form instance up into individual fields, with appropriate mark-up wrappers.
Effectively I keep recreating the output of BaseForm.as_p/as_ul/as_table
with template tags and mark-up.
For example, outputting a login form, rather than doing:
{{ form.as_p }}
We would do:
<p> {% if form.username.errors %} {% for error in form.username.errors %} {{ error }} {% endfor %} {% endif %} {{ form.username.label }} {{ form.username }} </p> <p> {% if form.password.errors %} {% for error in form.password.errors %} {{ error }} {% endfor %} {% endif %} {{ form.password.label }} {{ form.password }} </p>
Why would you want to do this? There are several reasons, but generally it’s to apply custom mark-up to a particular element (notice I said mark-up, not styling, that can be done with the generated field IDs), as well as completely customising the output of the form (using <div>
‘s instead etc.), and also because some designers tend to prefer this way of looking at a template.
“But”, you might say, “Django already creates all this for us with the handy as_p/as_ul/as_table
methods, can you just take the ouput from that?”
Well, yes, in fact on a project a couple of weeks ago that’s exactly what I did, outputting as_p
in a template, and then editing the source chucked out in a browser.
Which gave me the idea to create a simple little tool to do this for me, but with the Django template tags for dynamically outputting the field labels and fields themselves.
I created django-form-scaffold to do just this, and now I can do this from a Python shell:
>>> from dfs import scaffold >>> from MyProject.MyApp.forms import MyForm >>> form = MyForm() >>> # We can pass either an instance of our form class >>> # or the class itself, but better to pass an instance. >>> print scaffold.as_p(form) {% if form.email.errors %}{% for error in form.email.errors %} {{ error }}{% endfor %}{% endif %} <p>{{ form.email.label }} {{ form.email }}</p> {% if form.password1.errors %}{% for error in form.password1.errors %} {{ error }}{% endfor %}{% endif %} <p>l{{ form.password1.label }} {{ form.password1 }}</p> {% if form.password2.errors %}{% for error in form.password2.errors %} {{ error }}{% endfor %}{% endif %} <p>{{ form.password2.label }} {{ form.password2 }}</p>
Copy and paste this into a template, tweak, and Robert’s your mother’s brother.
As well as as_p()
, the dfs.scaffold
module also has the equivalent functions as_ul()
, as_table
, and an extra as_div()
function.
This is something that I’ve had pain points with fairly recently. Having something generate code for you that you copy and paste sounds a little bit wrong to me.
Would it not be better to extend Form and solve it this way? Currently form uses __iter__ to return instances of BoundField for each form – this basically ties together the form element and the label etc. Then simple extend BoundField to add as_p etc/ and return that from the new base form class.
After that you extend your new base form class. This would mean you could simple do
{{ form.field_name.as_p }}
Also, you probably want to use {{ form.field_name.label_tag }} rather than label, otherwise you just get the text and not the HTML label.
Dougal:
I would normally agree, and indeed I’ve created quite a few custom fields and renderers for just this before, however this is only for those times when you want to change mark-up, probably for only specific fields, and I prefer to change mark-up within templates rather than Python code.
Each to their own, but also as I said this can also be very helpful for designers who work with forms in templates.
Cheers for pointing out I’m using .label rather than .label_tag, will patch.