diff options
| author | Justin Bronn <jbronn@gmail.com> | 2007-08-26 01:10:53 +0000 |
|---|---|---|
| committer | Justin Bronn <jbronn@gmail.com> | 2007-08-26 01:10:53 +0000 |
| commit | 2052b508eb92c62fc0678efd4936c5ec1e0e735b (patch) | |
| tree | e510109b74b28c8ccef5f6955727cb9dce3da655 /tests/regressiontests | |
| parent | a7297a255f4bb86f608ea251e00253d18c31d9d4 (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')
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 & Jill & Jeroboam' +>>> fix_ampersands(u'Jack & Jill & Jeroboam') +u'Jack & Jill & 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') -'<some html & special characters > here' +>>> escape(u'<some html & special characters > here') +u'<some html & special characters > here' >>> escape(u'<some html & special characters > here ĐÅ€£') u'<some html & special characters > 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'<some html & special characters > 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'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 "quoted" & 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 = ( + ('&','&'), + ('<', '<'), + ('>', '>'), + ('"', '"'), + ("'", '''), + ) + # 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, '<&', '<&') + + 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 = ("", "", "&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", "b", "&a;", "& &x; ", "asdf") + patterns = ( + ("%s", "%s"), + ("&%s", "&%s"), + ("&%s&", "&%s&"), + ) + 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 = ( + ("&#;", "&#;"), + ("ͫ ;", "&#875 ;"), + ("abc;", "&#4abc;"), + ) + for value, output in items: + self.check_output(f, value, output) |
