summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorJustin Bronn <jbronn@gmail.com>2008-03-23 17:18:58 +0000
committerJustin Bronn <jbronn@gmail.com>2008-03-23 17:18:58 +0000
commit2efc34dc5e65da792700af6594c537104cbee9dc (patch)
treed78d906a2d7ea692b5c2f100f792cf1c1991e669 /tests
parentb0a895f9e891bb1fc653724691e6d85612de2708 (diff)
gis: Merged revisions 7280-7353 via svnmerge from trunk.
git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@7354 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'tests')
-rw-r--r--tests/modeltests/custom_pk/models.py5
-rw-r--r--tests/modeltests/lookup/models.py7
-rw-r--r--tests/modeltests/manipulators/models.py28
-rw-r--r--tests/modeltests/model_forms/models.py73
-rw-r--r--tests/modeltests/pagination/models.py199
-rw-r--r--tests/modeltests/validation/models.py24
-rw-r--r--tests/regressiontests/forms/extra.py83
-rw-r--r--tests/regressiontests/forms/fields.py27
-rw-r--r--tests/regressiontests/forms/forms.py159
-rw-r--r--tests/regressiontests/model_fields/models.py24
-rw-r--r--tests/regressiontests/syndication/__init__.py0
-rw-r--r--tests/regressiontests/syndication/models.py0
-rw-r--r--tests/regressiontests/syndication/tests.py14
-rw-r--r--tests/regressiontests/syndication/urls.py18
-rw-r--r--tests/regressiontests/test_client_regress/models.py30
-rw-r--r--tests/regressiontests/test_client_regress/urls.py1
-rw-r--r--tests/regressiontests/test_client_regress/views.py16
-rw-r--r--tests/urls.py3
18 files changed, 580 insertions, 131 deletions
diff --git a/tests/modeltests/custom_pk/models.py b/tests/modeltests/custom_pk/models.py
index 381d81b987..e3edf3187e 100644
--- a/tests/modeltests/custom_pk/models.py
+++ b/tests/modeltests/custom_pk/models.py
@@ -71,8 +71,9 @@ u'ABC123'
>>> fran.save()
>>> Employee.objects.filter(last_name__exact='Jones')
[<Employee: Dan Jones>, <Employee: Fran Jones>]
->>> Employee.objects.in_bulk(['ABC123', 'XYZ456'])
-{u'XYZ456': <Employee: Fran Jones>, u'ABC123': <Employee: Dan Jones>}
+>>> emps = Employee.objects.in_bulk(['ABC123', 'XYZ456'])
+>>> emps['ABC123']
+<Employee: Dan Jones>
>>> b = Business(name='Sears')
>>> b.save()
diff --git a/tests/modeltests/lookup/models.py b/tests/modeltests/lookup/models.py
index aa90d1e5ec..f31857e0fe 100644
--- a/tests/modeltests/lookup/models.py
+++ b/tests/modeltests/lookup/models.py
@@ -76,8 +76,11 @@ Article 4
# in_bulk() takes a list of IDs and returns a dictionary mapping IDs
# to objects.
->>> Article.objects.in_bulk([1, 2])
-{1: <Article: Article 1>, 2: <Article: Article 2>}
+>>> arts = Article.objects.in_bulk([1, 2])
+>>> arts[1]
+<Article: Article 1>
+>>> arts[2]
+<Article: Article 2>
>>> Article.objects.in_bulk([3])
{3: <Article: Article 3>}
>>> Article.objects.in_bulk([1000])
diff --git a/tests/modeltests/manipulators/models.py b/tests/modeltests/manipulators/models.py
index c9b9848235..3e52e33bbb 100644
--- a/tests/modeltests/manipulators/models.py
+++ b/tests/modeltests/manipulators/models.py
@@ -41,25 +41,33 @@ __test__ = {'API_TESTS':u"""
True
# Attempt to add a Musician without a first_name.
->>> man.get_validation_errors(MultiValueDict({'last_name': ['Blakey']}))
-{'first_name': [u'This field is required.']}
+>>> man.get_validation_errors(MultiValueDict({'last_name': ['Blakey']}))['first_name']
+[u'This field is required.']
# Attempt to add a Musician without a first_name and last_name.
->>> man.get_validation_errors(MultiValueDict({}))
-{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.']}
+>>> errors = man.get_validation_errors(MultiValueDict({}))
+>>> errors['first_name']
+[u'This field is required.']
+>>> errors['last_name']
+[u'This field is required.']
# Attempt to create an Album without a name or musician.
>>> man = Album.AddManipulator()
->>> man.get_validation_errors(MultiValueDict({}))
-{'musician': [u'This field is required.'], 'name': [u'This field is required.']}
+>>> errors = man.get_validation_errors(MultiValueDict({}))
+>>> errors['musician']
+[u'This field is required.']
+>>> errors['name']
+[u'This field is required.']
# Attempt to create an Album with an invalid musician.
->>> man.get_validation_errors(MultiValueDict({'name': ['Sallies Fforth'], 'musician': ['foo']}))
-{'musician': [u"Select a valid choice; 'foo' is not in [u'', u'1']."]}
+>>> errors = man.get_validation_errors(MultiValueDict({'name': ['Sallies Fforth'], 'musician': ['foo']}))
+>>> errors['musician']
+[u"Select a valid choice; 'foo' is not in [u'', u'1']."]
# Attempt to create an Album with an invalid release_date.
->>> man.get_validation_errors(MultiValueDict({'name': ['Sallies Fforth'], 'musician': ['1'], 'release_date': 'today'}))
-{'release_date': [u'Enter a valid date in YYYY-MM-DD format.']}
+>>> errors = man.get_validation_errors(MultiValueDict({'name': ['Sallies Fforth'], 'musician': ['1'], 'release_date': 'today'}))
+>>> errors['release_date']
+[u'Enter a valid date in YYYY-MM-DD format.']
# Create an Album without a release_date (because it's optional).
>>> data = MultiValueDict({'name': ['Ella and Basie'], 'musician': ['1']})
diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py
index c480899f84..470312f5ca 100644
--- a/tests/modeltests/model_forms/models.py
+++ b/tests/modeltests/model_forms/models.py
@@ -234,8 +234,12 @@ We can also subclass the Meta inner class to change the fields list.
>>> f = CategoryForm({'name': 'Entertainment', 'slug': 'entertainment', 'url': 'entertainment'})
>>> f.is_valid()
True
->>> f.cleaned_data
-{'url': u'entertainment', 'name': u'Entertainment', 'slug': u'entertainment'}
+>>> f.cleaned_data['url']
+u'entertainment'
+>>> f.cleaned_data['name']
+u'Entertainment'
+>>> f.cleaned_data['slug']
+u'entertainment'
>>> obj = f.save()
>>> obj
<Category: Entertainment>
@@ -245,8 +249,12 @@ True
>>> f = CategoryForm({'name': "It's a test", 'slug': 'its-test', 'url': 'test'})
>>> f.is_valid()
True
->>> f.cleaned_data
-{'url': u'test', 'name': u"It's a test", 'slug': u'its-test'}
+>>> f.cleaned_data['url']
+u'test'
+>>> f.cleaned_data['name']
+u"It's a test"
+>>> f.cleaned_data['slug']
+u'its-test'
>>> obj = f.save()
>>> obj
<Category: It's a test>
@@ -259,8 +267,12 @@ save() on the resulting model instance.
>>> f = CategoryForm({'name': 'Third test', 'slug': 'third-test', 'url': 'third'})
>>> f.is_valid()
True
->>> f.cleaned_data
-{'url': u'third', 'name': u'Third test', 'slug': u'third-test'}
+>>> f.cleaned_data['url']
+u'third'
+>>> f.cleaned_data['name']
+u'Third test'
+>>> f.cleaned_data['slug']
+u'third-test'
>>> obj = f.save(commit=False)
>>> obj
<Category: Third test>
@@ -272,8 +284,10 @@ True
If you call save() with invalid data, you'll get a ValueError.
>>> f = CategoryForm({'name': '', 'slug': '', 'url': 'foo'})
->>> f.errors
-{'name': [u'This field is required.'], 'slug': [u'This field is required.']}
+>>> f.errors['name']
+[u'This field is required.']
+>>> f.errors['slug']
+[u'This field is required.']
>>> f.cleaned_data
Traceback (most recent call last):
...
@@ -645,6 +659,19 @@ Traceback (most recent call last):
...
ValidationError: [u'Select a valid choice. That choice is not one of the available choices.']
+# check that we can safely iterate choices repeatedly
+>>> gen_one = list(f.choices)
+>>> gen_two = f.choices
+>>> gen_one[2]
+(2L, u"It's a test")
+>>> list(gen_two)
+[(u'', u'---------'), (1L, u'Entertainment'), (2L, u"It's a test"), (3L, u'Third')]
+
+# check that we can override the label_from_instance method to print custom labels (#4620)
+>>> f.queryset = Category.objects.all()
+>>> f.label_from_instance = lambda obj: "category " + str(obj)
+>>> list(f.choices)
+[(u'', u'---------'), (1L, 'category Entertainment'), (2L, "category It's a test"), (3L, 'category Third'), (4L, 'category Fourth')]
# ModelMultipleChoiceField ####################################################
@@ -730,6 +757,10 @@ Traceback (most recent call last):
...
ValidationError: [u'Select a valid choice. 4 is not one of the available choices.']
+>>> f.queryset = Category.objects.all()
+>>> f.label_from_instance = lambda obj: "multicategory " + str(obj)
+>>> list(f.choices)
+[(1L, 'multicategory Entertainment'), (2L, "multicategory It's a test"), (3L, 'multicategory Third'), (4L, 'multicategory Fourth')]
# PhoneNumberField ############################################################
@@ -739,8 +770,10 @@ ValidationError: [u'Select a valid choice. 4 is not one of the available choices
>>> f = PhoneNumberForm({'phone': '(312) 555-1212', 'description': 'Assistance'})
>>> f.is_valid()
True
->>> f.cleaned_data
-{'phone': u'312-555-1212', 'description': u'Assistance'}
+>>> f.cleaned_data['phone']
+u'312-555-1212'
+>>> f.cleaned_data['description']
+u'Assistance'
# FileField ###################################################################
@@ -766,7 +799,7 @@ True
<class 'django.newforms.fields.UploadedFile'>
>>> instance = f.save()
>>> instance.file
-u'.../test1.txt'
+u'...test1.txt'
# Edit an instance that already has the file defined in the model. This will not
# save the file again, but leave it exactly as it is.
@@ -775,10 +808,10 @@ u'.../test1.txt'
>>> f.is_valid()
True
>>> f.cleaned_data['file']
-u'.../test1.txt'
+u'...test1.txt'
>>> instance = f.save()
>>> instance.file
-u'.../test1.txt'
+u'...test1.txt'
# Delete the current file since this is not done by Django.
@@ -791,7 +824,7 @@ u'.../test1.txt'
True
>>> instance = f.save()
>>> instance.file
-u'.../test2.txt'
+u'...test2.txt'
>>> instance.delete()
@@ -810,7 +843,7 @@ True
True
>>> instance = f.save()
>>> instance.file
-u'.../test3.txt'
+u'...test3.txt'
>>> instance.delete()
# ImageField ###################################################################
@@ -832,7 +865,7 @@ True
<class 'django.newforms.fields.UploadedFile'>
>>> instance = f.save()
>>> instance.image
-u'.../test.png'
+u'...test.png'
# Edit an instance that already has the image defined in the model. This will not
# save the image again, but leave it exactly as it is.
@@ -841,10 +874,10 @@ u'.../test.png'
>>> f.is_valid()
True
>>> f.cleaned_data['image']
-u'.../test.png'
+u'...test.png'
>>> instance = f.save()
>>> instance.image
-u'.../test.png'
+u'...test.png'
# Delete the current image since this is not done by Django.
@@ -857,7 +890,7 @@ u'.../test.png'
True
>>> instance = f.save()
>>> instance.image
-u'.../test2.png'
+u'...test2.png'
>>> instance.delete()
@@ -876,7 +909,7 @@ True
True
>>> instance = f.save()
>>> instance.image
-u'.../test3.png'
+u'...test3.png'
>>> instance.delete()
"""}
diff --git a/tests/modeltests/pagination/models.py b/tests/modeltests/pagination/models.py
index f44c67a139..277c5961e3 100644
--- a/tests/modeltests/pagination/models.py
+++ b/tests/modeltests/pagination/models.py
@@ -4,6 +4,11 @@
Django provides a framework for paginating a list of objects in a few lines
of code. This is often useful for dividing search results or long lists of
objects into easily readable pages.
+
+In Django 0.96 and earlier, a single ObjectPaginator class implemented this
+functionality. In the Django development version, the behavior is split across
+two classes -- Paginator and Page -- that are more easier to use. The legacy
+ObjectPaginator class is deprecated.
"""
from django.db import models
@@ -16,70 +21,210 @@ class Article(models.Model):
return self.headline
__test__ = {'API_TESTS':"""
-# prepare a list of objects for pagination
+# Prepare a list of objects for pagination.
>>> from datetime import datetime
>>> for x in range(1, 10):
... a = Article(headline='Article %s' % x, pub_date=datetime(2005, 7, 29))
... a.save()
-# create a basic paginator, 5 articles per page
+####################################
+# New/current API (Paginator/Page) #
+####################################
+
+>>> from django.core.paginator import Paginator, InvalidPage
+>>> paginator = Paginator(Article.objects.all(), 5)
+>>> paginator.count
+9
+>>> paginator.num_pages
+2
+>>> paginator.page_range
+[1, 2]
+
+# Get the first page.
+>>> p = paginator.page(1)
+>>> p
+<Page 1 of 2>
+>>> p.object_list
+[<Article: Article 1>, <Article: Article 2>, <Article: Article 3>, <Article: Article 4>, <Article: Article 5>]
+>>> p.has_next()
+True
+>>> p.has_previous()
+False
+>>> p.has_other_pages()
+True
+>>> p.next_page_number()
+2
+>>> p.previous_page_number()
+0
+>>> p.start_index()
+1
+>>> p.end_index()
+5
+
+# Get the second page.
+>>> p = paginator.page(2)
+>>> p
+<Page 2 of 2>
+>>> p.object_list
+[<Article: Article 6>, <Article: Article 7>, <Article: Article 8>, <Article: Article 9>]
+>>> p.has_next()
+False
+>>> p.has_previous()
+True
+>>> p.has_other_pages()
+True
+>>> p.next_page_number()
+3
+>>> p.previous_page_number()
+1
+>>> p.start_index()
+6
+>>> p.end_index()
+9
+
+# Invalid pages raise InvalidPage.
+>>> paginator.page(0)
+Traceback (most recent call last):
+...
+InvalidPage: ...
+>>> paginator.page(3)
+Traceback (most recent call last):
+...
+InvalidPage: ...
+
+# Empty paginators with allow_empty_first_page=True.
+>>> paginator = Paginator(Article.objects.filter(id=0), 5, allow_empty_first_page=True)
+>>> paginator.count
+0
+>>> paginator.num_pages
+1
+>>> paginator.page_range
+[1]
+
+# Empty paginators with allow_empty_first_page=False.
+>>> paginator = Paginator(Article.objects.filter(id=0), 5, allow_empty_first_page=False)
+>>> paginator.count
+0
+>>> paginator.num_pages
+0
+>>> paginator.page_range
+[]
+
+# Paginators work with regular lists/tuples, too -- not just with QuerySets.
+>>> paginator = Paginator([1, 2, 3, 4, 5, 6, 7, 8, 9], 5)
+>>> paginator.count
+9
+>>> paginator.num_pages
+2
+>>> paginator.page_range
+[1, 2]
+
+# Get the first page.
+>>> p = paginator.page(1)
+>>> p
+<Page 1 of 2>
+>>> p.object_list
+[1, 2, 3, 4, 5]
+>>> p.has_next()
+True
+>>> p.has_previous()
+False
+>>> p.has_other_pages()
+True
+>>> p.next_page_number()
+2
+>>> p.previous_page_number()
+0
+>>> p.start_index()
+1
+>>> p.end_index()
+5
+
+################################
+# Legacy API (ObjectPaginator) #
+################################
+
+# Don't print out the deprecation warnings during testing.
+>>> from warnings import filterwarnings
+>>> filterwarnings("ignore")
+
>>> from django.core.paginator import ObjectPaginator, InvalidPage
>>> paginator = ObjectPaginator(Article.objects.all(), 5)
-
-# the paginator knows how many hits and pages it contains
>>> paginator.hits
9
-
>>> paginator.pages
2
+>>> paginator.page_range
+[1, 2]
-# get the first page (zero-based)
+# Get the first page.
>>> paginator.get_page(0)
[<Article: Article 1>, <Article: Article 2>, <Article: Article 3>, <Article: Article 4>, <Article: Article 5>]
-
-# get the second page
->>> paginator.get_page(1)
-[<Article: Article 6>, <Article: Article 7>, <Article: Article 8>, <Article: Article 9>]
-
-# does the first page have a next or previous page?
>>> paginator.has_next_page(0)
True
-
>>> paginator.has_previous_page(0)
False
+>>> paginator.first_on_page(0)
+1
+>>> paginator.last_on_page(0)
+5
-# check the second page
+# Get the second page.
+>>> paginator.get_page(1)
+[<Article: Article 6>, <Article: Article 7>, <Article: Article 8>, <Article: Article 9>]
>>> paginator.has_next_page(1)
False
-
>>> paginator.has_previous_page(1)
True
-
->>> paginator.first_on_page(0)
-1
>>> paginator.first_on_page(1)
6
->>> paginator.last_on_page(0)
-5
>>> paginator.last_on_page(1)
9
+# Invalid pages raise InvalidPage.
+>>> paginator.get_page(-1)
+Traceback (most recent call last):
+...
+InvalidPage: ...
+>>> paginator.get_page(2)
+Traceback (most recent call last):
+...
+InvalidPage: ...
+
+# Empty paginators with allow_empty_first_page=True.
+>>> paginator = ObjectPaginator(Article.objects.filter(id=0), 5)
+>>> paginator.count
+0
+>>> paginator.num_pages
+1
+>>> paginator.page_range
+[1]
+
+##################
+# Orphan support #
+##################
+
# Add a few more records to test out the orphans feature.
>>> for x in range(10, 13):
... Article(headline="Article %s" % x, pub_date=datetime(2006, 10, 6)).save()
-# With orphans set to 3 and 10 items per page, we should get all 12 items on a single page:
+# With orphans set to 3 and 10 items per page, we should get all 12 items on a single page.
+>>> paginator = Paginator(Article.objects.all(), 10, orphans=3)
+>>> paginator.num_pages
+1
+
+# With orphans only set to 1, we should get two pages.
+>>> paginator = ObjectPaginator(Article.objects.all(), 10, orphans=1)
+>>> paginator.num_pages
+2
+
+# LEGACY: With orphans set to 3 and 10 items per page, we should get all 12 items on a single page.
>>> paginator = ObjectPaginator(Article.objects.all(), 10, orphans=3)
>>> paginator.pages
1
-# With orphans only set to 1, we should get two pages:
+# LEGACY: With orphans only set to 1, we should get two pages.
>>> paginator = ObjectPaginator(Article.objects.all(), 10, orphans=1)
>>> paginator.pages
2
-
-# The paginator can provide a list of all available pages.
->>> paginator = ObjectPaginator(Article.objects.all(), 10)
->>> paginator.page_range
-[1, 2]
"""}
diff --git a/tests/modeltests/validation/models.py b/tests/modeltests/validation/models.py
index aacd041678..63f9f7a361 100644
--- a/tests/modeltests/validation/models.py
+++ b/tests/modeltests/validation/models.py
@@ -41,8 +41,8 @@ __test__ = {'API_TESTS':"""
23
>>> p = Person(**dict(valid_params, id='foo'))
->>> p.validate()
-{'id': [u'This value must be an integer.']}
+>>> p.validate()['id']
+[u'This value must be an integer.']
>>> p = Person(**dict(valid_params, id=None))
>>> p.validate()
@@ -75,8 +75,8 @@ True
False
>>> p = Person(**dict(valid_params, is_child='foo'))
->>> p.validate()
-{'is_child': [u'This value must be either True or False.']}
+>>> p.validate()['is_child']
+[u'This value must be either True or False.']
>>> p = Person(**dict(valid_params, name=u'Jose'))
>>> p.validate()
@@ -115,8 +115,8 @@ datetime.date(2000, 5, 3)
datetime.date(2000, 5, 3)
>>> p = Person(**dict(valid_params, birthdate='foo'))
->>> p.validate()
-{'birthdate': [u'Enter a valid date in YYYY-MM-DD format.']}
+>>> p.validate()['birthdate']
+[u'Enter a valid date in YYYY-MM-DD format.']
>>> p = Person(**dict(valid_params, favorite_moment=datetime.datetime(2002, 4, 3, 13, 23)))
>>> p.validate()
@@ -143,11 +143,15 @@ datetime.datetime(2002, 4, 3, 0, 0)
u'john@example.com'
>>> p = Person(**dict(valid_params, email=22))
->>> p.validate()
-{'email': [u'Enter a valid e-mail address.']}
+>>> p.validate()['email']
+[u'Enter a valid e-mail address.']
# Make sure that Date and DateTime return validation errors and don't raise Python errors.
->>> Person(name='John Doe', is_child=True, email='abc@def.com').validate()
-{'favorite_moment': [u'This field is required.'], 'birthdate': [u'This field is required.']}
+>>> p = Person(name='John Doe', is_child=True, email='abc@def.com')
+>>> errors = p.validate()
+>>> errors['favorite_moment']
+[u'This field is required.']
+>>> errors['birthdate']
+[u'This field is required.']
"""}
diff --git a/tests/regressiontests/forms/extra.py b/tests/regressiontests/forms/extra.py
index 9dff4071f1..a8b369715d 100644
--- a/tests/regressiontests/forms/extra.py
+++ b/tests/regressiontests/forms/extra.py
@@ -22,7 +22,7 @@ classes that demonstrate some of the library's abilities.
>>> from django.newforms.extras import SelectDateWidget
>>> w = SelectDateWidget(years=('2007','2008','2009','2010','2011','2012','2013','2014','2015','2016'))
>>> print w.render('mydate', '')
-<select name="mydate_month">
+<select name="mydate_month" id="id_mydate_month">
<option value="1">January</option>
<option value="2">February</option>
<option value="3">March</option>
@@ -36,7 +36,7 @@ classes that demonstrate some of the library's abilities.
<option value="11">November</option>
<option value="12">December</option>
</select>
-<select name="mydate_day">
+<select name="mydate_day" id="id_mydate_day">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
@@ -69,7 +69,7 @@ classes that demonstrate some of the library's abilities.
<option value="30">30</option>
<option value="31">31</option>
</select>
-<select name="mydate_year">
+<select name="mydate_year" id="id_mydate_year">
<option value="2007">2007</option>
<option value="2008">2008</option>
<option value="2009">2009</option>
@@ -84,7 +84,7 @@ classes that demonstrate some of the library's abilities.
>>> w.render('mydate', None) == w.render('mydate', '')
True
>>> print w.render('mydate', '2010-04-15')
-<select name="mydate_month">
+<select name="mydate_month" id="id_mydate_month">
<option value="1">January</option>
<option value="2">February</option>
<option value="3">March</option>
@@ -98,7 +98,7 @@ True
<option value="11">November</option>
<option value="12">December</option>
</select>
-<select name="mydate_day">
+<select name="mydate_day" id="id_mydate_day">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
@@ -131,7 +131,74 @@ True
<option value="30">30</option>
<option value="31">31</option>
</select>
-<select name="mydate_year">
+<select name="mydate_year" id="id_mydate_year">
+<option value="2007">2007</option>
+<option value="2008">2008</option>
+<option value="2009">2009</option>
+<option value="2010" selected="selected">2010</option>
+<option value="2011">2011</option>
+<option value="2012">2012</option>
+<option value="2013">2013</option>
+<option value="2014">2014</option>
+<option value="2015">2015</option>
+<option value="2016">2016</option>
+</select>
+
+Accepts a datetime or a string:
+
+>>> 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">
+<option value="1">January</option>
+<option value="2" selected="selected">February</option>
+<option value="3">March</option>
+<option value="4">April</option>
+<option value="5">May</option>
+<option value="6">June</option>
+<option value="7">July</option>
+<option value="8">August</option>
+<option value="9">September</option>
+<option value="10">October</option>
+<option value="11">November</option>
+<option value="12">December</option>
+</select>
+<select name="mydate_day" id="id_mydate_day">
+<option value="1">1</option>
+<option value="2">2</option>
+<option value="3">3</option>
+<option value="4">4</option>
+<option value="5">5</option>
+<option value="6">6</option>
+<option value="7">7</option>
+<option value="8">8</option>
+<option value="9">9</option>
+<option value="10">10</option>
+<option value="11">11</option>
+<option value="12">12</option>
+<option value="13">13</option>
+<option value="14">14</option>
+<option value="15">15</option>
+<option value="16">16</option>
+<option value="17">17</option>
+<option value="18">18</option>
+<option value="19">19</option>
+<option value="20">20</option>
+<option value="21">21</option>
+<option value="22">22</option>
+<option value="23">23</option>
+<option value="24">24</option>
+<option value="25">25</option>
+<option value="26">26</option>
+<option value="27">27</option>
+<option value="28">28</option>
+<option value="29">29</option>
+<option value="30">30</option>
+<option value="31" selected="selected">31</option>
+</select>
+<select name="mydate_year" id="id_mydate_year">
<option value="2007">2007</option>
<option value="2008">2008</option>
<option value="2009">2009</option>
@@ -252,8 +319,8 @@ ValidationError: [u'This field is required.']
</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'}
+>>> f.cleaned_data['field1']
+u'some text,JP,2007-04-25 06:24:00'
# IPAddressField ##################################################################
diff --git a/tests/regressiontests/forms/fields.py b/tests/regressiontests/forms/fields.py
index 9216210e09..9421d8c005 100644
--- a/tests/regressiontests/forms/fields.py
+++ b/tests/regressiontests/forms/fields.py
@@ -1133,6 +1133,33 @@ u''
>>> f.clean(None)
u''
+# FilePathField ###############################################################
+
+>>> import os
+>>> from django import newforms as forms
+>>> path = forms.__file__
+>>> path = os.path.dirname(path) + '/'
+>>> path
+'.../django/newforms/'
+>>> f = forms.FilePathField(path=path)
+>>> f.choices.sort()
+>>> f.choices
+[('.../django/newforms/__init__.py', '__init__.py'), ('.../django/newforms/__init__.pyc', '__init__.pyc'), ('.../django/newforms/fields.py', 'fields.py'), ('.../django/newforms/fields.pyc', 'fields.pyc'), ('.../django/newforms/forms.py', 'forms.py'), ('.../django/newforms/forms.pyc', 'forms.pyc'), ('.../django/newforms/models.py', 'models.py'), ('.../django/newforms/models.pyc', 'models.pyc'), ('.../django/newforms/util.py', 'util.py'), ('.../django/newforms/util.pyc', 'util.pyc'), ('.../django/newforms/widgets.py', 'widgets.py'), ('.../django/newforms/widgets.pyc', 'widgets.pyc')]
+>>> f.clean('fields.py')
+Traceback (most recent call last):
+...
+ValidationError: [u'Select a valid choice. That choice is not one of the available choices.']
+>>> f.clean(path + 'fields.py')
+u'.../django/newforms/fields.py'
+>>> f = forms.FilePathField(path=path, match='^.*?\.py$')
+>>> f.choices.sort()
+>>> f.choices
+[('.../django/newforms/__init__.py', '__init__.py'), ('.../django/newforms/fields.py', 'fields.py'), ('.../django/newforms/forms.py', 'forms.py'), ('.../django/newforms/models.py', 'models.py'), ('.../django/newforms/util.py', 'util.py'), ('.../django/newforms/widgets.py', 'widgets.py')]
+>>> f = forms.FilePathField(path=path, recursive=True, match='^.*?\.py$')
+>>> f.choices.sort()
+>>> f.choices
+[('.../django/newforms/__init__.py', '__init__.py'), ('.../django/newforms/extras/__init__.py', 'extras/__init__.py'), ('.../django/newforms/extras/widgets.py', 'extras/widgets.py'), ('.../django/newforms/fields.py', 'fields.py'), ('.../django/newforms/forms.py', 'forms.py'), ('.../django/newforms/models.py', 'models.py'), ('.../django/newforms/util.py', 'util.py'), ('.../django/newforms/widgets.py', 'widgets.py')]
+
# SplitDateTimeField ##########################################################
>>> f = SplitDateTimeField()
diff --git a/tests/regressiontests/forms/forms.py b/tests/regressiontests/forms/forms.py
index 7c0cf8abf3..d7fa1780f5 100644
--- a/tests/regressiontests/forms/forms.py
+++ b/tests/regressiontests/forms/forms.py
@@ -36,8 +36,8 @@ True
u''
>>> p.errors.as_text()
u''
->>> p.cleaned_data
-{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
+>>> p.cleaned_data["first_name"], p.cleaned_data["last_name"], p.cleaned_data["birthday"]
+(u'John', u'Lennon', datetime.date(1940, 10, 9))
>>> print p['first_name']
<input type="text" name="first_name" value="John" id="id_first_name" />
>>> print p['last_name']
@@ -68,8 +68,12 @@ Empty dictionaries are valid, too.
>>> p = Person({})
>>> p.is_bound
True
->>> p.errors
-{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']}
+>>> p.errors['first_name']
+[u'This field is required.']
+>>> p.errors['last_name']
+[u'This field is required.']
+>>> p.errors['birthday']
+[u'This field is required.']
>>> p.is_valid()
False
>>> p.cleaned_data
@@ -137,8 +141,10 @@ u'<li><label for="id_first_name">First name:</label> <input type="text" name="fi
u'<p><label for="id_first_name">First name:</label> <input type="text" name="first_name" value="John" id="id_first_name" /></p>\n<p><label for="id_last_name">Last name:</label> <input type="text" name="last_name" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" id="id_last_name" /></p>\n<p><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" value="1940-10-9" id="id_birthday" /></p>'
>>> p = Person({'last_name': u'Lennon'})
->>> p.errors
-{'first_name': [u'This field is required.'], 'birthday': [u'This field is required.']}
+>>> p.errors['first_name']
+[u'This field is required.']
+>>> p.errors['birthday']
+[u'This field is required.']
>>> p.is_valid()
False
>>> p.errors.as_ul()
@@ -175,8 +181,13 @@ but cleaned_data contains only the form's fields.
>>> p = Person(data)
>>> p.is_valid()
True
->>> p.cleaned_data
-{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
+>>> p.cleaned_data['first_name']
+u'John'
+>>> p.cleaned_data['last_name']
+u'Lennon'
+>>> p.cleaned_data['birthday']
+datetime.date(1940, 10, 9)
+
cleaned_data will include a key and value for *all* fields defined in the Form,
even if the Form's data didn't include a value for fields that are not
@@ -191,8 +202,12 @@ empty string.
>>> f = OptionalPersonForm(data)
>>> f.is_valid()
True
->>> f.cleaned_data
-{'nick_name': u'', 'first_name': u'John', 'last_name': u'Lennon'}
+>>> f.cleaned_data['nick_name']
+u''
+>>> f.cleaned_data['first_name']
+u'John'
+>>> f.cleaned_data['last_name']
+u'Lennon'
For DateFields, it's set to None.
>>> class OptionalPersonForm(Form):
@@ -203,8 +218,12 @@ For DateFields, it's set to None.
>>> f = OptionalPersonForm(data)
>>> f.is_valid()
True
->>> f.cleaned_data
-{'birth_date': None, 'first_name': u'John', 'last_name': u'Lennon'}
+>>> print f.cleaned_data['birth_date']
+None
+>>> f.cleaned_data['first_name']
+u'John'
+>>> f.cleaned_data['last_name']
+u'Lennon'
"auto_id" tells the Form to add an "id" attribute to each form element.
If it's a string that contains '%s', Django will use that as a format string
@@ -549,18 +568,22 @@ The MultipleHiddenInput widget renders multiple values as hidden fields.
When using CheckboxSelectMultiple, the framework expects a list of input and
returns a list of input.
>>> f = SongForm({'name': 'Yesterday'}, auto_id=False)
->>> f.errors
-{'composers': [u'This field is required.']}
+>>> f.errors['composers']
+[u'This field is required.']
>>> f = SongForm({'name': 'Yesterday', 'composers': ['J']}, auto_id=False)
>>> f.errors
{}
->>> f.cleaned_data
-{'composers': [u'J'], 'name': u'Yesterday'}
+>>> f.cleaned_data['composers']
+[u'J']
+>>> f.cleaned_data['name']
+u'Yesterday'
>>> f = SongForm({'name': 'Yesterday', 'composers': ['J', 'P']}, auto_id=False)
>>> f.errors
{}
->>> f.cleaned_data
-{'composers': [u'J', u'P'], 'name': u'Yesterday'}
+>>> f.cleaned_data['composers']
+[u'J', u'P']
+>>> f.cleaned_data['name']
+u'Yesterday'
Validation errors are HTML-escaped when output as HTML.
>>> class EscapingForm(Form):
@@ -598,16 +621,24 @@ including the current field (e.g., the field XXX if you're in clean_XXX()).
>>> f.errors
{}
>>> f = UserRegistration({}, auto_id=False)
->>> f.errors
-{'username': [u'This field is required.'], 'password1': [u'This field is required.'], 'password2': [u'This field is required.']}
+>>> f.errors['username']
+[u'This field is required.']
+>>> f.errors['password1']
+[u'This field is required.']
+>>> f.errors['password2']
+[u'This field is required.']
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'bar'}, auto_id=False)
->>> f.errors
-{'password2': [u'Please make sure your passwords match.']}
+>>> f.errors['password2']
+[u'Please make sure your passwords match.']
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False)
>>> f.errors
{}
->>> f.cleaned_data
-{'username': u'adrian', 'password1': u'foo', 'password2': u'foo'}
+>>> f.cleaned_data['username']
+u'adrian'
+>>> f.cleaned_data['password1']
+u'foo'
+>>> f.cleaned_data['password2']
+u'foo'
Another way of doing multiple-field validation is by implementing the
Form's clean() method. If you do this, any ValidationError raised by that
@@ -632,11 +663,15 @@ Form.clean() is required to return a dictionary of all clean data.
<tr><th>Username:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="username" maxlength="10" /></td></tr>
<tr><th>Password1:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="password" name="password1" /></td></tr>
<tr><th>Password2:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="password" name="password2" /></td></tr>
->>> f.errors
-{'username': [u'This field is required.'], 'password1': [u'This field is required.'], 'password2': [u'This field is required.']}
+>>> f.errors['username']
+[u'This field is required.']
+>>> f.errors['password1']
+[u'This field is required.']
+>>> f.errors['password2']
+[u'This field is required.']
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'bar'}, auto_id=False)
->>> f.errors
-{'__all__': [u'Please make sure your passwords match.']}
+>>> f.errors['__all__']
+[u'Please make sure your passwords match.']
>>> print f.as_table()
<tr><td colspan="2"><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></td></tr>
<tr><th>Username:</th><td><input type="text" name="username" value="adrian" maxlength="10" /></td></tr>
@@ -650,8 +685,12 @@ Form.clean() is required to return a dictionary of all clean data.
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False)
>>> f.errors
{}
->>> f.cleaned_data
-{'username': u'adrian', 'password1': u'foo', 'password2': u'foo'}
+>>> f.cleaned_data['username']
+u'adrian'
+>>> f.cleaned_data['password1']
+u'foo'
+>>> f.cleaned_data['password2']
+u'foo'
# Dynamic construction ########################################################
@@ -1024,8 +1063,8 @@ An 'initial' value is *not* used as a fallback if data is not provided. In this
example, we don't provide a value for 'username', and the form raises a
validation error rather than using the initial value for 'username'.
>>> p = UserRegistration({'password': 'secret'})
->>> p.errors
-{'username': [u'This field is required.']}
+>>> p.errors['username']
+[u'This field is required.']
>>> p.is_valid()
False
@@ -1069,8 +1108,8 @@ A dynamic 'initial' value is *not* used as a fallback if data is not provided.
In this example, we don't provide a value for 'username', and the form raises a
validation error rather than using the initial value for 'username'.
>>> p = UserRegistration({'password': 'secret'}, initial={'username': 'django'})
->>> p.errors
-{'username': [u'This field is required.']}
+>>> p.errors['username']
+[u'This field is required.']
>>> p.is_valid()
False
@@ -1123,8 +1162,8 @@ A callable 'initial' value is *not* used as a fallback if data is not provided.
In this example, we don't provide a value for 'username', and the form raises a
validation error rather than using the initial value for 'username'.
>>> p = UserRegistration({'password': 'secret'}, initial={'username': initial_django})
->>> p.errors
-{'username': [u'This field is required.']}
+>>> p.errors['username']
+[u'This field is required.']
>>> p.is_valid()
False
@@ -1258,8 +1297,12 @@ actual field name.
{}
>>> p.is_valid()
True
->>> p.cleaned_data
-{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
+>>> p.cleaned_data['first_name']
+u'John'
+>>> p.cleaned_data['last_name']
+u'Lennon'
+>>> p.cleaned_data['birthday']
+datetime.date(1940, 10, 9)
Let's try submitting some bad data to make sure form.errors and field.errors
work as expected.
@@ -1269,8 +1312,12 @@ work as expected.
... 'person1-birthday': u''
... }
>>> p = Person(data, prefix='person1')
->>> p.errors
-{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']}
+>>> p.errors['first_name']
+[u'This field is required.']
+>>> p.errors['last_name']
+[u'This field is required.']
+>>> p.errors['birthday']
+[u'This field is required.']
>>> p['first_name'].errors
[u'This field is required.']
>>> p['person1-first_name'].errors
@@ -1286,8 +1333,12 @@ the form doesn't "see" the fields.
... 'birthday': u'1940-10-9'
... }
>>> p = Person(data, prefix='person1')
->>> p.errors
-{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']}
+>>> p.errors['first_name']
+[u'This field is required.']
+>>> p.errors['last_name']
+[u'This field is required.']
+>>> p.errors['birthday']
+[u'This field is required.']
With prefixes, a single data dictionary can hold data for multiple instances
of the same form.
@@ -1302,13 +1353,21 @@ of the same form.
>>> p1 = Person(data, prefix='person1')
>>> p1.is_valid()
True
->>> p1.cleaned_data
-{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
+>>> p1.cleaned_data['first_name']
+u'John'
+>>> p1.cleaned_data['last_name']
+u'Lennon'
+>>> p1.cleaned_data['birthday']
+datetime.date(1940, 10, 9)
>>> p2 = Person(data, prefix='person2')
>>> p2.is_valid()
True
->>> p2.cleaned_data
-{'first_name': u'Jim', 'last_name': u'Morrison', 'birthday': datetime.date(1943, 12, 8)}
+>>> p2.cleaned_data['first_name']
+u'Jim'
+>>> p2.cleaned_data['last_name']
+u'Morrison'
+>>> p2.cleaned_data['birthday']
+datetime.date(1943, 12, 8)
By default, forms append a hyphen between the prefix and the field name, but a
form can alter that behavior by implementing the add_prefix() method. This
@@ -1333,8 +1392,12 @@ self.prefix.
>>> p = Person(data, prefix='foo')
>>> p.is_valid()
True
->>> p.cleaned_data
-{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
+>>> p.cleaned_data['first_name']
+u'John'
+>>> p.cleaned_data['last_name']
+u'Lennon'
+>>> p.cleaned_data['birthday']
+datetime.date(1940, 10, 9)
# Forms with NullBooleanFields ################################################
diff --git a/tests/regressiontests/model_fields/models.py b/tests/regressiontests/model_fields/models.py
index e69de29bb2..7e07227961 100644
--- a/tests/regressiontests/model_fields/models.py
+++ b/tests/regressiontests/model_fields/models.py
@@ -0,0 +1,24 @@
+
+from django.db import models
+
+class Foo(models.Model):
+ a = models.CharField(max_length=10)
+
+def get_foo():
+ return Foo.objects.get(id=1)
+
+class Bar(models.Model):
+ b = models.CharField(max_length=10)
+ a = models.ForeignKey(Foo, default=get_foo)
+
+__test__ = {'API_TESTS':"""
+# Create a couple of Places.
+>>> f = Foo.objects.create(a='abc')
+>>> f.id
+1
+>>> b = Bar(b = "bcd")
+>>> b.a
+<Foo: Foo object>
+>>> b.save()
+
+"""}
diff --git a/tests/regressiontests/syndication/__init__.py b/tests/regressiontests/syndication/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/regressiontests/syndication/__init__.py
diff --git a/tests/regressiontests/syndication/models.py b/tests/regressiontests/syndication/models.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/regressiontests/syndication/models.py
diff --git a/tests/regressiontests/syndication/tests.py b/tests/regressiontests/syndication/tests.py
new file mode 100644
index 0000000000..6a9dd643d7
--- /dev/null
+++ b/tests/regressiontests/syndication/tests.py
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+
+from django.test import TestCase
+from django.test.client import Client
+
+class SyndicationFeedTest(TestCase):
+ def test_complex_base_url(self):
+ """
+ Tests that that the base url for a complex feed doesn't raise a 500
+ exception.
+ """
+ c = Client()
+ response = c.get('/syndication/feeds/complex/')
+ self.assertEquals(response.status_code, 404)
diff --git a/tests/regressiontests/syndication/urls.py b/tests/regressiontests/syndication/urls.py
new file mode 100644
index 0000000000..24644ffd53
--- /dev/null
+++ b/tests/regressiontests/syndication/urls.py
@@ -0,0 +1,18 @@
+from django.conf.urls.defaults import patterns
+from django.core.exceptions import ObjectDoesNotExist
+from django.contrib.syndication import feeds
+
+
+class ComplexFeed(feeds.Feed):
+ def get_object(self, bits):
+ if len(bits) != 1:
+ raise ObjectDoesNotExist
+ return None
+
+
+urlpatterns = patterns('',
+ (r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed', {
+ 'feed_dict': dict(
+ complex = ComplexFeed,
+ )}),
+)
diff --git a/tests/regressiontests/test_client_regress/models.py b/tests/regressiontests/test_client_regress/models.py
index b5d9ae63b9..305ccc9aa3 100644
--- a/tests/regressiontests/test_client_regress/models.py
+++ b/tests/regressiontests/test_client_regress/models.py
@@ -3,7 +3,7 @@ Regression tests for the Test Client, especially the customized assertions.
"""
from django.test import Client, TestCase
-from django.core import mail
+from django.core.urlresolvers import reverse
import os
class AssertContainsTests(TestCase):
@@ -261,3 +261,31 @@ class LoginTests(TestCase):
# Check that assertRedirects uses the original client, not the
# default client.
self.assertRedirects(response, "http://testserver/test_client_regress/get_view/")
+
+
+class URLEscapingTests(TestCase):
+ def test_simple_argument_get(self):
+ "Get a view that has a simple string argument"
+ response = self.client.get(reverse('arg_view', args=['Slartibartfast']))
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.content, 'Howdy, Slartibartfast')
+
+ def test_argument_with_space_get(self):
+ "Get a view that has a string argument that requires escaping"
+ response = self.client.get(reverse('arg_view', args=['Arthur Dent']))
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.content, 'Hi, Arthur')
+
+ def test_simple_argument_post(self):
+ "Post for a view that has a simple string argument"
+ response = self.client.post(reverse('arg_view', args=['Slartibartfast']))
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.content, 'Howdy, Slartibartfast')
+
+ def test_argument_with_space_post(self):
+ "Post for a view that has a string argument that requires escaping"
+ response = self.client.post(reverse('arg_view', args=['Arthur Dent']))
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.content, 'Hi, Arthur')
+
+
diff --git a/tests/regressiontests/test_client_regress/urls.py b/tests/regressiontests/test_client_regress/urls.py
index e771707f61..d3304caef0 100644
--- a/tests/regressiontests/test_client_regress/urls.py
+++ b/tests/regressiontests/test_client_regress/urls.py
@@ -5,5 +5,6 @@ urlpatterns = patterns('',
(r'^no_template_view/$', views.no_template_view),
(r'^file_upload/$', views.file_upload_view),
(r'^get_view/$', views.get_view),
+ url(r'^arg_view/(?P<name>.+)/$', views.view_with_argument, name='arg_view'),
(r'^login_protected_redirect_view/$', views.login_protected_redirect_view)
)
diff --git a/tests/regressiontests/test_client_regress/views.py b/tests/regressiontests/test_client_regress/views.py
index 75efd212e1..f44757dc10 100644
--- a/tests/regressiontests/test_client_regress/views.py
+++ b/tests/regressiontests/test_client_regress/views.py
@@ -1,7 +1,5 @@
from django.contrib.auth.decorators import login_required
-from django.core.mail import EmailMessage, SMTPConnection
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseServerError
-from django.shortcuts import render_to_response
def no_template_view(request):
"A simple view that expects a GET request, and returns a rendered template"
@@ -20,10 +18,22 @@ def file_upload_view(request):
return HttpResponseServerError()
def get_view(request):
- "A simple login protected view"
+ "A simple login protected view"
return HttpResponse("Hello world")
get_view = login_required(get_view)
+def view_with_argument(request, name):
+ """A view that takes a string argument
+
+ The purpose of this view is to check that if a space is provided in
+ the argument, the test framework unescapes the %20 before passing
+ the value to the view.
+ """
+ if name == 'Arthur Dent':
+ return HttpResponse('Hi, Arthur')
+ else:
+ return HttpResponse('Howdy, %s' % name)
+
def login_protected_redirect_view(request):
"A view that redirects all requests to the GET view"
return HttpResponseRedirect('/test_client_regress/get_view/')
diff --git a/tests/urls.py b/tests/urls.py
index 41b4aaf6d3..dbdf9a8064 100644
--- a/tests/urls.py
+++ b/tests/urls.py
@@ -19,4 +19,7 @@ urlpatterns = patterns('',
(r'^middleware/', include('regressiontests.middleware.urls')),
(r'^utils/', include('regressiontests.utils.urls')),
+
+ # test urlconf for syndication tests
+ (r'^syndication/', include('regressiontests.syndication.urls')),
)