diff options
| author | Jeremy Dunck <jdunck@gmail.com> | 2007-06-18 16:48:27 +0000 |
|---|---|---|
| committer | Jeremy Dunck <jdunck@gmail.com> | 2007-06-18 16:48:27 +0000 |
| commit | bdcc95e5cce2754d78055f86d561ba2be92ba854 (patch) | |
| tree | 08a7e4c86244cb42fe577aec5c03a57b023822c2 /tests/regressiontests | |
| parent | 48c9f87e1f3ba9523d79c09f52c0ccc6221f08bf (diff) | |
gis: Merged revisions 4786-5490 via svnmerge from
http://code.djangoproject.com/svn/django/trunk
git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@5492 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'tests/regressiontests')
25 files changed, 2319 insertions, 464 deletions
diff --git a/tests/regressiontests/cache/tests.py b/tests/regressiontests/cache/tests.py index cf58ab321a..9dc7161c03 100644 --- a/tests/regressiontests/cache/tests.py +++ b/tests/regressiontests/cache/tests.py @@ -46,6 +46,11 @@ class Cache(unittest.TestCase): self.assertEqual(cache.has_key("hello"), True) self.assertEqual(cache.has_key("goodbye"), False) + def test_in(self): + cache.set("hello", "goodbye") + self.assertEqual("hello" in cache, True) + self.assertEqual("goodbye" in cache, False) + def test_data_types(self): # test data types stuff = { diff --git a/tests/regressiontests/datastructures/tests.py b/tests/regressiontests/datastructures/tests.py index e008c255f1..18eb4fcccd 100644 --- a/tests/regressiontests/datastructures/tests.py +++ b/tests/regressiontests/datastructures/tests.py @@ -52,6 +52,8 @@ 'not one' >>> d.keys() == d.copy().keys() True +>>> print repr(d) +{'one': 'not one', 'two': 'two', 'three': 'three'} ### DotExpandedDict ############################################################ diff --git a/tests/regressiontests/defaultfilters/tests.py b/tests/regressiontests/defaultfilters/tests.py index db3f7fab2a..4a2e9432b0 100644 --- a/tests/regressiontests/defaultfilters/tests.py +++ b/tests/regressiontests/defaultfilters/tests.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + r""" >>> floatformat(7.7) '7.7' @@ -87,19 +89,19 @@ u'\xeb' >>> truncatewords('A sentence with a few words in it', 'not a number') 'A sentence with a few words in it' ->>> truncatewords_html('<p>one <a href="#">two - three <br>four</a> five</p>', 0) +>>> truncatewords_html('<p>one <a href="#">two - three <br>four</a> five</p>', 0) '' - ->>> truncatewords_html('<p>one <a href="#">two - three <br>four</a> five</p>', 2) + +>>> truncatewords_html('<p>one <a href="#">two - three <br>four</a> five</p>', 2) '<p>one <a href="#">two ...</a></p>' - ->>> truncatewords_html('<p>one <a href="#">two - three <br>four</a> five</p>', 4) + +>>> 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('<p>one <a href="#">two - three <br>four</a> five</p>', 5) +>>> 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('<p>one <a href="#">two - three <br>four</a> five</p>', 100) +>>> 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>' >>> upper('Mixed case input') @@ -166,6 +168,9 @@ u'\xcb' >>> escape('<some html & special characters > here') '<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>' diff --git a/tests/regressiontests/dispatch/tests/test_saferef.py b/tests/regressiontests/dispatch/tests/test_saferef.py index 233a2023e0..c0ec879a4b 100644 --- a/tests/regressiontests/dispatch/tests/test_saferef.py +++ b/tests/regressiontests/dispatch/tests/test_saferef.py @@ -55,8 +55,10 @@ class Tester(unittest.TestCase): for t in self.ts: if hasattr(t, 'x'): self.assert_(sd.has_key(safeRef(t.x))) + self.assert_(safeRef(t.x) in sd) else: self.assert_(sd.has_key(safeRef(t))) + self.assert_(safeRef(t) in sd) def testRepresentation (self): """Test that the reference object's representation works diff --git a/tests/regressiontests/markup/__init__.py b/tests/regressiontests/fixtures_regress/__init__.py index e69de29bb2..e69de29bb2 100644 --- a/tests/regressiontests/markup/__init__.py +++ b/tests/regressiontests/fixtures_regress/__init__.py diff --git a/tests/regressiontests/fixtures_regress/fixtures/sequence.json b/tests/regressiontests/fixtures_regress/fixtures/sequence.json new file mode 100644 index 0000000000..ecaf637b9f --- /dev/null +++ b/tests/regressiontests/fixtures_regress/fixtures/sequence.json @@ -0,0 +1,10 @@ +[ + { + "pk": "1", + "model": "fixtures_regress.animal", + "fields": { + "name": "Lion", + "latin_name": "Panthera leo" + } + } +]
\ No newline at end of file diff --git a/tests/regressiontests/fixtures_regress/models.py b/tests/regressiontests/fixtures_regress/models.py new file mode 100644 index 0000000000..dd407df353 --- /dev/null +++ b/tests/regressiontests/fixtures_regress/models.py @@ -0,0 +1,29 @@ +from django.db import models + +class Animal(models.Model): + name = models.CharField(maxlength=150) + latin_name = models.CharField(maxlength=150) + + def __str__(self): + return self.common_name + +class Plant(models.Model): + name = models.CharField(maxlength=150) + + class Meta: + # For testing when upper case letter in app name; regression for #4057 + db_table = "Fixtures_regress_plant" + +__test__ = {'API_TESTS':""" +>>> from django.core import management + +# Load a fixture that uses PK=1 +>>> management.load_data(['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. +# This is a regression test for ticket #3790. +>>> animal = Animal(name='Platypus', latin_name='Ornithorhynchus anatinus') +>>> animal.save() + +"""}
\ No newline at end of file diff --git a/tests/regressiontests/forms/localflavor.py b/tests/regressiontests/forms/localflavor.py new file mode 100644 index 0000000000..ede89de2a0 --- /dev/null +++ b/tests/regressiontests/forms/localflavor.py @@ -0,0 +1,1359 @@ +# -*- coding: utf-8 -*- +# Tests for the different contrib/localflavor/ form fields. + +localflavor_tests = r""" +# USZipCodeField ############################################################## + +USZipCodeField validates that the data is either a five-digit U.S. zip code or +a zip+4. +>>> from django.contrib.localflavor.us.forms import USZipCodeField +>>> f = USZipCodeField() +>>> f.clean('60606') +u'60606' +>>> f.clean(60606) +u'60606' +>>> f.clean('04000') +u'04000' +>>> f.clean('4000') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX or XXXXX-XXXX.'] +>>> f.clean('60606-1234') +u'60606-1234' +>>> f.clean('6060-1234') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX or XXXXX-XXXX.'] +>>> f.clean('60606-') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX or XXXXX-XXXX.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = USZipCodeField(required=False) +>>> f.clean('60606') +u'60606' +>>> f.clean(60606) +u'60606' +>>> f.clean('04000') +u'04000' +>>> f.clean('4000') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX or XXXXX-XXXX.'] +>>> f.clean('60606-1234') +u'60606-1234' +>>> f.clean('6060-1234') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX or XXXXX-XXXX.'] +>>> f.clean('60606-') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX or XXXXX-XXXX.'] +>>> f.clean(None) +u'' +>>> f.clean('') +u'' + +# USPhoneNumberField ########################################################## + +USPhoneNumberField validates that the data is a valid U.S. phone number, +including the area code. It's normalized to XXX-XXX-XXXX format. +>>> from django.contrib.localflavor.us.forms import USPhoneNumberField +>>> f = USPhoneNumberField() +>>> f.clean('312-555-1212') +u'312-555-1212' +>>> f.clean('3125551212') +u'312-555-1212' +>>> f.clean('312 555-1212') +u'312-555-1212' +>>> f.clean('(312) 555-1212') +u'312-555-1212' +>>> f.clean('312 555 1212') +u'312-555-1212' +>>> f.clean('312.555.1212') +u'312-555-1212' +>>> f.clean('312.555-1212') +u'312-555-1212' +>>> f.clean(' (312) 555.1212 ') +u'312-555-1212' +>>> f.clean('555-1212') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.'] +>>> f.clean('312-55-1212') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = USPhoneNumberField(required=False) +>>> f.clean('312-555-1212') +u'312-555-1212' +>>> f.clean('3125551212') +u'312-555-1212' +>>> f.clean('312 555-1212') +u'312-555-1212' +>>> f.clean('(312) 555-1212') +u'312-555-1212' +>>> f.clean('312 555 1212') +u'312-555-1212' +>>> f.clean('312.555.1212') +u'312-555-1212' +>>> f.clean('312.555-1212') +u'312-555-1212' +>>> f.clean(' (312) 555.1212 ') +u'312-555-1212' +>>> f.clean('555-1212') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.'] +>>> f.clean('312-55-1212') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.'] +>>> f.clean(None) +u'' +>>> f.clean('') +u'' + +# USStateField ################################################################ + +USStateField validates that the data is either an abbreviation or name of a +U.S. state. +>>> from django.contrib.localflavor.us.forms import USStateField +>>> f = USStateField() +>>> f.clean('il') +u'IL' +>>> f.clean('IL') +u'IL' +>>> f.clean('illinois') +u'IL' +>>> f.clean(' illinois ') +u'IL' +>>> f.clean(60606) +Traceback (most recent call last): +... +ValidationError: [u'Enter a U.S. state or territory.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = USStateField(required=False) +>>> f.clean('il') +u'IL' +>>> f.clean('IL') +u'IL' +>>> f.clean('illinois') +u'IL' +>>> f.clean(' illinois ') +u'IL' +>>> f.clean(60606) +Traceback (most recent call last): +... +ValidationError: [u'Enter a U.S. state or territory.'] +>>> f.clean(None) +u'' +>>> f.clean('') +u'' + +# USStateSelect ############################################################### + +USStateSelect is a Select widget that uses a list of U.S. states/territories +as its choices. +>>> from django.contrib.localflavor.us.forms import USStateSelect +>>> w = USStateSelect() +>>> print w.render('state', 'IL') +<select name="state"> +<option value="AL">Alabama</option> +<option value="AK">Alaska</option> +<option value="AS">American Samoa</option> +<option value="AZ">Arizona</option> +<option value="AR">Arkansas</option> +<option value="CA">California</option> +<option value="CO">Colorado</option> +<option value="CT">Connecticut</option> +<option value="DE">Delaware</option> +<option value="DC">District of Columbia</option> +<option value="FM">Federated States of Micronesia</option> +<option value="FL">Florida</option> +<option value="GA">Georgia</option> +<option value="GU">Guam</option> +<option value="HI">Hawaii</option> +<option value="ID">Idaho</option> +<option value="IL" selected="selected">Illinois</option> +<option value="IN">Indiana</option> +<option value="IA">Iowa</option> +<option value="KS">Kansas</option> +<option value="KY">Kentucky</option> +<option value="LA">Louisiana</option> +<option value="ME">Maine</option> +<option value="MH">Marshall Islands</option> +<option value="MD">Maryland</option> +<option value="MA">Massachusetts</option> +<option value="MI">Michigan</option> +<option value="MN">Minnesota</option> +<option value="MS">Mississippi</option> +<option value="MO">Missouri</option> +<option value="MT">Montana</option> +<option value="NE">Nebraska</option> +<option value="NV">Nevada</option> +<option value="NH">New Hampshire</option> +<option value="NJ">New Jersey</option> +<option value="NM">New Mexico</option> +<option value="NY">New York</option> +<option value="NC">North Carolina</option> +<option value="ND">North Dakota</option> +<option value="MP">Northern Mariana Islands</option> +<option value="OH">Ohio</option> +<option value="OK">Oklahoma</option> +<option value="OR">Oregon</option> +<option value="PW">Palau</option> +<option value="PA">Pennsylvania</option> +<option value="PR">Puerto Rico</option> +<option value="RI">Rhode Island</option> +<option value="SC">South Carolina</option> +<option value="SD">South Dakota</option> +<option value="TN">Tennessee</option> +<option value="TX">Texas</option> +<option value="UT">Utah</option> +<option value="VT">Vermont</option> +<option value="VI">Virgin Islands</option> +<option value="VA">Virginia</option> +<option value="WA">Washington</option> +<option value="WV">West Virginia</option> +<option value="WI">Wisconsin</option> +<option value="WY">Wyoming</option> +</select> + +# USSocialSecurityNumberField ################################################# +>>> from django.contrib.localflavor.us.forms import USSocialSecurityNumberField +>>> f = USSocialSecurityNumberField() +>>> f.clean('987-65-4330') +u'987-65-4330' +>>> f.clean('987654330') +u'987-65-4330' +>>> f.clean('078-05-1120') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid U.S. Social Security number in XXX-XX-XXXX format.'] + +# UKPostcodeField ############################################################# + +UKPostcodeField validates that the data is a valid UK postcode. +>>> from django.contrib.localflavor.uk.forms import UKPostcodeField +>>> f = UKPostcodeField() +>>> f.clean('BT32 4PX') +u'BT32 4PX' +>>> f.clean('GIR 0AA') +u'GIR 0AA' +>>> f.clean('BT324PX') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postcode. A space is required between the two postcode parts.'] +>>> f.clean('1NV 4L1D') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postcode. A space is required between the two postcode parts.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = UKPostcodeField(required=False) +>>> f.clean('BT32 4PX') +u'BT32 4PX' +>>> f.clean('GIR 0AA') +u'GIR 0AA' +>>> f.clean('1NV 4L1D') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postcode. A space is required between the two postcode parts.'] +>>> f.clean('BT324PX') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postcode. A space is required between the two postcode parts.'] +>>> f.clean(None) +u'' +>>> f.clean('') +u'' + +# FRZipCodeField ############################################################# + +FRZipCodeField validates that the data is a valid FR zipcode. +>>> from django.contrib.localflavor.fr.forms import FRZipCodeField +>>> f = FRZipCodeField() +>>> f.clean('75001') +u'75001' +>>> f.clean('93200') +u'93200' +>>> f.clean('2A200') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX.'] +>>> f.clean('980001') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = FRZipCodeField(required=False) +>>> f.clean('75001') +u'75001' +>>> f.clean('93200') +u'93200' +>>> f.clean('2A200') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX.'] +>>> f.clean('980001') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX.'] +>>> f.clean(None) +u'' +>>> f.clean('') +u'' + + +# FRPhoneNumberField ########################################################## + +FRPhoneNumberField validates that the data is a valid french phone number. +It's normalized to 0X XX XX XX XX format. Dots are valid too. +>>> from django.contrib.localflavor.fr.forms import FRPhoneNumberField +>>> f = FRPhoneNumberField() +>>> f.clean('01 55 44 58 64') +u'01 55 44 58 64' +>>> f.clean('0155445864') +u'01 55 44 58 64' +>>> f.clean('01 5544 5864') +u'01 55 44 58 64' +>>> f.clean('01 55.44.58.64') +u'01 55 44 58 64' +>>> f.clean('01.55.44.58.64') +u'01 55 44 58 64' +>>> f.clean('01,55,44,58,64') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must be in 0X XX XX XX XX format.'] +>>> f.clean('555 015 544') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must be in 0X XX XX XX XX format.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = FRPhoneNumberField(required=False) +>>> f.clean('01 55 44 58 64') +u'01 55 44 58 64' +>>> f.clean('0155445864') +u'01 55 44 58 64' +>>> f.clean('01 5544 5864') +u'01 55 44 58 64' +>>> f.clean('01 55.44.58.64') +u'01 55 44 58 64' +>>> f.clean('01.55.44.58.64') +u'01 55 44 58 64' +>>> f.clean('01,55,44,58,64') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must be in 0X XX XX XX XX format.'] +>>> f.clean('555 015 544') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must be in 0X XX XX XX XX format.'] +>>> f.clean(None) +u'' +>>> f.clean('') +u'' + +# FRDepartmentSelect ############################################################### + +FRDepartmentSelect is a Select widget that uses a list of french departments +including DOM TOM +>>> from django.contrib.localflavor.fr.forms import FRDepartmentSelect +>>> w = FRDepartmentSelect() +>>> print w.render('dep', 'Paris') +<select name="dep"> +<option value="01">01 - Ain</option> +<option value="02">02 - Aisne</option> +<option value="03">03 - Allier</option> +<option value="04">04 - Alpes-de-Haute-Provence</option> +<option value="05">05 - Hautes-Alpes</option> +<option value="06">06 - Alpes-Maritimes</option> +<option value="07">07 - Ardeche</option> +<option value="08">08 - Ardennes</option> +<option value="09">09 - Ariege</option> +<option value="10">10 - Aube</option> +<option value="11">11 - Aude</option> +<option value="12">12 - Aveyron</option> +<option value="13">13 - Bouches-du-Rhone</option> +<option value="14">14 - Calvados</option> +<option value="15">15 - Cantal</option> +<option value="16">16 - Charente</option> +<option value="17">17 - Charente-Maritime</option> +<option value="18">18 - Cher</option> +<option value="19">19 - Correze</option> +<option value="21">21 - Cote-d'Or</option> +<option value="22">22 - Cotes-d'Armor</option> +<option value="23">23 - Creuse</option> +<option value="24">24 - Dordogne</option> +<option value="25">25 - Doubs</option> +<option value="26">26 - Drome</option> +<option value="27">27 - Eure</option> +<option value="28">28 - Eure-et-Loire</option> +<option value="29">29 - Finistere</option> +<option value="2A">2A - Corse-du-Sud</option> +<option value="2B">2B - Haute-Corse</option> +<option value="30">30 - Gard</option> +<option value="31">31 - Haute-Garonne</option> +<option value="32">32 - Gers</option> +<option value="33">33 - Gironde</option> +<option value="34">34 - Herault</option> +<option value="35">35 - Ille-et-Vilaine</option> +<option value="36">36 - Indre</option> +<option value="37">37 - Indre-et-Loire</option> +<option value="38">38 - Isere</option> +<option value="39">39 - Jura</option> +<option value="40">40 - Landes</option> +<option value="41">41 - Loir-et-Cher</option> +<option value="42">42 - Loire</option> +<option value="43">43 - Haute-Loire</option> +<option value="44">44 - Loire-Atlantique</option> +<option value="45">45 - Loiret</option> +<option value="46">46 - Lot</option> +<option value="47">47 - Lot-et-Garonne</option> +<option value="48">48 - Lozere</option> +<option value="49">49 - Maine-et-Loire</option> +<option value="50">50 - Manche</option> +<option value="51">51 - Marne</option> +<option value="52">52 - Haute-Marne</option> +<option value="53">53 - Mayenne</option> +<option value="54">54 - Meurthe-et-Moselle</option> +<option value="55">55 - Meuse</option> +<option value="56">56 - Morbihan</option> +<option value="57">57 - Moselle</option> +<option value="58">58 - Nievre</option> +<option value="59">59 - Nord</option> +<option value="60">60 - Oise</option> +<option value="61">61 - Orne</option> +<option value="62">62 - Pas-de-Calais</option> +<option value="63">63 - Puy-de-Dome</option> +<option value="64">64 - Pyrenees-Atlantiques</option> +<option value="65">65 - Hautes-Pyrenees</option> +<option value="66">66 - Pyrenees-Orientales</option> +<option value="67">67 - Bas-Rhin</option> +<option value="68">68 - Haut-Rhin</option> +<option value="69">69 - Rhone</option> +<option value="70">70 - Haute-Saone</option> +<option value="71">71 - Saone-et-Loire</option> +<option value="72">72 - Sarthe</option> +<option value="73">73 - Savoie</option> +<option value="74">74 - Haute-Savoie</option> +<option value="75">75 - Paris</option> +<option value="76">76 - Seine-Maritime</option> +<option value="77">77 - Seine-et-Marne</option> +<option value="78">78 - Yvelines</option> +<option value="79">79 - Deux-Sevres</option> +<option value="80">80 - Somme</option> +<option value="81">81 - Tarn</option> +<option value="82">82 - Tarn-et-Garonne</option> +<option value="83">83 - Var</option> +<option value="84">84 - Vaucluse</option> +<option value="85">85 - Vendee</option> +<option value="86">86 - Vienne</option> +<option value="87">87 - Haute-Vienne</option> +<option value="88">88 - Vosges</option> +<option value="89">89 - Yonne</option> +<option value="90">90 - Territoire de Belfort</option> +<option value="91">91 - Essonne</option> +<option value="92">92 - Hauts-de-Seine</option> +<option value="93">93 - Seine-Saint-Denis</option> +<option value="94">94 - Val-de-Marne</option> +<option value="95">95 - Val-d'Oise</option> +<option value="2A">2A - Corse du sud</option> +<option value="2B">2B - Haute Corse</option> +<option value="971">971 - Guadeloupe</option> +<option value="972">972 - Martinique</option> +<option value="973">973 - Guyane</option> +<option value="974">974 - La Reunion</option> +<option value="975">975 - Saint-Pierre-et-Miquelon</option> +<option value="976">976 - Mayotte</option> +<option value="984">984 - Terres Australes et Antarctiques</option> +<option value="986">986 - Wallis et Futuna</option> +<option value="987">987 - Polynesie Francaise</option> +<option value="988">988 - Nouvelle-Caledonie</option> +</select> + +# JPPostalCodeField ############################################################### + +A form field that validates its input is a Japanese postcode. + +Accepts 7 digits(with/out hyphen). +>>> from django.contrib.localflavor.jp.forms import JPPostalCodeField +>>> f = JPPostalCodeField() +>>> f.clean('251-0032') +u'2510032' +>>> f.clean('2510032') +u'2510032' +>>> f.clean('2510-032') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postal code in the format XXXXXXX or XXX-XXXX.'] +>>> f.clean('251a0032') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postal code in the format XXXXXXX or XXX-XXXX.'] +>>> f.clean('a51-0032') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postal code in the format XXXXXXX or XXX-XXXX.'] +>>> f.clean('25100321') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postal code in the format XXXXXXX or XXX-XXXX.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = JPPostalCodeField(required=False) +>>> f.clean('251-0032') +u'2510032' +>>> f.clean('2510032') +u'2510032' +>>> f.clean('2510-032') +Traceback (most recent call last): +... +ValidationError: [u'Enter a postal code in the format XXXXXXX or XXX-XXXX.'] +>>> f.clean('') +u'' +>>> f.clean(None) +u'' + +# JPPrefectureSelect ############################################################### + +A Select widget that uses a list of Japanese prefectures as its choices. +>>> from django.contrib.localflavor.jp.forms import JPPrefectureSelect +>>> w = JPPrefectureSelect() +>>> print w.render('prefecture', 'kanagawa') +<select name="prefecture"> +<option value="hokkaido">Hokkaido</option> +<option value="aomori">Aomori</option> +<option value="iwate">Iwate</option> +<option value="miyagi">Miyagi</option> +<option value="akita">Akita</option> +<option value="yamagata">Yamagata</option> +<option value="fukushima">Fukushima</option> +<option value="ibaraki">Ibaraki</option> +<option value="tochigi">Tochigi</option> +<option value="gunma">Gunma</option> +<option value="saitama">Saitama</option> +<option value="chiba">Chiba</option> +<option value="tokyo">Tokyo</option> +<option value="kanagawa" selected="selected">Kanagawa</option> +<option value="yamanashi">Yamanashi</option> +<option value="nagano">Nagano</option> +<option value="niigata">Niigata</option> +<option value="toyama">Toyama</option> +<option value="ishikawa">Ishikawa</option> +<option value="fukui">Fukui</option> +<option value="gifu">Gifu</option> +<option value="shizuoka">Shizuoka</option> +<option value="aichi">Aichi</option> +<option value="mie">Mie</option> +<option value="shiga">Shiga</option> +<option value="kyoto">Kyoto</option> +<option value="osaka">Osaka</option> +<option value="hyogo">Hyogo</option> +<option value="nara">Nara</option> +<option value="wakayama">Wakayama</option> +<option value="tottori">Tottori</option> +<option value="shimane">Shimane</option> +<option value="okayama">Okayama</option> +<option value="hiroshima">Hiroshima</option> +<option value="yamaguchi">Yamaguchi</option> +<option value="tokushima">Tokushima</option> +<option value="kagawa">Kagawa</option> +<option value="ehime">Ehime</option> +<option value="kochi">Kochi</option> +<option value="fukuoka">Fukuoka</option> +<option value="saga">Saga</option> +<option value="nagasaki">Nagasaki</option> +<option value="kumamoto">Kumamoto</option> +<option value="oita">Oita</option> +<option value="miyazaki">Miyazaki</option> +<option value="kagoshima">Kagoshima</option> +<option value="okinawa">Okinawa</option> +</select> + +# ITZipCodeField ############################################################# + +>>> from django.contrib.localflavor.it.forms import ITZipCodeField +>>> f = ITZipCodeField() +>>> f.clean('00100') +u'00100' +>>> f.clean(' 00100') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid zip code.'] + +# ITRegionSelect ############################################################# + +>>> from django.contrib.localflavor.it.forms import ITRegionSelect +>>> w = ITRegionSelect() +>>> w.render('regions', 'PMN') +u'<select name="regions">\n<option value="ABR">Abruzzo</option>\n<option value="BAS">Basilicata</option>\n<option value="CAL">Calabria</option>\n<option value="CAM">Campania</option>\n<option value="EMR">Emilia-Romagna</option>\n<option value="FVG">Friuli-Venezia Giulia</option>\n<option value="LAZ">Lazio</option>\n<option value="LIG">Liguria</option>\n<option value="LOM">Lombardia</option>\n<option value="MAR">Marche</option>\n<option value="MOL">Molise</option>\n<option value="PMN" selected="selected">Piemonte</option>\n<option value="PUG">Puglia</option>\n<option value="SAR">Sardegna</option>\n<option value="SIC">Sicilia</option>\n<option value="TOS">Toscana</option>\n<option value="TAA">Trentino-Alto Adige</option>\n<option value="UMB">Umbria</option>\n<option value="VAO">Valle d\u2019Aosta</option>\n<option value="VEN">Veneto</option>\n</select>' + +# ITSocialSecurityNumberField ################################################# + +>>> from django.contrib.localflavor.it.forms import ITSocialSecurityNumberField +>>> f = ITSocialSecurityNumberField() +>>> f.clean('LVSGDU99T71H501L') +u'LVSGDU99T71H501L' +>>> f.clean('LBRRME11A01L736W') +u'LBRRME11A01L736W' +>>> f.clean('lbrrme11a01l736w') +u'LBRRME11A01L736W' +>>> f.clean('LBR RME 11A01 L736W') +u'LBRRME11A01L736W' +>>> f.clean('LBRRME11A01L736A') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid Social Security number.'] +>>> f.clean('%BRRME11A01L736W') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid Social Security number.'] + +# ITVatNumberField ########################################################### + +>>> from django.contrib.localflavor.it.forms import ITVatNumberField +>>> f = ITVatNumberField() +>>> f.clean('07973780013') +u'07973780013' +>>> f.clean('7973780013') +u'07973780013' +>>> f.clean(7973780013) +u'07973780013' +>>> f.clean('07973780014') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid VAT number.'] +>>> f.clean('A7973780013') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid VAT number.'] + +# FIZipCodeField ############################################################# + +FIZipCodeField validates that the data is a valid FI zipcode. +>>> from django.contrib.localflavor.fi.forms import FIZipCodeField +>>> f = FIZipCodeField() +>>> f.clean('20540') +u'20540' +>>> f.clean('20101') +u'20101' +>>> f.clean('20s40') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX.'] +>>> f.clean('205401') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = FIZipCodeField(required=False) +>>> f.clean('20540') +u'20540' +>>> f.clean('20101') +u'20101' +>>> f.clean('20s40') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX.'] +>>> f.clean('205401') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX.'] +>>> f.clean(None) +u'' +>>> f.clean('') +u'' + +# FIMunicipalitySelect ############################################################### + +A Select widget that uses a list of Finnish municipalities as its choices. +>>> from django.contrib.localflavor.fi.forms import FIMunicipalitySelect +>>> w = FIMunicipalitySelect() +>>> unicode(w.render('municipalities', 'turku')) +u'<select name="municipalities">\n<option value="akaa">Akaa</option>\n<option value="alaharma">Alah\xe4rm\xe4</option>\n<option value="alajarvi">Alaj\xe4rvi</option>\n<option value="alastaro">Alastaro</option>\n<option value="alavieska">Alavieska</option>\n<option value="alavus">Alavus</option>\n<option value="anjalankoski">Anjalankoski</option>\n<option value="artjarvi">Artj\xe4rvi</option>\n<option value="asikkala">Asikkala</option>\n<option value="askainen">Askainen</option>\n<option value="askola">Askola</option>\n<option value="aura">Aura</option>\n<option value="brando">Br\xe4nd\xf6</option>\n<option value="dragsfjard">Dragsfj\xe4rd</option>\n<option value="eckero">Ecker\xf6</option>\n<option value="elimaki">Elim\xe4ki</option>\n<option value="eno">Eno</option>\n<option value="enonkoski">Enonkoski</option>\n<option value="enontekio">Enonteki\xf6</option>\n<option value="espoo">Espoo</option>\n<option value="eura">Eura</option>\n<option value="eurajoki">Eurajoki</option>\n<option value="evijarvi">Evij\xe4rvi</option>\n<option value="finstrom">Finstr\xf6m</option>\n<option value="forssa">Forssa</option>\n<option value="foglo">F\xf6gl\xf6</option>\n<option value="geta">Geta</option>\n<option value="haapajarvi">Haapaj\xe4rvi</option>\n<option value="haapavesi">Haapavesi</option>\n<option value="hailuoto">Hailuoto</option>\n<option value="halikko">Halikko</option>\n<option value="halsua">Halsua</option>\n<option value="hamina">Hamina</option>\n<option value="hammarland">Hammarland</option>\n<option value="hankasalmi">Hankasalmi</option>\n<option value="hanko">Hanko</option>\n<option value="harjavalta">Harjavalta</option>\n<option value="hartola">Hartola</option>\n<option value="hattula">Hattula</option>\n<option value="hauho">Hauho</option>\n<option value="haukipudas">Haukipudas</option>\n<option value="hausjarvi">Hausj\xe4rvi</option>\n<option value="heinola">Heinola</option>\n<option value="heinavesi">Hein\xe4vesi</option>\n<option value="helsinki">Helsinki</option>\n<option value="himanka">Himanka</option>\n<option value="hirvensalmi">Hirvensalmi</option>\n<option value="hollola">Hollola</option>\n<option value="honkajoki">Honkajoki</option>\n<option value="houtskari">Houtskari</option>\n<option value="huittinen">Huittinen</option>\n<option value="humppila">Humppila</option>\n<option value="hyrynsalmi">Hyrynsalmi</option>\n<option value="hyvinkaa">Hyvink\xe4\xe4</option>\n<option value="hameenkoski">H\xe4meenkoski</option>\n<option value="hameenkyro">H\xe4meenkyr\xf6</option>\n<option value="hameenlinna">H\xe4meenlinna</option>\n<option value="ii">Ii</option>\n<option value="iisalmi">Iisalmi</option>\n<option value="iitti">Iitti</option>\n<option value="ikaalinen">Ikaalinen</option>\n<option value="ilmajoki">Ilmajoki</option>\n<option value="ilomantsi">Ilomantsi</option>\n<option value="imatra">Imatra</option>\n<option value="inari">Inari</option>\n<option value="inio">Ini\xf6</option>\n<option value="inkoo">Inkoo</option>\n<option value="isojoki">Isojoki</option>\n<option value="isokyro">Isokyr\xf6</option>\n<option value="jaala">Jaala</option>\n<option value="jalasjarvi">Jalasj\xe4rvi</option>\n<option value="janakkala">Janakkala</option>\n<option value="joensuu">Joensuu</option>\n<option value="jokioinen">Jokioinen</option>\n<option value="jomala">Jomala</option>\n<option value="joroinen">Joroinen</option>\n<option value="joutsa">Joutsa</option>\n<option value="joutseno">Joutseno</option>\n<option value="juankoski">Juankoski</option>\n<option value="jurva">Jurva</option>\n<option value="juuka">Juuka</option>\n<option value="juupajoki">Juupajoki</option>\n<option value="juva">Juva</option>\n<option value="jyvaskyla">Jyv\xe4skyl\xe4</option>\n<option value="jyvaskylan_mlk">Jyv\xe4skyl\xe4n maalaiskunta</option>\n<option value="jamijarvi">J\xe4mij\xe4rvi</option>\n<option value="jamsa">J\xe4ms\xe4</option>\n<option value="jamsankoski">J\xe4ms\xe4nkoski</option>\n<option value="jarvenpaa">J\xe4rvenp\xe4\xe4</option>\n<option value="kaarina">Kaarina</option>\n<option value="kaavi">Kaavi</option>\n<option value="kajaani">Kajaani</option>\n<option value="kalajoki">Kalajoki</option>\n<option value="kalvola">Kalvola</option>\n<option value="kangasala">Kangasala</option>\n<option value="kangasniemi">Kangasniemi</option>\n<option value="kankaanpaa">Kankaanp\xe4\xe4</option>\n<option value="kannonkoski">Kannonkoski</option>\n<option value="kannus">Kannus</option>\n<option value="karijoki">Karijoki</option>\n<option value="karjaa">Karjaa</option>\n<option value="karjalohja">Karjalohja</option>\n<option value="karkkila">Karkkila</option>\n<option value="karstula">Karstula</option>\n<option value="karttula">Karttula</option>\n<option value="karvia">Karvia</option>\n<option value="kaskinen">Kaskinen</option>\n<option value="kauhajoki">Kauhajoki</option>\n<option value="kauhava">Kauhava</option>\n<option value="kauniainen">Kauniainen</option>\n<option value="kaustinen">Kaustinen</option>\n<option value="keitele">Keitele</option>\n<option value="kemi">Kemi</option>\n<option value="kemijarvi">Kemij\xe4rvi</option>\n<option value="keminmaa">Keminmaa</option>\n<option value="kemio">Kemi\xf6</option>\n<option value="kempele">Kempele</option>\n<option value="kerava">Kerava</option>\n<option value="kerimaki">Kerim\xe4ki</option>\n<option value="kestila">Kestil\xe4</option>\n<option value="kesalahti">Kes\xe4lahti</option>\n<option value="keuruu">Keuruu</option>\n<option value="kihnio">Kihni\xf6</option>\n<option value="kiikala">Kiikala</option>\n<option value="kiikoinen">Kiikoinen</option>\n<option value="kiiminki">Kiiminki</option>\n<option value="kinnula">Kinnula</option>\n<option value="kirkkonummi">Kirkkonummi</option>\n<option value="kisko">Kisko</option>\n<option value="kitee">Kitee</option>\n<option value="kittila">Kittil\xe4</option>\n<option value="kiukainen">Kiukainen</option>\n<option value="kiuruvesi">Kiuruvesi</option>\n<option value="kivijarvi">Kivij\xe4rvi</option>\n<option value="kokemaki">Kokem\xe4ki</option>\n<option value="kokkola">Kokkola</option>\n<option value="kolari">Kolari</option>\n<option value="konnevesi">Konnevesi</option>\n<option value="kontiolahti">Kontiolahti</option>\n<option value="korpilahti">Korpilahti</option>\n<option value="korppoo">Korppoo</option>\n<option value="korsnas">Korsn\xe4s</option>\n<option value="kortesjarvi">Kortesj\xe4rvi</option>\n<option value="koskitl">KoskiTl</option>\n<option value="kotka">Kotka</option>\n<option value="kouvola">Kouvola</option>\n<option value="kristiinankaupunki">Kristiinankaupunki</option>\n<option value="kruunupyy">Kruunupyy</option>\n<option value="kuhmalahti">Kuhmalahti</option>\n<option value="kuhmo">Kuhmo</option>\n<option value="kuhmoinen">Kuhmoinen</option>\n<option value="kumlinge">Kumlinge</option>\n<option value="kuopio">Kuopio</option>\n<option value="kuortane">Kuortane</option>\n<option value="kurikka">Kurikka</option>\n<option value="kuru">Kuru</option>\n<option value="kustavi">Kustavi</option>\n<option value="kuusamo">Kuusamo</option>\n<option value="kuusankoski">Kuusankoski</option>\n<option value="kuusjoki">Kuusjoki</option>\n<option value="kylmakoski">Kylm\xe4koski</option>\n<option value="kyyjarvi">Kyyj\xe4rvi</option>\n<option value="kalvia">K\xe4lvi\xe4</option>\n<option value="karkola">K\xe4rk\xf6l\xe4</option>\n<option value="karsamaki">K\xe4rs\xe4m\xe4ki</option>\n<option value="kokar">K\xf6kar</option>\n<option value="koylio">K\xf6yli\xf6</option>\n<option value="lahti">Lahti</option>\n<option value="laihia">Laihia</option>\n<option value="laitila">Laitila</option>\n<option value="lammi">Lammi</option>\n<option value="lapinjarvi">Lapinj\xe4rvi</option>\n<option value="lapinlahti">Lapinlahti</option>\n<option value="lappajarvi">Lappaj\xe4rvi</option>\n<option value="lappeenranta">Lappeenranta</option>\n<option value="lappi">Lappi</option>\n<option value="lapua">Lapua</option>\n<option value="laukaa">Laukaa</option>\n<option value="lavia">Lavia</option>\n<option value="lehtimaki">Lehtim\xe4ki</option>\n<option value="leivonmaki">Leivonm\xe4ki</option>\n<option value="lemi">Lemi</option>\n<option value="lemland">Lemland</option>\n<option value="lempaala">Lemp\xe4\xe4l\xe4</option>\n<option value="lemu">Lemu</option>\n<option value="leppavirta">Lepp\xe4virta</option>\n<option value="lestijarvi">Lestij\xe4rvi</option>\n<option value="lieksa">Lieksa</option>\n<option value="lieto">Lieto</option>\n<option value="liljendal">Liljendal</option>\n<option value="liminka">Liminka</option>\n<option value="liperi">Liperi</option>\n<option value="lohja">Lohja</option>\n<option value="lohtaja">Lohtaja</option>\n<option value="loimaa">Loimaa</option>\n<option value="loppi">Loppi</option>\n<option value="loviisa">Loviisa</option>\n<option value="luhanka">Luhanka</option>\n<option value="lumijoki">Lumijoki</option>\n<option value="lumparland">Lumparland</option>\n<option value="luoto">Luoto</option>\n<option value="luumaki">Luum\xe4ki</option>\n<option value="luvia">Luvia</option>\n<option value="maalahti">Maalahti</option>\n<option value="maaninka">Maaninka</option>\n<option value="maarianhamina">Maarianhamina</option>\n<option value="marttila">Marttila</option>\n<option value="masku">Masku</option>\n<option value="mellila">Mellil\xe4</option>\n<option value="merijarvi">Merij\xe4rvi</option>\n<option value="merikarvia">Merikarvia</option>\n<option value="merimasku">Merimasku</option>\n<option value="miehikkala">Miehikk\xe4l\xe4</option>\n<option value="mikkeli">Mikkeli</option>\n<option value="mouhijarvi">Mouhij\xe4rvi</option>\n<option value="muhos">Muhos</option>\n<option value="multia">Multia</option>\n<option value="muonio">Muonio</option>\n<option value="mustasaari">Mustasaari</option>\n<option value="muurame">Muurame</option>\n<option value="muurla">Muurla</option>\n<option value="mynamaki">Myn\xe4m\xe4ki</option>\n<option value="myrskyla">Myrskyl\xe4</option>\n<option value="mantsala">M\xe4nts\xe4l\xe4</option>\n<option value="mantta">M\xe4ntt\xe4</option>\n<option value="mantyharju">M\xe4ntyharju</option>\n<option value="naantali">Naantali</option>\n<option value="nakkila">Nakkila</option>\n<option value="nastola">Nastola</option>\n<option value="nauvo">Nauvo</option>\n<option value="nilsia">Nilsi\xe4</option>\n<option value="nivala">Nivala</option>\n<option value="nokia">Nokia</option>\n<option value="noormarkku">Noormarkku</option>\n<option value="nousiainen">Nousiainen</option>\n<option value="nummi-pusula">Nummi-Pusula</option>\n<option value="nurmes">Nurmes</option>\n<option value="nurmijarvi">Nurmij\xe4rvi</option>\n<option value="nurmo">Nurmo</option>\n<option value="narpio">N\xe4rpi\xf6</option>\n<option value="oravainen">Oravainen</option>\n<option value="orimattila">Orimattila</option>\n<option value="oripaa">Orip\xe4\xe4</option>\n<option value="orivesi">Orivesi</option>\n<option value="oulainen">Oulainen</option>\n<option value="oulu">Oulu</option>\n<option value="oulunsalo">Oulunsalo</option>\n<option value="outokumpu">Outokumpu</option>\n<option value="padasjoki">Padasjoki</option>\n<option value="paimio">Paimio</option>\n<option value="paltamo">Paltamo</option>\n<option value="parainen">Parainen</option>\n<option value="parikkala">Parikkala</option>\n<option value="parkano">Parkano</option>\n<option value="pedersore">Peders\xf6re</option>\n<option value="pelkosenniemi">Pelkosenniemi</option>\n<option value="pello">Pello</option>\n<option value="perho">Perho</option>\n<option value="pernaja">Pernaja</option>\n<option value="pernio">Perni\xf6</option>\n<option value="pertteli">Pertteli</option>\n<option value="pertunmaa">Pertunmaa</option>\n<option value="petajavesi">Pet\xe4j\xe4vesi</option>\n<option value="pieksamaki">Pieks\xe4m\xe4ki</option>\n<option value="pielavesi">Pielavesi</option>\n<option value="pietarsaari">Pietarsaari</option>\n<option value="pihtipudas">Pihtipudas</option>\n<option value="piikkio">Piikki\xf6</option>\n<option value="piippola">Piippola</option>\n<option value="pirkkala">Pirkkala</option>\n<option value="pohja">Pohja</option>\n<option value="polvijarvi">Polvij\xe4rvi</option>\n<option value="pomarkku">Pomarkku</option>\n<option value="pori">Pori</option>\n<option value="pornainen">Pornainen</option>\n<option value="porvoo">Porvoo</option>\n<option value="posio">Posio</option>\n<option value="pudasjarvi">Pudasj\xe4rvi</option>\n<option value="pukkila">Pukkila</option>\n<option value="pulkkila">Pulkkila</option>\n<option value="punkaharju">Punkaharju</option>\n<option value="punkalaidun">Punkalaidun</option>\n<option value="puolanka">Puolanka</option>\n<option value="puumala">Puumala</option>\n<option value="pyhtaa">Pyht\xe4\xe4</option>\n<option value="pyhajoki">Pyh\xe4joki</option>\n<option value="pyhajarvi">Pyh\xe4j\xe4rvi</option>\n<option value="pyhanta">Pyh\xe4nt\xe4</option>\n<option value="pyharanta">Pyh\xe4ranta</option>\n<option value="pyhaselka">Pyh\xe4selk\xe4</option>\n<option value="pylkonmaki">Pylk\xf6nm\xe4ki</option>\n<option value="palkane">P\xe4lk\xe4ne</option>\n<option value="poytya">P\xf6yty\xe4</option>\n<option value="raahe">Raahe</option>\n<option value="raisio">Raisio</option>\n<option value="rantasalmi">Rantasalmi</option>\n<option value="rantsila">Rantsila</option>\n<option value="ranua">Ranua</option>\n<option value="rauma">Rauma</option>\n<option value="rautalampi">Rautalampi</option>\n<option value="rautavaara">Rautavaara</option>\n<option value="rautjarvi">Rautj\xe4rvi</option>\n<option value="reisjarvi">Reisj\xe4rvi</option>\n<option value="renko">Renko</option>\n<option value="riihimaki">Riihim\xe4ki</option>\n<option value="ristiina">Ristiina</option>\n<option value="ristijarvi">Ristij\xe4rvi</option>\n<option value="rovaniemi">Rovaniemi</option>\n<option value="ruokolahti">Ruokolahti</option>\n<option value="ruotsinpyhtaa">Ruotsinpyht\xe4\xe4</option>\n<option value="ruovesi">Ruovesi</option>\n<option value="rusko">Rusko</option>\n<option value="rymattyla">Rym\xe4ttyl\xe4</option>\n<option value="raakkyla">R\xe4\xe4kkyl\xe4</option>\n<option value="saarijarvi">Saarij\xe4rvi</option>\n<option value="salla">Salla</option>\n<option value="salo">Salo</option>\n<option value="saltvik">Saltvik</option>\n<option value="sammatti">Sammatti</option>\n<option value="sauvo">Sauvo</option>\n<option value="savitaipale">Savitaipale</option>\n<option value="savonlinna">Savonlinna</option>\n<option value="savonranta">Savonranta</option>\n<option value="savukoski">Savukoski</option>\n<option value="seinajoki">Sein\xe4joki</option>\n<option value="sievi">Sievi</option>\n<option value="siikainen">Siikainen</option>\n<option value="siikajoki">Siikajoki</option>\n<option value="siilinjarvi">Siilinj\xe4rvi</option>\n<option value="simo">Simo</option>\n<option value="sipoo">Sipoo</option>\n<option value="siuntio">Siuntio</option>\n<option value="sodankyla">Sodankyl\xe4</option>\n<option value="soini">Soini</option>\n<option value="somero">Somero</option>\n<option value="sonkajarvi">Sonkaj\xe4rvi</option>\n<option value="sotkamo">Sotkamo</option>\n<option value="sottunga">Sottunga</option>\n<option value="sulkava">Sulkava</option>\n<option value="sund">Sund</option>\n<option value="suomenniemi">Suomenniemi</option>\n<option value="suomusjarvi">Suomusj\xe4rvi</option>\n<option value="suomussalmi">Suomussalmi</option>\n<option value="suonenjoki">Suonenjoki</option>\n<option value="sysma">Sysm\xe4</option>\n<option value="sakyla">S\xe4kyl\xe4</option>\n<option value="sarkisalo">S\xe4rkisalo</option>\n<option value="taipalsaari">Taipalsaari</option>\n<option value="taivalkoski">Taivalkoski</option>\n<option value="taivassalo">Taivassalo</option>\n<option value="tammela">Tammela</option>\n<option value="tammisaari">Tammisaari</option>\n<option value="tampere">Tampere</option>\n<option value="tarvasjoki">Tarvasjoki</option>\n<option value="tervo">Tervo</option>\n<option value="tervola">Tervola</option>\n<option value="teuva">Teuva</option>\n<option value="tohmajarvi">Tohmaj\xe4rvi</option>\n<option value="toholampi">Toholampi</option>\n<option value="toivakka">Toivakka</option>\n<option value="tornio">Tornio</option>\n<option value="turku" selected="selected">Turku</option>\n<option value="tuulos">Tuulos</option>\n<option value="tuusniemi">Tuusniemi</option>\n<option value="tuusula">Tuusula</option>\n<option value="tyrnava">Tyrn\xe4v\xe4</option>\n<option value="toysa">T\xf6ys\xe4</option>\n<option value="ullava">Ullava</option>\n<option value="ulvila">Ulvila</option>\n<option value="urjala">Urjala</option>\n<option value="utajarvi">Utaj\xe4rvi</option>\n<option value="utsjoki">Utsjoki</option>\n<option value="uurainen">Uurainen</option>\n<option value="uusikaarlepyy">Uusikaarlepyy</option>\n<option value="uusikaupunki">Uusikaupunki</option>\n<option value="vaala">Vaala</option>\n<option value="vaasa">Vaasa</option>\n<option value="vahto">Vahto</option>\n<option value="valkeakoski">Valkeakoski</option>\n<option value="valkeala">Valkeala</option>\n<option value="valtimo">Valtimo</option>\n<option value="vammala">Vammala</option>\n<option value="vampula">Vampula</option>\n<option value="vantaa">Vantaa</option>\n<option value="varkaus">Varkaus</option>\n<option value="varpaisjarvi">Varpaisj\xe4rvi</option>\n<option value="vehmaa">Vehmaa</option>\n<option value="velkua">Velkua</option>\n<option value="vesanto">Vesanto</option>\n<option value="vesilahti">Vesilahti</option>\n<option value="veteli">Veteli</option>\n<option value="vierema">Vierem\xe4</option>\n<option value="vihanti">Vihanti</option>\n<option value="vihti">Vihti</option>\n<option value="viitasaari">Viitasaari</option>\n<option value="vilppula">Vilppula</option>\n<option value="vimpeli">Vimpeli</option>\n<option value="virolahti">Virolahti</option>\n<option value="virrat">Virrat</option>\n<option value="vardo">V\xe5rd\xf6</option>\n<option value="vahakyro">V\xe4h\xe4kyr\xf6</option>\n<option value="vastanfjard">V\xe4stanfj\xe4rd</option>\n<option value="voyri-maksamaa">V\xf6yri-Maksamaa</option>\n<option value="yliharma">Ylih\xe4rm\xe4</option>\n<option value="yli-ii">Yli-Ii</option>\n<option value="ylikiiminki">Ylikiiminki</option>\n<option value="ylistaro">Ylistaro</option>\n<option value="ylitornio">Ylitornio</option>\n<option value="ylivieska">Ylivieska</option>\n<option value="ylamaa">Yl\xe4maa</option>\n<option value="ylane">Yl\xe4ne</option>\n<option value="ylojarvi">Yl\xf6j\xe4rvi</option>\n<option value="ypaja">Yp\xe4j\xe4</option>\n<option value="aetsa">\xc4ets\xe4</option>\n<option value="ahtari">\xc4ht\xe4ri</option>\n<option value="aanekoski">\xc4\xe4nekoski</option>\n</select>' + +# FISocialSecurityNumber +############################################################## + +>>> from django.contrib.localflavor.fi.forms import FISocialSecurityNumber +>>> f = FISocialSecurityNumber() +>>> f.clean('010101-0101') +u'010101-0101' +>>> f.clean('010101+0101') +u'010101+0101' +>>> f.clean('010101A0101') +u'010101A0101' +>>> f.clean('101010-0102') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid Finnish social security number.'] +>>> f.clean('10a010-0101') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid Finnish social security number.'] +>>> f.clean('101010-0\xe401') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid Finnish social security number.'] +>>> f.clean('101010b0101') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid Finnish social security number.'] +>>> 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 = FISocialSecurityNumber(required=False) +>>> f.clean('010101-0101') +u'010101-0101' +>>> f.clean(None) +u'' +>>> f.clean('') +u'' + +# BRZipCodeField ############################################################ +>>> from django.contrib.localflavor.br.forms import BRZipCodeField +>>> f = BRZipCodeField() +>>> f.clean('12345-123') +u'12345-123' +>>> f.clean('12345_123') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] +>>> f.clean('1234-123') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] +>>> f.clean('abcde-abc') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] +>>> f.clean('12345-') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] +>>> f.clean('-123') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] +>>> 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 = BRZipCodeField(required=False) +>>> f.clean(None) +u'' +>>> f.clean('') +u'' +>>> f.clean('-123') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] +>>> f.clean('12345-') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] +>>> f.clean('abcde-abc') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] +>>> f.clean('1234-123') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] +>>> f.clean('12345_123') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX-XXX.'] +>>> f.clean('12345-123') +u'12345-123' + +# BRCNPJField ############################################################ + +>>> from django.contrib.localflavor.br.forms import BRCNPJField +>>> f = BRCNPJField(required=True) +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('12-345-678/9012-10') +Traceback (most recent call last): +... +ValidationError: [u'Invalid CNPJ number.'] +>>> f.clean('12.345.678/9012-10') +Traceback (most recent call last): +... +ValidationError: [u'Invalid CNPJ number.'] +>>> f.clean('12345678/9012-10') +Traceback (most recent call last): +... +ValidationError: [u'Invalid CNPJ number.'] +>>> f.clean('64.132.916/0001-88') +'64.132.916/0001-88' +>>> f.clean('64-132-916/0001-88') +'64-132-916/0001-88' +>>> f.clean('64132916/0001-88') +'64132916/0001-88' +>>> f.clean('64.132.916/0001-XX') +Traceback (most recent call last): +... +ValidationError: [u'This field requires only numbers.'] +>>> f = BRCNPJField(required=False) +>>> f.clean('') +u'' + +# BRCPFField ################################################################# + +>>> from django.contrib.localflavor.br.forms import BRCPFField +>>> f = BRCPFField() +>>> 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('489.294.654-54') +Traceback (most recent call last): +... +ValidationError: [u'Invalid CPF number.'] +>>> f.clean('295.669.575-98') +Traceback (most recent call last): +... +ValidationError: [u'Invalid CPF number.'] +>>> f.clean('539.315.127-22') +Traceback (most recent call last): +... +ValidationError: [u'Invalid CPF number.'] +>>> f.clean('663.256.017-26') +u'663.256.017-26' +>>> f.clean('66325601726') +u'66325601726' +>>> f.clean('375.788.573-20') +u'375.788.573-20' +>>> f.clean('84828509895') +u'84828509895' +>>> f.clean('375.788.573-XX') +Traceback (most recent call last): +... +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.'] +>>> f.clean('123.456.78') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at least 11 characters.'] +>>> f.clean('123456789555') +Traceback (most recent call last): +... +ValidationError: [u'This field requires at most 11 digits or 14 characters.'] +>>> f = BRCPFField(required=False) +>>> f.clean('') +u'' +>>> f.clean(None) +u'' + +# BRPhoneNumberField ######################################################### + +>>> from django.contrib.localflavor.br.forms import BRPhoneNumberField +>>> f = BRPhoneNumberField() +>>> f.clean('41-3562-3464') +u'41-3562-3464' +>>> f.clean('4135623464') +u'41-3562-3464' +>>> f.clean('41 3562-3464') +u'41-3562-3464' +>>> f.clean('41 3562 3464') +u'41-3562-3464' +>>> f.clean('(41) 3562 3464') +u'41-3562-3464' +>>> f.clean('41.3562.3464') +u'41-3562-3464' +>>> f.clean('41.3562-3464') +u'41-3562-3464' +>>> f.clean(' (41) 3562.3464') +u'41-3562-3464' +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = BRPhoneNumberField(required=False) +>>> f.clean('') +u'' +>>> f.clean(None) +u'' +>>> f.clean(' (41) 3562.3464') +u'41-3562-3464' +>>> f.clean('41.3562-3464') +u'41-3562-3464' +>>> f.clean('(41) 3562 3464') +u'41-3562-3464' +>>> f.clean('4135623464') +u'41-3562-3464' +>>> f.clean('41 3562-3464') +u'41-3562-3464' + +# BRStateSelect ############################################################## + +>>> from django.contrib.localflavor.br.forms import BRStateSelect +>>> w = BRStateSelect() +>>> w.render('states', 'PR') +u'<select name="states">\n<option value="AC">Acre</option>\n<option value="AL">Alagoas</option>\n<option value="AP">Amap\xe1</option>\n<option value="AM">Amazonas</option>\n<option value="BA">Bahia</option>\n<option value="CE">Cear\xe1</option>\n<option value="DF">Distrito Federal</option>\n<option value="ES">Esp\xedrito Santo</option>\n<option value="GO">Goi\xe1s</option>\n<option value="MA">Maranh\xe3o</option>\n<option value="MT">Mato Grosso</option>\n<option value="MS">Mato Grosso do Sul</option>\n<option value="MG">Minas Gerais</option>\n<option value="PA">Par\xe1</option>\n<option value="PB">Para\xedba</option>\n<option value="PR" selected="selected">Paran\xe1</option>\n<option value="PE">Pernambuco</option>\n<option value="PI">Piau\xed</option>\n<option value="RJ">Rio de Janeiro</option>\n<option value="RN">Rio Grande do Norte</option>\n<option value="RS">Rio Grande do Sul</option>\n<option value="RO">Rond\xf4nia</option>\n<option value="RR">Roraima</option>\n<option value="SC">Santa Catarina</option>\n<option value="SP">S\xe3o Paulo</option>\n<option value="SE">Sergipe</option>\n<option value="TO">Tocantins</option>\n</select>' + +# DEZipCodeField ############################################################## + +>>> from django.contrib.localflavor.de.forms import DEZipCodeField +>>> f = DEZipCodeField() +>>> f.clean('99423') +u'99423' +>>> f.clean(' 99423') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXXX.'] + +# DEStateSelect ############################################################# + +>>> from django.contrib.localflavor.de.forms import DEStateSelect +>>> w = DEStateSelect() +>>> w.render('states', 'TH') +u'<select name="states">\n<option value="BW">Baden-Wuerttemberg</option>\n<option value="BY">Bavaria</option>\n<option value="BE">Berlin</option>\n<option value="BB">Brandenburg</option>\n<option value="HB">Bremen</option>\n<option value="HH">Hamburg</option>\n<option value="HE">Hessen</option>\n<option value="MV">Mecklenburg-Western Pomerania</option>\n<option value="NI">Lower Saxony</option>\n<option value="NW">North Rhine-Westphalia</option>\n<option value="RP">Rhineland-Palatinate</option>\n<option value="SL">Saarland</option>\n<option value="SN">Saxony</option>\n<option value="ST">Saxony-Anhalt</option>\n<option value="SH">Schleswig-Holstein</option>\n<option value="TH" selected="selected">Thuringia</option>\n</select>' + +# DEIdentityCardNumberField ################################################# + +>>> from django.contrib.localflavor.de.forms import DEIdentityCardNumberField +>>> f = DEIdentityCardNumberField() +>>> f.clean('7549313035D-6004103-0903042-0') +u'7549313035D-6004103-0903042-0' +>>> f.clean('9786324830D 6104243 0910271 2') +u'9786324830D-6104243-0910271-2' +>>> f.clean('0434657485D-6407276-0508137-9') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid German identity card number in XXXXXXXXXXX-XXXXXXX-XXXXXXX-X format.'] + +# CHZipCodeField ############################################################ + +>>> from django.contrib.localflavor.ch.forms import CHZipCodeField +>>> f = CHZipCodeField() +>>> f.clean('800x') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXX.'] +>>> f.clean('80 00') +Traceback (most recent call last): +... +ValidationError: [u'Enter a zip code in the format XXXX.'] +>>> f.clean('8000') +u'8000' + +# CHPhoneNumberField ######################################################## + +>>> from django.contrib.localflavor.ch.forms import CHPhoneNumberField +>>> f = CHPhoneNumberField() +>>> f.clean('01234567890') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must be in 0XX XXX XX XX format.'] +>>> f.clean('1234567890') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must be in 0XX XXX XX XX format.'] +>>> f.clean('0123456789') +u'012 345 67 89' + +# CHIdentityCardNumberField ################################################# + +>>> from django.contrib.localflavor.ch.forms import CHIdentityCardNumberField +>>> f = CHIdentityCardNumberField() +>>> f.clean('C1234567<0') +u'C1234567<0' +>>> f.clean('C1234567<1') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid Swiss identity or passport card number in X1234567<0 or 1234567890 format.'] +>>> f.clean('2123456700') +u'2123456700' +>>> f.clean('2123456701') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid Swiss identity or passport card number in X1234567<0 or 1234567890 format.'] + +# CHStateSelect ############################################################# + +>>> from django.contrib.localflavor.ch.forms import CHStateSelect +>>> w = CHStateSelect() +>>> w.render('state', 'AG') +u'<select name="state">\n<option value="AG" selected="selected">Aargau</option>\n<option value="AI">Appenzell Innerrhoden</option>\n<option value="AR">Appenzell Ausserrhoden</option>\n<option value="BS">Basel-Stadt</option>\n<option value="BL">Basel-Land</option>\n<option value="BE">Berne</option>\n<option value="FR">Fribourg</option>\n<option value="GE">Geneva</option>\n<option value="GL">Glarus</option>\n<option value="GR">Graubuenden</option>\n<option value="JU">Jura</option>\n<option value="LU">Lucerne</option>\n<option value="NE">Neuchatel</option>\n<option value="NW">Nidwalden</option>\n<option value="OW">Obwalden</option>\n<option value="SH">Schaffhausen</option>\n<option value="SZ">Schwyz</option>\n<option value="SO">Solothurn</option>\n<option value="SG">St. Gallen</option>\n<option value="TG">Thurgau</option>\n<option value="TI">Ticino</option>\n<option value="UR">Uri</option>\n<option value="VS">Valais</option>\n<option value="VD">Vaud</option>\n<option value="ZG">Zug</option>\n<option value="ZH">Zurich</option>\n</select>' + +## AUPostCodeField ########################################################## + +A field that accepts a four digit Australian post code. + +>>> from django.contrib.localflavor.au.forms import AUPostCodeField +>>> f = AUPostCodeField() +>>> f.clean('1234') +u'1234' +>>> f.clean('2000') +u'2000' +>>> f.clean('abcd') +Traceback (most recent call last): +... +ValidationError: [u'Enter a 4 digit post code.'] +>>> f.clean('20001') +Traceback (most recent call last): +... +ValidationError: [u'Enter a 4 digit post code.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = AUPostCodeField(required=False) +>>> f.clean('1234') +u'1234' +>>> f.clean('2000') +u'2000' +>>> f.clean('abcd') +Traceback (most recent call last): +... +ValidationError: [u'Enter a 4 digit post code.'] +>>> f.clean('20001') +Traceback (most recent call last): +... +ValidationError: [u'Enter a 4 digit post code.'] +>>> f.clean(None) +u'' +>>> f.clean('') +u'' + +## AUPhoneNumberField ######################################################## + +A field that accepts a 10 digit Australian phone number. +llows spaces and parentheses around area code. + +>>> from django.contrib.localflavor.au.forms import AUPhoneNumberField +>>> f = AUPhoneNumberField() +>>> f.clean('1234567890') +u'1234567890' +>>> f.clean('0213456789') +u'0213456789' +>>> f.clean('02 13 45 67 89') +u'0213456789' +>>> f.clean('(02) 1345 6789') +u'0213456789' +>>> f.clean('(02) 1345-6789') +u'0213456789' +>>> f.clean('(02)1345-6789') +u'0213456789' +>>> f.clean('0408 123 456') +u'0408123456' +>>> f.clean('123') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must contain 10 digits.'] +>>> f.clean('1800DJANGO') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must contain 10 digits.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] + +>>> f = AUPhoneNumberField(required=False) +>>> f.clean('1234567890') +u'1234567890' +>>> f.clean('0213456789') +u'0213456789' +>>> f.clean('02 13 45 67 89') +u'0213456789' +>>> f.clean('(02) 1345 6789') +u'0213456789' +>>> f.clean('(02) 1345-6789') +u'0213456789' +>>> f.clean('(02)1345-6789') +u'0213456789' +>>> f.clean('0408 123 456') +u'0408123456' +>>> f.clean('123') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must contain 10 digits.'] +>>> f.clean('1800DJANGO') +Traceback (most recent call last): +... +ValidationError: [u'Phone numbers must contain 10 digits.'] +>>> f.clean(None) +u'' +>>> f.clean('') +u'' + +## AUStateSelect ############################################################# + +AUStateSelect is a Select widget that uses a list of Australian +states/territories as its choices. + +>>> from django.contrib.localflavor.au.forms import AUStateSelect +>>> f = AUStateSelect() +>>> print f.render('state', 'NSW') +<select name="state"> +<option value="ACT">Australian Capital Territory</option> +<option value="NSW" selected="selected">New South Wales</option> +<option value="NT">Northern Territory</option> +<option value="QLD">Queensland</option> +<option value="SA">South Australia</option> +<option value="TAS">Tasmania</option> +<option value="VIC">Victoria</option> +<option value="WA">Western Australia</option> +</select> + +## ISIdNumberField ############################################################# + +>>> from django.contrib.localflavor.is_.forms import * +>>> f = ISIdNumberField() +>>> f.clean('2308803449') +u'230880-3449' +>>> f.clean('230880-3449') +u'230880-3449' +>>> f.clean('230880 3449') +u'230880-3449' +>>> f.clean('230880343') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at least 10 characters.'] +>>> f.clean('230880343234') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at most 11 characters.'] +>>> f.clean('abcdefghijk') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid Icelandic identification number. The format is XXXXXX-XXXX.'] +>>> 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('2308803439') +Traceback (most recent call last): +... +ValidationError: [u'The Icelandic identification number is not valid.'] +>>> f.clean('2308803440') +u'230880-3440' +>>> f = ISIdNumberField(required=False) +>>> f.clean(None) +u'' +>>> f.clean('') +u'' + +## ISPhoneNumberField ############################################################# + +>>> from django.contrib.localflavor.is_.forms import * +>>> f = ISPhoneNumberField() +>>> f.clean('1234567') +u'1234567' +>>> f.clean('123 4567') +u'1234567' +>>> f.clean('123-4567') +u'1234567' +>>> f.clean('123-456') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid value.'] +>>> f.clean('123456') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at least 7 characters.'] +>>> f.clean('123456555') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value has at most 8 characters.'] +>>> 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.'] +>>> f.clean(' 12367 ') +Traceback (most recent call last): +... +ValidationError: [u'Enter a valid value.'] + +>>> 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 = ISPhoneNumberField(required=False) +>>> f.clean(None) +u'' +>>> f.clean('') +u'' + +## ISPostalCodeSelect ############################################################# + +>>> from django.contrib.localflavor.is_.forms import * +>>> f = ISPostalCodeSelect() + +>>> f.render('foo', 'bar') +u'<select name="foo">\n<option value="101">101 Reykjav\xedk</option>\n<option value="103">103 Reykjav\xedk</option>\n<option value="104">104 Reykjav\xedk</option>\n<option value="105">105 Reykjav\xedk</option>\n<option value="107">107 Reykjav\xedk</option>\n<option value="108">108 Reykjav\xedk</option>\n<option value="109">109 Reykjav\xedk</option>\n<option value="110">110 Reykjav\xedk</option>\n<option value="111">111 Reykjav\xedk</option>\n<option value="112">112 Reykjav\xedk</option>\n<option value="113">113 Reykjav\xedk</option>\n<option value="116">116 Kjalarnes</option>\n<option value="121">121 Reykjav\xedk</option>\n<option value="123">123 Reykjav\xedk</option>\n<option value="124">124 Reykjav\xedk</option>\n<option value="125">125 Reykjav\xedk</option>\n<option value="127">127 Reykjav\xedk</option>\n<option value="128">128 Reykjav\xedk</option>\n<option value="129">129 Reykjav\xedk</option>\n<option value="130">130 Reykjav\xedk</option>\n<option value="132">132 Reykjav\xedk</option>\n<option value="150">150 Reykjav\xedk</option>\n<option value="155">155 Reykjav\xedk</option>\n<option value="170">170 Seltjarnarnes</option>\n<option value="172">172 Seltjarnarnes</option>\n<option value="190">190 Vogar</option>\n<option value="200">200 K\xf3pavogur</option>\n<option value="201">201 K\xf3pavogur</option>\n<option value="202">202 K\xf3pavogur</option>\n<option value="203">203 K\xf3pavogur</option>\n<option value="210">210 Gar\xf0ab\xe6r</option>\n<option value="212">212 Gar\xf0ab\xe6r</option>\n<option value="220">220 Hafnarfj\xf6r\xf0ur</option>\n<option value="221">221 Hafnarfj\xf6r\xf0ur</option>\n<option value="222">222 Hafnarfj\xf6r\xf0ur</option>\n<option value="225">225 \xc1lftanes</option>\n<option value="230">230 Reykjanesb\xe6r</option>\n<option value="232">232 Reykjanesb\xe6r</option>\n<option value="233">233 Reykjanesb\xe6r</option>\n<option value="235">235 Keflav\xedkurflugv\xf6llur</option>\n<option value="240">240 Grindav\xedk</option>\n<option value="245">245 Sandger\xf0i</option>\n<option value="250">250 Gar\xf0ur</option>\n<option value="260">260 Reykjanesb\xe6r</option>\n<option value="270">270 Mosfellsb\xe6r</option>\n<option value="300">300 Akranes</option>\n<option value="301">301 Akranes</option>\n<option value="302">302 Akranes</option>\n<option value="310">310 Borgarnes</option>\n<option value="311">311 Borgarnes</option>\n<option value="320">320 Reykholt \xed Borgarfir\xf0i</option>\n<option value="340">340 Stykkish\xf3lmur</option>\n<option value="345">345 Flatey \xe1 Brei\xf0afir\xf0i</option>\n<option value="350">350 Grundarfj\xf6r\xf0ur</option>\n<option value="355">355 \xd3lafsv\xedk</option>\n<option value="356">356 Sn\xe6fellsb\xe6r</option>\n<option value="360">360 Hellissandur</option>\n<option value="370">370 B\xfa\xf0ardalur</option>\n<option value="371">371 B\xfa\xf0ardalur</option>\n<option value="380">380 Reykh\xf3lahreppur</option>\n<option value="400">400 \xcdsafj\xf6r\xf0ur</option>\n<option value="401">401 \xcdsafj\xf6r\xf0ur</option>\n<option value="410">410 Hn\xedfsdalur</option>\n<option value="415">415 Bolungarv\xedk</option>\n<option value="420">420 S\xfa\xf0av\xedk</option>\n<option value="425">425 Flateyri</option>\n<option value="430">430 Su\xf0ureyri</option>\n<option value="450">450 Patreksfj\xf6r\xf0ur</option>\n<option value="451">451 Patreksfj\xf6r\xf0ur</option>\n<option value="460">460 T\xe1lknafj\xf6r\xf0ur</option>\n<option value="465">465 B\xedldudalur</option>\n<option value="470">470 \xdeingeyri</option>\n<option value="471">471 \xdeingeyri</option>\n<option value="500">500 Sta\xf0ur</option>\n<option value="510">510 H\xf3lmav\xedk</option>\n<option value="512">512 H\xf3lmav\xedk</option>\n<option value="520">520 Drangsnes</option>\n<option value="522">522 Kj\xf6rvogur</option>\n<option value="523">523 B\xe6r</option>\n<option value="524">524 Nor\xf0urfj\xf6r\xf0ur</option>\n<option value="530">530 Hvammstangi</option>\n<option value="531">531 Hvammstangi</option>\n<option value="540">540 Bl\xf6ndu\xf3s</option>\n<option value="541">541 Bl\xf6ndu\xf3s</option>\n<option value="545">545 Skagastr\xf6nd</option>\n<option value="550">550 Sau\xf0\xe1rkr\xf3kur</option>\n<option value="551">551 Sau\xf0\xe1rkr\xf3kur</option>\n<option value="560">560 Varmahl\xed\xf0</option>\n<option value="565">565 Hofs\xf3s</option>\n<option value="566">566 Hofs\xf3s</option>\n<option value="570">570 Flj\xf3t</option>\n<option value="580">580 Siglufj\xf6r\xf0ur</option>\n<option value="600">600 Akureyri</option>\n<option value="601">601 Akureyri</option>\n<option value="602">602 Akureyri</option>\n<option value="603">603 Akureyri</option>\n<option value="610">610 Greniv\xedk</option>\n<option value="611">611 Gr\xedmsey</option>\n<option value="620">620 Dalv\xedk</option>\n<option value="621">621 Dalv\xedk</option>\n<option value="625">625 \xd3lafsfj\xf6r\xf0ur</option>\n<option value="630">630 Hr\xedsey</option>\n<option value="640">640 H\xfasav\xedk</option>\n<option value="641">641 H\xfasav\xedk</option>\n<option value="645">645 Fossh\xf3ll</option>\n<option value="650">650 Laugar</option>\n<option value="660">660 M\xfdvatn</option>\n<option value="670">670 K\xf3pasker</option>\n<option value="671">671 K\xf3pasker</option>\n<option value="675">675 Raufarh\xf6fn</option>\n<option value="680">680 \xde\xf3rsh\xf6fn</option>\n<option value="681">681 \xde\xf3rsh\xf6fn</option>\n<option value="685">685 Bakkafj\xf6r\xf0ur</option>\n<option value="690">690 Vopnafj\xf6r\xf0ur</option>\n<option value="700">700 Egilssta\xf0ir</option>\n<option value="701">701 Egilssta\xf0ir</option>\n<option value="710">710 Sey\xf0isfj\xf6r\xf0ur</option>\n<option value="715">715 Mj\xf3ifj\xf6r\xf0ur</option>\n<option value="720">720 Borgarfj\xf6r\xf0ur eystri</option>\n<option value="730">730 Rey\xf0arfj\xf6r\xf0ur</option>\n<option value="735">735 Eskifj\xf6r\xf0ur</option>\n<option value="740">740 Neskaupsta\xf0ur</option>\n<option value="750">750 F\xe1skr\xfa\xf0sfj\xf6r\xf0ur</option>\n<option value="755">755 St\xf6\xf0varfj\xf6r\xf0ur</option>\n<option value="760">760 Brei\xf0dalsv\xedk</option>\n<option value="765">765 Dj\xfapivogur</option>\n<option value="780">780 H\xf6fn \xed Hornafir\xf0i</option>\n<option value="781">781 H\xf6fn \xed Hornafir\xf0i</option>\n<option value="785">785 \xd6r\xe6fi</option>\n<option value="800">800 Selfoss</option>\n<option value="801">801 Selfoss</option>\n<option value="802">802 Selfoss</option>\n<option value="810">810 Hverager\xf0i</option>\n<option value="815">815 \xdeorl\xe1ksh\xf6fn</option>\n<option value="820">820 Eyrarbakki</option>\n<option value="825">825 Stokkseyri</option>\n<option value="840">840 Laugarvatn</option>\n<option value="845">845 Fl\xfa\xf0ir</option>\n<option value="850">850 Hella</option>\n<option value="851">851 Hella</option>\n<option value="860">860 Hvolsv\xf6llur</option>\n<option value="861">861 Hvolsv\xf6llur</option>\n<option value="870">870 V\xedk</option>\n<option value="871">871 V\xedk</option>\n<option value="880">880 Kirkjub\xe6jarklaustur</option>\n<option value="900">900 Vestmannaeyjar</option>\n<option value="902">902 Vestmannaeyjar</option>\n</select>' + +## CLRutField ############################################################# + +CLRutField is a Field that checks the validity of the Chilean +personal identification number (RUT). It has two modes relaxed (default) and +strict. + +>>> from django.contrib.localflavor.cl.forms import CLRutField +>>> rut = CLRutField() + +>>> rut.clean('11-6') +'11-6' +>>> rut.clean('116') +'11-6' + +# valid format, bad verifier. +>>> rut.clean('11.111.111-0') +Traceback (most recent call last): +... +ValidationError: [u'The Chilean RUT is not valid.'] +>>> rut.clean('111') +Traceback (most recent call last): +... +ValidationError: [u'The Chilean RUT is not valid.'] + +>>> rut.clean('767484100') +'76.748.410-0' +>>> rut.clean('78.412.790-7') +'78.412.790-7' +>>> rut.clean('8.334.6043') +'8.334.604-3' +>>> rut.clean('76793310-K') +'76.793.310-K' + +Strict RUT usage (does not allow imposible values) +>>> rut = CLRutField(strict=True) + +>>> rut.clean('11-6') +Traceback (most recent call last): +... +ValidationError: [u'Enter valid a Chilean RUT. The format is XX.XXX.XXX-X.'] + +# valid format, bad verifier. +>>> rut.clean('11.111.111-0') +Traceback (most recent call last): +... +ValidationError: [u'The Chilean RUT is not valid.'] + +# Correct input, invalid format. +>>> rut.clean('767484100') +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' +>>> rut.clean('8.334.6043') +Traceback (most recent call last): +... +ValidationError: [u'Enter valid a Chilean RUT. The format is XX.XXX.XXX-X.'] +>>> rut.clean('76793310-K') +Traceback (most recent call last): +... +ValidationError: [u'Enter valid a Chilean RUT. The format is XX.XXX.XXX-X.'] + +""" diff --git a/tests/regressiontests/forms/regressions.py b/tests/regressiontests/forms/regressions.py new file mode 100644 index 0000000000..5fe057b5d8 --- /dev/null +++ b/tests/regressiontests/forms/regressions.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +# Tests to prevent against recurrences of earlier bugs. + +regression_tests = r""" +It should be possible to re-use attribute dictionaries (#3810) +>>> from django.newforms import * +>>> extra_attrs = {'class': 'special'} +>>> class TestForm(Form): +... f1 = CharField(max_length=10, widget=TextInput(attrs=extra_attrs)) +... f2 = CharField(widget=TextInput(attrs=extra_attrs)) +>>> TestForm(auto_id=False).as_p() +u'<p>F1: <input type="text" class="special" name="f1" maxlength="10" /></p>\n<p>F2: <input type="text" class="special" name="f2" /></p>' + +####################### +# Tests for form i18n # +####################### +There were some problems with form translations in #3600 + +>>> from django.utils.translation import gettext_lazy, activate, deactivate +>>> class SomeForm(Form): +... username = CharField(max_length=10, label=gettext_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> +>>> activate('de') +>>> print f.as_p() +<p><label for="id_username">Benutzername:</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')) +>>> class SomeForm(Form): +... somechoice = ChoiceField(choices=GENDERS, widget=RadioSelect()) +>>> 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>' + +####################### +# 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'} +""" diff --git a/tests/regressiontests/forms/tests.py b/tests/regressiontests/forms/tests.py index a9ce8d23b3..4844267b43 100644 --- a/tests/regressiontests/forms/tests.py +++ b/tests/regressiontests/forms/tests.py @@ -1,8 +1,16 @@ # -*- coding: utf-8 -*- -r""" +from localflavor import localflavor_tests +from regressions import regression_tests + +form_tests = r""" >>> from django.newforms import * >>> import datetime +>>> import time >>> import re +>>> try: +... from decimal import Decimal +... except ImportError: +... from django.utils._decimal import Decimal ########### # Widgets # @@ -190,30 +198,30 @@ u'<input type="file" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u01 >>> w = Textarea() >>> w.render('msg', '') -u'<textarea name="msg"></textarea>' +u'<textarea rows="10" cols="40" name="msg"></textarea>' >>> w.render('msg', None) -u'<textarea name="msg"></textarea>' +u'<textarea rows="10" cols="40" name="msg"></textarea>' >>> w.render('msg', 'value') -u'<textarea name="msg">value</textarea>' +u'<textarea rows="10" cols="40" name="msg">value</textarea>' >>> w.render('msg', 'some "quoted" & ampersanded value') -u'<textarea name="msg">some "quoted" & ampersanded value</textarea>' ->>> w.render('msg', 'value', attrs={'class': 'pretty'}) -u'<textarea name="msg" class="pretty">value</textarea>' +u'<textarea rows="10" cols="40" name="msg">some "quoted" & ampersanded value</textarea>' +>>> w.render('msg', 'value', attrs={'class': 'pretty', 'rows': 20}) +u'<textarea class="pretty" rows="20" cols="40" name="msg">value</textarea>' You can also pass 'attrs' to the constructor: >>> w = Textarea(attrs={'class': 'pretty'}) >>> w.render('msg', '') -u'<textarea class="pretty" name="msg"></textarea>' +u'<textarea rows="10" cols="40" name="msg" class="pretty"></textarea>' >>> w.render('msg', 'example') -u'<textarea class="pretty" name="msg">example</textarea>' +u'<textarea rows="10" cols="40" name="msg" class="pretty">example</textarea>' 'attrs' passed to render() get precedence over those passed to the constructor: >>> w = Textarea(attrs={'class': 'pretty'}) >>> w.render('msg', '', attrs={'class': 'special'}) -u'<textarea class="special" name="msg"></textarea>' +u'<textarea rows="10" cols="40" name="msg" class="special"></textarea>' >>> w.render('msg', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'}) -u'<textarea class="fun" name="msg">\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111</textarea>' +u'<textarea rows="10" cols="40" name="msg" class="fun">\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111</textarea>' # CheckboxInput Widget ######################################################## @@ -655,10 +663,31 @@ Traceback (most recent call last): ... IndexError: list index out of range +# Unicode choices are correctly rendered as HTML >>> w = RadioSelect() >>> unicode(w.render('email', 'ŠĐĆŽćžšđ', choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')])) u'<ul>\n<li><label><input checked="checked" type="radio" name="email" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" /> \u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</label></li>\n<li><label><input type="radio" name="email" value="\u0107\u017e\u0161\u0111" /> abc\u0107\u017e\u0161\u0111</label></li>\n</ul>' +# Attributes provided at instantiation are passed to the constituent inputs +>>> w = RadioSelect(attrs={'id':'foo'}) +>>> print w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) +<ul> +<li><label><input checked="checked" type="radio" id="foo_0" value="J" name="beatle" /> John</label></li> +<li><label><input type="radio" id="foo_1" value="P" name="beatle" /> Paul</label></li> +<li><label><input type="radio" id="foo_2" value="G" name="beatle" /> George</label></li> +<li><label><input type="radio" id="foo_3" value="R" name="beatle" /> Ringo</label></li> +</ul> + +# Attributes provided at render-time are passed to the constituent inputs +>>> w = RadioSelect() +>>> print w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')), attrs={'id':'bar'}) +<ul> +<li><label><input checked="checked" type="radio" id="bar_0" value="J" name="beatle" /> John</label></li> +<li><label><input type="radio" id="bar_1" value="P" name="beatle" /> Paul</label></li> +<li><label><input type="radio" id="bar_2" value="G" name="beatle" /> George</label></li> +<li><label><input type="radio" id="bar_3" value="R" name="beatle" /> Ringo</label></li> +</ul> + # CheckboxSelectMultiple Widget ############################################### >>> w = CheckboxSelectMultiple() @@ -780,6 +809,11 @@ u'<ul>\n<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li u'<input type="text" class="big" value="john" name="name_0" /><br /><input type="text" class="small" value="lennon" name="name_1" />' >>> w.render('name', 'john__lennon') u'<input type="text" class="big" value="john" name="name_0" /><br /><input type="text" class="small" value="lennon" name="name_1" />' +>>> w.render('name', 'john__lennon', attrs={'id':'foo'}) +u'<input id="foo_0" type="text" class="big" value="john" name="name_0" /><br /><input id="foo_1" type="text" class="small" value="lennon" name="name_1" />' +>>> w = MyMultiWidget(widgets=(TextInput(attrs={'class': 'big'}), TextInput(attrs={'class': 'small'})), attrs={'id': 'bar'}) +>>> w.render('name', ['john', 'lennon']) +u'<input id="bar_0" type="text" class="big" value="john" name="name_0" /><br /><input id="bar_1" type="text" class="small" value="lennon" name="name_1" />' # SplitDateTimeWidget ######################################################### @@ -1016,6 +1050,133 @@ Traceback (most recent call last): ... ValidationError: [u'Ensure this value is less than or equal to 20.'] +# FloatField ################################################################## + +>>> f = FloatField() +>>> 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('1') +1.0 +>>> isinstance(f.clean('1'), float) +True +>>> f.clean('23') +23.0 +>>> f.clean('3.14') +3.1400000000000001 +>>> f.clean('a') +Traceback (most recent call last): +... +ValidationError: [u'Enter a number.'] +>>> f.clean('1.0 ') +1.0 +>>> f.clean(' 1.0') +1.0 +>>> f.clean(' 1.0 ') +1.0 +>>> f.clean('1.0a') +Traceback (most recent call last): +... +ValidationError: [u'Enter a number.'] + +>>> f = FloatField(required=False) +>>> f.clean('') + +>>> f.clean(None) + +>>> f.clean('1') +1.0 + +FloatField accepts min_value and max_value just like IntegerField: +>>> f = FloatField(max_value=1.5, min_value=0.5) + +>>> f.clean('1.6') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value is less than or equal to 1.5.'] +>>> f.clean('0.4') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value is greater than or equal to 0.5.'] +>>> f.clean('1.5') +1.5 +>>> f.clean('0.5') +0.5 + +# DecimalField ################################################################ + +>>> f = DecimalField(max_digits=4, decimal_places=2) +>>> 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('1') +Decimal("1") +>>> isinstance(f.clean('1'), Decimal) +True +>>> f.clean('23') +Decimal("23") +>>> f.clean('3.14') +Decimal("3.14") +>>> f.clean('a') +Traceback (most recent call last): +... +ValidationError: [u'Enter a number.'] +>>> f.clean('1.0 ') +Decimal("1.0") +>>> f.clean(' 1.0') +Decimal("1.0") +>>> f.clean(' 1.0 ') +Decimal("1.0") +>>> f.clean('1.0a') +Traceback (most recent call last): +... +ValidationError: [u'Enter a number.'] +>>> f.clean('123.45') +Traceback (most recent call last): +... +ValidationError: [u'Ensure that there are no more than 4 digits in total.'] +>>> f.clean('1.234') +Traceback (most recent call last): +... +ValidationError: [u'Ensure that there are no more than 2 decimal places.'] +>>> f.clean('123.4') +Traceback (most recent call last): +... +ValidationError: [u'Ensure that there are no more than 2 digits before the decimal point.'] +>>> f = DecimalField(max_digits=4, decimal_places=2, required=False) +>>> f.clean('') + +>>> f.clean(None) + +>>> f.clean('1') +Decimal("1") + +DecimalField accepts min_value and max_value just like IntegerField: +>>> f = DecimalField(max_digits=4, decimal_places=2, max_value=Decimal('1.5'), min_value=Decimal('0.5')) + +>>> f.clean('1.6') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value is less than or equal to 1.5.'] +>>> f.clean('0.4') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value is greater than or equal to 0.5.'] +>>> f.clean('1.5') +Decimal("1.5") +>>> f.clean('0.5') +Decimal("0.5") + # DateField ################################################################### >>> import datetime @@ -1452,7 +1613,7 @@ u'' >>> f.clean('http://www.google.com') # This will fail if there's no Internet connection u'http://www.google.com' -EmailField also access min_length and max_length parameters, for convenience. +URLField also access min_length and max_length parameters, for convenience. >>> f = URLField(min_length=15, max_length=20) >>> f.clean('http://f.com') Traceback (most recent call last): @@ -1744,7 +1905,7 @@ True u'' >>> p.errors.as_text() u'' ->>> p.clean_data +>>> p.cleaned_data {'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)} >>> print p['first_name'] <input type="text" name="first_name" value="John" id="id_first_name" /> @@ -1780,10 +1941,10 @@ True {'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']} >>> p.is_valid() False ->>> p.clean_data +>>> p.cleaned_data Traceback (most recent call last): ... -AttributeError: 'Person' object has no attribute 'clean_data' +AttributeError: 'Person' object has no attribute 'cleaned_data' >>> print p <tr><th><label for="id_first_name">First name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="first_name" id="id_first_name" /></td></tr> <tr><th><label for="id_last_name">Last name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="last_name" id="id_last_name" /></td></tr> @@ -1814,10 +1975,10 @@ False {} >>> p.is_valid() False ->>> p.clean_data +>>> p.cleaned_data Traceback (most recent call last): ... -AttributeError: 'Person' object has no attribute 'clean_data' +AttributeError: 'Person' object has no attribute 'cleaned_data' >>> print p <tr><th><label for="id_first_name">First name:</label></th><td><input type="text" name="first_name" id="id_first_name" /></td></tr> <tr><th><label for="id_last_name">Last name:</label></th><td><input type="text" name="last_name" id="id_last_name" /></td></tr> @@ -1856,10 +2017,10 @@ u'<ul class="errorlist"><li>first_name<ul class="errorlist"><li>This field is re * This field is required. * birthday * This field is required. ->>> p.clean_data +>>> p.cleaned_data Traceback (most recent call last): ... -AttributeError: 'Person' object has no attribute 'clean_data' +AttributeError: 'Person' object has no attribute 'cleaned_data' >>> p['first_name'].errors [u'This field is required.'] >>> p['first_name'].errors.as_ul() @@ -1875,17 +2036,45 @@ u'* This field is required.' >>> print p['birthday'] <input type="text" name="birthday" id="id_birthday" /> -clean_data will always *only* contain a key for fields defined in the +cleaned_data will always *only* contain a key for fields defined in the Form, even if you pass extra data when you define the Form. In this example, we pass a bunch of extra fields to the form constructor, -but clean_data contains only the form's fields. +but cleaned_data contains only the form's fields. >>> data = {'first_name': u'John', 'last_name': u'Lennon', 'birthday': u'1940-10-9', 'extra1': 'hello', 'extra2': 'hello'} >>> p = Person(data) >>> p.is_valid() True ->>> p.clean_data +>>> p.cleaned_data {'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)} +cleaned_data will include a key and value for *all* fields defined in the Form, +even if the Form's data didn't include a value for fields that are not +required. In this example, the data dictionary doesn't include a value for the +"nick_name" field, but cleaned_data includes it. For CharFields, it's set to the +empty string. +>>> class OptionalPersonForm(Form): +... first_name = CharField() +... last_name = CharField() +... nick_name = CharField(required=False) +>>> data = {'first_name': u'John', 'last_name': u'Lennon'} +>>> f = OptionalPersonForm(data) +>>> f.is_valid() +True +>>> f.cleaned_data +{'nick_name': u'', 'first_name': u'John', 'last_name': u'Lennon'} + +For DateFields, it's set to None. +>>> class OptionalPersonForm(Form): +... first_name = CharField() +... last_name = CharField() +... birth_date = DateField(required=False) +>>> data = {'first_name': u'John', 'last_name': u'Lennon'} +>>> f = OptionalPersonForm(data) +>>> f.is_valid() +True +>>> f.cleaned_data +{'birth_date': None, 'first_name': u'John', 'last_name': u'Lennon'} + "auto_id" tells the Form to add an "id" attribute to each form element. If it's a string that contains '%s', Django will use that as a format string into which the field's name will be inserted. It will also put a <label> around @@ -1963,12 +2152,12 @@ Any Field can have a Widget class passed to its constructor: >>> print f['subject'] <input type="text" name="subject" /> >>> print f['message'] -<textarea name="message"></textarea> +<textarea rows="10" cols="40" name="message"></textarea> as_textarea(), as_text() and as_hidden() are shortcuts for changing the output widget type: >>> f['subject'].as_textarea() -u'<textarea name="subject"></textarea>' +u'<textarea rows="10" cols="40" name="subject"></textarea>' >>> f['message'].as_text() u'<input type="text" name="message" />' >>> f['message'].as_hidden() @@ -1988,7 +2177,7 @@ as_hidden(): u'<input type="text" name="message" />' >>> f = ContactForm({'subject': 'Hello', 'message': 'I love you.'}, auto_id=False) >>> f['subject'].as_textarea() -u'<textarea name="subject">Hello</textarea>' +u'<textarea rows="10" cols="40" name="subject">Hello</textarea>' >>> f['message'].as_text() u'<input type="text" name="message" value="I love you." />' >>> f['message'].as_hidden() @@ -2234,19 +2423,19 @@ returns a list of input. >>> f = SongForm({'name': 'Yesterday', 'composers': ['J']}, auto_id=False) >>> f.errors {} ->>> f.clean_data +>>> f.cleaned_data {'composers': [u'J'], 'name': u'Yesterday'} >>> f = SongForm({'name': 'Yesterday', 'composers': ['J', 'P']}, auto_id=False) >>> f.errors {} ->>> f.clean_data +>>> f.cleaned_data {'composers': [u'J', u'P'], 'name': u'Yesterday'} Validation errors are HTML-escaped when output as HTML. >>> class EscapingForm(Form): ... special_name = CharField() ... def clean_special_name(self): -... raise ValidationError("Something's wrong with '%s'" % self.clean_data['special_name']) +... raise ValidationError("Something's wrong with '%s'" % self.cleaned_data['special_name']) >>> f = EscapingForm({'special_name': "Nothing to escape"}, auto_id=False) >>> print f @@ -2261,7 +2450,7 @@ There are a couple of ways to do multiple-field validation. If you want the validation message to be associated with a particular field, implement the clean_XXX() method on the Form, where XXX is the field name. As in Field.clean(), the clean_XXX() method should return the cleaned value. In the -clean_XXX() method, you have access to self.clean_data, which is a dictionary +clean_XXX() method, you have access to self.cleaned_data, which is a dictionary of all the data that has been cleaned *so far*, in order by the fields, including the current field (e.g., the field XXX if you're in clean_XXX()). >>> class UserRegistration(Form): @@ -2269,9 +2458,9 @@ including the current field (e.g., the field XXX if you're in clean_XXX()). ... password1 = CharField(widget=PasswordInput) ... password2 = CharField(widget=PasswordInput) ... def clean_password2(self): -... if self.clean_data.get('password1') and self.clean_data.get('password2') and self.clean_data['password1'] != self.clean_data['password2']: +... if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']: ... raise ValidationError(u'Please make sure your passwords match.') -... return self.clean_data['password2'] +... return self.cleaned_data['password2'] >>> f = UserRegistration(auto_id=False) >>> f.errors {} @@ -2284,14 +2473,14 @@ including the current field (e.g., the field XXX if you're in clean_XXX()). >>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False) >>> f.errors {} ->>> f.clean_data +>>> f.cleaned_data {'username': u'adrian', 'password1': u'foo', 'password2': u'foo'} Another way of doing multiple-field validation is by implementing the Form's clean() method. If you do this, any ValidationError raised by that method will not be associated with a particular field; it will have a special-case association with the field named '__all__'. -Note that in Form.clean(), you have access to self.clean_data, a dictionary of +Note that in Form.clean(), you have access to self.cleaned_data, a dictionary of all the fields/values that have *not* raised a ValidationError. Also note Form.clean() is required to return a dictionary of all clean data. >>> class UserRegistration(Form): @@ -2299,9 +2488,9 @@ Form.clean() is required to return a dictionary of all clean data. ... password1 = CharField(widget=PasswordInput) ... password2 = CharField(widget=PasswordInput) ... def clean(self): -... if self.clean_data.get('password1') and self.clean_data.get('password2') and self.clean_data['password1'] != self.clean_data['password2']: +... if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']: ... raise ValidationError(u'Please make sure your passwords match.') -... return self.clean_data +... return self.cleaned_data >>> f = UserRegistration(auto_id=False) >>> f.errors {} @@ -2328,7 +2517,7 @@ Form.clean() is required to return a dictionary of all clean data. >>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False) >>> f.errors {} ->>> f.clean_data +>>> f.cleaned_data {'username': u'adrian', 'password1': u'foo', 'password2': u'foo'} # Dynamic construction ######################################################## @@ -2571,6 +2760,27 @@ underscores converted to spaces, and the initial letter capitalized. <li>Password1: <input type="password" name="password1" /></li> <li>Password (again): <input type="password" name="password2" /></li> +Labels for as_* methods will only end in a colon if they don't end in other +punctuation already. +>>> class Questions(Form): +... q1 = CharField(label='The first question') +... q2 = CharField(label='What is your name?') +... q3 = CharField(label='The answer to life is:') +... q4 = CharField(label='Answer this question!') +... q5 = CharField(label='The last question. Period.') +>>> print Questions(auto_id=False).as_p() +<p>The first question: <input type="text" name="q1" /></p> +<p>What is your name? <input type="text" name="q2" /></p> +<p>The answer to life is: <input type="text" name="q3" /></p> +<p>Answer this question! <input type="text" name="q4" /></p> +<p>The last question. Period. <input type="text" name="q5" /></p> +>>> print Questions().as_p() +<p><label for="id_q1">The first question:</label> <input type="text" name="q1" id="id_q1" /></p> +<p><label for="id_q2">What is your name?</label> <input type="text" name="q2" id="id_q2" /></p> +<p><label for="id_q3">The answer to life is:</label> <input type="text" name="q3" id="id_q3" /></p> +<p><label for="id_q4">Answer this question!</label> <input type="text" name="q4" id="id_q4" /></p> +<p><label for="id_q5">The last question. Period.</label> <input type="text" name="q5" id="id_q5" /></p> + A label can be a Unicode object or a bytestring with special characters. >>> class UserRegistration(Form): ... username = CharField(max_length=10, label='ŠĐĆŽćžšđ') @@ -2701,6 +2911,64 @@ then the latter will get precedence. <li>Username: <input type="text" name="username" value="babik" maxlength="10" /></li> <li>Password: <input type="password" name="password" /></li> +# Callable initial data ######################################################## + +The previous technique dealt with raw values as initial data, but it's also +possible to specify callable data. + +>>> class UserRegistration(Form): +... username = CharField(max_length=10) +... password = CharField(widget=PasswordInput) + +We need to define functions that get called later. +>>> def initial_django(): +... return 'django' +>>> def initial_stephane(): +... return 'stephane' + +Here, we're not submitting any data, so the initial value will be displayed. +>>> p = UserRegistration(initial={'username': initial_django}, auto_id=False) +>>> print p.as_ul() +<li>Username: <input type="text" name="username" value="django" maxlength="10" /></li> +<li>Password: <input type="password" name="password" /></li> + +The 'initial' parameter is meaningless if you pass data. +>>> p = UserRegistration({}, initial={'username': initial_django}, auto_id=False) +>>> print p.as_ul() +<li><ul class="errorlist"><li>This field is required.</li></ul>Username: <input type="text" name="username" maxlength="10" /></li> +<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li> +>>> p = UserRegistration({'username': u''}, initial={'username': initial_django}, auto_id=False) +>>> print p.as_ul() +<li><ul class="errorlist"><li>This field is required.</li></ul>Username: <input type="text" name="username" maxlength="10" /></li> +<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li> +>>> p = UserRegistration({'username': u'foo'}, initial={'username': initial_django}, auto_id=False) +>>> print p.as_ul() +<li>Username: <input type="text" name="username" value="foo" maxlength="10" /></li> +<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li> + +A callable 'initial' value is *not* used as a fallback if data is not provided. +In this example, we don't provide a value for 'username', and the form raises a +validation error rather than using the initial value for 'username'. +>>> p = UserRegistration({'password': 'secret'}, initial={'username': initial_django}) +>>> p.errors +{'username': [u'This field is required.']} +>>> p.is_valid() +False + +If a Form defines 'initial' *and* 'initial' is passed as a parameter to Form(), +then the latter will get precedence. +>>> class UserRegistration(Form): +... username = CharField(max_length=10, initial=initial_django) +... password = CharField(widget=PasswordInput) +>>> p = UserRegistration(auto_id=False) +>>> print p.as_ul() +<li>Username: <input type="text" name="username" value="django" maxlength="10" /></li> +<li>Password: <input type="password" name="password" /></li> +>>> p = UserRegistration(initial={'username': initial_stephane}, auto_id=False) +>>> print p.as_ul() +<li>Username: <input type="text" name="username" value="stephane" maxlength="10" /></li> +<li>Password: <input type="password" name="password" /></li> + # Help text ################################################################### You can specify descriptive text for a field by using the 'help_text' argument @@ -2817,7 +3085,7 @@ actual field name. {} >>> p.is_valid() True ->>> p.clean_data +>>> p.cleaned_data {'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)} Let's try submitting some bad data to make sure form.errors and field.errors @@ -2861,12 +3129,12 @@ of the same form. >>> p1 = Person(data, prefix='person1') >>> p1.is_valid() True ->>> p1.clean_data +>>> p1.cleaned_data {'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)} >>> p2 = Person(data, prefix='person2') >>> p2.is_valid() True ->>> p2.clean_data +>>> p2.cleaned_data {'first_name': u'Jim', 'last_name': u'Morrison', 'birthday': datetime.date(1943, 12, 8)} By default, forms append a hyphen between the prefix and the field name, but a @@ -2892,7 +3160,7 @@ self.prefix. >>> p = Person(data, prefix='foo') >>> p.is_valid() True ->>> p.clean_data +>>> p.cleaned_data {'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)} # Forms with NullBooleanFields ################################################ @@ -2954,16 +3222,16 @@ is different than its data. This is handled transparently, though. ... password1 = CharField(widget=PasswordInput) ... password2 = CharField(widget=PasswordInput) ... def clean(self): -... if self.clean_data.get('password1') and self.clean_data.get('password2') and self.clean_data['password1'] != self.clean_data['password2']: +... if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']: ... raise ValidationError(u'Please make sure your passwords match.') -... return self.clean_data +... return self.cleaned_data >>> def my_function(method, post_data): ... if method == 'POST': ... form = UserRegistration(post_data, auto_id=False) ... else: ... form = UserRegistration(auto_id=False) ... if form.is_valid(): -... return 'VALID: %r' % form.clean_data +... return 'VALID: %r' % form.cleaned_data ... t = Template('<form action="" method="post">\n<table>\n{{ form }}\n</table>\n<input type="submit" />\n</form>') ... return t.render(Context({'form': form})) @@ -3001,9 +3269,9 @@ VALID: {'username': u'adrian', 'password1': u'secret', 'password2': u'secret'} ... password1 = CharField(widget=PasswordInput) ... password2 = CharField(widget=PasswordInput) ... def clean(self): -... if self.clean_data.get('password1') and self.clean_data.get('password2') and self.clean_data['password1'] != self.clean_data['password2']: +... if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']: ... raise ValidationError(u'Please make sure your passwords match.') -... return self.clean_data +... return self.cleaned_data You have full flexibility in displaying form fields in a template. Just pass a Form instance to the template, and use "dot" access to refer to individual @@ -3268,300 +3536,100 @@ True <option value="2016">2016</option> </select> -# USZipCodeField ############################################################## +# MultiWidget and MultiValueField ############################################# +# MultiWidgets are widgets composed of other widgets. They are usually +# combined with MultiValueFields - a field that is composed of other fields. +# MulitWidgets can themselved be composed of other MultiWidgets. +# SplitDateTimeWidget is one example of a MultiWidget. -USZipCodeField validates that the data is either a five-digit U.S. zip code or -a zip+4. ->>> from django.contrib.localflavor.usa.forms import USZipCodeField ->>> f = USZipCodeField() ->>> f.clean('60606') -u'60606' ->>> f.clean(60606) -u'60606' ->>> f.clean('04000') -u'04000' ->>> f.clean('4000') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX or XXXXX-XXXX.'] ->>> f.clean('60606-1234') -u'60606-1234' ->>> f.clean('6060-1234') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX or XXXXX-XXXX.'] ->>> f.clean('60606-') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX or XXXXX-XXXX.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] - ->>> f = USZipCodeField(required=False) ->>> f.clean('60606') -u'60606' ->>> f.clean(60606) -u'60606' ->>> f.clean('04000') -u'04000' ->>> f.clean('4000') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX or XXXXX-XXXX.'] ->>> f.clean('60606-1234') -u'60606-1234' ->>> f.clean('6060-1234') -Traceback (most recent call last): +>>> class ComplexMultiWidget(MultiWidget): +... def __init__(self, attrs=None): +... widgets = ( +... TextInput(), +... SelectMultiple(choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), +... SplitDateTimeWidget(), +... ) +... super(ComplexMultiWidget, self).__init__(widgets, attrs) ... -ValidationError: [u'Enter a zip code in the format XXXXX or XXXXX-XXXX.'] ->>> f.clean('60606-') -Traceback (most recent call last): -... -ValidationError: [u'Enter a zip code in the format XXXXX or XXXXX-XXXX.'] ->>> f.clean(None) -u'' ->>> f.clean('') -u'' - -# USPhoneNumberField ########################################################## +... def decompress(self, value): +... if value: +... data = value.split(',') +... return [data[0], data[1], datetime.datetime(*time.strptime(data[2], "%Y-%m-%d %H:%M:%S")[0:6])] +... return [None, None, None] +... def format_output(self, rendered_widgets): +... return u'\n'.join(rendered_widgets) +>>> w = ComplexMultiWidget() +>>> print w.render('name', 'some text,JP,2007-04-25 06:24:00') +<input type="text" name="name_0" value="some text" /> +<select multiple="multiple" name="name_1"> +<option value="J" selected="selected">John</option> +<option value="P" selected="selected">Paul</option> +<option value="G">George</option> +<option value="R">Ringo</option> +</select> +<input type="text" name="name_2_0" value="2007-04-25" /><input type="text" name="name_2_1" value="06:24:00" /> -USPhoneNumberField validates that the data is a valid U.S. phone number, -including the area code. It's normalized to XXX-XXX-XXXX format. ->>> from django.contrib.localflavor.usa.forms import USPhoneNumberField ->>> f = USPhoneNumberField() ->>> f.clean('312-555-1212') -u'312-555-1212' ->>> f.clean('3125551212') -u'312-555-1212' ->>> f.clean('312 555-1212') -u'312-555-1212' ->>> f.clean('(312) 555-1212') -u'312-555-1212' ->>> f.clean('312 555 1212') -u'312-555-1212' ->>> f.clean('312.555.1212') -u'312-555-1212' ->>> f.clean('312.555-1212') -u'312-555-1212' ->>> f.clean(' (312) 555.1212 ') -u'312-555-1212' ->>> f.clean('555-1212') -Traceback (most recent call last): -... -ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.'] ->>> f.clean('312-55-1212') -Traceback (most recent call last): -... -ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('') -Traceback (most recent call last): +>>> class ComplexField(MultiValueField): +... def __init__(self, required=True, widget=None, label=None, initial=None): +... fields = ( +... CharField(), +... MultipleChoiceField(choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), +... SplitDateTimeField() +... ) +... super(ComplexField, self).__init__(fields, required, widget, label, initial) ... -ValidationError: [u'This field is required.'] +... def compress(self, data_list): +... if data_list: +... return '%s,%s,%s' % (data_list[0],''.join(data_list[1]),data_list[2]) +... return None ->>> f = USPhoneNumberField(required=False) ->>> f.clean('312-555-1212') -u'312-555-1212' ->>> f.clean('3125551212') -u'312-555-1212' ->>> f.clean('312 555-1212') -u'312-555-1212' ->>> f.clean('(312) 555-1212') -u'312-555-1212' ->>> f.clean('312 555 1212') -u'312-555-1212' ->>> f.clean('312.555.1212') -u'312-555-1212' ->>> f.clean('312.555-1212') -u'312-555-1212' ->>> f.clean(' (312) 555.1212 ') -u'312-555-1212' ->>> f.clean('555-1212') +>>> f = ComplexField(widget=w) +>>> f.clean(['some text', ['J','P'], ['2007-04-25','6:24:00']]) +u'some text,JP,2007-04-25 06:24:00' +>>> f.clean(['some text',['X'], ['2007-04-25','6:24:00']]) Traceback (most recent call last): ... -ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.'] ->>> f.clean('312-55-1212') -Traceback (most recent call last): -... -ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.'] ->>> f.clean(None) -u'' ->>> f.clean('') -u'' - -# USStateField ################################################################ +ValidationError: [u'Select a valid choice. X is not one of the available choices.'] -USStateField validates that the data is either an abbreviation or name of a -U.S. state. ->>> from django.contrib.localflavor.usa.forms import USStateField ->>> f = USStateField() ->>> f.clean('il') -u'IL' ->>> f.clean('IL') -u'IL' ->>> f.clean('illinois') -u'IL' ->>> f.clean(' illinois ') -u'IL' ->>> f.clean(60606) -Traceback (most recent call last): -... -ValidationError: [u'Enter a U.S. state or territory.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('') +# If insufficient data is provided, None is substituted +>>> f.clean(['some text',['JP']]) Traceback (most recent call last): ... ValidationError: [u'This field is required.'] ->>> f = USStateField(required=False) ->>> f.clean('il') -u'IL' ->>> f.clean('IL') -u'IL' ->>> f.clean('illinois') -u'IL' ->>> f.clean(' illinois ') -u'IL' ->>> f.clean(60606) -Traceback (most recent call last): -... -ValidationError: [u'Enter a U.S. state or territory.'] ->>> f.clean(None) -u'' ->>> f.clean('') -u'' - -# USStateSelect ############################################################### - -USStateSelect is a Select widget that uses a list of U.S. states/territories -as its choices. ->>> from django.contrib.localflavor.usa.forms import USStateSelect ->>> w = USStateSelect() ->>> print w.render('state', 'IL') -<select name="state"> -<option value="AL">Alabama</option> -<option value="AK">Alaska</option> -<option value="AS">American Samoa</option> -<option value="AZ">Arizona</option> -<option value="AR">Arkansas</option> -<option value="CA">California</option> -<option value="CO">Colorado</option> -<option value="CT">Connecticut</option> -<option value="DE">Deleware</option> -<option value="DC">District of Columbia</option> -<option value="FM">Federated States of Micronesia</option> -<option value="FL">Florida</option> -<option value="GA">Georgia</option> -<option value="GU">Guam</option> -<option value="HI">Hawaii</option> -<option value="ID">Idaho</option> -<option value="IL" selected="selected">Illinois</option> -<option value="IN">Indiana</option> -<option value="IA">Iowa</option> -<option value="KS">Kansas</option> -<option value="KY">Kentucky</option> -<option value="LA">Louisiana</option> -<option value="ME">Maine</option> -<option value="MH">Marshall Islands</option> -<option value="MD">Maryland</option> -<option value="MA">Massachusetts</option> -<option value="MI">Michigan</option> -<option value="MN">Minnesota</option> -<option value="MS">Mississippi</option> -<option value="MO">Missouri</option> -<option value="MT">Montana</option> -<option value="NE">Nebraska</option> -<option value="NV">Nevada</option> -<option value="NH">New Hampshire</option> -<option value="NJ">New Jersey</option> -<option value="NM">New Mexico</option> -<option value="NY">New York</option> -<option value="NC">North Carolina</option> -<option value="ND">North Dakota</option> -<option value="MP">Northern Mariana Islands</option> -<option value="OH">Ohio</option> -<option value="OK">Oklahoma</option> -<option value="OR">Oregon</option> -<option value="PW">Palau</option> -<option value="PA">Pennsylvania</option> -<option value="PR">Puerto Rico</option> -<option value="RI">Rhode Island</option> -<option value="SC">South Carolina</option> -<option value="SD">South Dakota</option> -<option value="TN">Tennessee</option> -<option value="TX">Texas</option> -<option value="UT">Utah</option> -<option value="VT">Vermont</option> -<option value="VI">Virgin Islands</option> -<option value="VA">Virginia</option> -<option value="WA">Washington</option> -<option value="WV">West Virginia</option> -<option value="WI">Wisconsin</option> -<option value="WY">Wyoming</option> +>>> class ComplexFieldForm(Form): +... field1 = ComplexField(widget=w) +>>> f = ComplexFieldForm() +>>> print f +<tr><th><label for="id_field1_0">Field1:</label></th><td><input type="text" name="field1_0" id="id_field1_0" /> +<select multiple="multiple" name="field1_1" id="id_field1_1"> +<option value="J">John</option> +<option value="P">Paul</option> +<option value="G">George</option> +<option value="R">Ringo</option> </select> +<input type="text" name="field1_2_0" id="id_field1_2_0" /><input type="text" name="field1_2_1" id="id_field1_2_1" /></td></tr> -# UKPostcodeField ############################################################# - -UKPostcodeField validates that the data is a valid UK postcode. ->>> from django.contrib.localflavor.uk.forms import UKPostcodeField ->>> f = UKPostcodeField() ->>> f.clean('BT32 4PX') -u'BT32 4PX' ->>> f.clean('GIR 0AA') -u'GIR 0AA' ->>> f.clean('BT324PX') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postcode. A space is required between the two postcode parts.'] ->>> f.clean('1NV 4L1D') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postcode. A space is required between the two postcode parts.'] ->>> f.clean(None) -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] ->>> f.clean('') -Traceback (most recent call last): -... -ValidationError: [u'This field is required.'] +>>> f = ComplexFieldForm({'field1_0':'some text','field1_1':['J','P'], 'field1_2_0':'2007-04-25', 'field1_2_1':'06:24:00'}) +>>> print f +<tr><th><label for="id_field1_0">Field1:</label></th><td><input type="text" name="field1_0" value="some text" id="id_field1_0" /> +<select multiple="multiple" name="field1_1" id="id_field1_1"> +<option value="J" selected="selected">John</option> +<option value="P" selected="selected">Paul</option> +<option value="G">George</option> +<option value="R">Ringo</option> +</select> +<input type="text" name="field1_2_0" value="2007-04-25" id="id_field1_2_0" /><input type="text" name="field1_2_1" value="06:24:00" id="id_field1_2_1" /></td></tr> ->>> f = UKPostcodeField(required=False) ->>> f.clean('BT32 4PX') -u'BT32 4PX' ->>> f.clean('GIR 0AA') -u'GIR 0AA' ->>> f.clean('1NV 4L1D') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postcode. A space is required between the two postcode parts.'] ->>> f.clean('BT324PX') -Traceback (most recent call last): -... -ValidationError: [u'Enter a postcode. A space is required between the two postcode parts.'] ->>> f.clean(None) -u'' ->>> f.clean('') -u'' +>>> f.cleaned_data +{'field1': u'some text,JP,2007-04-25 06:24:00'} ################################# # Tests of underlying functions # ################################# # smart_unicode tests ->>> from django.newforms.util import smart_unicode +>>> from django.utils.encoding import smart_unicode >>> class Test: ... def __str__(self): ... return 'ŠĐĆŽćžšđ' @@ -3578,8 +3646,42 @@ u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111' u'1' >>> smart_unicode('foo') u'foo' + +# flatatt tests +>>> from django.newforms.util import flatatt +>>> flatatt({'id': "header"}) +u' id="header"' +>>> flatatt({'class': "news", 'title': "Read this"}) +u' class="news" title="Read this"' +>>> flatatt({}) +u'' + +#################################### +# Test accessing errors in clean() # +#################################### + +>>> class UserForm(Form): +... username = CharField(max_length=10) +... password = CharField(widget=PasswordInput) +... def clean(self): +... data = self.cleaned_data +... if not self.errors: +... data['username'] = data['username'].lower() +... return data + +>>> f = UserForm({'username': 'SirRobin', 'password': 'blue'}) +>>> f.is_valid() +True +>>> f.cleaned_data['username'] +u'sirrobin' """ +__test__ = { + 'form_tests': form_tests, + 'localflavor': localflavor_tests, + 'regressions': regression_tests, +} + if __name__ == "__main__": import doctest doctest.testmod() diff --git a/tests/regressiontests/httpwrappers/tests.py b/tests/regressiontests/httpwrappers/tests.py index 385c3048d9..e7245104e9 100644 --- a/tests/regressiontests/httpwrappers/tests.py +++ b/tests/regressiontests/httpwrappers/tests.py @@ -34,6 +34,9 @@ AttributeError: This QueryDict instance is immutable >>> q.has_key('foo') False +>>> 'foo' in q +False + >>> q.items() [] @@ -93,6 +96,12 @@ MultiValueDictKeyError: "Key 'foo' not found in <MultiValueDict: {}>" >>> q['name'] 'john' +>>> del q['name'] +>>> 'name' in q +False + +>>> q['name'] = 'john' + >>> q.get('foo', 'default') 'default' @@ -124,6 +133,9 @@ MultiValueDictKeyError: "Key 'foo' not found in <MultiValueDict: {}>" >>> q.has_key('foo') True +>>> 'foo' in q +True + >>> q.items() [('foo', 'another'), ('name', 'john')] @@ -154,6 +166,9 @@ True >>> q.pop('foo') ['bar', 'baz', 'another', 'hello'] +>>> q.pop('foo', 'not there') +'not there' + >>> q.get('foo', 'not there') 'not there' @@ -218,9 +233,15 @@ AttributeError: This QueryDict instance is immutable >>> q.has_key('foo') True +>>> 'foo' in q +True + >>> q.has_key('bar') False +>>> 'bar' in q +False + >>> q.items() [('foo', 'bar')] @@ -303,9 +324,15 @@ AttributeError: This QueryDict instance is immutable >>> q.has_key('vote') True +>>> 'vote' in q +True + >>> q.has_key('foo') False +>>> 'foo' in q +False + >>> q.items() [('vote', 'no')] @@ -349,6 +376,11 @@ AttributeError: This QueryDict instance is immutable >>> q.urlencode() 'vote=yes&vote=no' +>>> del q['vote'] +Traceback (most recent call last): +... +AttributeError: This QueryDict instance is immutable + """ from django.http import QueryDict diff --git a/tests/regressiontests/humanize/tests.py b/tests/regressiontests/humanize/tests.py index e342d7ded8..eca65f7575 100644 --- a/tests/regressiontests/humanize/tests.py +++ b/tests/regressiontests/humanize/tests.py @@ -27,8 +27,10 @@ should've produced %s""" % (method, rendered, result_list[index])) self.humanize_tester(test_list, result_list, 'ordinal') def test_intcomma(self): - test_list = ('100','1000','10123','10311','1000000') - result_list = ('100', '1,000', '10,123', '10,311', '1,000,000') + test_list = (100, 1000, 10123, 10311, 1000000, 1234567.25, + '100', '1000', '10123', '10311', '1000000', '1234567.1234567') + result_list = ('100', '1,000', '10,123', '10,311', '1,000,000', '1,234,567.25', + '100', '1,000', '10,123', '10,311', '1,000,000', '1,234,567.1234567') self.humanize_tester(test_list, result_list, 'intcomma') diff --git a/tests/regressiontests/markup/tests.py b/tests/regressiontests/markup/tests.py deleted file mode 100644 index bd3f52b9dd..0000000000 --- a/tests/regressiontests/markup/tests.py +++ /dev/null @@ -1,69 +0,0 @@ -# Quick tests for the markup templatetags (django.contrib.markup) - -from django.template import Template, Context, add_to_builtins -import re -import unittest - -add_to_builtins('django.contrib.markup.templatetags.markup') - -class Templates(unittest.TestCase): - def test_textile(self): - try: - import textile - except ImportError: - textile = None - - textile_content = """Paragraph 1 - -Paragraph 2 with "quotes" and @code@""" - - t = Template("{{ textile_content|textile }}") - rendered = t.render(Context(locals())).strip() - if textile: - self.assertEqual(rendered, """<p>Paragraph 1</p> - -<p>Paragraph 2 with “quotes” and <code>code</code></p>""") - else: - self.assertEqual(rendered, textile_content) - - def test_markdown(self): - try: - import markdown - except ImportError: - markdown = None - - markdown_content = """Paragraph 1 - -## An h2""" - - t = Template("{{ markdown_content|markdown }}") - rendered = t.render(Context(locals())).strip() - if markdown: - pattern = re.compile("""<p>Paragraph 1\s*</p>\s*<h2>\s*An h2</h2>""") - self.assert_(pattern.match(rendered)) - else: - self.assertEqual(rendered, markdown_content) - - def test_docutils(self): - try: - import docutils - except ImportError: - docutils = None - - rest_content = """Paragraph 1 - -Paragraph 2 with a link_ - -.. _link: http://www.example.com/""" - - t = Template("{{ rest_content|restructuredtext }}") - rendered = t.render(Context(locals())).strip() - if docutils: - self.assertEqual(rendered, """<p>Paragraph 1</p> -<p>Paragraph 2 with a <a class="reference" href="http://www.example.com/">link</a></p>""") - else: - self.assertEqual(rendered, rest_content) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/regressiontests/null_queries/models.py b/tests/regressiontests/null_queries/models.py index 09024f18c2..21944d9e7a 100644 --- a/tests/regressiontests/null_queries/models.py +++ b/tests/regressiontests/null_queries/models.py @@ -32,7 +32,7 @@ __test__ = {'API_TESTS':""" >>> Choice.objects.filter(foo__exact=None) Traceback (most recent call last): ... -TypeError: Cannot resolve keyword 'foo' into field +TypeError: Cannot resolve keyword 'foo' into field. Choices are: id, poll, choice # Can't use None on anything other than __exact >>> Choice.objects.filter(id__gt=None) diff --git a/tests/regressiontests/serializers_regress/models.py b/tests/regressiontests/serializers_regress/models.py index d3415ac1b9..b441885f10 100644 --- a/tests/regressiontests/serializers_regress/models.py +++ b/tests/regressiontests/serializers_regress/models.py @@ -1,19 +1,20 @@ """ A test spanning all the capabilities of all the serializers. -This class sets up a model for each model field type +This class sets up a model for each model field type (except for image types, because of the PIL dependency). """ from django.db import models +from django.contrib.contenttypes import generic from django.contrib.contenttypes.models import ContentType -# The following classes are for testing basic data +# The following classes are for testing basic data # marshalling, including NULL values. class BooleanData(models.Model): data = models.BooleanField(null=True) - + class CharData(models.Model): data = models.CharField(maxlength=30, null=True) @@ -23,6 +24,9 @@ class DateData(models.Model): class DateTimeData(models.Model): data = models.DateTimeField(null=True) +class DecimalData(models.Model): + data = models.DecimalField(null=True, decimal_places=3, max_digits=5) + class EmailData(models.Model): data = models.EmailField(null=True) @@ -33,7 +37,7 @@ class FilePathData(models.Model): data = models.FilePathField(null=True) class FloatData(models.Model): - data = models.FloatField(null=True, decimal_places=3, max_digits=5) + data = models.FloatField(null=True) class IntegerData(models.Model): data = models.IntegerField(null=True) @@ -80,7 +84,7 @@ class Tag(models.Model): content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() - content_object = models.GenericForeignKey() + content_object = generic.GenericForeignKey() class Meta: ordering = ["data"] @@ -88,7 +92,7 @@ class Tag(models.Model): class GenericData(models.Model): data = models.CharField(maxlength=30) - tags = models.GenericRelation(Tag) + tags = generic.GenericRelation(Tag) # The following test classes are all for validation # of related objects; in particular, forward, backward, @@ -99,6 +103,12 @@ class Anchor(models.Model): something for other models to point at""" data = models.CharField(maxlength=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) class FKData(models.Model): data = models.ForeignKey(Anchor, null=True) @@ -115,6 +125,13 @@ class FKSelfData(models.Model): class M2MSelfData(models.Model): data = models.ManyToManyField('self', null=True, symmetrical=False) + +class FKDataToField(models.Model): + data = models.ForeignKey(UniqueAnchor, null=True, to_field='data') + +class FKDataToO2O(models.Model): + data = models.ForeignKey(O2OData, null=True) + # The following test classes are for validating the # deserialization of objects that use a user-defined # field as the primary key. @@ -134,6 +151,9 @@ class CharPKData(models.Model): # class DateTimePKData(models.Model): # data = models.DateTimeField(primary_key=True) +class DecimalPKData(models.Model): + data = models.DecimalField(primary_key=True, decimal_places=3, max_digits=5) + class EmailPKData(models.Model): data = models.EmailField(primary_key=True) @@ -144,7 +164,7 @@ class FilePathPKData(models.Model): data = models.FilePathField(primary_key=True) class FloatPKData(models.Model): - data = models.FloatField(primary_key=True, decimal_places=3, max_digits=5) + data = models.FloatField(primary_key=True) class IntegerPKData(models.Model): data = models.IntegerField(primary_key=True) @@ -185,3 +205,7 @@ class USStatePKData(models.Model): # class XMLPKData(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) diff --git a/tests/regressiontests/serializers_regress/tests.py b/tests/regressiontests/serializers_regress/tests.py index 97b3fbacbe..1a144c8356 100644 --- a/tests/regressiontests/serializers_regress/tests.py +++ b/tests/regressiontests/serializers_regress/tests.py @@ -2,13 +2,14 @@ A test spanning all the capabilities of all the serializers. This class defines sample data and a dynamically generated -test case that is capable of testing the capabilities of +test case that is capable of testing the capabilities of the serializers. This includes all valid data values, plus forward, backwards and self references. """ import unittest, datetime +from cStringIO import StringIO from django.utils.functional import curry from django.core import serializers @@ -16,13 +17,17 @@ from django.db import transaction from django.core import management from models import * +try: + import decimal +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 def data_create(pk, klass, data): instance = klass(id=pk) instance.data = data - instance.save() + instance.save() return instance def generic_create(pk, klass, data): @@ -32,13 +37,13 @@ def generic_create(pk, klass, data): for tag in data[1:]: instance.tags.create(data=tag) return instance - + def fk_create(pk, klass, data): instance = klass(id=pk) setattr(instance, 'data_id', data) instance.save() return instance - + def m2m_create(pk, klass, data): instance = klass(id=pk) instance.save() @@ -61,14 +66,14 @@ def pk_create(pk, klass, data): # test data objects of various kinds def data_compare(testcase, pk, klass, data): instance = klass.objects.get(id=pk) - testcase.assertEqual(data, instance.data, + testcase.assertEqual(data, instance.data, "Objects with PK=%d not equal; expected '%s' (%s), got '%s' (%s)" % (pk,data, type(data), instance.data, type(instance.data))) def generic_compare(testcase, pk, klass, data): instance = klass.objects.get(id=pk) testcase.assertEqual(data[0], instance.data) testcase.assertEqual(data[1:], [t.data for t in instance.tags.all()]) - + def fk_compare(testcase, pk, klass, data): instance = klass.objects.get(id=pk) testcase.assertEqual(data, instance.data_id) @@ -84,7 +89,7 @@ def o2o_compare(testcase, pk, klass, data): def pk_compare(testcase, pk, klass, data): instance = klass.objects.get(data=data) testcase.assertEqual(data, instance.data) - + # Define some data types. Each data type is # actually a pair of functions; one to create # and one to compare objects of that type @@ -96,7 +101,7 @@ o2o_obj = (o2o_create, o2o_compare) pk_obj = (pk_create, pk_compare) test_data = [ - # Format: (data type, PK value, Model Class, data) + # Format: (data type, PK value, Model Class, data) (data_obj, 1, BooleanData, True), (data_obj, 2, BooleanData, False), (data_obj, 10, CharData, "Test Char Data"), @@ -115,10 +120,14 @@ test_data = [ (data_obj, 51, FileData, None), (data_obj, 60, FilePathData, "/foo/bar/whiz.txt"), (data_obj, 61, FilePathData, None), - (data_obj, 70, FloatData, 12.345), - (data_obj, 71, FloatData, -12.345), - (data_obj, 72, FloatData, 0.0), - (data_obj, 73, FloatData, None), + (data_obj, 70, DecimalData, decimal.Decimal('12.345')), + (data_obj, 71, DecimalData, decimal.Decimal('-12.345')), + (data_obj, 72, DecimalData, decimal.Decimal('0.0')), + (data_obj, 73, DecimalData, None), + (data_obj, 74, FloatData, 12.345), + (data_obj, 75, FloatData, -12.345), + (data_obj, 76, FloatData, 0.0), + (data_obj, 77, FloatData, None), (data_obj, 80, IntegerData, 123456789), (data_obj, 81, IntegerData, -123456789), (data_obj, 82, IntegerData, 0), @@ -137,10 +146,10 @@ test_data = [ (data_obj, 131, PositiveSmallIntegerData, None), (data_obj, 140, SlugData, "this-is-a-slug"), (data_obj, 141, SlugData, None), - (data_obj, 150, SmallData, 12), - (data_obj, 151, SmallData, -12), - (data_obj, 152, SmallData, 0), - (data_obj, 153, SmallData, None), + (data_obj, 150, SmallData, 12), + (data_obj, 151, SmallData, -12), + (data_obj, 152, SmallData, 0), + (data_obj, 153, SmallData, None), (data_obj, 160, TextData, """This is a long piece of text. It contains line breaks. Several of them. @@ -159,6 +168,7 @@ The end."""), (data_obj, 300, Anchor, "Anchor 1"), (data_obj, 301, Anchor, "Anchor 2"), + (data_obj, 302, UniqueAnchor, "UAnchor 1"), (fk_obj, 400, FKData, 300), # Post reference (fk_obj, 401, FKData, 500), # Pre reference @@ -184,8 +194,15 @@ The end."""), (m2m_obj, 445, M2MSelfData, []), (m2m_obj, 446, M2MSelfData, []), + (fk_obj, 450, FKDataToField, "UAnchor 1"), + (fk_obj, 451, FKDataToField, "UAnchor 2"), + (fk_obj, 452, FKDataToField, None), + + (fk_obj, 460, FKDataToO2O, 300), + (data_obj, 500, Anchor, "Anchor 3"), (data_obj, 501, Anchor, "Anchor 4"), + (data_obj, 502, UniqueAnchor, "UAnchor 2"), (pk_obj, 601, BooleanPKData, True), (pk_obj, 602, BooleanPKData, False), @@ -195,9 +212,12 @@ The end."""), (pk_obj, 640, EmailPKData, "hovercraft@example.com"), (pk_obj, 650, FilePKData, 'file:///foo/bar/whiz.txt'), (pk_obj, 660, FilePathPKData, "/foo/bar/whiz.txt"), - (pk_obj, 670, FloatPKData, 12.345), - (pk_obj, 671, FloatPKData, -12.345), - (pk_obj, 672, FloatPKData, 0.0), + (pk_obj, 670, DecimalPKData, decimal.Decimal('12.345')), + (pk_obj, 671, DecimalPKData, decimal.Decimal('-12.345')), + (pk_obj, 672, DecimalPKData, decimal.Decimal('0.0')), + (pk_obj, 673, FloatPKData, 12.345), + (pk_obj, 674, FloatPKData, -12.345), + (pk_obj, 675, FloatPKData, 0.0), (pk_obj, 680, IntegerPKData, 123456789), (pk_obj, 681, IntegerPKData, -123456789), (pk_obj, 682, IntegerPKData, 0), @@ -209,9 +229,9 @@ The end."""), (pk_obj, 720, PositiveIntegerPKData, 123456789), (pk_obj, 730, PositiveSmallIntegerPKData, 12), (pk_obj, 740, SlugPKData, "this-is-a-slug"), - (pk_obj, 750, SmallPKData, 12), - (pk_obj, 751, SmallPKData, -12), - (pk_obj, 752, SmallPKData, 0), + (pk_obj, 750, SmallPKData, 12), + (pk_obj, 751, SmallPKData, -12), + (pk_obj, 752, SmallPKData, 0), # (pk_obj, 760, TextPKData, """This is a long piece of text. # It contains line breaks. # Several of them. @@ -220,7 +240,7 @@ The end."""), (pk_obj, 780, USStatePKData, "MA"), # (pk_obj, 790, XMLPKData, "<foo></foo>"), ] - + # Dynamically create serializer tests to ensure that all # registered serializers are automatically tested. class SerializerTests(unittest.TestCase): @@ -228,7 +248,7 @@ class SerializerTests(unittest.TestCase): def serializerTest(format, self): # Clear the database first - management.flush(verbosity=0, interactive=False) + management.flush(verbosity=0, interactive=False) # Create all the objects defined in the test data objects = [] @@ -239,14 +259,14 @@ def serializerTest(format, self): transaction.commit() transaction.leave_transaction_management() - # Add the generic tagged objects to the object list + # Add the generic tagged objects to the object list objects.extend(Tag.objects.all()) - + # Serialize the test database serialized_data = serializers.serialize(format, objects, indent=2) # Flush the database and recreate from the serialized data - management.flush(verbosity=0, interactive=False) + management.flush(verbosity=0, interactive=False) transaction.enter_transaction_management() transaction.managed(True) for obj in serializers.deserialize(format, serialized_data): @@ -254,10 +274,47 @@ def serializerTest(format, self): transaction.commit() transaction.leave_transaction_management() - # Assert that the deserialized data is the same + # Assert that the deserialized data is the same # as the original source for (func, pk, klass, datum) in test_data: func[1](self, pk, klass, datum) - + +def fieldsTest(format, self): + # Clear the database first + management.flush(verbosity=0, interactive=False) + + obj = ComplexModel(field1='first',field2='second',field3='third') + obj.save() + + # Serialize then deserialize the test database + serialized_data = serializers.serialize(format, [obj], indent=2, fields=('field1','field3')) + result = serializers.deserialize(format, serialized_data).next() + + # Check that the deserialized object contains data in only the serialized fields. + self.assertEqual(result.object.field1, 'first') + self.assertEqual(result.object.field2, '') + self.assertEqual(result.object.field3, 'third') + +def streamTest(format, self): + # Clear the database first + management.flush(verbosity=0, interactive=False) + + obj = ComplexModel(field1='first',field2='second',field3='third') + obj.save() + + # Serialize the test database to a stream + stream = StringIO() + serializers.serialize(format, [obj], indent=2, stream=stream) + + # Serialize normally for a comparison + string_data = serializers.serialize(format, [obj], indent=2) + + # Check that the two are the same + self.assertEqual(string_data, stream.getvalue()) + stream.close() + for format in serializers.get_serializer_formats(): setattr(SerializerTests, 'test_'+format+'_serializer', curry(serializerTest, format)) + setattr(SerializerTests, 'test_'+format+'_serializer_fields', curry(fieldsTest, format)) + if format != 'python': + setattr(SerializerTests, 'test_'+format+'_serializer_stream', curry(streamTest, format)) diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py index 375fd36196..e983a539ae 100644 --- a/tests/regressiontests/templates/tests.py +++ b/tests/regressiontests/templates/tests.py @@ -127,6 +127,18 @@ class Templates(unittest.TestCase): # Fail silently when accessing a non-simple method 'basic-syntax20': ("{{ var.method2 }}", {"var": SomeClass()}, ("","INVALID")), + # Don't get confused when parsing something that is almost, but not + # quite, a template tag. + 'basic-syntax21': ("a {{ moo %} b", {}, "a {{ moo %} b"), + 'basic-syntax22': ("{{ moo #}", {}, "{{ moo #}"), + + # Will try to treat "moo #} {{ cow" as the variable. Not ideal, but + # costly to work around, so this triggers an error. + 'basic-syntax23': ("{{ moo #} {{ cow }}", {"cow": "cow"}, template.TemplateSyntaxError), + + # Embedded newlines make it not-a-tag. + 'basic-syntax24': ("{{ moo\n }}", {}, "{{ moo\n }}"), + # List-index syntax allows a template to access a certain item of a subscriptable object. 'list-index01': ("{{ var.1 }}", {"var": ["first item", "second item"]}, "second item"), @@ -149,62 +161,63 @@ class Templates(unittest.TestCase): # Dictionary lookup wins out when there is a string and int version of the key. 'list-index07': ("{{ var.1 }}", {"var": {'1': "hello", 1: "world"}}, "hello"), - + # Basic filter usage - 'basic-syntax21': ("{{ var|upper }}", {"var": "Django is the greatest!"}, "DJANGO IS THE GREATEST!"), + 'filter-syntax01': ("{{ var|upper }}", {"var": "Django is the greatest!"}, "DJANGO IS THE GREATEST!"), # Chained filters - 'basic-syntax22': ("{{ var|upper|lower }}", {"var": "Django is the greatest!"}, "django is the greatest!"), + 'filter-syntax02': ("{{ var|upper|lower }}", {"var": "Django is the greatest!"}, "django is the greatest!"), # Raise TemplateSyntaxError for space between a variable and filter pipe - 'basic-syntax23': ("{{ var |upper }}", {}, template.TemplateSyntaxError), + 'filter-syntax03': ("{{ var |upper }}", {}, template.TemplateSyntaxError), # Raise TemplateSyntaxError for space after a filter pipe - 'basic-syntax24': ("{{ var| upper }}", {}, template.TemplateSyntaxError), + 'filter-syntax04': ("{{ var| upper }}", {}, template.TemplateSyntaxError), # Raise TemplateSyntaxError for a nonexistent filter - 'basic-syntax25': ("{{ var|does_not_exist }}", {}, template.TemplateSyntaxError), + 'filter-syntax05': ("{{ var|does_not_exist }}", {}, template.TemplateSyntaxError), # Raise TemplateSyntaxError when trying to access a filter containing an illegal character - 'basic-syntax26': ("{{ var|fil(ter) }}", {}, template.TemplateSyntaxError), + 'filter-syntax06': ("{{ var|fil(ter) }}", {}, template.TemplateSyntaxError), # Raise TemplateSyntaxError for invalid block tags - 'basic-syntax27': ("{% nothing_to_see_here %}", {}, template.TemplateSyntaxError), + 'filter-syntax07': ("{% nothing_to_see_here %}", {}, template.TemplateSyntaxError), # Raise TemplateSyntaxError for empty block tags - 'basic-syntax28': ("{% %}", {}, template.TemplateSyntaxError), + 'filter-syntax08': ("{% %}", {}, template.TemplateSyntaxError), # Chained filters, with an argument to the first one - 'basic-syntax29': ('{{ var|removetags:"b i"|upper|lower }}', {"var": "<b><i>Yes</i></b>"}, "yes"), + 'filter-syntax09': ('{{ var|removetags:"b i"|upper|lower }}', {"var": "<b><i>Yes</i></b>"}, "yes"), # Escaped string as argument - 'basic-syntax30': (r'{{ var|default_if_none:" endquote\" hah" }}', {"var": None}, ' endquote" hah'), + 'filter-syntax10': (r'{{ var|default_if_none:" endquote\" hah" }}', {"var": None}, ' endquote" hah'), # Variable as argument - 'basic-syntax31': (r'{{ var|default_if_none:var2 }}', {"var": None, "var2": "happy"}, 'happy'), + 'filter-syntax11': (r'{{ var|default_if_none:var2 }}', {"var": None, "var2": "happy"}, 'happy'), # Default argument testing - 'basic-syntax32': (r'{{ var|yesno:"yup,nup,mup" }} {{ var|yesno }}', {"var": True}, 'yup yes'), + 'filter-syntax12': (r'{{ var|yesno:"yup,nup,mup" }} {{ var|yesno }}', {"var": True}, 'yup yes'), - # Fail silently for methods that raise an exception with a "silent_variable_failure" attribute - 'basic-syntax33': (r'1{{ var.method3 }}2', {"var": SomeClass()}, ("12", "1INVALID2")), + # Fail silently for methods that raise an exception with a + # "silent_variable_failure" attribute + 'filter-syntax13': (r'1{{ var.method3 }}2', {"var": SomeClass()}, ("12", "1INVALID2")), - # In methods that raise an exception without a "silent_variable_attribute" set to True, - # the exception propagates - 'basic-syntax34': (r'1{{ var.method4 }}2', {"var": SomeClass()}, SomeOtherException), + # In methods that raise an exception without a + # "silent_variable_attribute" set to True, the exception propagates + 'filter-syntax14': (r'1{{ var.method4 }}2', {"var": SomeClass()}, SomeOtherException), # Escaped backslash in argument - 'basic-syntax35': (r'{{ var|default_if_none:"foo\bar" }}', {"var": None}, r'foo\bar'), + 'filter-syntax15': (r'{{ var|default_if_none:"foo\bar" }}', {"var": None}, r'foo\bar'), # Escaped backslash using known escape char - 'basic-syntax35': (r'{{ var|default_if_none:"foo\now" }}', {"var": None}, r'foo\now'), + 'filter-syntax16': (r'{{ var|default_if_none:"foo\now" }}', {"var": None}, r'foo\now'), # Empty strings can be passed as arguments to filters - 'basic-syntax36': (r'{{ var|join:"" }}', {'var': ['a', 'b', 'c']}, 'abc'), + '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. - 'basic-syntax37': (r'{{ var }}', {'var': UnicodeInStrClass()}, '\xc5\xa0\xc4\x90\xc4\x86\xc5\xbd\xc4\x87\xc5\xbe\xc5\xa1\xc4\x91'), + # 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'), ### COMMENT SYNTAX ######################################################## 'comment-syntax01': ("{# this is hidden #}hello", {}, "hello"), @@ -259,6 +272,7 @@ class Templates(unittest.TestCase): 'filter01': ('{% filter upper %}{% endfilter %}', {}, ''), 'filter02': ('{% filter upper %}django{% endfilter %}', {}, 'DJANGO'), 'filter03': ('{% filter upper|lower %}django{% endfilter %}', {}, 'django'), + 'filter04': ('{% filter cut:remove %}djangospam{% endfilter %}', {'remove': 'spam'}, 'django'), ### FIRSTOF TAG ########################################################### 'firstof01': ('{% firstof a b c %}', {'a':0,'b':0,'c':0}, ''), @@ -275,6 +289,20 @@ class Templates(unittest.TestCase): 'for-tag-vars02': ("{% for val in values %}{{ forloop.counter0 }}{% endfor %}", {"values": [6, 6, 6]}, "012"), 'for-tag-vars03': ("{% for val in values %}{{ forloop.revcounter }}{% endfor %}", {"values": [6, 6, 6]}, "321"), 'for-tag-vars04': ("{% for val in values %}{{ forloop.revcounter0 }}{% endfor %}", {"values": [6, 6, 6]}, "210"), + 'for-tag-unpack01': ("{% for key,value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"), + 'for-tag-unpack03': ("{% for key, value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"), + 'for-tag-unpack04': ("{% for key , value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"), + 'for-tag-unpack05': ("{% for key ,value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"), + 'for-tag-unpack06': ("{% for key value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, template.TemplateSyntaxError), + 'for-tag-unpack07': ("{% for key,,value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, template.TemplateSyntaxError), + 'for-tag-unpack08': ("{% for key,value, in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, template.TemplateSyntaxError), + # Ensure that a single loopvar doesn't truncate the list in val. + 'for-tag-unpack09': ("{% for val in items %}{{ val.0 }}:{{ val.1 }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"), + # Otherwise, silently truncate if the length of loopvars differs to the length of each set of items. + 'for-tag-unpack10': ("{% for x,y in items %}{{ x }}:{{ y }}/{% endfor %}", {"items": (('one', 1, 'carrot'), ('two', 2, 'orange'))}, "one:1/two:2/"), + 'for-tag-unpack11': ("{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, ("one:1,/two:2,/", "one:1,INVALID/two:2,INVALID/")), + 'for-tag-unpack12': ("{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}", {"items": (('one', 1, 'carrot'), ('two', 2))}, ("one:1,carrot/two:2,/", "one:1,carrot/two:2,INVALID/")), + 'for-tag-unpack13': ("{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}", {"items": (('one', 1, 'carrot'), ('two', 2, 'cheese'))}, ("one:1,carrot/two:2,cheese/", "one:1,carrot/two:2,cheese/")), ### IF TAG ################################################################ 'if-tag01': ("{% if foo %}yes{% else %}no{% endif %}", {"foo": True}, "yes"), @@ -522,8 +550,8 @@ class Templates(unittest.TestCase): ### I18N ################################################################## # {% spaceless %} tag - 'spaceless01': ("{% spaceless %} <b> <i> text </i> </b> {% endspaceless %}", {}, "<b> <i> text </i> </b>"), - 'spaceless02': ("{% spaceless %} <b> \n <i> text </i> \n </b> {% endspaceless %}", {}, "<b> <i> text </i> </b>"), + 'spaceless01': ("{% spaceless %} <b> <i> text </i> </b> {% endspaceless %}", {}, "<b><i> text </i></b>"), + 'spaceless02': ("{% spaceless %} <b> \n <i> text </i> \n </b> {% endspaceless %}", {}, "<b><i> text </i></b>"), 'spaceless03': ("{% spaceless %}<b><i>text</i></b>{% endspaceless %}", {}, "<b><i>text</i></b>"), # simple translation of a string delimited by ' @@ -572,6 +600,8 @@ class Templates(unittest.TestCase): 'invalidstr03': ('{% for v in var %}({{ v }}){% endfor %}', {}, ''), 'invalidstr04': ('{% if var %}Yes{% else %}No{% endif %}', {}, 'No'), 'invalidstr04': ('{% if var|default:"Foo" %}Yes{% else %}No{% endif %}', {}, 'Yes'), + 'invalidstr05': ('{{ var }}', {}, ('', 'INVALID %s', 'var')), + 'invalidstr06': ('{{ var.prop }}', {'var': {}}, ('', 'INVALID %s', 'var.prop')), ### MULTILINE ############################################################# @@ -650,6 +680,13 @@ class Templates(unittest.TestCase): 'widthratio09': ('{% widthratio a b %}', {'a':50,'b':100}, template.TemplateSyntaxError), 'widthratio10': ('{% widthratio a b 100.0 %}', {'a':50,'b':100}, template.TemplateSyntaxError), + ### WITH TAG ######################################################## + 'with01': ('{% with dict.key as key %}{{ key }}{% endwith %}', {'dict': {'key':50}}, '50'), + 'with02': ('{{ key }}{% with dict.key as key %}{{ key }}-{{ dict.key }}-{{ key }}{% endwith %}{{ key }}', {'dict': {'key':50}}, ('50-50-50', 'INVALID50-50-50INVALID')), + + 'with-error01': ('{% with dict.key xx key %}{{ key }}{% endwith %}', {'dict': {'key':50}}, template.TemplateSyntaxError), + 'with-error02': ('{% with dict.key as %}{{ key }}{% endwith %}', {'dict': {'key':50}}, template.TemplateSyntaxError), + ### NOW TAG ######################################################## # Simple case 'now01' : ('{% now "j n Y"%}', {}, str(datetime.now().day) + ' ' + str(datetime.now().month) + ' ' + str(datetime.now().year)), @@ -686,13 +723,14 @@ class Templates(unittest.TestCase): ### URL TAG ######################################################## # Successes 'url01' : ('{% url regressiontests.templates.views.client client.id %}', {'client': {'id': 1}}, '/url_tag/client/1/'), - 'url02' : ('{% url regressiontests.templates.views.client_action client.id,action="update" %}', {'client': {'id': 1}}, '/url_tag/client/1/update/'), + '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/'), # Failures - 'url04' : ('{% url %}', {}, template.TemplateSyntaxError), - 'url05' : ('{% url no_such_view %}', {}, ''), - 'url06' : ('{% url regressiontests.templates.views.client no_such_param="value" %}', {}, ''), + 'url-fail01' : ('{% url %}', {}, template.TemplateSyntaxError), + 'url-fail02' : ('{% url no_such_view %}', {}, ''), + 'url-fail03' : ('{% url regressiontests.templates.views.client no_such_param="value" %}', {}, ''), } # Register our custom template loader. @@ -715,6 +753,7 @@ class Templates(unittest.TestCase): # Set TEMPLATE_STRING_IF_INVALID to a known string old_invalid = settings.TEMPLATE_STRING_IF_INVALID + expected_invalid_str = 'INVALID' for name, vals in tests: install() @@ -722,6 +761,10 @@ class Templates(unittest.TestCase): if isinstance(vals[2], tuple): normal_string_result = vals[2][0] invalid_string_result = vals[2][1] + if '%s' in invalid_string_result: + expected_invalid_str = 'INVALID %s' + invalid_string_result = invalid_string_result % vals[2][2] + template.invalid_var_format_string = True else: normal_string_result = vals[2] invalid_string_result = vals[2] @@ -732,7 +775,7 @@ class Templates(unittest.TestCase): activate('en-us') for invalid_str, result in [('', normal_string_result), - ('INVALID', invalid_string_result)]: + (expected_invalid_str, invalid_string_result)]: settings.TEMPLATE_STRING_IF_INVALID = invalid_str try: output = loader.get_template(name).render(template.Context(vals[1])) @@ -746,6 +789,10 @@ class Templates(unittest.TestCase): if 'LANGUAGE_CODE' in vals[1]: deactivate() + if template.invalid_var_format_string: + expected_invalid_str = 'INVALID' + template.invalid_var_format_string = False + loader.template_source_loaders = old_template_loaders deactivate() settings.TEMPLATE_DEBUG = old_td diff --git a/tests/regressiontests/templates/urls.py b/tests/regressiontests/templates/urls.py index dc5b36b08b..eaa9fd5d9f 100644 --- a/tests/regressiontests/templates/urls.py +++ b/tests/regressiontests/templates/urls.py @@ -7,4 +7,5 @@ urlpatterns = patterns('', (r'^$', views.index), (r'^client/(\d+)/$', views.client), (r'^client/(\d+)/(?P<action>[^/]+)/$', views.client_action), + url(r'^named-client/(\d+)/$', views.client, name="named-client"), ) diff --git a/tests/regressiontests/markup/models.py b/tests/regressiontests/test_client_regress/__init__.py index e69de29bb2..e69de29bb2 100644 --- a/tests/regressiontests/markup/models.py +++ b/tests/regressiontests/test_client_regress/__init__.py diff --git a/tests/regressiontests/test_client_regress/models.py b/tests/regressiontests/test_client_regress/models.py new file mode 100644 index 0000000000..40d022a47a --- /dev/null +++ b/tests/regressiontests/test_client_regress/models.py @@ -0,0 +1,164 @@ +""" +Regression tests for the Test Client, especially the customized assertions. + +""" +from django.test import Client, TestCase +from django.core import mail + +class AssertTemplateUsedTests(TestCase): + fixtures = ['testdata.json'] + + def test_no_context(self): + "Template usage assertions work then templates aren't in use" + response = self.client.get('/test_client_regress/no_template_view/') + + # Check that the no template case doesn't mess with the template assertions + self.assertTemplateNotUsed(response, 'GET Template') + + try: + self.assertTemplateUsed(response, 'GET Template') + except AssertionError, e: + self.assertEquals(str(e), "No templates used to render the response") + + def test_single_context(self): + "Template assertions work when there is a single context" + response = self.client.get('/test_client/post_view/', {}) + + # + try: + self.assertTemplateNotUsed(response, 'Empty GET Template') + except AssertionError, e: + self.assertEquals(str(e), "Template 'Empty GET Template' was used unexpectedly in rendering the response") + + try: + self.assertTemplateUsed(response, 'Empty POST Template') + except AssertionError, e: + self.assertEquals(str(e), "Template 'Empty POST Template' was not used to render the response. Actual template was 'Empty GET Template'") + + def test_multiple_context(self): + "Template assertions work when there are multiple contexts" + post_data = { + 'text': 'Hello World', + 'email': 'foo@example.com', + 'value': 37, + 'single': 'b', + 'multi': ('b','c','e') + } + response = self.client.post('/test_client/form_view_with_template/', post_data) + self.assertContains(response, 'POST data OK') + try: + self.assertTemplateNotUsed(response, "form_view.html") + except AssertionError, e: + self.assertEquals(str(e), "Template 'form_view.html' was used unexpectedly in rendering the response") + + try: + self.assertTemplateNotUsed(response, 'base.html') + except AssertionError, e: + self.assertEquals(str(e), "Template 'base.html' was used unexpectedly in rendering the response") + + 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']") + +class AssertRedirectsTests(TestCase): + def test_redirect_page(self): + "An assertion is raised if the original page couldn't be retrieved as expected" + # This page will redirect with code 301, not 302 + response = self.client.get('/test_client/permanent_redirect_view/') + 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)") + + def test_incorrect_target(self): + "An assertion is raised if the response redirects to another target" + response = self.client.get('/test_client/permanent_redirect_view/') + try: + # 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)") + + def test_target_page(self): + "An assertion is raised if the reponse 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 + self.assertRedirects(response, '/test_client/permanent_redirect_view/') + except AssertionError, e: + self.assertEquals(str(e), "Couldn't retrieve redirection page '/test_client/permanent_redirect_view/': response code was 301 (expected 200)") + +class AssertFormErrorTests(TestCase): + def test_unknown_form(self): + "An assertion is raised if the form name is unknown" + post_data = { + 'text': 'Hello World', + 'email': 'not an email address', + 'value': 37, + 'single': 'b', + 'multi': ('b','c','e') + } + response = self.client.post('/test_client/form_view/', post_data) + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, "Invalid POST Template") + + try: + self.assertFormError(response, 'wrong_form', 'some_field', 'Some error.') + except AssertionError, e: + self.assertEqual(str(e), "The form 'wrong_form' was not used to render the response") + + def test_unknown_field(self): + "An assertion is raised if the field name is unknown" + post_data = { + 'text': 'Hello World', + 'email': 'not an email address', + 'value': 37, + 'single': 'b', + 'multi': ('b','c','e') + } + response = self.client.post('/test_client/form_view/', post_data) + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, "Invalid POST Template") + + try: + self.assertFormError(response, 'form', 'some_field', 'Some error.') + except AssertionError, e: + self.assertEqual(str(e), "The form 'form' in context 0 does not contain the field 'some_field'") + + def test_noerror_field(self): + "An assertion is raised if the field doesn't have any errors" + post_data = { + 'text': 'Hello World', + 'email': 'not an email address', + 'value': 37, + 'single': 'b', + 'multi': ('b','c','e') + } + response = self.client.post('/test_client/form_view/', post_data) + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, "Invalid POST Template") + + try: + self.assertFormError(response, 'form', 'value', 'Some error.') + except AssertionError, e: + self.assertEqual(str(e), "The field 'value' on form 'form' in context 0 contains no errors") + + def test_unknown_error(self): + "An assertion is raised if the field doesn't contain the provided error" + post_data = { + 'text': 'Hello World', + 'email': 'not an email address', + 'value': 37, + 'single': 'b', + 'multi': ('b','c','e') + } + response = self.client.post('/test_client/form_view/', post_data) + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, "Invalid POST Template") + + try: + self.assertFormError(response, 'form', 'email', 'Some error.') + 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.'])") + diff --git a/tests/regressiontests/test_client_regress/urls.py b/tests/regressiontests/test_client_regress/urls.py new file mode 100644 index 0000000000..e9cd0aea15 --- /dev/null +++ b/tests/regressiontests/test_client_regress/urls.py @@ -0,0 +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), +) diff --git a/tests/regressiontests/test_client_regress/views.py b/tests/regressiontests/test_client_regress/views.py new file mode 100644 index 0000000000..d8dd2b349c --- /dev/null +++ b/tests/regressiontests/test_client_regress/views.py @@ -0,0 +1,8 @@ +from django.core.mail import EmailMessage, SMTPConnection +from django.http import HttpResponse +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") + diff --git a/tests/regressiontests/text/__init__.py b/tests/regressiontests/text/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/regressiontests/text/__init__.py diff --git a/tests/regressiontests/text/models.py b/tests/regressiontests/text/models.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/regressiontests/text/models.py diff --git a/tests/regressiontests/text/tests.py b/tests/regressiontests/text/tests.py new file mode 100644 index 0000000000..f758ecaf90 --- /dev/null +++ b/tests/regressiontests/text/tests.py @@ -0,0 +1,17 @@ +""" +# Tests for stuff in django.utils.text. + +>>> from django.utils.text import * + +### smart_split ########################################################### +>>> list(smart_split(r'''This is "a person" test.''')) +['This', 'is', '"a person"', '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] +"a person"s" +>>> list(smart_split('''"a 'one''')) +['"a', "'one"] +>>> print list(smart_split(r'''all friends' tests'''))[1] +friends' +""" |
