Django and ModelForm
The current SVN version of Django has a new way of creating forms for adding and editing objects in a database. The most recent released version, 0.96, had two functions to create objects that generated forms for you: form_for_model and form_for_instance. The form_for_model function would take a Model class and generate an input form for it. The form_for_instance function would take a Model class instance and do the same, but would also fill in the values specified in the database. These were pretty handy, but it was inconvenient to have two functions that did basically the same thing. So the Django team added ModelForms.
ModelForms combine the features of the two previously mentioned functions into a single ModelForm class. Let’s say that we have a Student model in our Django application and we want to create a form for it in our view. We would create a subclass of ModelForm as follows.
from django.newforms import ModelForm
class StudentForm(ModelForm):
class Meta:
model = Student
If you were to create an instance of StudentForm in a view method and run the as_table method, you would get an HTML form that contained all of the fields in the model. A sample view method is shown below that creates a new user if there isn’t posted data, or saves the student information if there was posted data.
def studentEditor(request):
# Save the newly created student using posted data
if request.method == 'POST':
form = StudentForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/students/')
# Create a new Student with no data
else:
form = StudentForm()
return render_to_response('studentEditor.html', {'form':form})
This is great, and does basically the same thing as the old form_for_model function, but it still leaves the editing portion to be done. Before ModelForms came out, you had to use the form_for_instance function to convert an existing instance to a form instance, then render the HTML form to edit the data. Now it is much easier. You simply add an ‘instance’ keyword option to the ModelForm constructor that speficies the instance to read the initial data from. Below is the same view as above with a new argument that is the primary key of a student in the database. This primary key is used to get a student instance to pass to the StudentForm constructor.
def studentEditor(request, id=None):
instance = None
if id is not None:
instance = Student.objects.get(id=id)
# Save the created or edited student using posted data
if request.method == 'POST':
form = StudentForm(request.POST, instance=instance)
if form.is_valid():
form.save()
return HttpResponseRedirect('/students/')
# Create/edit a Student with database values
else:
form = StudentForm(instance=instance)
return render_to_response('studentEditor.html', {'form':form})
This is pretty nice, but there is still a redundancy in creating the StudentForm instance. You have to do it in two different places for the create and edit cases. However, with a couple of little Python tricks using ‘and’ and ‘or’, you can simplify the above code to this:
def studentEditor(request, id=None):
form = StudentForm(request.POST or None,
instance=id and Student.objects.get(id=id))
# Save new/edited student
if request.method == 'POST' and form.is_valid():
form.save()
return HttpResponseRedirect('/students/')
return render_to_response('studentEditor.html', {'form':form})
Now that’s an idiom that would make a mother proud.
