Working with Django Forms


django.forms is Django’s form-handling library.
While it is possible to process form submissions just using Django’s HttpRequest class, using the form library takes care of a number of common form-related tasks. Using it, you can:
  1. Display an HTML form with automatically generated form widgets.
  2. Check submitted data against a set of validation rules.
  3. Redisplay a form in the case of validation errors.
  4. Convert submitted form data to the relevant Python data types.

Overview

The library deals with these concepts:
Widget
A class that corresponds to an HTML form widget, e.g. <input type="text"> or <textarea>. This handles rendering of the widget as HTML.
Field
A class that is responsible for doing validation, e.g. an EmailField that makes sure its data is a valid email address.
Form
A collection of fields that knows how to validate itself and display itself as HTML.
Form Media
The CSS and JavaScript resources that are required to render a form.
The library is decoupled from the other Django components, such as the database layer, views and templates. It relies only on Django settings, a couple of django.utils helper functions and Django’s internationalization hooks (but you’re not required to be using internationalization features to use this library).

For example, consider a form used to implement “contact me” functionality on a personal Web site:
from django import forms

class ContactForm(forms.Form):
    subject = forms.CharField(max_length=100)
    message = forms.CharField()
    sender = forms.EmailField()
    cc_myself = forms.BooleanField(required=False)

Using a form in a view

The standard pattern for processing a form in a view looks like this:
from django.shortcuts import render
from django.http import HttpResponseRedirect

def contact(request):
    if request.method == 'POST': # If the form has been submitted...
        form = ContactForm(request.POST) # A form bound to the POST data
        if form.is_valid(): # All validation rules pass
            # Process the data in form.cleaned_data
            # ...
            return HttpResponseRedirect('/thanks/') # Redirect after POST
    else:
        form = ContactForm() # An unbound form

    return render(request, 'contact.html', {
        'form': form,
    })

Processing the data from a form

Once is_valid() returns True, the successfully validated form data will be in the form.cleaned_data dictionary. This data will have been converted nicely into Python types for you.
Note
You can still access the unvalidated data directly from request.POST at this point, but the validated data is better.
In the above example, cc_myself will be a boolean value. Likewise, fields such as IntegerField and FloatField convert values to a Python int and float respectively.
Read-only fields are not available in form.cleaned_data (and setting a value in a custom clean() method won't have any effect). These fields are displayed as text rather than as input elements, and thus are not posted back to the server.
Extending the earlier example, here's how the form data could be processed:
if form.is_valid():
    subject = form.cleaned_data['subject']
    message = form.cleaned_data['message']
    sender = form.cleaned_data['sender']
    cc_myself = form.cleaned_data['cc_myself']

    recipients = ['info@example.com']
    if cc_myself:
        recipients.append(sender)

    from django.core.mail import send_mail
    send_mail(subject, message, sender, recipients)
    return HttpResponseRedirect('/thanks/') # Redirect after POST

Displaying a form using a template

Forms are designed to work with the Django template language. In the above example, we passed our ContactForm instance to the template using the context variable form. Here's a simple example template:
<form action="/contact/" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
The form only outputs its own fields; it is up to you to provide the surrounding <form> tags and the submit button.

Customizing the form template

If the default generated HTML is not to your taste, you can completely customize the way a form is presented using the Django template language. Extending the above example:
<form action="/contact/" method="post">
    {{ form.non_field_errors }}
    <div class="fieldWrapper">
        {{ form.subject.errors }}
        <label for="id_subject">Email subject:</label>
        {{ form.subject }}
    </div>
    <div class="fieldWrapper">
        {{ form.message.errors }}
        <label for="id_message">Your message:</label>
        {{ form.message }}
    </div>
    <div class="fieldWrapper">
        {{ form.sender.errors }}
        <label for="id_sender">Your email address:</label>
        {{ form.sender }}
    </div>
    <div class="fieldWrapper">
        {{ form.cc_myself.errors }}
        <label for="id_cc_myself">CC yourself?</label>
        {{ form.cc_myself }}
    </div>
    <p><input type="submit" value="Send message" /></p>
</form>
Each named form-field can be output to the template using {{ form.name_of_field }}, which will produce the HTML needed to display the form widget. Using {{ form.name_of_field.errors }} displays a list of form errors, rendered as an unordered list. This might look like:
<ul class="errorlist">
    <li>Sender is required.</li>
</ul>
The list has a CSS class of errorlist to allow you to style its appearance. If you wish to further customize the display of errors you can do so by looping over them:
{% if form.subject.errors %}
    <ol>
    {% for error in form.subject.errors %}
        <li><strong>{{ error|escape }}</strong></li>
    {% endfor %}
    </ol>
{% endif %}

Looping over the form's fields

If you're using the same HTML for each of your form fields, you can reduce duplicate code by looping through each field in turn using a {% for %} loop:
<form action="/contact/" method="post">
    {% for field in form %}
        <div class="fieldWrapper">
            {{ field.errors }}
            {{ field.label_tag }}: {{ field }}
        </div>
    {% endfor %}
    <p><input type="submit" value="Send message" /></p>
</form>
Within this loop, {{ field }} is an instance of BoundFieldBoundField also has the following attributes, which can be useful in your templates:
{{ field.label }}
The label of the field, e.g. Email address.
{{ field.label_tag }}
The field's label wrapped in the appropriate HTML <label> tag, e.g. <label for="id_email">Email address</label>
{{ field.value }}
The value of the field. e.g someone@example.com
{{ field.html_name }}
The name of the field that will be used in the input element's name field. This takes the form prefix into account, if it has been set.
{{ field.help_text }}
Any help text that has been associated with the field.
{{ field.errors }}
Outputs a <ul class="errorlist"> containing any validation errors corresponding to this field. You can customize the presentation of the errors with a {% for error in field.errors %} loop. In this case, each object in the loop is a simple string containing the error message.

Comments

Popular Posts