1. Skip to navigation
  2. Skip to content

NewForms Generator

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.


Discussion