Dealing with Forms: A Beginner's Look into Django Forms and Sessions

This is a considerably important aspect of web development and Django seems to be known for it.

In basic, the user interacts with the back-end application running the site through forms (mostly).   Taking user input is certainly a proverbial capability in the realm of software, so this, as with learning any other language or programming technology, is an inevitability.  That said, here's a overview of how I went about learning form and session processing -- at least, I learned the very basics, so I have a ways to go.

For the record:

  • I personally used PyDev/Eclipse to develop this.
  • I used vim throughout this tutorial.
  • I used Windows.
Enjoy!
+
Getting Started

First, start your project:

django-admin.py startproject textsite

Go to the project's install directory:

cd C:\projects\textsite

Check out the directory:

dir

Start a new application:

manage.py startapp textize

Make a template directory:

mkdir templates

Now, your directory structure should look like this:

textsite/  
    manage.py
    templates/
    textize/
        __init__.py
        models.py
        tests.py
        views.py
    textsite/
        __init__.py
        settings.py
        urls.py
        wsgi.py

Let's get started with some actual code.

Settings

Go to your /textsite/ directory and open settings.py:

vim settings.py

Edit your settings file so that the values in each area are similar to these configurations:

DATABASES = {  
    'default': {
        'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
        'NAME': 'C:\\projects\\textsite\\sqldb.db',                      # Or path to database file if using sqlite3.
        'USER': '',                      # Not used with sqlite3.
        'PASSWORD': '',                  # Not used with sqlite3.
        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
    }
}
TEMPLATE_DIRS = (  
    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
    # Always use forward slashes, even on Windows.
    # Don't forget to use absolute paths, not relative paths.
    'C:\\projects\\textsite\\templates'
)
INSTALLED_APPS = (  
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # Uncomment the next line to enable the admin:
    # 'django.contrib.admin',
    # Uncomment the next line to enable admin documentation:
    'textize',
    # 'django.contrib.admindocs',
)

In INSTALLEDAPPS, make sure 'django.contrib.sessions' is in the list because we're going to depend on that particular module to handle the form data.  Additionally, in MIDDLEWARECLASSES, there should be 'django.contrib.sessions.middleware.SessionMiddleware.'  This is just as important as contrib.sessions.

Models

We're going to create an app that does two basic things:

  1. Shows a user a form, allowing the user to submit text.
  2. Displays the text the user submitted.  It's a copycat, essentially.

A simple task, this demonstrates the use of the rudimentary forms modules while providing an introductory understanding of sessions.  Remember that.

So the model is simple.  We're just going to have one CharField (character field) displayed on an otherwise blank page.  The field will take up to 100 characters.

Open models.py, found in your /textize/ folder:

vim models.py

Replace what's there with the following code:

from django import forms

class Textizer(forms.Form):  
    to_textize = forms.CharField(max_length=100)

    def __unicode__(self):
        return self.to_textize

In the background, this will create the following HTML:

<p><label for="id_to_textize">To textize:</label> <input id="id_to_textize" type="text" name="to_textize" maxlength="100" /></p>

In the HTML template we'll create to form our index page, we'll access this field using a convenient accessor, form.as_p.  You should note that you need to create the form yourself and that Django will only create the field as it did above.   That is, you need to create the 'Submit' button and any surrounding associative form constructs.

Save your models.py file.

Go to your /templates/ folder.  Create a file named index.html:

vim index.html

Here's the code you should fill it with:

<form action="/index" method="POST"> {% csrf_token %}  
    {{ form.as_p }}
    <input type="submit" value="Submit" />
</form>

If you happen to know how to run the code you have so far and you decide to, you'll notice that nothing will really work at this point.  In order to give your code functionality, you'll need to create a corresponding 'view' for your HTML.

Views

This is the most important part of this tutorial and the program you're creating right now, so pay attention!

Go to your /textize/ folder and open views.py:

vim views.py

Replace what's in that file with the following piece of code:

from textize.models import Textizer  
from django.http import HttpResponseRedirect  
from django.shortcuts import render_to_response  
from django.core.context_processors import csrf

def index(request):  
    if request.method  'POST':
        form = Textizer(request.POST)
        print "debug 0"
        if form.is_valid():  
            print "debug 1"
            request.session['text'] = form.cleaned_data['to_textize']  
            return HttpResponseRedirect('/results') 

    else:
        form = Textizer()
        print "debug 2"

    c = {'form': form}
    c.update(csrf(request))

    return render_to_response('index.html', c)

def results(request):  
    text = request.session.get("text", None)
    c = {'text' : text}
    return render_to_response('results.html', c)

Let's take a look at the important parts here.

def index(request):

Your index page is about to process an HTTP request.

if request.method  'POST':

When the request is being processed, there will be a particular type of method by which the information is processed.  In our case, a 'POST' request is being sent, indicating that data is being POSTed to the HTTP server.  Alternatively, you'd check for a GET request if you're attempting to GET information from a server.  Since the user is submitting data, a POST is in order.

form = Textizer(request.POST)

Here, we define our form, which will be accessed once the HTTP request is received.  The form is declared using the model class we defined in models.py.   In this case, that class is Textizer().

print "debug 0"

I throw in lots of debug statements to see where things are going wrong if they go wrong.    There was a case in which these statements came in handy and I'll explain that later on in the tutorial.

if form.is_valid():

This is there to check to see if the data supplied by the user abides by the restrictions instated by the form validation modules.

request.session['text'] = form.cleaned_data['to_textize']

A few things going on here:

  • request.session - this is a Python dictionary.  Therefore, you fill it with keys and their values.   In this case, you're adding a key 'text' and setting its value to whatever is in the 'to_textize' field.  The session key identifies the data that's being stored in the session.  There's only one session and this data needs to be accessed and stored somehow, right?  Therefore, the dictionary schema is a convenient data structure for session data to be processed.  It makes accessing the data ultra-simple too.  You'll see when we're trying to access the data to print it to the page.
  • form.cleaned_data - This cleans the data for validation.  I suggest you see: Form and Field Validation.   I'm not a fan of reading documentation either, but this is actually completely useful.

Basically, we're just setting a { key : value } for the session to store the input text.  In this case, that's { 'text' : to_textize }.

    else:  
        form = Textizer()

Here, you're just saying, "if all else fails, there the input was botched, so reprint the form for the user to try again."

    c = {'form': form}  
    c.update(csrf(request))

This is a security measure.  CSRF stands for "Cross-site Request Forgery."  In the simplest terms: a person can be a mean hacker who wants to penetrate your site and do bad things with their new, unauthorized access.  To prevent that, you test for CSRF queries.   Django has that built in to make life easy for you.  Just make sure that whenever you want to check for it, you include the second line in the above segment.

But wait!  You haven't explained the first line!  What's 'c' here?

In this case, 'c' stands for 'context.'

The context in which data is processed in handled by the view in partnership with templates.  In this case, we indicate that wherever 'form' is called in the template, the 'form = Textize()' declaration should be looked to for functionality or value.

In our template, we see {{ form.asp }}.  'form' is accessed and an attribute 'asp' is used.  In this case, 'as_p' is just what makes the form display in <p></p> tags.

If a context isn't indicated in the view in the page-rendering statement, rendertoresponse(), then the page is processed in a near-raw way in that the template language is omitted.  Therefore,

<form action="/index" method="POST"> {% csrf_token %}  
    {{ form.as_p }}
    <input type="submit" value="Submit" />
</form>

if {{ form }} isn't in the context dictionary in your view, then it isn't processed and you'll see an erroneous page as a result -- usually it's one in which nothing is processed, clearly.  See: The Django Template Language:  For Python Programmers for details.

def results(request):  
    text = request.session.get("text", None)

In this area of the code, you're just setting the 'text' variable to the data from the session.  In this case, that data is stored as a key:value pair (a dictionary), and its key is 'text.'  If there is no data in the area of the session dictionary labeled 'text,' then the value of the variable you're declaring (in this case, also called 'text') is set to 'None.'  Basically: if there's data in the session under the category 'text,' pull that data; otherwise, there's no data.

That's all for views!

But there's one more thing.  Now you have to make the corresponding results.html for your results view.  Go to /templates/ and create results.html:

vim results.html

Now, place the following in there:

<b>Input text: </b>{{ text }}

Finished.  Save it and move on.

URLs

Last, but certainly not least, we have to configure the URLs that'll get this show on the road.  This is fairly rudimentary and you should understand from the beginner's tutorial.  If not, this should be fairly simple to read.

Go ahead and open up urls.py in your /textsite/ folder:

vim urls.py

Now, replace the code there with this:

from django.conf.urls import patterns, url

urlpatterns = patterns('',  
    url(r'^$', 'textize.views.index'),
    url(r'^index', 'textize.views.index'),
    url(r'^results', 'textize.views.results'),
)

Done!

Now, sync your databases (if you haven't already done do):

manage.py syncdb

Then, run your server:

manage.py runserver

Now, go to your default browser and go to:

127.0.0.1:8000

There you go!  It was that easy.

I made a mistake when I was first doing this.  I tried to post data where I wasn't supposed to in the index page, so the request wasn't picking up anything from the form.  In asking for help, I was provided a much simpler code (to my pleasure).  Maybe you'll like this approach better:

views.py

from textize.models import Textizer  
from django.http import HttpResponseRedirect  
from django.shortcuts import render_to_response  
from django.core.context_processors import csrf

def index(request):

    form = Textizer(request.POST or None)

    if request.method == 'POST':
        print "debug 0"
        if form.is_valid():
            print "debug 1"
            request.session['text'] = form.cleaned_data['to_textize']

    c = {'form': form, 'text':request.session.get("text", "")}
    c.update(csrf(request))

    return render_to_response('index.html', c)

 index.html

<form action="" method="POST"> {% csrf_token %}  
    {{ form.as_p }}
    <input type="submit" value="Submit" />
</form> 
result: {{ text }}

*Remember to take out the extra URL patterns in your urls.py file once you've implemented the above code.  All you need is the index (the first pattern).

You can find the above code on StackOverflow here.

I hope you had as much fun as I did.  I'll keep posting as I go along.

 

Greg

Software Engineer

Subscribe to GregBlogs