summaryrefslogtreecommitdiff
path: root/tests/regressiontests
diff options
context:
space:
mode:
authorJustin Bronn <jbronn@gmail.com>2007-08-26 01:10:53 +0000
committerJustin Bronn <jbronn@gmail.com>2007-08-26 01:10:53 +0000
commit2052b508eb92c62fc0678efd4936c5ec1e0e735b (patch)
treee510109b74b28c8ccef5f6955727cb9dce3da655 /tests/regressiontests
parenta7297a255f4bb86f608ea251e00253d18c31d9d4 (diff)
gis: Made necessary modifications for unicode, manage refactor, backend refactor and merged 5584-6000 via svnmerge from [repos:django/trunk trunk].
git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@6018 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'tests/regressiontests')
-rw-r--r--tests/regressiontests/bug639/models.py2
-rw-r--r--tests/regressiontests/cache/tests.py41
-rw-r--r--tests/regressiontests/datastructures/tests.py9
-rw-r--r--tests/regressiontests/datatypes/models.py2
-rw-r--r--tests/regressiontests/dateformat/tests.py46
-rw-r--r--tests/regressiontests/defaultfilters/tests.py446
-rw-r--r--tests/regressiontests/fixtures_regress/fixtures/pretty.xml11
-rw-r--r--tests/regressiontests/fixtures_regress/models.py31
-rw-r--r--tests/regressiontests/forms/localflavor.py118
-rw-r--r--tests/regressiontests/forms/regressions.py49
-rw-r--r--tests/regressiontests/forms/tests.py154
-rw-r--r--tests/regressiontests/httpwrappers/tests.py76
-rw-r--r--tests/regressiontests/humanize/tests.py32
-rw-r--r--tests/regressiontests/i18n/__init__.py0
-rw-r--r--tests/regressiontests/i18n/models.py0
-rw-r--r--tests/regressiontests/i18n/tests.py33
-rw-r--r--tests/regressiontests/initial_sql_regress/models.py2
-rw-r--r--tests/regressiontests/invalid_admin_options/models.py74
-rw-r--r--tests/regressiontests/many_to_one_regress/models.py6
-rw-r--r--tests/regressiontests/maxlength/__init__.py0
-rw-r--r--tests/regressiontests/maxlength/models.py0
-rw-r--r--tests/regressiontests/maxlength/tests.py160
-rw-r--r--tests/regressiontests/model_fields/__init__.py0
-rw-r--r--tests/regressiontests/model_fields/models.py0
-rw-r--r--tests/regressiontests/model_fields/tests.py18
-rw-r--r--tests/regressiontests/model_regress/__init__.py0
-rw-r--r--tests/regressiontests/model_regress/models.py40
-rw-r--r--tests/regressiontests/null_queries/models.py12
-rw-r--r--tests/regressiontests/one_to_one_regress/models.py18
-rw-r--r--tests/regressiontests/serializers_regress/models.py37
-rw-r--r--tests/regressiontests/serializers_regress/tests.py39
-rw-r--r--tests/regressiontests/string_lookup/models.py62
-rw-r--r--tests/regressiontests/templates/tests.py72
-rw-r--r--tests/regressiontests/templates/unicode.py33
-rw-r--r--tests/regressiontests/templates/urls.py4
-rw-r--r--tests/regressiontests/test_client_regress/models.py59
-rw-r--r--tests/regressiontests/test_client_regress/urls.py2
-rw-r--r--tests/regressiontests/test_client_regress/views.py16
-rw-r--r--tests/regressiontests/text/tests.py31
-rw-r--r--tests/regressiontests/utils/__init__.py0
-rw-r--r--tests/regressiontests/utils/models.py1
-rw-r--r--tests/regressiontests/utils/tests.py115
42 files changed, 1384 insertions, 467 deletions
diff --git a/tests/regressiontests/bug639/models.py b/tests/regressiontests/bug639/models.py
index 7cfdfc82ef..fc241aba8c 100644
--- a/tests/regressiontests/bug639/models.py
+++ b/tests/regressiontests/bug639/models.py
@@ -2,7 +2,7 @@ import tempfile
from django.db import models
class Photo(models.Model):
- title = models.CharField(maxlength=30)
+ title = models.CharField(max_length=30)
image = models.FileField(upload_to=tempfile.gettempdir())
# Support code for the tests; this keeps track of how many times save() gets
diff --git a/tests/regressiontests/cache/tests.py b/tests/regressiontests/cache/tests.py
index 9dc7161c03..752083bd2f 100644
--- a/tests/regressiontests/cache/tests.py
+++ b/tests/regressiontests/cache/tests.py
@@ -1,10 +1,12 @@
+# -*- coding: utf-8 -*-
+
# Unit tests for cache framework
# Uses whatever cache backend is set in the test settings file.
from django.core.cache import cache
import time, unittest
-# functions/classes for complex data type tests
+# functions/classes for complex data type tests
def f():
return 42
class C:
@@ -19,8 +21,8 @@ class Cache(unittest.TestCase):
def test_non_existent(self):
# get with non-existent keys
- self.assertEqual(cache.get("does not exist"), None)
- self.assertEqual(cache.get("does not exist", "bang!"), "bang!")
+ self.assertEqual(cache.get("does_not_exist"), None)
+ self.assertEqual(cache.get("does_not_exist", "bang!"), "bang!")
def test_get_many(self):
# get_many
@@ -42,17 +44,16 @@ class Cache(unittest.TestCase):
def test_has_key(self):
# has_key
- cache.set("hello", "goodbye")
- self.assertEqual(cache.has_key("hello"), True)
- self.assertEqual(cache.has_key("goodbye"), False)
+ cache.set("hello1", "goodbye1")
+ self.assertEqual(cache.has_key("hello1"), True)
+ self.assertEqual(cache.has_key("goodbye1"), False)
- def test_in(self):
- cache.set("hello", "goodbye")
- self.assertEqual("hello" in cache, True)
- self.assertEqual("goodbye" in cache, False)
+ def test_in(self):
+ cache.set("hello2", "goodbye2")
+ self.assertEqual("hello2" in cache, True)
+ self.assertEqual("goodbye2" in cache, False)
def test_data_types(self):
- # test data types
stuff = {
'string' : 'this is a string',
'int' : 42,
@@ -62,15 +63,23 @@ class Cache(unittest.TestCase):
'function' : f,
'class' : C,
}
- for (key, value) in stuff.items():
- cache.set(key, value)
- self.assertEqual(cache.get(key), value)
-
+
def test_expiration(self):
# expiration
cache.set('expire', 'very quickly', 1)
time.sleep(2)
self.assertEqual(cache.get("expire"), None)
+ def test_unicode(self):
+ stuff = {
+ u'ascii': u'ascii_value',
+ u'unicode_ascii': u'Iñtërnâtiônàlizætiøn1',
+ u'Iñtërnâtiônàlizætiøn': u'Iñtërnâtiônàlizætiøn2',
+ u'ascii': {u'x' : 1 }
+ }
+ for (key, value) in stuff.items():
+ cache.set(key, value)
+ self.assertEqual(cache.get(key), value)
+
if __name__ == '__main__':
- unittest.main() \ No newline at end of file
+ unittest.main()
diff --git a/tests/regressiontests/datastructures/tests.py b/tests/regressiontests/datastructures/tests.py
index 18eb4fcccd..3920e1ca40 100644
--- a/tests/regressiontests/datastructures/tests.py
+++ b/tests/regressiontests/datastructures/tests.py
@@ -64,4 +64,13 @@ True
['Holovaty']
>>> d['person']['2']['firstname']
['Adrian']
+
+### FileDict ################################################################
+
+>>> d = FileDict({'content': 'once upon a time...'})
+>>> repr(d)
+"{'content': '<omitted>'}"
+>>> d = FileDict({'other-key': 'once upon a time...'})
+>>> repr(d)
+"{'other-key': 'once upon a time...'}"
"""
diff --git a/tests/regressiontests/datatypes/models.py b/tests/regressiontests/datatypes/models.py
index 8c5c8c285d..ff9666bc0c 100644
--- a/tests/regressiontests/datatypes/models.py
+++ b/tests/regressiontests/datatypes/models.py
@@ -7,7 +7,7 @@ from django.db import models
from django.conf import settings
class Donut(models.Model):
- name = models.CharField(maxlength=100)
+ name = models.CharField(max_length=100)
is_frosted = models.BooleanField(default=False)
has_sprinkles = models.NullBooleanField()
baked_date = models.DateField(null=True)
diff --git a/tests/regressiontests/dateformat/tests.py b/tests/regressiontests/dateformat/tests.py
index f9f84145c5..30c9a4e6dd 100644
--- a/tests/regressiontests/dateformat/tests.py
+++ b/tests/regressiontests/dateformat/tests.py
@@ -1,54 +1,54 @@
r"""
>>> format(my_birthday, '')
-''
+u''
>>> format(my_birthday, 'a')
-'p.m.'
+u'p.m.'
>>> format(my_birthday, 'A')
-'PM'
+u'PM'
>>> format(my_birthday, 'd')
-'08'
+u'08'
>>> format(my_birthday, 'j')
-'8'
+u'8'
>>> format(my_birthday, 'l')
-'Sunday'
+u'Sunday'
>>> format(my_birthday, 'L')
-'False'
+u'False'
>>> format(my_birthday, 'm')
-'07'
+u'07'
>>> format(my_birthday, 'M')
-'Jul'
+u'Jul'
>>> format(my_birthday, 'b')
-'jul'
+u'jul'
>>> format(my_birthday, 'n')
-'7'
+u'7'
>>> format(my_birthday, 'N')
-'July'
+u'July'
>>> no_tz or format(my_birthday, 'O') == '+0100'
True
>>> format(my_birthday, 'P')
-'10 p.m.'
+u'10 p.m.'
>>> no_tz or format(my_birthday, 'r') == 'Sun, 8 Jul 1979 22:00:00 +0100'
True
>>> format(my_birthday, 's')
-'00'
+u'00'
>>> format(my_birthday, 'S')
-'th'
+u'th'
>>> format(my_birthday, 't')
-'31'
+u'31'
>>> no_tz or format(my_birthday, 'T') == 'CET'
True
>>> no_tz or format(my_birthday, 'U') == '300531600'
True
>>> format(my_birthday, 'w')
-'0'
+u'0'
>>> format(my_birthday, 'W')
-'27'
+u'27'
>>> format(my_birthday, 'y')
-'79'
+u'79'
>>> format(my_birthday, 'Y')
-'1979'
+u'1979'
>>> format(my_birthday, 'z')
-'189'
+u'189'
>>> no_tz or format(my_birthday, 'Z') == '3600'
True
@@ -62,10 +62,10 @@ True
True
>>> format(my_birthday, r'Y z \C\E\T')
-'1979 189 CET'
+u'1979 189 CET'
>>> format(my_birthday, r'jS o\f F')
-'8th of July'
+u'8th of July'
"""
from django.utils import dateformat, translation
diff --git a/tests/regressiontests/defaultfilters/tests.py b/tests/regressiontests/defaultfilters/tests.py
index b35636aba6..a1efae66f6 100644
--- a/tests/regressiontests/defaultfilters/tests.py
+++ b/tests/regressiontests/defaultfilters/tests.py
@@ -2,197 +2,211 @@
r"""
>>> floatformat(7.7)
-'7.7'
+u'7.7'
>>> floatformat(7.0)
-'7'
+u'7'
>>> floatformat(0.7)
-'0.7'
+u'0.7'
>>> floatformat(0.07)
-'0.1'
+u'0.1'
>>> floatformat(0.007)
-'0.0'
+u'0.0'
>>> floatformat(0.0)
-'0'
+u'0'
>>> floatformat(7.7,3)
-'7.700'
+u'7.700'
>>> floatformat(6.000000,3)
-'6.000'
+u'6.000'
>>> floatformat(13.1031,-3)
-'13.103'
+u'13.103'
>>> floatformat(11.1197, -2)
-'11.12'
+u'11.12'
>>> floatformat(11.0000, -2)
-'11'
+u'11'
>>> floatformat(11.000001, -2)
-'11.00'
+u'11.00'
>>> floatformat(8.2798, 3)
-'8.280'
->>> floatformat('foo')
-''
->>> floatformat(13.1031, 'bar')
-'13.1031'
->>> floatformat('foo', 'bar')
-''
+u'8.280'
+>>> floatformat(u'foo')
+u''
+>>> floatformat(13.1031, u'bar')
+u'13.1031'
+>>> floatformat(u'foo', u'bar')
+u''
->>> addslashes('"double quotes" and \'single quotes\'')
-'\\"double quotes\\" and \\\'single quotes\\\''
+>>> addslashes(u'"double quotes" and \'single quotes\'')
+u'\\"double quotes\\" and \\\'single quotes\\\''
->>> addslashes(r'\ : backslashes, too')
-'\\\\ : backslashes, too'
+>>> addslashes(ur'\ : backslashes, too')
+u'\\\\ : backslashes, too'
->>> capfirst('hello world')
-'Hello world'
+>>> capfirst(u'hello world')
+u'Hello world'
->>> fix_ampersands('Jack & Jill & Jeroboam')
-'Jack &amp; Jill &amp; Jeroboam'
+>>> fix_ampersands(u'Jack & Jill & Jeroboam')
+u'Jack &amp; Jill &amp; Jeroboam'
->>> linenumbers('line 1\nline 2')
-'1. line 1\n2. line 2'
+>>> linenumbers(u'line 1\nline 2')
+u'1. line 1\n2. line 2'
->>> linenumbers('\n'.join(['x'] * 10))
-'01. x\n02. x\n03. x\n04. x\n05. x\n06. x\n07. x\n08. x\n09. x\n10. x'
+>>> linenumbers(u'\n'.join([u'x'] * 10))
+u'01. x\n02. x\n03. x\n04. x\n05. x\n06. x\n07. x\n08. x\n09. x\n10. x'
>>> lower('TEST')
-'test'
+u'test'
>>> lower(u'\xcb') # uppercase E umlaut
u'\xeb'
>>> make_list('abc')
-['a', 'b', 'c']
+[u'a', u'b', u'c']
>>> make_list(1234)
-['1', '2', '3', '4']
+[u'1', u'2', u'3', u'4']
>>> slugify(' Jack & Jill like numbers 1,2,3 and 4 and silly characters ?%.$!/')
-'jack-jill-like-numbers-123-and-4-and-silly-characters'
+u'jack-jill-like-numbers-123-and-4-and-silly-characters'
->>> stringformat(1, '03d')
-'001'
+>>> slugify(u"Un \xe9l\xe9phant \xe0 l'or\xe9e du bois")
+u'un-elephant-a-loree-du-bois'
->>> stringformat(1, 'z')
-''
+>>> stringformat(1, u'03d')
+u'001'
+
+>>> stringformat(1, u'z')
+u''
>>> title('a nice title, isn\'t it?')
-"A Nice Title, Isn't It?"
+u"A Nice Title, Isn't It?"
+
+>>> title(u'discoth\xe8que')
+u'Discoth\xe8que'
+>>> truncatewords(u'A sentence with a few words in it', 1)
+u'A ...'
->>> truncatewords('A sentence with a few words in it', 1)
-'A ...'
+>>> truncatewords(u'A sentence with a few words in it', 5)
+u'A sentence with a few ...'
->>> truncatewords('A sentence with a few words in it', 5)
-'A sentence with a few ...'
+>>> truncatewords(u'A sentence with a few words in it', 100)
+u'A sentence with a few words in it'
->>> truncatewords('A sentence with a few words in it', 100)
-'A sentence with a few words in it'
+>>> truncatewords(u'A sentence with a few words in it', 'not a number')
+u'A sentence with a few words in it'
->>> truncatewords('A sentence with a few words in it', 'not a number')
-'A sentence with a few words in it'
+>>> truncatewords_html(u'<p>one <a href="#">two - three <br>four</a> five</p>', 0)
+u''
->>> truncatewords_html('<p>one <a href="#">two - three <br>four</a> five</p>', 0)
-''
+>>> truncatewords_html(u'<p>one <a href="#">two - three <br>four</a> five</p>', 2)
+u'<p>one <a href="#">two ...</a></p>'
->>> truncatewords_html('<p>one <a href="#">two - three <br>four</a> five</p>', 2)
-'<p>one <a href="#">two ...</a></p>'
+>>> truncatewords_html(u'<p>one <a href="#">two - three <br>four</a> five</p>', 4)
+u'<p>one <a href="#">two - three <br>four ...</a></p>'
->>> truncatewords_html('<p>one <a href="#">two - three <br>four</a> five</p>', 4)
-'<p>one <a href="#">two - three <br>four ...</a></p>'
+>>> truncatewords_html(u'<p>one <a href="#">two - three <br>four</a> five</p>', 5)
+u'<p>one <a href="#">two - three <br>four</a> five</p>'
->>> truncatewords_html('<p>one <a href="#">two - three <br>four</a> five</p>', 5)
-'<p>one <a href="#">two - three <br>four</a> five</p>'
+>>> truncatewords_html(u'<p>one <a href="#">two - three <br>four</a> five</p>', 100)
+u'<p>one <a href="#">two - three <br>four</a> five</p>'
->>> truncatewords_html('<p>one <a href="#">two - three <br>four</a> five</p>', 100)
-'<p>one <a href="#">two - three <br>four</a> five</p>'
+>>> truncatewords_html(u'\xc5ngstr\xf6m was here', 1)
+u'\xc5ngstr\xf6m ...'
->>> upper('Mixed case input')
-'MIXED CASE INPUT'
+>>> upper(u'Mixed case input')
+u'MIXED CASE INPUT'
>>> upper(u'\xeb') # lowercase e umlaut
u'\xcb'
->>> urlencode('jack & jill')
-'jack%20%26%20jill'
+>>> urlencode(u'fran\xe7ois & jill')
+u'fran%C3%A7ois%20%26%20jill'
>>> urlencode(1)
-'1'
+u'1'
+>>> iriencode(u'S\xf8r-Tr\xf8ndelag')
+u'S%C3%B8r-Tr%C3%B8ndelag'
+>>> iriencode(urlencode(u'fran\xe7ois & jill'))
+u'fran%C3%A7ois%20%26%20jill'
+>>> urlizetrunc(u'http://short.com/', 20)
+u'<a href="http://short.com/" rel="nofollow">http://short.com/</a>'
->>> urlizetrunc('http://short.com/', 20)
-'<a href="http://short.com/" rel="nofollow">http://short.com/</a>'
+>>> urlizetrunc(u'http://www.google.co.uk/search?hl=en&q=some+long+url&btnG=Search&meta=', 20)
+u'<a href="http://www.google.co.uk/search?hl=en&q=some+long+url&btnG=Search&meta=" rel="nofollow">http://www.google....</a>'
>>> urlizetrunc('http://www.google.co.uk/search?hl=en&q=some+long+url&btnG=Search&meta=', 20)
-'<a href="http://www.google.co.uk/search?hl=en&q=some+long+url&btnG=Search&meta=" rel="nofollow">http://www.google...</a>'
+u'<a href="http://www.google.co.uk/search?hl=en&q=some+long+url&btnG=Search&meta=" rel="nofollow">http://www.google...</a>'
# Check truncating of URIs which are the exact length
>>> uri = 'http://31characteruri.com/test/'
>>> len(uri)
31
>>> urlizetrunc(uri, 31)
-'<a href="http://31characteruri.com/test/" rel="nofollow">http://31characteruri.com/test/</a>'
+u'<a href="http://31characteruri.com/test/" rel="nofollow">http://31characteruri.com/test/</a>'
>>> urlizetrunc(uri, 30)
-'<a href="http://31characteruri.com/test/" rel="nofollow">http://31characteruri.com/t...</a>'
+u'<a href="http://31characteruri.com/test/" rel="nofollow">http://31characteruri.com/t...</a>'
>>> urlizetrunc(uri, 2)
-'<a href="http://31characteruri.com/test/" rel="nofollow">...</a>'
+u'<a href="http://31characteruri.com/test/" rel="nofollow">...</a>'
>>> wordcount('')
0
->>> wordcount('oneword')
+>>> wordcount(u'oneword')
1
->>> wordcount('lots of words')
+>>> wordcount(u'lots of words')
3
->>> wordwrap('this is a long paragraph of text that really needs to be wrapped I\'m afraid', 14)
-"this is a long\nparagraph of\ntext that\nreally needs\nto be wrapped\nI'm afraid"
+>>> wordwrap(u'this is a long paragraph of text that really needs to be wrapped I\'m afraid', 14)
+u"this is a long\nparagraph of\ntext that\nreally needs\nto be wrapped\nI'm afraid"
->>> wordwrap('this is a short paragraph of text.\n But this line should be indented',14)
-'this is a\nshort\nparagraph of\ntext.\n But this\nline should be\nindented'
+>>> wordwrap(u'this is a short paragraph of text.\n But this line should be indented',14)
+u'this is a\nshort\nparagraph of\ntext.\n But this\nline should be\nindented'
->>> wordwrap('this is a short paragraph of text.\n But this line should be indented',15)
-'this is a short\nparagraph of\ntext.\n But this line\nshould be\nindented'
+>>> wordwrap(u'this is a short paragraph of text.\n But this line should be indented',15)
+u'this is a short\nparagraph of\ntext.\n But this line\nshould be\nindented'
->>> ljust('test', 10)
-'test '
+>>> ljust(u'test', 10)
+u'test '
->>> ljust('test', 3)
-'test'
+>>> ljust(u'test', 3)
+u'test'
->>> rjust('test', 10)
-' test'
+>>> rjust(u'test', 10)
+u' test'
->>> rjust('test', 3)
-'test'
+>>> rjust(u'test', 3)
+u'test'
->>> center('test', 6)
-' test '
+>>> center(u'test', 6)
+u' test '
->>> cut('a string to be mangled', 'a')
-' string to be mngled'
+>>> cut(u'a string to be mangled', 'a')
+u' string to be mngled'
->>> cut('a string to be mangled', 'ng')
-'a stri to be maled'
+>>> cut(u'a string to be mangled', 'ng')
+u'a stri to be maled'
->>> cut('a string to be mangled', 'strings')
-'a string to be mangled'
+>>> cut(u'a string to be mangled', 'strings')
+u'a string to be mangled'
->>> escape('<some html & special characters > here')
-'&lt;some html &amp; special characters &gt; here'
+>>> escape(u'<some html & special characters > here')
+u'&lt;some html &amp; special characters &gt; here'
>>> escape(u'<some html & special characters > here ĐÅ€£')
u'&lt;some html &amp; special characters &gt; here \xc4\x90\xc3\x85\xe2\x82\xac\xc2\xa3'
->>> linebreaks('line 1')
-'<p>line 1</p>'
+>>> linebreaks(u'line 1')
+u'<p>line 1</p>'
->>> linebreaks('line 1\nline 2')
-'<p>line 1<br />line 2</p>'
+>>> linebreaks(u'line 1\nline 2')
+u'<p>line 1<br />line 2</p>'
->>> removetags('some <b>html</b> with <script>alert("You smell")</script> disallowed <img /> tags', 'script img')
-'some <b>html</b> with alert("You smell") disallowed tags'
+>>> removetags(u'some <b>html</b> with <script>alert("You smell")</script> disallowed <img /> tags', 'script img')
+u'some <b>html</b> with alert("You smell") disallowed tags'
->>> striptags('some <b>html</b> with <script>alert("You smell")</script> disallowed <img /> tags')
-'some html with alert("You smell") disallowed tags'
+>>> striptags(u'some <b>html</b> with <script>alert("You smell")</script> disallowed <img /> tags')
+u'some html with alert("You smell") disallowed tags'
>>> dictsort([{'age': 23, 'name': 'Barbara-Ann'},
... {'age': 63, 'name': 'Ra Ra Rasputin'},
@@ -207,16 +221,16 @@ u'&lt;some html &amp; special characters &gt; here \xc4\x90\xc3\x85\xe2\x82\xac\
>>> first([0,1,2])
0
->>> first('')
-''
+>>> first(u'')
+u''
->>> first('test')
-'t'
+>>> first(u'test')
+u't'
->>> join([0,1,2], 'glue')
-'0glue1glue2'
+>>> join([0,1,2], u'glue')
+u'0glue1glue2'
->>> length('1234')
+>>> length(u'1234')
4
>>> length([1,2,3,4])
@@ -231,37 +245,37 @@ False
>>> length_is('a', 1)
True
->>> length_is('a', 10)
+>>> length_is(u'a', 10)
False
->>> slice_('abcdefg', '0')
-''
+>>> slice_(u'abcdefg', u'0')
+u''
->>> slice_('abcdefg', '1')
-'a'
+>>> slice_(u'abcdefg', u'1')
+u'a'
->>> slice_('abcdefg', '-1')
-'abcdef'
+>>> slice_(u'abcdefg', u'-1')
+u'abcdef'
->>> slice_('abcdefg', '1:2')
-'b'
+>>> slice_(u'abcdefg', u'1:2')
+u'b'
->>> slice_('abcdefg', '1:3')
-'bc'
+>>> slice_(u'abcdefg', u'1:3')
+u'bc'
->>> slice_('abcdefg', '0::2')
-'aceg'
+>>> slice_(u'abcdefg', u'0::2')
+u'aceg'
->>> unordered_list(['item 1', []])
-'\t<li>item 1</li>'
+>>> unordered_list([u'item 1', []])
+u'\t<li>item 1</li>'
->>> unordered_list(['item 1', [['item 1.1', []]]])
-'\t<li>item 1\n\t<ul>\n\t\t<li>item 1.1</li>\n\t</ul>\n\t</li>'
+>>> unordered_list([u'item 1', [[u'item 1.1', []]]])
+u'\t<li>item 1\n\t<ul>\n\t\t<li>item 1.1</li>\n\t</ul>\n\t</li>'
->>> unordered_list(['item 1', [['item 1.1', []], ['item 1.2', []]]])
-'\t<li>item 1\n\t<ul>\n\t\t<li>item 1.1</li>\n\t\t<li>item 1.2</li>\n\t</ul>\n\t</li>'
+>>> unordered_list([u'item 1', [[u'item 1.1', []], [u'item 1.2', []]]])
+u'\t<li>item 1\n\t<ul>\n\t\t<li>item 1.1</li>\n\t\t<li>item 1.2</li>\n\t</ul>\n\t</li>'
->>> add('1', '2')
+>>> add(u'1', u'2')
3
>>> get_digit(123, 1)
@@ -279,43 +293,43 @@ False
>>> get_digit(123, 0)
123
->>> get_digit('xyz', 0)
-'xyz'
+>>> get_digit(u'xyz', 0)
+u'xyz'
# real testing of date() is in dateformat.py
->>> date(datetime.datetime(2005, 12, 29), "d F Y")
-'29 December 2005'
->>> date(datetime.datetime(2005, 12, 29), r'jS o\f F')
-'29th of December'
+>>> date(datetime.datetime(2005, 12, 29), u"d F Y")
+u'29 December 2005'
+>>> date(datetime.datetime(2005, 12, 29), ur'jS o\f F')
+u'29th of December'
# real testing of time() is done in dateformat.py
->>> time(datetime.time(13), "h")
-'01'
+>>> time(datetime.time(13), u"h")
+u'01'
->>> time(datetime.time(0), "h")
-'12'
+>>> time(datetime.time(0), u"h")
+u'12'
# real testing is done in timesince.py, where we can provide our own 'now'
>>> timesince(datetime.datetime.now() - datetime.timedelta(1))
-'1 day'
+u'1 day'
->>> default("val", "default")
-'val'
+>>> default(u"val", u"default")
+u'val'
->>> default(None, "default")
-'default'
+>>> default(None, u"default")
+u'default'
->>> default('', "default")
-'default'
+>>> default(u'', u"default")
+u'default'
->>> default_if_none("val", "default")
-'val'
+>>> default_if_none(u"val", u"default")
+u'val'
->>> default_if_none(None, "default")
-'default'
+>>> default_if_none(None, u"default")
+u'default'
->>> default_if_none('', "default")
-''
+>>> default_if_none(u'', u"default")
+u''
>>> divisibleby(4, 2)
True
@@ -324,139 +338,139 @@ True
False
>>> yesno(True)
-'yes'
+u'yes'
>>> yesno(False)
-'no'
+u'no'
>>> yesno(None)
-'maybe'
+u'maybe'
->>> yesno(True, 'certainly,get out of town,perhaps')
-'certainly'
+>>> yesno(True, u'certainly,get out of town,perhaps')
+u'certainly'
->>> yesno(False, 'certainly,get out of town,perhaps')
-'get out of town'
+>>> yesno(False, u'certainly,get out of town,perhaps')
+u'get out of town'
->>> yesno(None, 'certainly,get out of town,perhaps')
-'perhaps'
+>>> yesno(None, u'certainly,get out of town,perhaps')
+u'perhaps'
->>> yesno(None, 'certainly,get out of town')
-'get out of town'
+>>> yesno(None, u'certainly,get out of town')
+u'get out of town'
>>> filesizeformat(1023)
-'1023 bytes'
+u'1023 bytes'
>>> filesizeformat(1024)
-'1.0 KB'
+u'1.0 KB'
>>> filesizeformat(10*1024)
-'10.0 KB'
+u'10.0 KB'
>>> filesizeformat(1024*1024-1)
-'1024.0 KB'
+u'1024.0 KB'
>>> filesizeformat(1024*1024)
-'1.0 MB'
+u'1.0 MB'
>>> filesizeformat(1024*1024*50)
-'50.0 MB'
+u'50.0 MB'
>>> filesizeformat(1024*1024*1024-1)
-'1024.0 MB'
+u'1024.0 MB'
>>> filesizeformat(1024*1024*1024)
-'1.0 GB'
+u'1.0 GB'
>>> pluralize(1)
-''
+u''
>>> pluralize(0)
-'s'
+u's'
>>> pluralize(2)
-'s'
+u's'
>>> pluralize([1])
-''
+u''
>>> pluralize([])
-'s'
+u's'
>>> pluralize([1,2,3])
-'s'
+u's'
->>> pluralize(1,'es')
-''
+>>> pluralize(1,u'es')
+u''
->>> pluralize(0,'es')
-'es'
+>>> pluralize(0,u'es')
+u'es'
->>> pluralize(2,'es')
-'es'
+>>> pluralize(2,u'es')
+u'es'
->>> pluralize(1,'y,ies')
-'y'
+>>> pluralize(1,u'y,ies')
+u'y'
->>> pluralize(0,'y,ies')
-'ies'
+>>> pluralize(0,u'y,ies')
+u'ies'
->>> pluralize(2,'y,ies')
-'ies'
+>>> pluralize(2,u'y,ies')
+u'ies'
->>> pluralize(0,'y,ies,error')
-''
+>>> pluralize(0,u'y,ies,error')
+u''
->>> phone2numeric('0800 flowers')
-'0800 3569377'
+>>> phone2numeric(u'0800 flowers')
+u'0800 3569377'
# Filters shouldn't break if passed non-strings
>>> addslashes(123)
-'123'
+u'123'
>>> linenumbers(123)
-'1. 123'
+u'1. 123'
>>> lower(123)
-'123'
+u'123'
>>> make_list(123)
-['1', '2', '3']
+[u'1', u'2', u'3']
>>> slugify(123)
-'123'
+u'123'
>>> title(123)
-'123'
+u'123'
>>> truncatewords(123, 2)
-'123'
+u'123'
>>> upper(123)
-'123'
+u'123'
>>> urlencode(123)
-'123'
+u'123'
>>> urlize(123)
-'123'
+u'123'
>>> urlizetrunc(123, 1)
-'123'
+u'123'
>>> wordcount(123)
1
>>> wordwrap(123, 2)
-'123'
+u'123'
>>> ljust('123', 4)
-'123 '
+u'123 '
>>> rjust('123', 4)
-' 123'
+u' 123'
>>> center('123', 5)
-' 123 '
+u' 123 '
>>> center('123', 6)
-' 123 '
+u' 123 '
>>> cut(123, '2')
-'13'
+u'13'
>>> escape(123)
-'123'
+u'123'
>>> linebreaks(123)
-'<p>123</p>'
+u'<p>123</p>'
>>> linebreaksbr(123)
-'123'
+u'123'
>>> removetags(123, 'a')
-'123'
+u'123'
>>> striptags(123)
-'123'
+u'123'
"""
diff --git a/tests/regressiontests/fixtures_regress/fixtures/pretty.xml b/tests/regressiontests/fixtures_regress/fixtures/pretty.xml
new file mode 100644
index 0000000000..68e5710c6a
--- /dev/null
+++ b/tests/regressiontests/fixtures_regress/fixtures/pretty.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<django-objects version="1.0">
+ <object pk="1" model="fixtures_regress.stuff">
+ <field type="CharField" name="name">
+ <None/>
+ </field>
+ <field to="auth.user" name="owner" rel="ManyToOneRel">
+ <None/>
+ </field>
+ </object>
+</django-objects> \ No newline at end of file
diff --git a/tests/regressiontests/fixtures_regress/models.py b/tests/regressiontests/fixtures_regress/models.py
index dd407df353..c6a50f73ce 100644
--- a/tests/regressiontests/fixtures_regress/models.py
+++ b/tests/regressiontests/fixtures_regress/models.py
@@ -1,24 +1,32 @@
from django.db import models
+from django.contrib.auth.models import User
class Animal(models.Model):
- name = models.CharField(maxlength=150)
- latin_name = models.CharField(maxlength=150)
+ name = models.CharField(max_length=150)
+ latin_name = models.CharField(max_length=150)
- def __str__(self):
- return self.common_name
+ def __unicode__(self):
+ return self.common_name
class Plant(models.Model):
- name = models.CharField(maxlength=150)
+ name = models.CharField(max_length=150)
class Meta:
# For testing when upper case letter in app name; regression for #4057
db_table = "Fixtures_regress_plant"
+class Stuff(models.Model):
+ name = models.CharField(max_length=20, null=True)
+ owner = models.ForeignKey(User, null=True)
+
+ def __unicode__(self):
+ return unicode(self.name) + u' is owned by ' + unicode(self.owner)
+
__test__ = {'API_TESTS':"""
>>> from django.core import management
# Load a fixture that uses PK=1
->>> management.load_data(['sequence'], verbosity=0)
+>>> management.call_command('loaddata', 'sequence', verbosity=0)
# Create a new animal. Without a sequence reset, this new object
# will take a PK of 1 (on Postgres), and the save will fail.
@@ -26,4 +34,13 @@ __test__ = {'API_TESTS':"""
>>> animal = Animal(name='Platypus', latin_name='Ornithorhynchus anatinus')
>>> animal.save()
-"""} \ No newline at end of file
+###############################################
+# Regression test for ticket #4558 -- pretty printing of XML fixtures
+# doesn't affect parsing of None values.
+
+# Load a pretty-printed XML fixture with Nulls.
+>>> management.call_command('loaddata', 'pretty.xml', verbosity=0)
+>>> Stuff.objects.all()
+[<Stuff: None is owned by None>]
+
+"""}
diff --git a/tests/regressiontests/forms/localflavor.py b/tests/regressiontests/forms/localflavor.py
index ede89de2a0..a896013b0d 100644
--- a/tests/regressiontests/forms/localflavor.py
+++ b/tests/regressiontests/forms/localflavor.py
@@ -913,11 +913,11 @@ ValidationError: [u'This field requires only numbers.']
>>> f.clean('375.788.573-000')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at most 14 characters.']
+ValidationError: [u'Ensure this value has at most 14 characters (it has 15).']
>>> f.clean('123.456.78')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at least 11 characters.']
+ValidationError: [u'Ensure this value has at least 11 characters (it has 10).']
>>> f.clean('123456789555')
Traceback (most recent call last):
...
@@ -1208,11 +1208,11 @@ u'230880-3449'
>>> f.clean('230880343')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at least 10 characters.']
+ValidationError: [u'Ensure this value has at least 10 characters (it has 9).']
>>> f.clean('230880343234')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at most 11 characters.']
+ValidationError: [u'Ensure this value has at most 11 characters (it has 12).']
>>> f.clean('abcdefghijk')
Traceback (most recent call last):
...
@@ -1254,18 +1254,18 @@ ValidationError: [u'Enter a valid value.']
>>> f.clean('123456')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at least 7 characters.']
+ValidationError: [u'Ensure this value has at least 7 characters (it has 6).']
>>> f.clean('123456555')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at most 8 characters.']
+ValidationError: [u'Ensure this value has at most 8 characters (it has 9).']
>>> f.clean('abcdefg')
Traceback (most recent call last):
ValidationError: [u'Enter a valid value.']
>>> f.clean(' 1234567 ')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at most 8 characters.']
+ValidationError: [u'Ensure this value has at most 8 characters (it has 9).']
>>> f.clean(' 12367 ')
Traceback (most recent call last):
...
@@ -1303,9 +1303,9 @@ strict.
>>> rut = CLRutField()
>>> rut.clean('11-6')
-'11-6'
+u'11-6'
>>> rut.clean('116')
-'11-6'
+u'11-6'
# valid format, bad verifier.
>>> rut.clean('11.111.111-0')
@@ -1318,13 +1318,13 @@ Traceback (most recent call last):
ValidationError: [u'The Chilean RUT is not valid.']
>>> rut.clean('767484100')
-'76.748.410-0'
+u'76.748.410-0'
>>> rut.clean('78.412.790-7')
-'78.412.790-7'
+u'78.412.790-7'
>>> rut.clean('8.334.6043')
-'8.334.604-3'
+u'8.334.604-3'
>>> rut.clean('76793310-K')
-'76.793.310-K'
+u'76.793.310-K'
Strict RUT usage (does not allow imposible values)
>>> rut = CLRutField(strict=True)
@@ -1346,7 +1346,7 @@ Traceback (most recent call last):
...
ValidationError: [u'Enter valid a Chilean RUT. The format is XX.XXX.XXX-X.']
>>> rut.clean('78.412.790-7')
-'78.412.790-7'
+u'78.412.790-7'
>>> rut.clean('8.334.6043')
Traceback (most recent call last):
...
@@ -1356,4 +1356,94 @@ Traceback (most recent call last):
...
ValidationError: [u'Enter valid a Chilean RUT. The format is XX.XXX.XXX-X.']
+## CLRegionSelect #########################################################
+>>> from django.contrib.localflavor.cl.forms import CLRegionSelect
+>>> f = CLRegionSelect()
+
+>>> f.render('foo', 'bar')
+u'<select name="foo">\n<option value="RM">Regi\xf3n Metropolitana de Santiago</option>\n<option value="I">Regi\xf3n de Tarapac\xe1</option>\n<option value="II">Regi\xf3n de Antofagasta</option>\n<option value="III">Regi\xf3n de Atacama</option>\n<option value="IV">Regi\xf3n de Coquimbo</option>\n<option value="V">Regi\xf3n de Valpara\xedso</option>\n<option value="VI">Regi\xf3n del Libertador Bernardo O&#39;Higgins</option>\n<option value="VII">Regi\xf3n del Maule</option>\n<option value="VIII">Regi\xf3n del B\xedo B\xedo</option>\n<option value="IX">Regi\xf3n de la Araucan\xeda</option>\n<option value="X">Regi\xf3n de los Lagos</option>\n<option value="XI">Regi\xf3n de Ays\xe9n del General Carlos Ib\xe1\xf1ez del Campo</option>\n<option value="XII">Regi\xf3n de Magallanes y la Ant\xe1rtica Chilena</option>\n<option value="XIV">Regi\xf3n de Los R\xedos</option>\n<option value="XV">Regi\xf3n de Arica-Parinacota</option>\n</select>'
+
+# SKPostalCodeField #########################################################
+
+>>> from django.contrib.localflavor.sk.forms import SKPostalCodeField
+>>> f = SKPostalCodeField()
+>>> f.clean('84545x')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a postal code in the format XXXXX or XXX XX.']
+>>> f.clean('91909')
+u'91909'
+>>> f.clean('917 01')
+u'91701'
+
+# SKRegionSelect ############################################################
+
+>>> from django.contrib.localflavor.sk.forms import SKRegionSelect
+>>> w = SKRegionSelect()
+>>> w.render('regions', 'TT')
+u'<select name="regions">\n<option value="BB">Banska Bystrica region</option>\n<option value="BA">Bratislava region</option>\n<option value="KE">Kosice region</option>\n<option value="NR">Nitra region</option>\n<option value="PO">Presov region</option>\n<option value="TN">Trencin region</option>\n<option value="TT" selected="selected">Trnava region</option>\n<option value="ZA">Zilina region</option>\n</select>'
+
+# SKDistrictSelect ##########################################################
+
+>>> from django.contrib.localflavor.sk.forms import SKDistrictSelect
+>>> w = SKDistrictSelect()
+>>> w.render('Districts', 'RK')
+u'<select name="Districts">\n<option value="BB">Banska Bystrica</option>\n<option value="BS">Banska Stiavnica</option>\n<option value="BJ">Bardejov</option>\n<option value="BN">Banovce nad Bebravou</option>\n<option value="BR">Brezno</option>\n<option value="BA1">Bratislava I</option>\n<option value="BA2">Bratislava II</option>\n<option value="BA3">Bratislava III</option>\n<option value="BA4">Bratislava IV</option>\n<option value="BA5">Bratislava V</option>\n<option value="BY">Bytca</option>\n<option value="CA">Cadca</option>\n<option value="DT">Detva</option>\n<option value="DK">Dolny Kubin</option>\n<option value="DS">Dunajska Streda</option>\n<option value="GA">Galanta</option>\n<option value="GL">Gelnica</option>\n<option value="HC">Hlohovec</option>\n<option value="HE">Humenne</option>\n<option value="IL">Ilava</option>\n<option value="KK">Kezmarok</option>\n<option value="KN">Komarno</option>\n<option value="KE1">Kosice I</option>\n<option value="KE2">Kosice II</option>\n<option value="KE3">Kosice III</option>\n<option value="KE4">Kosice IV</option>\n<option value="KEO">Kosice - okolie</option>\n<option value="KA">Krupina</option>\n<option value="KM">Kysucke Nove Mesto</option>\n<option value="LV">Levice</option>\n<option value="LE">Levoca</option>\n<option value="LM">Liptovsky Mikulas</option>\n<option value="LC">Lucenec</option>\n<option value="MA">Malacky</option>\n<option value="MT">Martin</option>\n<option value="ML">Medzilaborce</option>\n<option value="MI">Michalovce</option>\n<option value="MY">Myjava</option>\n<option value="NO">Namestovo</option>\n<option value="NR">Nitra</option>\n<option value="NM">Nove Mesto nad Vahom</option>\n<option value="NZ">Nove Zamky</option>\n<option value="PE">Partizanske</option>\n<option value="PK">Pezinok</option>\n<option value="PN">Piestany</option>\n<option value="PT">Poltar</option>\n<option value="PP">Poprad</option>\n<option value="PB">Povazska Bystrica</option>\n<option value="PO">Presov</option>\n<option value="PD">Prievidza</option>\n<option value="PU">Puchov</option>\n<option value="RA">Revuca</option>\n<option value="RS">Rimavska Sobota</option>\n<option value="RV">Roznava</option>\n<option value="RK" selected="selected">Ruzomberok</option>\n<option value="SB">Sabinov</option>\n<option value="SC">Senec</option>\n<option value="SE">Senica</option>\n<option value="SI">Skalica</option>\n<option value="SV">Snina</option>\n<option value="SO">Sobrance</option>\n<option value="SN">Spisska Nova Ves</option>\n<option value="SL">Stara Lubovna</option>\n<option value="SP">Stropkov</option>\n<option value="SK">Svidnik</option>\n<option value="SA">Sala</option>\n<option value="TO">Topolcany</option>\n<option value="TV">Trebisov</option>\n<option value="TN">Trencin</option>\n<option value="TT">Trnava</option>\n<option value="TR">Turcianske Teplice</option>\n<option value="TS">Tvrdosin</option>\n<option value="VK">Velky Krtis</option>\n<option value="VT">Vranov nad Toplou</option>\n<option value="ZM">Zlate Moravce</option>\n<option value="ZV">Zvolen</option>\n<option value="ZC">Zarnovica</option>\n<option value="ZH">Ziar nad Hronom</option>\n<option value="ZA">Zilina</option>\n</select>'
+
+# PLVoivodeshipSelect ##########################################################
+
+>>> from django.contrib.localflavor.pl.forms import PLVoivodeshipSelect
+>>> f = PLVoivodeshipSelect()
+>>> f.render('voivodeships','pomerania')
+u'<select name="voivodeships">\n<option value="lower_silesia">Lower Silesia</option>\n<option value="kuyavia-pomerania">Kuyavia-Pomerania</option>\n<option value="lublin">Lublin</option>\n<option value="lubusz">Lubusz</option>\n<option value="lodz">Lodz</option>\n<option value="lesser_poland">Lesser Poland</option>\n<option value="masovia">Masovia</option>\n<option value="opole">Opole</option>\n<option value="subcarpatia">Subcarpatia</option>\n<option value="podlasie">Podlasie</option>\n<option value="pomerania" selected="selected">Pomerania</option>\n<option value="silesia">Silesia</option>\n<option value="swietokrzyskie">Swietokrzyskie</option>\n<option value="warmia-masuria">Warmia-Masuria</option>\n<option value="greater_poland">Greater Poland</option>\n<option value="west_pomerania">West Pomerania</option>\n</select>'
+
+# PLAdministrativeUnitSelect ##########################################################
+
+>>> from django.contrib.localflavor.pl.forms import PLAdministrativeUnitSelect
+>>> f = PLAdministrativeUnitSelect()
+>>> f.render('administrativeunit','katowice')
+u'<select name="administrativeunit">\n<option value="wroclaw">Wroc\u0142aw</option>\n<option value="jeleniagora">Jelenia G\xf3ra</option>\n<option value="legnica">Legnica</option>\n<option value="boleslawiecki">boles\u0142awiecki</option>\n<option value="dzierzoniowski">dzier\u017coniowski</option>\n<option value="glogowski">g\u0142ogowski</option>\n<option value="gorowski">g\xf3rowski</option>\n<option value="jaworski">jaworski</option>\n<option value="jeleniogorski">jeleniog\xf3rski</option>\n<option value="kamiennogorski">kamiennog\xf3rski</option>\n<option value="klodzki">k\u0142odzki</option>\n<option value="legnicki">legnicki</option>\n<option value="lubanski">luba\u0144ski</option>\n<option value="lubinski">lubi\u0144ski</option>\n<option value="lwowecki">lw\xf3wecki</option>\n<option value="milicki">milicki</option>\n<option value="olesnicki">ole\u015bnicki</option>\n<option value="olawski">o\u0142awski</option>\n<option value="polkowicki">polkowicki</option>\n<option value="strzelinski">strzeli\u0144ski</option>\n<option value="sredzki">\u015bredzki</option>\n<option value="swidnicki">\u015bwidnicki</option>\n<option value="trzebnicki">trzebnicki</option>\n<option value="walbrzyski">wa\u0142brzyski</option>\n<option value="wolowski">wo\u0142owski</option>\n<option value="wroclawski">wroc\u0142awski</option>\n<option value="zabkowicki">z\u0105bkowicki</option>\n<option value="zgorzelecki">zgorzelecki</option>\n<option value="zlotoryjski">z\u0142otoryjski</option>\n<option value="bydgoszcz">Bydgoszcz</option>\n<option value="torun">Toru\u0144</option>\n<option value="wloclawek">W\u0142oc\u0142awek</option>\n<option value="grudziadz">Grudzi\u0105dz</option>\n<option value="aleksandrowski">aleksandrowski</option>\n<option value="brodnicki">brodnicki</option>\n<option value="bydgoski">bydgoski</option>\n<option value="chelminski">che\u0142mi\u0144ski</option>\n<option value="golubsko-dobrzynski">golubsko-dobrzy\u0144ski</option>\n<option value="grudziadzki">grudzi\u0105dzki</option>\n<option value="inowroclawski">inowroc\u0142awski</option>\n<option value="lipnowski">lipnowski</option>\n<option value="mogilenski">mogile\u0144ski</option>\n<option value="nakielski">nakielski</option>\n<option value="radziejowski">radziejowski</option>\n<option value="rypinski">rypi\u0144ski</option>\n<option value="sepolenski">s\u0119pole\u0144ski</option>\n<option value="swiecki">\u015bwiecki</option>\n<option value="torunski">toru\u0144ski</option>\n<option value="tucholski">tucholski</option>\n<option value="wabrzeski">w\u0105brzeski</option>\n<option value="wloclawski">wroc\u0142awski</option>\n<option value="zninski">\u017ani\u0144ski</option>\n<option value="lublin">Lublin</option>\n<option value="biala-podlaska">Bia\u0142a Podlaska</option>\n<option value="chelm">Che\u0142m</option>\n<option value="zamosc">Zamo\u015b\u0107</option>\n<option value="bialski">bialski</option>\n<option value="bilgorajski">bi\u0142gorajski</option>\n<option value="chelmski">che\u0142mski</option>\n<option value="hrubieszowski">hrubieszowski</option>\n<option value="janowski">janowski</option>\n<option value="krasnostawski">krasnostawski</option>\n<option value="krasnicki">kra\u015bnicki</option>\n<option value="lubartowski">lubartowski</option>\n<option value="lubelski">lubelski</option>\n<option value="leczynski">\u0142\u0119czy\u0144ski</option>\n<option value="lukowski">\u0142ukowski</option>\n<option value="opolski">opolski</option>\n<option value="parczewski">parczewski</option>\n<option value="pulawski">pu\u0142awski</option>\n<option value="radzynski">radzy\u0144ski</option>\n<option value="rycki">rycki</option>\n<option value="swidnicki">\u015bwidnicki</option>\n<option value="tomaszowski">tomaszowski</option>\n<option value="wlodawski">w\u0142odawski</option>\n<option value="zamojski">zamojski</option>\n<option value="gorzow-wielkopolski">Gorz\xf3w Wielkopolski</option>\n<option value="zielona-gora">Zielona G\xf3ra</option>\n<option value="gorzowski">gorzowski</option>\n<option value="krosnienski">kro\u015bnie\u0144ski</option>\n<option value="miedzyrzecki">mi\u0119dzyrzecki</option>\n<option value="nowosolski">nowosolski</option>\n<option value="slubicki">s\u0142ubicki</option>\n<option value="strzelecko-drezdenecki">strzelecko-drezdenecki</option>\n<option value="sulecinski">sule\u0144ci\u0144ski</option>\n<option value="swiebodzinski">\u015bwiebodzi\u0144ski</option>\n<option value="wschowski">wschowski</option>\n<option value="zielonogorski">zielonog\xf3rski</option>\n<option value="zaganski">\u017caga\u0144ski</option>\n<option value="zarski">\u017carski</option>\n<option value="lodz">\u0141\xf3d\u017a</option>\n<option value="piotrkow-trybunalski">Piotrk\xf3w Trybunalski</option>\n<option value="skierniewice">Skierniewice</option>\n<option value="belchatowski">be\u0142chatowski</option>\n<option value="brzezinski">brzezi\u0144ski</option>\n<option value="kutnowski">kutnowski</option>\n<option value="laski">\u0142aski</option>\n<option value="leczycki">\u0142\u0119czycki</option>\n<option value="lowicki">\u0142owicki</option>\n<option value="lodzki wschodni">\u0142\xf3dzki wschodni</option>\n<option value="opoczynski">opoczy\u0144ski</option>\n<option value="pabianicki">pabianicki</option>\n<option value="pajeczanski">paj\u0119cza\u0144ski</option>\n<option value="piotrkowski">piotrkowski</option>\n<option value="poddebicki">podd\u0119bicki</option>\n<option value="radomszczanski">radomszcza\u0144ski</option>\n<option value="rawski">rawski</option>\n<option value="sieradzki">sieradzki</option>\n<option value="skierniewicki">skierniewicki</option>\n<option value="tomaszowski">tomaszowski</option>\n<option value="wielunski">wielu\u0144ski</option>\n<option value="wieruszowski">wieruszowski</option>\n<option value="zdunskowolski">zdu\u0144skowolski</option>\n<option value="zgierski">zgierski</option>\n<option value="krakow">Krak\xf3w</option>\n<option value="tarnow">Tarn\xf3w</option>\n<option value="nowy-sacz">Nowy S\u0105cz</option>\n<option value="bochenski">boche\u0144ski</option>\n<option value="brzeski">brzeski</option>\n<option value="chrzanowski">chrzanowski</option>\n<option value="dabrowski">d\u0105browski</option>\n<option value="gorlicki">gorlicki</option>\n<option value="krakowski">krakowski</option>\n<option value="limanowski">limanowski</option>\n<option value="miechowski">miechowski</option>\n<option value="myslenicki">my\u015blenicki</option>\n<option value="nowosadecki">nowos\u0105decki</option>\n<option value="nowotarski">nowotarski</option>\n<option value="olkuski">olkuski</option>\n<option value="oswiecimski">o\u015bwi\u0119cimski</option>\n<option value="proszowicki">proszowicki</option>\n<option value="suski">suski</option>\n<option value="tarnowski">tarnowski</option>\n<option value="tatrzanski">tatrza\u0144ski</option>\n<option value="wadowicki">wadowicki</option>\n<option value="wielicki">wielicki</option>\n<option value="warszawa">Warszawa</option>\n<option value="ostroleka">Ostro\u0142\u0119ka</option>\n<option value="plock">P\u0142ock</option>\n<option value="radom">Radom</option>\n<option value="siedlce">Siedlce</option>\n<option value="bialobrzeski">bia\u0142obrzeski</option>\n<option value="ciechanowski">ciechanowski</option>\n<option value="garwolinski">garwoli\u0144ski</option>\n<option value="gostyninski">gostyni\u0144ski</option>\n<option value="grodziski">grodziski</option>\n<option value="grojecki">gr\xf3jecki</option>\n<option value="kozienicki">kozenicki</option>\n<option value="legionowski">legionowski</option>\n<option value="lipski">lipski</option>\n<option value="losicki">\u0142osicki</option>\n<option value="makowski">makowski</option>\n<option value="minski">mi\u0144ski</option>\n<option value="mlawski">m\u0142awski</option>\n<option value="nowodworski">nowodworski</option>\n<option value="ostrolecki">ostro\u0142\u0119cki</option>\n<option value="ostrowski">ostrowski</option>\n<option value="otwocki">otwocki</option>\n<option value="piaseczynski">piaseczy\u0144ski</option>\n<option value="plocki">p\u0142ocki</option>\n<option value="plonski">p\u0142o\u0144ski</option>\n<option value="pruszkowski">pruszkowski</option>\n<option value="przasnyski">przasnyski</option>\n<option value="przysuski">przysuski</option>\n<option value="pultuski">pu\u0142tuski</option>\n<option value="radomski">radomski</option>\n<option value="siedlecki">siedlecki</option>\n<option value="sierpecki">sierpecki</option>\n<option value="sochaczewski">sochaczewski</option>\n<option value="sokolowski">soko\u0142owski</option>\n<option value="szydlowiecki">szyd\u0142owiecki</option>\n<option value="warszawski-zachodni">warszawski zachodni</option>\n<option value="wegrowski">w\u0119growski</option>\n<option value="wolominski">wo\u0142omi\u0144ski</option>\n<option value="wyszkowski">wyszkowski</option>\n<option value="zwolenski">zwole\u0144ski</option>\n<option value="zurominski">\u017curomi\u0144ski</option>\n<option value="zyrardowski">\u017cyrardowski</option>\n<option value="opole">Opole</option>\n<option value="brzeski">brzeski</option>\n<option value="glubczycki">g\u0142ubczyski</option>\n<option value="kedzierzynsko-kozielski">k\u0119dzierzy\u0144ski-kozielski</option>\n<option value="kluczborski">kluczborski</option>\n<option value="krapkowicki">krapkowicki</option>\n<option value="namyslowski">namys\u0142owski</option>\n<option value="nyski">nyski</option>\n<option value="oleski">oleski</option>\n<option value="opolski">opolski</option>\n<option value="prudnicki">prudnicki</option>\n<option value="strzelecki">strzelecki</option>\n<option value="rzeszow">Rzesz\xf3w</option>\n<option value="krosno">Krosno</option>\n<option value="przemysl">Przemy\u015bl</option>\n<option value="tarnobrzeg">Tarnobrzeg</option>\n<option value="bieszczadzki">bieszczadzki</option>\n<option value="brzozowski">brzozowski</option>\n<option value="debicki">d\u0119bicki</option>\n<option value="jaroslawski">jaros\u0142awski</option>\n<option value="jasielski">jasielski</option>\n<option value="kolbuszowski">kolbuszowski</option>\n<option value="krosnienski">kro\u015bnie\u0144ski</option>\n<option value="leski">leski</option>\n<option value="lezajski">le\u017cajski</option>\n<option value="lubaczowski">lubaczowski</option>\n<option value="lancucki">\u0142a\u0144cucki</option>\n<option value="mielecki">mielecki</option>\n<option value="nizanski">ni\u017ca\u0144ski</option>\n<option value="przemyski">przemyski</option>\n<option value="przeworski">przeworski</option>\n<option value="ropczycko-sedziszowski">ropczycko-s\u0119dziszowski</option>\n<option value="rzeszowski">rzeszowski</option>\n<option value="sanocki">sanocki</option>\n<option value="stalowowolski">stalowowolski</option>\n<option value="strzyzowski">strzy\u017cowski</option>\n<option value="tarnobrzeski">tarnobrzeski</option>\n<option value="bialystok">Bia\u0142ystok</option>\n<option value="lomza">\u0141om\u017ca</option>\n<option value="suwalki">Suwa\u0142ki</option>\n<option value="augustowski">augustowski</option>\n<option value="bialostocki">bia\u0142ostocki</option>\n<option value="bielski">bielski</option>\n<option value="grajewski">grajewski</option>\n<option value="hajnowski">hajnowski</option>\n<option value="kolnenski">kolne\u0144ski</option>\n<option value="\u0142omzynski">\u0142om\u017cy\u0144ski</option>\n<option value="moniecki">moniecki</option>\n<option value="sejnenski">sejne\u0144ski</option>\n<option value="siemiatycki">siematycki</option>\n<option value="sokolski">sok\xf3lski</option>\n<option value="suwalski">suwalski</option>\n<option value="wysokomazowiecki">wysokomazowiecki</option>\n<option value="zambrowski">zambrowski</option>\n<option value="gdansk">Gda\u0144sk</option>\n<option value="gdynia">Gdynia</option>\n<option value="slupsk">S\u0142upsk</option>\n<option value="sopot">Sopot</option>\n<option value="bytowski">bytowski</option>\n<option value="chojnicki">chojnicki</option>\n<option value="czluchowski">cz\u0142uchowski</option>\n<option value="kartuski">kartuski</option>\n<option value="koscierski">ko\u015bcierski</option>\n<option value="kwidzynski">kwidzy\u0144ski</option>\n<option value="leborski">l\u0119borski</option>\n<option value="malborski">malborski</option>\n<option value="nowodworski">nowodworski</option>\n<option value="gdanski">gda\u0144ski</option>\n<option value="pucki">pucki</option>\n<option value="slupski">s\u0142upski</option>\n<option value="starogardzki">starogardzki</option>\n<option value="sztumski">sztumski</option>\n<option value="tczewski">tczewski</option>\n<option value="wejherowski">wejcherowski</option>\n<option value="katowice" selected="selected">Katowice</option>\n<option value="bielsko-biala">Bielsko-Bia\u0142a</option>\n<option value="bytom">Bytom</option>\n<option value="chorzow">Chorz\xf3w</option>\n<option value="czestochowa">Cz\u0119stochowa</option>\n<option value="dabrowa-gornicza">D\u0105browa G\xf3rnicza</option>\n<option value="gliwice">Gliwice</option>\n<option value="jastrzebie-zdroj">Jastrz\u0119bie Zdr\xf3j</option>\n<option value="jaworzno">Jaworzno</option>\n<option value="myslowice">Mys\u0142owice</option>\n<option value="piekary-slaskie">Piekary \u015al\u0105skie</option>\n<option value="ruda-slaska">Ruda \u015al\u0105ska</option>\n<option value="rybnik">Rybnik</option>\n<option value="siemianowice-slaskie">Siemianowice \u015al\u0105skie</option>\n<option value="sosnowiec">Sosnowiec</option>\n<option value="swietochlowice">\u015awi\u0119toch\u0142owice</option>\n<option value="tychy">Tychy</option>\n<option value="zabrze">Zabrze</option>\n<option value="zory">\u017bory</option>\n<option value="bedzinski">b\u0119dzi\u0144ski</option>\n<option value="bielski">bielski</option>\n<option value="bierunsko-ledzinski">bieru\u0144sko-l\u0119dzi\u0144ski</option>\n<option value="cieszynski">cieszy\u0144ski</option>\n<option value="czestochowski">cz\u0119stochowski</option>\n<option value="gliwicki">gliwicki</option>\n<option value="klobucki">k\u0142obucki</option>\n<option value="lubliniecki">lubliniecki</option>\n<option value="mikolowski">miko\u0142owski</option>\n<option value="myszkowski">myszkowski</option>\n<option value="pszczynski">pszczy\u0144ski</option>\n<option value="raciborski">raciborski</option>\n<option value="rybnicki">rybnicki</option>\n<option value="tarnogorski">tarnog\xf3rski</option>\n<option value="wodzislawski">wodzis\u0142awski</option>\n<option value="zawiercianski">zawiercia\u0144ski</option>\n<option value="zywiecki">\u017cywiecki</option>\n<option value="kielce">Kielce</option>\n<option value="buski">buski</option>\n<option value="jedrzejowski">j\u0119drzejowski</option>\n<option value="kazimierski">kazimierski</option>\n<option value="kielecki">kielecki</option>\n<option value="konecki">konecki</option>\n<option value="opatowski">opatowski</option>\n<option value="ostrowiecki">ostrowiecki</option>\n<option value="pinczowski">pi\u0144czowski</option>\n<option value="sandomierski">sandomierski</option>\n<option value="skarzyski">skar\u017cyski</option>\n<option value="starachowicki">starachowicki</option>\n<option value="staszowski">staszowski</option>\n<option value="wloszczowski">w\u0142oszczowski</option>\n<option value="olsztyn">Olsztyn</option>\n<option value="elblag">Elbl\u0105g</option>\n<option value="bartoszycki">bartoszycki</option>\n<option value="braniewski">braniewski</option>\n<option value="dzialdowski">dzia\u0142dowski</option>\n<option value="elblaski">elbl\u0105ski</option>\n<option value="elcki">e\u0142cki</option>\n<option value="gizycki">gi\u017cycki</option>\n<option value="goldapski">go\u0142dapski</option>\n<option value="ilawski">i\u0142awski</option>\n<option value="ketrzynski">k\u0119trzy\u0144ski</option>\n<option value="lidzbarski">lidzbarski</option>\n<option value="mragowski">mr\u0105gowski</option>\n<option value="nidzicki">nidzicki</option>\n<option value="nowomiejski">nowomiejski</option>\n<option value="olecki">olecki</option>\n<option value="olsztynski">olszty\u0144ski</option>\n<option value="ostrodzki">ostr\xf3dzki</option>\n<option value="piski">piski</option>\n<option value="szczycienski">szczycie\u0144ski</option>\n<option value="wegorzewski">w\u0119gorzewski</option>\n<option value="poznan">Pozna\u0144</option>\n<option value="kalisz">Kalisz</option>\n<option value="konin">Konin</option>\n<option value="leszno">Leszno</option>\n<option value="chodzieski">chodziejski</option>\n<option value="czarnkowsko-trzcianecki">czarnkowsko-trzcianecki</option>\n<option value="gnieznienski">gnie\u017anie\u0144ski</option>\n<option value="gostynski">gosty\u0144ski</option>\n<option value="grodziski">grodziski</option>\n<option value="jarocinski">jaroci\u0144ski</option>\n<option value="kaliski">kaliski</option>\n<option value="kepinski">k\u0119pi\u0144ski</option>\n<option value="kolski">kolski</option>\n<option value="koninski">koni\u0144ski</option>\n<option value="koscianski">ko\u015bcia\u0144ski</option>\n<option value="krotoszynski">krotoszy\u0144ski</option>\n<option value="leszczynski">leszczy\u0144ski</option>\n<option value="miedzychodzki">mi\u0119dzychodzki</option>\n<option value="nowotomyski">nowotomyski</option>\n<option value="obornicki">obornicki</option>\n<option value="ostrowski">ostrowski</option>\n<option value="ostrzeszowski">ostrzeszowski</option>\n<option value="pilski">pilski</option>\n<option value="pleszewski">pleszewski</option>\n<option value="poznanski">pozna\u0144ski</option>\n<option value="rawicki">rawicki</option>\n<option value="slupecki">s\u0142upecki</option>\n<option value="szamotulski">szamotulski</option>\n<option value="sredzki">\u015bredzki</option>\n<option value="sremski">\u015bremski</option>\n<option value="turecki">turecki</option>\n<option value="wagrowiecki">w\u0105growiecki</option>\n<option value="wolsztynski">wolszty\u0144ski</option>\n<option value="wrzesinski">wrzesi\u0144ski</option>\n<option value="zlotowski">z\u0142otowski</option>\n<option value="bialogardzki">bia\u0142ogardzki</option>\n<option value="choszczenski">choszcze\u0144ski</option>\n<option value="drawski">drawski</option>\n<option value="goleniowski">goleniowski</option>\n<option value="gryficki">gryficki</option>\n<option value="gryfinski">gryfi\u0144ski</option>\n<option value="kamienski">kamie\u0144ski</option>\n<option value="kolobrzeski">ko\u0142obrzeski</option>\n<option value="koszalinski">koszali\u0144ski</option>\n<option value="lobeski">\u0142obeski</option>\n<option value="mysliborski">my\u015bliborski</option>\n<option value="policki">policki</option>\n<option value="pyrzycki">pyrzycki</option>\n<option value="slawienski">s\u0142awie\u0144ski</option>\n<option value="stargardzki">stargardzki</option>\n<option value="szczecinecki">szczecinecki</option>\n<option value="swidwinski">\u015bwidwi\u0144ski</option>\n<option value="walecki">wa\u0142ecki</option>\n</select>'
+
+# PLPostalCodeField ##############################################################
+
+>>> from django.contrib.localflavor.pl.forms import PLPostalCodeField
+>>> f = PLPostalCodeField()
+>>> f.clean('43--434')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a postal code in the format XX-XXX.']
+>>> f.clean('41-403')
+u'41-403'
+
+# PLTaxNumberField ###############################################################
+
+>>> from django.contrib.localflavor.pl.forms import PLTaxNumberField
+>>> f = PLTaxNumberField()
+>>> f.clean('43-343-234-323')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a tax number field (NIP) in the format XXX-XXX-XX-XX or XX-XX-XXX-XXX.']
+>>> f.clean('43-34-234-323')
+u'43-34-234-323'
+>>> f.clean('433-344-24-23')
+u'433-344-24-23'
+
+# PLNationalIdentificationNumberField ############################################
+
+>>> from django.contrib.localflavor.pl.forms import PLNationalIdentificationNumberField
+>>> f = PLNationalIdentificationNumberField()
+>>> f.clean('80071610614')
+u'80071610614'
+>>> f.clean('80071610610')
+Traceback (most recent call last):
+...
+ValidationError: [u'Wrong checksum for the National Identification Number.']
+>>> f.clean('80')
+Traceback (most recent call last):
+...
+ValidationError: [u'National Identification Number consists of 11 digits.']
+>>> f.clean('800716106AA')
+Traceback (most recent call last):
+...
+ValidationError: [u'National Identification Number consists of 11 digits.']
"""
diff --git a/tests/regressiontests/forms/regressions.py b/tests/regressiontests/forms/regressions.py
index 5fe057b5d8..784ef49902 100644
--- a/tests/regressiontests/forms/regressions.py
+++ b/tests/regressiontests/forms/regressions.py
@@ -16,24 +16,63 @@ u'<p>F1: <input type="text" class="special" name="f1" maxlength="10" /></p>\n<p>
#######################
There were some problems with form translations in #3600
->>> from django.utils.translation import gettext_lazy, activate, deactivate
+>>> from django.utils.translation import ugettext_lazy, activate, deactivate
>>> class SomeForm(Form):
-... username = CharField(max_length=10, label=gettext_lazy('Username'))
+... username = CharField(max_length=10, label=ugettext_lazy('Username'))
>>> f = SomeForm()
>>> print f.as_p()
<p><label for="id_username">Username:</label> <input id="id_username" type="text" name="username" maxlength="10" /></p>
+
+Translations are done at rendering time, so multi-lingual apps can define forms
+early and still send back the right translation.
+
+# XFAIL
>>> activate('de')
>>> print f.as_p()
<p><label for="id_username">Benutzername:</label> <input id="id_username" type="text" name="username" maxlength="10" /></p>
+>>> activate('pl')
+>>> f.as_p()
+u'<p><label for="id_username">Nazwa u\u017cytkownika:</label> <input id="id_username" type="text" name="username" maxlength="10" /></p>'
>>> deactivate()
Unicode decoding problems...
->>> GENDERS = (('0', u'En tied\xe4'), ('1', u'Mies'), ('2', u'Nainen'))
+>>> GENDERS = ((u'\xc5', u'En tied\xe4'), (u'\xf8', u'Mies'), (u'\xdf', u'Nainen'))
>>> class SomeForm(Form):
-... somechoice = ChoiceField(choices=GENDERS, widget=RadioSelect())
+... somechoice = ChoiceField(choices=GENDERS, widget=RadioSelect(), label=u'\xc5\xf8\xdf')
>>> f = SomeForm()
>>> f.as_p()
-u'<p><label for="id_somechoice_0">Somechoice:</label> <ul>\n<li><label><input type="radio" id="id_somechoice_0" value="0" name="somechoice" /> En tied\xe4</label></li>\n<li><label><input type="radio" id="id_somechoice_1" value="1" name="somechoice" /> Mies</label></li>\n<li><label><input type="radio" id="id_somechoice_2" value="2" name="somechoice" /> Nainen</label></li>\n</ul></p>'
+u'<p><label for="id_somechoice_0">\xc5\xf8\xdf:</label> <ul>\n<li><label><input type="radio" id="id_somechoice_0" value="\xc5" name="somechoice" /> En tied\xe4</label></li>\n<li><label><input type="radio" id="id_somechoice_1" value="\xf8" name="somechoice" /> Mies</label></li>\n<li><label><input type="radio" id="id_somechoice_2" value="\xdf" name="somechoice" /> Nainen</label></li>\n</ul></p>'
+
+Testing choice validation with UTF-8 bytestrings as input (these are the
+Russian abbreviations "мес." and "шт.".
+
+>>> UNITS = (('\xd0\xbc\xd0\xb5\xd1\x81.', '\xd0\xbc\xd0\xb5\xd1\x81.'), ('\xd1\x88\xd1\x82.', '\xd1\x88\xd1\x82.'))
+>>> f = ChoiceField(choices=UNITS)
+>>> f.clean(u'\u0448\u0442.')
+u'\u0448\u0442.'
+>>> f.clean('\xd1\x88\xd1\x82.')
+u'\u0448\u0442.'
+
+Translated error messages used to be buggy.
+>>> activate('ru')
+>>> f = SomeForm({})
+>>> f.as_p()
+u'<ul class="errorlist"><li>\u041e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043f\u043e\u043b\u0435.</li></ul>\n<p><label for="id_somechoice_0">\xc5\xf8\xdf:</label> <ul>\n<li><label><input type="radio" id="id_somechoice_0" value="\xc5" name="somechoice" /> En tied\xe4</label></li>\n<li><label><input type="radio" id="id_somechoice_1" value="\xf8" name="somechoice" /> Mies</label></li>\n<li><label><input type="radio" id="id_somechoice_2" value="\xdf" name="somechoice" /> Nainen</label></li>\n</ul></p>'
+>>> deactivate()
+
+#######################
+# Miscellaneous Tests #
+#######################
+
+There once was a problem with Form fields called "data". Let's make sure that
+doesn't come back.
+>>> class DataForm(Form):
+... data = CharField(max_length=10)
+>>> f = DataForm({'data': 'xyzzy'})
+>>> f.is_valid()
+True
+>>> f.cleaned_data
+{'data': u'xyzzy'}
#######################
# Miscellaneous Tests #
diff --git a/tests/regressiontests/forms/tests.py b/tests/regressiontests/forms/tests.py
index 0808ebe97e..78442677eb 100644
--- a/tests/regressiontests/forms/tests.py
+++ b/tests/regressiontests/forms/tests.py
@@ -4,6 +4,7 @@ from regressions import regression_tests
form_tests = r"""
>>> from django.newforms import *
+>>> from django.newforms.widgets import RadioFieldRenderer
>>> import datetime
>>> import time
>>> import re
@@ -172,27 +173,29 @@ u'<input type="hidden" class="special" value="foo@example.com" name="email" />'
# FileInput Widget ############################################################
+FileInput widgets don't ever show the value, because the old value is of no use
+if you are updating the form or if the provided file generated an error.
>>> w = FileInput()
>>> w.render('email', '')
u'<input type="file" name="email" />'
>>> w.render('email', None)
u'<input type="file" name="email" />'
>>> w.render('email', 'test@example.com')
-u'<input type="file" name="email" value="test@example.com" />'
+u'<input type="file" name="email" />'
>>> w.render('email', 'some "quoted" & ampersanded value')
-u'<input type="file" name="email" value="some &quot;quoted&quot; &amp; ampersanded value" />'
+u'<input type="file" name="email" />'
>>> w.render('email', 'test@example.com', attrs={'class': 'fun'})
-u'<input type="file" name="email" value="test@example.com" class="fun" />'
+u'<input type="file" name="email" class="fun" />'
You can also pass 'attrs' to the constructor:
>>> w = FileInput(attrs={'class': 'fun'})
>>> w.render('email', '')
u'<input type="file" class="fun" name="email" />'
>>> w.render('email', 'foo@example.com')
-u'<input type="file" class="fun" value="foo@example.com" name="email" />'
+u'<input type="file" class="fun" name="email" />'
>>> w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'})
-u'<input type="file" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />'
+u'<input type="file" class="fun" name="email" />'
# Textarea Widget #############################################################
@@ -614,11 +617,11 @@ If 'choices' is passed to both the constructor and render(), then they'll both b
<li><label><input type="radio" name="num" value="5" /> 5</label></li>
</ul>
-The render() method returns a RadioFieldRenderer object, whose str() is a <ul>.
+RadioSelect uses a RadioFieldRenderer to render the individual radio inputs.
You can manipulate that object directly to customize the way the RadioSelect
is rendered.
>>> w = RadioSelect()
->>> r = w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
+>>> r = w.get_renderer('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
>>> for inp in r:
... print inp
<label><input checked="checked" type="radio" name="beatle" value="J" /> John</label>
@@ -644,10 +647,21 @@ beatle J P Paul False
beatle J G George False
beatle J R Ringo False
+You can create your own custom renderers for RadioSelect to use.
+>>> class MyRenderer(RadioFieldRenderer):
+... def render(self):
+... return u'<br />\n'.join([unicode(choice) for choice in self])
+>>> w = RadioSelect(renderer=MyRenderer)
+>>> print w.render('beatle', 'G', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
+<label><input type="radio" name="beatle" value="J" /> John</label><br />
+<label><input type="radio" name="beatle" value="P" /> Paul</label><br />
+<label><input checked="checked" type="radio" name="beatle" value="G" /> George</label><br />
+<label><input type="radio" name="beatle" value="R" /> Ringo</label>
+
A RadioFieldRenderer object also allows index access to individual RadioInput
objects.
>>> w = RadioSelect()
->>> r = w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
+>>> r = w.get_renderer('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
>>> print r[1]
<label><input type="radio" name="beatle" value="P" /> Paul</label>
>>> print r[0]
@@ -896,7 +910,7 @@ u'1234567890'
>>> f.clean('1234567890a')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at most 10 characters.']
+ValidationError: [u'Ensure this value has at most 10 characters (it has 11).']
CharField accepts an optional min_length parameter:
>>> f = CharField(min_length=10, required=False)
@@ -905,7 +919,7 @@ u''
>>> f.clean('12345')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at least 10 characters.']
+ValidationError: [u'Ensure this value has at least 10 characters (it has 5).']
>>> f.clean('1234567890')
u'1234567890'
>>> f.clean('1234567890a')
@@ -919,7 +933,7 @@ ValidationError: [u'This field is required.']
>>> f.clean('12345')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at least 10 characters.']
+ValidationError: [u'Ensure this value has at least 10 characters (it has 5).']
>>> f.clean('1234567890')
u'1234567890'
>>> f.clean('1234567890a')
@@ -1176,6 +1190,10 @@ ValidationError: [u'Ensure this value is greater than or equal to 0.5.']
Decimal("1.5")
>>> f.clean('0.5')
Decimal("0.5")
+>>> f.clean('.5')
+Decimal("0.5")
+>>> f.clean('00.50')
+Decimal("0.50")
# DateField ###################################################################
@@ -1439,11 +1457,11 @@ RegexField also access min_length and max_length parameters, for convenience.
>>> f.clean('123')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at least 5 characters.']
+ValidationError: [u'Ensure this value has at least 5 characters (it has 3).']
>>> f.clean('abc')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at least 5 characters.']
+ValidationError: [u'Ensure this value has at least 5 characters (it has 3).']
>>> f.clean('12345')
u'12345'
>>> f.clean('1234567890')
@@ -1451,7 +1469,7 @@ u'1234567890'
>>> f.clean('12345678901')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at most 10 characters.']
+ValidationError: [u'Ensure this value has at most 10 characters (it has 11).']
>>> f.clean('12345a')
Traceback (most recent call last):
...
@@ -1508,13 +1526,49 @@ EmailField also access min_length and max_length parameters, for convenience.
>>> f.clean('a@foo.com')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at least 10 characters.']
+ValidationError: [u'Ensure this value has at least 10 characters (it has 9).']
>>> f.clean('alf@foo.com')
u'alf@foo.com'
>>> f.clean('alf123456788@foo.com')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at most 15 characters.']
+ValidationError: [u'Ensure this value has at most 15 characters (it has 20).']
+
+# FileField ##################################################################
+
+>>> f = FileField()
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+
+>>> f.clean(None)
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+
+>>> f.clean({})
+Traceback (most recent call last):
+...
+ValidationError: [u'No file was submitted.']
+
+>>> f.clean('some content that is not a file')
+Traceback (most recent call last):
+...
+ValidationError: [u'No file was submitted. Check the encoding type on the form.']
+
+>>> f.clean({'filename': 'name', 'content':None})
+Traceback (most recent call last):
+...
+ValidationError: [u'The submitted file is empty.']
+
+>>> f.clean({'filename': 'name', 'content':''})
+Traceback (most recent call last):
+...
+ValidationError: [u'The submitted file is empty.']
+
+>>> type(f.clean({'filename': 'name', 'content':'Some File Content'}))
+<class 'django.newforms.fields.UploadedFile'>
# URLField ##################################################################
@@ -1618,13 +1672,13 @@ URLField also access min_length and max_length parameters, for convenience.
>>> f.clean('http://f.com')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at least 15 characters.']
+ValidationError: [u'Ensure this value has at least 15 characters (it has 12).']
>>> f.clean('http://example.com')
u'http://example.com'
>>> f.clean('http://abcdefghijklmnopqrstuvwxyz.com')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at most 20 characters.']
+ValidationError: [u'Ensure this value has at most 20 characters (it has 37).']
# BooleanField ################################################################
@@ -1796,7 +1850,7 @@ u'test@example.com'
>>> f.clean('longemailaddress@example.com')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at most 20 characters.']
+ValidationError: [u'Ensure this value has at most 20 characters (it has 28).']
>>> f.clean('not an e-mail')
Traceback (most recent call last):
...
@@ -1816,7 +1870,7 @@ u'test@example.com'
>>> f.clean('longemailaddress@example.com')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at most 20 characters.']
+ValidationError: [u'Ensure this value has at most 20 characters (it has 28).']
>>> f.clean('not an e-mail')
Traceback (most recent call last):
...
@@ -2557,7 +2611,7 @@ Instances of a dynamic Form do not persist fields from one Form instance to
the next.
>>> class MyForm(Form):
... def __init__(self, data=None, auto_id=False, field_list=[]):
-... Form.__init__(self, data, auto_id)
+... Form.__init__(self, data, auto_id=auto_id)
... for field in field_list:
... self.fields[field[0]] = field[1]
>>> field_list = [('field1', CharField()), ('field2', CharField())]
@@ -2575,7 +2629,7 @@ the next.
... default_field_1 = CharField()
... default_field_2 = CharField()
... def __init__(self, data=None, auto_id=False, field_list=[]):
-... Form.__init__(self, data, auto_id)
+... Form.__init__(self, data, auto_id=auto_id)
... for field in field_list:
... self.fields[field[0]] = field[1]
>>> field_list = [('field1', CharField()), ('field2', CharField())]
@@ -3230,6 +3284,35 @@ is different than its data. This is handled transparently, though.
<option value="3" selected="selected">No</option>
</select>
+# Forms with FileFields ################################################
+
+FileFields are a special case because they take their data from the request.FILES,
+not request.POST.
+
+>>> class FileForm(Form):
+... file1 = FileField()
+>>> f = FileForm(auto_id=False)
+>>> print f
+<tr><th>File1:</th><td><input type="file" name="file1" /></td></tr>
+
+>>> f = FileForm(data={}, files={}, auto_id=False)
+>>> print f
+<tr><th>File1:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="file" name="file1" /></td></tr>
+
+>>> f = FileForm(data={}, files={'file1': {'filename': 'name', 'content':''}}, auto_id=False)
+>>> print f
+<tr><th>File1:</th><td><ul class="errorlist"><li>The submitted file is empty.</li></ul><input type="file" name="file1" /></td></tr>
+
+>>> f = FileForm(data={}, files={'file1': 'something that is not a file'}, auto_id=False)
+>>> print f
+<tr><th>File1:</th><td><ul class="errorlist"><li>No file was submitted. Check the encoding type on the form.</li></ul><input type="file" name="file1" /></td></tr>
+
+>>> f = FileForm(data={}, files={'file1': {'filename': 'name', 'content':'some content'}}, auto_id=False)
+>>> print f
+<tr><th>File1:</th><td><input type="file" name="file1" /></td></tr>
+>>> f.is_valid()
+True
+
# Basic form processing in a view #############################################
>>> from django.template import Template, Context
@@ -3267,7 +3350,7 @@ Case 2: POST with erroneous data (a redisplayed form, with errors).
<form action="" method="post">
<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.</li></ul><input type="text" name="username" value="this-is-a-long-username" maxlength="10" /></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>
</table>
@@ -3374,7 +3457,7 @@ does not have help text, nothing will be output.
<input type="submit" />
</form>
>>> Template('{{ form.password1.help_text }}').render(Context({'form': UserRegistration(auto_id=False)}))
-''
+u''
The label_tag() method takes an optional attrs argument: a dictionary of HTML
attributes to add to the <label> tag.
@@ -3552,6 +3635,29 @@ True
<option value="2016">2016</option>
</select>
+Using a SelectDateWidget in a form:
+
+>>> class GetDate(Form):
+... mydate = DateField(widget=SelectDateWidget)
+>>> a = GetDate({'mydate_month':'4', 'mydate_day':'1', 'mydate_year':'2008'})
+>>> print a.is_valid()
+True
+>>> print a.cleaned_data['mydate']
+2008-04-01
+
+As with any widget that implements get_value_from_datadict,
+we must be prepared to accept the input from the "as_hidden"
+rendering as well.
+
+>>> print a['mydate'].as_hidden()
+<input type="hidden" name="mydate" value="2008-4-1" id="id_mydate" />
+>>> b=GetDate({'mydate':'2008-4-1'})
+>>> print b.is_valid()
+True
+>>> print b.cleaned_data['mydate']
+2008-04-01
+
+
# MultiWidget and MultiValueField #############################################
# MultiWidgets are widgets composed of other widgets. They are usually
# combined with MultiValueFields - a field that is composed of other fields.
diff --git a/tests/regressiontests/httpwrappers/tests.py b/tests/regressiontests/httpwrappers/tests.py
index e7245104e9..9d5ca2006a 100644
--- a/tests/regressiontests/httpwrappers/tests.py
+++ b/tests/regressiontests/httpwrappers/tests.py
@@ -94,7 +94,7 @@ MultiValueDictKeyError: "Key 'foo' not found in <MultiValueDict: {}>"
>>> q['name'] = 'john'
>>> q['name']
-'john'
+u'john'
>>> del q['name']
>>> 'name' in q
@@ -106,10 +106,10 @@ False
'default'
>>> q.get('name', 'default')
-'john'
+u'john'
>>> q.getlist('name')
-['john']
+[u'john']
>>> q.getlist('foo')
[]
@@ -117,18 +117,18 @@ False
>>> q.setlist('foo', ['bar', 'baz'])
>>> q.get('foo', 'default')
-'baz'
+u'baz'
>>> q.getlist('foo')
-['bar', 'baz']
+[u'bar', u'baz']
>>> q.appendlist('foo', 'another')
>>> q.getlist('foo')
-['bar', 'baz', 'another']
+[u'bar', u'baz', u'another']
>>> q['foo']
-'another'
+u'another'
>>> q.has_key('foo')
True
@@ -137,16 +137,16 @@ True
True
>>> q.items()
-[('foo', 'another'), ('name', 'john')]
+[(u'foo', u'another'), (u'name', u'john')]
>>> q.lists()
-[('foo', ['bar', 'baz', 'another']), ('name', ['john'])]
+[(u'foo', [u'bar', u'baz', u'another']), (u'name', [u'john'])]
>>> q.keys()
-['foo', 'name']
+[u'foo', u'name']
>>> q.values()
-['another', 'john']
+[u'another', u'john']
>>> len(q)
2
@@ -155,16 +155,16 @@ True
# Displays last value
>>> q['foo']
-'hello'
+u'hello'
>>> q.get('foo', 'not available')
-'hello'
+u'hello'
>>> q.getlist('foo')
-['bar', 'baz', 'another', 'hello']
+[u'bar', u'baz', u'another', u'hello']
>>> q.pop('foo')
-['bar', 'baz', 'another', 'hello']
+[u'bar', u'baz', u'another', u'hello']
>>> q.pop('foo', 'not there')
'not there'
@@ -173,13 +173,13 @@ True
'not there'
>>> q.setdefault('foo', 'bar')
-'bar'
+u'bar'
>>> q['foo']
-'bar'
+u'bar'
>>> q.getlist('foo')
-['bar']
+[u'bar']
>>> q.urlencode()
'foo=bar&name=john'
@@ -196,12 +196,12 @@ True
>>> q = QueryDict('foo=bar')
>>> q['foo']
-'bar'
+u'bar'
>>> q['bar']
Traceback (most recent call last):
...
-MultiValueDictKeyError: "Key 'bar' not found in <MultiValueDict: {'foo': ['bar']}>"
+MultiValueDictKeyError: "Key 'bar' not found in <MultiValueDict: {u'foo': [u'bar']}>"
>>> q['something'] = 'bar'
Traceback (most recent call last):
@@ -209,13 +209,13 @@ Traceback (most recent call last):
AttributeError: This QueryDict instance is immutable
>>> q.get('foo', 'default')
-'bar'
+u'bar'
>>> q.get('bar', 'default')
'default'
>>> q.getlist('foo')
-['bar']
+[u'bar']
>>> q.getlist('bar')
[]
@@ -243,16 +243,16 @@ False
False
>>> q.items()
-[('foo', 'bar')]
+[(u'foo', u'bar')]
>>> q.lists()
-[('foo', ['bar'])]
+[(u'foo', [u'bar'])]
>>> q.keys()
-['foo']
+[u'foo']
>>> q.values()
-['bar']
+[u'bar']
>>> len(q)
1
@@ -292,7 +292,7 @@ AttributeError: This QueryDict instance is immutable
>>> q = QueryDict('vote=yes&vote=no')
>>> q['vote']
-'no'
+u'no'
>>> q['something'] = 'bar'
Traceback (most recent call last):
@@ -300,13 +300,13 @@ Traceback (most recent call last):
AttributeError: This QueryDict instance is immutable
>>> q.get('vote', 'default')
-'no'
+u'no'
>>> q.get('foo', 'default')
'default'
>>> q.getlist('vote')
-['yes', 'no']
+[u'yes', u'no']
>>> q.getlist('foo')
[]
@@ -334,16 +334,16 @@ False
False
>>> q.items()
-[('vote', 'no')]
+[(u'vote', u'no')]
>>> q.lists()
-[('vote', ['yes', 'no'])]
+[(u'vote', [u'yes', u'no'])]
>>> q.keys()
-['vote']
+[u'vote']
>>> q.values()
-['no']
+[u'no']
>>> len(q)
1
@@ -381,6 +381,16 @@ Traceback (most recent call last):
...
AttributeError: This QueryDict instance is immutable
+# QueryDicts must be able to handle invalid input encoding (in this case, bad
+# UTF-8 encoding).
+>>> q = QueryDict('foo=bar&foo=\xff')
+
+>>> q['foo']
+u'\ufffd'
+
+>>> q.getlist('foo')
+[u'bar', u'\ufffd']
+
"""
from django.http import QueryDict
diff --git a/tests/regressiontests/humanize/tests.py b/tests/regressiontests/humanize/tests.py
index eca65f7575..196488ba6e 100644
--- a/tests/regressiontests/humanize/tests.py
+++ b/tests/regressiontests/humanize/tests.py
@@ -1,5 +1,8 @@
import unittest
+from datetime import timedelta, date
from django.template import Template, Context, add_to_builtins
+from django.utils.dateformat import DateFormat
+from django.utils.translation import ugettext as _
add_to_builtins('django.contrib.humanize.templatetags.humanize')
@@ -8,14 +11,13 @@ class HumanizeTests(unittest.TestCase):
def humanize_tester(self, test_list, result_list, method):
# Using max below ensures we go through both lists
# However, if the lists are not equal length, this raises an exception
- for index in xrange(len(max(test_list,result_list))):
+ for index in xrange(max(len(test_list), len(result_list))):
test_content = test_list[index]
t = Template('{{ test_content|%s }}' % method)
rendered = t.render(Context(locals())).strip()
self.assertEqual(rendered, result_list[index],
- msg="""%s test failed, produced %s,
-should've produced %s""" % (method, rendered, result_list[index]))
-
+ msg="%s test failed, produced %s, should've produced %s" % (method, rendered, result_list[index]))
+
def test_ordinal(self):
test_list = ('1','2','3','4','11','12',
'13','101','102','103','111',
@@ -43,12 +45,26 @@ should've produced %s""" % (method, rendered, result_list[index]))
self.humanize_tester(test_list, result_list, 'intword')
def test_apnumber(self):
- test_list = [str(x) for x in xrange(1,11)]
- result_list = ('one', 'two', 'three', 'four', 'five', 'six',
- 'seven', 'eight', 'nine', '10')
+ test_list = [str(x) for x in range(1, 11)]
+ result_list = (u'one', u'two', u'three', u'four', u'five', u'six',
+ u'seven', u'eight', u'nine', u'10')
self.humanize_tester(test_list, result_list, 'apnumber')
+ def test_naturalday(self):
+ from django.template import defaultfilters
+ today = date.today()
+ yesterday = today - timedelta(days=1)
+ tomorrow = today + timedelta(days=1)
+ someday = today - timedelta(days=10)
+ notdate = u"I'm not a date value"
+
+ test_list = (today, yesterday, tomorrow, someday, notdate)
+ someday_result = defaultfilters.date(someday)
+ result_list = (_(u'today'), _(u'yesterday'), _(u'tomorrow'),
+ someday_result, u"I'm not a date value")
+ self.humanize_tester(test_list, result_list, 'naturalday')
+
if __name__ == '__main__':
unittest.main()
-
+
diff --git a/tests/regressiontests/i18n/__init__.py b/tests/regressiontests/i18n/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/regressiontests/i18n/__init__.py
diff --git a/tests/regressiontests/i18n/models.py b/tests/regressiontests/i18n/models.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/regressiontests/i18n/models.py
diff --git a/tests/regressiontests/i18n/tests.py b/tests/regressiontests/i18n/tests.py
new file mode 100644
index 0000000000..8a7d2bee3e
--- /dev/null
+++ b/tests/regressiontests/i18n/tests.py
@@ -0,0 +1,33 @@
+# coding: utf-8
+
+ur"""
+Format string interpolation should work with *_lazy objects.
+
+>>> from django.utils.translation import ugettext_lazy, activate, deactivate, gettext_lazy
+>>> s = ugettext_lazy('Add %(name)s')
+>>> d = {'name': 'Ringo'}
+>>> s % d
+u'Add Ringo'
+>>> activate('de')
+>>> s % d
+u'Ringo hinzuf\xfcgen'
+>>> activate('pl')
+>>> s % d
+u'Dodaj Ringo'
+>>> deactivate()
+
+It should be possible to compare *_lazy objects.
+
+>>> s1 = ugettext_lazy('Add %(name)s')
+>>> s == s1
+True
+>>> s2 = gettext_lazy('Add %(name)s')
+>>> s3 = gettext_lazy('Add %(name)s')
+>>> s2 == s3
+True
+>>> s == s2
+True
+>>> s4 = ugettext_lazy('Some other string')
+>>> s == s4
+False
+"""
diff --git a/tests/regressiontests/initial_sql_regress/models.py b/tests/regressiontests/initial_sql_regress/models.py
index dedbba8e5c..7cf725991a 100644
--- a/tests/regressiontests/initial_sql_regress/models.py
+++ b/tests/regressiontests/initial_sql_regress/models.py
@@ -5,7 +5,7 @@ Regression tests for initial SQL insertion.
from django.db import models
class Simple(models.Model):
- name = models.CharField(maxlength = 50)
+ name = models.CharField(max_length = 50)
__test__ = {'API_TESTS':""}
diff --git a/tests/regressiontests/invalid_admin_options/models.py b/tests/regressiontests/invalid_admin_options/models.py
index 43bcc533ba..14db463735 100644
--- a/tests/regressiontests/invalid_admin_options/models.py
+++ b/tests/regressiontests/invalid_admin_options/models.py
@@ -12,7 +12,7 @@ model_errors = ""
##This should fail gracefully but is causing a metaclass error
#class BadAdminOption(models.Model):
# "Test nonexistent admin option"
-# name = models.CharField(maxlength=30)
+# name = models.CharField(max_length=30)
#
# class Admin:
# nonexistent = 'option'
@@ -22,7 +22,7 @@ model_errors = ""
class ListDisplayBadOne(models.Model):
"Test list_display, list_display must be a list or tuple"
- first_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
class Admin:
list_display = 'first_name'
@@ -32,7 +32,7 @@ model_errors += """invalid_admin_options.listdisplaybadone: "admin.list_display"
class ListDisplayBadTwo(models.Model):
"Test list_display, list_display items must be attributes, methods or properties."
- first_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
class Admin:
list_display = ['first_name','nonexistent']
@@ -41,7 +41,7 @@ model_errors += """invalid_admin_options.listdisplaybadtwo: "admin.list_display"
"""
class ListDisplayBadThree(models.Model):
"Test list_display, list_display items can not be a ManyToManyField."
- first_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
nick_names = models.ManyToManyField('ListDisplayGood')
class Admin:
@@ -52,7 +52,7 @@ model_errors += """invalid_admin_options.listdisplaybadthree: "admin.list_displa
class ListDisplayGood(models.Model):
"Test list_display, Admin list_display can be a attribute, method or property."
- first_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
def _last_name(self):
return self.first_name
@@ -66,8 +66,8 @@ class ListDisplayGood(models.Model):
class ListDisplayLinksBadOne(models.Model):
"Test list_display_links, item must be included in list_display."
- first_name = models.CharField(maxlength=30)
- last_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
+ last_name = models.CharField(max_length=30)
class Admin:
list_display = ['last_name']
@@ -78,8 +78,8 @@ model_errors += """invalid_admin_options.listdisplaylinksbadone: "admin.list_dis
class ListDisplayLinksBadTwo(models.Model):
"Test list_display_links, must be a list or tuple."
- first_name = models.CharField(maxlength=30)
- last_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
+ last_name = models.CharField(max_length=30)
class Admin:
list_display = ['first_name','last_name']
@@ -92,8 +92,8 @@ model_errors += """invalid_admin_options.listdisplaylinksbadtwo: "admin.list_dis
## This is failing but the validation which should fail is not.
#class ListDisplayLinksBadThree(models.Model):
# "Test list_display_links, must define list_display to use list_display_links."
-# first_name = models.CharField(maxlength=30)
-# last_name = models.CharField(maxlength=30)
+# first_name = models.CharField(max_length=30)
+# last_name = models.CharField(max_length=30)
#
# class Admin:
# list_display_links = ('first_name',)
@@ -103,7 +103,7 @@ model_errors += """invalid_admin_options.listdisplaylinksbadtwo: "admin.list_dis
class ListDisplayLinksGood(models.Model):
"Test list_display_links, Admin list_display_list can be a attribute, method or property."
- first_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
def _last_name(self):
return self.first_name
@@ -118,7 +118,7 @@ class ListDisplayLinksGood(models.Model):
class ListFilterBadOne(models.Model):
"Test list_filter, must be a list or tuple."
- first_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
class Admin:
list_filter = 'first_name'
@@ -128,7 +128,7 @@ model_errors += """invalid_admin_options.listfilterbadone: "admin.list_filter",
class ListFilterBadTwo(models.Model):
"Test list_filter, must be a field not a property or method."
- first_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
def _last_name(self):
return self.first_name
@@ -146,7 +146,7 @@ invalid_admin_options.listfilterbadtwo: "admin.list_filter" refers to 'full_name
class DateHierarchyBadOne(models.Model):
"Test date_hierarchy, must be a date or datetime field."
- first_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
birth_day = models.DateField()
class Admin:
@@ -158,7 +158,7 @@ class DateHierarchyBadOne(models.Model):
class DateHierarchyBadTwo(models.Model):
"Test date_hieracrhy, must be a field."
- first_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
birth_day = models.DateField()
class Admin:
@@ -169,7 +169,7 @@ model_errors += """invalid_admin_options.datehierarchybadtwo: "admin.date_hierar
class DateHierarchyGood(models.Model):
"Test date_hieracrhy, must be a field."
- first_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
birth_day = models.DateField()
class Admin:
@@ -177,7 +177,7 @@ class DateHierarchyGood(models.Model):
class SearchFieldsBadOne(models.Model):
"Test search_fields, must be a list or tuple."
- first_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
class Admin:
search_fields = ('nonexistent')
@@ -188,7 +188,7 @@ class SearchFieldsBadOne(models.Model):
class SearchFieldsBadTwo(models.Model):
"Test search_fields, must be a field."
- first_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
def _last_name(self):
return self.first_name
@@ -203,8 +203,8 @@ class SearchFieldsBadTwo(models.Model):
class SearchFieldsGood(models.Model):
"Test search_fields, must be a list or tuple."
- first_name = models.CharField(maxlength=30)
- last_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
+ last_name = models.CharField(max_length=30)
class Admin:
search_fields = ['first_name','last_name']
@@ -212,7 +212,7 @@ class SearchFieldsGood(models.Model):
class JsBadOne(models.Model):
"Test js, must be a list or tuple"
- name = models.CharField(maxlength=30)
+ name = models.CharField(max_length=30)
class Admin:
js = 'test.js'
@@ -223,7 +223,7 @@ class JsBadOne(models.Model):
class SaveAsBad(models.Model):
"Test save_as, should be True or False"
- name = models.CharField(maxlength=30)
+ name = models.CharField(max_length=30)
class Admin:
save_as = 'not True or False'
@@ -234,7 +234,7 @@ class SaveAsBad(models.Model):
class SaveOnTopBad(models.Model):
"Test save_on_top, should be True or False"
- name = models.CharField(maxlength=30)
+ name = models.CharField(max_length=30)
class Admin:
save_on_top = 'not True or False'
@@ -245,7 +245,7 @@ class SaveOnTopBad(models.Model):
class ListSelectRelatedBad(models.Model):
"Test list_select_related, should be True or False"
- name = models.CharField(maxlength=30)
+ name = models.CharField(max_length=30)
class Admin:
list_select_related = 'not True or False'
@@ -256,7 +256,7 @@ class ListSelectRelatedBad(models.Model):
class ListPerPageBad(models.Model):
"Test list_per_page, should be a positive integer value."
- name = models.CharField(maxlength=30)
+ name = models.CharField(max_length=30)
class Admin:
list_per_page = 89.3
@@ -267,8 +267,8 @@ class ListPerPageBad(models.Model):
class FieldsBadOne(models.Model):
"Test fields, should be a tuple"
- first_name = models.CharField(maxlength=30)
- last_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
+ last_name = models.CharField(max_length=30)
class Admin:
fields = 'not a tuple'
@@ -279,8 +279,8 @@ class FieldsBadOne(models.Model):
class FieldsBadTwo(models.Model):
"""Test fields, 'fields' dict option is required."""
- first_name = models.CharField(maxlength=30)
- last_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
+ last_name = models.CharField(max_length=30)
class Admin:
fields = ('Name', {'description': 'this fieldset needs fields'})
@@ -291,8 +291,8 @@ class FieldsBadTwo(models.Model):
class FieldsBadThree(models.Model):
"""Test fields, 'classes' and 'description' are the only allowable extra dict options."""
- first_name = models.CharField(maxlength=30)
- last_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
+ last_name = models.CharField(max_length=30)
class Admin:
fields = ('Name', {'fields': ('first_name','last_name'),'badoption': 'verybadoption'})
@@ -303,8 +303,8 @@ class FieldsBadThree(models.Model):
class FieldsGood(models.Model):
"Test fields, working example"
- first_name = models.CharField(maxlength=30)
- last_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
+ last_name = models.CharField(max_length=30)
birth_day = models.DateField()
class Admin:
@@ -315,8 +315,8 @@ class FieldsGood(models.Model):
class OrderingBad(models.Model):
"Test ordering, must be a field."
- first_name = models.CharField(maxlength=30)
- last_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
+ last_name = models.CharField(max_length=30)
class Admin:
ordering = 'nonexistent'
@@ -328,7 +328,7 @@ class OrderingBad(models.Model):
## TODO: Add a manager validator, this should fail gracefully.
#class ManagerBad(models.Model):
# "Test manager, must be a manager object."
-# first_name = models.CharField(maxlength=30)
+# first_name = models.CharField(max_length=30)
#
# class Admin:
# manager = 'nonexistent'
diff --git a/tests/regressiontests/many_to_one_regress/models.py b/tests/regressiontests/many_to_one_regress/models.py
index 8ddec98da4..57bbcd8489 100644
--- a/tests/regressiontests/many_to_one_regress/models.py
+++ b/tests/regressiontests/many_to_one_regress/models.py
@@ -12,15 +12,15 @@ class Second(models.Model):
# Protect against repetition of #1839, #2415 and #2536.
class Third(models.Model):
- name = models.CharField(maxlength=20)
+ name = models.CharField(max_length=20)
third = models.ForeignKey('self', null=True, related_name='child_set')
class Parent(models.Model):
- name = models.CharField(maxlength=20)
+ name = models.CharField(max_length=20)
bestchild = models.ForeignKey('Child', null=True, related_name='favored_by')
class Child(models.Model):
- name = models.CharField(maxlength=20)
+ name = models.CharField(max_length=20)
parent = models.ForeignKey(Parent)
diff --git a/tests/regressiontests/maxlength/__init__.py b/tests/regressiontests/maxlength/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/regressiontests/maxlength/__init__.py
diff --git a/tests/regressiontests/maxlength/models.py b/tests/regressiontests/maxlength/models.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/regressiontests/maxlength/models.py
diff --git a/tests/regressiontests/maxlength/tests.py b/tests/regressiontests/maxlength/tests.py
new file mode 100644
index 0000000000..8a5f874c78
--- /dev/null
+++ b/tests/regressiontests/maxlength/tests.py
@@ -0,0 +1,160 @@
+# Test access to max_length while still providing full backwards compatibility
+# with legacy maxlength attribute.
+"""
+
+Don't print out the deprecation warnings during testing.
+>>> from warnings import filterwarnings
+>>> filterwarnings("ignore")
+
+# legacy_maxlength function
+
+>>> from django.utils.maxlength import legacy_maxlength
+
+>>> legacy_maxlength(None, None)
+
+
+>>> legacy_maxlength(10, None)
+10
+
+>>> legacy_maxlength(None, 10)
+10
+
+>>> legacy_maxlength(10, 12)
+Traceback (most recent call last):
+...
+TypeError: field can not take both the max_length argument and the legacy maxlength argument.
+
+>>> legacy_maxlength(0, 10)
+Traceback (most recent call last):
+...
+TypeError: field can not take both the max_length argument and the legacy maxlength argument.
+
+>>> legacy_maxlength(0, None)
+0
+
+>>> legacy_maxlength(None, 0)
+0
+
+#===============================================================================
+# Fields
+#===============================================================================
+
+# Set up fields
+>>> from django.db.models import fields
+>>> new = fields.Field(max_length=15)
+>>> old = fields.Field(maxlength=10)
+
+# Ensure both max_length and legacy maxlength are not able to both be specified
+>>> fields.Field(maxlength=10, max_length=15)
+Traceback (most recent call last):
+ ...
+TypeError: field can not take both the max_length argument and the legacy maxlength argument.
+
+# Test max_length
+>>> new.max_length
+15
+>>> old.max_length
+10
+
+# Test accessing maxlength
+>>> new.maxlength
+15
+>>> old.maxlength
+10
+
+# Test setting maxlength
+>>> new.maxlength += 1
+>>> old.maxlength += 1
+>>> new.max_length
+16
+>>> old.max_length
+11
+
+# SlugField __init__ passes through max_length so test that too
+>>> fields.SlugField('new', max_length=15).max_length
+15
+>>> fields.SlugField('empty').max_length
+50
+>>> fields.SlugField('old', maxlength=10).max_length
+10
+
+#===============================================================================
+# (old)forms
+#===============================================================================
+
+>>> from django import oldforms
+
+# Test max_length attribute
+
+>>> oldforms.TextField('new', max_length=15).render('')
+u'<input type="text" id="id_new" class="vTextField" name="new" size="30" value="" maxlength="15" />'
+
+>>> oldforms.IntegerField('new', max_length=15).render('')
+u'<input type="text" id="id_new" class="vIntegerField" name="new" size="10" value="" maxlength="15" />'
+
+>>> oldforms.SmallIntegerField('new', max_length=15).render('')
+u'<input type="text" id="id_new" class="vSmallIntegerField" name="new" size="5" value="" maxlength="15" />'
+
+>>> oldforms.PositiveIntegerField('new', max_length=15).render('')
+u'<input type="text" id="id_new" class="vPositiveIntegerField" name="new" size="10" value="" maxlength="15" />'
+
+>>> oldforms.PositiveSmallIntegerField('new', max_length=15).render('')
+u'<input type="text" id="id_new" class="vPositiveSmallIntegerField" name="new" size="5" value="" maxlength="15" />'
+
+>>> oldforms.DatetimeField('new', max_length=15).render('')
+u'<input type="text" id="id_new" class="vDatetimeField" name="new" size="30" value="" maxlength="15" />'
+
+>>> oldforms.EmailField('new', max_length=15).render('')
+u'<input type="text" id="id_new" class="vEmailField" name="new" size="50" value="" maxlength="15" />'
+>>> oldforms.EmailField('new').render('')
+u'<input type="text" id="id_new" class="vEmailField" name="new" size="50" value="" maxlength="75" />'
+
+>>> oldforms.URLField('new', max_length=15).render('')
+u'<input type="text" id="id_new" class="vURLField" name="new" size="50" value="" maxlength="15" />'
+>>> oldforms.URLField('new').render('')
+u'<input type="text" id="id_new" class="vURLField" name="new" size="50" value="" maxlength="200" />'
+
+>>> oldforms.IPAddressField('new', max_length=15).render('')
+u'<input type="text" id="id_new" class="vIPAddressField" name="new" size="15" value="" maxlength="15" />'
+>>> oldforms.IPAddressField('new').render('')
+u'<input type="text" id="id_new" class="vIPAddressField" name="new" size="15" value="" maxlength="15" />'
+
+>>> oldforms.CommaSeparatedIntegerField('new', max_length=15).render('')
+u'<input type="text" id="id_new" class="vCommaSeparatedIntegerField" name="new" size="20" value="" maxlength="15" />'
+
+
+# Test legacy maxlength attribute
+
+>>> oldforms.TextField('old', maxlength=10).render('')
+u'<input type="text" id="id_old" class="vTextField" name="old" size="30" value="" maxlength="10" />'
+
+>>> oldforms.IntegerField('old', maxlength=10).render('')
+u'<input type="text" id="id_old" class="vIntegerField" name="old" size="10" value="" maxlength="10" />'
+
+>>> oldforms.SmallIntegerField('old', maxlength=10).render('')
+u'<input type="text" id="id_old" class="vSmallIntegerField" name="old" size="5" value="" maxlength="10" />'
+
+>>> oldforms.PositiveIntegerField('old', maxlength=10).render('')
+u'<input type="text" id="id_old" class="vPositiveIntegerField" name="old" size="10" value="" maxlength="10" />'
+
+>>> oldforms.PositiveSmallIntegerField('old', maxlength=10).render('')
+u'<input type="text" id="id_old" class="vPositiveSmallIntegerField" name="old" size="5" value="" maxlength="10" />'
+
+>>> oldforms.DatetimeField('old', maxlength=10).render('')
+u'<input type="text" id="id_old" class="vDatetimeField" name="old" size="30" value="" maxlength="10" />'
+
+>>> oldforms.EmailField('old', maxlength=10).render('')
+u'<input type="text" id="id_old" class="vEmailField" name="old" size="50" value="" maxlength="10" />'
+
+>>> oldforms.URLField('old', maxlength=10).render('')
+u'<input type="text" id="id_old" class="vURLField" name="old" size="50" value="" maxlength="10" />'
+
+>>> oldforms.IPAddressField('old', maxlength=10).render('')
+u'<input type="text" id="id_old" class="vIPAddressField" name="old" size="15" value="" maxlength="10" />'
+
+>>> oldforms.CommaSeparatedIntegerField('old', maxlength=10).render('')
+u'<input type="text" id="id_old" class="vCommaSeparatedIntegerField" name="old" size="20" value="" maxlength="10" />'
+"""
+if __name__ == "__main__":
+ import doctest
+ doctest.testmod()
diff --git a/tests/regressiontests/model_fields/__init__.py b/tests/regressiontests/model_fields/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/regressiontests/model_fields/__init__.py
diff --git a/tests/regressiontests/model_fields/models.py b/tests/regressiontests/model_fields/models.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/regressiontests/model_fields/models.py
diff --git a/tests/regressiontests/model_fields/tests.py b/tests/regressiontests/model_fields/tests.py
new file mode 100644
index 0000000000..e279a0669f
--- /dev/null
+++ b/tests/regressiontests/model_fields/tests.py
@@ -0,0 +1,18 @@
+"""
+>>> from django.db.models.fields import *
+
+# DecimalField
+
+>>> f = DecimalField()
+
+>>> f.to_python(3)
+Decimal("3")
+
+>>> f.to_python("3.14")
+Decimal("3.14")
+
+>>> f.to_python("abc")
+Traceback (most recent call last):
+...
+ValidationError: [u'This value must be a decimal number.']
+"""
diff --git a/tests/regressiontests/model_regress/__init__.py b/tests/regressiontests/model_regress/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/regressiontests/model_regress/__init__.py
diff --git a/tests/regressiontests/model_regress/models.py b/tests/regressiontests/model_regress/models.py
new file mode 100644
index 0000000000..7aa9e2a7c4
--- /dev/null
+++ b/tests/regressiontests/model_regress/models.py
@@ -0,0 +1,40 @@
+# coding: utf-8
+from django.db import models
+
+CHOICES = (
+ (1, 'first'),
+ (2, 'second'),
+)
+
+class Article(models.Model):
+ headline = models.CharField(max_length=100, default='Default headline')
+ pub_date = models.DateTimeField()
+ status = models.IntegerField(blank=True, null=True, choices=CHOICES)
+ misc_data = models.CharField(max_length=100, blank=True)
+
+ class Meta:
+ ordering = ('pub_date','headline')
+ # A utf-8 verbose name (Ångström's Articles) to test they are valid.
+ verbose_name = "\xc3\x85ngstr\xc3\xb6m's Articles"
+
+ def __unicode__(self):
+ return self.headline
+
+__test__ = {'API_TESTS': """
+(NOTE: Part of the regression test here is merely parsing the model
+declaration. The verbose_name, in particular, did not always work.)
+
+An empty choice field should return None for the display name.
+
+>>> from datetime import datetime
+>>> a = Article(headline="Look at me!", pub_date=datetime.now())
+>>> a.save()
+>>> a.get_status_display() is None
+True
+
+Empty strings should be returned as Unicode
+>>> a2 = Article.objects.get(pk=a.id)
+>>> a2.misc_data
+u''
+"""
+}
diff --git a/tests/regressiontests/null_queries/models.py b/tests/regressiontests/null_queries/models.py
index 21944d9e7a..2aa36b2c1a 100644
--- a/tests/regressiontests/null_queries/models.py
+++ b/tests/regressiontests/null_queries/models.py
@@ -1,17 +1,17 @@
from django.db import models
class Poll(models.Model):
- question = models.CharField(maxlength=200)
+ question = models.CharField(max_length=200)
- def __str__(self):
- return "Q: %s " % self.question
+ def __unicode__(self):
+ return u"Q: %s " % self.question
class Choice(models.Model):
poll = models.ForeignKey(Poll)
- choice = models.CharField(maxlength=200)
+ choice = models.CharField(max_length=200)
- def __str__(self):
- return "Choice: %s in poll %s" % (self.choice, self.poll)
+ def __unicode__(self):
+ return u"Choice: %s in poll %s" % (self.choice, self.poll)
__test__ = {'API_TESTS':"""
# Regression test for the use of None as a query value. None is interpreted as
diff --git a/tests/regressiontests/one_to_one_regress/models.py b/tests/regressiontests/one_to_one_regress/models.py
index b81f4266e1..c5ffd3fb3c 100644
--- a/tests/regressiontests/one_to_one_regress/models.py
+++ b/tests/regressiontests/one_to_one_regress/models.py
@@ -1,26 +1,26 @@
from django.db import models
class Place(models.Model):
- name = models.CharField(maxlength=50)
- address = models.CharField(maxlength=80)
+ name = models.CharField(max_length=50)
+ address = models.CharField(max_length=80)
- def __str__(self):
- return "%s the place" % self.name
+ def __unicode__(self):
+ return u"%s the place" % self.name
class Restaurant(models.Model):
place = models.OneToOneField(Place)
serves_hot_dogs = models.BooleanField()
serves_pizza = models.BooleanField()
- def __str__(self):
- return "%s the restaurant" % self.place.name
+ def __unicode__(self):
+ return u"%s the restaurant" % self.place.name
class Favorites(models.Model):
- name = models.CharField(maxlength = 50)
+ name = models.CharField(max_length = 50)
restaurants = models.ManyToManyField(Restaurant)
- def __str__(self):
- return "Favorites for %s" % self.name
+ def __unicode__(self):
+ return u"Favorites for %s" % self.name
__test__ = {'API_TESTS':"""
# Regression test for #1064 and #1506: Check that we create models via the m2m
diff --git a/tests/regressiontests/serializers_regress/models.py b/tests/regressiontests/serializers_regress/models.py
index b441885f10..e9df508822 100644
--- a/tests/regressiontests/serializers_regress/models.py
+++ b/tests/regressiontests/serializers_regress/models.py
@@ -16,7 +16,7 @@ class BooleanData(models.Model):
data = models.BooleanField(null=True)
class CharData(models.Model):
- data = models.CharField(maxlength=30, null=True)
+ data = models.CharField(max_length=30, null=True)
class DateData(models.Model):
data = models.DateField(null=True)
@@ -90,7 +90,7 @@ class Tag(models.Model):
ordering = ["data"]
class GenericData(models.Model):
- data = models.CharField(maxlength=30)
+ data = models.CharField(max_length=30)
tags = generic.GenericRelation(Tag)
@@ -102,13 +102,13 @@ class Anchor(models.Model):
"""This is a model that can be used as
something for other models to point at"""
- data = models.CharField(maxlength=30)
+ data = models.CharField(max_length=30)
class UniqueAnchor(models.Model):
"""This is a model that can be used as
something for other models to point at"""
- data = models.CharField(unique=True, maxlength=30)
+ data = models.CharField(unique=True, max_length=30)
class FKData(models.Model):
data = models.ForeignKey(Anchor, null=True)
@@ -117,7 +117,8 @@ class M2MData(models.Model):
data = models.ManyToManyField(Anchor, null=True)
class O2OData(models.Model):
- data = models.OneToOneField(Anchor, null=True)
+ # One to one field can't be null, since it is a PK.
+ data = models.OneToOneField(Anchor)
class FKSelfData(models.Model):
data = models.ForeignKey('self', null=True)
@@ -143,7 +144,7 @@ class BooleanPKData(models.Model):
data = models.BooleanField(primary_key=True)
class CharPKData(models.Model):
- data = models.CharField(maxlength=30, primary_key=True)
+ data = models.CharField(max_length=30, primary_key=True)
# class DatePKData(models.Model):
# data = models.DateField(primary_key=True)
@@ -175,8 +176,9 @@ class IntegerPKData(models.Model):
class IPAddressPKData(models.Model):
data = models.IPAddressField(primary_key=True)
-class NullBooleanPKData(models.Model):
- data = models.NullBooleanField(primary_key=True)
+# This is just a Boolean field with null=True, and we can't test a PK value of NULL.
+# class NullBooleanPKData(models.Model):
+# data = models.NullBooleanField(primary_key=True)
class PhonePKData(models.Model):
data = models.PhoneNumberField(primary_key=True)
@@ -206,6 +208,19 @@ class USStatePKData(models.Model):
# data = models.XMLField(primary_key=True)
class ComplexModel(models.Model):
- field1 = models.CharField(maxlength=10)
- field2 = models.CharField(maxlength=10)
- field3 = models.CharField(maxlength=10)
+ field1 = models.CharField(max_length=10)
+ field2 = models.CharField(max_length=10)
+ field3 = models.CharField(max_length=10)
+
+# Tests for handling fields with pre_save functions, or
+# models with save functions that modify data
+class AutoNowDateTimeData(models.Model):
+ data = models.DateTimeField(null=True, auto_now=True)
+
+class ModifyingSaveData(models.Model):
+ data = models.IntegerField(null=True)
+
+ def save(self):
+ "A save method that modifies the data in the object"
+ self.data = 666
+ super(ModifyingSaveData, self).save(raw)
diff --git a/tests/regressiontests/serializers_regress/tests.py b/tests/regressiontests/serializers_regress/tests.py
index febcfa822e..24111308d7 100644
--- a/tests/regressiontests/serializers_regress/tests.py
+++ b/tests/regressiontests/serializers_regress/tests.py
@@ -24,17 +24,20 @@ except ImportError:
from django.utils import _decimal as decimal
# A set of functions that can be used to recreate
-# test data objects of various kinds
+# test data objects of various kinds.
+# The save method is a raw base model save, to make
+# sure that the data in the database matches the
+# exact test case.
def data_create(pk, klass, data):
instance = klass(id=pk)
instance.data = data
- instance.save()
+ models.Model.save(instance, raw=True)
return instance
def generic_create(pk, klass, data):
instance = klass(id=pk)
instance.data = data[0]
- instance.save()
+ models.Model.save(instance, raw=True)
for tag in data[1:]:
instance.tags.create(data=tag)
return instance
@@ -42,25 +45,25 @@ def generic_create(pk, klass, data):
def fk_create(pk, klass, data):
instance = klass(id=pk)
setattr(instance, 'data_id', data)
- instance.save()
+ models.Model.save(instance, raw=True)
return instance
def m2m_create(pk, klass, data):
instance = klass(id=pk)
- instance.save()
+ models.Model.save(instance, raw=True)
instance.data = data
return instance
def o2o_create(pk, klass, data):
instance = klass()
instance.data_id = data
- instance.save()
+ models.Model.save(instance, raw=True)
return instance
def pk_create(pk, klass, data):
instance = klass()
instance.data = data
- instance.save()
+ models.Model.save(instance, raw=True)
return instance
# A set of functions that can be used to compare
@@ -111,6 +114,9 @@ test_data = [
(data_obj, 13, CharData, "null"),
(data_obj, 14, CharData, "NULL"),
(data_obj, 15, CharData, None),
+ # (We use something that will fit into a latin1 database encoding here,
+ # because that is still the default used on many system setups.)
+ (data_obj, 16, CharData, u'\xa5'),
(data_obj, 20, DateData, datetime.date(2006,6,16)),
(data_obj, 21, DateData, None),
(data_obj, 30, DateTimeData, datetime.datetime(2006,6,16,10,42,37)),
@@ -230,8 +236,8 @@ The end."""),
(pk_obj, 682, IntegerPKData, 0),
# (XX, ImagePKData
(pk_obj, 690, IPAddressPKData, "127.0.0.1"),
- (pk_obj, 700, NullBooleanPKData, True),
- (pk_obj, 701, NullBooleanPKData, False),
+ # (pk_obj, 700, NullBooleanPKData, True),
+ # (pk_obj, 701, NullBooleanPKData, False),
(pk_obj, 710, PhonePKData, "212-634-5789"),
(pk_obj, 720, PositiveIntegerPKData, 123456789),
(pk_obj, 730, PositiveSmallIntegerPKData, 12),
@@ -246,6 +252,9 @@ The end."""),
# (pk_obj, 770, TimePKData, datetime.time(10,42,37)),
(pk_obj, 780, USStatePKData, "MA"),
# (pk_obj, 790, XMLPKData, "<foo></foo>"),
+
+ (data_obj, 800, AutoNowDateTimeData, datetime.datetime(2006,6,16,10,42,37)),
+ (data_obj, 810, ModifyingSaveData, 42),
]
# Because Oracle treats the empty string as NULL, Oracle is expected to fail
@@ -264,7 +273,7 @@ class SerializerTests(unittest.TestCase):
def serializerTest(format, self):
# Clear the database first
- management.flush(verbosity=0, interactive=False)
+ management.call_command('flush', verbosity=0, interactive=False)
# Create all the objects defined in the test data
objects = []
@@ -282,7 +291,7 @@ def serializerTest(format, self):
serialized_data = serializers.serialize(format, objects, indent=2)
# Flush the database and recreate from the serialized data
- management.flush(verbosity=0, interactive=False)
+ management.call_command('flush', verbosity=0, interactive=False)
transaction.enter_transaction_management()
transaction.managed(True)
for obj in serializers.deserialize(format, serialized_data):
@@ -297,10 +306,10 @@ def serializerTest(format, self):
def fieldsTest(format, self):
# Clear the database first
- management.flush(verbosity=0, interactive=False)
+ management.call_command('flush', verbosity=0, interactive=False)
obj = ComplexModel(field1='first',field2='second',field3='third')
- obj.save()
+ obj.save(raw=True)
# Serialize then deserialize the test database
serialized_data = serializers.serialize(format, [obj], indent=2, fields=('field1','field3'))
@@ -313,10 +322,10 @@ def fieldsTest(format, self):
def streamTest(format, self):
# Clear the database first
- management.flush(verbosity=0, interactive=False)
+ management.call_command('flush', verbosity=0, interactive=False)
obj = ComplexModel(field1='first',field2='second',field3='third')
- obj.save()
+ obj.save(raw=True)
# Serialize the test database to a stream
stream = StringIO()
diff --git a/tests/regressiontests/string_lookup/models.py b/tests/regressiontests/string_lookup/models.py
index 441bb3f8a3..12ebd0cf07 100644
--- a/tests/regressiontests/string_lookup/models.py
+++ b/tests/regressiontests/string_lookup/models.py
@@ -1,46 +1,55 @@
+# -*- coding: utf-8 -*-
from django.db import models
class Foo(models.Model):
- name = models.CharField(maxlength=50)
+ name = models.CharField(max_length=50)
+ friend = models.CharField(max_length=50, blank=True)
- def __str__(self):
+ def __unicode__(self):
return "Foo %s" % self.name
class Bar(models.Model):
- name = models.CharField(maxlength=50)
+ name = models.CharField(max_length=50)
normal = models.ForeignKey(Foo, related_name='normal_foo')
fwd = models.ForeignKey("Whiz")
back = models.ForeignKey("Foo")
- def __str__(self):
+ def __unicode__(self):
return "Bar %s" % self.place.name
class Whiz(models.Model):
- name = models.CharField(maxlength = 50)
+ name = models.CharField(max_length = 50)
- def __str__(self):
+ def __unicode__(self):
return "Whiz %s" % self.name
class Child(models.Model):
parent = models.OneToOneField('Base')
- name = models.CharField(maxlength = 50)
+ name = models.CharField(max_length = 50)
- def __str__(self):
+ def __unicode__(self):
return "Child %s" % self.name
-
+
class Base(models.Model):
+ name = models.CharField(max_length = 50)
+
+ def __unicode__(self):
+ return "Base %s" % self.name
+
+class Article(models.Model):
name = models.CharField(maxlength = 50)
+ text = models.TextField()
def __str__(self):
- return "Base %s" % self.name
+ return "Article %s" % self.name
-__test__ = {'API_TESTS':"""
-# Regression test for #1661 and #1662: Check that string form referencing of models works,
-# both as pre and post reference, on all RelatedField types.
+__test__ = {'API_TESTS': ur"""
+# Regression test for #1661 and #1662: Check that string form referencing of
+# models works, both as pre and post reference, on all RelatedField types.
>>> f1 = Foo(name="Foo1")
>>> f1.save()
->>> f2 = Foo(name="Foo1")
+>>> f2 = Foo(name="Foo2")
>>> f2.save()
>>> w1 = Whiz(name="Whiz1")
@@ -56,7 +65,7 @@ __test__ = {'API_TESTS':"""
<Whiz: Whiz Whiz1>
>>> b1.back
-<Foo: Foo Foo1>
+<Foo: Foo Foo2>
>>> base1 = Base(name="Base1")
>>> base1.save()
@@ -66,4 +75,27 @@ __test__ = {'API_TESTS':"""
>>> child1.parent
<Base: Base Base1>
+
+# Regression tests for #3937: make sure we can use unicode characters in
+# queries.
+# BUG: These tests fail on MySQL, but it's a problem with the test setup. A
+# properly configured UTF-8 database can handle this.
+
+>>> fx = Foo(name='Bjorn', friend=u'François')
+>>> fx.save()
+>>> Foo.objects.get(friend__contains=u'\xe7')
+<Foo: Foo Bjorn>
+
+# We can also do the above query using UTF-8 strings.
+>>> Foo.objects.get(friend__contains='\xc3\xa7')
+<Foo: Foo Bjorn>
+
+# Regression tests for #5087: make sure we can perform queries on TextFields.
+>>> a = Article(name='Test', text='The quick brown fox jumps over the lazy dog.')
+>>> a.save()
+>>> Article.objects.get(text__exact='The quick brown fox jumps over the lazy dog.')
+<Article: Article Test>
+
+>>> Article.objects.get(text__contains='quick brown fox')
+<Article: Article Test>
"""}
diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py
index 8801100bcc..0f7ac2f352 100644
--- a/tests/regressiontests/templates/tests.py
+++ b/tests/regressiontests/templates/tests.py
@@ -6,12 +6,22 @@ if __name__ == '__main__':
# before importing 'template'.
settings.configure()
+import os
+import unittest
+from datetime import datetime, timedelta
+
from django import template
from django.template import loader
-from django.utils.translation import activate, deactivate, install
+from django.template.loaders import app_directories, filesystem
+from django.utils.translation import activate, deactivate, install, ugettext as _
from django.utils.tzinfo import LocalTimezone
-from datetime import datetime, timedelta
-import unittest
+
+from unicode import unicode_tests
+
+# Some other tests we would like to run
+__test__ = {
+ 'unicode': unicode_tests,
+}
#################################
# Custom template tag for tests #
@@ -63,12 +73,52 @@ class OtherClass:
def method(self):
return "OtherClass.method"
-class UnicodeInStrClass:
- "Class whose __str__ returns a Unicode object."
+class UTF8Class:
+ "Class whose __str__ returns non-ASCII data"
def __str__(self):
- return u'ŠĐĆŽćžšđ'
+ return u'ŠĐĆŽćžšđ'.encode('utf-8')
class Templates(unittest.TestCase):
+ def test_loaders_security(self):
+ def test_template_sources(path, template_dirs, expected_sources):
+ # Fix expected sources so they are normcased and abspathed
+ expected_sources = [os.path.normcase(os.path.abspath(s)) for s in expected_sources]
+ # Test app_directories loader
+ sources = app_directories.get_template_sources(path, template_dirs)
+ self.assertEqual(list(sources), expected_sources)
+ # Test filesystem loader
+ sources = filesystem.get_template_sources(path, template_dirs)
+ self.assertEqual(list(sources), expected_sources)
+
+ template_dirs = ['/dir1', '/dir2']
+ test_template_sources('index.html', template_dirs,
+ ['/dir1/index.html', '/dir2/index.html'])
+ test_template_sources('/etc/passwd', template_dirs,
+ [])
+ test_template_sources('etc/passwd', template_dirs,
+ ['/dir1/etc/passwd', '/dir2/etc/passwd'])
+ test_template_sources('../etc/passwd', template_dirs,
+ [])
+ test_template_sources('../../../etc/passwd', template_dirs,
+ [])
+ test_template_sources('/dir1/index.html', template_dirs,
+ ['/dir1/index.html'])
+ test_template_sources('../dir2/index.html', template_dirs,
+ ['/dir2/index.html'])
+ test_template_sources('/dir1blah', template_dirs,
+ [])
+ test_template_sources('../dir1blah', template_dirs,
+ [])
+
+ # Case insensitive tests (for win32). Not run unless we're on
+ # a case insensitive operating system.
+ if os.path.normcase('/TEST') == os.path.normpath('/test'):
+ template_dirs = ['/dir1', '/DIR2']
+ test_template_sources('index.html', template_dirs,
+ ['/dir1/index.html', '/dir2/index.html'])
+ test_template_sources('/DIR1/index.HTML', template_dirs,
+ ['/dir1/index.html'])
+
def test_templates(self):
# NOW and NOW_tz are used by timesince tag tests.
NOW = datetime.now()
@@ -215,9 +265,9 @@ class Templates(unittest.TestCase):
# Empty strings can be passed as arguments to filters
'filter-syntax17': (r'{{ var|join:"" }}', {'var': ['a', 'b', 'c']}, 'abc'),
- # If a variable has a __str__() that returns a Unicode object, the
- # value will be converted to a bytestring.
- 'filter-syntax18': (r'{{ var }}', {'var': UnicodeInStrClass()}, '\xc5\xa0\xc4\x90\xc4\x86\xc5\xbd\xc4\x87\xc5\xbe\xc5\xa1\xc4\x91'),
+ # Make sure that any unicode strings are converted to bytestrings
+ # in the final output.
+ 'filter-syntax18': (r'{{ var }}', {'var': UTF8Class()}, u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111'),
# Numbers as filter arguments should work
'filter-syntax19': ('{{ var|truncatewords:1 }}', {"var": "hello world"}, "hello ..."),
@@ -596,7 +646,7 @@ class Templates(unittest.TestCase):
# translation of a constant string
'i18n13': ('{{ _("Page not found") }}', {'LANGUAGE_CODE': 'de'}, 'Seite nicht gefunden'),
- ### HANDLING OF TEMPLATE_TAG_IF_INVALID ###################################
+ ### HANDLING OF TEMPLATE_STRING_IF_INVALID ###################################
'invalidstr01': ('{{ var|default:"Foo" }}', {}, ('Foo','INVALID')),
'invalidstr02': ('{{ var|default_if_none:"Foo" }}', {}, ('','INVALID')),
@@ -729,6 +779,8 @@ class Templates(unittest.TestCase):
'url02' : ('{% url regressiontests.templates.views.client_action client.id, action="update" %}', {'client': {'id': 1}}, '/url_tag/client/1/update/'),
'url03' : ('{% url regressiontests.templates.views.index %}', {}, '/url_tag/'),
'url04' : ('{% url named.client client.id %}', {'client': {'id': 1}}, '/url_tag/named-client/1/'),
+ 'url05' : (u'{% url метка_оператора v %}', {'v': u'Ω'},
+ '/url_tag/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4/%CE%A9/'),
# Failures
'url-fail01' : ('{% url %}', {}, template.TemplateSyntaxError),
diff --git a/tests/regressiontests/templates/unicode.py b/tests/regressiontests/templates/unicode.py
new file mode 100644
index 0000000000..efda11c2da
--- /dev/null
+++ b/tests/regressiontests/templates/unicode.py
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+
+unicode_tests = ur"""
+Templates can be created from unicode strings.
+>>> from django.template import *
+>>> t1 = Template(u'ŠĐĆŽćžšđ {{ var }}')
+
+Templates can also be created from bytestrings. These are assumed by encoded
+using UTF-8.
+
+>>> s = '\xc5\xa0\xc4\x90\xc4\x86\xc5\xbd\xc4\x87\xc5\xbe\xc5\xa1\xc4\x91 {{ var }}'
+>>> t2 = Template(s)
+>>> s = '\x80\xc5\xc0'
+>>> Template(s)
+Traceback (most recent call last):
+ ...
+TemplateEncodingError: Templates can only be constructed from unicode or UTF-8 strings.
+
+Contexts can be constructed from unicode or UTF-8 bytestrings.
+
+>>> c1 = Context({'var': 'foo'})
+>>> c2 = Context({u'var': 'foo'})
+>>> c3 = Context({'var': u'Đđ'})
+>>> c4 = Context({u'var': '\xc4\x90\xc4\x91'})
+
+Since both templates and all four contexts represent the same thing, they all
+render the same (and are returned as unicode objects).
+
+>>> t1.render(c3) == t2.render(c3)
+True
+>>> type(t1.render(c3))
+<type 'unicode'>
+"""
diff --git a/tests/regressiontests/templates/urls.py b/tests/regressiontests/templates/urls.py
index 5fbade5c58..d79f38e0a7 100644
--- a/tests/regressiontests/templates/urls.py
+++ b/tests/regressiontests/templates/urls.py
@@ -1,3 +1,4 @@
+# coding: utf-8
from django.conf.urls.defaults import *
from regressiontests.templates import views
@@ -8,4 +9,7 @@ urlpatterns = patterns('',
(r'^client/(\d+)/$', views.client),
(r'^client/(\d+)/(?P<action>[^/]+)/$', views.client_action),
url(r'^named-client/(\d+)/$', views.client, name="named.client"),
+
+ # Unicode strings are permitted everywhere.
+ url(ur'^Юникод/(\w+)/$', views.client, name=u"метка_оператора"),
)
diff --git a/tests/regressiontests/test_client_regress/models.py b/tests/regressiontests/test_client_regress/models.py
index 40d022a47a..e17770cd03 100644
--- a/tests/regressiontests/test_client_regress/models.py
+++ b/tests/regressiontests/test_client_regress/models.py
@@ -4,7 +4,49 @@ Regression tests for the Test Client, especially the customized assertions.
"""
from django.test import Client, TestCase
from django.core import mail
+import os
+class AssertContainsTests(TestCase):
+ def test_contains(self):
+ "Responses can be inspected for content, including counting repeated substrings"
+ response = self.client.get('/test_client_regress/no_template_view/')
+
+ self.assertContains(response, 'never', 0)
+ self.assertContains(response, 'once')
+ self.assertContains(response, 'once', 1)
+ self.assertContains(response, 'twice')
+ self.assertContains(response, 'twice', 2)
+
+ try:
+ self.assertContains(response, 'never', 1)
+ except AssertionError, e:
+ self.assertEquals(str(e), "Found 0 instances of 'never' in response (expected 1)")
+
+ try:
+ self.assertContains(response, 'once', 0)
+ except AssertionError, e:
+ self.assertEquals(str(e), "Found 1 instances of 'once' in response (expected 0)")
+
+ try:
+ self.assertContains(response, 'once', 2)
+ except AssertionError, e:
+ self.assertEquals(str(e), "Found 1 instances of 'once' in response (expected 2)")
+
+ try:
+ self.assertContains(response, 'twice', 1)
+ except AssertionError, e:
+ self.assertEquals(str(e), "Found 2 instances of 'twice' in response (expected 1)")
+
+ try:
+ self.assertContains(response, 'thrice')
+ except AssertionError, e:
+ self.assertEquals(str(e), "Couldn't find 'thrice' in response")
+
+ try:
+ self.assertContains(response, 'thrice', 3)
+ except AssertionError, e:
+ self.assertEquals(str(e), "Found 0 instances of 'thrice' in response (expected 3)")
+
class AssertTemplateUsedTests(TestCase):
fixtures = ['testdata.json']
@@ -59,7 +101,7 @@ class AssertTemplateUsedTests(TestCase):
try:
self.assertTemplateUsed(response, "Valid POST Template")
except AssertionError, e:
- self.assertEquals(str(e), "Template 'Valid POST Template' was not one of the templates used to render the response. Templates used: ['form_view.html', 'base.html']")
+ self.assertEquals(str(e), "Template 'Valid POST Template' was not one of the templates used to render the response. Templates used: form_view.html, base.html")
class AssertRedirectsTests(TestCase):
def test_redirect_page(self):
@@ -69,7 +111,7 @@ class AssertRedirectsTests(TestCase):
try:
self.assertRedirects(response, '/test_client/get_view/')
except AssertionError, e:
- self.assertEquals(str(e), "Response didn't redirect as expected: Reponse code was 301 (expected 302)")
+ self.assertEquals(str(e), "Response didn't redirect as expected: Response code was 301 (expected 302)")
def test_incorrect_target(self):
"An assertion is raised if the response redirects to another target"
@@ -78,10 +120,10 @@ class AssertRedirectsTests(TestCase):
# Should redirect to get_view
self.assertRedirects(response, '/test_client/some_view/')
except AssertionError, e:
- self.assertEquals(str(e), "Response didn't redirect as expected: Reponse code was 301 (expected 302)")
+ self.assertEquals(str(e), "Response didn't redirect as expected: Response code was 301 (expected 302)")
def test_target_page(self):
- "An assertion is raised if the reponse redirect target cannot be retrieved as expected"
+ "An assertion is raised if the response redirect target cannot be retrieved as expected"
response = self.client.get('/test_client/double_redirect_view/')
try:
# The redirect target responds with a 301 code, not 200
@@ -162,3 +204,12 @@ class AssertFormErrorTests(TestCase):
except AssertionError, e:
self.assertEqual(str(e), "The field 'email' on form 'form' in context 0 does not contain the error 'Some error.' (actual errors: [u'Enter a valid e-mail address.'])")
+class AssertFileUploadTests(TestCase):
+ def test_simple_upload(self):
+ fd = open(os.path.join(os.path.dirname(__file__), "views.py"))
+ post_data = {
+ 'name': 'Ringo',
+ 'file_field': fd,
+ }
+ response = self.client.post('/test_client_regress/file_upload/', post_data)
+ self.assertEqual(response.status_code, 200)
diff --git a/tests/regressiontests/test_client_regress/urls.py b/tests/regressiontests/test_client_regress/urls.py
index e9cd0aea15..160f9f992d 100644
--- a/tests/regressiontests/test_client_regress/urls.py
+++ b/tests/regressiontests/test_client_regress/urls.py
@@ -1,7 +1,7 @@
from django.conf.urls.defaults import *
-from django.views.generic.simple import redirect_to
import views
urlpatterns = patterns('',
(r'^no_template_view/$', views.no_template_view),
+ (r'^file_upload/$', views.file_upload_view),
)
diff --git a/tests/regressiontests/test_client_regress/views.py b/tests/regressiontests/test_client_regress/views.py
index d8dd2b349c..d3fbcf8448 100644
--- a/tests/regressiontests/test_client_regress/views.py
+++ b/tests/regressiontests/test_client_regress/views.py
@@ -1,8 +1,20 @@
from django.core.mail import EmailMessage, SMTPConnection
-from django.http import HttpResponse
+from django.http import HttpResponse, HttpResponseServerError
from django.shortcuts import render_to_response
def no_template_view(request):
"A simple view that expects a GET request, and returns a rendered template"
- return HttpResponse("No template used")
+ return HttpResponse("No template used. Sample content: twice once twice. Content ends.")
+
+def file_upload_view(request):
+ """
+ Check that a file upload can be updated into the POST dictionary without
+ going pear-shaped.
+ """
+ form_data = request.POST.copy()
+ form_data.update(request.FILES)
+ if isinstance(form_data['file_field'], dict) and isinstance(form_data['name'], unicode):
+ return HttpResponse('')
+ else:
+ return HttpResponseServerError()
diff --git a/tests/regressiontests/text/tests.py b/tests/regressiontests/text/tests.py
index f758ecaf90..0fd22b58b0 100644
--- a/tests/regressiontests/text/tests.py
+++ b/tests/regressiontests/text/tests.py
@@ -1,17 +1,38 @@
-"""
-# Tests for stuff in django.utils.text.
+# coding: utf-8
+r"""
+# Tests for stuff in django.utils.text and other text munging util functions.
>>> from django.utils.text import *
### smart_split ###########################################################
>>> list(smart_split(r'''This is "a person" test.'''))
-['This', 'is', '"a person"', 'test.']
+[u'This', u'is', u'"a person"', u'test.']
>>> print list(smart_split(r'''This is "a person's" test.'''))[2]
"a person's"
->>> print list(smart_split(r'''This is "a person\\"s" test.'''))[2]
+>>> print list(smart_split(r'''This is "a person\"s" test.'''))[2]
"a person"s"
>>> list(smart_split('''"a 'one'''))
-['"a', "'one"]
+[u'"a', u"'one"]
>>> print list(smart_split(r'''all friends' tests'''))[1]
friends'
+
+### urlquote #############################################################
+>>> from django.utils.http import urlquote, urlquote_plus
+>>> urlquote(u'Paris & Orl\xe9ans')
+u'Paris%20%26%20Orl%C3%A9ans'
+>>> urlquote_plus(u'Paris & Orl\xe9ans')
+u'Paris+%26+Orl%C3%A9ans'
+
+### iri_to_uri ###########################################################
+>>> from django.utils.encoding import iri_to_uri
+>>> iri_to_uri(u'red%09ros\xe9#red')
+'red%09ros%C3%A9#red'
+>>> iri_to_uri(u'/blog/for/J\xfcrgen M\xfcnster/')
+'/blog/for/J%C3%BCrgen%20M%C3%BCnster/'
+>>> iri_to_uri(u'locations/%s' % urlquote_plus(u'Paris & Orl\xe9ans'))
+'locations/Paris+%26+Orl%C3%A9ans'
+
+iri_to_uri() is idempotent:
+>>> iri_to_uri(iri_to_uri(u'red%09ros\xe9#red'))
+'red%09ros%C3%A9#red'
"""
diff --git a/tests/regressiontests/utils/__init__.py b/tests/regressiontests/utils/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/regressiontests/utils/__init__.py
diff --git a/tests/regressiontests/utils/models.py b/tests/regressiontests/utils/models.py
new file mode 100644
index 0000000000..97a72bab14
--- /dev/null
+++ b/tests/regressiontests/utils/models.py
@@ -0,0 +1 @@
+# Test runner needs a models.py file.
diff --git a/tests/regressiontests/utils/tests.py b/tests/regressiontests/utils/tests.py
new file mode 100644
index 0000000000..258aea697e
--- /dev/null
+++ b/tests/regressiontests/utils/tests.py
@@ -0,0 +1,115 @@
+"""
+Tests for django.utils.
+"""
+
+from unittest import TestCase
+
+from django.utils import html
+
+class TestUtilsHtml(TestCase):
+
+ def check_output(self, function, value, output=None):
+ """
+ Check that function(value) equals output. If output is None,
+ check that function(value) equals value.
+ """
+ if output is None:
+ output = value
+ self.assertEqual(function(value), output)
+
+ def test_escape(self):
+ f = html.escape
+ items = (
+ ('&','&amp;'),
+ ('<', '&lt;'),
+ ('>', '&gt;'),
+ ('"', '&quot;'),
+ ("'", '&#39;'),
+ )
+ # Substitution patterns for testing the above items.
+ patterns = ("%s", "asdf%sfdsa", "%s1", "1%sb")
+ for value, output in items:
+ for pattern in patterns:
+ self.check_output(f, pattern % value, pattern % output)
+ # Check repeated values.
+ self.check_output(f, value * 2, output * 2)
+ # Verify it doesn't double replace &.
+ self.check_output(f, '<&', '&lt;&amp;')
+
+ def test_linebreaks(self):
+ f = html.linebreaks
+ items = (
+ ("para1\n\npara2\r\rpara3", "<p>para1</p>\n\n<p>para2</p>\n\n<p>para3</p>"),
+ ("para1\nsub1\rsub2\n\npara2", "<p>para1<br />sub1<br />sub2</p>\n\n<p>para2</p>"),
+ ("para1\r\n\r\npara2\rsub1\r\rpara4", "<p>para1</p>\n\n<p>para2<br />sub1</p>\n\n<p>para4</p>"),
+ ("para1\tmore\n\npara2", "<p>para1\tmore</p>\n\n<p>para2</p>"),
+ )
+ for value, output in items:
+ self.check_output(f, value, output)
+
+ def test_strip_tags(self):
+ f = html.strip_tags
+ items = (
+ ('<adf>a', 'a'),
+ ('</adf>a', 'a'),
+ ('<asdf><asdf>e', 'e'),
+ ('<f', '<f'),
+ ('</fe', '</fe'),
+ ('<x>b<y>', 'b'),
+ )
+ for value, output in items:
+ self.check_output(f, value, output)
+
+ def test_strip_spaces_between_tags(self):
+ f = html.strip_spaces_between_tags
+ # Strings that should come out untouched.
+ items = (' <adf>', '<adf> ', ' </adf> ', ' <f> x</f>')
+ for value in items:
+ self.check_output(f, value)
+ # Strings that have spaces to strip.
+ items = (
+ ('<d> </d>', '<d></d>'),
+ ('<p>hello </p>\n<p> world</p>', '<p>hello </p><p> world</p>'),
+ ('\n<p>\t</p>\n<p> </p>\n', '\n<p></p><p></p>\n'),
+ )
+ for value, output in items:
+ self.check_output(f, value, output)
+
+ def test_strip_entities(self):
+ f = html.strip_entities
+ # Strings that should come out untouched.
+ values = ("&", "&a", "&a", "a&#a")
+ for value in values:
+ self.check_output(f, value)
+ # Valid entities that should be stripped from the patterns.
+ entities = ("&#1;", "&#12;", "&a;", "&fdasdfasdfasdf;")
+ patterns = (
+ ("asdf %(entity)s ", "asdf "),
+ ("%(entity)s%(entity)s", ""),
+ ("&%(entity)s%(entity)s", "&"),
+ ("%(entity)s3", "3"),
+ )
+ for entity in entities:
+ for in_pattern, output in patterns:
+ self.check_output(f, in_pattern % {'entity': entity}, output)
+
+ def test_fix_ampersands(self):
+ f = html.fix_ampersands
+ # Strings without ampersands or with ampersands already encoded.
+ values = ("a&#1;", "b", "&a;", "&amp; &x; ", "asdf")
+ patterns = (
+ ("%s", "%s"),
+ ("&%s", "&amp;%s"),
+ ("&%s&", "&amp;%s&amp;"),
+ )
+ for value in values:
+ for in_pattern, out_pattern in patterns:
+ self.check_output(f, in_pattern % value, out_pattern % value)
+ # Strings with ampersands that need encoding.
+ items = (
+ ("&#;", "&amp;#;"),
+ ("&#875 ;", "&amp;#875 ;"),
+ ("&#4abc;", "&amp;#4abc;"),
+ )
+ for value, output in items:
+ self.check_output(f, value, output)