Django
Forms
Validation

WHY SHOULD I USE DJANGO FORMS?

Never trust the client

 

  1. Easy to customize HTML output
  2. Validation is encapsulated
  3. Easy to report errors back to user
  4. Works well with models

WE WILL FOCUS ON VALIDATION THIS TIME

Let's assume there is no validation in client. That allows me to submit the form with values like these:

<form action="" method="GET">
	Name: <input type="text" name="name" placeholder="name">
	Email: <input type="text" name="email" placeholder="email">
	Message: <textarea name="message" placeholder="Some space for your request"></textarea>
	<input type="submit">
</form>

Our innocent server (it trusts clients) now wants to show back the data it got.

from django.http import HttpResponse
from django.template import RequestContext, loader

# Create your views here.
def my_view(request):
    template = loader.get_template('my_faulty_template.html')
    if request.GET.get('name'):
        context = {
            'name': request.GET.get('name'),
            'email': request.GET.get('email'),
            'message': request.GET.get('message'),
        }
    else:
        context={}
    return HttpResponse(template.render(context))

What a fool!

Our user should see something like this now:

<p>Name: {{ name }}</p>
<p>Email: {{ email }}</p>
<p>Message: {{ message }}</p>

Any errors on sight?

The email field, which is supposed to be an email, can be anything at the moment.

Never trust the client. Client side validation can be ignored easily, so we won't try to add that manually for now.

 

Let's focus on teaching our server how mean can clients be!

STEP 1: BUILD OUR DJANGO FORM

from django import forms

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

STEP 2: CHANGE OUR VIEW

from django.http import HttpResponse
from django.template import RequestContext, loader
from .forms import ContactForm

def my_better_view(request):
    template = loader.get_template('my_slightly_better_template.html')
    context = {}
    if request.method == 'GET':
        form = ContactForm(request.GET)
        if form.is_valid():
            context = {
                'name': form.cleaned_data['name'],
                'email': form.cleaned_data['email'],
                'message': form.cleaned_data['message'],
            }
        context['form'] = form

        return HttpResponse(template.render(context))

STEP 3: UPDATE OUR TEMPLATE

<form action="" method="GET">
  {{ form }}
  <input type="submit">
</form>

<p>Name: {{ name }}</p>
<p>Email: {{ email }}</p>
<p>Message: {{ message }}</p>

YEAH...SO WHAT? WHAT CHANGED?

  1. We used an EmailField in server, which check that the entered value is indeed a valid email
  2. We rendered the form with Django. This is optional, but now our client is using an email input instead of a text input, adding some validation on client side
  3. If we decide to add more fields, we can just add them to our ContactForm class.

HOW CAN SERVER KNOW WHAT IS VALID AND WHAT IS NOT?

Let me introduce you your new right hand:

form.is_valid()

What if i want to show only the messages of love for Django Cali?

Time to get started with custom validations.

Let's change our ContactForm class 

from django import forms

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

	def clean_message(self):
		message = self.cleaned_data['message']
		if 'I Love DjangoCali' not in message:
			raise forms.ValidationError("What's wrong with you!!!")

		return message

Ok, that was easy, wasn't it?

Let's try rejecting messages where the contact name has more than 2 words

def clean_name(self):
	name = self.cleaned_data['name']
	if len(name.split()) > 2:
		raise forms.ValidationError("Your name is weird!!!")

	return name

People with only one name is saying that they actually got 2 in their message! I don't want liars!

def clean(self):
        cleaned_data = super(ContactForm, self).clean()
        name = cleaned_data.get("name", '')
        message = cleaned_data.get("message", '')

        if len(name.split())==1 and 'i got 2 names' in message:
            # Only do something if both fields are valid so far.
            raise forms.ValidationError(
                "I know you are lying!!!"
            )

When should i use forms?

Forms are a must if you are accepting user input, of any kind.

Suppose we have a CSV file and we want to show it to our users or save it to the database. We need to make sure that every set of data is valid according to our rules. In this case you are not going to display the form, but you can use it to validate it.

my name is cristian,now i try breaking your csv,with dumb random text
three names yay,my@email.com,What should i do?
Two names,valid@email.com,I Love DjangoCali

Our test CSV

Our output

Contact_list:

  • Name: Two names
  • Email: valid@email.com
  • Message: I Love DjangoCali
  • ---------------------------------

How did you do that?

def my_csv_display(request):
    template = loader.get_template('my_csv_data_display.html')
    context = {}
    my_valid_contacts = []
    import csv
    with open('my_app/csv_file.csv', 'rb') as csv_file:
        reader = csv.reader(csv_file, delimiter=',')
        for row in reader:
            data={
                'name': row[0],
                'email': row[1],
                'message': row[2],
            }
            form = ContactForm(data=data)
            if form.is_valid():
                my_valid_contacts.append(
                        [form.cleaned_data.get('name'),\
                        form.cleaned_data.get('email'),\
                        form.cleaned_data.get('message')]
                    )
    context['contact_list'] = my_valid_contacts
    return HttpResponse(template.render(context))

DRY

I just reused the same form we were using before, but for validating data from other source

I want to check the examples by myself, can i?

https://github.com/djangocali/forms_validation

PD: The functionality is there, but there is absolutely nothing helping with aesthetics, just the absolutely required bits to make it work.

Thanks for your attention

Django Forms Validation

By Cristian Vargas

Django Forms Validation

Slides for presentation related to Django Forms Validation

  • 581