Migrated forms (minus localflavor) doctests. A huge thanks to Daniel Lindsley for the patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14570 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Russell Keith-Magee 2010-11-16 13:20:56 +00:00
parent 8da8d6c586
commit 8bafde1229
24 changed files with 5027 additions and 5619 deletions

View File

@ -309,7 +309,7 @@ answer newbie questions, and generally made Django that much better:
limodou
Philip Lindborg <philip.lindborg@gmail.com>
Simon Litchfield <simon@quo.com.au>
Daniel Lindsley <polarcowz@gmail.com>
Daniel Lindsley <daniel@toastdriven.com>
Trey Long <trey@ktrl.com>
Laurent Luce <http://www.laurentluce.com>
Martin Mahner <http://www.mahner.org/>

View File

@ -1,399 +0,0 @@
# -*- coding: utf-8 -*-
tests = r"""
>>> from django.forms import *
>>> from django.core.files.uploadedfile import SimpleUploadedFile
# CharField ###################################################################
>>> e = {'required': 'REQUIRED'}
>>> e['min_length'] = 'LENGTH %(show_value)s, MIN LENGTH %(limit_value)s'
>>> e['max_length'] = 'LENGTH %(show_value)s, MAX LENGTH %(limit_value)s'
>>> f = CharField(min_length=5, max_length=10, error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('1234')
Traceback (most recent call last):
...
ValidationError: [u'LENGTH 4, MIN LENGTH 5']
>>> f.clean('12345678901')
Traceback (most recent call last):
...
ValidationError: [u'LENGTH 11, MAX LENGTH 10']
# IntegerField ################################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid'] = 'INVALID'
>>> e['min_value'] = 'MIN VALUE IS %(limit_value)s'
>>> e['max_value'] = 'MAX VALUE IS %(limit_value)s'
>>> f = IntegerField(min_value=5, max_value=10, error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('abc')
Traceback (most recent call last):
...
ValidationError: [u'INVALID']
>>> f.clean('4')
Traceback (most recent call last):
...
ValidationError: [u'MIN VALUE IS 5']
>>> f.clean('11')
Traceback (most recent call last):
...
ValidationError: [u'MAX VALUE IS 10']
# FloatField ##################################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid'] = 'INVALID'
>>> e['min_value'] = 'MIN VALUE IS %(limit_value)s'
>>> e['max_value'] = 'MAX VALUE IS %(limit_value)s'
>>> f = FloatField(min_value=5, max_value=10, error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('abc')
Traceback (most recent call last):
...
ValidationError: [u'INVALID']
>>> f.clean('4')
Traceback (most recent call last):
...
ValidationError: [u'MIN VALUE IS 5']
>>> f.clean('11')
Traceback (most recent call last):
...
ValidationError: [u'MAX VALUE IS 10']
# DecimalField ################################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid'] = 'INVALID'
>>> e['min_value'] = 'MIN VALUE IS %(limit_value)s'
>>> e['max_value'] = 'MAX VALUE IS %(limit_value)s'
>>> e['max_digits'] = 'MAX DIGITS IS %s'
>>> e['max_decimal_places'] = 'MAX DP IS %s'
>>> e['max_whole_digits'] = 'MAX DIGITS BEFORE DP IS %s'
>>> f = DecimalField(min_value=5, max_value=10, error_messages=e)
>>> f2 = DecimalField(max_digits=4, decimal_places=2, error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('abc')
Traceback (most recent call last):
...
ValidationError: [u'INVALID']
>>> f.clean('4')
Traceback (most recent call last):
...
ValidationError: [u'MIN VALUE IS 5']
>>> f.clean('11')
Traceback (most recent call last):
...
ValidationError: [u'MAX VALUE IS 10']
>>> f2.clean('123.45')
Traceback (most recent call last):
...
ValidationError: [u'MAX DIGITS IS 4']
>>> f2.clean('1.234')
Traceback (most recent call last):
...
ValidationError: [u'MAX DP IS 2']
>>> f2.clean('123.4')
Traceback (most recent call last):
...
ValidationError: [u'MAX DIGITS BEFORE DP IS 2']
# DateField ###################################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid'] = 'INVALID'
>>> f = DateField(error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('abc')
Traceback (most recent call last):
...
ValidationError: [u'INVALID']
# TimeField ###################################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid'] = 'INVALID'
>>> f = TimeField(error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('abc')
Traceback (most recent call last):
...
ValidationError: [u'INVALID']
# DateTimeField ###############################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid'] = 'INVALID'
>>> f = DateTimeField(error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('abc')
Traceback (most recent call last):
...
ValidationError: [u'INVALID']
# RegexField ##################################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid'] = 'INVALID'
>>> e['min_length'] = 'LENGTH %(show_value)s, MIN LENGTH %(limit_value)s'
>>> e['max_length'] = 'LENGTH %(show_value)s, MAX LENGTH %(limit_value)s'
>>> f = RegexField(r'^\d+$', min_length=5, max_length=10, error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('abcde')
Traceback (most recent call last):
...
ValidationError: [u'INVALID']
>>> f.clean('1234')
Traceback (most recent call last):
...
ValidationError: [u'LENGTH 4, MIN LENGTH 5']
>>> f.clean('12345678901')
Traceback (most recent call last):
...
ValidationError: [u'LENGTH 11, MAX LENGTH 10']
# EmailField ##################################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid'] = 'INVALID'
>>> e['min_length'] = 'LENGTH %(show_value)s, MIN LENGTH %(limit_value)s'
>>> e['max_length'] = 'LENGTH %(show_value)s, MAX LENGTH %(limit_value)s'
>>> f = EmailField(min_length=8, max_length=10, error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('abcdefgh')
Traceback (most recent call last):
...
ValidationError: [u'INVALID']
>>> f.clean('a@b.com')
Traceback (most recent call last):
...
ValidationError: [u'LENGTH 7, MIN LENGTH 8']
>>> f.clean('aye@bee.com')
Traceback (most recent call last):
...
ValidationError: [u'LENGTH 11, MAX LENGTH 10']
# FileField ##################################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid'] = 'INVALID'
>>> e['missing'] = 'MISSING'
>>> e['empty'] = 'EMPTY FILE'
>>> f = FileField(error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('abc')
Traceback (most recent call last):
...
ValidationError: [u'INVALID']
>>> f.clean(SimpleUploadedFile('name', None))
Traceback (most recent call last):
...
ValidationError: [u'EMPTY FILE']
>>> f.clean(SimpleUploadedFile('name', ''))
Traceback (most recent call last):
...
ValidationError: [u'EMPTY FILE']
# URLField ##################################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid'] = 'INVALID'
>>> e['invalid_link'] = 'INVALID LINK'
>>> f = URLField(verify_exists=True, error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('abc.c')
Traceback (most recent call last):
...
ValidationError: [u'INVALID']
>>> f.clean('http://www.broken.djangoproject.com')
Traceback (most recent call last):
...
ValidationError: [u'INVALID LINK']
# BooleanField ################################################################
>>> e = {'required': 'REQUIRED'}
>>> f = BooleanField(error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
# ChoiceField #################################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid_choice'] = '%(value)s IS INVALID CHOICE'
>>> f = ChoiceField(choices=[('a', 'aye')], error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('b')
Traceback (most recent call last):
...
ValidationError: [u'b IS INVALID CHOICE']
# MultipleChoiceField #########################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid_choice'] = '%(value)s IS INVALID CHOICE'
>>> e['invalid_list'] = 'NOT A LIST'
>>> f = MultipleChoiceField(choices=[('a', 'aye')], error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('b')
Traceback (most recent call last):
...
ValidationError: [u'NOT A LIST']
>>> f.clean(['b'])
Traceback (most recent call last):
...
ValidationError: [u'b IS INVALID CHOICE']
# SplitDateTimeField ##########################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid_date'] = 'INVALID DATE'
>>> e['invalid_time'] = 'INVALID TIME'
>>> f = SplitDateTimeField(error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean(['a', 'b'])
Traceback (most recent call last):
...
ValidationError: [u'INVALID DATE', u'INVALID TIME']
# IPAddressField ##############################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid'] = 'INVALID IP ADDRESS'
>>> f = IPAddressField(error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('127.0.0')
Traceback (most recent call last):
...
ValidationError: [u'INVALID IP ADDRESS']
###############################################################################
# Create choices for the model choice field tests below.
>>> from regressiontests.forms.models import ChoiceModel
>>> ChoiceModel.objects.create(pk=1, name='a')
<ChoiceModel: ChoiceModel object>
>>> ChoiceModel.objects.create(pk=2, name='b')
<ChoiceModel: ChoiceModel object>
>>> ChoiceModel.objects.create(pk=3, name='c')
<ChoiceModel: ChoiceModel object>
# ModelChoiceField ############################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid_choice'] = 'INVALID CHOICE'
>>> f = ModelChoiceField(queryset=ChoiceModel.objects.all(), error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('4')
Traceback (most recent call last):
...
ValidationError: [u'INVALID CHOICE']
# ModelMultipleChoiceField ####################################################
>>> e = {'required': 'REQUIRED'}
>>> e['invalid_choice'] = '%s IS INVALID CHOICE'
>>> e['list'] = 'NOT A LIST OF VALUES'
>>> f = ModelMultipleChoiceField(queryset=ChoiceModel.objects.all(), error_messages=e)
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'REQUIRED']
>>> f.clean('3')
Traceback (most recent call last):
...
ValidationError: [u'NOT A LIST OF VALUES']
>>> f.clean(['4'])
Traceback (most recent call last):
...
ValidationError: [u'4 IS INVALID CHOICE']
# Subclassing ErrorList #######################################################
>>> from django.utils.safestring import mark_safe
>>>
>>> class TestForm(Form):
... first_name = CharField()
... last_name = CharField()
... birthday = DateField()
...
... def clean(self):
... raise ValidationError("I like to be awkward.")
...
>>> class CustomErrorList(util.ErrorList):
... def __unicode__(self):
... return self.as_divs()
... def as_divs(self):
... if not self: return u''
... return mark_safe(u'<div class="error">%s</div>'
... % ''.join([u'<p>%s</p>' % e for e in self]))
...
This form should print errors the default way.
>>> form1 = TestForm({'first_name': 'John'})
>>> print form1['last_name'].errors
<ul class="errorlist"><li>This field is required.</li></ul>
>>> print form1.errors['__all__']
<ul class="errorlist"><li>I like to be awkward.</li></ul>
This one should wrap error groups in the customized way.
>>> form2 = TestForm({'first_name': 'John'}, error_class=CustomErrorList)
>>> print form2['last_name'].errors
<div class="error"><p>This field is required.</p></div>
>>> print form2.errors['__all__']
<div class="error"><p>I like to be awkward.</p></div>
"""

File diff suppressed because it is too large Load Diff

View File

@ -1,762 +0,0 @@
# -*- coding: utf-8 -*-
from django.test.testcases import TestCase
from django.forms.forms import Form
from django.forms.fields import CharField, IntegerField
from django.forms.formsets import formset_factory
tests = """
# Basic FormSet creation and usage ############################################
FormSet allows us to use multiple instance of the same form on 1 page. For now,
the best way to create a FormSet is by using the formset_factory function.
>>> from django.forms import Form, CharField, IntegerField, ValidationError
>>> from django.forms.formsets import formset_factory, BaseFormSet
>>> class Choice(Form):
... choice = CharField()
... votes = IntegerField()
>>> ChoiceFormSet = formset_factory(Choice)
A FormSet constructor takes the same arguments as Form. Let's create a FormSet
for adding data. By default, it displays 1 blank form. It can display more,
but we'll look at how to do so later.
>>> formset = ChoiceFormSet(auto_id=False, prefix='choices')
>>> print formset
<input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" /><input type="hidden" name="choices-MAX_NUM_FORMS" />
<tr><th>Choice:</th><td><input type="text" name="choices-0-choice" /></td></tr>
<tr><th>Votes:</th><td><input type="text" name="choices-0-votes" /></td></tr>
On thing to note is that there needs to be a special value in the data. This
value tells the FormSet how many forms were displayed so it can tell how
many forms it needs to clean and validate. You could use javascript to create
new forms on the client side, but they won't get validated unless you increment
the TOTAL_FORMS field appropriately.
>>> data = {
... 'choices-TOTAL_FORMS': '1', # the number of forms rendered
... 'choices-INITIAL_FORMS': '0', # the number of forms with initial data
... 'choices-MAX_NUM_FORMS': '0', # max number of forms
... 'choices-0-choice': 'Calexico',
... 'choices-0-votes': '100',
... }
We treat FormSet pretty much like we would treat a normal Form. FormSet has an
is_valid method, and a cleaned_data or errors attribute depending on whether all
the forms passed validation. However, unlike a Form instance, cleaned_data and
errors will be a list of dicts rather than just a single dict.
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
>>> formset.is_valid()
True
>>> [form.cleaned_data for form in formset.forms]
[{'votes': 100, 'choice': u'Calexico'}]
If a FormSet was not passed any data, its is_valid method should return False.
>>> formset = ChoiceFormSet()
>>> formset.is_valid()
False
FormSet instances can also have an error attribute if validation failed for
any of the forms.
>>> data = {
... 'choices-TOTAL_FORMS': '1', # the number of forms rendered
... 'choices-INITIAL_FORMS': '0', # the number of forms with initial data
... 'choices-MAX_NUM_FORMS': '0', # max number of forms
... 'choices-0-choice': 'Calexico',
... 'choices-0-votes': '',
... }
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
>>> formset.is_valid()
False
>>> formset.errors
[{'votes': [u'This field is required.']}]
We can also prefill a FormSet with existing data by providing an ``initial``
argument to the constructor. ``initial`` should be a list of dicts. By default,
an extra blank form is included.
>>> initial = [{'choice': u'Calexico', 'votes': 100}]
>>> formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices')
>>> for form in formset.forms:
... print form.as_ul()
<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
<li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>
<li>Choice: <input type="text" name="choices-1-choice" /></li>
<li>Votes: <input type="text" name="choices-1-votes" /></li>
Let's simulate what would happen if we submitted this form.
>>> data = {
... 'choices-TOTAL_FORMS': '2', # the number of forms rendered
... 'choices-INITIAL_FORMS': '1', # the number of forms with initial data
... 'choices-MAX_NUM_FORMS': '0', # max number of forms
... 'choices-0-choice': 'Calexico',
... 'choices-0-votes': '100',
... 'choices-1-choice': '',
... 'choices-1-votes': '',
... }
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
>>> formset.is_valid()
True
>>> [form.cleaned_data for form in formset.forms]
[{'votes': 100, 'choice': u'Calexico'}, {}]
But the second form was blank! Shouldn't we get some errors? No. If we display
a form as blank, it's ok for it to be submitted as blank. If we fill out even
one of the fields of a blank form though, it will be validated. We may want to
required that at least x number of forms are completed, but we'll show how to
handle that later.
>>> data = {
... 'choices-TOTAL_FORMS': '2', # the number of forms rendered
... 'choices-INITIAL_FORMS': '1', # the number of forms with initial data
... 'choices-MAX_NUM_FORMS': '0', # max number of forms
... 'choices-0-choice': 'Calexico',
... 'choices-0-votes': '100',
... 'choices-1-choice': 'The Decemberists',
... 'choices-1-votes': '', # missing value
... }
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
>>> formset.is_valid()
False
>>> formset.errors
[{}, {'votes': [u'This field is required.']}]
If we delete data that was pre-filled, we should get an error. Simply removing
data from form fields isn't the proper way to delete it. We'll see how to
handle that case later.
>>> data = {
... 'choices-TOTAL_FORMS': '2', # the number of forms rendered
... 'choices-INITIAL_FORMS': '1', # the number of forms with initial data
... 'choices-MAX_NUM_FORMS': '0', # max number of forms
... 'choices-0-choice': '', # deleted value
... 'choices-0-votes': '', # deleted value
... 'choices-1-choice': '',
... 'choices-1-votes': '',
... }
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
>>> formset.is_valid()
False
>>> formset.errors
[{'votes': [u'This field is required.'], 'choice': [u'This field is required.']}, {}]
# Displaying more than 1 blank form ###########################################
We can also display more than 1 empty form at a time. To do so, pass a
extra argument to formset_factory.
>>> ChoiceFormSet = formset_factory(Choice, extra=3)
>>> formset = ChoiceFormSet(auto_id=False, prefix='choices')
>>> for form in formset.forms:
... print form.as_ul()
<li>Choice: <input type="text" name="choices-0-choice" /></li>
<li>Votes: <input type="text" name="choices-0-votes" /></li>
<li>Choice: <input type="text" name="choices-1-choice" /></li>
<li>Votes: <input type="text" name="choices-1-votes" /></li>
<li>Choice: <input type="text" name="choices-2-choice" /></li>
<li>Votes: <input type="text" name="choices-2-votes" /></li>
Since we displayed every form as blank, we will also accept them back as blank.
This may seem a little strange, but later we will show how to require a minimum
number of forms to be completed.
>>> data = {
... 'choices-TOTAL_FORMS': '3', # the number of forms rendered
... 'choices-INITIAL_FORMS': '0', # the number of forms with initial data
... 'choices-MAX_NUM_FORMS': '0', # max number of forms
... 'choices-0-choice': '',
... 'choices-0-votes': '',
... 'choices-1-choice': '',
... 'choices-1-votes': '',
... 'choices-2-choice': '',
... 'choices-2-votes': '',
... }
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
>>> formset.is_valid()
True
>>> [form.cleaned_data for form in formset.forms]
[{}, {}, {}]
We can just fill out one of the forms.
>>> data = {
... 'choices-TOTAL_FORMS': '3', # the number of forms rendered
... 'choices-INITIAL_FORMS': '0', # the number of forms with initial data
... 'choices-MAX_NUM_FORMS': '0', # max number of forms
... 'choices-0-choice': 'Calexico',
... 'choices-0-votes': '100',
... 'choices-1-choice': '',
... 'choices-1-votes': '',
... 'choices-2-choice': '',
... 'choices-2-votes': '',
... }
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
>>> formset.is_valid()
True
>>> [form.cleaned_data for form in formset.forms]
[{'votes': 100, 'choice': u'Calexico'}, {}, {}]
And once again, if we try to partially complete a form, validation will fail.
>>> data = {
... 'choices-TOTAL_FORMS': '3', # the number of forms rendered
... 'choices-INITIAL_FORMS': '0', # the number of forms with initial data
... 'choices-MAX_NUM_FORMS': '0', # max number of forms
... 'choices-0-choice': 'Calexico',
... 'choices-0-votes': '100',
... 'choices-1-choice': 'The Decemberists',
... 'choices-1-votes': '', # missing value
... 'choices-2-choice': '',
... 'choices-2-votes': '',
... }
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
>>> formset.is_valid()
False
>>> formset.errors
[{}, {'votes': [u'This field is required.']}, {}]
The extra argument also works when the formset is pre-filled with initial
data.
>>> initial = [{'choice': u'Calexico', 'votes': 100}]
>>> formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices')
>>> for form in formset.forms:
... print form.as_ul()
<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
<li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>
<li>Choice: <input type="text" name="choices-1-choice" /></li>
<li>Votes: <input type="text" name="choices-1-votes" /></li>
<li>Choice: <input type="text" name="choices-2-choice" /></li>
<li>Votes: <input type="text" name="choices-2-votes" /></li>
<li>Choice: <input type="text" name="choices-3-choice" /></li>
<li>Votes: <input type="text" name="choices-3-votes" /></li>
Make sure retrieving an empty form works, and it shows up in the form list
>>> formset.empty_form.empty_permitted
True
>>> print formset.empty_form.as_ul()
<li>Choice: <input type="text" name="choices-__prefix__-choice" /></li>
<li>Votes: <input type="text" name="choices-__prefix__-votes" /></li>
# FormSets with deletion ######################################################
We can easily add deletion ability to a FormSet with an argument to
formset_factory. This will add a boolean field to each form instance. When
that boolean field is True, the form will be in formset.deleted_forms
>>> ChoiceFormSet = formset_factory(Choice, can_delete=True)
>>> initial = [{'choice': u'Calexico', 'votes': 100}, {'choice': u'Fergie', 'votes': 900}]
>>> formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices')
>>> for form in formset.forms:
... print form.as_ul()
<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
<li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>
<li>Delete: <input type="checkbox" name="choices-0-DELETE" /></li>
<li>Choice: <input type="text" name="choices-1-choice" value="Fergie" /></li>
<li>Votes: <input type="text" name="choices-1-votes" value="900" /></li>
<li>Delete: <input type="checkbox" name="choices-1-DELETE" /></li>
<li>Choice: <input type="text" name="choices-2-choice" /></li>
<li>Votes: <input type="text" name="choices-2-votes" /></li>
<li>Delete: <input type="checkbox" name="choices-2-DELETE" /></li>
To delete something, we just need to set that form's special delete field to
'on'. Let's go ahead and delete Fergie.
>>> data = {
... 'choices-TOTAL_FORMS': '3', # the number of forms rendered
... 'choices-INITIAL_FORMS': '2', # the number of forms with initial data
... 'choices-MAX_NUM_FORMS': '0', # max number of forms
... 'choices-0-choice': 'Calexico',
... 'choices-0-votes': '100',
... 'choices-0-DELETE': '',
... 'choices-1-choice': 'Fergie',
... 'choices-1-votes': '900',
... 'choices-1-DELETE': 'on',
... 'choices-2-choice': '',
... 'choices-2-votes': '',
... 'choices-2-DELETE': '',
... }
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
>>> formset.is_valid()
True
>>> [form.cleaned_data for form in formset.forms]
[{'votes': 100, 'DELETE': False, 'choice': u'Calexico'}, {'votes': 900, 'DELETE': True, 'choice': u'Fergie'}, {}]
>>> [form.cleaned_data for form in formset.deleted_forms]
[{'votes': 900, 'DELETE': True, 'choice': u'Fergie'}]
If we fill a form with something and then we check the can_delete checkbox for
that form, that form's errors should not make the entire formset invalid since
it's going to be deleted.
>>> class CheckForm(Form):
... field = IntegerField(min_value=100)
>>> data = {
... 'check-TOTAL_FORMS': '3', # the number of forms rendered
... 'check-INITIAL_FORMS': '2', # the number of forms with initial data
... 'check-MAX_NUM_FORMS': '0', # max number of forms
... 'check-0-field': '200',
... 'check-0-DELETE': '',
... 'check-1-field': '50',
... 'check-1-DELETE': 'on',
... 'check-2-field': '',
... 'check-2-DELETE': '',
... }
>>> CheckFormSet = formset_factory(CheckForm, can_delete=True)
>>> formset = CheckFormSet(data, prefix='check')
>>> formset.is_valid()
True
If we remove the deletion flag now we will have our validation back.
>>> data['check-1-DELETE'] = ''
>>> formset = CheckFormSet(data, prefix='check')
>>> formset.is_valid()
False
Should be able to get deleted_forms from a valid formset even if a
deleted form would have been invalid.
>>> class Person(Form):
... name = CharField()
>>> PeopleForm = formset_factory(
... form=Person,
... can_delete=True)
>>> p = PeopleForm(
... {'form-0-name': u'', 'form-0-DELETE': u'on', # no name!
... 'form-TOTAL_FORMS': 1, 'form-INITIAL_FORMS': 1,
... 'form-MAX_NUM_FORMS': 1})
>>> p.is_valid()
True
>>> len(p.deleted_forms)
1
# FormSets with ordering ######################################################
We can also add ordering ability to a FormSet with an agrument to
formset_factory. This will add a integer field to each form instance. When
form validation succeeds, [form.cleaned_data for form in formset.forms] will have the data in the correct
order specified by the ordering fields. If a number is duplicated in the set
of ordering fields, for instance form 0 and form 3 are both marked as 1, then
the form index used as a secondary ordering criteria. In order to put
something at the front of the list, you'd need to set it's order to 0.
>>> ChoiceFormSet = formset_factory(Choice, can_order=True)
>>> initial = [{'choice': u'Calexico', 'votes': 100}, {'choice': u'Fergie', 'votes': 900}]
>>> formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices')
>>> for form in formset.forms:
... print form.as_ul()
<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
<li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>
<li>Order: <input type="text" name="choices-0-ORDER" value="1" /></li>
<li>Choice: <input type="text" name="choices-1-choice" value="Fergie" /></li>
<li>Votes: <input type="text" name="choices-1-votes" value="900" /></li>
<li>Order: <input type="text" name="choices-1-ORDER" value="2" /></li>
<li>Choice: <input type="text" name="choices-2-choice" /></li>
<li>Votes: <input type="text" name="choices-2-votes" /></li>
<li>Order: <input type="text" name="choices-2-ORDER" /></li>
>>> data = {
... 'choices-TOTAL_FORMS': '3', # the number of forms rendered
... 'choices-INITIAL_FORMS': '2', # the number of forms with initial data
... 'choices-MAX_NUM_FORMS': '0', # max number of forms
... 'choices-0-choice': 'Calexico',
... 'choices-0-votes': '100',
... 'choices-0-ORDER': '1',
... 'choices-1-choice': 'Fergie',
... 'choices-1-votes': '900',
... 'choices-1-ORDER': '2',
... 'choices-2-choice': 'The Decemberists',
... 'choices-2-votes': '500',
... 'choices-2-ORDER': '0',
... }
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
>>> formset.is_valid()
True
>>> for form in formset.ordered_forms:
... print form.cleaned_data
{'votes': 500, 'ORDER': 0, 'choice': u'The Decemberists'}
{'votes': 100, 'ORDER': 1, 'choice': u'Calexico'}
{'votes': 900, 'ORDER': 2, 'choice': u'Fergie'}
Ordering fields are allowed to be left blank, and if they *are* left blank,
they will be sorted below everything else.
>>> data = {
... 'choices-TOTAL_FORMS': '4', # the number of forms rendered
... 'choices-INITIAL_FORMS': '3', # the number of forms with initial data
... 'choices-MAX_NUM_FORMS': '0', # max number of forms
... 'choices-0-choice': 'Calexico',
... 'choices-0-votes': '100',
... 'choices-0-ORDER': '1',
... 'choices-1-choice': 'Fergie',
... 'choices-1-votes': '900',
... 'choices-1-ORDER': '2',
... 'choices-2-choice': 'The Decemberists',
... 'choices-2-votes': '500',
... 'choices-2-ORDER': '',
... 'choices-3-choice': 'Basia Bulat',
... 'choices-3-votes': '50',
... 'choices-3-ORDER': '',
... }
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
>>> formset.is_valid()
True
>>> for form in formset.ordered_forms:
... print form.cleaned_data
{'votes': 100, 'ORDER': 1, 'choice': u'Calexico'}
{'votes': 900, 'ORDER': 2, 'choice': u'Fergie'}
{'votes': 500, 'ORDER': None, 'choice': u'The Decemberists'}
{'votes': 50, 'ORDER': None, 'choice': u'Basia Bulat'}
Ordering should work with blank fieldsets.
>>> data = {
... 'choices-TOTAL_FORMS': '3', # the number of forms rendered
... 'choices-INITIAL_FORMS': '0', # the number of forms with initial data
... 'choices-MAX_NUM_FORMS': '0', # max number of forms
... }
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
>>> formset.is_valid()
True
>>> for form in formset.ordered_forms:
... print form.cleaned_data
# FormSets with ordering + deletion ###########################################
Let's try throwing ordering and deletion into the same form.
>>> ChoiceFormSet = formset_factory(Choice, can_order=True, can_delete=True)
>>> initial = [
... {'choice': u'Calexico', 'votes': 100},
... {'choice': u'Fergie', 'votes': 900},
... {'choice': u'The Decemberists', 'votes': 500},
... ]
>>> formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices')
>>> for form in formset.forms:
... print form.as_ul()
<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
<li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>
<li>Order: <input type="text" name="choices-0-ORDER" value="1" /></li>
<li>Delete: <input type="checkbox" name="choices-0-DELETE" /></li>
<li>Choice: <input type="text" name="choices-1-choice" value="Fergie" /></li>
<li>Votes: <input type="text" name="choices-1-votes" value="900" /></li>
<li>Order: <input type="text" name="choices-1-ORDER" value="2" /></li>
<li>Delete: <input type="checkbox" name="choices-1-DELETE" /></li>
<li>Choice: <input type="text" name="choices-2-choice" value="The Decemberists" /></li>
<li>Votes: <input type="text" name="choices-2-votes" value="500" /></li>
<li>Order: <input type="text" name="choices-2-ORDER" value="3" /></li>
<li>Delete: <input type="checkbox" name="choices-2-DELETE" /></li>
<li>Choice: <input type="text" name="choices-3-choice" /></li>
<li>Votes: <input type="text" name="choices-3-votes" /></li>
<li>Order: <input type="text" name="choices-3-ORDER" /></li>
<li>Delete: <input type="checkbox" name="choices-3-DELETE" /></li>
Let's delete Fergie, and put The Decemberists ahead of Calexico.
>>> data = {
... 'choices-TOTAL_FORMS': '4', # the number of forms rendered
... 'choices-INITIAL_FORMS': '3', # the number of forms with initial data
... 'choices-MAX_NUM_FORMS': '0', # max number of forms
... 'choices-0-choice': 'Calexico',
... 'choices-0-votes': '100',
... 'choices-0-ORDER': '1',
... 'choices-0-DELETE': '',
... 'choices-1-choice': 'Fergie',
... 'choices-1-votes': '900',
... 'choices-1-ORDER': '2',
... 'choices-1-DELETE': 'on',
... 'choices-2-choice': 'The Decemberists',
... 'choices-2-votes': '500',
... 'choices-2-ORDER': '0',
... 'choices-2-DELETE': '',
... 'choices-3-choice': '',
... 'choices-3-votes': '',
... 'choices-3-ORDER': '',
... 'choices-3-DELETE': '',
... }
>>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
>>> formset.is_valid()
True
>>> for form in formset.ordered_forms:
... print form.cleaned_data
{'votes': 500, 'DELETE': False, 'ORDER': 0, 'choice': u'The Decemberists'}
{'votes': 100, 'DELETE': False, 'ORDER': 1, 'choice': u'Calexico'}
>>> [form.cleaned_data for form in formset.deleted_forms]
[{'votes': 900, 'DELETE': True, 'ORDER': 2, 'choice': u'Fergie'}]
Should be able to get ordered forms from a valid formset even if a
deleted form would have been invalid.
>>> class Person(Form):
... name = CharField()
>>> PeopleForm = formset_factory(
... form=Person,
... can_delete=True,
... can_order=True)
>>> p = PeopleForm(
... {'form-0-name': u'', 'form-0-DELETE': u'on', # no name!
... 'form-TOTAL_FORMS': 1, 'form-INITIAL_FORMS': 1,
... 'form-MAX_NUM_FORMS': 1})
>>> p.is_valid()
True
>>> p.ordered_forms
[]
# FormSet clean hook ##########################################################
FormSets have a hook for doing extra validation that shouldn't be tied to any
particular form. It follows the same pattern as the clean hook on Forms.
Let's define a FormSet that takes a list of favorite drinks, but raises am
error if there are any duplicates.
>>> class FavoriteDrinkForm(Form):
... name = CharField()
...
>>> class BaseFavoriteDrinksFormSet(BaseFormSet):
... def clean(self):
... seen_drinks = []
... for drink in self.cleaned_data:
... if drink['name'] in seen_drinks:
... raise ValidationError('You may only specify a drink once.')
... seen_drinks.append(drink['name'])
...
>>> FavoriteDrinksFormSet = formset_factory(FavoriteDrinkForm,
... formset=BaseFavoriteDrinksFormSet, extra=3)
We start out with a some duplicate data.
>>> data = {
... 'drinks-TOTAL_FORMS': '2', # the number of forms rendered
... 'drinks-INITIAL_FORMS': '0', # the number of forms with initial data
... 'drinks-MAX_NUM_FORMS': '0', # max number of forms
... 'drinks-0-name': 'Gin and Tonic',
... 'drinks-1-name': 'Gin and Tonic',
... }
>>> formset = FavoriteDrinksFormSet(data, prefix='drinks')
>>> formset.is_valid()
False
Any errors raised by formset.clean() are available via the
formset.non_form_errors() method.
>>> for error in formset.non_form_errors():
... print error
You may only specify a drink once.
Make sure we didn't break the valid case.
>>> data = {
... 'drinks-TOTAL_FORMS': '2', # the number of forms rendered
... 'drinks-INITIAL_FORMS': '0', # the number of forms with initial data
... 'drinks-MAX_NUM_FORMS': '0', # max number of forms
... 'drinks-0-name': 'Gin and Tonic',
... 'drinks-1-name': 'Bloody Mary',
... }
>>> formset = FavoriteDrinksFormSet(data, prefix='drinks')
>>> formset.is_valid()
True
>>> for error in formset.non_form_errors():
... print error
# Limiting the maximum number of forms ########################################
# Base case for max_num.
# When not passed, max_num will take its default value of None, i.e. unlimited
# number of forms, only controlled by the value of the extra parameter.
>>> LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=3)
>>> formset = LimitedFavoriteDrinkFormSet()
>>> for form in formset.forms:
... print form
<tr><th><label for="id_form-0-name">Name:</label></th><td><input type="text" name="form-0-name" id="id_form-0-name" /></td></tr>
<tr><th><label for="id_form-1-name">Name:</label></th><td><input type="text" name="form-1-name" id="id_form-1-name" /></td></tr>
<tr><th><label for="id_form-2-name">Name:</label></th><td><input type="text" name="form-2-name" id="id_form-2-name" /></td></tr>
# If max_num is 0 then no form is rendered at all.
>>> LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=3, max_num=0)
>>> formset = LimitedFavoriteDrinkFormSet()
>>> for form in formset.forms:
... print form
>>> LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=5, max_num=2)
>>> formset = LimitedFavoriteDrinkFormSet()
>>> for form in formset.forms:
... print form
<tr><th><label for="id_form-0-name">Name:</label></th><td><input type="text" name="form-0-name" id="id_form-0-name" /></td></tr>
<tr><th><label for="id_form-1-name">Name:</label></th><td><input type="text" name="form-1-name" id="id_form-1-name" /></td></tr>
# Ensure that max_num has no effect when extra is less than max_num.
>>> LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=1, max_num=2)
>>> formset = LimitedFavoriteDrinkFormSet()
>>> for form in formset.forms:
... print form
<tr><th><label for="id_form-0-name">Name:</label></th><td><input type="text" name="form-0-name" id="id_form-0-name" /></td></tr>
# max_num with initial data
# When not passed, max_num will take its default value of None, i.e. unlimited
# number of forms, only controlled by the values of the initial and extra
# parameters.
>>> initial = [
... {'name': 'Fernet and Coke'},
... ]
>>> LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=1)
>>> formset = LimitedFavoriteDrinkFormSet(initial=initial)
>>> for form in formset.forms:
... print form
<tr><th><label for="id_form-0-name">Name:</label></th><td><input type="text" name="form-0-name" value="Fernet and Coke" id="id_form-0-name" /></td></tr>
<tr><th><label for="id_form-1-name">Name:</label></th><td><input type="text" name="form-1-name" id="id_form-1-name" /></td></tr>
# If max_num is 0 then no form is rendered at all, even if extra and initial
# are specified.
>>> initial = [
... {'name': 'Fernet and Coke'},
... {'name': 'Bloody Mary'},
... ]
>>> LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=1, max_num=0)
>>> formset = LimitedFavoriteDrinkFormSet(initial=initial)
>>> for form in formset.forms:
... print form
# More initial forms than max_num will result in only the first max_num of
# them to be displayed with no extra forms.
>>> initial = [
... {'name': 'Gin Tonic'},
... {'name': 'Bloody Mary'},
... {'name': 'Jack and Coke'},
... ]
>>> LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=1, max_num=2)
>>> formset = LimitedFavoriteDrinkFormSet(initial=initial)
>>> for form in formset.forms:
... print form
<tr><th><label for="id_form-0-name">Name:</label></th><td><input type="text" name="form-0-name" value="Gin Tonic" id="id_form-0-name" /></td></tr>
<tr><th><label for="id_form-1-name">Name:</label></th><td><input type="text" name="form-1-name" value="Bloody Mary" id="id_form-1-name" /></td></tr>
# One form from initial and extra=3 with max_num=2 should result in the one
# initial form and one extra.
>>> initial = [
... {'name': 'Gin Tonic'},
... ]
>>> LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=3, max_num=2)
>>> formset = LimitedFavoriteDrinkFormSet(initial=initial)
>>> for form in formset.forms:
... print form
<tr><th><label for="id_form-0-name">Name:</label></th><td><input type="text" name="form-0-name" value="Gin Tonic" id="id_form-0-name" /></td></tr>
<tr><th><label for="id_form-1-name">Name:</label></th><td><input type="text" name="form-1-name" id="id_form-1-name" /></td></tr>
# Regression test for #6926 ##################################################
Make sure the management form has the correct prefix.
>>> formset = FavoriteDrinksFormSet()
>>> formset.management_form.prefix
'form'
>>> formset = FavoriteDrinksFormSet(data={})
>>> formset.management_form.prefix
'form'
>>> formset = FavoriteDrinksFormSet(initial={})
>>> formset.management_form.prefix
'form'
# Regression test for #12878 #################################################
>>> data = {
... 'drinks-TOTAL_FORMS': '2', # the number of forms rendered
... 'drinks-INITIAL_FORMS': '0', # the number of forms with initial data
... 'drinks-MAX_NUM_FORMS': '0', # max number of forms
... 'drinks-0-name': 'Gin and Tonic',
... 'drinks-1-name': 'Gin and Tonic',
... }
>>> formset = FavoriteDrinksFormSet(data, prefix='drinks')
>>> formset.is_valid()
False
>>> print formset.non_form_errors()
<ul class="errorlist"><li>You may only specify a drink once.</li></ul>
"""
data = {
'choices-TOTAL_FORMS': '1', # the number of forms rendered
'choices-INITIAL_FORMS': '0', # the number of forms with initial data
'choices-MAX_NUM_FORMS': '0', # max number of forms
'choices-0-choice': 'Calexico',
'choices-0-votes': '100',
}
class Choice(Form):
choice = CharField()
votes = IntegerField()
ChoiceFormSet = formset_factory(Choice)
class FormsetAsFooTests(TestCase):
def test_as_table(self):
formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
self.assertEqual(formset.as_table(),"""<input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" /><input type="hidden" name="choices-MAX_NUM_FORMS" value="0" />
<tr><th>Choice:</th><td><input type="text" name="choices-0-choice" value="Calexico" /></td></tr>
<tr><th>Votes:</th><td><input type="text" name="choices-0-votes" value="100" /></td></tr>""")
def test_as_p(self):
formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
self.assertEqual(formset.as_p(),"""<input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" /><input type="hidden" name="choices-MAX_NUM_FORMS" value="0" />
<p>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></p>
<p>Votes: <input type="text" name="choices-0-votes" value="100" /></p>""")
def test_as_ul(self):
formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
self.assertEqual(formset.as_ul(),"""<input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" /><input type="hidden" name="choices-MAX_NUM_FORMS" value="0" />
<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
<li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>""")

View File

@ -51,7 +51,7 @@ class BETests(TestCase):
self.assertEqual(u'0412.34.56.78', f.clean('0412.34.56.78'))
self.assertEqual(u'012345678', f.clean('012345678'))
self.assertEqual(u'0412345678', f.clean('0412345678'))
err_message = "[u'Enter a valid phone number in one of the formats 0x xxx xx xx, 0xx xx xx xx, 04xx xx xx xx, 0x/xxx.xx.xx, 0xx/xx.xx.xx, 04xx/xx.xx.xx, 0xxxxxxxx, 04xxxxxxxx, 0x.xxx.xx.xx, 0xx.xx.xx.xx, 04xx.xx.xx.xx.']"
err_message = "[u'Enter a valid phone number in one of the formats 0x xxx xx xx, 0xx xx xx xx, 04xx xx xx xx, 0x/xxx.xx.xx, 0xx/xx.xx.xx, 04xx/xx.xx.xx, 0x.xxx.xx.xx, 0xx.xx.xx.xx, 04xx.xx.xx.xx, 0xxxxxxxx or 04xxxxxxxx.']"
self.assertRaisesErrorWithMessage(ValidationError, err_message, f.clean, '01234567')
self.assertRaisesErrorWithMessage(ValidationError, err_message, f.clean, '12/345.67.89')
self.assertRaisesErrorWithMessage(ValidationError, err_message, f.clean, '012/345.678.90')
@ -75,7 +75,7 @@ class BETests(TestCase):
self.assertEqual(u'012345678', f.clean('012345678'))
self.assertEqual(u'0412345678', f.clean('0412345678'))
self.assertEqual(u'', f.clean(''))
err_message = "[u'Enter a valid phone number in one of the formats 0x xxx xx xx, 0xx xx xx xx, 04xx xx xx xx, 0x/xxx.xx.xx, 0xx/xx.xx.xx, 04xx/xx.xx.xx, 0xxxxxxxx, 04xxxxxxxx, 0x.xxx.xx.xx, 0xx.xx.xx.xx, 04xx.xx.xx.xx.']"
err_message = "[u'Enter a valid phone number in one of the formats 0x xxx xx xx, 0xx xx xx xx, 04xx xx xx xx, 0x/xxx.xx.xx, 0xx/xx.xx.xx, 04xx/xx.xx.xx, 0x.xxx.xx.xx, 0xx.xx.xx.xx, 04xx.xx.xx.xx, 0xxxxxxxx or 04xxxxxxxx.']"
self.assertRaisesErrorWithMessage(ValidationError, err_message, f.clean, '01234567')
self.assertRaisesErrorWithMessage(ValidationError, err_message, f.clean, '12/345.67.89')
self.assertRaisesErrorWithMessage(ValidationError, err_message, f.clean, '012/345.678.90')
@ -85,10 +85,10 @@ class BETests(TestCase):
self.assertRaisesErrorWithMessage(ValidationError, err_message, f.clean, '012/34 56 789')
self.assertRaisesErrorWithMessage(ValidationError, err_message, f.clean, '012.34 56 789')
def test_phone_number_field(self):
def test_region_field(self):
w = BERegionSelect()
self.assertEqual(u'<select name="regions">\n<option value="BRU">Brussels Capital Region</option>\n<option value="VLG" selected="selected">Flemish Region</option>\n<option value="WAL">Wallonia</option>\n</select>', w.render('regions', 'VLG'))
def test_phone_number_field(self):
def test_province_field(self):
w = BEProvinceSelect()
self.assertEqual(u'<select name="provinces">\n<option value="VAN">Antwerp</option>\n<option value="BRU">Brussels</option>\n<option value="VOV">East Flanders</option>\n<option value="VBR">Flemish Brabant</option>\n<option value="WHT">Hainaut</option>\n<option value="WLG" selected="selected">Liege</option>\n<option value="VLI">Limburg</option>\n<option value="WLX">Luxembourg</option>\n<option value="WNA">Namur</option>\n<option value="WBR">Walloon Brabant</option>\n<option value="VWV">West Flanders</option>\n</select>', w.render('provinces', 'WLG'))

View File

@ -1,7 +1,4 @@
# -*- coding: utf-8 -*-
from extra import tests as extra_tests
from forms import tests as form_tests
from error_messages import tests as custom_error_message_tests
from localflavor.ar import tests as localflavor_ar_tests
from localflavor.at import tests as localflavor_at_tests
from localflavor.au import tests as localflavor_au_tests
@ -32,25 +29,10 @@ from localflavor.uk import tests as localflavor_uk_tests
from localflavor.us import tests as localflavor_us_tests
from localflavor.uy import tests as localflavor_uy_tests
from localflavor.za import tests as localflavor_za_tests
from regressions import tests as regression_tests
from util import tests as util_tests
from widgets import tests as widgets_tests
from formsets import tests as formset_tests
from media import media_tests
from formsets import FormsetAsFooTests
from fields import FieldsTests
from validators import TestFieldWithValidators
from widgets import WidgetTests, ClearableFileInputTests
from localflavor.be import BETests
from input_formats import *
__test__ = {
'extra_tests': extra_tests,
'form_tests': form_tests,
'custom_error_message_tests': custom_error_message_tests,
'localflavor_ar_tests': localflavor_ar_tests,
'localflavor_at_tests': localflavor_at_tests,
'localflavor_au_tests': localflavor_au_tests,
@ -80,11 +62,6 @@ __test__ = {
'localflavor_us_tests': localflavor_us_tests,
'localflavor_uy_tests': localflavor_uy_tests,
'localflavor_za_tests': localflavor_za_tests,
'regression_tests': regression_tests,
'formset_tests': formset_tests,
'media_tests': media_tests,
'util_tests': util_tests,
'widgets_tests': widgets_tests,
}
if __name__ == "__main__":

View File

@ -1,385 +0,0 @@
# -*- coding: utf-8 -*-
# Tests for the media handling on widgets and forms
media_tests = r"""
>>> from django.forms import TextInput, Media, TextInput, CharField, Form, MultiWidget
>>> from django.conf import settings
>>> ORIGINAL_MEDIA_URL = settings.MEDIA_URL
>>> settings.MEDIA_URL = 'http://media.example.com/media/'
# Check construction of media objects
>>> m = Media(css={'all': ('path/to/css1','/path/to/css2')}, js=('/path/to/js1','http://media.other.com/path/to/js2','https://secure.other.com/path/to/js3'))
>>> print m
<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
>>> class Foo:
... css = {
... 'all': ('path/to/css1','/path/to/css2')
... }
... js = ('/path/to/js1','http://media.other.com/path/to/js2','https://secure.other.com/path/to/js3')
>>> m3 = Media(Foo)
>>> print m3
<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
>>> m3 = Media(Foo)
>>> print m3
<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
# A widget can exist without a media definition
>>> class MyWidget(TextInput):
... pass
>>> w = MyWidget()
>>> print w.media
<BLANKLINE>
###############################################################
# DSL Class-based media definitions
###############################################################
# A widget can define media if it needs to.
# Any absolute path will be preserved; relative paths are combined
# with the value of settings.MEDIA_URL
>>> class MyWidget1(TextInput):
... class Media:
... css = {
... 'all': ('path/to/css1','/path/to/css2')
... }
... js = ('/path/to/js1','http://media.other.com/path/to/js2','https://secure.other.com/path/to/js3')
>>> w1 = MyWidget1()
>>> print w1.media
<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
# Media objects can be interrogated by media type
>>> print w1.media['css']
<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
>>> print w1.media['js']
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
# Media objects can be combined. Any given media resource will appear only
# once. Duplicated media definitions are ignored.
>>> class MyWidget2(TextInput):
... class Media:
... css = {
... 'all': ('/path/to/css2','/path/to/css3')
... }
... js = ('/path/to/js1','/path/to/js4')
>>> class MyWidget3(TextInput):
... class Media:
... css = {
... 'all': ('/path/to/css3','path/to/css1')
... }
... js = ('/path/to/js1','/path/to/js4')
>>> w2 = MyWidget2()
>>> w3 = MyWidget3()
>>> print w1.media + w2.media + w3.media
<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
<script type="text/javascript" src="/path/to/js4"></script>
# Check that media addition hasn't affected the original objects
>>> print w1.media
<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
# Regression check for #12879: specifying the same CSS or JS file
# multiple times in a single Media instance should result in that file
# only being included once.
>>> class MyWidget4(TextInput):
... class Media:
... css = {'all': ('/path/to/css1', '/path/to/css1')}
... js = ('/path/to/js1', '/path/to/js1')
>>> w4 = MyWidget4()
>>> print w4.media
<link href="/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
###############################################################
# Property-based media definitions
###############################################################
# Widget media can be defined as a property
>>> class MyWidget4(TextInput):
... def _media(self):
... return Media(css={'all': ('/some/path',)}, js = ('/some/js',))
... media = property(_media)
>>> w4 = MyWidget4()
>>> print w4.media
<link href="/some/path" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/some/js"></script>
# Media properties can reference the media of their parents
>>> class MyWidget5(MyWidget4):
... def _media(self):
... return super(MyWidget5, self).media + Media(css={'all': ('/other/path',)}, js = ('/other/js',))
... media = property(_media)
>>> w5 = MyWidget5()
>>> print w5.media
<link href="/some/path" type="text/css" media="all" rel="stylesheet" />
<link href="/other/path" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/some/js"></script>
<script type="text/javascript" src="/other/js"></script>
# Media properties can reference the media of their parents,
# even if the parent media was defined using a class
>>> class MyWidget6(MyWidget1):
... def _media(self):
... return super(MyWidget6, self).media + Media(css={'all': ('/other/path',)}, js = ('/other/js',))
... media = property(_media)
>>> w6 = MyWidget6()
>>> print w6.media
<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<link href="/other/path" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
<script type="text/javascript" src="/other/js"></script>
###############################################################
# Inheritance of media
###############################################################
# If a widget extends another but provides no media definition, it inherits the parent widget's media
>>> class MyWidget7(MyWidget1):
... pass
>>> w7 = MyWidget7()
>>> print w7.media
<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
# If a widget extends another but defines media, it extends the parent widget's media by default
>>> class MyWidget8(MyWidget1):
... class Media:
... css = {
... 'all': ('/path/to/css3','path/to/css1')
... }
... js = ('/path/to/js1','/path/to/js4')
>>> w8 = MyWidget8()
>>> print w8.media
<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
<script type="text/javascript" src="/path/to/js4"></script>
# If a widget extends another but defines media, it extends the parents widget's media,
# even if the parent defined media using a property.
>>> class MyWidget9(MyWidget4):
... class Media:
... css = {
... 'all': ('/other/path',)
... }
... js = ('/other/js',)
>>> w9 = MyWidget9()
>>> print w9.media
<link href="/some/path" type="text/css" media="all" rel="stylesheet" />
<link href="/other/path" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/some/js"></script>
<script type="text/javascript" src="/other/js"></script>
# A widget can disable media inheritance by specifying 'extend=False'
>>> class MyWidget10(MyWidget1):
... class Media:
... extend = False
... css = {
... 'all': ('/path/to/css3','path/to/css1')
... }
... js = ('/path/to/js1','/path/to/js4')
>>> w10 = MyWidget10()
>>> print w10.media
<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="/path/to/js4"></script>
# A widget can explicitly enable full media inheritance by specifying 'extend=True'
>>> class MyWidget11(MyWidget1):
... class Media:
... extend = True
... css = {
... 'all': ('/path/to/css3','path/to/css1')
... }
... js = ('/path/to/js1','/path/to/js4')
>>> w11 = MyWidget11()
>>> print w11.media
<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
<script type="text/javascript" src="/path/to/js4"></script>
# A widget can enable inheritance of one media type by specifying extend as a tuple
>>> class MyWidget12(MyWidget1):
... class Media:
... extend = ('css',)
... css = {
... 'all': ('/path/to/css3','path/to/css1')
... }
... js = ('/path/to/js1','/path/to/js4')
>>> w12 = MyWidget12()
>>> print w12.media
<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="/path/to/js4"></script>
###############################################################
# Multi-media handling for CSS
###############################################################
# A widget can define CSS media for multiple output media types
>>> class MultimediaWidget(TextInput):
... class Media:
... css = {
... 'screen, print': ('/file1','/file2'),
... 'screen': ('/file3',),
... 'print': ('/file4',)
... }
... js = ('/path/to/js1','/path/to/js4')
>>> multimedia = MultimediaWidget()
>>> print multimedia.media
<link href="/file4" type="text/css" media="print" rel="stylesheet" />
<link href="/file3" type="text/css" media="screen" rel="stylesheet" />
<link href="/file1" type="text/css" media="screen, print" rel="stylesheet" />
<link href="/file2" type="text/css" media="screen, print" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="/path/to/js4"></script>
###############################################################
# Multiwidget media handling
###############################################################
# MultiWidgets have a default media definition that gets all the
# media from the component widgets
>>> class MyMultiWidget(MultiWidget):
... def __init__(self, attrs=None):
... widgets = [MyWidget1, MyWidget2, MyWidget3]
... super(MyMultiWidget, self).__init__(widgets, attrs)
>>> mymulti = MyMultiWidget()
>>> print mymulti.media
<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
<script type="text/javascript" src="/path/to/js4"></script>
###############################################################
# Media processing for forms
###############################################################
# You can ask a form for the media required by its widgets.
>>> class MyForm(Form):
... field1 = CharField(max_length=20, widget=MyWidget1())
... field2 = CharField(max_length=20, widget=MyWidget2())
>>> f1 = MyForm()
>>> print f1.media
<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
<script type="text/javascript" src="/path/to/js4"></script>
# Form media can be combined to produce a single media definition.
>>> class AnotherForm(Form):
... field3 = CharField(max_length=20, widget=MyWidget3())
>>> f2 = AnotherForm()
>>> print f1.media + f2.media
<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
<script type="text/javascript" src="/path/to/js4"></script>
# Forms can also define media, following the same rules as widgets.
>>> class FormWithMedia(Form):
... field1 = CharField(max_length=20, widget=MyWidget1())
... field2 = CharField(max_length=20, widget=MyWidget2())
... class Media:
... js = ('/some/form/javascript',)
... css = {
... 'all': ('/some/form/css',)
... }
>>> f3 = FormWithMedia()
>>> print f3.media
<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
<link href="/some/form/css" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
<script type="text/javascript" src="/path/to/js4"></script>
<script type="text/javascript" src="/some/form/javascript"></script>
# Media works in templates
>>> from django.template import Template, Context
>>> Template("{{ form.media.js }}{{ form.media.css }}").render(Context({'form': f3}))
u'<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
<script type="text/javascript" src="/path/to/js4"></script>
<script type="text/javascript" src="/some/form/javascript"></script><link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
<link href="/some/form/css" type="text/css" media="all" rel="stylesheet" />'
>>> settings.MEDIA_URL = ORIGINAL_MEDIA_URL
"""

View File

@ -1,14 +1,9 @@
# -*- coding: utf-8 -*-
import datetime
import shutil
import tempfile
from django.db import models
# Can't import as "forms" due to implementation details in the test suite (the
# current file is called "forms" and is already imported).
from django import forms as django_forms
from django.core.files.storage import FileSystemStorage
from django.test import TestCase
temp_storage_location = tempfile.mkdtemp()
temp_storage = FileSystemStorage(location=temp_storage_location)
@ -56,182 +51,11 @@ class ChoiceFieldModel(models.Model):
multi_choice_int = models.ManyToManyField(ChoiceOptionModel, blank=False, related_name='multi_choice_int',
default=lambda: [1])
class ChoiceFieldForm(django_forms.ModelForm):
class Meta:
model = ChoiceFieldModel
class FileModel(models.Model):
file = models.FileField(storage=temp_storage, upload_to='tests')
class FileForm(django_forms.Form):
file1 = django_forms.FileField()
class Group(models.Model):
name = models.CharField(max_length=10)
def __unicode__(self):
return u'%s' % self.name
class TestTicket12510(TestCase):
''' It is not necessary to generate choices for ModelChoiceField (regression test for #12510). '''
def setUp(self):
self.groups = [Group.objects.create(name=name) for name in 'abc']
def test_choices_not_fetched_when_not_rendering(self):
def test():
field = django_forms.ModelChoiceField(Group.objects.order_by('-name'))
self.assertEqual('a', field.clean(self.groups[0].pk).name)
# only one query is required to pull the model from DB
self.assertNumQueries(1, test)
class ModelFormCallableModelDefault(TestCase):
def test_no_empty_option(self):
"If a model's ForeignKey has blank=False and a default, no empty option is created (Refs #10792)."
option = ChoiceOptionModel.objects.create(name='default')
choices = list(ChoiceFieldForm().fields['choice'].choices)
self.assertEquals(len(choices), 1)
self.assertEquals(choices[0], (option.pk, unicode(option)))
def test_callable_initial_value(self):
"The initial value for a callable default returning a queryset is the pk (refs #13769)"
obj1 = ChoiceOptionModel.objects.create(id=1, name='default')
obj2 = ChoiceOptionModel.objects.create(id=2, name='option 2')
obj3 = ChoiceOptionModel.objects.create(id=3, name='option 3')
self.assertEquals(ChoiceFieldForm().as_p(), """<p><label for="id_choice">Choice:</label> <select name="choice" id="id_choice">
<option value="1" selected="selected">ChoiceOption 1</option>
<option value="2">ChoiceOption 2</option>
<option value="3">ChoiceOption 3</option>
</select><input type="hidden" name="initial-choice" value="1" id="initial-id_choice" /></p>
<p><label for="id_choice_int">Choice int:</label> <select name="choice_int" id="id_choice_int">
<option value="1" selected="selected">ChoiceOption 1</option>
<option value="2">ChoiceOption 2</option>
<option value="3">ChoiceOption 3</option>
</select><input type="hidden" name="initial-choice_int" value="1" id="initial-id_choice_int" /></p>
<p><label for="id_multi_choice">Multi choice:</label> <select multiple="multiple" name="multi_choice" id="id_multi_choice">
<option value="1" selected="selected">ChoiceOption 1</option>
<option value="2">ChoiceOption 2</option>
<option value="3">ChoiceOption 3</option>
</select><input type="hidden" name="initial-multi_choice" value="1" id="initial-id_multi_choice_0" /> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></p>
<p><label for="id_multi_choice_int">Multi choice int:</label> <select multiple="multiple" name="multi_choice_int" id="id_multi_choice_int">
<option value="1" selected="selected">ChoiceOption 1</option>
<option value="2">ChoiceOption 2</option>
<option value="3">ChoiceOption 3</option>
</select><input type="hidden" name="initial-multi_choice_int" value="1" id="initial-id_multi_choice_int_0" /> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></p>""")
def test_initial_instance_value(self):
"Initial instances for model fields may also be instances (refs #7287)"
obj1 = ChoiceOptionModel.objects.create(id=1, name='default')
obj2 = ChoiceOptionModel.objects.create(id=2, name='option 2')
obj3 = ChoiceOptionModel.objects.create(id=3, name='option 3')
self.assertEquals(ChoiceFieldForm(initial={
'choice': obj2,
'choice_int': obj2,
'multi_choice': [obj2,obj3],
'multi_choice_int': ChoiceOptionModel.objects.exclude(name="default"),
}).as_p(), """<p><label for="id_choice">Choice:</label> <select name="choice" id="id_choice">
<option value="1">ChoiceOption 1</option>
<option value="2" selected="selected">ChoiceOption 2</option>
<option value="3">ChoiceOption 3</option>
</select><input type="hidden" name="initial-choice" value="2" id="initial-id_choice" /></p>
<p><label for="id_choice_int">Choice int:</label> <select name="choice_int" id="id_choice_int">
<option value="1">ChoiceOption 1</option>
<option value="2" selected="selected">ChoiceOption 2</option>
<option value="3">ChoiceOption 3</option>
</select><input type="hidden" name="initial-choice_int" value="2" id="initial-id_choice_int" /></p>
<p><label for="id_multi_choice">Multi choice:</label> <select multiple="multiple" name="multi_choice" id="id_multi_choice">
<option value="1">ChoiceOption 1</option>
<option value="2" selected="selected">ChoiceOption 2</option>
<option value="3" selected="selected">ChoiceOption 3</option>
</select><input type="hidden" name="initial-multi_choice" value="2" id="initial-id_multi_choice_0" />
<input type="hidden" name="initial-multi_choice" value="3" id="initial-id_multi_choice_1" /> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></p>
<p><label for="id_multi_choice_int">Multi choice int:</label> <select multiple="multiple" name="multi_choice_int" id="id_multi_choice_int">
<option value="1">ChoiceOption 1</option>
<option value="2" selected="selected">ChoiceOption 2</option>
<option value="3" selected="selected">ChoiceOption 3</option>
</select><input type="hidden" name="initial-multi_choice_int" value="2" id="initial-id_multi_choice_int_0" />
<input type="hidden" name="initial-multi_choice_int" value="3" id="initial-id_multi_choice_int_1" /> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></p>""")
__test__ = {'API_TESTS': """
>>> from django.forms.models import ModelForm
>>> from django.core.files.uploadedfile import SimpleUploadedFile
# FileModel with unicode filename and data #########################
>>> f = FileForm(data={}, files={'file1': SimpleUploadedFile('我隻氣墊船裝滿晒鱔.txt', 'मेरी मँडराने वाली नाव सर्पमीनों से भरी ह')}, auto_id=False)
>>> f.is_valid()
True
>>> f.cleaned_data
{'file1': <SimpleUploadedFile: 我隻氣墊船裝滿晒鱔.txt (text/plain)>}
>>> m = FileModel.objects.create(file=f.cleaned_data['file1'])
# It's enough that m gets created without error. Preservation of the exotic name is checked
# in a file_uploads test; it's hard to do that correctly with doctest's unicode issues. So
# we create and then immediately delete m so as to not leave the exotically named file around
# for shutil.rmtree (on Windows) to have trouble with later.
>>> m.delete()
# Boundary conditions on a PostitiveIntegerField #########################
>>> class BoundaryForm(ModelForm):
... class Meta:
... model = BoundaryModel
>>> f = BoundaryForm({'positive_integer': 100})
>>> f.is_valid()
True
>>> f = BoundaryForm({'positive_integer': 0})
>>> f.is_valid()
True
>>> f = BoundaryForm({'positive_integer': -100})
>>> f.is_valid()
False
# Formfield initial values ########
If the model has default values for some fields, they are used as the formfield
initial values.
>>> class DefaultsForm(ModelForm):
... class Meta:
... model = Defaults
>>> DefaultsForm().fields['name'].initial
u'class default value'
>>> DefaultsForm().fields['def_date'].initial
datetime.date(1980, 1, 1)
>>> DefaultsForm().fields['value'].initial
42
>>> r1 = DefaultsForm()['callable_default'].as_widget()
>>> r2 = DefaultsForm()['callable_default'].as_widget()
>>> r1 == r2
False
In a ModelForm that is passed an instance, the initial values come from the
instance's values, not the model's defaults.
>>> foo_instance = Defaults(name=u'instance value', def_date=datetime.date(1969, 4, 4), value=12)
>>> instance_form = DefaultsForm(instance=foo_instance)
>>> instance_form.initial['name']
u'instance value'
>>> instance_form.initial['def_date']
datetime.date(1969, 4, 4)
>>> instance_form.initial['value']
12
>>> from django.forms import CharField
>>> class ExcludingForm(ModelForm):
... name = CharField(max_length=255)
... class Meta:
... model = Defaults
... exclude = ['name', 'callable_default']
>>> f = ExcludingForm({'name': u'Hello', 'value': 99, 'def_date': datetime.date(1999, 3, 2)})
>>> f.is_valid()
True
>>> f.cleaned_data['name']
u'Hello'
>>> obj = f.save()
>>> obj.name
u'class default value'
>>> obj.value
99
>>> obj.def_date
datetime.date(1999, 3, 2)
>>> shutil.rmtree(temp_storage_location)
"""}

View File

@ -1,135 +0,0 @@
# -*- coding: utf-8 -*-
# Tests to prevent against recurrences of earlier bugs.
tests = r"""
It should be possible to re-use attribute dictionaries (#3810)
>>> from django.forms import *
>>> extra_attrs = {'class': 'special'}
>>> class TestForm(Form):
... f1 = CharField(max_length=10, widget=TextInput(attrs=extra_attrs))
... f2 = CharField(widget=TextInput(attrs=extra_attrs))
>>> TestForm(auto_id=False).as_p()
u'<p>F1: <input type="text" class="special" name="f1" maxlength="10" /></p>\n<p>F2: <input type="text" class="special" name="f2" /></p>'
#######################
# Tests for form i18n #
#######################
There were some problems with form translations in #3600
>>> from django.utils.translation import ugettext_lazy, activate, deactivate
>>> class SomeForm(Form):
... username = CharField(max_length=10, label=ugettext_lazy('Username'))
>>> f = SomeForm()
>>> print f.as_p()
<p><label for="id_username">Username:</label> <input id="id_username" type="text" name="username" maxlength="10" /></p>
Translations are done at rendering time, so multi-lingual apps can define forms
early and still send back the right translation.
>>> activate('de')
>>> print f.as_p()
<p><label for="id_username">Benutzername:</label> <input id="id_username" type="text" name="username" maxlength="10" /></p>
>>> activate('pl')
>>> f.as_p()
u'<p><label for="id_username">Nazwa u\u017cytkownika:</label> <input id="id_username" type="text" name="username" maxlength="10" /></p>'
>>> deactivate()
There was some problems with form translations in #5216
>>> class SomeForm(Form):
... field_1 = CharField(max_length=10, label=ugettext_lazy('field_1'))
... field_2 = CharField(max_length=10, label=ugettext_lazy('field_2'), widget=TextInput(attrs={'id': 'field_2_id'}))
>>> f = SomeForm()
>>> print f['field_1'].label_tag()
<label for="id_field_1">field_1</label>
>>> print f['field_2'].label_tag()
<label for="field_2_id">field_2</label>
Unicode decoding problems...
>>> GENDERS = ((u'\xc5', u'En tied\xe4'), (u'\xf8', u'Mies'), (u'\xdf', u'Nainen'))
>>> class SomeForm(Form):
... somechoice = ChoiceField(choices=GENDERS, widget=RadioSelect(), label=u'\xc5\xf8\xdf')
>>> f = SomeForm()
>>> f.as_p()
u'<p><label for="id_somechoice_0">\xc5\xf8\xdf:</label> <ul>\n<li><label for="id_somechoice_0"><input type="radio" id="id_somechoice_0" value="\xc5" name="somechoice" /> En tied\xe4</label></li>\n<li><label for="id_somechoice_1"><input type="radio" id="id_somechoice_1" value="\xf8" name="somechoice" /> Mies</label></li>\n<li><label for="id_somechoice_2"><input type="radio" id="id_somechoice_2" value="\xdf" name="somechoice" /> Nainen</label></li>\n</ul></p>'
Testing choice validation with UTF-8 bytestrings as input (these are the
Russian abbreviations "мес." and "шт.".
>>> UNITS = (('\xd0\xbc\xd0\xb5\xd1\x81.', '\xd0\xbc\xd0\xb5\xd1\x81.'), ('\xd1\x88\xd1\x82.', '\xd1\x88\xd1\x82.'))
>>> f = ChoiceField(choices=UNITS)
>>> f.clean(u'\u0448\u0442.')
u'\u0448\u0442.'
>>> f.clean('\xd1\x88\xd1\x82.')
u'\u0448\u0442.'
Translated error messages used to be buggy.
>>> activate('ru')
>>> f = SomeForm({})
>>> f.as_p()
u'<ul class="errorlist"><li>\u041e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043f\u043e\u043b\u0435.</li></ul>\n<p><label for="id_somechoice_0">\xc5\xf8\xdf:</label> <ul>\n<li><label for="id_somechoice_0"><input type="radio" id="id_somechoice_0" value="\xc5" name="somechoice" /> En tied\xe4</label></li>\n<li><label for="id_somechoice_1"><input type="radio" id="id_somechoice_1" value="\xf8" name="somechoice" /> Mies</label></li>\n<li><label for="id_somechoice_2"><input type="radio" id="id_somechoice_2" value="\xdf" name="somechoice" /> Nainen</label></li>\n</ul></p>'
>>> deactivate()
Deep copying translated text shouldn't raise an error
>>> from django.utils.translation import gettext_lazy
>>> class CopyForm(Form):
... degree = IntegerField(widget=Select(choices=((1, gettext_lazy('test')),)))
>>> f = CopyForm()
#######################
# Miscellaneous Tests #
#######################
There once was a problem with Form fields called "data". Let's make sure that
doesn't come back.
>>> class DataForm(Form):
... data = CharField(max_length=10)
>>> f = DataForm({'data': 'xyzzy'})
>>> f.is_valid()
True
>>> f.cleaned_data
{'data': u'xyzzy'}
A form with *only* hidden fields that has errors is going to be very unusual.
But we can try to make sure it doesn't generate invalid XHTML. In this case,
the as_p() method is the tricky one, since error lists cannot be nested
(validly) inside p elements.
>>> class HiddenForm(Form):
... data = IntegerField(widget=HiddenInput)
>>> f = HiddenForm({})
>>> f.as_p()
u'<ul class="errorlist"><li>(Hidden field data) This field is required.</li></ul>\n<p> <input type="hidden" name="data" id="id_data" /></p>'
>>> f.as_table()
u'<tr><td colspan="2"><ul class="errorlist"><li>(Hidden field data) This field is required.</li></ul><input type="hidden" name="data" id="id_data" /></td></tr>'
###################################################
# Tests for XSS vulnerabilities in error messages #
###################################################
# The forms layer doesn't escape input values directly because error messages
# might be presented in non-HTML contexts. Instead, the message is just marked
# for escaping by the template engine. So we'll need to construct a little
# silly template to trigger the escaping.
>>> from django.template import Template, Context
>>> t = Template('{{ form.errors }}')
>>> class SomeForm(Form):
... field = ChoiceField(choices=[('one', 'One')])
>>> f = SomeForm({'field': '<script>'})
>>> t.render(Context({'form': f}))
u'<ul class="errorlist"><li>field<ul class="errorlist"><li>Select a valid choice. &lt;script&gt; is not one of the available choices.</li></ul></li></ul>'
>>> class SomeForm(Form):
... field = MultipleChoiceField(choices=[('one', 'One')])
>>> f = SomeForm({'field': ['<script>']})
>>> t.render(Context({'form': f}))
u'<ul class="errorlist"><li>field<ul class="errorlist"><li>Select a valid choice. &lt;script&gt; is not one of the available choices.</li></ul></li></ul>'
>>> from regressiontests.forms.models import ChoiceModel
>>> class SomeForm(Form):
... field = ModelMultipleChoiceField(ChoiceModel.objects.all())
>>> f = SomeForm({'field': ['<script>']})
>>> t.render(Context({'form': f}))
u'<ul class="errorlist"><li>field<ul class="errorlist"><li>&quot;&lt;script&gt;&quot; is not a valid value for a primary key.</li></ul></li></ul>'
"""

View File

@ -0,0 +1,15 @@
from error_messages import *
from extra import *
from fields import FieldsTests
from forms import *
from formsets import *
from input_formats import *
from media import *
from models import *
from regressions import *
from util import *
from validators import TestFieldWithValidators
from widgets import *
from regressiontests.forms.localflavortests import __test__
from regressiontests.forms.localflavortests import BETests, IsraelLocalFlavorTests

View File

@ -0,0 +1,253 @@
# -*- coding: utf-8 -*-
from django.core.files.uploadedfile import SimpleUploadedFile
from django.forms import *
from django.test import TestCase
from django.utils.safestring import mark_safe
from django.utils import unittest
class AssertFormErrorsMixin(object):
def assertFormErrors(self, expected, the_callable, *args, **kwargs):
try:
the_callable(*args, **kwargs)
self.fail("Testing the 'clean' method on %s failed to raise a ValidationError.")
except ValidationError, e:
self.assertEqual(e.messages, expected)
class FormsErrorMessagesTestCase(unittest.TestCase, AssertFormErrorsMixin):
def test_charfield(self):
e = {
'required': 'REQUIRED',
'min_length': 'LENGTH %(show_value)s, MIN LENGTH %(limit_value)s',
'max_length': 'LENGTH %(show_value)s, MAX LENGTH %(limit_value)s',
}
f = CharField(min_length=5, max_length=10, error_messages=e)
self.assertFormErrors([u'REQUIRED'], f.clean, '')
self.assertFormErrors([u'LENGTH 4, MIN LENGTH 5'], f.clean, '1234')
self.assertFormErrors([u'LENGTH 11, MAX LENGTH 10'], f.clean, '12345678901')
def test_integerfield(self):
e = {
'required': 'REQUIRED',
'invalid': 'INVALID',
'min_value': 'MIN VALUE IS %(limit_value)s',
'max_value': 'MAX VALUE IS %(limit_value)s',
}
f = IntegerField(min_value=5, max_value=10, error_messages=e)
self.assertFormErrors([u'REQUIRED'], f.clean, '')
self.assertFormErrors([u'INVALID'], f.clean, 'abc')
self.assertFormErrors([u'MIN VALUE IS 5'], f.clean, '4')
self.assertFormErrors([u'MAX VALUE IS 10'], f.clean, '11')
def test_floatfield(self):
e = {
'required': 'REQUIRED',
'invalid': 'INVALID',
'min_value': 'MIN VALUE IS %(limit_value)s',
'max_value': 'MAX VALUE IS %(limit_value)s',
}
f = FloatField(min_value=5, max_value=10, error_messages=e)
self.assertFormErrors([u'REQUIRED'], f.clean, '')
self.assertFormErrors([u'INVALID'], f.clean, 'abc')
self.assertFormErrors([u'MIN VALUE IS 5'], f.clean, '4')
self.assertFormErrors([u'MAX VALUE IS 10'], f.clean, '11')
def test_decimalfield(self):
e = {
'required': 'REQUIRED',
'invalid': 'INVALID',
'min_value': 'MIN VALUE IS %(limit_value)s',
'max_value': 'MAX VALUE IS %(limit_value)s',
'max_digits': 'MAX DIGITS IS %s',
'max_decimal_places': 'MAX DP IS %s',
'max_whole_digits': 'MAX DIGITS BEFORE DP IS %s',
}
f = DecimalField(min_value=5, max_value=10, error_messages=e)
self.assertFormErrors([u'REQUIRED'], f.clean, '')
self.assertFormErrors([u'INVALID'], f.clean, 'abc')
self.assertFormErrors([u'MIN VALUE IS 5'], f.clean, '4')
self.assertFormErrors([u'MAX VALUE IS 10'], f.clean, '11')
f2 = DecimalField(max_digits=4, decimal_places=2, error_messages=e)
self.assertFormErrors([u'MAX DIGITS IS 4'], f2.clean, '123.45')
self.assertFormErrors([u'MAX DP IS 2'], f2.clean, '1.234')
self.assertFormErrors([u'MAX DIGITS BEFORE DP IS 2'], f2.clean, '123.4')
def test_datefield(self):
e = {
'required': 'REQUIRED',
'invalid': 'INVALID',
}
f = DateField(error_messages=e)
self.assertFormErrors([u'REQUIRED'], f.clean, '')
self.assertFormErrors([u'INVALID'], f.clean, 'abc')
def test_timefield(self):
e = {
'required': 'REQUIRED',
'invalid': 'INVALID',
}
f = TimeField(error_messages=e)
self.assertFormErrors([u'REQUIRED'], f.clean, '')
self.assertFormErrors([u'INVALID'], f.clean, 'abc')
def test_datetimefield(self):
e = {
'required': 'REQUIRED',
'invalid': 'INVALID',
}
f = DateTimeField(error_messages=e)
self.assertFormErrors([u'REQUIRED'], f.clean, '')
self.assertFormErrors([u'INVALID'], f.clean, 'abc')
def test_regexfield(self):
e = {
'required': 'REQUIRED',
'invalid': 'INVALID',
'min_length': 'LENGTH %(show_value)s, MIN LENGTH %(limit_value)s',
'max_length': 'LENGTH %(show_value)s, MAX LENGTH %(limit_value)s',
}
f = RegexField(r'^\d+$', min_length=5, max_length=10, error_messages=e)
self.assertFormErrors([u'REQUIRED'], f.clean, '')
self.assertFormErrors([u'INVALID'], f.clean, 'abcde')
self.assertFormErrors([u'LENGTH 4, MIN LENGTH 5'], f.clean, '1234')
self.assertFormErrors([u'LENGTH 11, MAX LENGTH 10'], f.clean, '12345678901')
def test_emailfield(self):
e = {
'required': 'REQUIRED',
'invalid': 'INVALID',
'min_length': 'LENGTH %(show_value)s, MIN LENGTH %(limit_value)s',
'max_length': 'LENGTH %(show_value)s, MAX LENGTH %(limit_value)s',
}
f = EmailField(min_length=8, max_length=10, error_messages=e)
self.assertFormErrors([u'REQUIRED'], f.clean, '')
self.assertFormErrors([u'INVALID'], f.clean, 'abcdefgh')
self.assertFormErrors([u'LENGTH 7, MIN LENGTH 8'], f.clean, 'a@b.com')
self.assertFormErrors([u'LENGTH 11, MAX LENGTH 10'], f.clean, 'aye@bee.com')
def test_filefield(self):
e = {
'required': 'REQUIRED',
'invalid': 'INVALID',
'missing': 'MISSING',
'empty': 'EMPTY FILE',
}
f = FileField(error_messages=e)
self.assertFormErrors([u'REQUIRED'], f.clean, '')
self.assertFormErrors([u'INVALID'], f.clean, 'abc')
self.assertFormErrors([u'EMPTY FILE'], f.clean, SimpleUploadedFile('name', None))
self.assertFormErrors([u'EMPTY FILE'], f.clean, SimpleUploadedFile('name', ''))
def test_urlfield(self):
e = {
'required': 'REQUIRED',
'invalid': 'INVALID',
'invalid_link': 'INVALID LINK',
}
f = URLField(verify_exists=True, error_messages=e)
self.assertFormErrors([u'REQUIRED'], f.clean, '')
self.assertFormErrors([u'INVALID'], f.clean, 'abc.c')
self.assertFormErrors([u'INVALID LINK'], f.clean, 'http://www.broken.djangoproject.com')
def test_booleanfield(self):
e = {
'required': 'REQUIRED',
}
f = BooleanField(error_messages=e)
self.assertFormErrors([u'REQUIRED'], f.clean, '')
def test_choicefield(self):
e = {
'required': 'REQUIRED',
'invalid_choice': '%(value)s IS INVALID CHOICE',
}
f = ChoiceField(choices=[('a', 'aye')], error_messages=e)
self.assertFormErrors([u'REQUIRED'], f.clean, '')
self.assertFormErrors([u'b IS INVALID CHOICE'], f.clean, 'b')
def test_multiplechoicefield(self):
e = {
'required': 'REQUIRED',
'invalid_choice': '%(value)s IS INVALID CHOICE',
'invalid_list': 'NOT A LIST',
}
f = MultipleChoiceField(choices=[('a', 'aye')], error_messages=e)
self.assertFormErrors([u'REQUIRED'], f.clean, '')
self.assertFormErrors([u'NOT A LIST'], f.clean, 'b')
self.assertFormErrors([u'b IS INVALID CHOICE'], f.clean, ['b'])
def test_splitdatetimefield(self):
e = {
'required': 'REQUIRED',
'invalid_date': 'INVALID DATE',
'invalid_time': 'INVALID TIME',
}
f = SplitDateTimeField(error_messages=e)
self.assertFormErrors([u'REQUIRED'], f.clean, '')
self.assertFormErrors([u'INVALID DATE', u'INVALID TIME'], f.clean, ['a', 'b'])
def test_ipaddressfield(self):
e = {
'required': 'REQUIRED',
'invalid': 'INVALID IP ADDRESS',
}
f = IPAddressField(error_messages=e)
self.assertFormErrors([u'REQUIRED'], f.clean, '')
self.assertFormErrors([u'INVALID IP ADDRESS'], f.clean, '127.0.0')
def test_subclassing_errorlist(self):
class TestForm(Form):
first_name = CharField()
last_name = CharField()
birthday = DateField()
def clean(self):
raise ValidationError("I like to be awkward.")
class CustomErrorList(util.ErrorList):
def __unicode__(self):
return self.as_divs()
def as_divs(self):
if not self: return u''
return mark_safe(u'<div class="error">%s</div>' % ''.join([u'<p>%s</p>' % e for e in self]))
# This form should print errors the default way.
form1 = TestForm({'first_name': 'John'})
self.assertEqual(str(form1['last_name'].errors), '<ul class="errorlist"><li>This field is required.</li></ul>')
self.assertEqual(str(form1.errors['__all__']), '<ul class="errorlist"><li>I like to be awkward.</li></ul>')
# This one should wrap error groups in the customized way.
form2 = TestForm({'first_name': 'John'}, error_class=CustomErrorList)
self.assertEqual(str(form2['last_name'].errors), '<div class="error"><p>This field is required.</p></div>')
self.assertEqual(str(form2.errors['__all__']), '<div class="error"><p>I like to be awkward.</p></div>')
class ModelChoiceFieldErrorMessagesTestCase(TestCase, AssertFormErrorsMixin):
def test_modelchoicefield(self):
# Create choices for the model choice field tests below.
from regressiontests.forms.models import ChoiceModel
c1 = ChoiceModel.objects.create(pk=1, name='a')
c2 = ChoiceModel.objects.create(pk=2, name='b')
c3 = ChoiceModel.objects.create(pk=3, name='c')
# ModelChoiceField
e = {
'required': 'REQUIRED',
'invalid_choice': 'INVALID CHOICE',
}
f = ModelChoiceField(queryset=ChoiceModel.objects.all(), error_messages=e)
self.assertFormErrors([u'REQUIRED'], f.clean, '')
self.assertFormErrors([u'INVALID CHOICE'], f.clean, '4')
# ModelMultipleChoiceField
e = {
'required': 'REQUIRED',
'invalid_choice': '%s IS INVALID CHOICE',
'list': 'NOT A LIST OF VALUES',
}
f = ModelMultipleChoiceField(queryset=ChoiceModel.objects.all(), error_messages=e)
self.assertFormErrors([u'REQUIRED'], f.clean, '')
self.assertFormErrors([u'NOT A LIST OF VALUES'], f.clean, '3')
self.assertFormErrors([u'4 IS INVALID CHOICE'], f.clean, ['4'])

View File

@ -1,25 +1,29 @@
# -*- coding: utf-8 -*-
tests = r"""
>>> from django.forms import *
>>> from django.utils.encoding import force_unicode
>>> import datetime
>>> import time
>>> import re
>>> from decimal import Decimal
import datetime
from decimal import Decimal
import re
import time
from django.conf import settings
from django.forms import *
from django.forms.extras import SelectDateWidget
from django.forms.util import ErrorList
from django.test import TestCase
from django.utils import translation
from django.utils import unittest
from django.utils.encoding import force_unicode
from django.utils.encoding import smart_unicode
from error_messages import AssertFormErrorsMixin
###############
# Extra stuff #
###############
The forms library comes with some extra, higher-level Field and Widget
classes that demonstrate some of the library's abilities.
class FormsExtraTestCase(unittest.TestCase, AssertFormErrorsMixin):
###############
# Extra stuff #
###############
# SelectDateWidget ############################################################
>>> from django.forms.extras import SelectDateWidget
>>> w = SelectDateWidget(years=('2007','2008','2009','2010','2011','2012','2013','2014','2015','2016'))
>>> print w.render('mydate', '')
<select name="mydate_month" id="id_mydate_month">
# The forms library comes with some extra, higher-level Field and Widget
def test_selectdate(self):
w = SelectDateWidget(years=('2007','2008','2009','2010','2011','2012','2013','2014','2015','2016'))
self.assertEqual(w.render('mydate', ''), """<select name="mydate_month" id="id_mydate_month">
<option value="0">---</option>
<option value="1">January</option>
<option value="2">February</option>
@ -80,11 +84,10 @@ classes that demonstrate some of the library's abilities.
<option value="2014">2014</option>
<option value="2015">2015</option>
<option value="2016">2016</option>
</select>
>>> w.render('mydate', None) == w.render('mydate', '')
True
>>> print w.render('mydate', '2010-04-15')
<select name="mydate_month" id="id_mydate_month">
</select>""")
self.assertEqual(w.render('mydate', None), w.render('mydate', ''))
self.assertEqual(w.render('mydate', '2010-04-15'), """<select name="mydate_month" id="id_mydate_month">
<option value="1">January</option>
<option value="2">February</option>
<option value="3">March</option>
@ -142,16 +145,13 @@ True
<option value="2014">2014</option>
<option value="2015">2015</option>
<option value="2016">2016</option>
</select>
</select>""")
Accepts a datetime or a string:
# Accepts a datetime or a string:
self.assertEqual(w.render('mydate', datetime.date(2010, 4, 15)), w.render('mydate', '2010-04-15'))
>>> w.render('mydate', datetime.date(2010, 4, 15)) == w.render('mydate', '2010-04-15')
True
Invalid dates still render the failed date:
>>> print w.render('mydate', '2010-02-31')
<select name="mydate_month" id="id_mydate_month">
# Invalid dates still render the failed date:
self.assertEqual(w.render('mydate', '2010-02-31'), """<select name="mydate_month" id="id_mydate_month">
<option value="1">January</option>
<option value="2" selected="selected">February</option>
<option value="3">March</option>
@ -209,13 +209,11 @@ Invalid dates still render the failed date:
<option value="2014">2014</option>
<option value="2015">2015</option>
<option value="2016">2016</option>
</select>
</select>""")
Using a SelectDateWidget in a form:
>>> w = SelectDateWidget(years=('2007','2008','2009','2010','2011','2012','2013','2014','2015','2016'), required=False)
>>> print w.render('mydate', '')
<select name="mydate_month" id="id_mydate_month">
# Using a SelectDateWidget in a form:
w = SelectDateWidget(years=('2007','2008','2009','2010','2011','2012','2013','2014','2015','2016'), required=False)
self.assertEqual(w.render('mydate', ''), """<select name="mydate_month" id="id_mydate_month">
<option value="0">---</option>
<option value="1">January</option>
<option value="2">February</option>
@ -276,9 +274,8 @@ Using a SelectDateWidget in a form:
<option value="2014">2014</option>
<option value="2015">2015</option>
<option value="2016">2016</option>
</select>
>>> print w.render('mydate', '2010-04-15')
<select name="mydate_month" id="id_mydate_month">
</select>""")
self.assertEqual(w.render('mydate', '2010-04-15'), """<select name="mydate_month" id="id_mydate_month">
<option value="0">---</option>
<option value="1">January</option>
<option value="2">February</option>
@ -339,40 +336,213 @@ Using a SelectDateWidget in a form:
<option value="2014">2014</option>
<option value="2015">2015</option>
<option value="2016">2016</option>
</select>""")
class GetDate(Form):
mydate = DateField(widget=SelectDateWidget)
a = GetDate({'mydate_month':'4', 'mydate_day':'1', 'mydate_year':'2008'})
self.assertTrue(a.is_valid())
self.assertEqual(a.cleaned_data['mydate'], datetime.date(2008, 4, 1))
# As with any widget that implements get_value_from_datadict,
# we must be prepared to accept the input from the "as_hidden"
# rendering as well.
self.assertEqual(a['mydate'].as_hidden(), '<input type="hidden" name="mydate" value="2008-4-1" id="id_mydate" />')
b = GetDate({'mydate':'2008-4-1'})
self.assertTrue(b.is_valid())
self.assertEqual(b.cleaned_data['mydate'], datetime.date(2008, 4, 1))
def test_multiwidget(self):
# MultiWidget and MultiValueField #############################################
# MultiWidgets are widgets composed of other widgets. They are usually
# combined with MultiValueFields - a field that is composed of other fields.
# MulitWidgets can themselved be composed of other MultiWidgets.
# SplitDateTimeWidget is one example of a MultiWidget.
class ComplexMultiWidget(MultiWidget):
def __init__(self, attrs=None):
widgets = (
TextInput(),
SelectMultiple(choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))),
SplitDateTimeWidget(),
)
super(ComplexMultiWidget, self).__init__(widgets, attrs)
def decompress(self, value):
if value:
data = value.split(',')
return [data[0], data[1], datetime.datetime(*time.strptime(data[2], "%Y-%m-%d %H:%M:%S")[0:6])]
return [None, None, None]
def format_output(self, rendered_widgets):
return u'\n'.join(rendered_widgets)
w = ComplexMultiWidget()
self.assertEqual(w.render('name', 'some text,JP,2007-04-25 06:24:00'), """<input type="text" name="name_0" value="some text" />
<select multiple="multiple" name="name_1">
<option value="J" selected="selected">John</option>
<option value="P" selected="selected">Paul</option>
<option value="G">George</option>
<option value="R">Ringo</option>
</select>
>>> class GetDate(Form):
... mydate = DateField(widget=SelectDateWidget)
>>> a = GetDate({'mydate_month':'4', 'mydate_day':'1', 'mydate_year':'2008'})
>>> print a.is_valid()
True
>>> print a.cleaned_data['mydate']
2008-04-01
<input type="text" name="name_2_0" value="2007-04-25" /><input type="text" name="name_2_1" value="06:24:00" />""")
As with any widget that implements get_value_from_datadict,
we must be prepared to accept the input from the "as_hidden"
rendering as well.
class ComplexField(MultiValueField):
def __init__(self, required=True, widget=None, label=None, initial=None):
fields = (
CharField(),
MultipleChoiceField(choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))),
SplitDateTimeField()
)
super(ComplexField, self).__init__(fields, required, widget, label, initial)
>>> print a['mydate'].as_hidden()
<input type="hidden" name="mydate" value="2008-4-1" id="id_mydate" />
>>> b=GetDate({'mydate':'2008-4-1'})
>>> print b.is_valid()
True
>>> print b.cleaned_data['mydate']
2008-04-01
def compress(self, data_list):
if data_list:
return '%s,%s,%s' % (data_list[0],''.join(data_list[1]),data_list[2])
return None
f = ComplexField(widget=w)
self.assertEqual(f.clean(['some text', ['J','P'], ['2007-04-25','6:24:00']]), u'some text,JP,2007-04-25 06:24:00')
self.assertFormErrors([u'Select a valid choice. X is not one of the available choices.'], f.clean, ['some text',['X'], ['2007-04-25','6:24:00']])
# If insufficient data is provided, None is substituted
self.assertFormErrors([u'This field is required.'], f.clean, ['some text',['JP']])
class ComplexFieldForm(Form):
field1 = ComplexField(widget=w)
f = ComplexFieldForm()
self.assertEqual(f.as_table(), """<tr><th><label for="id_field1_0">Field1:</label></th><td><input type="text" name="field1_0" id="id_field1_0" />
<select multiple="multiple" name="field1_1" id="id_field1_1">
<option value="J">John</option>
<option value="P">Paul</option>
<option value="G">George</option>
<option value="R">Ringo</option>
</select>
<input type="text" name="field1_2_0" id="id_field1_2_0" /><input type="text" name="field1_2_1" id="id_field1_2_1" /></td></tr>""")
f = ComplexFieldForm({'field1_0':'some text','field1_1':['J','P'], 'field1_2_0':'2007-04-25', 'field1_2_1':'06:24:00'})
self.assertEqual(f.as_table(), """<tr><th><label for="id_field1_0">Field1:</label></th><td><input type="text" name="field1_0" value="some text" id="id_field1_0" />
<select multiple="multiple" name="field1_1" id="id_field1_1">
<option value="J" selected="selected">John</option>
<option value="P" selected="selected">Paul</option>
<option value="G">George</option>
<option value="R">Ringo</option>
</select>
<input type="text" name="field1_2_0" value="2007-04-25" id="id_field1_2_0" /><input type="text" name="field1_2_1" value="06:24:00" id="id_field1_2_1" /></td></tr>""")
self.assertEqual(f.cleaned_data['field1'], u'some text,JP,2007-04-25 06:24:00')
def test_ipaddress(self):
f = IPAddressField()
self.assertFormErrors([u'This field is required.'], f.clean, '')
self.assertFormErrors([u'This field is required.'], f.clean, None)
self.assertEqual(f.clean('127.0.0.1'), u'127.0.0.1')
self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, 'foo')
self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '127.0.0.')
self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '1.2.3.4.5')
self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '256.125.1.5')
f = IPAddressField(required=False)
self.assertEqual(f.clean(''), u'')
self.assertEqual(f.clean(None), u'')
self.assertEqual(f.clean('127.0.0.1'), u'127.0.0.1')
self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, 'foo')
self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '127.0.0.')
self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '1.2.3.4.5')
self.assertFormErrors([u'Enter a valid IPv4 address.'], f.clean, '256.125.1.5')
def test_smart_unicode(self):
class Test:
def __str__(self):
return 'ŠĐĆŽćžšđ'
class TestU:
def __str__(self):
return 'Foo'
def __unicode__(self):
return u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111'
self.assertEqual(smart_unicode(Test()), u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111')
self.assertEqual(smart_unicode(TestU()), u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111')
self.assertEqual(smart_unicode(1), u'1')
self.assertEqual(smart_unicode('foo'), u'foo')
def test_accessing_clean(self):
class UserForm(Form):
username = CharField(max_length=10)
password = CharField(widget=PasswordInput)
def clean(self):
data = self.cleaned_data
if not self.errors:
data['username'] = data['username'].lower()
return data
f = UserForm({'username': 'SirRobin', 'password': 'blue'})
self.assertTrue(f.is_valid())
self.assertEqual(f.cleaned_data['username'], u'sirrobin')
def test_overriding_errorlist(self):
class DivErrorList(ErrorList):
def __unicode__(self):
return self.as_divs()
def as_divs(self):
if not self: return u''
return u'<div class="errorlist">%s</div>' % ''.join([u'<div class="error">%s</div>' % force_unicode(e) for e in self])
class CommentForm(Form):
name = CharField(max_length=50, required=False)
email = EmailField()
comment = CharField()
data = dict(email='invalid')
f = CommentForm(data, auto_id=False, error_class=DivErrorList)
self.assertEqual(f.as_p(), """<p>Name: <input type="text" name="name" maxlength="50" /></p>
<div class="errorlist"><div class="error">Enter a valid e-mail address.</div></div>
<p>Email: <input type="text" name="email" value="invalid" /></p>
<div class="errorlist"><div class="error">This field is required.</div></div>
<p>Comment: <input type="text" name="comment" /></p>""")
def test_multipart_encoded_form(self):
class FormWithoutFile(Form):
username = CharField()
class FormWithFile(Form):
username = CharField()
file = FileField()
class FormWithImage(Form):
image = ImageField()
self.assertFalse(FormWithoutFile().is_multipart())
self.assertTrue(FormWithFile().is_multipart())
self.assertTrue(FormWithImage().is_multipart())
USE_L10N tests
class FormsExtraL10NTestCase(unittest.TestCase):
def setUp(self):
super(FormsExtraL10NTestCase, self).setUp()
self.old_use_l10n = getattr(settings, 'USE_L10N', False)
settings.USE_L10N = True
translation.activate('nl')
>>> from django.utils import translation
>>> translation.activate('nl')
>>> from django.conf import settings
>>> settings.USE_L10N=True
def tearDown(self):
translation.deactivate()
settings.USE_L10N = self.old_use_l10n
super(FormsExtraL10NTestCase, self).tearDown()
>>> w.value_from_datadict({'date_year': '2010', 'date_month': '8', 'date_day': '13'}, {}, 'date')
'13-08-2010'
def test_l10n(self):
w = SelectDateWidget(years=('2007','2008','2009','2010','2011','2012','2013','2014','2015','2016'), required=False)
self.assertEqual(w.value_from_datadict({'date_year': '2010', 'date_month': '8', 'date_day': '13'}, {}, 'date'), '13-08-2010')
>>> print w.render('date', '13-08-2010')
<select name="date_day" id="id_date_day">
self.assertEqual(w.render('date', '13-08-2010'), """<select name="date_day" id="id_date_day">
<option value="0">---</option>
<option value="1">1</option>
<option value="2">2</option>
@ -433,242 +603,8 @@ USE_L10N tests
<option value="2014">2014</option>
<option value="2015">2015</option>
<option value="2016">2016</option>
</select>
</select>""")
Years before 1900 work
>>> w = SelectDateWidget(years=('1899',))
>>> w.value_from_datadict({'date_year': '1899', 'date_month': '8', 'date_day': '13'}, {}, 'date')
'13-08-1899'
>>> translation.deactivate()
# MultiWidget and MultiValueField #############################################
# MultiWidgets are widgets composed of other widgets. They are usually
# combined with MultiValueFields - a field that is composed of other fields.
# MulitWidgets can themselved be composed of other MultiWidgets.
# SplitDateTimeWidget is one example of a MultiWidget.
>>> class ComplexMultiWidget(MultiWidget):
... def __init__(self, attrs=None):
... widgets = (
... TextInput(),
... SelectMultiple(choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))),
... SplitDateTimeWidget(),
... )
... super(ComplexMultiWidget, self).__init__(widgets, attrs)
...
... def decompress(self, value):
... if value:
... data = value.split(',')
... return [data[0], data[1], datetime.datetime(*time.strptime(data[2], "%Y-%m-%d %H:%M:%S")[0:6])]
... return [None, None, None]
... def format_output(self, rendered_widgets):
... return u'\n'.join(rendered_widgets)
>>> w = ComplexMultiWidget()
>>> print w.render('name', 'some text,JP,2007-04-25 06:24:00')
<input type="text" name="name_0" value="some text" />
<select multiple="multiple" name="name_1">
<option value="J" selected="selected">John</option>
<option value="P" selected="selected">Paul</option>
<option value="G">George</option>
<option value="R">Ringo</option>
</select>
<input type="text" name="name_2_0" value="2007-04-25" /><input type="text" name="name_2_1" value="06:24:00" />
>>> class ComplexField(MultiValueField):
... def __init__(self, required=True, widget=None, label=None, initial=None):
... fields = (
... CharField(),
... MultipleChoiceField(choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))),
... SplitDateTimeField()
... )
... super(ComplexField, self).__init__(fields, required, widget, label, initial)
...
... def compress(self, data_list):
... if data_list:
... return '%s,%s,%s' % (data_list[0],''.join(data_list[1]),data_list[2])
... return None
>>> f = ComplexField(widget=w)
>>> f.clean(['some text', ['J','P'], ['2007-04-25','6:24:00']])
u'some text,JP,2007-04-25 06:24:00'
>>> f.clean(['some text',['X'], ['2007-04-25','6:24:00']])
Traceback (most recent call last):
...
ValidationError: [u'Select a valid choice. X is not one of the available choices.']
# If insufficient data is provided, None is substituted
>>> f.clean(['some text',['JP']])
Traceback (most recent call last):
...
ValidationError: [u'This field is required.']
>>> class ComplexFieldForm(Form):
... field1 = ComplexField(widget=w)
>>> f = ComplexFieldForm()
>>> print f
<tr><th><label for="id_field1_0">Field1:</label></th><td><input type="text" name="field1_0" id="id_field1_0" />
<select multiple="multiple" name="field1_1" id="id_field1_1">
<option value="J">John</option>
<option value="P">Paul</option>
<option value="G">George</option>
<option value="R">Ringo</option>
</select>
<input type="text" name="field1_2_0" id="id_field1_2_0" /><input type="text" name="field1_2_1" id="id_field1_2_1" /></td></tr>
>>> f = ComplexFieldForm({'field1_0':'some text','field1_1':['J','P'], 'field1_2_0':'2007-04-25', 'field1_2_1':'06:24:00'})
>>> print f
<tr><th><label for="id_field1_0">Field1:</label></th><td><input type="text" name="field1_0" value="some text" id="id_field1_0" />
<select multiple="multiple" name="field1_1" id="id_field1_1">
<option value="J" selected="selected">John</option>
<option value="P" selected="selected">Paul</option>
<option value="G">George</option>
<option value="R">Ringo</option>
</select>
<input type="text" name="field1_2_0" value="2007-04-25" id="id_field1_2_0" /><input type="text" name="field1_2_1" value="06:24:00" id="id_field1_2_1" /></td></tr>
>>> f.cleaned_data['field1']
u'some text,JP,2007-04-25 06:24:00'
# IPAddressField ##################################################################
>>> f = IPAddressField()
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'This field is required.']
>>> f.clean(None)
Traceback (most recent call last):
...
ValidationError: [u'This field is required.']
>>> f.clean('127.0.0.1')
u'127.0.0.1'
>>> f.clean('foo')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid IPv4 address.']
>>> f.clean('127.0.0.')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid IPv4 address.']
>>> f.clean('1.2.3.4.5')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid IPv4 address.']
>>> f.clean('256.125.1.5')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid IPv4 address.']
>>> f = IPAddressField(required=False)
>>> f.clean('')
u''
>>> f.clean(None)
u''
>>> f.clean('127.0.0.1')
u'127.0.0.1'
>>> f.clean('foo')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid IPv4 address.']
>>> f.clean('127.0.0.')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid IPv4 address.']
>>> f.clean('1.2.3.4.5')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid IPv4 address.']
>>> f.clean('256.125.1.5')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid IPv4 address.']
#################################
# Tests of underlying functions #
#################################
# smart_unicode tests
>>> from django.utils.encoding import smart_unicode
>>> class Test:
... def __str__(self):
... return 'ŠĐĆŽćžšđ'
>>> class TestU:
... def __str__(self):
... return 'Foo'
... def __unicode__(self):
... return u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111'
>>> smart_unicode(Test())
u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111'
>>> smart_unicode(TestU())
u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111'
>>> smart_unicode(1)
u'1'
>>> smart_unicode('foo')
u'foo'
####################################
# Test accessing errors in clean() #
####################################
>>> class UserForm(Form):
... username = CharField(max_length=10)
... password = CharField(widget=PasswordInput)
... def clean(self):
... data = self.cleaned_data
... if not self.errors:
... data['username'] = data['username'].lower()
... return data
>>> f = UserForm({'username': 'SirRobin', 'password': 'blue'})
>>> f.is_valid()
True
>>> f.cleaned_data['username']
u'sirrobin'
#######################################
# Test overriding ErrorList in a form #
#######################################
>>> from django.forms.util import ErrorList
>>> class DivErrorList(ErrorList):
... def __unicode__(self):
... return self.as_divs()
... def as_divs(self):
... if not self: return u''
... return u'<div class="errorlist">%s</div>' % ''.join([u'<div class="error">%s</div>' % force_unicode(e) for e in self])
>>> class CommentForm(Form):
... name = CharField(max_length=50, required=False)
... email = EmailField()
... comment = CharField()
>>> data = dict(email='invalid')
>>> f = CommentForm(data, auto_id=False, error_class=DivErrorList)
>>> print f.as_p()
<p>Name: <input type="text" name="name" maxlength="50" /></p>
<div class="errorlist"><div class="error">Enter a valid e-mail address.</div></div>
<p>Email: <input type="text" name="email" value="invalid" /></p>
<div class="errorlist"><div class="error">This field is required.</div></div>
<p>Comment: <input type="text" name="comment" /></p>
#################################
# Test multipart-encoded form #
#################################
>>> class FormWithoutFile(Form):
... username = CharField()
>>> class FormWithFile(Form):
... username = CharField()
... file = FileField()
>>> class FormWithImage(Form):
... image = ImageField()
>>> FormWithoutFile().is_multipart()
False
>>> FormWithFile().is_multipart()
True
>>> FormWithImage().is_multipart()
True
"""
# Years before 1900 work
w = SelectDateWidget(years=('1899',))
self.assertEqual(w.value_from_datadict({'date_year': '1899', 'date_month': '8', 'date_day': '13'}, {}, 'date'), '13-08-1899')

View File

@ -57,12 +57,12 @@ class FieldsTests(TestCase):
self.assertEqual(message, str(e))
def test_field_sets_widget_is_required(self):
self.assertEqual(Field(required=True).widget.is_required, True)
self.assertEqual(Field(required=False).widget.is_required, False)
self.assertTrue(Field(required=True).widget.is_required)
self.assertFalse(Field(required=False).widget.is_required)
# CharField ###################################################################
def test_charfield_0(self):
def test_charfield_1(self):
f = CharField()
self.assertEqual(u'1', f.clean(1))
self.assertEqual(u'hello', f.clean('hello'))
@ -70,7 +70,7 @@ class FieldsTests(TestCase):
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, '')
self.assertEqual(u'[1, 2, 3]', f.clean([1, 2, 3]))
def test_charfield_1(self):
def test_charfield_2(self):
f = CharField(required=False)
self.assertEqual(u'1', f.clean(1))
self.assertEqual(u'hello', f.clean('hello'))
@ -78,20 +78,20 @@ class FieldsTests(TestCase):
self.assertEqual(u'', f.clean(''))
self.assertEqual(u'[1, 2, 3]', f.clean([1, 2, 3]))
def test_charfield_2(self):
def test_charfield_3(self):
f = CharField(max_length=10, required=False)
self.assertEqual(u'12345', f.clean('12345'))
self.assertEqual(u'1234567890', f.clean('1234567890'))
self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value has at most 10 characters (it has 11).']", f.clean, '1234567890a')
def test_charfield_3(self):
def test_charfield_4(self):
f = CharField(min_length=10, required=False)
self.assertEqual(u'', f.clean(''))
self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value has at least 10 characters (it has 5).']", f.clean, '12345')
self.assertEqual(u'1234567890', f.clean('1234567890'))
self.assertEqual(u'1234567890a', f.clean('1234567890a'))
def test_charfield_4(self):
def test_charfield_5(self):
f = CharField(min_length=10, required=True)
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, '')
self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value has at least 10 characters (it has 5).']", f.clean, '12345')
@ -100,7 +100,7 @@ class FieldsTests(TestCase):
# IntegerField ################################################################
def test_integerfield_5(self):
def test_integerfield_1(self):
f = IntegerField()
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, '')
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, None)
@ -115,7 +115,7 @@ class FieldsTests(TestCase):
self.assertEqual(1, f.clean(' 1 '))
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a whole number.']", f.clean, '1a')
def test_integerfield_6(self):
def test_integerfield_2(self):
f = IntegerField(required=False)
self.assertEqual(None, f.clean(''))
self.assertEqual('None', repr(f.clean('')))
@ -130,7 +130,7 @@ class FieldsTests(TestCase):
self.assertEqual(1, f.clean(' 1 '))
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a whole number.']", f.clean, '1a')
def test_integerfield_7(self):
def test_integerfield_3(self):
f = IntegerField(max_value=10)
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, None)
self.assertEqual(1, f.clean(1))
@ -139,7 +139,7 @@ class FieldsTests(TestCase):
self.assertEqual(10, f.clean('10'))
self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value is less than or equal to 10.']", f.clean, '11')
def test_integerfield_8(self):
def test_integerfield_4(self):
f = IntegerField(min_value=10)
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, None)
self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value is greater than or equal to 10.']", f.clean, 1)
@ -148,7 +148,7 @@ class FieldsTests(TestCase):
self.assertEqual(10, f.clean('10'))
self.assertEqual(11, f.clean('11'))
def test_integerfield_9(self):
def test_integerfield_5(self):
f = IntegerField(min_value=10, max_value=20)
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, None)
self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value is greater than or equal to 10.']", f.clean, 1)
@ -161,7 +161,7 @@ class FieldsTests(TestCase):
# FloatField ##################################################################
def test_floatfield_10(self):
def test_floatfield_1(self):
f = FloatField()
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, '')
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, None)
@ -177,13 +177,13 @@ class FieldsTests(TestCase):
self.assertEqual(1.0, f.clean(' 1.0 '))
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a number.']", f.clean, '1.0a')
def test_floatfield_11(self):
def test_floatfield_2(self):
f = FloatField(required=False)
self.assertEqual(None, f.clean(''))
self.assertEqual(None, f.clean(None))
self.assertEqual(1.0, f.clean('1'))
def test_floatfield_12(self):
def test_floatfield_3(self):
f = FloatField(max_value=1.5, min_value=0.5)
self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value is less than or equal to 1.5.']", f.clean, '1.6')
self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value is greater than or equal to 0.5.']", f.clean, '0.4')
@ -192,7 +192,7 @@ class FieldsTests(TestCase):
# DecimalField ################################################################
def test_decimalfield_13(self):
def test_decimalfield_1(self):
f = DecimalField(max_digits=4, decimal_places=2)
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, '')
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, None)
@ -223,13 +223,13 @@ class FieldsTests(TestCase):
self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure that there are no more than 4 digits in total.']", f.clean, '-000.12345')
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a number.']", f.clean, '--0.12')
def test_decimalfield_14(self):
def test_decimalfield_2(self):
f = DecimalField(max_digits=4, decimal_places=2, required=False)
self.assertEqual(None, f.clean(''))
self.assertEqual(None, f.clean(None))
self.assertEqual(f.clean('1'), Decimal("1"))
def test_decimalfield_15(self):
def test_decimalfield_3(self):
f = DecimalField(max_digits=4, decimal_places=2, max_value=Decimal('1.5'), min_value=Decimal('0.5'))
self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value is less than or equal to 1.5.']", f.clean, '1.6')
self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value is greater than or equal to 0.5.']", f.clean, '0.4')
@ -238,11 +238,11 @@ class FieldsTests(TestCase):
self.assertEqual(f.clean('.5'), Decimal("0.5"))
self.assertEqual(f.clean('00.50'), Decimal("0.50"))
def test_decimalfield_16(self):
def test_decimalfield_4(self):
f = DecimalField(decimal_places=2)
self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure that there are no more than 2 decimal places.']", f.clean, '0.00000001')
def test_decimalfield_17(self):
def test_decimalfield_5(self):
f = DecimalField(max_digits=3)
# Leading whole zeros "collapse" to one digit.
self.assertEqual(f.clean('0000000.10'), Decimal("0.1"))
@ -253,14 +253,14 @@ class FieldsTests(TestCase):
self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure that there are no more than 3 digits in total.']", f.clean, '000000.0002')
self.assertEqual(f.clean('.002'), Decimal("0.002"))
def test_decimalfield_18(self):
def test_decimalfield_6(self):
f = DecimalField(max_digits=2, decimal_places=2)
self.assertEqual(f.clean('.01'), Decimal(".01"))
self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure that there are no more than 0 digits before the decimal point.']", f.clean, '1.1')
# DateField ###################################################################
def test_datefield_19(self):
def test_datefield_1(self):
f = DateField()
self.assertEqual(datetime.date(2006, 10, 25), f.clean(datetime.date(2006, 10, 25)))
self.assertEqual(datetime.date(2006, 10, 25), f.clean(datetime.datetime(2006, 10, 25, 14, 30)))
@ -279,14 +279,14 @@ class FieldsTests(TestCase):
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid date.']", f.clean, '25/10/06')
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, None)
def test_datefield_20(self):
def test_datefield_2(self):
f = DateField(required=False)
self.assertEqual(None, f.clean(None))
self.assertEqual('None', repr(f.clean(None)))
self.assertEqual(None, f.clean(''))
self.assertEqual('None', repr(f.clean('')))
def test_datefield_21(self):
def test_datefield_3(self):
f = DateField(input_formats=['%Y %m %d'])
self.assertEqual(datetime.date(2006, 10, 25), f.clean(datetime.date(2006, 10, 25)))
self.assertEqual(datetime.date(2006, 10, 25), f.clean(datetime.datetime(2006, 10, 25, 14, 30)))
@ -297,7 +297,7 @@ class FieldsTests(TestCase):
# TimeField ###################################################################
def test_timefield_22(self):
def test_timefield_1(self):
f = TimeField()
self.assertEqual(datetime.time(14, 25), f.clean(datetime.time(14, 25)))
self.assertEqual(datetime.time(14, 25, 59), f.clean(datetime.time(14, 25, 59)))
@ -306,7 +306,7 @@ class FieldsTests(TestCase):
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid time.']", f.clean, 'hello')
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid time.']", f.clean, '1:24 p.m.')
def test_timefield_23(self):
def test_timefield_2(self):
f = TimeField(input_formats=['%I:%M %p'])
self.assertEqual(datetime.time(14, 25), f.clean(datetime.time(14, 25)))
self.assertEqual(datetime.time(14, 25, 59), f.clean(datetime.time(14, 25, 59)))
@ -316,7 +316,7 @@ class FieldsTests(TestCase):
# DateTimeField ###############################################################
def test_datetimefield_24(self):
def test_datetimefield_1(self):
f = DateTimeField()
self.assertEqual(datetime.datetime(2006, 10, 25, 0, 0), f.clean(datetime.date(2006, 10, 25)))
self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30), f.clean(datetime.datetime(2006, 10, 25, 14, 30)))
@ -337,7 +337,7 @@ class FieldsTests(TestCase):
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid date/time.']", f.clean, 'hello')
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid date/time.']", f.clean, '2006-10-25 4:30 p.m.')
def test_datetimefield_25(self):
def test_datetimefield_2(self):
f = DateTimeField(input_formats=['%Y %m %d %I:%M %p'])
self.assertEqual(datetime.datetime(2006, 10, 25, 0, 0), f.clean(datetime.date(2006, 10, 25)))
self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30), f.clean(datetime.datetime(2006, 10, 25, 14, 30)))
@ -346,7 +346,7 @@ class FieldsTests(TestCase):
self.assertEqual(datetime.datetime(2006, 10, 25, 14, 30), f.clean('2006 10 25 2:30 PM'))
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid date/time.']", f.clean, '2006-10-25 14:30:45')
def test_datetimefield_26(self):
def test_datetimefield_3(self):
f = DateTimeField(required=False)
self.assertEqual(None, f.clean(None))
self.assertEqual('None', repr(f.clean(None)))
@ -355,7 +355,7 @@ class FieldsTests(TestCase):
# RegexField ##################################################################
def test_regexfield_27(self):
def test_regexfield_1(self):
f = RegexField('^\d[A-F]\d$')
self.assertEqual(u'2A2', f.clean('2A2'))
self.assertEqual(u'3F3', f.clean('3F3'))
@ -364,14 +364,14 @@ class FieldsTests(TestCase):
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid value.']", f.clean, '2A2 ')
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, '')
def test_regexfield_28(self):
def test_regexfield_2(self):
f = RegexField('^\d[A-F]\d$', required=False)
self.assertEqual(u'2A2', f.clean('2A2'))
self.assertEqual(u'3F3', f.clean('3F3'))
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid value.']", f.clean, '3G3')
self.assertEqual(u'', f.clean(''))
def test_regexfield_29(self):
def test_regexfield_3(self):
f = RegexField(re.compile('^\d[A-F]\d$'))
self.assertEqual(u'2A2', f.clean('2A2'))
self.assertEqual(u'3F3', f.clean('3F3'))
@ -379,13 +379,13 @@ class FieldsTests(TestCase):
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid value.']", f.clean, ' 2A2')
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid value.']", f.clean, '2A2 ')
def test_regexfield_30(self):
def test_regexfield_4(self):
f = RegexField('^\d\d\d\d$', error_message='Enter a four-digit number.')
self.assertEqual(u'1234', f.clean('1234'))
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a four-digit number.']", f.clean, '123')
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a four-digit number.']", f.clean, 'abcd')
def test_regexfield_31(self):
def test_regexfield_5(self):
f = RegexField('^\d+$', min_length=5, max_length=10)
self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value has at least 5 characters (it has 3).']", f.clean, '123')
self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value has at least 5 characters (it has 3).', u'Enter a valid value.']", f.clean, 'abc')
@ -396,7 +396,7 @@ class FieldsTests(TestCase):
# EmailField ##################################################################
def test_emailfield_32(self):
def test_emailfield_1(self):
f = EmailField()
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, '')
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, None)
@ -424,7 +424,7 @@ class FieldsTests(TestCase):
'viewx3dtextx26qx3d@yahoo.comx26latlngx3d15854521645943074058'
)
def test_emailfield_33(self):
def test_emailfield_2(self):
f = EmailField(required=False)
self.assertEqual(u'', f.clean(''))
self.assertEqual(u'', f.clean(None))
@ -434,7 +434,7 @@ class FieldsTests(TestCase):
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid e-mail address.']", f.clean, 'foo@')
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid e-mail address.']", f.clean, 'foo@bar')
def test_emailfield_34(self):
def test_emailfield_3(self):
f = EmailField(min_length=10, max_length=15)
self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value has at least 10 characters (it has 9).']", f.clean, 'a@foo.com')
self.assertEqual(u'alf@foo.com', f.clean('alf@foo.com'))
@ -442,7 +442,7 @@ class FieldsTests(TestCase):
# FileField ##################################################################
def test_filefield_35(self):
def test_filefield_1(self):
f = FileField()
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, '')
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, '', '')
@ -460,7 +460,7 @@ class FieldsTests(TestCase):
self.assertEqual(SimpleUploadedFile, type(f.clean(SimpleUploadedFile('我隻氣墊船裝滿晒鱔.txt', 'मेरी मँडराने वाली नाव सर्पमीनों से भरी ह'))))
self.assertEqual(SimpleUploadedFile, type(f.clean(SimpleUploadedFile('name', 'Some File Content'), 'files/test4.pdf')))
def test_filefield_36(self):
def test_filefield_2(self):
f = FileField(max_length = 5)
self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this filename has at most 5 characters (it has 18).']", f.clean, SimpleUploadedFile('test_maxlength.txt', 'hello world'))
self.assertEqual('files/test1.pdf', f.clean('', 'files/test1.pdf'))
@ -469,7 +469,7 @@ class FieldsTests(TestCase):
# URLField ##################################################################
def test_urlfield_37(self):
def test_urlfield_1(self):
f = URLField()
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, '')
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, None)
@ -505,7 +505,7 @@ class FieldsTests(TestCase):
# domains that don't fail the domain label length check in the regex
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid URL.']", f.clean, 'http://%s' % ("X"*60,))
def test_urlfield_38(self):
def test_urlfield_2(self):
f = URLField(required=False)
self.assertEqual(u'', f.clean(''))
self.assertEqual(u'', f.clean(None))
@ -517,7 +517,7 @@ class FieldsTests(TestCase):
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid URL.']", f.clean, 'http://example.')
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid URL.']", f.clean, 'http://.com')
def test_urlfield_39(self):
def test_urlfield_3(self):
f = URLField(verify_exists=True)
self.assertEqual(u'http://www.google.com/', f.clean('http://www.google.com')) # This will fail if there's no Internet connection
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid URL.']", f.clean, 'http://example')
@ -539,24 +539,24 @@ class FieldsTests(TestCase):
except ValidationError, e:
self.assertEqual("[u'This URL appears to be a broken link.']", str(e))
def test_urlfield_40(self):
def test_urlfield_4(self):
f = URLField(verify_exists=True, required=False)
self.assertEqual(u'', f.clean(''))
self.assertEqual(u'http://www.google.com/', f.clean('http://www.google.com')) # This will fail if there's no Internet connection
def test_urlfield_41(self):
def test_urlfield_5(self):
f = URLField(min_length=15, max_length=20)
self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value has at least 15 characters (it has 13).']", f.clean, 'http://f.com')
self.assertEqual(u'http://example.com/', f.clean('http://example.com'))
self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value has at most 20 characters (it has 38).']", f.clean, 'http://abcdefghijklmnopqrstuvwxyz.com')
def test_urlfield_42(self):
def test_urlfield_6(self):
f = URLField(required=False)
self.assertEqual(u'http://example.com/', f.clean('example.com'))
self.assertEqual(u'', f.clean(''))
self.assertEqual(u'https://example.com/', f.clean('https://example.com'))
def test_urlfield_43(self):
def test_urlfield_7(self):
f = URLField()
self.assertEqual(u'http://example.com/', f.clean('http://example.com'))
self.assertEqual(u'http://example.com/test', f.clean('http://example.com/test'))
@ -567,7 +567,7 @@ class FieldsTests(TestCase):
# BooleanField ################################################################
def test_booleanfield_44(self):
def test_booleanfield_1(self):
f = BooleanField()
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, '')
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, None)
@ -579,7 +579,7 @@ class FieldsTests(TestCase):
self.assertEqual(True, f.clean('True'))
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, 'False')
def test_booleanfield_45(self):
def test_booleanfield_2(self):
f = BooleanField(required=False)
self.assertEqual(False, f.clean(''))
self.assertEqual(False, f.clean(None))
@ -594,7 +594,7 @@ class FieldsTests(TestCase):
# ChoiceField #################################################################
def test_choicefield_46(self):
def test_choicefield_1(self):
f = ChoiceField(choices=[('1', 'One'), ('2', 'Two')])
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, '')
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, None)
@ -602,7 +602,7 @@ class FieldsTests(TestCase):
self.assertEqual(u'1', f.clean('1'))
self.assertRaisesErrorWithMessage(ValidationError, "[u'Select a valid choice. 3 is not one of the available choices.']", f.clean, '3')
def test_choicefield_47(self):
def test_choicefield_2(self):
f = ChoiceField(choices=[('1', 'One'), ('2', 'Two')], required=False)
self.assertEqual(u'', f.clean(''))
self.assertEqual(u'', f.clean(None))
@ -610,12 +610,12 @@ class FieldsTests(TestCase):
self.assertEqual(u'1', f.clean('1'))
self.assertRaisesErrorWithMessage(ValidationError, "[u'Select a valid choice. 3 is not one of the available choices.']", f.clean, '3')
def test_choicefield_48(self):
def test_choicefield_3(self):
f = ChoiceField(choices=[('J', 'John'), ('P', 'Paul')])
self.assertEqual(u'J', f.clean('J'))
self.assertRaisesErrorWithMessage(ValidationError, "[u'Select a valid choice. John is not one of the available choices.']", f.clean, 'John')
def test_choicefield_49(self):
def test_choicefield_4(self):
f = ChoiceField(choices=[('Numbers', (('1', 'One'), ('2', 'Two'))), ('Letters', (('3','A'),('4','B'))), ('5','Other')])
self.assertEqual(u'1', f.clean(1))
self.assertEqual(u'1', f.clean('1'))
@ -629,22 +629,22 @@ class FieldsTests(TestCase):
# TypedChoiceField is just like ChoiceField, except that coerced types will
# be returned:
def test_typedchoicefield_50(self):
def test_typedchoicefield_1(self):
f = TypedChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int)
self.assertEqual(1, f.clean('1'))
self.assertRaisesErrorWithMessage(ValidationError, "[u'Select a valid choice. 2 is not one of the available choices.']", f.clean, '2')
def test_typedchoicefield_51(self):
def test_typedchoicefield_2(self):
# Different coercion, same validation.
f = TypedChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=float)
self.assertEqual(1.0, f.clean('1'))
def test_typedchoicefield_52(self):
def test_typedchoicefield_3(self):
# This can also cause weirdness: be careful (bool(-1) == True, remember)
f = TypedChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=bool)
self.assertEqual(True, f.clean('-1'))
def test_typedchoicefield_53(self):
def test_typedchoicefield_4(self):
# Even more weirdness: if you have a valid choice but your coercion function
# can't coerce, you'll still get a validation error. Don't do this!
f = TypedChoiceField(choices=[('A', 'A'), ('B', 'B')], coerce=int)
@ -652,19 +652,19 @@ class FieldsTests(TestCase):
# Required fields require values
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, '')
def test_typedchoicefield_54(self):
def test_typedchoicefield_5(self):
# Non-required fields aren't required
f = TypedChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=False)
self.assertEqual('', f.clean(''))
# If you want cleaning an empty value to return a different type, tell the field
def test_typedchoicefield_55(self):
def test_typedchoicefield_6(self):
f = TypedChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=False, empty_value=None)
self.assertEqual(None, f.clean(''))
# NullBooleanField ############################################################
def test_nullbooleanfield_56(self):
def test_nullbooleanfield_1(self):
f = NullBooleanField()
self.assertEqual(None, f.clean(''))
self.assertEqual(True, f.clean(True))
@ -677,7 +677,7 @@ class FieldsTests(TestCase):
self.assertEqual(None, f.clean('hello'))
def test_nullbooleanfield_57(self):
def test_nullbooleanfield_2(self):
# Make sure that the internal value is preserved if using HiddenInput (#7753)
class HiddenNullBooleanForm(Form):
hidden_nullbool1 = NullBooleanField(widget=HiddenInput, initial=True)
@ -685,7 +685,7 @@ class FieldsTests(TestCase):
f = HiddenNullBooleanForm()
self.assertEqual('<input type="hidden" name="hidden_nullbool1" value="True" id="id_hidden_nullbool1" /><input type="hidden" name="hidden_nullbool2" value="False" id="id_hidden_nullbool2" />', str(f))
def test_nullbooleanfield_58(self):
def test_nullbooleanfield_3(self):
class HiddenNullBooleanForm(Form):
hidden_nullbool1 = NullBooleanField(widget=HiddenInput, initial=True)
hidden_nullbool2 = NullBooleanField(widget=HiddenInput, initial=False)
@ -694,7 +694,7 @@ class FieldsTests(TestCase):
self.assertEqual(True, f.cleaned_data['hidden_nullbool1'])
self.assertEqual(False, f.cleaned_data['hidden_nullbool2'])
def test_nullbooleanfield_59(self):
def test_nullbooleanfield_4(self):
# Make sure we're compatible with MySQL, which uses 0 and 1 for its boolean
# values. (#9609)
NULLBOOL_CHOICES = (('1', 'Yes'), ('0', 'No'), ('', 'Unknown'))
@ -710,7 +710,7 @@ class FieldsTests(TestCase):
# MultipleChoiceField #########################################################
def test_multiplechoicefield_60(self):
def test_multiplechoicefield_1(self):
f = MultipleChoiceField(choices=[('1', 'One'), ('2', 'Two')])
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, '')
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, None)
@ -724,7 +724,7 @@ class FieldsTests(TestCase):
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, ())
self.assertRaisesErrorWithMessage(ValidationError, "[u'Select a valid choice. 3 is not one of the available choices.']", f.clean, ['3'])
def test_multiplechoicefield_61(self):
def test_multiplechoicefield_2(self):
f = MultipleChoiceField(choices=[('1', 'One'), ('2', 'Two')], required=False)
self.assertEqual([], f.clean(''))
self.assertEqual([], f.clean(None))
@ -738,7 +738,7 @@ class FieldsTests(TestCase):
self.assertEqual([], f.clean(()))
self.assertRaisesErrorWithMessage(ValidationError, "[u'Select a valid choice. 3 is not one of the available choices.']", f.clean, ['3'])
def test_multiplechoicefield_62(self):
def test_multiplechoicefield_3(self):
f = MultipleChoiceField(choices=[('Numbers', (('1', 'One'), ('2', 'Two'))), ('Letters', (('3','A'),('4','B'))), ('5','Other')])
self.assertEqual([u'1'], f.clean([1]))
self.assertEqual([u'1'], f.clean(['1']))
@ -751,7 +751,7 @@ class FieldsTests(TestCase):
# ComboField ##################################################################
def test_combofield_63(self):
def test_combofield_1(self):
f = ComboField(fields=[CharField(max_length=20), EmailField()])
self.assertEqual(u'test@example.com', f.clean('test@example.com'))
self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value has at most 20 characters (it has 28).']", f.clean, 'longemailaddress@example.com')
@ -759,7 +759,7 @@ class FieldsTests(TestCase):
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, '')
self.assertRaisesErrorWithMessage(ValidationError, "[u'This field is required.']", f.clean, None)
def test_combofield_64(self):
def test_combofield_2(self):
f = ComboField(fields=[CharField(max_length=20), EmailField()], required=False)
self.assertEqual(u'test@example.com', f.clean('test@example.com'))
self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value has at most 20 characters (it has 28).']", f.clean, 'longemailaddress@example.com')
@ -769,12 +769,12 @@ class FieldsTests(TestCase):
# FilePathField ###############################################################
def test_filepathfield_65(self):
def test_filepathfield_1(self):
path = os.path.abspath(forms.__file__)
path = os.path.dirname(path) + '/'
self.assertTrue(fix_os_paths(path).endswith('/django/forms/'))
def test_filepathfield_66(self):
def test_filepathfield_2(self):
path = forms.__file__
path = os.path.dirname(os.path.abspath(path)) + '/'
f = FilePathField(path=path)
@ -795,7 +795,7 @@ class FieldsTests(TestCase):
self.assertRaisesErrorWithMessage(ValidationError, "[u'Select a valid choice. fields.py is not one of the available choices.']", f.clean, 'fields.py')
assert fix_os_paths(f.clean(path + 'fields.py')).endswith('/django/forms/fields.py')
def test_filepathfield_67(self):
def test_filepathfield_3(self):
path = forms.__file__
path = os.path.dirname(os.path.abspath(path)) + '/'
f = FilePathField(path=path, match='^.*?\.py$')
@ -813,7 +813,7 @@ class FieldsTests(TestCase):
self.assertEqual(exp[1], got[1])
self.assertTrue(got[0].endswith(exp[0]))
def test_filepathfield_68(self):
def test_filepathfield_4(self):
path = os.path.abspath(forms.__file__)
path = os.path.dirname(path) + '/'
f = FilePathField(path=path, recursive=True, match='^.*?\.py$')
@ -835,7 +835,7 @@ class FieldsTests(TestCase):
# SplitDateTimeField ##########################################################
def test_splitdatetimefield_69(self):
def test_splitdatetimefield_1(self):
from django.forms.widgets import SplitDateTimeWidget
f = SplitDateTimeField()
assert isinstance(f.widget, SplitDateTimeWidget)
@ -847,7 +847,7 @@ class FieldsTests(TestCase):
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid time.']", f.clean, ['2006-01-10', 'there'])
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid date.']", f.clean, ['hello', '07:30'])
def test_splitdatetimefield_70(self):
def test_splitdatetimefield_2(self):
f = SplitDateTimeField(required=False)
self.assertEqual(datetime.datetime(2006, 1, 10, 7, 30), f.clean([datetime.date(2006, 1, 10), datetime.time(7, 30)]))
self.assertEqual(datetime.datetime(2006, 1, 10, 7, 30), f.clean(['2006-01-10', '07:30']))

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,797 @@
# -*- coding: utf-8 -*-
from django.forms import Form, CharField, IntegerField, ValidationError
from django.forms.formsets import formset_factory, BaseFormSet
from django.utils.unittest import TestCase
class Choice(Form):
choice = CharField()
votes = IntegerField()
# FormSet allows us to use multiple instance of the same form on 1 page. For now,
# the best way to create a FormSet is by using the formset_factory function.
ChoiceFormSet = formset_factory(Choice)
class FavoriteDrinkForm(Form):
name = CharField()
class BaseFavoriteDrinksFormSet(BaseFormSet):
def clean(self):
seen_drinks = []
for drink in self.cleaned_data:
if drink['name'] in seen_drinks:
raise ValidationError('You may only specify a drink once.')
seen_drinks.append(drink['name'])
# Let's define a FormSet that takes a list of favorite drinks, but raises an
# error if there are any duplicates. Used in ``test_clean_hook``,
# ``test_regression_6926`` & ``test_regression_12878``.
FavoriteDrinksFormSet = formset_factory(FavoriteDrinkForm,
formset=BaseFavoriteDrinksFormSet, extra=3)
class FormsFormsetTestCase(TestCase):
def test_basic_formset(self):
# A FormSet constructor takes the same arguments as Form. Let's create a FormSet
# for adding data. By default, it displays 1 blank form. It can display more,
# but we'll look at how to do so later.
formset = ChoiceFormSet(auto_id=False, prefix='choices')
self.assertEqual(str(formset), """<input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" /><input type="hidden" name="choices-MAX_NUM_FORMS" />
<tr><th>Choice:</th><td><input type="text" name="choices-0-choice" /></td></tr>
<tr><th>Votes:</th><td><input type="text" name="choices-0-votes" /></td></tr>""")
# On thing to note is that there needs to be a special value in the data. This
# value tells the FormSet how many forms were displayed so it can tell how
# many forms it needs to clean and validate. You could use javascript to create
# new forms on the client side, but they won't get validated unless you increment
# the TOTAL_FORMS field appropriately.
data = {
'choices-TOTAL_FORMS': '1', # the number of forms rendered
'choices-INITIAL_FORMS': '0', # the number of forms with initial data
'choices-MAX_NUM_FORMS': '0', # max number of forms
'choices-0-choice': 'Calexico',
'choices-0-votes': '100',
}
# We treat FormSet pretty much like we would treat a normal Form. FormSet has an
# is_valid method, and a cleaned_data or errors attribute depending on whether all
# the forms passed validation. However, unlike a Form instance, cleaned_data and
# errors will be a list of dicts rather than just a single dict.
formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
self.assertTrue(formset.is_valid())
self.assertEqual([form.cleaned_data for form in formset.forms], [{'votes': 100, 'choice': u'Calexico'}])
# If a FormSet was not passed any data, its is_valid method should return False.
formset = ChoiceFormSet()
self.assertFalse(formset.is_valid())
def test_formset_validation(self):
# FormSet instances can also have an error attribute if validation failed for
# any of the forms.
data = {
'choices-TOTAL_FORMS': '1', # the number of forms rendered
'choices-INITIAL_FORMS': '0', # the number of forms with initial data
'choices-MAX_NUM_FORMS': '0', # max number of forms
'choices-0-choice': 'Calexico',
'choices-0-votes': '',
}
formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
self.assertFalse(formset.is_valid())
self.assertEqual(formset.errors, [{'votes': [u'This field is required.']}])
def test_formset_initial_data(self):
# We can also prefill a FormSet with existing data by providing an ``initial``
# argument to the constructor. ``initial`` should be a list of dicts. By default,
# an extra blank form is included.
initial = [{'choice': u'Calexico', 'votes': 100}]
formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices')
form_output = []
for form in formset.forms:
form_output.append(form.as_ul())
self.assertEqual('\n'.join(form_output), """<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
<li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>
<li>Choice: <input type="text" name="choices-1-choice" /></li>
<li>Votes: <input type="text" name="choices-1-votes" /></li>""")
# Let's simulate what would happen if we submitted this form.
data = {
'choices-TOTAL_FORMS': '2', # the number of forms rendered
'choices-INITIAL_FORMS': '1', # the number of forms with initial data
'choices-MAX_NUM_FORMS': '0', # max number of forms
'choices-0-choice': 'Calexico',
'choices-0-votes': '100',
'choices-1-choice': '',
'choices-1-votes': '',
}
formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
self.assertTrue(formset.is_valid())
self.assertEqual([form.cleaned_data for form in formset.forms], [{'votes': 100, 'choice': u'Calexico'}, {}])
def test_second_form_partially_filled(self):
# But the second form was blank! Shouldn't we get some errors? No. If we display
# a form as blank, it's ok for it to be submitted as blank. If we fill out even
# one of the fields of a blank form though, it will be validated. We may want to
# required that at least x number of forms are completed, but we'll show how to
# handle that later.
data = {
'choices-TOTAL_FORMS': '2', # the number of forms rendered
'choices-INITIAL_FORMS': '1', # the number of forms with initial data
'choices-MAX_NUM_FORMS': '0', # max number of forms
'choices-0-choice': 'Calexico',
'choices-0-votes': '100',
'choices-1-choice': 'The Decemberists',
'choices-1-votes': '', # missing value
}
formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
self.assertFalse(formset.is_valid())
self.assertEqual(formset.errors, [{}, {'votes': [u'This field is required.']}])
def test_delete_prefilled_data(self):
# If we delete data that was pre-filled, we should get an error. Simply removing
# data from form fields isn't the proper way to delete it. We'll see how to
# handle that case later.
data = {
'choices-TOTAL_FORMS': '2', # the number of forms rendered
'choices-INITIAL_FORMS': '1', # the number of forms with initial data
'choices-MAX_NUM_FORMS': '0', # max number of forms
'choices-0-choice': '', # deleted value
'choices-0-votes': '', # deleted value
'choices-1-choice': '',
'choices-1-votes': '',
}
formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
self.assertFalse(formset.is_valid())
self.assertEqual(formset.errors, [{'votes': [u'This field is required.'], 'choice': [u'This field is required.']}, {}])
def test_displaying_more_than_one_blank_form(self):
# Displaying more than 1 blank form ###########################################
# We can also display more than 1 empty form at a time. To do so, pass a
# extra argument to formset_factory.
ChoiceFormSet = formset_factory(Choice, extra=3)
formset = ChoiceFormSet(auto_id=False, prefix='choices')
form_output = []
for form in formset.forms:
form_output.append(form.as_ul())
self.assertEqual('\n'.join(form_output), """<li>Choice: <input type="text" name="choices-0-choice" /></li>
<li>Votes: <input type="text" name="choices-0-votes" /></li>
<li>Choice: <input type="text" name="choices-1-choice" /></li>
<li>Votes: <input type="text" name="choices-1-votes" /></li>
<li>Choice: <input type="text" name="choices-2-choice" /></li>
<li>Votes: <input type="text" name="choices-2-votes" /></li>""")
# Since we displayed every form as blank, we will also accept them back as blank.
# This may seem a little strange, but later we will show how to require a minimum
# number of forms to be completed.
data = {
'choices-TOTAL_FORMS': '3', # the number of forms rendered
'choices-INITIAL_FORMS': '0', # the number of forms with initial data
'choices-MAX_NUM_FORMS': '0', # max number of forms
'choices-0-choice': '',
'choices-0-votes': '',
'choices-1-choice': '',
'choices-1-votes': '',
'choices-2-choice': '',
'choices-2-votes': '',
}
formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
self.assertTrue(formset.is_valid())
self.assertEqual([form.cleaned_data for form in formset.forms], [{}, {}, {}])
def test_single_form_completed(self):
# We can just fill out one of the forms.
data = {
'choices-TOTAL_FORMS': '3', # the number of forms rendered
'choices-INITIAL_FORMS': '0', # the number of forms with initial data
'choices-MAX_NUM_FORMS': '0', # max number of forms
'choices-0-choice': 'Calexico',
'choices-0-votes': '100',
'choices-1-choice': '',
'choices-1-votes': '',
'choices-2-choice': '',
'choices-2-votes': '',
}
ChoiceFormSet = formset_factory(Choice, extra=3)
formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
self.assertTrue(formset.is_valid())
self.assertEqual([form.cleaned_data for form in formset.forms], [{'votes': 100, 'choice': u'Calexico'}, {}, {}])
def test_second_form_partially_filled_2(self):
# And once again, if we try to partially complete a form, validation will fail.
data = {
'choices-TOTAL_FORMS': '3', # the number of forms rendered
'choices-INITIAL_FORMS': '0', # the number of forms with initial data
'choices-MAX_NUM_FORMS': '0', # max number of forms
'choices-0-choice': 'Calexico',
'choices-0-votes': '100',
'choices-1-choice': 'The Decemberists',
'choices-1-votes': '', # missing value
'choices-2-choice': '',
'choices-2-votes': '',
}
ChoiceFormSet = formset_factory(Choice, extra=3)
formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
self.assertFalse(formset.is_valid())
self.assertEqual(formset.errors, [{}, {'votes': [u'This field is required.']}, {}])
def test_more_initial_data(self):
# The extra argument also works when the formset is pre-filled with initial
# data.
data = {
'choices-TOTAL_FORMS': '3', # the number of forms rendered
'choices-INITIAL_FORMS': '0', # the number of forms with initial data
'choices-MAX_NUM_FORMS': '0', # max number of forms
'choices-0-choice': 'Calexico',
'choices-0-votes': '100',
'choices-1-choice': '',
'choices-1-votes': '', # missing value
'choices-2-choice': '',
'choices-2-votes': '',
}
initial = [{'choice': u'Calexico', 'votes': 100}]
ChoiceFormSet = formset_factory(Choice, extra=3)
formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices')
form_output = []
for form in formset.forms:
form_output.append(form.as_ul())
self.assertEqual('\n'.join(form_output), """<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
<li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>
<li>Choice: <input type="text" name="choices-1-choice" /></li>
<li>Votes: <input type="text" name="choices-1-votes" /></li>
<li>Choice: <input type="text" name="choices-2-choice" /></li>
<li>Votes: <input type="text" name="choices-2-votes" /></li>
<li>Choice: <input type="text" name="choices-3-choice" /></li>
<li>Votes: <input type="text" name="choices-3-votes" /></li>""")
# Make sure retrieving an empty form works, and it shows up in the form list
self.assertTrue(formset.empty_form.empty_permitted)
self.assertEqual(formset.empty_form.as_ul(), """<li>Choice: <input type="text" name="choices-__prefix__-choice" /></li>
<li>Votes: <input type="text" name="choices-__prefix__-votes" /></li>""")
def test_formset_with_deletion(self):
# FormSets with deletion ######################################################
# We can easily add deletion ability to a FormSet with an argument to
# formset_factory. This will add a boolean field to each form instance. When
# that boolean field is True, the form will be in formset.deleted_forms
ChoiceFormSet = formset_factory(Choice, can_delete=True)
initial = [{'choice': u'Calexico', 'votes': 100}, {'choice': u'Fergie', 'votes': 900}]
formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices')
form_output = []
for form in formset.forms:
form_output.append(form.as_ul())
self.assertEqual('\n'.join(form_output), """<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
<li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>
<li>Delete: <input type="checkbox" name="choices-0-DELETE" /></li>
<li>Choice: <input type="text" name="choices-1-choice" value="Fergie" /></li>
<li>Votes: <input type="text" name="choices-1-votes" value="900" /></li>
<li>Delete: <input type="checkbox" name="choices-1-DELETE" /></li>
<li>Choice: <input type="text" name="choices-2-choice" /></li>
<li>Votes: <input type="text" name="choices-2-votes" /></li>
<li>Delete: <input type="checkbox" name="choices-2-DELETE" /></li>""")
# To delete something, we just need to set that form's special delete field to
# 'on'. Let's go ahead and delete Fergie.
data = {
'choices-TOTAL_FORMS': '3', # the number of forms rendered
'choices-INITIAL_FORMS': '2', # the number of forms with initial data
'choices-MAX_NUM_FORMS': '0', # max number of forms
'choices-0-choice': 'Calexico',
'choices-0-votes': '100',
'choices-0-DELETE': '',
'choices-1-choice': 'Fergie',
'choices-1-votes': '900',
'choices-1-DELETE': 'on',
'choices-2-choice': '',
'choices-2-votes': '',
'choices-2-DELETE': '',
}
formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
self.assertTrue(formset.is_valid())
self.assertEqual([form.cleaned_data for form in formset.forms], [{'votes': 100, 'DELETE': False, 'choice': u'Calexico'}, {'votes': 900, 'DELETE': True, 'choice': u'Fergie'}, {}])
self.assertEqual([form.cleaned_data for form in formset.deleted_forms], [{'votes': 900, 'DELETE': True, 'choice': u'Fergie'}])
# If we fill a form with something and then we check the can_delete checkbox for
# that form, that form's errors should not make the entire formset invalid since
# it's going to be deleted.
class CheckForm(Form):
field = IntegerField(min_value=100)
data = {
'check-TOTAL_FORMS': '3', # the number of forms rendered
'check-INITIAL_FORMS': '2', # the number of forms with initial data
'check-MAX_NUM_FORMS': '0', # max number of forms
'check-0-field': '200',
'check-0-DELETE': '',
'check-1-field': '50',
'check-1-DELETE': 'on',
'check-2-field': '',
'check-2-DELETE': '',
}
CheckFormSet = formset_factory(CheckForm, can_delete=True)
formset = CheckFormSet(data, prefix='check')
self.assertTrue(formset.is_valid())
# If we remove the deletion flag now we will have our validation back.
data['check-1-DELETE'] = ''
formset = CheckFormSet(data, prefix='check')
self.assertFalse(formset.is_valid())
# Should be able to get deleted_forms from a valid formset even if a
# deleted form would have been invalid.
class Person(Form):
name = CharField()
PeopleForm = formset_factory(
form=Person,
can_delete=True)
p = PeopleForm(
{'form-0-name': u'', 'form-0-DELETE': u'on', # no name!
'form-TOTAL_FORMS': 1, 'form-INITIAL_FORMS': 1,
'form-MAX_NUM_FORMS': 1})
self.assertTrue(p.is_valid())
self.assertEqual(len(p.deleted_forms), 1)
def test_formsets_with_ordering(self):
# FormSets with ordering ######################################################
# We can also add ordering ability to a FormSet with an argument to
# formset_factory. This will add a integer field to each form instance. When
# form validation succeeds, [form.cleaned_data for form in formset.forms] will have the data in the correct
# order specified by the ordering fields. If a number is duplicated in the set
# of ordering fields, for instance form 0 and form 3 are both marked as 1, then
# the form index used as a secondary ordering criteria. In order to put
# something at the front of the list, you'd need to set it's order to 0.
ChoiceFormSet = formset_factory(Choice, can_order=True)
initial = [{'choice': u'Calexico', 'votes': 100}, {'choice': u'Fergie', 'votes': 900}]
formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices')
form_output = []
for form in formset.forms:
form_output.append(form.as_ul())
self.assertEqual('\n'.join(form_output), """<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
<li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>
<li>Order: <input type="text" name="choices-0-ORDER" value="1" /></li>
<li>Choice: <input type="text" name="choices-1-choice" value="Fergie" /></li>
<li>Votes: <input type="text" name="choices-1-votes" value="900" /></li>
<li>Order: <input type="text" name="choices-1-ORDER" value="2" /></li>
<li>Choice: <input type="text" name="choices-2-choice" /></li>
<li>Votes: <input type="text" name="choices-2-votes" /></li>
<li>Order: <input type="text" name="choices-2-ORDER" /></li>""")
data = {
'choices-TOTAL_FORMS': '3', # the number of forms rendered
'choices-INITIAL_FORMS': '2', # the number of forms with initial data
'choices-MAX_NUM_FORMS': '0', # max number of forms
'choices-0-choice': 'Calexico',
'choices-0-votes': '100',
'choices-0-ORDER': '1',
'choices-1-choice': 'Fergie',
'choices-1-votes': '900',
'choices-1-ORDER': '2',
'choices-2-choice': 'The Decemberists',
'choices-2-votes': '500',
'choices-2-ORDER': '0',
}
formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
self.assertTrue(formset.is_valid())
form_output = []
for form in formset.ordered_forms:
form_output.append(form.cleaned_data)
self.assertEqual(form_output, [
{'votes': 500, 'ORDER': 0, 'choice': u'The Decemberists'},
{'votes': 100, 'ORDER': 1, 'choice': u'Calexico'},
{'votes': 900, 'ORDER': 2, 'choice': u'Fergie'},
])
def test_empty_ordered_fields(self):
# Ordering fields are allowed to be left blank, and if they *are* left blank,
# they will be sorted below everything else.
data = {
'choices-TOTAL_FORMS': '4', # the number of forms rendered
'choices-INITIAL_FORMS': '3', # the number of forms with initial data
'choices-MAX_NUM_FORMS': '0', # max number of forms
'choices-0-choice': 'Calexico',
'choices-0-votes': '100',
'choices-0-ORDER': '1',
'choices-1-choice': 'Fergie',
'choices-1-votes': '900',
'choices-1-ORDER': '2',
'choices-2-choice': 'The Decemberists',
'choices-2-votes': '500',
'choices-2-ORDER': '',
'choices-3-choice': 'Basia Bulat',
'choices-3-votes': '50',
'choices-3-ORDER': '',
}
ChoiceFormSet = formset_factory(Choice, can_order=True)
formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
self.assertTrue(formset.is_valid())
form_output = []
for form in formset.ordered_forms:
form_output.append(form.cleaned_data)
self.assertEqual(form_output, [
{'votes': 100, 'ORDER': 1, 'choice': u'Calexico'},
{'votes': 900, 'ORDER': 2, 'choice': u'Fergie'},
{'votes': 500, 'ORDER': None, 'choice': u'The Decemberists'},
{'votes': 50, 'ORDER': None, 'choice': u'Basia Bulat'},
])
def test_ordering_blank_fieldsets(self):
# Ordering should work with blank fieldsets.
data = {
'choices-TOTAL_FORMS': '3', # the number of forms rendered
'choices-INITIAL_FORMS': '0', # the number of forms with initial data
'choices-MAX_NUM_FORMS': '0', # max number of forms
}
ChoiceFormSet = formset_factory(Choice, can_order=True)
formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
self.assertTrue(formset.is_valid())
form_output = []
for form in formset.ordered_forms:
form_output.append(form.cleaned_data)
self.assertEqual(form_output, [])
def test_formset_with_ordering_and_deletion(self):
# FormSets with ordering + deletion ###########################################
# Let's try throwing ordering and deletion into the same form.
ChoiceFormSet = formset_factory(Choice, can_order=True, can_delete=True)
initial = [
{'choice': u'Calexico', 'votes': 100},
{'choice': u'Fergie', 'votes': 900},
{'choice': u'The Decemberists', 'votes': 500},
]
formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices')
form_output = []
for form in formset.forms:
form_output.append(form.as_ul())
self.assertEqual('\n'.join(form_output), """<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
<li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>
<li>Order: <input type="text" name="choices-0-ORDER" value="1" /></li>
<li>Delete: <input type="checkbox" name="choices-0-DELETE" /></li>
<li>Choice: <input type="text" name="choices-1-choice" value="Fergie" /></li>
<li>Votes: <input type="text" name="choices-1-votes" value="900" /></li>
<li>Order: <input type="text" name="choices-1-ORDER" value="2" /></li>
<li>Delete: <input type="checkbox" name="choices-1-DELETE" /></li>
<li>Choice: <input type="text" name="choices-2-choice" value="The Decemberists" /></li>
<li>Votes: <input type="text" name="choices-2-votes" value="500" /></li>
<li>Order: <input type="text" name="choices-2-ORDER" value="3" /></li>
<li>Delete: <input type="checkbox" name="choices-2-DELETE" /></li>
<li>Choice: <input type="text" name="choices-3-choice" /></li>
<li>Votes: <input type="text" name="choices-3-votes" /></li>
<li>Order: <input type="text" name="choices-3-ORDER" /></li>
<li>Delete: <input type="checkbox" name="choices-3-DELETE" /></li>""")
# Let's delete Fergie, and put The Decemberists ahead of Calexico.
data = {
'choices-TOTAL_FORMS': '4', # the number of forms rendered
'choices-INITIAL_FORMS': '3', # the number of forms with initial data
'choices-MAX_NUM_FORMS': '0', # max number of forms
'choices-0-choice': 'Calexico',
'choices-0-votes': '100',
'choices-0-ORDER': '1',
'choices-0-DELETE': '',
'choices-1-choice': 'Fergie',
'choices-1-votes': '900',
'choices-1-ORDER': '2',
'choices-1-DELETE': 'on',
'choices-2-choice': 'The Decemberists',
'choices-2-votes': '500',
'choices-2-ORDER': '0',
'choices-2-DELETE': '',
'choices-3-choice': '',
'choices-3-votes': '',
'choices-3-ORDER': '',
'choices-3-DELETE': '',
}
formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
self.assertTrue(formset.is_valid())
form_output = []
for form in formset.ordered_forms:
form_output.append(form.cleaned_data)
self.assertEqual(form_output, [
{'votes': 500, 'DELETE': False, 'ORDER': 0, 'choice': u'The Decemberists'},
{'votes': 100, 'DELETE': False, 'ORDER': 1, 'choice': u'Calexico'},
])
self.assertEqual([form.cleaned_data for form in formset.deleted_forms], [{'votes': 900, 'DELETE': True, 'ORDER': 2, 'choice': u'Fergie'}])
def test_invalid_deleted_form_with_ordering(self):
# Should be able to get ordered forms from a valid formset even if a
# deleted form would have been invalid.
class Person(Form):
name = CharField()
PeopleForm = formset_factory(form=Person, can_delete=True, can_order=True)
p = PeopleForm({
'form-0-name': u'',
'form-0-DELETE': u'on', # no name!
'form-TOTAL_FORMS': 1,
'form-INITIAL_FORMS': 1,
'form-MAX_NUM_FORMS': 1
})
self.assertTrue(p.is_valid())
self.assertEqual(p.ordered_forms, [])
def test_clean_hook(self):
# FormSet clean hook ##########################################################
# FormSets have a hook for doing extra validation that shouldn't be tied to any
# particular form. It follows the same pattern as the clean hook on Forms.
# We start out with a some duplicate data.
data = {
'drinks-TOTAL_FORMS': '2', # the number of forms rendered
'drinks-INITIAL_FORMS': '0', # the number of forms with initial data
'drinks-MAX_NUM_FORMS': '0', # max number of forms
'drinks-0-name': 'Gin and Tonic',
'drinks-1-name': 'Gin and Tonic',
}
formset = FavoriteDrinksFormSet(data, prefix='drinks')
self.assertFalse(formset.is_valid())
# Any errors raised by formset.clean() are available via the
# formset.non_form_errors() method.
for error in formset.non_form_errors():
self.assertEqual(str(error), 'You may only specify a drink once.')
# Make sure we didn't break the valid case.
data = {
'drinks-TOTAL_FORMS': '2', # the number of forms rendered
'drinks-INITIAL_FORMS': '0', # the number of forms with initial data
'drinks-MAX_NUM_FORMS': '0', # max number of forms
'drinks-0-name': 'Gin and Tonic',
'drinks-1-name': 'Bloody Mary',
}
formset = FavoriteDrinksFormSet(data, prefix='drinks')
self.assertTrue(formset.is_valid())
self.assertEqual(formset.non_form_errors(), [])
def test_limiting_max_forms(self):
# Limiting the maximum number of forms ########################################
# Base case for max_num.
# When not passed, max_num will take its default value of None, i.e. unlimited
# number of forms, only controlled by the value of the extra parameter.
LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=3)
formset = LimitedFavoriteDrinkFormSet()
form_output = []
for form in formset.forms:
form_output.append(str(form))
self.assertEqual('\n'.join(form_output), """<tr><th><label for="id_form-0-name">Name:</label></th><td><input type="text" name="form-0-name" id="id_form-0-name" /></td></tr>
<tr><th><label for="id_form-1-name">Name:</label></th><td><input type="text" name="form-1-name" id="id_form-1-name" /></td></tr>
<tr><th><label for="id_form-2-name">Name:</label></th><td><input type="text" name="form-2-name" id="id_form-2-name" /></td></tr>""")
# If max_num is 0 then no form is rendered at all.
LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=3, max_num=0)
formset = LimitedFavoriteDrinkFormSet()
form_output = []
for form in formset.forms:
form_output.append(str(form))
self.assertEqual('\n'.join(form_output), "")
LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=5, max_num=2)
formset = LimitedFavoriteDrinkFormSet()
form_output = []
for form in formset.forms:
form_output.append(str(form))
self.assertEqual('\n'.join(form_output), """<tr><th><label for="id_form-0-name">Name:</label></th><td><input type="text" name="form-0-name" id="id_form-0-name" /></td></tr>
<tr><th><label for="id_form-1-name">Name:</label></th><td><input type="text" name="form-1-name" id="id_form-1-name" /></td></tr>""")
# Ensure that max_num has no effect when extra is less than max_num.
LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=1, max_num=2)
formset = LimitedFavoriteDrinkFormSet()
form_output = []
for form in formset.forms:
form_output.append(str(form))
self.assertEqual('\n'.join(form_output), """<tr><th><label for="id_form-0-name">Name:</label></th><td><input type="text" name="form-0-name" id="id_form-0-name" /></td></tr>""")
def test_max_num_with_initial_data(self):
# max_num with initial data
# When not passed, max_num will take its default value of None, i.e. unlimited
# number of forms, only controlled by the values of the initial and extra
# parameters.
initial = [
{'name': 'Fernet and Coke'},
]
LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=1)
formset = LimitedFavoriteDrinkFormSet(initial=initial)
form_output = []
for form in formset.forms:
form_output.append(str(form))
self.assertEqual('\n'.join(form_output), """<tr><th><label for="id_form-0-name">Name:</label></th><td><input type="text" name="form-0-name" value="Fernet and Coke" id="id_form-0-name" /></td></tr>
<tr><th><label for="id_form-1-name">Name:</label></th><td><input type="text" name="form-1-name" id="id_form-1-name" /></td></tr>""")
def test_max_num_zero(self):
# If max_num is 0 then no form is rendered at all, even if extra and initial
# are specified.
initial = [
{'name': 'Fernet and Coke'},
{'name': 'Bloody Mary'},
]
LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=1, max_num=0)
formset = LimitedFavoriteDrinkFormSet(initial=initial)
form_output = []
for form in formset.forms:
form_output.append(str(form))
self.assertEqual('\n'.join(form_output), "")
def test_more_initial_than_max_num(self):
# More initial forms than max_num will result in only the first max_num of
# them to be displayed with no extra forms.
initial = [
{'name': 'Gin Tonic'},
{'name': 'Bloody Mary'},
{'name': 'Jack and Coke'},
]
LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=1, max_num=2)
formset = LimitedFavoriteDrinkFormSet(initial=initial)
form_output = []
for form in formset.forms:
form_output.append(str(form))
self.assertEqual('\n'.join(form_output), """<tr><th><label for="id_form-0-name">Name:</label></th><td><input type="text" name="form-0-name" value="Gin Tonic" id="id_form-0-name" /></td></tr>
<tr><th><label for="id_form-1-name">Name:</label></th><td><input type="text" name="form-1-name" value="Bloody Mary" id="id_form-1-name" /></td></tr>""")
# One form from initial and extra=3 with max_num=2 should result in the one
# initial form and one extra.
initial = [
{'name': 'Gin Tonic'},
]
LimitedFavoriteDrinkFormSet = formset_factory(FavoriteDrinkForm, extra=3, max_num=2)
formset = LimitedFavoriteDrinkFormSet(initial=initial)
form_output = []
for form in formset.forms:
form_output.append(str(form))
self.assertEqual('\n'.join(form_output), """<tr><th><label for="id_form-0-name">Name:</label></th><td><input type="text" name="form-0-name" value="Gin Tonic" id="id_form-0-name" /></td></tr>
<tr><th><label for="id_form-1-name">Name:</label></th><td><input type="text" name="form-1-name" id="id_form-1-name" /></td></tr>""")
def test_regression_6926(self):
# Regression test for #6926 ##################################################
# Make sure the management form has the correct prefix.
formset = FavoriteDrinksFormSet()
self.assertEqual(formset.management_form.prefix, 'form')
formset = FavoriteDrinksFormSet(data={})
self.assertEqual(formset.management_form.prefix, 'form')
formset = FavoriteDrinksFormSet(initial={})
self.assertEqual(formset.management_form.prefix, 'form')
def test_regression_12878(self):
# Regression test for #12878 #################################################
data = {
'drinks-TOTAL_FORMS': '2', # the number of forms rendered
'drinks-INITIAL_FORMS': '0', # the number of forms with initial data
'drinks-MAX_NUM_FORMS': '0', # max number of forms
'drinks-0-name': 'Gin and Tonic',
'drinks-1-name': 'Gin and Tonic',
}
formset = FavoriteDrinksFormSet(data, prefix='drinks')
self.assertFalse(formset.is_valid())
self.assertEqual(formset.non_form_errors(), [u'You may only specify a drink once.'])
data = {
'choices-TOTAL_FORMS': '1', # the number of forms rendered
'choices-INITIAL_FORMS': '0', # the number of forms with initial data
'choices-MAX_NUM_FORMS': '0', # max number of forms
'choices-0-choice': 'Calexico',
'choices-0-votes': '100',
}
class Choice(Form):
choice = CharField()
votes = IntegerField()
ChoiceFormSet = formset_factory(Choice)
class FormsetAsFooTests(TestCase):
def test_as_table(self):
formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
self.assertEqual(formset.as_table(),"""<input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" /><input type="hidden" name="choices-MAX_NUM_FORMS" value="0" />
<tr><th>Choice:</th><td><input type="text" name="choices-0-choice" value="Calexico" /></td></tr>
<tr><th>Votes:</th><td><input type="text" name="choices-0-votes" value="100" /></td></tr>""")
def test_as_p(self):
formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
self.assertEqual(formset.as_p(),"""<input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" /><input type="hidden" name="choices-MAX_NUM_FORMS" value="0" />
<p>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></p>
<p>Votes: <input type="text" name="choices-0-votes" value="100" /></p>""")
def test_as_ul(self):
formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
self.assertEqual(formset.as_ul(),"""<input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" /><input type="hidden" name="choices-MAX_NUM_FORMS" value="0" />
<li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
<li>Votes: <input type="text" name="choices-0-votes" value="100" /></li>""")

View File

@ -0,0 +1,460 @@
# -*- coding: utf-8 -*-
from django.conf import settings
from django.forms import TextInput, Media, TextInput, CharField, Form, MultiWidget
from django.utils.unittest import TestCase
class FormsMediaTestCase(TestCase):
# Tests for the media handling on widgets and forms
def setUp(self):
super(FormsMediaTestCase, self).setUp()
self.original_media_url = settings.MEDIA_URL
settings.MEDIA_URL = 'http://media.example.com/media/'
def tearDown(self):
settings.MEDIA_URL = self.original_media_url
super(FormsMediaTestCase, self).tearDown()
def test_construction(self):
# Check construction of media objects
m = Media(css={'all': ('path/to/css1','/path/to/css2')}, js=('/path/to/js1','http://media.other.com/path/to/js2','https://secure.other.com/path/to/js3'))
self.assertEqual(str(m), """<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>""")
class Foo:
css = {
'all': ('path/to/css1','/path/to/css2')
}
js = ('/path/to/js1','http://media.other.com/path/to/js2','https://secure.other.com/path/to/js3')
m3 = Media(Foo)
self.assertEqual(str(m3), """<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>""")
# A widget can exist without a media definition
class MyWidget(TextInput):
pass
w = MyWidget()
self.assertEqual(str(w.media), '')
def test_media_dsl(self):
###############################################################
# DSL Class-based media definitions
###############################################################
# A widget can define media if it needs to.
# Any absolute path will be preserved; relative paths are combined
# with the value of settings.MEDIA_URL
class MyWidget1(TextInput):
class Media:
css = {
'all': ('path/to/css1','/path/to/css2')
}
js = ('/path/to/js1','http://media.other.com/path/to/js2','https://secure.other.com/path/to/js3')
w1 = MyWidget1()
self.assertEqual(str(w1.media), """<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>""")
# Media objects can be interrogated by media type
self.assertEqual(str(w1.media['css']), """<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />""")
self.assertEqual(str(w1.media['js']), """<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>""")
def test_combine_media(self):
# Media objects can be combined. Any given media resource will appear only
# once. Duplicated media definitions are ignored.
class MyWidget1(TextInput):
class Media:
css = {
'all': ('path/to/css1','/path/to/css2')
}
js = ('/path/to/js1','http://media.other.com/path/to/js2','https://secure.other.com/path/to/js3')
class MyWidget2(TextInput):
class Media:
css = {
'all': ('/path/to/css2','/path/to/css3')
}
js = ('/path/to/js1','/path/to/js4')
class MyWidget3(TextInput):
class Media:
css = {
'all': ('/path/to/css3','path/to/css1')
}
js = ('/path/to/js1','/path/to/js4')
w1 = MyWidget1()
w2 = MyWidget2()
w3 = MyWidget3()
self.assertEqual(str(w1.media + w2.media + w3.media), """<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
<script type="text/javascript" src="/path/to/js4"></script>""")
# Check that media addition hasn't affected the original objects
self.assertEqual(str(w1.media), """<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>""")
# Regression check for #12879: specifying the same CSS or JS file
# multiple times in a single Media instance should result in that file
# only being included once.
class MyWidget4(TextInput):
class Media:
css = {'all': ('/path/to/css1', '/path/to/css1')}
js = ('/path/to/js1', '/path/to/js1')
w4 = MyWidget4()
self.assertEqual(str(w4.media), """<link href="/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>""")
def test_media_property(self):
###############################################################
# Property-based media definitions
###############################################################
# Widget media can be defined as a property
class MyWidget4(TextInput):
def _media(self):
return Media(css={'all': ('/some/path',)}, js = ('/some/js',))
media = property(_media)
w4 = MyWidget4()
self.assertEqual(str(w4.media), """<link href="/some/path" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/some/js"></script>""")
# Media properties can reference the media of their parents
class MyWidget5(MyWidget4):
def _media(self):
return super(MyWidget5, self).media + Media(css={'all': ('/other/path',)}, js = ('/other/js',))
media = property(_media)
w5 = MyWidget5()
self.assertEqual(str(w5.media), """<link href="/some/path" type="text/css" media="all" rel="stylesheet" />
<link href="/other/path" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/some/js"></script>
<script type="text/javascript" src="/other/js"></script>""")
def test_media_property_parent_references(self):
# Media properties can reference the media of their parents,
# even if the parent media was defined using a class
class MyWidget1(TextInput):
class Media:
css = {
'all': ('path/to/css1','/path/to/css2')
}
js = ('/path/to/js1','http://media.other.com/path/to/js2','https://secure.other.com/path/to/js3')
class MyWidget6(MyWidget1):
def _media(self):
return super(MyWidget6, self).media + Media(css={'all': ('/other/path',)}, js = ('/other/js',))
media = property(_media)
w6 = MyWidget6()
self.assertEqual(str(w6.media), """<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<link href="/other/path" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
<script type="text/javascript" src="/other/js"></script>""")
def test_media_inheritance(self):
###############################################################
# Inheritance of media
###############################################################
# If a widget extends another but provides no media definition, it inherits the parent widget's media
class MyWidget1(TextInput):
class Media:
css = {
'all': ('path/to/css1','/path/to/css2')
}
js = ('/path/to/js1','http://media.other.com/path/to/js2','https://secure.other.com/path/to/js3')
class MyWidget7(MyWidget1):
pass
w7 = MyWidget7()
self.assertEqual(str(w7.media), """<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>""")
# If a widget extends another but defines media, it extends the parent widget's media by default
class MyWidget8(MyWidget1):
class Media:
css = {
'all': ('/path/to/css3','path/to/css1')
}
js = ('/path/to/js1','/path/to/js4')
w8 = MyWidget8()
self.assertEqual(str(w8.media), """<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
<script type="text/javascript" src="/path/to/js4"></script>""")
def test_media_inheritance_from_property(self):
# If a widget extends another but defines media, it extends the parents widget's media,
# even if the parent defined media using a property.
class MyWidget1(TextInput):
class Media:
css = {
'all': ('path/to/css1','/path/to/css2')
}
js = ('/path/to/js1','http://media.other.com/path/to/js2','https://secure.other.com/path/to/js3')
class MyWidget4(TextInput):
def _media(self):
return Media(css={'all': ('/some/path',)}, js = ('/some/js',))
media = property(_media)
class MyWidget9(MyWidget4):
class Media:
css = {
'all': ('/other/path',)
}
js = ('/other/js',)
w9 = MyWidget9()
self.assertEqual(str(w9.media), """<link href="/some/path" type="text/css" media="all" rel="stylesheet" />
<link href="/other/path" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/some/js"></script>
<script type="text/javascript" src="/other/js"></script>""")
# A widget can disable media inheritance by specifying 'extend=False'
class MyWidget10(MyWidget1):
class Media:
extend = False
css = {
'all': ('/path/to/css3','path/to/css1')
}
js = ('/path/to/js1','/path/to/js4')
w10 = MyWidget10()
self.assertEqual(str(w10.media), """<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="/path/to/js4"></script>""")
def test_media_inheritance_extends(self):
# A widget can explicitly enable full media inheritance by specifying 'extend=True'
class MyWidget1(TextInput):
class Media:
css = {
'all': ('path/to/css1','/path/to/css2')
}
js = ('/path/to/js1','http://media.other.com/path/to/js2','https://secure.other.com/path/to/js3')
class MyWidget11(MyWidget1):
class Media:
extend = True
css = {
'all': ('/path/to/css3','path/to/css1')
}
js = ('/path/to/js1','/path/to/js4')
w11 = MyWidget11()
self.assertEqual(str(w11.media), """<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
<script type="text/javascript" src="/path/to/js4"></script>""")
def test_media_inheritance_single_type(self):
# A widget can enable inheritance of one media type by specifying extend as a tuple
class MyWidget1(TextInput):
class Media:
css = {
'all': ('path/to/css1','/path/to/css2')
}
js = ('/path/to/js1','http://media.other.com/path/to/js2','https://secure.other.com/path/to/js3')
class MyWidget12(MyWidget1):
class Media:
extend = ('css',)
css = {
'all': ('/path/to/css3','path/to/css1')
}
js = ('/path/to/js1','/path/to/js4')
w12 = MyWidget12()
self.assertEqual(str(w12.media), """<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="/path/to/js4"></script>""")
def test_multi_media(self):
###############################################################
# Multi-media handling for CSS
###############################################################
# A widget can define CSS media for multiple output media types
class MultimediaWidget(TextInput):
class Media:
css = {
'screen, print': ('/file1','/file2'),
'screen': ('/file3',),
'print': ('/file4',)
}
js = ('/path/to/js1','/path/to/js4')
multimedia = MultimediaWidget()
self.assertEqual(str(multimedia.media), """<link href="/file4" type="text/css" media="print" rel="stylesheet" />
<link href="/file3" type="text/css" media="screen" rel="stylesheet" />
<link href="/file1" type="text/css" media="screen, print" rel="stylesheet" />
<link href="/file2" type="text/css" media="screen, print" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="/path/to/js4"></script>""")
def test_multi_widget(self):
###############################################################
# Multiwidget media handling
###############################################################
class MyWidget1(TextInput):
class Media:
css = {
'all': ('path/to/css1','/path/to/css2')
}
js = ('/path/to/js1','http://media.other.com/path/to/js2','https://secure.other.com/path/to/js3')
class MyWidget2(TextInput):
class Media:
css = {
'all': ('/path/to/css2','/path/to/css3')
}
js = ('/path/to/js1','/path/to/js4')
class MyWidget3(TextInput):
class Media:
css = {
'all': ('/path/to/css3','path/to/css1')
}
js = ('/path/to/js1','/path/to/js4')
# MultiWidgets have a default media definition that gets all the
# media from the component widgets
class MyMultiWidget(MultiWidget):
def __init__(self, attrs=None):
widgets = [MyWidget1, MyWidget2, MyWidget3]
super(MyMultiWidget, self).__init__(widgets, attrs)
mymulti = MyMultiWidget()
self.assertEqual(str(mymulti.media), """<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
<script type="text/javascript" src="/path/to/js4"></script>""")
def test_form_media(self):
###############################################################
# Media processing for forms
###############################################################
class MyWidget1(TextInput):
class Media:
css = {
'all': ('path/to/css1','/path/to/css2')
}
js = ('/path/to/js1','http://media.other.com/path/to/js2','https://secure.other.com/path/to/js3')
class MyWidget2(TextInput):
class Media:
css = {
'all': ('/path/to/css2','/path/to/css3')
}
js = ('/path/to/js1','/path/to/js4')
class MyWidget3(TextInput):
class Media:
css = {
'all': ('/path/to/css3','path/to/css1')
}
js = ('/path/to/js1','/path/to/js4')
# You can ask a form for the media required by its widgets.
class MyForm(Form):
field1 = CharField(max_length=20, widget=MyWidget1())
field2 = CharField(max_length=20, widget=MyWidget2())
f1 = MyForm()
self.assertEqual(str(f1.media), """<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
<script type="text/javascript" src="/path/to/js4"></script>""")
# Form media can be combined to produce a single media definition.
class AnotherForm(Form):
field3 = CharField(max_length=20, widget=MyWidget3())
f2 = AnotherForm()
self.assertEqual(str(f1.media + f2.media), """<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
<script type="text/javascript" src="/path/to/js4"></script>""")
# Forms can also define media, following the same rules as widgets.
class FormWithMedia(Form):
field1 = CharField(max_length=20, widget=MyWidget1())
field2 = CharField(max_length=20, widget=MyWidget2())
class Media:
js = ('/some/form/javascript',)
css = {
'all': ('/some/form/css',)
}
f3 = FormWithMedia()
self.assertEqual(str(f3.media), """<link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
<link href="/some/form/css" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
<script type="text/javascript" src="/path/to/js4"></script>
<script type="text/javascript" src="/some/form/javascript"></script>""")
# Media works in templates
from django.template import Template, Context
self.assertEqual(Template("{{ form.media.js }}{{ form.media.css }}").render(Context({'form': f3})), """<script type="text/javascript" src="/path/to/js1"></script>
<script type="text/javascript" src="http://media.other.com/path/to/js2"></script>
<script type="text/javascript" src="https://secure.other.com/path/to/js3"></script>
<script type="text/javascript" src="/path/to/js4"></script>
<script type="text/javascript" src="/some/form/javascript"></script><link href="http://media.example.com/media/path/to/css1" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css2" type="text/css" media="all" rel="stylesheet" />
<link href="/path/to/css3" type="text/css" media="all" rel="stylesheet" />
<link href="/some/form/css" type="text/css" media="all" rel="stylesheet" />""")

View File

@ -0,0 +1,161 @@
# -*- coding: utf-8 -*-
import datetime
from django.core.files.uploadedfile import SimpleUploadedFile
from django.forms import Form, ModelForm, FileField, ModelChoiceField
from django.test import TestCase
from regressiontests.forms.models import ChoiceModel, ChoiceOptionModel, ChoiceFieldModel, FileModel, Group, BoundaryModel, Defaults
class ChoiceFieldForm(ModelForm):
class Meta:
model = ChoiceFieldModel
class FileForm(Form):
file1 = FileField()
class TestTicket12510(TestCase):
''' It is not necessary to generate choices for ModelChoiceField (regression test for #12510). '''
def setUp(self):
self.groups = [Group.objects.create(name=name) for name in 'abc']
def test_choices_not_fetched_when_not_rendering(self):
def test():
field = ModelChoiceField(Group.objects.order_by('-name'))
self.assertEqual('a', field.clean(self.groups[0].pk).name)
# only one query is required to pull the model from DB
self.assertNumQueries(1, test)
class ModelFormCallableModelDefault(TestCase):
def test_no_empty_option(self):
"If a model's ForeignKey has blank=False and a default, no empty option is created (Refs #10792)."
option = ChoiceOptionModel.objects.create(name='default')
choices = list(ChoiceFieldForm().fields['choice'].choices)
self.assertEquals(len(choices), 1)
self.assertEquals(choices[0], (option.pk, unicode(option)))
def test_callable_initial_value(self):
"The initial value for a callable default returning a queryset is the pk (refs #13769)"
obj1 = ChoiceOptionModel.objects.create(id=1, name='default')
obj2 = ChoiceOptionModel.objects.create(id=2, name='option 2')
obj3 = ChoiceOptionModel.objects.create(id=3, name='option 3')
self.assertEquals(ChoiceFieldForm().as_p(), """<p><label for="id_choice">Choice:</label> <select name="choice" id="id_choice">
<option value="1" selected="selected">ChoiceOption 1</option>
<option value="2">ChoiceOption 2</option>
<option value="3">ChoiceOption 3</option>
</select><input type="hidden" name="initial-choice" value="1" id="initial-id_choice" /></p>
<p><label for="id_choice_int">Choice int:</label> <select name="choice_int" id="id_choice_int">
<option value="1" selected="selected">ChoiceOption 1</option>
<option value="2">ChoiceOption 2</option>
<option value="3">ChoiceOption 3</option>
</select><input type="hidden" name="initial-choice_int" value="1" id="initial-id_choice_int" /></p>
<p><label for="id_multi_choice">Multi choice:</label> <select multiple="multiple" name="multi_choice" id="id_multi_choice">
<option value="1" selected="selected">ChoiceOption 1</option>
<option value="2">ChoiceOption 2</option>
<option value="3">ChoiceOption 3</option>
</select><input type="hidden" name="initial-multi_choice" value="1" id="initial-id_multi_choice_0" /> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></p>
<p><label for="id_multi_choice_int">Multi choice int:</label> <select multiple="multiple" name="multi_choice_int" id="id_multi_choice_int">
<option value="1" selected="selected">ChoiceOption 1</option>
<option value="2">ChoiceOption 2</option>
<option value="3">ChoiceOption 3</option>
</select><input type="hidden" name="initial-multi_choice_int" value="1" id="initial-id_multi_choice_int_0" /> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></p>""")
def test_initial_instance_value(self):
"Initial instances for model fields may also be instances (refs #7287)"
obj1 = ChoiceOptionModel.objects.create(id=1, name='default')
obj2 = ChoiceOptionModel.objects.create(id=2, name='option 2')
obj3 = ChoiceOptionModel.objects.create(id=3, name='option 3')
self.assertEquals(ChoiceFieldForm(initial={
'choice': obj2,
'choice_int': obj2,
'multi_choice': [obj2,obj3],
'multi_choice_int': ChoiceOptionModel.objects.exclude(name="default"),
}).as_p(), """<p><label for="id_choice">Choice:</label> <select name="choice" id="id_choice">
<option value="1">ChoiceOption 1</option>
<option value="2" selected="selected">ChoiceOption 2</option>
<option value="3">ChoiceOption 3</option>
</select><input type="hidden" name="initial-choice" value="2" id="initial-id_choice" /></p>
<p><label for="id_choice_int">Choice int:</label> <select name="choice_int" id="id_choice_int">
<option value="1">ChoiceOption 1</option>
<option value="2" selected="selected">ChoiceOption 2</option>
<option value="3">ChoiceOption 3</option>
</select><input type="hidden" name="initial-choice_int" value="2" id="initial-id_choice_int" /></p>
<p><label for="id_multi_choice">Multi choice:</label> <select multiple="multiple" name="multi_choice" id="id_multi_choice">
<option value="1">ChoiceOption 1</option>
<option value="2" selected="selected">ChoiceOption 2</option>
<option value="3" selected="selected">ChoiceOption 3</option>
</select><input type="hidden" name="initial-multi_choice" value="2" id="initial-id_multi_choice_0" />
<input type="hidden" name="initial-multi_choice" value="3" id="initial-id_multi_choice_1" /> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></p>
<p><label for="id_multi_choice_int">Multi choice int:</label> <select multiple="multiple" name="multi_choice_int" id="id_multi_choice_int">
<option value="1">ChoiceOption 1</option>
<option value="2" selected="selected">ChoiceOption 2</option>
<option value="3" selected="selected">ChoiceOption 3</option>
</select><input type="hidden" name="initial-multi_choice_int" value="2" id="initial-id_multi_choice_int_0" />
<input type="hidden" name="initial-multi_choice_int" value="3" id="initial-id_multi_choice_int_1" /> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></p>""")
class FormsModelTestCase(TestCase):
def test_unicode_filename(self):
# FileModel with unicode filename and data #########################
f = FileForm(data={}, files={'file1': SimpleUploadedFile('我隻氣墊船裝滿晒鱔.txt', 'मेरी मँडराने वाली नाव सर्पमीनों से भरी ह')}, auto_id=False)
self.assertTrue(f.is_valid())
self.assertTrue('file1' in f.cleaned_data)
m = FileModel.objects.create(file=f.cleaned_data['file1'])
self.assertEqual(m.file.name, u'tests/\u6211\u96bb\u6c23\u588a\u8239\u88dd\u6eff\u6652\u9c54.txt')
m.delete()
def test_boundary_conditions(self):
# Boundary conditions on a PostitiveIntegerField #########################
class BoundaryForm(ModelForm):
class Meta:
model = BoundaryModel
f = BoundaryForm({'positive_integer': 100})
self.assertTrue(f.is_valid())
f = BoundaryForm({'positive_integer': 0})
self.assertTrue(f.is_valid())
f = BoundaryForm({'positive_integer': -100})
self.assertFalse(f.is_valid())
def test_formfield_initial(self):
# Formfield initial values ########
# If the model has default values for some fields, they are used as the formfield
# initial values.
class DefaultsForm(ModelForm):
class Meta:
model = Defaults
self.assertEqual(DefaultsForm().fields['name'].initial, u'class default value')
self.assertEqual(DefaultsForm().fields['def_date'].initial, datetime.date(1980, 1, 1))
self.assertEqual(DefaultsForm().fields['value'].initial, 42)
r1 = DefaultsForm()['callable_default'].as_widget()
r2 = DefaultsForm()['callable_default'].as_widget()
self.assertNotEqual(r1, r2)
# In a ModelForm that is passed an instance, the initial values come from the
# instance's values, not the model's defaults.
foo_instance = Defaults(name=u'instance value', def_date=datetime.date(1969, 4, 4), value=12)
instance_form = DefaultsForm(instance=foo_instance)
self.assertEqual(instance_form.initial['name'], u'instance value')
self.assertEqual(instance_form.initial['def_date'], datetime.date(1969, 4, 4))
self.assertEqual(instance_form.initial['value'], 12)
from django.forms import CharField
class ExcludingForm(ModelForm):
name = CharField(max_length=255)
class Meta:
model = Defaults
exclude = ['name', 'callable_default']
f = ExcludingForm({'name': u'Hello', 'value': 99, 'def_date': datetime.date(1999, 3, 2)})
self.assertTrue(f.is_valid())
self.assertEqual(f.cleaned_data['name'], u'Hello')
obj = f.save()
self.assertEqual(obj.name, u'class default value')
self.assertEqual(obj.value, 99)
self.assertEqual(obj.def_date, datetime.date(1999, 3, 2))

View File

@ -0,0 +1,122 @@
# -*- coding: utf-8 -*-
from django.forms import *
from django.utils.unittest import TestCase
from django.utils.translation import ugettext_lazy, activate, deactivate
class FormsRegressionsTestCase(TestCase):
def test_class(self):
# Tests to prevent against recurrences of earlier bugs.
extra_attrs = {'class': 'special'}
class TestForm(Form):
f1 = CharField(max_length=10, widget=TextInput(attrs=extra_attrs))
f2 = CharField(widget=TextInput(attrs=extra_attrs))
self.assertEqual(TestForm(auto_id=False).as_p(), u'<p>F1: <input type="text" class="special" name="f1" maxlength="10" /></p>\n<p>F2: <input type="text" class="special" name="f2" /></p>')
def test_regression_3600(self):
# Tests for form i18n #
# There were some problems with form translations in #3600
class SomeForm(Form):
username = CharField(max_length=10, label=ugettext_lazy('Username'))
f = SomeForm()
self.assertEqual(f.as_p(), '<p><label for="id_username">Username:</label> <input id="id_username" type="text" name="username" maxlength="10" /></p>')
# Translations are done at rendering time, so multi-lingual apps can define forms)
activate('de')
self.assertEqual(f.as_p(), '<p><label for="id_username">Benutzername:</label> <input id="id_username" type="text" name="username" maxlength="10" /></p>')
activate('pl')
self.assertEqual(f.as_p(), u'<p><label for="id_username">Nazwa u\u017cytkownika:</label> <input id="id_username" type="text" name="username" maxlength="10" /></p>')
deactivate()
def test_regression_5216(self):
# There was some problems with form translations in #5216
class SomeForm(Form):
field_1 = CharField(max_length=10, label=ugettext_lazy('field_1'))
field_2 = CharField(max_length=10, label=ugettext_lazy('field_2'), widget=TextInput(attrs={'id': 'field_2_id'}))
f = SomeForm()
self.assertEqual(f['field_1'].label_tag(), '<label for="id_field_1">field_1</label>')
self.assertEqual(f['field_2'].label_tag(), '<label for="field_2_id">field_2</label>')
# Unicode decoding problems...
GENDERS = ((u'\xc5', u'En tied\xe4'), (u'\xf8', u'Mies'), (u'\xdf', u'Nainen'))
class SomeForm(Form):
somechoice = ChoiceField(choices=GENDERS, widget=RadioSelect(), label=u'\xc5\xf8\xdf')
f = SomeForm()
self.assertEqual(f.as_p(), u'<p><label for="id_somechoice_0">\xc5\xf8\xdf:</label> <ul>\n<li><label for="id_somechoice_0"><input type="radio" id="id_somechoice_0" value="\xc5" name="somechoice" /> En tied\xe4</label></li>\n<li><label for="id_somechoice_1"><input type="radio" id="id_somechoice_1" value="\xf8" name="somechoice" /> Mies</label></li>\n<li><label for="id_somechoice_2"><input type="radio" id="id_somechoice_2" value="\xdf" name="somechoice" /> Nainen</label></li>\n</ul></p>')
# Testing choice validation with UTF-8 bytestrings as input (these are the
# Russian abbreviations "мес." and "шт.".
UNITS = (('\xd0\xbc\xd0\xb5\xd1\x81.', '\xd0\xbc\xd0\xb5\xd1\x81.'), ('\xd1\x88\xd1\x82.', '\xd1\x88\xd1\x82.'))
f = ChoiceField(choices=UNITS)
self.assertEqual(f.clean(u'\u0448\u0442.'), u'\u0448\u0442.')
self.assertEqual(f.clean('\xd1\x88\xd1\x82.'), u'\u0448\u0442.')
# Translated error messages used to be buggy.
activate('ru')
f = SomeForm({})
self.assertEqual(f.as_p(), u'<ul class="errorlist"><li>\u041e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043f\u043e\u043b\u0435.</li></ul>\n<p><label for="id_somechoice_0">\xc5\xf8\xdf:</label> <ul>\n<li><label for="id_somechoice_0"><input type="radio" id="id_somechoice_0" value="\xc5" name="somechoice" /> En tied\xe4</label></li>\n<li><label for="id_somechoice_1"><input type="radio" id="id_somechoice_1" value="\xf8" name="somechoice" /> Mies</label></li>\n<li><label for="id_somechoice_2"><input type="radio" id="id_somechoice_2" value="\xdf" name="somechoice" /> Nainen</label></li>\n</ul></p>')
deactivate()
# Deep copying translated text shouldn't raise an error)
from django.utils.translation import gettext_lazy
class CopyForm(Form):
degree = IntegerField(widget=Select(choices=((1, gettext_lazy('test')),)))
f = CopyForm()
def test_misc(self):
# There once was a problem with Form fields called "data". Let's make sure that
# doesn't come back.
class DataForm(Form):
data = CharField(max_length=10)
f = DataForm({'data': 'xyzzy'})
self.assertTrue(f.is_valid())
self.assertEqual(f.cleaned_data, {'data': u'xyzzy'})
# A form with *only* hidden fields that has errors is going to be very unusual.
class HiddenForm(Form):
data = IntegerField(widget=HiddenInput)
f = HiddenForm({})
self.assertEqual(f.as_p(), u'<ul class="errorlist"><li>(Hidden field data) This field is required.</li></ul>\n<p> <input type="hidden" name="data" id="id_data" /></p>')
self.assertEqual(f.as_table(), u'<tr><td colspan="2"><ul class="errorlist"><li>(Hidden field data) This field is required.</li></ul><input type="hidden" name="data" id="id_data" /></td></tr>')
def test_xss_error_messages(self):
###################################################
# Tests for XSS vulnerabilities in error messages #
###################################################
# The forms layer doesn't escape input values directly because error messages
# might be presented in non-HTML contexts. Instead, the message is just marked
# for escaping by the template engine. So we'll need to construct a little
# silly template to trigger the escaping.
from django.template import Template, Context
t = Template('{{ form.errors }}')
class SomeForm(Form):
field = ChoiceField(choices=[('one', 'One')])
f = SomeForm({'field': '<script>'})
self.assertEqual(t.render(Context({'form': f})), u'<ul class="errorlist"><li>field<ul class="errorlist"><li>Select a valid choice. &lt;script&gt; is not one of the available choices.</li></ul></li></ul>')
class SomeForm(Form):
field = MultipleChoiceField(choices=[('one', 'One')])
f = SomeForm({'field': ['<script>']})
self.assertEqual(t.render(Context({'form': f})), u'<ul class="errorlist"><li>field<ul class="errorlist"><li>Select a valid choice. &lt;script&gt; is not one of the available choices.</li></ul></li></ul>')
from regressiontests.forms.models import ChoiceModel
class SomeForm(Form):
field = ModelMultipleChoiceField(ChoiceModel.objects.all())
f = SomeForm({'field': ['<script>']})
self.assertEqual(t.render(Context({'form': f})), u'<ul class="errorlist"><li>field<ul class="errorlist"><li>&quot;&lt;script&gt;&quot; is not a valid value for a primary key.</li></ul></li></ul>')

View File

@ -0,0 +1,57 @@
# -*- coding: utf-8 -*-
from django.core.exceptions import ValidationError
from django.forms.util import *
from django.utils.translation import ugettext_lazy
from django.utils.unittest import TestCase
class FormsUtilTestCase(TestCase):
# Tests for forms/util.py module.
def test_flatatt(self):
###########
# flatatt #
###########
self.assertEqual(flatatt({'id': "header"}), u' id="header"')
self.assertEqual(flatatt({'class': "news", 'title': "Read this"}), u' class="news" title="Read this"')
self.assertEqual(flatatt({}), u'')
def test_validation_error(self):
###################
# ValidationError #
###################
# Can take a string.
self.assertEqual(str(ErrorList(ValidationError("There was an error.").messages)),
'<ul class="errorlist"><li>There was an error.</li></ul>')
# Can take a unicode string.
self.assertEqual(str(ErrorList(ValidationError(u"Not \u03C0.").messages)),
'<ul class="errorlist"><li>Not π.</li></ul>')
# Can take a lazy string.
self.assertEqual(str(ErrorList(ValidationError(ugettext_lazy("Error.")).messages)),
'<ul class="errorlist"><li>Error.</li></ul>')
# Can take a list.
self.assertEqual(str(ErrorList(ValidationError(["Error one.", "Error two."]).messages)),
'<ul class="errorlist"><li>Error one.</li><li>Error two.</li></ul>')
# Can take a mixture in a list.
self.assertEqual(str(ErrorList(ValidationError(["First error.", u"Not \u03C0.", ugettext_lazy("Error.")]).messages)),
'<ul class="errorlist"><li>First error.</li><li>Not π.</li><li>Error.</li></ul>')
class VeryBadError:
def __unicode__(self): return u"A very bad error."
# Can take a non-string.
self.assertEqual(str(ErrorList(ValidationError(VeryBadError()).messages)),
'<ul class="errorlist"><li>A very bad error.</li></ul>')
# Escapes non-safe input but not input marked safe.
example = 'Example of link: <a href="http://www.example.com/">example</a>'
self.assertEqual(str(ErrorList([example])),
'<ul class="errorlist"><li>Example of link: &lt;a href=&quot;http://www.example.com/&quot;&gt;example&lt;/a&gt;</li></ul>')
self.assertEqual(str(ErrorList([mark_safe(example)])),
'<ul class="errorlist"><li>Example of link: <a href="http://www.example.com/">example</a></li></ul>')

File diff suppressed because it is too large Load Diff

View File

@ -1,60 +0,0 @@
# coding: utf-8
"""
Tests for forms/util.py module.
"""
tests = r"""
>>> from django.forms.util import *
>>> from django.core.exceptions import ValidationError
>>> from django.utils.translation import ugettext_lazy
###########
# flatatt #
###########
>>> from django.forms.util import flatatt
>>> flatatt({'id': "header"})
u' id="header"'
>>> flatatt({'class': "news", 'title': "Read this"})
u' class="news" title="Read this"'
>>> flatatt({})
u''
###################
# ValidationError #
###################
# Can take a string.
>>> print ErrorList(ValidationError("There was an error.").messages)
<ul class="errorlist"><li>There was an error.</li></ul>
# Can take a unicode string.
>>> print ErrorList(ValidationError(u"Not \u03C0.").messages)
<ul class="errorlist"><li>Not π.</li></ul>
# Can take a lazy string.
>>> print ErrorList(ValidationError(ugettext_lazy("Error.")).messages)
<ul class="errorlist"><li>Error.</li></ul>
# Can take a list.
>>> print ErrorList(ValidationError(["Error one.", "Error two."]).messages)
<ul class="errorlist"><li>Error one.</li><li>Error two.</li></ul>
# Can take a mixture in a list.
>>> print ErrorList(ValidationError(["First error.", u"Not \u03C0.", ugettext_lazy("Error.")]).messages)
<ul class="errorlist"><li>First error.</li><li>Not π.</li><li>Error.</li></ul>
>>> class VeryBadError:
... def __unicode__(self): return u"A very bad error."
# Can take a non-string.
>>> print ErrorList(ValidationError(VeryBadError()).messages)
<ul class="errorlist"><li>A very bad error.</li></ul>
# Escapes non-safe input but not input marked safe.
>>> example = 'Example of link: <a href="http://www.example.com/">example</a>'
>>> print ErrorList([example])
<ul class="errorlist"><li>Example of link: &lt;a href=&quot;http://www.example.com/&quot;&gt;example&lt;/a&gt;</li></ul>
>>> print ErrorList([mark_safe(example)])
<ul class="errorlist"><li>Example of link: <a href="http://www.example.com/">example</a></li></ul>
"""

File diff suppressed because it is too large Load Diff