summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorAlex Gaynor <alex.gaynor@gmail.com>2010-08-09 21:22:37 +0000
committerAlex Gaynor <alex.gaynor@gmail.com>2010-08-09 21:22:37 +0000
commit6001ba016a3db4701d56abc6d30868d4e5d88dbf (patch)
tree7a42c57d802484300c2384d3cd3a968de1804383 /tests
parentc7bd48cb9f645e5ff07d1e68b86130e3bb2b043f (diff)
downloaddjango-soc2010/query-refactor.tar.gz
[soc2010/query-refactor] Merged up to trunk r13556, resolved merge conflictsarchive/soc2010/query-refactorsoc2010/query-refactor
git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2010/query-refactor@13565 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'tests')
-rw-r--r--tests/modeltests/fixtures/tests.py51
-rw-r--r--tests/modeltests/model_forms/models.py14
-rw-r--r--tests/regressiontests/admin_views/models.py6
-rw-r--r--tests/regressiontests/admin_views/tests.py29
-rw-r--r--tests/regressiontests/backends/models.py17
-rw-r--r--tests/regressiontests/backends/tests.py19
-rw-r--r--tests/regressiontests/forms/forms.py40
-rw-r--r--tests/regressiontests/forms/input_formats.py894
-rw-r--r--tests/regressiontests/forms/localflavor/au.py2
-rw-r--r--tests/regressiontests/forms/tests.py2
-rw-r--r--tests/regressiontests/forms/widgets.py33
-rw-r--r--tests/regressiontests/localflavor/models.py8
-rw-r--r--tests/regressiontests/localflavor/tests.py84
-rw-r--r--tests/regressiontests/localflavor/us/__init__.py0
-rw-r--r--tests/regressiontests/localflavor/us/forms.py (renamed from tests/regressiontests/localflavor/forms.py)6
-rw-r--r--tests/regressiontests/localflavor/us/models.py13
-rw-r--r--tests/regressiontests/localflavor/us/tests.py82
-rw-r--r--tests/regressiontests/model_forms_regress/models.py3
-rw-r--r--tests/regressiontests/model_forms_regress/tests.py40
-rw-r--r--tests/regressiontests/multiple_database/tests.py112
-rw-r--r--tests/regressiontests/templates/filters.py7
-rw-r--r--tests/regressiontests/templates/tests.py33
-rw-r--r--tests/regressiontests/test_client_regress/models.py34
-rw-r--r--tests/regressiontests/urlpatterns_reverse/included_namespace_urls.py7
-rw-r--r--tests/regressiontests/urlpatterns_reverse/namespace_urls.py9
-rw-r--r--tests/regressiontests/urlpatterns_reverse/tests.py73
-rw-r--r--tests/regressiontests/urlpatterns_reverse/views.py6
-rwxr-xr-xtests/runtests.py6
28 files changed, 1470 insertions, 160 deletions
diff --git a/tests/modeltests/fixtures/tests.py b/tests/modeltests/fixtures/tests.py
index 4facc6dee9..e818423b48 100644
--- a/tests/modeltests/fixtures/tests.py
+++ b/tests/modeltests/fixtures/tests.py
@@ -23,9 +23,14 @@ class TestCaseFixtureLoadingTests(TestCase):
class FixtureLoadingTests(TestCase):
- def _dumpdata_assert(self, args, output, format='json', natural_keys=False):
+ def _dumpdata_assert(self, args, output, format='json', natural_keys=False,
+ exclude_list=[]):
new_io = StringIO.StringIO()
- management.call_command('dumpdata', *args, **{'format':format, 'stdout':new_io, 'use_natural_keys':natural_keys})
+ management.call_command('dumpdata', *args, **{'format':format,
+ 'stdout':new_io,
+ 'stderr':new_io,
+ 'use_natural_keys':natural_keys,
+ 'exclude': exclude_list})
command_output = new_io.getvalue().strip()
self.assertEqual(command_output, output)
@@ -150,6 +155,48 @@ class FixtureLoadingTests(TestCase):
self._dumpdata_assert(['fixtures'], """<?xml version="1.0" encoding="utf-8"?>
<django-objects version="1.0"><object pk="1" model="fixtures.category"><field type="CharField" name="title">News Stories</field><field type="TextField" name="description">Latest news stories</field></object><object pk="5" model="fixtures.article"><field type="CharField" name="headline">XML identified as leading cause of cancer</field><field type="DateTimeField" name="pub_date">2006-06-16 16:00:00</field></object><object pk="4" model="fixtures.article"><field type="CharField" name="headline">Django conquers world!</field><field type="DateTimeField" name="pub_date">2006-06-16 15:00:00</field></object><object pk="3" model="fixtures.article"><field type="CharField" name="headline">Copyright is fine the way it is</field><field type="DateTimeField" name="pub_date">2006-06-16 14:00:00</field></object><object pk="2" model="fixtures.article"><field type="CharField" name="headline">Poker on TV is great!</field><field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field></object><object pk="1" model="fixtures.article"><field type="CharField" name="headline">Python program becomes self aware</field><field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field></object><object pk="1" model="fixtures.tag"><field type="CharField" name="name">copyright</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="2" model="fixtures.tag"><field type="CharField" name="name">legal</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="3" model="fixtures.tag"><field type="CharField" name="name">django</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">4</field></object><object pk="4" model="fixtures.tag"><field type="CharField" name="name">world domination</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">4</field></object><object pk="3" model="fixtures.person"><field type="CharField" name="name">Artist formerly known as "Prince"</field></object><object pk="1" model="fixtures.person"><field type="CharField" name="name">Django Reinhardt</field></object><object pk="2" model="fixtures.person"><field type="CharField" name="name">Stephane Grappelli</field></object><object pk="1" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Django Reinhardt</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>add_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>change_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>delete_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="2" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Stephane Grappelli</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>add_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>delete_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="3" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Artist formerly known as "Prince"</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>change_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="1" model="fixtures.book"><field type="CharField" name="name">Music for all ages</field><field to="fixtures.person" name="authors" rel="ManyToManyRel"><object><natural>Artist formerly known as "Prince"</natural></object><object><natural>Django Reinhardt</natural></object></field></object></django-objects>""", format='xml', natural_keys=True)
+ def test_dumpdata_with_excludes(self):
+ # Load fixture1 which has a site, two articles, and a category
+ management.call_command('loaddata', 'fixture1.json', verbosity=0, commit=False)
+
+ # Excluding fixtures app should only leave sites
+ self._dumpdata_assert(
+ ['sites', 'fixtures'],
+ '[{"pk": 1, "model": "sites.site", "fields": {"domain": "example.com", "name": "example.com"}}]',
+ exclude_list=['fixtures'])
+
+ # Excluding fixtures.Article should leave fixtures.Category
+ self._dumpdata_assert(
+ ['sites', 'fixtures'],
+ '[{"pk": 1, "model": "sites.site", "fields": {"domain": "example.com", "name": "example.com"}}, {"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}]',
+ exclude_list=['fixtures.Article'])
+
+ # Excluding fixtures and fixtures.Article should be a no-op
+ self._dumpdata_assert(
+ ['sites', 'fixtures'],
+ '[{"pk": 1, "model": "sites.site", "fields": {"domain": "example.com", "name": "example.com"}}, {"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}]',
+ exclude_list=['fixtures.Article'])
+
+ # Excluding sites and fixtures.Article should only leave fixtures.Category
+ self._dumpdata_assert(
+ ['sites', 'fixtures'],
+ '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}]',
+ exclude_list=['fixtures.Article', 'sites'])
+
+ # Excluding a bogus app should throw an error
+ self.assertRaises(SystemExit,
+ self._dumpdata_assert,
+ ['fixtures', 'sites'],
+ '',
+ exclude_list=['foo_app'])
+
+ # Excluding a bogus model should throw an error
+ self.assertRaises(SystemExit,
+ self._dumpdata_assert,
+ ['fixtures', 'sites'],
+ '',
+ exclude_list=['fixtures.FooModel'])
+
def test_compress_format_loading(self):
# Load fixture 4 (compressed), using format specification
management.call_command('loaddata', 'fixture4.json', verbosity=0, commit=False)
diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py
index 1087cf8795..7ded82bb7c 100644
--- a/tests/modeltests/model_forms/models.py
+++ b/tests/modeltests/model_forms/models.py
@@ -554,7 +554,7 @@ fields with the 'choices' attribute are represented by a ChoiceField.
<option value="1">Entertainment</option>
<option value="2">It&#39;s a test</option>
<option value="3">Third test</option>
-</select><br /> Hold down "Control", or "Command" on a Mac, to select more than one.</td></tr>
+</select><br /><span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></td></tr>
You can restrict a form to a subset of the complete list of fields
by providing a 'fields' argument. If you try to save a
@@ -579,7 +579,7 @@ inserted as 'initial' data in each Field.
... model = Writer
>>> f = RoykoForm(auto_id=False, instance=w)
>>> print f
-<tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="50" /><br />Use both first and last names.</td></tr>
+<tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="50" /><br /><span class="helptext">Use both first and last names.</span></td></tr>
>>> art = Article(headline='Test article', slug='test-article', pub_date=datetime.date(1988, 1, 4), writer=w, article='Hello.')
>>> art.save()
@@ -609,7 +609,7 @@ inserted as 'initial' data in each Field.
<option value="1">Entertainment</option>
<option value="2">It&#39;s a test</option>
<option value="3">Third test</option>
-</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
+</select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></li>
>>> f = TestArticleForm({'headline': u'Test headline', 'slug': 'test-headline', 'pub_date': u'1984-02-06', 'writer': unicode(w_royko.pk), 'article': 'Hello.'}, instance=art)
>>> f.errors
{}
@@ -672,7 +672,7 @@ Add some categories and test the many-to-many form output.
<option value="1" selected="selected">Entertainment</option>
<option value="2">It&#39;s a test</option>
<option value="3">Third test</option>
-</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
+</select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></li>
Initial values can be provided for model forms
>>> f = TestArticleForm(auto_id=False, initial={'headline': 'Your headline here', 'categories': ['1','2']})
@@ -696,7 +696,7 @@ Initial values can be provided for model forms
<option value="1" selected="selected">Entertainment</option>
<option value="2" selected="selected">It&#39;s a test</option>
<option value="3">Third test</option>
-</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
+</select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></li>
>>> f = TestArticleForm({'headline': u'New headline', 'slug': u'new-headline', 'pub_date': u'1988-01-04',
... 'writer': unicode(w_royko.pk), 'article': u'Hello.', 'categories': [u'1', u'2']}, instance=new_art)
@@ -812,7 +812,7 @@ the data in the database when the form is instantiated.
<option value="1">Entertainment</option>
<option value="2">It&#39;s a test</option>
<option value="3">Third</option>
-</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
+</select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></li>
>>> Category.objects.create(name='Fourth', url='4th')
<Category: Fourth>
>>> Writer.objects.create(name='Carl Bernstein')
@@ -839,7 +839,7 @@ the data in the database when the form is instantiated.
<option value="2">It&#39;s a test</option>
<option value="3">Third</option>
<option value="4">Fourth</option>
-</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
+</select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></li>
# ModelChoiceField ############################################################
diff --git a/tests/regressiontests/admin_views/models.py b/tests/regressiontests/admin_views/models.py
index a2700ba747..b25a9b9a96 100644
--- a/tests/regressiontests/admin_views/models.py
+++ b/tests/regressiontests/admin_views/models.py
@@ -10,6 +10,7 @@ from django.core.mail import EmailMessage
from django.db import models
from django import forms
from django.forms.models import BaseModelFormSet
+from django.contrib.auth.models import User
from django.contrib.contenttypes import generic
from django.contrib.contenttypes.models import ContentType
@@ -579,6 +580,10 @@ class Pizza(models.Model):
class PizzaAdmin(admin.ModelAdmin):
readonly_fields = ('toppings',)
+class Album(models.Model):
+ owner = models.ForeignKey(User)
+ title = models.CharField(max_length=30)
+
admin.site.register(Article, ArticleAdmin)
admin.site.register(CustomArticle, CustomArticleAdmin)
admin.site.register(Section, save_as=True, inlines=[ArticleInline])
@@ -625,3 +630,4 @@ admin.site.register(Promo)
admin.site.register(ChapterXtra1)
admin.site.register(Pizza, PizzaAdmin)
admin.site.register(Topping)
+admin.site.register(Album)
diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py
index 1385e5e0aa..41aade0561 100644
--- a/tests/regressiontests/admin_views/tests.py
+++ b/tests/regressiontests/admin_views/tests.py
@@ -2113,11 +2113,9 @@ class ReadonlyTest(TestCase):
response = self.client.get('/test_admin/admin/admin_views/pizza/add/')
self.assertEqual(response.status_code, 200)
-class IncompleteFormTest(TestCase):
+class UserAdminTest(TestCase):
"""
- Tests validation of a ModelForm that doesn't explicitly have all data
- corresponding to model fields. Model validation shouldn't fail
- such a forms.
+ Tests user CRUD functionality.
"""
fixtures = ['admin-views-users.xml']
@@ -2128,6 +2126,7 @@ class IncompleteFormTest(TestCase):
self.client.logout()
def test_user_creation(self):
+ user_count = User.objects.count()
response = self.client.post('/test_admin/admin/auth/user/add/', {
'username': 'newuser',
'password1': 'newpassword',
@@ -2136,6 +2135,7 @@ class IncompleteFormTest(TestCase):
})
new_user = User.objects.order_by('-id')[0]
self.assertRedirects(response, '/test_admin/admin/auth/user/%s/' % new_user.pk)
+ self.assertEquals(User.objects.count(), user_count + 1)
self.assertNotEquals(new_user.password, UNUSABLE_PASSWORD)
def test_password_mismatch(self):
@@ -2149,3 +2149,24 @@ class IncompleteFormTest(TestCase):
self.assert_('password' not in adminform.form.errors)
self.assertEquals(adminform.form.errors['password2'],
[u"The two password fields didn't match."])
+
+ def test_user_fk_popup(self):
+ response = self.client.get('/test_admin/admin/admin_views/album/add/')
+ self.failUnlessEqual(response.status_code, 200)
+ self.assertContains(response, '/test_admin/admin/auth/user/add')
+ self.assertContains(response, 'class="add-another" id="add_id_owner" onclick="return showAddAnotherPopup(this);"')
+ response = self.client.get('/test_admin/admin/auth/user/add/?_popup=1')
+ self.assertNotContains(response, 'name="_continue"')
+
+ def test_user_add_another(self):
+ user_count = User.objects.count()
+ response = self.client.post('/test_admin/admin/auth/user/add/', {
+ 'username': 'newuser',
+ 'password1': 'newpassword',
+ 'password2': 'newpassword',
+ '_addanother': '1',
+ })
+ new_user = User.objects.order_by('-id')[0]
+ self.assertRedirects(response, '/test_admin/admin/auth/user/add/')
+ self.assertEquals(User.objects.count(), user_count + 1)
+ self.assertNotEquals(new_user.password, UNUSABLE_PASSWORD)
diff --git a/tests/regressiontests/backends/models.py b/tests/regressiontests/backends/models.py
index e3137f2710..7315408d49 100644
--- a/tests/regressiontests/backends/models.py
+++ b/tests/regressiontests/backends/models.py
@@ -1,3 +1,5 @@
+from django.contrib.contenttypes import generic
+from django.contrib.contenttypes.models import ContentType
from django.conf import settings
from django.db import models
from django.db import connection, DEFAULT_DB_ALIAS
@@ -37,6 +39,21 @@ if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.mysql':
m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz = models.ManyToManyField(Person,blank=True)
+class Tag(models.Model):
+ name = models.CharField(max_length=30)
+ content_type = models.ForeignKey(ContentType, related_name='backend_tags')
+ object_id = models.PositiveIntegerField()
+ content_object = generic.GenericForeignKey('content_type', 'object_id')
+
+
+class Post(models.Model):
+ name = models.CharField(max_length=30)
+ text = models.TextField()
+ tags = generic.GenericRelation('Tag')
+
+ class Meta:
+ db_table = 'CaseSensitive_Post'
+
qn = connection.ops.quote_name
__test__ = {'API_TESTS': """
diff --git a/tests/regressiontests/backends/tests.py b/tests/regressiontests/backends/tests.py
index ee3ccdc72f..19d7fc5f6c 100644
--- a/tests/regressiontests/backends/tests.py
+++ b/tests/regressiontests/backends/tests.py
@@ -6,7 +6,7 @@ import unittest
from django.conf import settings
from django.core import management
from django.core.management.color import no_style
-from django.db import backend, connection, DEFAULT_DB_ALIAS
+from django.db import backend, connection, connections, DEFAULT_DB_ALIAS
from django.db.backends.signals import connection_created
from django.test import TestCase
@@ -137,6 +137,23 @@ if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.mysql':
for statement in connection.ops.sql_flush(no_style(), tables, sequences):
cursor.execute(statement)
+class SequenceResetTest(TestCase):
+ def test_generic_relation(self):
+ "Sequence names are correct when resetting generic relations (Ref #13941)"
+ # Create an object with a manually specified PK
+ models.Post.objects.create(id=10, name='1st post', text='hello world')
+
+ # Reset the sequences for the database
+ cursor = connection.cursor()
+ commands = connections[DEFAULT_DB_ALIAS].ops.sequence_reset_sql(no_style(), [models.Post])
+ for sql in commands:
+ cursor.execute(sql)
+
+ # If we create a new object now, it should have a PK greater
+ # than the PK we specified manually.
+ obj = models.Post.objects.create(name='New post', text='goodbye world')
+ self.assertTrue(obj.pk > 10)
+
def connection_created_test(sender, **kwargs):
print 'connection_created signal'
diff --git a/tests/regressiontests/forms/forms.py b/tests/regressiontests/forms/forms.py
index 58051fd133..9e45a267db 100644
--- a/tests/regressiontests/forms/forms.py
+++ b/tests/regressiontests/forms/forms.py
@@ -705,13 +705,13 @@ Form.clean() is required to return a dictionary of all clean data.
>>> 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>
-<tr><th>Password1:</th><td><input type="password" name="password1" value="foo" /></td></tr>
-<tr><th>Password2:</th><td><input type="password" name="password2" value="bar" /></td></tr>
+<tr><th>Password1:</th><td><input type="password" name="password1" /></td></tr>
+<tr><th>Password2:</th><td><input type="password" name="password2" /></td></tr>
>>> print f.as_ul()
<li><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></li>
<li>Username: <input type="text" name="username" value="adrian" maxlength="10" /></li>
-<li>Password1: <input type="password" name="password1" value="foo" /></li>
-<li>Password2: <input type="password" name="password2" value="bar" /></li>
+<li>Password1: <input type="password" name="password1" /></li>
+<li>Password2: <input type="password" name="password2" /></li>
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False)
>>> f.errors
{}
@@ -1258,20 +1258,20 @@ to a Field class. This help text is displayed when a Form is rendered.
... password = CharField(widget=PasswordInput, help_text='Choose wisely.')
>>> p = UserRegistration(auto_id=False)
>>> print p.as_ul()
-<li>Username: <input type="text" name="username" maxlength="10" /> e.g., user@example.com</li>
-<li>Password: <input type="password" name="password" /> Choose wisely.</li>
+<li>Username: <input type="text" name="username" maxlength="10" /> <span class="helptext">e.g., user@example.com</span></li>
+<li>Password: <input type="password" name="password" /> <span class="helptext">Choose wisely.</span></li>
>>> print p.as_p()
-<p>Username: <input type="text" name="username" maxlength="10" /> e.g., user@example.com</p>
-<p>Password: <input type="password" name="password" /> Choose wisely.</p>
+<p>Username: <input type="text" name="username" maxlength="10" /> <span class="helptext">e.g., user@example.com</span></p>
+<p>Password: <input type="password" name="password" /> <span class="helptext">Choose wisely.</span></p>
>>> print p.as_table()
-<tr><th>Username:</th><td><input type="text" name="username" maxlength="10" /><br />e.g., user@example.com</td></tr>
-<tr><th>Password:</th><td><input type="password" name="password" /><br />Choose wisely.</td></tr>
+<tr><th>Username:</th><td><input type="text" name="username" maxlength="10" /><br /><span class="helptext">e.g., user@example.com</span></td></tr>
+<tr><th>Password:</th><td><input type="password" name="password" /><br /><span class="helptext">Choose wisely.</span></td></tr>
The help text is displayed whether or not data is provided for the form.
>>> p = UserRegistration({'username': u'foo'}, auto_id=False)
>>> print p.as_ul()
-<li>Username: <input type="text" name="username" value="foo" maxlength="10" /> e.g., user@example.com</li>
-<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /> Choose wisely.</li>
+<li>Username: <input type="text" name="username" value="foo" maxlength="10" /> <span class="helptext">e.g., user@example.com</span></li>
+<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /> <span class="helptext">Choose wisely.</span></li>
help_text is not displayed for hidden fields. It can be used for documentation
purposes, though.
@@ -1281,7 +1281,7 @@ purposes, though.
... next = CharField(widget=HiddenInput, initial='/', help_text='Redirect destination')
>>> p = UserRegistration(auto_id=False)
>>> print p.as_ul()
-<li>Username: <input type="text" name="username" maxlength="10" /> e.g., user@example.com</li>
+<li>Username: <input type="text" name="username" maxlength="10" /> <span class="helptext">e.g., user@example.com</span></li>
<li>Password: <input type="password" name="password" /><input type="hidden" name="next" value="/" /></li>
Help text can include arbitrary Unicode characters.
@@ -1289,7 +1289,7 @@ Help text can include arbitrary Unicode characters.
... username = CharField(max_length=10, help_text='ŠĐĆŽćžšđ')
>>> p = UserRegistration(auto_id=False)
>>> p.as_ul()
-u'<li>Username: <input type="text" name="username" maxlength="10" /> \u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111</li>'
+u'<li>Username: <input type="text" name="username" maxlength="10" /> <span class="helptext">\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111</span></li>'
# Subclassing forms ###########################################################
@@ -1589,8 +1589,8 @@ Case 2: POST with erroneous data (a redisplayed form, with errors).
<table>
<tr><td colspan="2"><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></td></tr>
<tr><th>Username:</th><td><ul class="errorlist"><li>Ensure this value has at most 10 characters (it has 23).</li></ul><input type="text" name="username" value="this-is-a-long-username" maxlength="10" /></td></tr>
-<tr><th>Password1:</th><td><input type="password" name="password1" value="foo" /></td></tr>
-<tr><th>Password2:</th><td><input type="password" name="password2" value="bar" /></td></tr>
+<tr><th>Password1:</th><td><input type="password" name="password1" /></td></tr>
+<tr><th>Password2:</th><td><input type="password" name="password2" /></td></tr>
</table>
<input type="submit" />
</form>
@@ -1719,8 +1719,8 @@ the list of errors is empty). You can also use it in {% if %} statements.
>>> print t.render(Context({'form': UserRegistration({'username': 'django', 'password1': 'foo', 'password2': 'bar'}, auto_id=False)}))
<form action="">
<p><label>Your username: <input type="text" name="username" value="django" maxlength="10" /></label></p>
-<p><label>Password: <input type="password" name="password1" value="foo" /></label></p>
-<p><label>Password (again): <input type="password" name="password2" value="bar" /></label></p>
+<p><label>Password: <input type="password" name="password1" /></label></p>
+<p><label>Password (again): <input type="password" name="password2" /></label></p>
<input type="submit" />
</form>
>>> t = Template('''<form action="">
@@ -1734,8 +1734,8 @@ the list of errors is empty). You can also use it in {% if %} statements.
<form action="">
<ul class="errorlist"><li>Please make sure your passwords match.</li></ul>
<p><label>Your username: <input type="text" name="username" value="django" maxlength="10" /></label></p>
-<p><label>Password: <input type="password" name="password1" value="foo" /></label></p>
-<p><label>Password (again): <input type="password" name="password2" value="bar" /></label></p>
+<p><label>Password: <input type="password" name="password1" /></label></p>
+<p><label>Password (again): <input type="password" name="password2" /></label></p>
<input type="submit" />
</form>
diff --git a/tests/regressiontests/forms/input_formats.py b/tests/regressiontests/forms/input_formats.py
new file mode 100644
index 0000000000..498c6de9fb
--- /dev/null
+++ b/tests/regressiontests/forms/input_formats.py
@@ -0,0 +1,894 @@
+from datetime import time, date, datetime
+from unittest import TestCase
+
+from django import forms
+from django.conf import settings
+from django.utils.translation import activate, deactivate
+
+
+class LocalizedTimeTests(TestCase):
+ def setUp(self):
+ self.old_TIME_INPUT_FORMATS = settings.TIME_INPUT_FORMATS
+ self.old_USE_L10N = settings.USE_L10N
+
+ settings.TIME_INPUT_FORMATS = ["%I:%M:%S %p", "%I:%M %p"]
+ settings.USE_L10N = True
+
+ activate('de')
+
+ def tearDown(self):
+ settings.TIME_INPUT_FORMATS = self.old_TIME_INPUT_FORMATS
+ settings.USE_L10N = self.old_USE_L10N
+
+ deactivate()
+
+ def test_timeField(self):
+ "TimeFields can parse dates in the default format"
+ f = forms.TimeField()
+ # Parse a time in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM')
+
+ # Parse a time in a valid format, get a parsed result
+ result = f.clean('13:30:05')
+ self.assertEqual(result, time(13,30,5))
+
+ # Check that the parsed result does a round trip
+ text = f.widget._format_value(result)
+ self.assertEqual(text, '13:30:05')
+
+ # Parse a time in a valid, but non-default format, get a parsed result
+ result = f.clean('13:30')
+ self.assertEqual(result, time(13,30,0))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "13:30:00")
+
+ def test_localized_timeField(self):
+ "Localized TimeFields act as unlocalized widgets"
+ f = forms.TimeField(localize=True)
+ # Parse a time in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM')
+
+ # Parse a time in a valid format, get a parsed result
+ result = f.clean('13:30:05')
+ self.assertEqual(result, time(13,30,5))
+
+ # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, '13:30:05')
+
+ # Parse a time in a valid format, get a parsed result
+ result = f.clean('13:30')
+ self.assertEqual(result, time(13,30,0))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "13:30:00")
+
+ def test_timeField_with_inputformat(self):
+ "TimeFields with manually specified input formats can accept those formats"
+ f = forms.TimeField(input_formats=["%H.%M.%S", "%H.%M"])
+ # Parse a time in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM')
+ self.assertRaises(forms.ValidationError, f.clean, '13:30:05')
+
+ # Parse a time in a valid format, get a parsed result
+ result = f.clean('13.30.05')
+ self.assertEqual(result, time(13,30,5))
+
+ # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "13:30:05")
+
+ # Parse a time in a valid format, get a parsed result
+ result = f.clean('13.30')
+ self.assertEqual(result, time(13,30,0))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "13:30:00")
+
+ def test_localized_timeField_with_inputformat(self):
+ "Localized TimeFields with manually specified input formats can accept those formats"
+ f = forms.TimeField(input_formats=["%H.%M.%S", "%H.%M"], localize=True)
+ # Parse a time in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM')
+ self.assertRaises(forms.ValidationError, f.clean, '13:30:05')
+
+ # Parse a time in a valid format, get a parsed result
+ result = f.clean('13.30.05')
+ self.assertEqual(result, time(13,30,5))
+
+ # # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "13:30:05")
+
+ # Parse a time in a valid format, get a parsed result
+ result = f.clean('13.30')
+ self.assertEqual(result, time(13,30,0))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "13:30:00")
+
+
+class CustomTimeInputFormatsTests(TestCase):
+ def setUp(self):
+ self.old_TIME_INPUT_FORMATS = settings.TIME_INPUT_FORMATS
+ settings.TIME_INPUT_FORMATS = ["%I:%M:%S %p", "%I:%M %p"]
+
+ def tearDown(self):
+ settings.TIME_INPUT_FORMATS = self.old_TIME_INPUT_FORMATS
+
+ def test_timeField(self):
+ "TimeFields can parse dates in the default format"
+ f = forms.TimeField()
+ # Parse a time in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '13:30:05')
+
+ # Parse a time in a valid format, get a parsed result
+ result = f.clean('1:30:05 PM')
+ self.assertEqual(result, time(13,30,5))
+
+ # Check that the parsed result does a round trip
+ text = f.widget._format_value(result)
+ self.assertEqual(text, '01:30:05 PM')
+
+ # Parse a time in a valid, but non-default format, get a parsed result
+ result = f.clean('1:30 PM')
+ self.assertEqual(result, time(13,30,0))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "01:30:00 PM")
+
+ def test_localized_timeField(self):
+ "Localized TimeFields act as unlocalized widgets"
+ f = forms.TimeField(localize=True)
+ # Parse a time in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '13:30:05')
+
+ # Parse a time in a valid format, get a parsed result
+ result = f.clean('1:30:05 PM')
+ self.assertEqual(result, time(13,30,5))
+
+ # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, '01:30:05 PM')
+
+ # Parse a time in a valid format, get a parsed result
+ result = f.clean('01:30 PM')
+ self.assertEqual(result, time(13,30,0))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "01:30:00 PM")
+
+ def test_timeField_with_inputformat(self):
+ "TimeFields with manually specified input formats can accept those formats"
+ f = forms.TimeField(input_formats=["%H.%M.%S", "%H.%M"])
+ # Parse a time in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM')
+ self.assertRaises(forms.ValidationError, f.clean, '13:30:05')
+
+ # Parse a time in a valid format, get a parsed result
+ result = f.clean('13.30.05')
+ self.assertEqual(result, time(13,30,5))
+
+ # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "01:30:05 PM")
+
+ # Parse a time in a valid format, get a parsed result
+ result = f.clean('13.30')
+ self.assertEqual(result, time(13,30,0))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "01:30:00 PM")
+
+ def test_localized_timeField_with_inputformat(self):
+ "Localized TimeFields with manually specified input formats can accept those formats"
+ f = forms.TimeField(input_formats=["%H.%M.%S", "%H.%M"], localize=True)
+ # Parse a time in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM')
+ self.assertRaises(forms.ValidationError, f.clean, '13:30:05')
+
+ # Parse a time in a valid format, get a parsed result
+ result = f.clean('13.30.05')
+ self.assertEqual(result, time(13,30,5))
+
+ # # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "01:30:05 PM")
+
+ # Parse a time in a valid format, get a parsed result
+ result = f.clean('13.30')
+ self.assertEqual(result, time(13,30,0))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "01:30:00 PM")
+
+
+class SimpleTimeFormatTests(TestCase):
+ def test_timeField(self):
+ "TimeFields can parse dates in the default format"
+ f = forms.TimeField()
+ # Parse a time in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM')
+
+ # Parse a time in a valid format, get a parsed result
+ result = f.clean('13:30:05')
+ self.assertEqual(result, time(13,30,5))
+
+ # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "13:30:05")
+
+ # Parse a time in a valid, but non-default format, get a parsed result
+ result = f.clean('13:30')
+ self.assertEqual(result, time(13,30,0))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "13:30:00")
+
+ def test_localized_timeField(self):
+ "Localized TimeFields in a non-localized environment act as unlocalized widgets"
+ f = forms.TimeField()
+ # Parse a time in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM')
+
+ # Parse a time in a valid format, get a parsed result
+ result = f.clean('13:30:05')
+ self.assertEqual(result, time(13,30,5))
+
+ # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "13:30:05")
+
+ # Parse a time in a valid format, get a parsed result
+ result = f.clean('13:30')
+ self.assertEqual(result, time(13,30,0))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "13:30:00")
+
+ def test_timeField_with_inputformat(self):
+ "TimeFields with manually specified input formats can accept those formats"
+ f = forms.TimeField(input_formats=["%I:%M:%S %p", "%I:%M %p"])
+ # Parse a time in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '13:30:05')
+
+ # Parse a time in a valid format, get a parsed result
+ result = f.clean('1:30:05 PM')
+ self.assertEqual(result, time(13,30,5))
+
+ # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "13:30:05")
+
+ # Parse a time in a valid format, get a parsed result
+ result = f.clean('1:30 PM')
+ self.assertEqual(result, time(13,30,0))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "13:30:00")
+
+ def test_localized_timeField_with_inputformat(self):
+ "Localized TimeFields with manually specified input formats can accept those formats"
+ f = forms.TimeField(input_formats=["%I:%M:%S %p", "%I:%M %p"], localize=True)
+ # Parse a time in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '13:30:05')
+
+ # Parse a time in a valid format, get a parsed result
+ result = f.clean('1:30:05 PM')
+ self.assertEqual(result, time(13,30,5))
+
+ # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "13:30:05")
+
+ # Parse a time in a valid format, get a parsed result
+ result = f.clean('1:30 PM')
+ self.assertEqual(result, time(13,30,0))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "13:30:00")
+
+
+class LocalizedDateTests(TestCase):
+ def setUp(self):
+ self.old_DATE_INPUT_FORMATS = settings.DATE_INPUT_FORMATS
+ self.old_USE_L10N = settings.USE_L10N
+
+ settings.DATE_INPUT_FORMATS = ["%d/%m/%Y", "%d-%m-%Y"]
+ settings.USE_L10N = True
+
+ activate('de')
+
+ def tearDown(self):
+ settings.DATE_INPUT_FORMATS = self.old_DATE_INPUT_FORMATS
+ settings.USE_L10N = self.old_USE_L10N
+
+ deactivate()
+
+ def test_dateField(self):
+ "DateFields can parse dates in the default format"
+ f = forms.DateField()
+ # Parse a date in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '21/12/2010')
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('21.12.2010')
+ self.assertEqual(result, date(2010,12,21))
+
+ # Check that the parsed result does a round trip
+ text = f.widget._format_value(result)
+ self.assertEqual(text, '21.12.2010')
+
+ # Parse a date in a valid, but non-default format, get a parsed result
+ result = f.clean('21.12.10')
+ self.assertEqual(result, date(2010,12,21))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "21.12.2010")
+
+ def test_localized_dateField(self):
+ "Localized DateFields act as unlocalized widgets"
+ f = forms.DateField(localize=True)
+ # Parse a date in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '21/12/2010')
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('21.12.2010')
+ self.assertEqual(result, date(2010,12,21))
+
+ # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, '21.12.2010')
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('21.12.10')
+ self.assertEqual(result, date(2010,12,21))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "21.12.2010")
+
+ def test_dateField_with_inputformat(self):
+ "DateFields with manually specified input formats can accept those formats"
+ f = forms.DateField(input_formats=["%m.%d.%Y", "%m-%d-%Y"])
+ # Parse a date in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '2010-12-21')
+ self.assertRaises(forms.ValidationError, f.clean, '21/12/2010')
+ self.assertRaises(forms.ValidationError, f.clean, '21.12.2010')
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('12.21.2010')
+ self.assertEqual(result, date(2010,12,21))
+
+ # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "21.12.2010")
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('12-21-2010')
+ self.assertEqual(result, date(2010,12,21))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "21.12.2010")
+
+ def test_localized_dateField_with_inputformat(self):
+ "Localized DateFields with manually specified input formats can accept those formats"
+ f = forms.DateField(input_formats=["%m.%d.%Y", "%m-%d-%Y"], localize=True)
+ # Parse a date in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '2010-12-21')
+ self.assertRaises(forms.ValidationError, f.clean, '21/12/2010')
+ self.assertRaises(forms.ValidationError, f.clean, '21.12.2010')
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('12.21.2010')
+ self.assertEqual(result, date(2010,12,21))
+
+ # # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "21.12.2010")
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('12-21-2010')
+ self.assertEqual(result, date(2010,12,21))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "21.12.2010")
+
+class CustomDateInputFormatsTests(TestCase):
+ def setUp(self):
+ self.old_DATE_INPUT_FORMATS = settings.DATE_INPUT_FORMATS
+ settings.DATE_INPUT_FORMATS = ["%d.%m.%Y", "%d-%m-%Y"]
+
+ def tearDown(self):
+ settings.DATE_INPUT_FORMATS = self.old_DATE_INPUT_FORMATS
+
+ def test_dateField(self):
+ "DateFields can parse dates in the default format"
+ f = forms.DateField()
+ # Parse a date in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '2010-12-21')
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('21.12.2010')
+ self.assertEqual(result, date(2010,12,21))
+
+ # Check that the parsed result does a round trip
+ text = f.widget._format_value(result)
+ self.assertEqual(text, '21.12.2010')
+
+ # Parse a date in a valid, but non-default format, get a parsed result
+ result = f.clean('21-12-2010')
+ self.assertEqual(result, date(2010,12,21))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "21.12.2010")
+
+ def test_localized_dateField(self):
+ "Localized DateFields act as unlocalized widgets"
+ f = forms.DateField(localize=True)
+ # Parse a date in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '2010-12-21')
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('21.12.2010')
+ self.assertEqual(result, date(2010,12,21))
+
+ # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, '21.12.2010')
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('21-12-2010')
+ self.assertEqual(result, date(2010,12,21))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "21.12.2010")
+
+ def test_dateField_with_inputformat(self):
+ "DateFields with manually specified input formats can accept those formats"
+ f = forms.DateField(input_formats=["%m.%d.%Y", "%m-%d-%Y"])
+ # Parse a date in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '21.12.2010')
+ self.assertRaises(forms.ValidationError, f.clean, '2010-12-21')
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('12.21.2010')
+ self.assertEqual(result, date(2010,12,21))
+
+ # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "21.12.2010")
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('12-21-2010')
+ self.assertEqual(result, date(2010,12,21))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "21.12.2010")
+
+ def test_localized_dateField_with_inputformat(self):
+ "Localized DateFields with manually specified input formats can accept those formats"
+ f = forms.DateField(input_formats=["%m.%d.%Y", "%m-%d-%Y"], localize=True)
+ # Parse a date in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '21.12.2010')
+ self.assertRaises(forms.ValidationError, f.clean, '2010-12-21')
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('12.21.2010')
+ self.assertEqual(result, date(2010,12,21))
+
+ # # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "21.12.2010")
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('12-21-2010')
+ self.assertEqual(result, date(2010,12,21))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "21.12.2010")
+
+class SimpleDateFormatTests(TestCase):
+ def test_dateField(self):
+ "DateFields can parse dates in the default format"
+ f = forms.DateField()
+ # Parse a date in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '21.12.2010')
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('2010-12-21')
+ self.assertEqual(result, date(2010,12,21))
+
+ # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "2010-12-21")
+
+ # Parse a date in a valid, but non-default format, get a parsed result
+ result = f.clean('12/21/2010')
+ self.assertEqual(result, date(2010,12,21))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "2010-12-21")
+
+ def test_localized_dateField(self):
+ "Localized DateFields in a non-localized environment act as unlocalized widgets"
+ f = forms.DateField()
+ # Parse a date in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '21.12.2010')
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('2010-12-21')
+ self.assertEqual(result, date(2010,12,21))
+
+ # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "2010-12-21")
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('12/21/2010')
+ self.assertEqual(result, date(2010,12,21))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "2010-12-21")
+
+ def test_dateField_with_inputformat(self):
+ "DateFields with manually specified input formats can accept those formats"
+ f = forms.DateField(input_formats=["%d.%m.%Y", "%d-%m-%Y"])
+ # Parse a date in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '2010-12-21')
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('21.12.2010')
+ self.assertEqual(result, date(2010,12,21))
+
+ # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "2010-12-21")
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('21-12-2010')
+ self.assertEqual(result, date(2010,12,21))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "2010-12-21")
+
+ def test_localized_dateField_with_inputformat(self):
+ "Localized DateFields with manually specified input formats can accept those formats"
+ f = forms.DateField(input_formats=["%d.%m.%Y", "%d-%m-%Y"], localize=True)
+ # Parse a date in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '2010-12-21')
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('21.12.2010')
+ self.assertEqual(result, date(2010,12,21))
+
+ # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "2010-12-21")
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('21-12-2010')
+ self.assertEqual(result, date(2010,12,21))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "2010-12-21")
+
+class LocalizedDateTimeTests(TestCase):
+ def setUp(self):
+ self.old_DATETIME_INPUT_FORMATS = settings.DATETIME_INPUT_FORMATS
+ self.old_USE_L10N = settings.USE_L10N
+
+ settings.DATETIME_INPUT_FORMATS = ["%I:%M:%S %p %d/%m/%Y", "%I:%M %p %d-%m-%Y"]
+ settings.USE_L10N = True
+
+ activate('de')
+
+ def tearDown(self):
+ settings.DATETIME_INPUT_FORMATS = self.old_DATETIME_INPUT_FORMATS
+ settings.USE_L10N = self.old_USE_L10N
+
+ deactivate()
+
+ def test_dateTimeField(self):
+ "DateTimeFields can parse dates in the default format"
+ f = forms.DateTimeField()
+ # Parse a date in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM 21/12/2010')
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('21.12.2010 13:30:05')
+ self.assertEqual(result, datetime(2010,12,21,13,30,5))
+
+ # Check that the parsed result does a round trip
+ text = f.widget._format_value(result)
+ self.assertEqual(text, '21.12.2010 13:30:05')
+
+ # Parse a date in a valid, but non-default format, get a parsed result
+ result = f.clean('21.12.2010 13:30')
+ self.assertEqual(result, datetime(2010,12,21,13,30))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "21.12.2010 13:30:00")
+
+ def test_localized_dateTimeField(self):
+ "Localized DateTimeFields act as unlocalized widgets"
+ f = forms.DateTimeField(localize=True)
+ # Parse a date in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM 21/12/2010')
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('21.12.2010 13:30:05')
+ self.assertEqual(result, datetime(2010,12,21,13,30,5))
+
+ # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, '21.12.2010 13:30:05')
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('21.12.2010 13:30')
+ self.assertEqual(result, datetime(2010,12,21,13,30))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "21.12.2010 13:30:00")
+
+ def test_dateTimeField_with_inputformat(self):
+ "DateTimeFields with manually specified input formats can accept those formats"
+ f = forms.DateTimeField(input_formats=["%H.%M.%S %m.%d.%Y", "%H.%M %m-%d-%Y"])
+ # Parse a date in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05 13:30:05')
+ self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM 21/12/2010')
+ self.assertRaises(forms.ValidationError, f.clean, '13:30:05 21.12.2010')
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('13.30.05 12.21.2010')
+ self.assertEqual(result, datetime(2010,12,21,13,30,5))
+
+ # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "21.12.2010 13:30:05")
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('13.30 12-21-2010')
+ self.assertEqual(result, datetime(2010,12,21,13,30))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "21.12.2010 13:30:00")
+
+ def test_localized_dateTimeField_with_inputformat(self):
+ "Localized DateTimeFields with manually specified input formats can accept those formats"
+ f = forms.DateTimeField(input_formats=["%H.%M.%S %m.%d.%Y", "%H.%M %m-%d-%Y"], localize=True)
+ # Parse a date in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05')
+ self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM 21/12/2010')
+ self.assertRaises(forms.ValidationError, f.clean, '13:30:05 21.12.2010')
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('13.30.05 12.21.2010')
+ self.assertEqual(result, datetime(2010,12,21,13,30,5))
+
+ # # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "21.12.2010 13:30:05")
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('13.30 12-21-2010')
+ self.assertEqual(result, datetime(2010,12,21,13,30))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "21.12.2010 13:30:00")
+
+
+class CustomDateTimeInputFormatsTests(TestCase):
+ def setUp(self):
+ self.old_DATETIME_INPUT_FORMATS = settings.DATETIME_INPUT_FORMATS
+ settings.DATETIME_INPUT_FORMATS = ["%I:%M:%S %p %d/%m/%Y", "%I:%M %p %d-%m-%Y"]
+
+ def tearDown(self):
+ settings.DATETIME_INPUT_FORMATS = self.old_DATETIME_INPUT_FORMATS
+
+ def test_dateTimeField(self):
+ "DateTimeFields can parse dates in the default format"
+ f = forms.DateTimeField()
+ # Parse a date in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05')
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('1:30:05 PM 21/12/2010')
+ self.assertEqual(result, datetime(2010,12,21,13,30,5))
+
+ # Check that the parsed result does a round trip
+ text = f.widget._format_value(result)
+ self.assertEqual(text, '01:30:05 PM 21/12/2010')
+
+ # Parse a date in a valid, but non-default format, get a parsed result
+ result = f.clean('1:30 PM 21-12-2010')
+ self.assertEqual(result, datetime(2010,12,21,13,30))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "01:30:00 PM 21/12/2010")
+
+ def test_localized_dateTimeField(self):
+ "Localized DateTimeFields act as unlocalized widgets"
+ f = forms.DateTimeField(localize=True)
+ # Parse a date in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05')
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('1:30:05 PM 21/12/2010')
+ self.assertEqual(result, datetime(2010,12,21,13,30,5))
+
+ # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, '01:30:05 PM 21/12/2010')
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('1:30 PM 21-12-2010')
+ self.assertEqual(result, datetime(2010,12,21,13,30))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "01:30:00 PM 21/12/2010")
+
+ def test_dateTimeField_with_inputformat(self):
+ "DateTimeFields with manually specified input formats can accept those formats"
+ f = forms.DateTimeField(input_formats=["%m.%d.%Y %H:%M:%S", "%m-%d-%Y %H:%M"])
+ # Parse a date in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '13:30:05 21.12.2010')
+ self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05')
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('12.21.2010 13:30:05')
+ self.assertEqual(result, datetime(2010,12,21,13,30,5))
+
+ # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "01:30:05 PM 21/12/2010")
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('12-21-2010 13:30')
+ self.assertEqual(result, datetime(2010,12,21,13,30))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "01:30:00 PM 21/12/2010")
+
+ def test_localized_dateTimeField_with_inputformat(self):
+ "Localized DateTimeFields with manually specified input formats can accept those formats"
+ f = forms.DateTimeField(input_formats=["%m.%d.%Y %H:%M:%S", "%m-%d-%Y %H:%M"], localize=True)
+ # Parse a date in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '13:30:05 21.12.2010')
+ self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05')
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('12.21.2010 13:30:05')
+ self.assertEqual(result, datetime(2010,12,21,13,30,5))
+
+ # # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "01:30:05 PM 21/12/2010")
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('12-21-2010 13:30')
+ self.assertEqual(result, datetime(2010,12,21,13,30))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "01:30:00 PM 21/12/2010")
+
+class SimpleDateTimeFormatTests(TestCase):
+ def test_dateTimeField(self):
+ "DateTimeFields can parse dates in the default format"
+ f = forms.DateTimeField()
+ # Parse a date in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '13:30:05 21.12.2010')
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('2010-12-21 13:30:05')
+ self.assertEqual(result, datetime(2010,12,21,13,30,5))
+
+ # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "2010-12-21 13:30:05")
+
+ # Parse a date in a valid, but non-default format, get a parsed result
+ result = f.clean('12/21/2010 13:30:05')
+ self.assertEqual(result, datetime(2010,12,21,13,30,5))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "2010-12-21 13:30:05")
+
+ def test_localized_dateTimeField(self):
+ "Localized DateTimeFields in a non-localized environment act as unlocalized widgets"
+ f = forms.DateTimeField()
+ # Parse a date in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '13:30:05 21.12.2010')
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('2010-12-21 13:30:05')
+ self.assertEqual(result, datetime(2010,12,21,13,30,5))
+
+ # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "2010-12-21 13:30:05")
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('12/21/2010 13:30:05')
+ self.assertEqual(result, datetime(2010,12,21,13,30,5))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "2010-12-21 13:30:05")
+
+ def test_dateTimeField_with_inputformat(self):
+ "DateTimeFields with manually specified input formats can accept those formats"
+ f = forms.DateTimeField(input_formats=["%I:%M:%S %p %d.%m.%Y", "%I:%M %p %d-%m-%Y"])
+ # Parse a date in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05')
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('1:30:05 PM 21.12.2010')
+ self.assertEqual(result, datetime(2010,12,21,13,30,5))
+
+ # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "2010-12-21 13:30:05")
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('1:30 PM 21-12-2010')
+ self.assertEqual(result, datetime(2010,12,21,13,30))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "2010-12-21 13:30:00")
+
+ def test_localized_dateTimeField_with_inputformat(self):
+ "Localized DateTimeFields with manually specified input formats can accept those formats"
+ f = forms.DateTimeField(input_formats=["%I:%M:%S %p %d.%m.%Y", "%I:%M %p %d-%m-%Y"], localize=True)
+ # Parse a date in an unaccepted format; get an error
+ self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05')
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('1:30:05 PM 21.12.2010')
+ self.assertEqual(result, datetime(2010,12,21,13,30,5))
+
+ # Check that the parsed result does a round trip to the same format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "2010-12-21 13:30:05")
+
+ # Parse a date in a valid format, get a parsed result
+ result = f.clean('1:30 PM 21-12-2010')
+ self.assertEqual(result, datetime(2010,12,21,13,30))
+
+ # Check that the parsed result does a round trip to default format
+ text = f.widget._format_value(result)
+ self.assertEqual(text, "2010-12-21 13:30:00")
diff --git a/tests/regressiontests/forms/localflavor/au.py b/tests/regressiontests/forms/localflavor/au.py
index fd4c0d6980..cda782094a 100644
--- a/tests/regressiontests/forms/localflavor/au.py
+++ b/tests/regressiontests/forms/localflavor/au.py
@@ -50,7 +50,7 @@ u''
## AUPhoneNumberField ########################################################
A field that accepts a 10 digit Australian phone number.
-llows spaces and parentheses around area code.
+Allows spaces and parentheses around area code.
>>> from django.contrib.localflavor.au.forms import AUPhoneNumberField
>>> f = AUPhoneNumberField()
diff --git a/tests/regressiontests/forms/tests.py b/tests/regressiontests/forms/tests.py
index 8757e799a9..7a91cb701e 100644
--- a/tests/regressiontests/forms/tests.py
+++ b/tests/regressiontests/forms/tests.py
@@ -41,6 +41,8 @@ from fields import FieldsTests
from validators import TestFieldWithValidators
from widgets import WidgetTests
+from input_formats import *
+
__test__ = {
'extra_tests': extra_tests,
'form_tests': form_tests,
diff --git a/tests/regressiontests/forms/widgets.py b/tests/regressiontests/forms/widgets.py
index 39d7d569a3..b8ec789649 100644
--- a/tests/regressiontests/forms/widgets.py
+++ b/tests/regressiontests/forms/widgets.py
@@ -62,6 +62,17 @@ u'<input type="text" class="special" name="email" />'
u'<input type="password" name="email" />'
>>> w.render('email', None)
u'<input type="password" name="email" />'
+>>> w.render('email', 'secret')
+u'<input type="password" name="email" />'
+
+The render_value argument lets you specify whether the widget should render
+its value. For security reasons, this is off by default.
+
+>>> w = PasswordInput(render_value=True)
+>>> w.render('email', '')
+u'<input type="password" name="email" />'
+>>> w.render('email', None)
+u'<input type="password" name="email" />'
>>> w.render('email', 'test@example.com')
u'<input type="password" name="email" value="test@example.com" />'
>>> w.render('email', 'some "quoted" & ampersanded value')
@@ -70,36 +81,20 @@ u'<input type="password" name="email" value="some &quot;quoted&quot; &amp; amper
u'<input type="password" name="email" value="test@example.com" class="fun" />'
You can also pass 'attrs' to the constructor:
->>> w = PasswordInput(attrs={'class': 'fun'})
+>>> w = PasswordInput(attrs={'class': 'fun'}, render_value=True)
>>> w.render('email', '')
u'<input type="password" class="fun" name="email" />'
>>> w.render('email', 'foo@example.com')
u'<input type="password" class="fun" value="foo@example.com" name="email" />'
'attrs' passed to render() get precedence over those passed to the constructor:
->>> w = PasswordInput(attrs={'class': 'pretty'})
+>>> w = PasswordInput(attrs={'class': 'pretty'}, render_value=True)
>>> w.render('email', '', attrs={'class': 'special'})
u'<input type="password" class="special" name="email" />'
>>> w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'})
u'<input type="password" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />'
-The render_value argument lets you specify whether the widget should render
-its value. You may want to do this for security reasons.
->>> w = PasswordInput(render_value=True)
->>> w.render('email', 'secret')
-u'<input type="password" name="email" value="secret" />'
->>> w = PasswordInput(render_value=False)
->>> w.render('email', '')
-u'<input type="password" name="email" />'
->>> w.render('email', None)
-u'<input type="password" name="email" />'
->>> w.render('email', 'secret')
-u'<input type="password" name="email" />'
->>> w = PasswordInput(attrs={'class': 'fun'}, render_value=False)
->>> w.render('email', 'secret')
-u'<input type="password" class="fun" name="email" />'
-
# HiddenInput Widget ############################################################
>>> w = HiddenInput()
@@ -1286,7 +1281,7 @@ class SelectAndTextWidget(forms.MultiWidget):
forms.TextInput
]
super(SelectAndTextWidget, self).__init__(widgets)
-
+
def _set_choices(self, choices):
"""
When choices are set for this widget, we want to pass those along to the Select widget
diff --git a/tests/regressiontests/localflavor/models.py b/tests/regressiontests/localflavor/models.py
index f74a5051d4..e69de29bb2 100644
--- a/tests/regressiontests/localflavor/models.py
+++ b/tests/regressiontests/localflavor/models.py
@@ -1,8 +0,0 @@
-from django.db import models
-from django.contrib.localflavor.us.models import USStateField
-
-class Place(models.Model):
- state = USStateField(blank=True)
- state_req = USStateField()
- state_default = USStateField(default="CA", blank=True)
- name = models.CharField(max_length=20)
diff --git a/tests/regressiontests/localflavor/tests.py b/tests/regressiontests/localflavor/tests.py
index 0ea3c52568..69682360d3 100644
--- a/tests/regressiontests/localflavor/tests.py
+++ b/tests/regressiontests/localflavor/tests.py
@@ -1,83 +1,5 @@
+import unittest
from django.test import TestCase
-from models import Place
-from forms import PlaceForm
-class USLocalflavorTests(TestCase):
- def setUp(self):
- self.form = PlaceForm({'state':'GA', 'state_req':'NC', 'name':'impossible'})
-
- def test_get_display_methods(self):
- """Test that the get_*_display() methods are added to the model instances."""
- place = self.form.save()
- self.assertEqual(place.get_state_display(), 'Georgia')
- self.assertEqual(place.get_state_req_display(), 'North Carolina')
-
- def test_required(self):
- """Test that required USStateFields throw appropriate errors."""
- form = PlaceForm({'state':'GA', 'name':'Place in GA'})
- self.assertFalse(form.is_valid())
- self.assertEqual(form.errors['state_req'], [u'This field is required.'])
-
- def test_field_blank_option(self):
- """Test that the empty option is there."""
- state_select_html = """\
-<select name="state" id="id_state">
-<option value="">---------</option>
-<option value="AL">Alabama</option>
-<option value="AK">Alaska</option>
-<option value="AS">American Samoa</option>
-<option value="AZ">Arizona</option>
-<option value="AR">Arkansas</option>
-<option value="CA">California</option>
-<option value="CO">Colorado</option>
-<option value="CT">Connecticut</option>
-<option value="DE">Delaware</option>
-<option value="DC">District of Columbia</option>
-<option value="FL">Florida</option>
-<option value="GA" selected="selected">Georgia</option>
-<option value="GU">Guam</option>
-<option value="HI">Hawaii</option>
-<option value="ID">Idaho</option>
-<option value="IL">Illinois</option>
-<option value="IN">Indiana</option>
-<option value="IA">Iowa</option>
-<option value="KS">Kansas</option>
-<option value="KY">Kentucky</option>
-<option value="LA">Louisiana</option>
-<option value="ME">Maine</option>
-<option value="MD">Maryland</option>
-<option value="MA">Massachusetts</option>
-<option value="MI">Michigan</option>
-<option value="MN">Minnesota</option>
-<option value="MS">Mississippi</option>
-<option value="MO">Missouri</option>
-<option value="MT">Montana</option>
-<option value="NE">Nebraska</option>
-<option value="NV">Nevada</option>
-<option value="NH">New Hampshire</option>
-<option value="NJ">New Jersey</option>
-<option value="NM">New Mexico</option>
-<option value="NY">New York</option>
-<option value="NC">North Carolina</option>
-<option value="ND">North Dakota</option>
-<option value="MP">Northern Mariana Islands</option>
-<option value="OH">Ohio</option>
-<option value="OK">Oklahoma</option>
-<option value="OR">Oregon</option>
-<option value="PA">Pennsylvania</option>
-<option value="PR">Puerto Rico</option>
-<option value="RI">Rhode Island</option>
-<option value="SC">South Carolina</option>
-<option value="SD">South Dakota</option>
-<option value="TN">Tennessee</option>
-<option value="TX">Texas</option>
-<option value="UT">Utah</option>
-<option value="VT">Vermont</option>
-<option value="VI">Virgin Islands</option>
-<option value="VA">Virginia</option>
-<option value="WA">Washington</option>
-<option value="WV">West Virginia</option>
-<option value="WI">Wisconsin</option>
-<option value="WY">Wyoming</option>
-</select>"""
- self.assertEqual(str(self.form['state']), state_select_html)
+# just import your tests here
+from us.tests import *
diff --git a/tests/regressiontests/localflavor/us/__init__.py b/tests/regressiontests/localflavor/us/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/regressiontests/localflavor/us/__init__.py
diff --git a/tests/regressiontests/localflavor/forms.py b/tests/regressiontests/localflavor/us/forms.py
index 2dd1da6dd0..9b77e1078b 100644
--- a/tests/regressiontests/localflavor/forms.py
+++ b/tests/regressiontests/localflavor/us/forms.py
@@ -1,7 +1,7 @@
from django.forms import ModelForm
-from models import Place
+from models import USPlace
-class PlaceForm(ModelForm):
+class USPlaceForm(ModelForm):
"""docstring for PlaceForm"""
class Meta:
- model = Place
+ model = USPlace
diff --git a/tests/regressiontests/localflavor/us/models.py b/tests/regressiontests/localflavor/us/models.py
new file mode 100644
index 0000000000..a8a4cf0f50
--- /dev/null
+++ b/tests/regressiontests/localflavor/us/models.py
@@ -0,0 +1,13 @@
+from django.db import models
+from django.contrib.localflavor.us.models import USStateField
+
+# When creating models you need to remember to add a app_label as
+# 'localflavor', so your model can be found
+
+class USPlace(models.Model):
+ state = USStateField(blank=True)
+ state_req = USStateField()
+ state_default = USStateField(default="CA", blank=True)
+ name = models.CharField(max_length=20)
+ class Meta:
+ app_label = 'localflavor'
diff --git a/tests/regressiontests/localflavor/us/tests.py b/tests/regressiontests/localflavor/us/tests.py
new file mode 100644
index 0000000000..07fe057833
--- /dev/null
+++ b/tests/regressiontests/localflavor/us/tests.py
@@ -0,0 +1,82 @@
+from django.test import TestCase
+from forms import USPlaceForm
+
+class USLocalflavorTests(TestCase):
+ def setUp(self):
+ self.form = USPlaceForm({'state':'GA', 'state_req':'NC', 'name':'impossible'})
+
+ def test_get_display_methods(self):
+ """Test that the get_*_display() methods are added to the model instances."""
+ place = self.form.save()
+ self.assertEqual(place.get_state_display(), 'Georgia')
+ self.assertEqual(place.get_state_req_display(), 'North Carolina')
+
+ def test_required(self):
+ """Test that required USStateFields throw appropriate errors."""
+ form = USPlaceForm({'state':'GA', 'name':'Place in GA'})
+ self.assertFalse(form.is_valid())
+ self.assertEqual(form.errors['state_req'], [u'This field is required.'])
+
+ def test_field_blank_option(self):
+ """Test that the empty option is there."""
+ state_select_html = """\
+<select name="state" id="id_state">
+<option value="">---------</option>
+<option value="AL">Alabama</option>
+<option value="AK">Alaska</option>
+<option value="AS">American Samoa</option>
+<option value="AZ">Arizona</option>
+<option value="AR">Arkansas</option>
+<option value="CA">California</option>
+<option value="CO">Colorado</option>
+<option value="CT">Connecticut</option>
+<option value="DE">Delaware</option>
+<option value="DC">District of Columbia</option>
+<option value="FL">Florida</option>
+<option value="GA" selected="selected">Georgia</option>
+<option value="GU">Guam</option>
+<option value="HI">Hawaii</option>
+<option value="ID">Idaho</option>
+<option value="IL">Illinois</option>
+<option value="IN">Indiana</option>
+<option value="IA">Iowa</option>
+<option value="KS">Kansas</option>
+<option value="KY">Kentucky</option>
+<option value="LA">Louisiana</option>
+<option value="ME">Maine</option>
+<option value="MD">Maryland</option>
+<option value="MA">Massachusetts</option>
+<option value="MI">Michigan</option>
+<option value="MN">Minnesota</option>
+<option value="MS">Mississippi</option>
+<option value="MO">Missouri</option>
+<option value="MT">Montana</option>
+<option value="NE">Nebraska</option>
+<option value="NV">Nevada</option>
+<option value="NH">New Hampshire</option>
+<option value="NJ">New Jersey</option>
+<option value="NM">New Mexico</option>
+<option value="NY">New York</option>
+<option value="NC">North Carolina</option>
+<option value="ND">North Dakota</option>
+<option value="MP">Northern Mariana Islands</option>
+<option value="OH">Ohio</option>
+<option value="OK">Oklahoma</option>
+<option value="OR">Oregon</option>
+<option value="PA">Pennsylvania</option>
+<option value="PR">Puerto Rico</option>
+<option value="RI">Rhode Island</option>
+<option value="SC">South Carolina</option>
+<option value="SD">South Dakota</option>
+<option value="TN">Tennessee</option>
+<option value="TX">Texas</option>
+<option value="UT">Utah</option>
+<option value="VT">Vermont</option>
+<option value="VI">Virgin Islands</option>
+<option value="VA">Virginia</option>
+<option value="WA">Washington</option>
+<option value="WV">West Virginia</option>
+<option value="WI">Wisconsin</option>
+<option value="WY">Wyoming</option>
+</select>"""
+ self.assertEqual(str(self.form['state']), state_select_html)
diff --git a/tests/regressiontests/model_forms_regress/models.py b/tests/regressiontests/model_forms_regress/models.py
index 871bb6f815..4f9811a963 100644
--- a/tests/regressiontests/model_forms_regress/models.py
+++ b/tests/regressiontests/model_forms_regress/models.py
@@ -54,3 +54,6 @@ class Author(models.Model):
class Author1(models.Model):
publication = models.OneToOneField(Publication, null=False)
full_name = models.CharField(max_length=255)
+
+class Homepage(models.Model):
+ url = models.URLField(verify_exists=False)
diff --git a/tests/regressiontests/model_forms_regress/tests.py b/tests/regressiontests/model_forms_regress/tests.py
index 5a7a83bc0e..5bee73ce7e 100644
--- a/tests/regressiontests/model_forms_regress/tests.py
+++ b/tests/regressiontests/model_forms_regress/tests.py
@@ -6,7 +6,8 @@ from django.forms.models import modelform_factory, ModelChoiceField
from django.conf import settings
from django.test import TestCase
-from models import Person, RealPerson, Triple, FilePathModel, Article, Publication, CustomFF, Author, Author1
+from models import Person, RealPerson, Triple, FilePathModel, Article, \
+ Publication, CustomFF, Author, Author1, Homepage
class ModelMultipleChoiceFieldTests(TestCase):
@@ -128,7 +129,7 @@ class ManyToManyCallableInitialTests(TestCase):
<option value="1" selected="selected">First Book</option>
<option value="2" selected="selected">Second Book</option>
<option value="3">Third Book</option>
-</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>""")
+</select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></li>""")
class CFFForm(forms.ModelForm):
class Meta:
@@ -212,7 +213,40 @@ class TestTicket11183(TestCase):
def test_11183(self):
form1 = ModelChoiceForm()
field1 = form1.fields['person']
- # To allow the widget to change the queryset of field1.widget.choices correctly,
+ # To allow the widget to change the queryset of field1.widget.choices correctly,
# without affecting other forms, the following must hold:
self.assert_(field1 is not ModelChoiceForm.base_fields['person'])
self.assert_(field1.widget.choices.field is field1)
+
+class HomepageForm(forms.ModelForm):
+ class Meta:
+ model = Homepage
+
+class URLFieldTests(TestCase):
+ def test_url_on_modelform(self):
+ "Check basic URL field validation on model forms"
+ self.assertFalse(HomepageForm({'url': 'foo'}).is_valid())
+ self.assertFalse(HomepageForm({'url': 'http://'}).is_valid())
+ self.assertFalse(HomepageForm({'url': 'http://example'}).is_valid())
+ self.assertFalse(HomepageForm({'url': 'http://example.'}).is_valid())
+ self.assertFalse(HomepageForm({'url': 'http://com.'}).is_valid())
+
+ self.assertTrue(HomepageForm({'url': 'http://localhost'}).is_valid())
+ self.assertTrue(HomepageForm({'url': 'http://example.com'}).is_valid())
+ self.assertTrue(HomepageForm({'url': 'http://www.example.com'}).is_valid())
+ self.assertTrue(HomepageForm({'url': 'http://www.example.com:8000'}).is_valid())
+ self.assertTrue(HomepageForm({'url': 'http://www.example.com/test'}).is_valid())
+ self.assertTrue(HomepageForm({'url': 'http://www.example.com:8000/test'}).is_valid())
+ self.assertTrue(HomepageForm({'url': 'http://example.com/foo/bar'}).is_valid())
+
+ def test_http_prefixing(self):
+ "If the http:// prefix is omitted on form input, the field adds it again. (Refs #13613)"
+ form = HomepageForm({'url': 'example.com'})
+ form.is_valid()
+ # self.assertTrue(form.is_valid())
+ # self.assertEquals(form.cleaned_data['url'], 'http://example.com/')
+
+ form = HomepageForm({'url': 'example.com/test'})
+ form.is_valid()
+ # self.assertTrue(form.is_valid())
+ # self.assertEquals(form.cleaned_data['url'], 'http://example.com/test')
diff --git a/tests/regressiontests/multiple_database/tests.py b/tests/regressiontests/multiple_database/tests.py
index 6675fdcc6c..b55e708944 100644
--- a/tests/regressiontests/multiple_database/tests.py
+++ b/tests/regressiontests/multiple_database/tests.py
@@ -7,6 +7,7 @@ from django.conf import settings
from django.contrib.auth.models import User
from django.core import management
from django.db import connections, router, DEFAULT_DB_ALIAS
+from django.db.models import signals
from django.db.utils import ConnectionRouter
from django.test import TestCase
@@ -1619,3 +1620,114 @@ class PickleQuerySetTestCase(TestCase):
Book.objects.using(db).create(title='Dive into Python', published=datetime.date(2009, 5, 4))
qs = Book.objects.all()
self.assertEqual(qs.db, pickle.loads(pickle.dumps(qs)).db)
+
+
+class DatabaseReceiver(object):
+ """
+ Used in the tests for the database argument in signals (#13552)
+ """
+ def __call__(self, signal, sender, **kwargs):
+ self._database = kwargs['using']
+
+class WriteToOtherRouter(object):
+ """
+ A router that sends all writes to the other database.
+ """
+ def db_for_write(self, model, **hints):
+ return "other"
+
+class SignalTests(TestCase):
+ multi_db = True
+
+ def setUp(self):
+ self.old_routers = router.routers
+
+ def tearDown(self):
+ router.routser = self.old_routers
+
+ def _write_to_other(self):
+ "Sends all writes to 'other'."
+ router.routers = [WriteToOtherRouter()]
+
+ def _write_to_default(self):
+ "Sends all writes to the default DB"
+ router.routers = self.old_routers
+
+ def test_database_arg_save_and_delete(self):
+ """
+ Tests that the pre/post_save signal contains the correct database.
+ (#13552)
+ """
+ # Make some signal receivers
+ pre_save_receiver = DatabaseReceiver()
+ post_save_receiver = DatabaseReceiver()
+ pre_delete_receiver = DatabaseReceiver()
+ post_delete_receiver = DatabaseReceiver()
+ # Make model and connect receivers
+ signals.pre_save.connect(sender=Person, receiver=pre_save_receiver)
+ signals.post_save.connect(sender=Person, receiver=post_save_receiver)
+ signals.pre_delete.connect(sender=Person, receiver=pre_delete_receiver)
+ signals.post_delete.connect(sender=Person, receiver=post_delete_receiver)
+ p = Person.objects.create(name='Darth Vader')
+ # Save and test receivers got calls
+ p.save()
+ self.assertEqual(pre_save_receiver._database, DEFAULT_DB_ALIAS)
+ self.assertEqual(post_save_receiver._database, DEFAULT_DB_ALIAS)
+ # Delete, and test
+ p.delete()
+ self.assertEqual(pre_delete_receiver._database, DEFAULT_DB_ALIAS)
+ self.assertEqual(post_delete_receiver._database, DEFAULT_DB_ALIAS)
+ # Save again to a different database
+ p.save(using="other")
+ self.assertEqual(pre_save_receiver._database, "other")
+ self.assertEqual(post_save_receiver._database, "other")
+ # Delete, and test
+ p.delete(using="other")
+ self.assertEqual(pre_delete_receiver._database, "other")
+ self.assertEqual(post_delete_receiver._database, "other")
+
+ def test_database_arg_m2m(self):
+ """
+ Test that the m2m_changed signal has a correct database arg (#13552)
+ """
+ # Make a receiver
+ receiver = DatabaseReceiver()
+ # Connect it, and make the models
+ signals.m2m_changed.connect(receiver=receiver)
+
+ b = Book.objects.create(title="Pro Django",
+ published=datetime.date(2008, 12, 16))
+
+ p = Person.objects.create(name="Marty Alchin")
+
+ # Test addition
+ b.authors.add(p)
+ self.assertEqual(receiver._database, DEFAULT_DB_ALIAS)
+ self._write_to_other()
+ b.authors.add(p)
+ self._write_to_default()
+ self.assertEqual(receiver._database, "other")
+
+ # Test removal
+ b.authors.remove(p)
+ self.assertEqual(receiver._database, DEFAULT_DB_ALIAS)
+ self._write_to_other()
+ b.authors.remove(p)
+ self._write_to_default()
+ self.assertEqual(receiver._database, "other")
+
+ # Test addition in reverse
+ p.book_set.add(b)
+ self.assertEqual(receiver._database, DEFAULT_DB_ALIAS)
+ self._write_to_other()
+ p.book_set.add(b)
+ self._write_to_default()
+ self.assertEqual(receiver._database, "other")
+
+ # Test clearing
+ b.authors.clear()
+ self.assertEqual(receiver._database, DEFAULT_DB_ALIAS)
+ self._write_to_other()
+ b.authors.clear()
+ self._write_to_default()
+ self.assertEqual(receiver._database, "other")
diff --git a/tests/regressiontests/templates/filters.py b/tests/regressiontests/templates/filters.py
index 3d6284e881..d351c550b7 100644
--- a/tests/regressiontests/templates/filters.py
+++ b/tests/regressiontests/templates/filters.py
@@ -328,7 +328,12 @@ def get_filter_tests():
'join03': (r'{{ a|join:" &amp; " }}', {'a': ['alpha', 'beta & me']}, 'alpha &amp; beta &amp; me'),
'join04': (r'{% autoescape off %}{{ a|join:" &amp; " }}{% endautoescape %}', {'a': ['alpha', 'beta & me']}, 'alpha &amp; beta & me'),
-
+ # Test that joining with unsafe joiners don't result in unsafe strings (#11377)
+ 'join05': (r'{{ a|join:var }}', {'a': ['alpha', 'beta & me'], 'var': ' & '}, 'alpha &amp; beta &amp; me'),
+ 'join06': (r'{{ a|join:var }}', {'a': ['alpha', 'beta & me'], 'var': mark_safe(' & ')}, 'alpha & beta &amp; me'),
+ 'join07': (r'{{ a|join:var|lower }}', {'a': ['Alpha', 'Beta & me'], 'var': ' & ' }, 'alpha &amp; beta &amp; me'),
+ 'join08': (r'{{ a|join:var|lower }}', {'a': ['Alpha', 'Beta & me'], 'var': mark_safe(' & ')}, 'alpha & beta &amp; me'),
+
'date01': (r'{{ d|date:"m" }}', {'d': datetime(2008, 1, 1)}, '01'),
'date02': (r'{{ d|date }}', {'d': datetime(2008, 1, 1)}, 'Jan. 1, 2008'),
#Ticket 9520: Make sure |date doesn't blow up on non-dates
diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py
index 5902e8d5e7..2f2df65e96 100644
--- a/tests/regressiontests/templates/tests.py
+++ b/tests/regressiontests/templates/tests.py
@@ -506,6 +506,17 @@ class Templates(unittest.TestCase):
'basic-syntax28': ("{{ a.b }}", {'a': SilentGetItemClass()}, ('', 'INVALID')),
'basic-syntax29': ("{{ a.b }}", {'a': SilentAttrClass()}, ('', 'INVALID')),
+ # Something that starts like a number but has an extra lookup works as a lookup.
+ 'basic-syntax30': ("{{ 1.2.3 }}", {"1": {"2": {"3": "d"}}}, "d"),
+ 'basic-syntax31': ("{{ 1.2.3 }}", {"1": {"2": ("a", "b", "c", "d")}}, "d"),
+ 'basic-syntax32': ("{{ 1.2.3 }}", {"1": (("x", "x", "x", "x"), ("y", "y", "y", "y"), ("a", "b", "c", "d"))}, "d"),
+ 'basic-syntax33': ("{{ 1.2.3 }}", {"1": ("xxxx", "yyyy", "abcd")}, "d"),
+ 'basic-syntax34': ("{{ 1.2.3 }}", {"1": ({"x": "x"}, {"y": "y"}, {"z": "z", "3": "d"})}, "d"),
+
+ # Numbers are numbers even if their digits are in the context.
+ 'basic-syntax35': ("{{ 1 }}", {"1": "abc"}, "1"),
+ 'basic-syntax36': ("{{ 1.2 }}", {"1": "abc"}, "1.2"),
+
# List-index syntax allows a template to access a certain item of a subscriptable object.
'list-index01': ("{{ var.1 }}", {"var": ["first item", "second item"]}, "second item"),
@@ -592,7 +603,7 @@ class Templates(unittest.TestCase):
#filters should accept empty string constants
'filter-syntax20': ('{{ ""|default_if_none:"was none" }}', {}, ""),
-
+
### COMMENT SYNTAX ########################################################
'comment-syntax01': ("{# this is hidden #}hello", {}, "hello"),
'comment-syntax02': ("{# this is hidden #}hello{# foo #}", {}, "hello"),
@@ -1285,7 +1296,8 @@ class Templates(unittest.TestCase):
# Regression test for #11270.
'cache17': ('{% load cache %}{% cache 10 long_cache_key poem %}Some Content{% endcache %}', {'poem': 'Oh freddled gruntbuggly/Thy micturations are to me/As plurdled gabbleblotchits/On a lurgid bee/That mordiously hath bitled out/Its earted jurtles/Into a rancid festering/Or else I shall rend thee in the gobberwarts with my blurglecruncheon/See if I dont.'}, 'Some Content'),
-
+
+
### AUTOESCAPE TAG ##############################################
'autoescape-tag01': ("{% autoescape off %}hello{% endautoescape %}", {}, "hello"),
'autoescape-tag02': ("{% autoescape off %}{{ first }}{% endautoescape %}", {"first": "<b>hello</b>"}, "<b>hello</b>"),
@@ -1314,6 +1326,23 @@ class Templates(unittest.TestCase):
# implementation details (fortunately, the (no)autoescape block
# tags can be used in those cases)
'autoescape-filtertag01': ("{{ first }}{% filter safe %}{{ first }} x<y{% endfilter %}", {"first": "<a>"}, template.TemplateSyntaxError),
+
+ # ifqeual compares unescaped vales.
+ 'autoescape-ifequal01': ('{% ifequal var "this & that" %}yes{% endifequal %}', { "var": "this & that" }, "yes" ),
+
+ # Arguments to filters are 'safe' and manipulate their input unescaped.
+ 'autoescape-filters01': ('{{ var|cut:"&" }}', { "var": "this & that" }, "this that" ),
+ 'autoescape-filters02': ('{{ var|join:" & \" }}', { "var": ("Tom", "Dick", "Harry") }, "Tom & Dick & Harry" ),
+
+ # Literal strings are safe.
+ 'autoescape-literals01': ('{{ "this & that" }}',{}, "this & that" ),
+
+ # Iterating over strings outputs safe characters.
+ 'autoescape-stringiterations01': ('{% for l in var %}{{ l }},{% endfor %}', {'var': 'K&R'}, "K,&amp;,R," ),
+
+ # Escape requirement survives lookup.
+ 'autoescape-lookup01': ('{{ var.key }}', { "var": {"key": "this & that" }}, "this &amp; that" ),
+
}
diff --git a/tests/regressiontests/test_client_regress/models.py b/tests/regressiontests/test_client_regress/models.py
index 6da7ae4445..cb8e1b3103 100644
--- a/tests/regressiontests/test_client_regress/models.py
+++ b/tests/regressiontests/test_client_regress/models.py
@@ -11,6 +11,7 @@ from django.core.urlresolvers import reverse
from django.core.exceptions import SuspiciousOperation
from django.template import TemplateDoesNotExist, TemplateSyntaxError, Context
from django.template import loader
+from django.test.client import encode_file
class AssertContainsTests(TestCase):
def setUp(self):
@@ -34,20 +35,20 @@ class AssertContainsTests(TestCase):
try:
self.assertContains(response, 'text', status_code=999)
except AssertionError, e:
- self.assertEquals(str(e), "Couldn't retrieve page: Response code was 200 (expected 999)")
+ self.assertEquals(str(e), "Couldn't retrieve content: Response code was 200 (expected 999)")
try:
self.assertContains(response, 'text', status_code=999, msg_prefix='abc')
except AssertionError, e:
- self.assertEquals(str(e), "abc: Couldn't retrieve page: Response code was 200 (expected 999)")
+ self.assertEquals(str(e), "abc: Couldn't retrieve content: Response code was 200 (expected 999)")
try:
self.assertNotContains(response, 'text', status_code=999)
except AssertionError, e:
- self.assertEquals(str(e), "Couldn't retrieve page: Response code was 200 (expected 999)")
+ self.assertEquals(str(e), "Couldn't retrieve content: Response code was 200 (expected 999)")
try:
self.assertNotContains(response, 'text', status_code=999, msg_prefix='abc')
except AssertionError, e:
- self.assertEquals(str(e), "abc: Couldn't retrieve page: Response code was 200 (expected 999)")
+ self.assertEquals(str(e), "abc: Couldn't retrieve content: Response code was 200 (expected 999)")
try:
self.assertNotContains(response, 'once')
@@ -619,6 +620,7 @@ class ContextTests(TestCase):
"Context variables can be retrieved from a single context"
response = self.client.get("/test_client_regress/request_data/", data={'foo':'whiz'})
self.assertEqual(response.context.__class__, Context)
+ self.assertTrue('get-foo' in response.context)
self.assertEqual(response.context['get-foo'], 'whiz')
self.assertEqual(response.context['request-foo'], 'whiz')
self.assertEqual(response.context['data'], 'sausage')
@@ -634,6 +636,7 @@ class ContextTests(TestCase):
response = self.client.get("/test_client_regress/request_data_extended/", data={'foo':'whiz'})
self.assertEqual(response.context.__class__, ContextList)
self.assertEqual(len(response.context), 2)
+ self.assertTrue('get-foo' in response.context)
self.assertEqual(response.context['get-foo'], 'whiz')
self.assertEqual(response.context['request-foo'], 'whiz')
self.assertEqual(response.context['data'], 'bacon')
@@ -821,3 +824,26 @@ class UnicodePayloadTests(TestCase):
response = self.client.post("/test_client_regress/parse_unicode_json/", json,
content_type="application/json; charset=koi8-r")
self.assertEqual(response.content, json.encode('koi8-r'))
+
+class DummyFile(object):
+ def __init__(self, filename):
+ self.name = filename
+ def read(self):
+ return 'TEST_FILE_CONTENT'
+
+class UploadedFileEncodingTest(TestCase):
+ def test_file_encoding(self):
+ encoded_file = encode_file('TEST_BOUNDARY', 'TEST_KEY', DummyFile('test_name.bin'))
+ self.assertEqual('--TEST_BOUNDARY', encoded_file[0])
+ self.assertEqual('Content-Disposition: form-data; name="TEST_KEY"; filename="test_name.bin"', encoded_file[1])
+ self.assertEqual('TEST_FILE_CONTENT', encoded_file[-1])
+
+ def test_guesses_content_type_on_file_encoding(self):
+ self.assertEqual('Content-Type: application/octet-stream',
+ encode_file('IGNORE', 'IGNORE', DummyFile("file.bin"))[2])
+ self.assertEqual('Content-Type: text/plain',
+ encode_file('IGNORE', 'IGNORE', DummyFile("file.txt"))[2])
+ self.assertEqual('Content-Type: application/zip',
+ encode_file('IGNORE', 'IGNORE', DummyFile("file.zip"))[2])
+ self.assertEqual('Content-Type: application/octet-stream',
+ encode_file('IGNORE', 'IGNORE', DummyFile("file.unknown"))[2])
diff --git a/tests/regressiontests/urlpatterns_reverse/included_namespace_urls.py b/tests/regressiontests/urlpatterns_reverse/included_namespace_urls.py
index 073190657c..16887e2a9b 100644
--- a/tests/regressiontests/urlpatterns_reverse/included_namespace_urls.py
+++ b/tests/regressiontests/urlpatterns_reverse/included_namespace_urls.py
@@ -1,5 +1,6 @@
from django.conf.urls.defaults import *
from namespace_urls import URLObject
+from views import view_class_instance
testobj3 = URLObject('testapp', 'test-ns3')
@@ -7,7 +8,13 @@ urlpatterns = patterns('regressiontests.urlpatterns_reverse.views',
url(r'^normal/$', 'empty_view', name='inc-normal-view'),
url(r'^normal/(?P<arg1>\d+)/(?P<arg2>\d+)/$', 'empty_view', name='inc-normal-view'),
+ url(r'^mixed_args/(\d+)/(?P<arg2>\d+)/$', 'empty_view', name='inc-mixed-args'),
+ url(r'^no_kwargs/(\d+)/(\d+)/$', 'empty_view', name='inc-no-kwargs'),
+
+ url(r'^view_class/(?P<arg1>\d+)/(?P<arg2>\d+)/$', view_class_instance, name='inc-view-class'),
+
(r'^test3/', include(testobj3.urls)),
(r'^ns-included3/', include('regressiontests.urlpatterns_reverse.included_urls', namespace='inc-ns3')),
+ (r'^ns-included4/', include('regressiontests.urlpatterns_reverse.namespace_urls', namespace='inc-ns4')),
)
diff --git a/tests/regressiontests/urlpatterns_reverse/namespace_urls.py b/tests/regressiontests/urlpatterns_reverse/namespace_urls.py
index 27cc7f7a22..3d34049932 100644
--- a/tests/regressiontests/urlpatterns_reverse/namespace_urls.py
+++ b/tests/regressiontests/urlpatterns_reverse/namespace_urls.py
@@ -1,4 +1,5 @@
from django.conf.urls.defaults import *
+from views import view_class_instance
class URLObject(object):
def __init__(self, app_name, namespace):
@@ -23,6 +24,14 @@ urlpatterns = patterns('regressiontests.urlpatterns_reverse.views',
url(r'^normal/$', 'empty_view', name='normal-view'),
url(r'^normal/(?P<arg1>\d+)/(?P<arg2>\d+)/$', 'empty_view', name='normal-view'),
+ url(r'^mixed_args/(\d+)/(?P<arg2>\d+)/$', 'empty_view', name='mixed-args'),
+ url(r'^no_kwargs/(\d+)/(\d+)/$', 'empty_view', name='no-kwargs'),
+
+ url(r'^view_class/(?P<arg1>\d+)/(?P<arg2>\d+)/$', view_class_instance, name='view-class'),
+
+ (r'^unnamed/normal/(?P<arg1>\d+)/(?P<arg2>\d+)/$', 'empty_view'),
+ (r'^unnamed/view_class/(?P<arg1>\d+)/(?P<arg2>\d+)/$', view_class_instance),
+
(r'^test1/', include(testobj1.urls)),
(r'^test2/', include(testobj2.urls)),
(r'^default/', include(default_testobj.urls)),
diff --git a/tests/regressiontests/urlpatterns_reverse/tests.py b/tests/regressiontests/urlpatterns_reverse/tests.py
index 3fcc935da2..dcd942649c 100644
--- a/tests/regressiontests/urlpatterns_reverse/tests.py
+++ b/tests/regressiontests/urlpatterns_reverse/tests.py
@@ -18,7 +18,7 @@ import unittest
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
-from django.core.urlresolvers import reverse, resolve, NoReverseMatch, Resolver404
+from django.core.urlresolvers import reverse, resolve, NoReverseMatch, Resolver404, ResolverMatch
from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect
from django.shortcuts import redirect
from django.test import TestCase
@@ -26,6 +26,41 @@ from django.test import TestCase
import urlconf_outer
import urlconf_inner
import middleware
+import views
+
+resolve_test_data = (
+ # These entries are in the format: (path, url_name, app_name, namespace, view_func, args, kwargs)
+ # Simple case
+ ('/normal/42/37/', 'normal-view', None, '', views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'}),
+ ('/view_class/42/37/', 'view-class', None, '', views.view_class_instance, tuple(), {'arg1': '42', 'arg2': '37'}),
+ ('/included/normal/42/37/', 'inc-normal-view', None, '', views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'}),
+ ('/included/view_class/42/37/', 'inc-view-class', None, '', views.view_class_instance, tuple(), {'arg1': '42', 'arg2': '37'}),
+
+ # Unnamed args are dropped if you have *any* kwargs in a pattern
+ ('/mixed_args/42/37/', 'mixed-args', None, '', views.empty_view, tuple(), {'arg2': '37'}),
+ ('/included/mixed_args/42/37/', 'inc-mixed-args', None, '', views.empty_view, tuple(), {'arg2': '37'}),
+
+ # Unnamed views will be resolved to the function/class name
+ ('/unnamed/normal/42/37/', 'regressiontests.urlpatterns_reverse.views.empty_view', None, '', views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'}),
+ ('/unnamed/view_class/42/37/', 'regressiontests.urlpatterns_reverse.views.ViewClass', None, '', views.view_class_instance, tuple(), {'arg1': '42', 'arg2': '37'}),
+
+ # If you have no kwargs, you get an args list.
+ ('/no_kwargs/42/37/', 'no-kwargs', None, '', views.empty_view, ('42','37'), {}),
+ ('/included/no_kwargs/42/37/', 'inc-no-kwargs', None, '', views.empty_view, ('42','37'), {}),
+
+ # Namespaces
+ ('/test1/inner/42/37/', 'urlobject-view', 'testapp', 'test-ns1', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}),
+ ('/included/test3/inner/42/37/', 'urlobject-view', 'testapp', 'test-ns3', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}),
+ ('/ns-included1/normal/42/37/', 'inc-normal-view', None, 'inc-ns1', views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'}),
+ ('/included/test3/inner/42/37/', 'urlobject-view', 'testapp', 'test-ns3', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}),
+ ('/default/inner/42/37/', 'urlobject-view', 'testapp', 'testapp', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}),
+ ('/other2/inner/42/37/', 'urlobject-view', 'nodefault', 'other-ns2', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}),
+ ('/other1/inner/42/37/', 'urlobject-view', 'nodefault', 'other-ns1', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}),
+
+ # Nested namespaces
+ ('/ns-included1/test3/inner/42/37/', 'urlobject-view', 'testapp', 'inc-ns1:test-ns3', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}),
+ ('/ns-included1/ns-included4/ns-included2/test3/inner/42/37/', 'urlobject-view', 'testapp', 'inc-ns1:inc-ns4:inc-ns2:test-ns3', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}),
+)
test_data = (
('places', '/places/3/', [3], {}),
@@ -119,6 +154,10 @@ class URLPatternReverse(TestCase):
else:
self.assertEquals(got, expected)
+ def test_reverse_none(self):
+ # Reversing None should raise an error, not return the last un-named view.
+ self.assertRaises(NoReverseMatch, reverse, None)
+
class ResolverTests(unittest.TestCase):
def test_non_regex(self):
"""
@@ -229,6 +268,12 @@ class NamespaceTests(TestCase):
self.assertEquals('/ns-included1/test3/inner/37/42/', reverse('inc-ns1:test-ns3:urlobject-view', args=[37,42]))
self.assertEquals('/ns-included1/test3/inner/42/37/', reverse('inc-ns1:test-ns3:urlobject-view', kwargs={'arg1':42, 'arg2':37}))
+ def test_nested_namespace_pattern(self):
+ "Namespaces can be nested"
+ self.assertEquals('/ns-included1/ns-included4/ns-included1/test3/inner/', reverse('inc-ns1:inc-ns4:inc-ns1:test-ns3:urlobject-view'))
+ self.assertEquals('/ns-included1/ns-included4/ns-included1/test3/inner/37/42/', reverse('inc-ns1:inc-ns4:inc-ns1:test-ns3:urlobject-view', args=[37,42]))
+ self.assertEquals('/ns-included1/ns-included4/ns-included1/test3/inner/42/37/', reverse('inc-ns1:inc-ns4:inc-ns1:test-ns3:urlobject-view', kwargs={'arg1':42, 'arg2':37}))
+
def test_app_lookup_object(self):
"A default application namespace can be used for lookup"
self.assertEquals('/default/inner/', reverse('testapp:urlobject-view'))
@@ -317,3 +362,29 @@ class NoRootUrlConfTests(TestCase):
def test_no_handler_exception(self):
self.assertRaises(ImproperlyConfigured, self.client.get, '/test/me/')
+
+class ResolverMatchTests(TestCase):
+ urls = 'regressiontests.urlpatterns_reverse.namespace_urls'
+
+ def test_urlpattern_resolve(self):
+ for path, name, app_name, namespace, func, args, kwargs in resolve_test_data:
+ # Test legacy support for extracting "function, args, kwargs"
+ match_func, match_args, match_kwargs = resolve(path)
+ self.assertEqual(match_func, func)
+ self.assertEqual(match_args, args)
+ self.assertEqual(match_kwargs, kwargs)
+
+ # Test ResolverMatch capabilities.
+ match = resolve(path)
+ self.assertEqual(match.__class__, ResolverMatch)
+ self.assertEqual(match.url_name, name)
+ self.assertEqual(match.args, args)
+ self.assertEqual(match.kwargs, kwargs)
+ self.assertEqual(match.app_name, app_name)
+ self.assertEqual(match.namespace, namespace)
+ self.assertEqual(match.func, func)
+
+ # ... and for legacy purposes:
+ self.assertEquals(match[0], func)
+ self.assertEquals(match[1], args)
+ self.assertEquals(match[2], kwargs)
diff --git a/tests/regressiontests/urlpatterns_reverse/views.py b/tests/regressiontests/urlpatterns_reverse/views.py
index 99c00bde70..27dca3d0e2 100644
--- a/tests/regressiontests/urlpatterns_reverse/views.py
+++ b/tests/regressiontests/urlpatterns_reverse/views.py
@@ -6,3 +6,9 @@ def kwargs_view(request, arg1=1, arg2=2):
def absolute_kwargs_view(request, arg1=1, arg2=2):
pass
+
+class ViewClass(object):
+ def __call__(self, request, *args, **kwargs):
+ pass
+
+view_class_instance = ViewClass()
diff --git a/tests/runtests.py b/tests/runtests.py
index 5585f75d5a..4d7c55bae0 100755
--- a/tests/runtests.py
+++ b/tests/runtests.py
@@ -128,7 +128,7 @@ def django_tests(verbosity, interactive, failfast, test_labels):
# no models were named (i.e., run all), import
# this model and add it to the list to test.
if not test_labels or model_name in set([label.split('.')[0] for label in test_labels]):
- if verbosity >= 1:
+ if verbosity >= 2:
print "Importing model %s" % model_name
mod = load_app(model_label)
if mod:
@@ -187,8 +187,8 @@ if __name__ == "__main__":
from optparse import OptionParser
usage = "%prog [options] [model model model ...]"
parser = OptionParser(usage=usage)
- parser.add_option('-v','--verbosity', action='store', dest='verbosity', default='0',
- type='choice', choices=['0', '1', '2'],
+ parser.add_option('-v','--verbosity', action='store', dest='verbosity', default='1',
+ type='choice', choices=['0', '1', '2', '3'],
help='Verbosity level; 0=minimal output, 1=normal output, 2=all output')
parser.add_option('--noinput', action='store_false', dest='interactive', default=True,
help='Tells Django to NOT prompt the user for input of any kind.')