Proudly ProcrasDonating

Technology Thoughts

Django Tricks, Part 4 – Forms

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

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'),                       }

1 Comment »

[...] We started with automating Admin Model creation, and continued with automating initialization and installation, model mixins and forms. [...]


Your comment

HTML-Tags:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>