Fork me on GitHub

Using Django's New Form Engine

The Django web framework's new form engine (not yet released, but available from the project's Subversion repository: see the Django installation page) eliminates much of the pain in creating and updating data submitted by users.

As an example I've written a Django app using a design pattern that is likely to be familiar to those coming from a PHP background.

This simple example allows the creation, updating, and deletion of contacts.

Project Setup

Start a project named 'myproject' and, within the project, an application named 'contacts'.

$ django-admin.py startproject myproject
$ cd myproject
$ python manage.py startapp contacts
$ mkdir html

Edit your project's settings.py file and make the following changes:
  1. Configure your database (sqlite3 is convenient)
  2. Add 'myproject.contacts' to your INSTALLED_APPS list
  3. Add 'html' to your TEMPLATE_DIRS list

Edit the project's urls.py file and add the following url pattern: (r'^contacts/', 'myproject.contacts.views.main').

The Model

Edit the contacts/models.py file to contain the following code:

from django.db import models
 
# Create your models here.
class Contact(models.Model):
  name  = models.CharField(maxlength=200)
  phone = models.CharField(maxlength=200)

The View

Edit the contacts/views.py file to contain the following code:
from myproject.contacts.models import Contact
from django import newforms as forms
from django.shortcuts import render_to_response
 
def main(request):
 
  # initialize variables to sent to template
  message = ''
  submit_action = 'Add'
  edit_id = ''
 
  # generate default form
  ContactForm = forms.form_for_model(Contact)
  f = ContactForm()
 
  # handle edit and delete events
  if request.method == 'GET':
    if request.has_key('edit_id'):
      # replace default form with form based on row to edit
      contact = Contact.objects.get(pk=request.GET['edit_id'])
      ContactForm = forms.form_for_instance(contact)
      f = ContactForm()
      submit_action = 'Update'
      edit_id = request.GET['edit_id']
      message = 'Editing contact ID ' + request.GET['edit_id']
 
    if request.has_key('delete_id'):
      Contact.objects.get(pk=request.GET['delete_id']).delete()
      message = 'Contact deleted.'
 
  # handle add and update events
  if request.method == 'POST':
    if request.POST['submit_action'] == 'Add':
      # attempt to do add
      add_f = ContactForm(request.POST)
      if add_f.is_valid():
        add_f.save()
        message = 'Contact added.'
      else:
        # validation failed: show submitted values in form
        f = add_f
 
    if request.POST['submit_action'] == 'Update':
      # attempt to do update
      contact = Contact.objects.get(pk=request.POST['edit_id'])
      ContactForm = forms.form_for_instance(contact)
      update_f = ContactForm(request.POST.copy())
      if update_f.is_valid():
        update_f.save()
        message = 'Contact updated.'
      else:
        # validation failed: prepare form for new update attempt
        submit_action = 'Update'
        edit_id = request.POST['edit_id']
        f = update_f
 
  # get existing contacts
  contact_list = Contact.objects.all()
 
  # return rendered HTML page
  return render_to_response(
    'contacts.html',
    { 'request': request,
      'message': message,
      'contact_list': contact_list,
      'form': f.as_table(),
      'submit_action': submit_action,
      'edit_id': edit_id
    }
  )

The Template

Last, we have to define our Django template.

Create a file at html/contacts.html that contains the following code:
<html>
<head>
  <title>Contacts</title>
</head>
 
<body>
{% if message %}
  <b>{{ message }}</ b>
  <p />
{% endif %}
 
{% if contact_list %}
<table>
  {% for contact in contact_list %}
  <tr bgcolor='{% cycle FFFFFF,EEEEEE as rowcolor %}'>
    <td>{{ contact.name }}</td>
    <td><a href='{{ request.path }}?edit_id={{ contact.id }}'>Edit</a></td>
    <td><a href='{{ request.path }}?delete_id={{ contact.id }}'>Delete</a></td>
  </tr>
  {% endfor %}
</table>
<p />
{% endif %}
 
<form action='{{ request.path }}' method='POST'>
<input type='hidden' name='edit_id' value='{{ edit_id }}'>
<table>
{{ form }}
<tr>
  <td colspan=2 align=right>
    <input type=submit name='submit_action' value='{{ submit_action }}'>
  </td>
</tr>
</table>
</form>
 
{% ifnotequal submit_action 'Add' %}
  <p />
  <a href='{{ request.path }}'>Add New Contact</a>
{% endifnotequal %}
</html>
Note the use of request.path in the template: this is similar to the use of $_SERVER['PHP_SELF'] in PHP.

The End Result

Now that we've set up the project and defined a model, view, and template we synchronize the database and run the test server with the commands below:
$ python manage.py syncdb
$ python manage.py runserver
With any luck, the application should now be accessable via http://localhost:8000/contacts.