Dynamic Django ModelForms

he forms module in Django is pretty amazing and it allows you to do some rather complex things with not a lot of code. [Django](http://www.djangoproject.com) forms ships with a class called, ModelForm. ModelForm, if you are not familiar, when given a model class, on an instance of a model, will generate a form instance for you complete with validation. You can, of course, specify which fields to exclude from the form and which widgets to use for each field.

Django forms ships with a class called, ModelForm. ModelForms, if you are not familiar, when given a model class, or an instance of a model, will generate a form instance for you complete with validation. You can, of course, specify which fields to exclude from the form and which widgets to use for each field. As you can imagine, as your application grows in size and complexity, the number of forms as well as the variations on each form also grows.

For example, I may have a model that I want to show a certain set of fields to admins and a certain set of fields for everyone else. Or, I may want to show different fields of a model during creation versus editing. The most common case I find myself in is, I really don't want to define a new ModelForm class every time I create a new model. Luckily, understanding python's lexical scoping, closures and a little help from Django's ContentTypes framework, we can write a factory function that generate a form class for model in your project.

You can see here that going down this path will take you down a dark and dirty path. As the size of your django project grows, you will quickly end up more forms that you can keep track of. Trying to code each situation in which certain forms are used will become a mess to maintain.

Lexical [lek]-si-kuhl -a adjective ( scope )

if a variable name's scope is a certain function, then its scope is the program text of the function definition: within that text, the variable name exists, and is bound to its variable, but outside that text, the variable name does not exist

What we really need to do is create a form class on the fly. Python implements Lexical scoping, and more importantly, closures to a degree, that makes doing something like this fairly easy. For starters, we can define a factory function that returns a new ModelForm class for our blog post example.

from django import forms
def get_post_form( ):
class _PostForm( forms.ModelForm ):
class Meta:
model = Post
return _PostForm
```
This in it self is much more useful, however it illustrates the fact that we can define on the fly and return it for use somewhere else in our application. It would only take a small amount of work from here to modify this function to generate any number of forms based on some other criteria.
#### Generic Approach
Because a class is a Python object, we can pass them to and return them from functions. Now we can take our factory function one step further be eliminating the need to hard code the Model class with the help of Django's [ContentTypes](http://docs.djangoproject.com/en/1.4/ref/contrib/contenttypes/) framework.
```python:git
from django.contrib.contenttypes.models import ContentType
from django import forms
def get_object_form( type_id, excludes=None ):
ctype = ContentType.objects.get( pk=type_id )
model_class = ctype.model_class( )
class _ObjectForm( forms.ModelForm ):
class Meta:
model = model_class
excludes = excludes
return _ObjectForm
```
Given the id of the ContentType we want to add/edit we can generate a form class suitable for use in our code. The first step is to retrieve the model class based on the content type we are dealing with via the **model_class** method on ContentType instances. After we have that, we can define a basic ModelForm passing it our model class and a simple configuration to exclude certain fields if so desired. This simple function allows you to do something like this in a django view:
```python:git
from mypackage.forms import get_object_form
from django.shortcuts import render_to_response
from django.template.context import RequestContext
from django.http import HttpResponseRedirect
def create_object(request, type_id):
form_class = get_object_form( type_id )
form = form_class(
request.POST or None,
request.FILES or None
)
if form.is_valid():
item = form.save()
return HttpResponseRedirect('/')
return render_to_response( "mytemplate.html",
{"form":form},
context_instance=RequestContext( request )
)

Although these are overly simple examples, using a function to create a class on the fly lays the foundation for doing some pretty interesting things. One could take this example one step further and use a quick for loop to filter out field names based on a simple set of criteria and set the included fields dynamically. The possibilities may not be limitless, but they are vast.

I'm a software developer and system architect working at help.com. Most of my day is spent working with Javascript & Node.js. I've also done a good deal of web and print design work in my day. I created this space to share my experiences with the world and hopefully learn something in the process.

This Space

Here you will find my ramblings and rants about web development. My focus is around JavaScript( MooTools, Sencha, NodeJS ), Python & Django, HTML & CSS. Most things here target a wide range of skill levels - from the very simple to the moderately complicated. You may also find the occasionaly personal ranting and I may stand on a soap box from time to time.