The Django Web Framework – Part V

The Django Web
Framework — Part V
Web Programming Course – Fall 2013
Outline
•
Introduction to Forms in Django
•
Addition of a Form Section to Our Blog
2
Forms
•
<form> is the way that allows users to send data to
server for processing.
3
Forms
4
Processing Forms
•
It’s possible to process forms just using Django’s
HttpRequest class.
•
request.GET, request.POST, request.FILES
•
Ain't Nobody Got Time For That!
5
Processing Forms – The Stone Age Way
Default value in the case that
def register(request):
‘username’ doesn’t exist.
if request.method == "POST":
username = request.POST.get('username', None)
if not username:
username_error = u"‫نام کاربری را وارد کنید‬."
elif not re.match("[\d\w_\.]{3,}", username):
username_error = u"‫نام کاربری باید حداقل سه کاراکتر و شامل اعداد و حروف باشد‬."
elif User.objects.filter(username=username).count() > 0:
username_error = u"‫این نام کاربری قبال گرفته شده است‬."
email = request.POST.get('email', None)
if not email:
email_error = u"‫آدرس ایمیل را وارد کنید‬."
elif not email_re.search(email):
email_error = u"‫ایمیل معتبری را وارد کنید‬."
elif User.objects.filter(email=email).count() > 0:
email_error = u"‫این آدرس ایمیل قبال استفاده شده است‬."
6
Processing Forms – The Stone Age Way
•
Pretty cool, huh?
•
Don’t Repeat Yourself, dude.
7
Django Forms API
•
Helps you with some common tasks:
•
Auto-generate an HTML form for you,
•
Check submitted data against set of validation rules,
•
Redisplay form in case of errors,
•
Convert submitted data to relevant Python types.
8
Main Concepts
•
Widget
Handles rendering the widget as HTML.
•
Fields
Responsible for doing validation.
•
Forms
A collection of fields that knows how to display and
validate itself.
•
Form Assets
CSS & Javascript resources required to render the form.
9
Form Object
•
A Form object encapsulates a sequence of form fields
and a collection of validation rules.
forms.py
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)
CharField
DateField
FileField
EmailField
BooleanField
RegexField
…
10
Field Object
•
Important field arguments:
• Field.required father_name = forms.CharField(required=False)
• Field.label url = forms.URLField(label='Your Web site')
• Field.initial url = forms.URLField(initial='http://') Not for fallback.
• Field.widget comment = forms.CharField(widget=forms.Textarea)
• Field.help_text sender = forms.EmailField(help_text='Real address.')
• Field.error_messages
!name
•
= forms.CharField(error_messages={'required': ‘Enter your name, dude.’})
Field.validators
even_field = forms.IntegerField(validators=[validate_even])
11
Using a Form in a View
There are three different
code paths here.
from django.shortcuts import render
from django.http import HttpResponseRedirect
from mysite.forms import ContactForm
!
The successfully validated
form data will be in the
form.cleaned_data
dictionary.
def contact(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
# processing the form and doing something
return HttpResponseRedirect('/thanks/')
else:
form = ContactForm()
!
return render(request, 'contact.html', {'form': form,})
Bound vs. Unbound
12
Processing the Form
You can still access
the unvalidated data
directly from
request.POST.
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 = ['[email protected]']
if cc_myself:
recipients.append(sender)
!
from django.core.mail import send_mail
send_mail(subject, message, sender, recipients)
return HttpResponseRedirect('/thanks/')
13
Displaying the Form
Don’t panic. We’ll talk about
this in a later session.
Only outputs form
fields, don’t forget
<form></form>.
<form action="/contact/" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
Render result:
<form action="/contact/" method="post">
<p><label for="id_subject">Subject:</label>
<input id="id_subject" type="text" name="subject" maxlength="100" /></p>
<p><label for="id_message">Message:</label>
<input type="text" name="message" id="id_message" /></p>
<p><label for="id_sender">Sender:</label>
<input type="email" name="sender" id="id_sender" /></p>
<p><label for="id_cc_myself">Cc myself:</label>
<input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>
<input type="submit" value="Submit" />
</form>
14
Don’t Like It? Okay, whatever.
<form action="/contact/" method="post">
We’ll talk about it in a minute. Be
{{ form.non_field_errors }}
patience.
<div class="fieldWrapper">
{{ form.subject.errors }}
Produces the HTML needed to <label for="id_subject">Email subject:</label>
{{ form.subject }}
display the form widget.
</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 }}
Displays a list of form errors,
</div>
rendered as an unordered list.
<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>
15
More Convenient Way
<form action="/contact/" method="post">
{% for field in form %}
{{ field.label }}
<div class="fieldWrapper">
{{ field.value }}
{{ field.errors }}
{{ field.help_text }}
{{ field.is_hidden }}
{{ field.label_tag }} {{ field }}
{{ field.field }}
</div>
{{ field.html_name }}
{% endfor %}
...
<p><input type="submit" value="Send message" /></p>
</form>
16
Using Forms Directly with a Model
•
Don’t Repeat Yourself
class Author(models.Model):
name = models.CharField(max_length=100)
title = models.CharField(max_length=3, choices=TITLE_CHOICES)
birth_date = models.DateField(blank=True, null=True)
!
!
def __str__(self):
return self.name
class Book(models.Model):
name = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
class AuthorForm(forms.ModelForm):
class Meta:
model = Author
fields = ['name', 'title', 'birth_date']
!
class BookForm(forms.ModelForm):
class Meta:
model = Book
fields = ['name', 'authors']
ModelForm objects have an
extra .save() method.
17
Form Validation
•
From validation happens when data is cleaned.
•
You remember form.cleaned_data[].
18
ValidationError
•
Any cleaning method can raise ValidationError if
there is a problem with the data it is processing.
•
Otherwise, just return cleaned data.
>>> from django import forms
>>> f = forms.EmailField()
>>> f.clean('[email protected]')
u'[email protected]'
>>> f.clean('invalid email address')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid email address.']
ValidationError('Enter a name, please.', code='invalid')
Don’t forget code (not required, but recommended).
19
Form Validation Steps
1, 2) to_python() and validate() on a Field.
from django import forms
from django.core.validators import validate_email
!
class MultiEmailField(forms.Field):
def to_python(self, value):
"Normalize data to a list of strings."
!
!
!
!
# Return an empty list if no input was given.
if not value:
return []
return value.split(',')
def validate(self, value):
"Check if value consists only of valid emails."
# Use the parent's handling of required fields, etc.
super(MultiEmailField, self).validate(value)
for email in value:
validate_email(email)
20
Form Validation Steps
3) run_validators() on a Field.
from django.core.exceptions import ValidationError
!
def validate_even(value):
if value % 2 != 0:
raise ValidationError(u'%s is not an even number' % value)
even_field = forms.IntegerField(validators=[validate_even])
21
Form Validation Steps
4) clean() on a Field subclass
•
This is responsible for running to_python,
validate and run_validators in the correct
order and propagating their errors.
22
Form Validation Steps
5) clean_<fieldname>() on a Form subclass.
from django import forms
!
class ContactForm(forms.Form):
# Everything as before.
!
def clean_recipients(self):
data = self.cleaned_data['recipients']
if "[email protected]" not in data:
raise forms.ValidationError("You have forgotten about Milad!")
!
# Always return the cleaned data, whether you have changed it or
# not.
return data
23
Form Validation Steps
6) clean() on a Form subclass.
from django import forms
!
class ContactForm(forms.Form):
# Everything as before.
...
!
!
!
def clean(self):
cleaned_data = super(ContactForm, self).clean()
cc_myself = cleaned_data.get("cc_myself")
subject = cleaned_data.get("subject")
if cc_myself and subject:
# Only do something if both fields are valid so far.
if "help" not in subject:
raise forms.ValidationError("Did not send for 'help' in "
"the subject despite CC'ing yourself.")
# Always return the full collection of cleaned data.
return cleaned_data
24
Showtime
•
Let’s add a comment section to our blog.
25
Any Questions?
26
References
•
https://docs.djangoproject.com/en/1.6/topics/forms/
•
https://docs.djangoproject.com/en/1.6/ref/forms/fields/
•
https://docs.djangoproject.com/en/1.6/ref/forms/
widgets/
•
https://docs.djangoproject.com/en/1.6/ref/forms/
validation/
•
http://www.python.org
27