Getting Started with NewForms
NewForms are a wonderful thing, and once you “get it” there is an “a ha” moment and you begin to appreciate the architecture behind the new forms implementation. Until then it is easy to find yourself struggling with getting basic functionality working. I do not want to duplicate the excellent documentation already provided, so instead I’ll assume you have familiarized yourself with what is there. In addition to the standard documentation, I highly recommend that you familiarize yourself with the Big Nerd Ranch’s post on Using the Django newforms Library.
This post will be a simple walkthrough on creating newforms. I will build on it in a followup post that will address customizing newforms.
Mapping Urls
When I get started on something I always like to start with defining my urls information. A lot of times this helps me think through my requirements for what I’ll be doing and how it will fall into the constructs of the rest of my application. In this case we are just going to create a simple add / edit form for a Guest Book page. Guest Book will consist of a standard entry form for people to “sign our guest book”. Along with a the standard generic list and object detail view urls we’re going to add in our add / edit view url. When we get done it should look something like this:
from guestbook.models import Entry
info_dict = {
'queryset': Entry.objects.all(),
}
urlpatterns = patterns('django.views.generic.list_detail',
(r'^(?P[\w-]+)/$', 'object_detail', dict(info_dict, slug_field='slug')),
(r'^/?$', 'object_list', info_dict),
)
urlpatterns += patterns('guestbook.views',
(r'^add/$', 'add_edit_entry'),
(r'^edit/(?P\d+)/$', 'add_edit_entry'),
)
Notice that we’re just mapping /add/
and /edit/#/
to a view method named add_edit_entry. This method will combin both our add and edit functionality for the newform.
That’s all we need to do for the urls. You can choose to make them appear however fits your application the best.
Next we need to lay out our view code. This is where the meat of newforms gets implemented.
Create the View
Most people get their first experience with newforms through the use of the form_for_model and form_for_instance helper methods on the forms class.
These methods make it really easy to get started. The form_for_model
and form_for_instance
methods are used to create unbound and bound forms respectively. Unbound forms are what you will generally use when you are creating an “add” type of form. The form itself is not bound to any existing data. The bound form is bound to existing data and is used when creating an “edit” type of form. Consider the following views:
from django.shortcuts import render_to_response, get_object_or_404
from django.http import HttpResponseRedirect
from django import newforms as forms
from models import Entry
def add_edit_entry(request, id=None):
if id is None:
EntryForm = forms.form_for_model(Entry)
else:
entry = get_object_or_404(Entry, pk=id)
EntryForm = forms.form_for_instance(entry)
if request.method == 'POST':
form = EntryForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect("/guestbook")
else:
form = EntryForm()
return render_to_response('guestbook/add_edit_question.html', {'form': form})
This is a pretty straightforward implementation of an add / edit form. You’ll see this type of code structured many different ways, but I’ve found that this format works well for me. Notice how we’re using form_for_model
when an id
is not specified, whereas we are using form_for_instance
when we have an object instance.
On thing that sometimes trips people up is the fact that form_for_model
and form_for_instance
return a Form class, not a Form instance. Its the reason we’re instantiating the EntryForm
class in the else clause.
form.is_valid()
is used to validate the contents of the form. If everything checks out the form is saved and the user is redirected back to our Guestbook listing page. If the form doesn’t validate then the user is returned back to the add/edit form.
When the form is returned this time the form object will be bound and have error information pertaining to the invalid information. The HTML output will include the validation errors near the field that has the problem. You can control how this information gets output by taking a more granular approach to form rendering.
And Now for the Template
A very basic template implementation typically will look like this:
{% extends "base.html" %}
{% block content %}
{% endblock %}
This will display our form using the as_table
specification, meaning that the form will be enclosed in an HTML table. The as_table
rendering does not include the containing table tags, so be sure to include them in your template as I’ve shown above. Or better yet investigate the as_ul
or as_p
implementations.
That’s all it takes to get started with newforms. It’s pretty simple and we’ve only touched the surface of what’s possible. The real value in newforms is the built-in validation and clean data functionality. You can read more about each of these in the newforms documentation.
I’ve got an Introduction to NewForms screencast in the works that will give me the opportunity to dive into some of these things much deeper.
Update
James Bennett just posted an excellent post on NewForms. It’s definitely a required read.