Django’s newforms has a couple of very useful helper methods for generating a Form class based on a model (form_for_model) and a model instance (form_for_instance). Both of these are quite useful, but quickly I find that I want to override the default widget and/or hide certain fields that I will be handling in the POST operation, etc… In no time I reach a point to where I often realize that it’s just easier to define my own Form class for my model and make all my specification in there. You can read all about creating your own Form classes in the Django Documentation on newforms.
The problem is I’m lazy. If I have a model with a fairly large number of fields, this can be a tedious task. To counteract this, I decided to create a simple generator that I use to create the Form class for me based on the model I pass in. It can also accept just certain fields if you know that you want to limit the list of fields in your Form class. The generator is not perfect and will sometimes include defaults, leading to more verbosity than necessary, but it’s a great way to get going on custom forms.
def describe_form(model, fields=None):
"""
Returns a string describing a form based on the model
"""
opts = model._meta
field_list = []
for f in opts.fields + opts.many_to_many:
if not f.editable:
continue
if fields and not f.name in fields:
continue
formfield = f.formfield()
if not '__dict__' in dir(formfield):
continue
attrs = {}
valid_fields = ['required', 'initial', 'max_length', 'min_length',
'max_value', 'min_value', 'max_digits', 'decimal_places',
'choices', 'help_text', 'label']
for k,v in formfield.__dict__.items():
if k in valid_fields and v != None:
# ignore defaults, to minimize verbosity
if k == 'required' and v:
continue
if k == 'help_text' and not v:
continue
if k == 'widget':
attrs[k] = v.__class__
else:
attrs[k] = v
params = ', '.join(['%s=%r' % (k, v) for k, v in attrs.items()])
field_list.append(' %(field_name)s = forms.%(field_type)s(%(params)s)' % { 'field_name': f.name,
'field_type': formfield.__class__.__name__,
'params': params })
return '''
from django import newforms as forms
from models import %(object_name)s
class %(object_name)sForm(forms.Form):
%(field_list)s
''' % { 'object_name': opts.object_name, 'field_list': '\n'.join(field_list) }
The code is pretty straightforward. I generally just import it and call the describe_form method from a shell. Obviously there’s a a lot more I can do with this, such as more intelligent ignoring of defaults. In addition, I would like to wire it into django-admin.py so I can do something like:
./manange.py describe_form blog.Post
I could then have it output to the shell, or redirect it to a file.



Great work! Just like you i am lazy, this will definitely make working with django’s newform more fun fun fun!
Thanks for sharing this
Very handy generator, thanks!
You can also use formfield_callback for overriding a lot of fields but still using form_for_model:
def test_formfield_callback(field): if field.name in [‘hideme1’, ‘hideme2’]: return formfield = field.formfield() if field.name == ‘sometextfield’: formfield.widget = forms.Textarea() return formfield TestForm = forms.form_for_model(Test, formfield_callback=test_formfield_callback)
Ouch. It massacred that post. Here’s a .py file. http://tinyurl.com/2ab3vd
Nathan: you’re correct that is another way to handle it and I’ve used that on occasion. In addition you can also make the changes inline after you’ve made your call to form_for_model. For instance:
A great blog post on a lot on newforms and making these types of modifications is available at:
http://weblog.bignerdranch.com/?p=31