Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save dubeyji10/425f6e67e5468bb08510daacc9836509 to your computer and use it in GitHub Desktop.
Save dubeyji10/425f6e67e5468bb08510daacc9836509 to your computer and use it in GitHub Desktop.
[Django - Forms] #forms #django #GET # POST
  • Working with forms
    • HTML forms
      • GET and POST
    • Django’s role in forms
    • Forms in Django
      • The Django Form class
      • Instantiating, processing, and rendering forms
    • Building a form
      • The work that needs to be done
      • Building a form in Django
        • The Form class
        • The view
        • The template
    • More about Django Form classes
      • Bound and unbound form instances
      • More on fields
        • Widgets
        • Field data
    • Working with form templates
      • Form rendering options
      • Rendering fields manually
        • Rendering form error messages
      • Looping over the form’s fields
        • Looping over hidden and visible fields
    • Reusable form templates

Handling forms is a complex business.

  • display in a form
  • rendered as HTML
  • edited using a convenient interface
  • returned to the server
  • validated
  • cleaned up
  • then saved or passed on for further processing

Django handles three distinct parts of the work involved in forms:

  1. preparing and restructuring data to make it ready for rendering
  2. creating HTML forms for the data
  3. receiving and processing submitted forms and data from the client
  • Forms in Django
    • The Django Form class
    • Instantiating, processing, and rendering forms

the Django Form class

a Form class describes a form and determines how it works and appears.

  • a model class’s fields map to database fields
  • a form class’s fields map to HTML form <input> elements
  • A ModelForm maps a model class’s fields to HTML form <input> elements via a Form
    • this is what the Django admin is based upon.

A form’s fields are themselves classes:

  • they manage form data
  • perform validation when a form is submitted.

Instantiating, processing, and rendering forms

When rendering an object in Django, we generally:

  1. get hold of it in the view (fetch it from the database, for example)
  2. pass it to the template context
  3. expand it to HTML markup using template variables
  • When we handle a model instance in a view, we typically retrieve it from the database.
  • When we’re dealing with a form we typically instantiate it in the view

when we instantiate a form, we can opt to leave it empty or pre-populate it, for example with:

  • data from a saved model instance (as in the case of admin forms for editing)
    • for editing
  • data that we have collated from other sources
    • for retrieving from db
  • data received from a previous HTML form submission
    • for getting input
- Building a form
    - The work that needs to be done
    - Building a form in Django
        - The Form class
        - The view
        - The template

The work that needs to be done

Suppose:

  • create a simple form on your website
  • in order to obtain the user's name

template

<!-- somewhere in your template -->

<form action="/your-name/" method="post">
    <label for="your_name">Your name: </label>
    <input id="your_name" type="text" name="your_name" value="{{ current_name }}">
    <input type="submit" value="OK">
</form>
  • tell the browser to return the form data to the URL /your-name
  • use the POST method
  • display a text field, labeled "Your name:"
  • a button marked "OK"
  • if the template context contains a current_name variable, that will be used to pre-fill the your_name field

view

need a view that renders the template containing the HTML form, and can supply the current_name field as appropriate.

When the form is submitted, the POST request which is sent to the server will contain the form data.

Then you'll need a view corresponding to that /your-name/ URL which will find the appropriate key/value pairs in the request, and then process them.

This is a very simple form. In practice, a form might contain dozens or hundreds of fields, many of which might need to be pre-populated, and we might expect the user to work through the edit-submit cycle several times before concluding the operation.

We might :

  • require some validation to occur in the browser, even before the form is submitted
  • use much more complex fields, that allow the user to do things like pick dates from a calendar and so on

Building a form in Django

The Form class

We already know what we want our HTML form to look like.

starting point in Django:

# forms.py

from django import forms

class NameForm(forms.Form):
    your_name = forms.CharField(label='Your name', max_length=100)

This defines a Form class with a single field (your_name).

label='Your name'

We’ve applied a human-friendly label to the field, which will appear in the <label> when it’s rendered (although in this case, the label we specified is actually the same one that would be generated automatically if we had omitted it).

max_length=100

The field’s maximum allowable length is defined by max_length.

This does two things:

  1. It puts a maxlength="100" on the HTML <input> (so the browser should prevent the user from entering more than that number of characters in the first place).
  2. It also means that when Django receives the form back from the browser, it will validate the length of the data.

A Form instance has an is_valid() method, which runs validation routines for all its fields.

When this method is called, if all fields contain valid data, it will:

  • return True
  • place the form’s data in its cleaned_data attribute.

The whole form, when rendered for the first time, will look like:

<label for="your_name">Your name: </label>
<input id="your_name" type="text" name="your_name" maxlength="100" required />

Note that it does not include the

tags, or a submit button. We’ll have to provide those ourselves in the template.

The view

Form data sent back to a Django website is processed by a view, generally the same view which published the form. This allows us to reuse some of the same logic.

To handle the form we need to instantiate it in the view for the URL where we want it to be published:

# views.py

from django.http import HttpResponseRedirect
from django.shortcuts import render

from .forms import NameForm

def get_name(request):
    # if this is a POST request we need to process the form data
    if request.method == 'POST':
        # create a form instance and populate it with data from the request:
        form = NameForm(request.POST)
        # check whether it's valid:
        if form.is_valid():
            # process the data in form.cleaned_data as required
            # ...
            # redirect to a new URL:
            return HttpResponseRedirect('/thanks/')

    # if a GET (or any other method) we'll create a blank form
    else:
        form = NameForm()

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

The template

<form action="/your-name/" method="post">
    {% csrf_token %}
    {{ form }}
    <input type="submit" value="Submit" />
</form>

All the form’s fields and their attributes will be unpacked into HTML markup from that {{ form }} by Django’s template language.

Forms and Cross Site Request Forgery protection

Django ships with an easy-to-use protection against Cross Site Request Forgeries.

When submitting a form via POST with CSRF protection enabled you must use the csrf_token template tag as in the preceding example. However, since CSRF protection is not directly tied to forms in templates, this tag is omitted from the following examples in this document.

HTML5 input types and browser validation

If your form includes a URLField, an EmailField or any integer field type, Django will use the url, email and number HTML5 input types. By default, browsers may apply their own validation on these fields, which may be stricter than Django’s validation. If you would like to disable this behavior, set the novalidate attribute on the form tag, or specify a different widget on the field, like TextInput.

We now have a working web form, described by a Django Form, processed by a view, and rendered as an HTML <form>.

That’s all you need to get started, but the forms framework puts a lot more at your fingertips. Once you understand the basics of the process described above, you should be prepared to understand other features of the forms system and ready to learn a bit more about the underlying machinery.

  • More about Django Form classes
    • Bound and unbound form instances
    • More on fields
      • Widgets
      • Field data

All form classes are created as subclasses of either django.forms.Form or django.forms.ModelForm.

You can think of ModelForm as a subclass of Form.Form and ModelForm actually inherit common functionality from a (private) BaseForm class, but this implementation detail is rarely important.

Models and Forms

In fact if your form is going to be used to directly add or edit a Django model, a ModelForm can save you a great deal of time, effort, and code, because it will build a form, along with the appropriate fields and their attributes, from a Model class.

Bound and unbound form instances

The distinction between Bound and unbound forms is important:

  • An unbound form has no data associated with it.
    • When rendered to the user, it will be empty or will contain default values.
  • A bound form has submitted data, and hence can be used to tell if that data is valid.
    • If an invalid bound form is rendered, it can include inline error messages telling the user what data to correct.

The form’s is_bound attribute will tell you whether a form has data bound to it or not.

More on fields

Consider a more useful form than our minimal example above, which we could use to implement “contact me” functionality on a personal website:

# forms.py

from django import forms

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

Our earlier form used a single field, your_name, a CharField.

In this case, our form has four fields: subject, message, sender and cc_myself. CharField, EmailField and BooleanField are just three of the available field types; a full list can be found in Form fields.

Widgets

Each form field has a corresponding Widget class, which in turn corresponds to an HTML form widget such as <input type="text">.

In most cases, the field will have a sensible default widget.

For example, by default, a CharField will have a TextInput widget, that produces an <input type="text"> in the HTML. If you needed <textarea> instead, you’d specify the appropriate widget when defining your form field, as we have done for the message field.

Field data

Whatever the data submitted with a form, once it has been successfully validated by calling is_valid() (and is_valid() has returned True), the validated form data will be in the form.cleaned_data dictionary. This data will have been nicely converted into Python types for you.

You can still access the unvalidated data directly from request.POST at this point, but the validated data is better.

In the contact form example above, cc_myself will be a boolean value. Likewise, fields such as IntegerField and FloatField convert values to a Python int and float respectively.

Here’s how the form data could be processed in the view that handles this form:

# views.py

from django.core.mail import send_mail

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)

    send_mail(subject, message, sender, recipients)
    return HttpResponseRedirect('/thanks/')

For more on sending email from Django, see Sending email.

Some field types need some extra handling. For example, files that are uploaded using a form need to be handled differently (they can be retrieved from request.FILES, rather than request.POST). For details of how to handle file uploads with your form, see Binding uploaded files to a form.

  • Working with form templates
    • Form rendering options
    • Rendering fields manually
      • Rendering form error messages
    • Looping over the form’s fields
      • Looping over hidden and visible fields
    • Reusable form templates

All you need to do to get your form into a template is to place the form instance into the template context. So if your form is called form in the context, {{ form }} will render its <label> and <input> elements appropriately.

Form rendering options

Additional form template furniture

Don’t forget that a form’s output does not include the surrounding <form> tags, or the form’s submit control. You will have to provide these yourself.

There are other output options though for the <label>/<input> pairs:

  • {{ form.as_table }} will render them as table cells wrapped in <tr> tags
  • {{ form.as_p }} will render them wrapped in <p> tags
  • {{ form.as_ul }} will render them wrapped in <li> tags Note that you’ll have to provide the surrounding <table> or <ul> elements yourself.

Here’s the output of {{ form.as_p }} for our ContactForm instance:

<p><label for="id_subject">Subject:</label>
    <input id="id_subject" type="text" name="subject" maxlength="100" required /></p>
<p><label for="id_message">Message:</label>
    <textarea name="message" id="id_message" required></textarea></p>
<p><label for="id_sender">Sender:</label>
    <input type="email" name="sender" id="id_sender" required /></p>
<p><label for="id_cc_myself">Cc myself:</label>
    <input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>

Note that each form field has an ID attribute set to id_<field-name>, which is referenced by the accompanying label tag. This is important in ensuring that forms are accessible to assistive technology such as screen reader software. You can also customize the way in which labels and ids are generated.

See Outputting forms as HTML for more on this.

Rendering fields manually

We don’t have to let Django unpack the form’s fields; we can do it manually if we like (allowing us to reorder the fields, for example).

Each field is available as an attribute of the form using {{ form.name_of_field }}, and in a Django template, will be rendered appropriately. For example:

{{ form.non_field_errors }}
<div class="fieldWrapper">
    {{ form.subject.errors }}
    <label for="{{ form.subject.id_for_label }}">Email subject:</label>
    {{ form.subject }}
</div>
<div class="fieldWrapper">
    {{ form.message.errors }}
    <label for="{{ form.message.id_for_label }}">Your message:</label>
    {{ form.message }}
</div>
<div class="fieldWrapper">
    {{ form.sender.errors }}
    <label for="{{ form.sender.id_for_label }}">Your email address:</label>
    {{ form.sender }}
</div>
<div class="fieldWrapper">
    {{ form.cc_myself.errors }}
    <label for="{{ form.cc_myself.id_for_label }}">CC yourself?</label>
    {{ form.cc_myself }}
</div>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment