Knowledge for the World

Getting Started With Django Forms

Django forms are an excellent way to manage user input in a Django application. Once you learn how to wield them, you'll never go back to manually coding a form tag and handling the response. This guide will scratch the surface but get you far enough to begin using and learning the power of Django's Form class.

Let's dive in.

In these interests [?]
  • django
    15 Subscribers Subscribe
  • python
    80 Subscribers Subscribe
1

Why should you use Django forms?

Django forms save time and code. Because of their class based nature, you are able to reuse forms saving you time and complexity.

Using a mature web framework's form class also enforces more secure code than you write (probably). For example, Django forms will complain loudly if you do not use CSRF protection, something that can easily go forgotten if you're manually coding a form.

2

How Do Django forms work?

Django's Form class is the heartbeat of Django's form components. In the same way Django's Model class describes an object, the Form class describes a form. A Model's fields describe individual database columns and a Form's fields describe individual form elements.

class ArticleForm(forms.ModelForm):
    title = forms.CharField()
    desc = forms.TextField()

    class Meta:
        model = Article

3

4 basic steps to using Django forms

1. create a form class for your needs (usually in forms.py)
2. instantiate that form class (usually in a view)
3. render that form (usually in a template)
4. instantiate the same form class with POST data (aka bind data to the form)

There are other minor steps which we will cover, but this is the basic framework for using Django forms.

4

Example #1 - Setting up a basic contact form

Create a form class. This class represents a contact form that takes name, email, and message inputs. Django chooses an HTML element based on the type of field you specify. You can also override this choice by passing in a 'widget' argument.

from django import forms

class ContactForm(forms.Form):
    name = forms.CharField()
    email = forms.EmailField()
    message = forms.CharField(widget=forms.Textarea)

Create a view

# at the top of the file
from .forms import ContactForm

def contact(request):
    contact_form = ContactForm()
    return render(request, "contact.html", {"form": contact_form})

Setup a new url

url(r'^contact/?$', contact, name='contact')

Create a template

<h1>Contact</h1>
<form role="form" action="" method="post">
    {% csrf_token %}
    {{ form }}
    <button type="submit">Submit</button>
</form>

Handle the POST request in the view

if request.method == 'POST':
    contact_form = ContactForm(data=request.POST)

    if contact_form.is_valid():
        name = request.POST.get('name', '')
        email = request.POST.get('email', '')
        message = request.POST.get('message', '')
        # do some processing with name and email
        messages.success(request, 'Your message has been sent!')
        return redirect('contact')
else:
	contact_form = ContactForm()

5

Example #2 - Setting up a model form to create objects

Create a model

from django.db import models

class Article(models.Model):
    title = models.CharField(max_length=1000)
    desc = models.CharField(max_length=1000)

# makemigrations && migrate

Create a form

from django import forms

from .models import Article

class ArticleForm(forms.ModelForm):
   class Meta:
       model = Article
       fields = ['title', 'desc',]

Create a view

# at the top of the file
from .forms import ArticleForm

def article_create(request):
    article_form = ArticleForm()
    return render(request, "article_form.html", {"form": article_form})

Setup a new url

url(r'^articles/create?$', contact, name='article_create')

Create a template

<h1>Article Form</h1>
<form role="form" action="" method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Submit</button>
</form>

Update description field widget

class ArticleForm(forms.ModelForm):
    class Meta:
        model = Article
        fields = ['title', 'desc',]
        widgets = {
                'desc': forms.Textarea(attrs={'class': 'desc'}),
        }

Handle the POST request in the view

if request.method == 'POST':
    article_form = ArticleForm(data=request.POST)

    if article_form.is_valid():
        article = article_form.save()
        return redirect('article-detail', pk=article.pk)
else:
    article_form = ArticleForm()

6

Example #3 - Setting up a model form to update objects

Create a view

def article_edit(request, pk):
    try:
        article = Article.objects.get(pk=pk)
    except Article.DoesNotExist:
        return redirect('article-create')

    article_form = ArticleForm(instance=article)

    return render(request, "article_form.html", {"form": article_form})

Setup a new url

url(r'^articles/(?P<pk>\d+)/edit?$', contact, name='article-edit')

Handle the POST request in the view

if request.method == 'POST':
    article_form = ArticleForm(instance=article, data=request.POST)

    if article_form.is_valid():
        article = article_form.save()
        return redirect('article-detail', pk=article.pk)
else:
    article_form = ArticleForm(instance=article)

7

Example #4 - Adding custom validation to a form

Custom Clean Method

def clean_title(self):
    title = self.cleaned_data.get('title', None)

    if 'django' not in title.lower():
        raise forms.ValidationError("You forgot to talk about Django!")

    return title

8

Odds and ends

Different ways to display a form in HTML

Default or .as_table()

article_form = ArticleForm()
print(article_form) # or print(article_form.as_table())

# results 

<tr>
<th><label for="id_title">Title:</label></th>
<td><input id="id_title" maxlength="1000" name="title" type="text" /></td>
</tr>
<tr>
<th><label for="id_desc">Desc:</label></th><td>
<textarea class="desc" cols="40" id="id_desc" maxlength="1000" name="desc" rows="10"></textarea></td>
</tr>

.as_p()

article_form = ArticleForm()
print(article_form.as_p())

#results

<p>
<label for="id_title">Title:</label> 
<input id="id_title" maxlength="1000" name="title" type="text" />
</p>
<p>
<label for="id_desc">Desc:</label>
<textarea class="desc" cols="40" id="id_desc" maxlength="1000" name="desc" rows="10"></textarea>
</p>

article_form.as_ul()

article_form = ArticleForm()
print(article_form.as_ul())

#results

<li>
<label for="id_title">Title:</label>
<input id="id_title" maxlength="1000" name="title" type="text" />
</li>
<li>
<label for="id_desc">Desc:</label>
<textarea class="desc" cols="40" id="id_desc" maxlength="1000" name="desc" rows="10"></textarea>
</li>

9

Iterating through form object

In Python

article_form = ArticleForm()
for field in article_form:
    print(field)

# results
<input id="id_title" maxlength="1000" name="title" type="text" />
<textarea class="desc" cols="40" id="id_desc" maxlength="1000" name="desc" rows="10"></textarea>

In Template

{% for field in form %}
    <div class="fieldWrapper">
        {{ field.errors }}
        {{ field.label_tag }}
    {{ field }}
    {% if field.help_text %}
    		<p class="help">{{ field.help_text|safe }}</p>
    {% endif %}
</div>
{% endfor %}

10

Conclusion

Pros of Django Forms

1. Huge timesaver and code saver
2. Very powerful
3. Well documented
4. Good community support

Weaknesses

1. There is a lot of configuration
2. You will spend tons of time in the docs at first
3. You will run into trouble trying to customize the display of your form fields (crispy forms helps)
4. Django does nothing with Javascript so for super Javascripty forms, you're on your own

A word about Crispy Forms

• Extremely customizable form output
• Helps keep code DRY
• Sticks to Django conventions so it does not feel like a 3rd party package
https://github.com/maraujop/django-crispy-forms