This post is the fourth in a series on Django tricks.
We started with automating Admin Model creation. This post describes how we automate the creation of form classes for models.

Automatic Model Form creation
The point of automatic forms is to reduce the effort that goes into writing views. Form classes for models should be created on the fly as we need them, with the same flexibility as if we had defined the form class directly.
Here’s an example that retrieves a form for the public fields of the Recipient model:
from lib.forms import get_form, EDIT_TYPE @login_required def edit_public_information(request): recipient = request.user.get_profile().recipient FormKlass = get_form(Recipient, EDIT_TYPE, includes=('name', 'category', 'mission', 'description', 'url')) if request.POST: form = FormKlass(request.POST, instance=recipient) if form.is_valid(): form.save()
The get_form utility function takes the Model class as a required parameter. If that is the only parameter, it will return the default ModelForm for that model.
The includes and excludes fields normally specified in the ModelForm’s inner Meta class can be provided as parameters to get_form().
The forms module also specifies three ModelForm types, NEW_TYPE, EDIT_TYPE and ADMIN_TYPE. Originally, the includes and excludes were defined per type per model in the forms module. This is useful if there are multiple places in the web app where models are edited, and you don’t want to keep remembering which immutable or signal-computed fields to exclude.
Note: If we go back to using the NEW_, EDIT_ and ADMIN_TYPEs we would move their definitions to inside models themselves rather than editing a single dictionary in the forms module. Live, learn and move on.
The forms module is appended below.
Proudly ProcrasDonating,
Lucy.
from django.forms import ModelForm """ We want to create three kinds of forms: 1. Admin<Model>Form admin forms. these have all fields and are used for both creation and editing 2. New<Model>Form user new forms. these exclude hidden or contextually obvious fields (eg state, a review's node) 3. Edit<Model>Form user edit forms. these exclude hidden fields """ # shows all fields ADMIN_TYPE = 'admin' # user facing form for creating new instance NEW_TYPE = 'new' # user facing form for editing existing instance EDIT_TYPE = 'edit' # users should not reference this cache directly. # instead, use get_form FORMS = { ADMIN_TYPE: {}, NEW_TYPE: {}, EDIT_TYPE: {}, } def get_form(model, type=ADMIN_TYPE, excludes=None, includes=None): """ @param excludes: in addition to type-based excludes. for on-the-fly forms. @param includes: for on-the-fly forms. return form class for model and type """ if model in FORMS[type] and not excludes and not includes: # don't cache on-the-fly forms form = FORMS[type][model] else: mname = model.__name__ excludes = excludes or () if type == ADMIN_TYPE: exec('Admin%sForm = model_form_class(model, excludes, includes)' % mname ) form = locals()['%sForm' % mname] elif type == NEW_TYPE: if mname in new_forms_excludes: excludes += new_forms_excludes[mname] exec('New%sForm = model_form_class(model, excludes, includes)' % mname ) form = locals()['New%sForm' % mname] elif type == EDIT_TYPE: if mname in edit_forms_excludes: excludes += edit_forms_excludes[mname] exec('Edit%sForm = model_form_class(model, excludes, includes)' % mname ) form = locals()['Edit%sForm' % mname] else: raise "Type no good" if not excludes and not includes: # don't cache on-the-fly forms FORMS[type][model] = form return form def model_form_class(_model, excludes=None, includes=None): class klass(ModelForm): class Meta: model = _model if excludes: exclude = excludes if includes: fields = includes return klass new_forms_excludes = {#'OldModel':('url',), #'OldModel2':('old_model','foo','bar'), } edit_forms_excludes = { #'MyModel':('slug','immutable','calculated_from_post_save'), }







