summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorJustin Bronn <jbronn@gmail.com>2008-01-02 16:57:53 +0000
committerJustin Bronn <jbronn@gmail.com>2008-01-02 16:57:53 +0000
commit7e322b5908bf5ec255c90c615d1b45b358ecee5f (patch)
tree3390dc70c5b4c4a8ff583308bd904eb749f97c04 /django
parentef0f46f1d0b9e41c811d3d6b833f1652ad46aec3 (diff)
gis: Merged revisions 6920-6989 via svnmerge from trunk.
git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@6990 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django')
-rw-r--r--django/conf/__init__.py4
-rw-r--r--django/conf/locale/nl/LC_MESSAGES/django.mobin38003 -> 38049 bytes
-rw-r--r--django/conf/locale/nl/LC_MESSAGES/django.po73
-rw-r--r--django/contrib/admin/media/js/urlify.js7
-rw-r--r--django/contrib/databrowse/datastructures.py5
-rw-r--r--django/contrib/localflavor/ar/forms.py28
-rw-r--r--django/contrib/localflavor/au/forms.py14
-rw-r--r--django/contrib/localflavor/br/forms.py47
-rw-r--r--django/contrib/localflavor/ca/forms.py102
-rw-r--r--django/contrib/localflavor/ch/forms.py25
-rw-r--r--django/contrib/localflavor/cl/forms.py13
-rw-r--r--django/contrib/localflavor/de/forms.py18
-rw-r--r--django/contrib/localflavor/es/forms.py49
-rw-r--r--django/contrib/localflavor/fi/forms.py15
-rw-r--r--django/contrib/localflavor/fr/forms.py14
-rw-r--r--django/contrib/localflavor/in_/forms.py14
-rw-r--r--django/contrib/localflavor/is_/forms.py10
-rw-r--r--django/contrib/localflavor/it/forms.py28
-rw-r--r--django/contrib/localflavor/jp/forms.py8
-rw-r--r--django/contrib/localflavor/nl/forms.py56
-rw-r--r--django/contrib/localflavor/no/forms.py21
-rw-r--r--django/contrib/localflavor/pe/forms.py18
-rw-r--r--django/contrib/localflavor/pl/forms.py37
-rw-r--r--django/contrib/localflavor/sk/forms.py8
-rw-r--r--django/contrib/localflavor/uk/forms.py32
-rw-r--r--django/contrib/localflavor/us/forms.py31
-rw-r--r--django/contrib/localflavor/za/forms.py22
-rw-r--r--django/core/mail.py60
-rw-r--r--django/core/management/base.py2
-rw-r--r--django/core/management/commands/dumpdata.py12
-rw-r--r--django/core/management/commands/loaddata.py11
-rw-r--r--django/core/management/sql.py3
-rw-r--r--django/core/serializers/__init__.py5
-rw-r--r--django/core/serializers/base.py4
-rw-r--r--django/core/serializers/json.py2
-rw-r--r--django/core/serializers/python.py4
-rw-r--r--django/core/serializers/pyyaml.py2
-rw-r--r--django/core/servers/basehttp.py16
-rw-r--r--django/db/backends/oracle/base.py18
-rw-r--r--django/db/models/fields/__init__.py13
-rw-r--r--django/http/__init__.py13
-rw-r--r--django/newforms/models.py24
-rw-r--r--django/template/__init__.py171
-rw-r--r--django/template/context.py6
-rw-r--r--django/template/debug.py97
-rw-r--r--django/template/defaultfilters.py6
-rw-r--r--django/template/defaulttags.py62
-rw-r--r--django/test/utils.py44
-rw-r--r--django/views/debug.py2
-rw-r--r--django/views/static.py3
50 files changed, 769 insertions, 510 deletions
diff --git a/django/conf/__init__.py b/django/conf/__init__.py
index c24e87e6ed..61d9ff7536 100644
--- a/django/conf/__init__.py
+++ b/django/conf/__init__.py
@@ -52,7 +52,9 @@ class LazySettings(object):
if not settings_module: # If it's set but is an empty string.
raise KeyError
except KeyError:
- raise ImportError, "Environment variable %s is undefined so settings cannot be imported." % ENVIRONMENT_VARIABLE # NOTE: This is arguably an EnvironmentError, but that causes problems with Python's interactive help
+ # NOTE: This is arguably an EnvironmentError, but that causes
+ # problems with Python's interactive help.
+ raise ImportError("Settings cannot be imported, because environment variable %s is undefined." % ENVIRONMENT_VARIABLE)
self._target = Settings(settings_module)
diff --git a/django/conf/locale/nl/LC_MESSAGES/django.mo b/django/conf/locale/nl/LC_MESSAGES/django.mo
index 4c70c28ff4..b49be6a3e3 100644
--- a/django/conf/locale/nl/LC_MESSAGES/django.mo
+++ b/django/conf/locale/nl/LC_MESSAGES/django.mo
Binary files differ
diff --git a/django/conf/locale/nl/LC_MESSAGES/django.po b/django/conf/locale/nl/LC_MESSAGES/django.po
index 9aaeef8af9..b8c516b72d 100644
--- a/django/conf/locale/nl/LC_MESSAGES/django.po
+++ b/django/conf/locale/nl/LC_MESSAGES/django.po
@@ -1,17 +1,12 @@
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the django package.
-# Johan C. Stöver <johan@nilling.nl>, 2005.
-# Rudolph Froger <rfroger@estrate.nl>, 2006.
-#
+
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: Django 1.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2006-12-09 15:51+0100\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: Johan C. Stöver <johan@nilling.nl>\n"
+"PO-Revision-Date: 2007-12-17 21:14+0100\n"
+"Last-Translator: I.S. van Oostveen\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
@@ -69,7 +64,7 @@ msgstr "De waarde moet een geheel getal zijn."
#: db/models/fields/__init__.py:381
msgid "This value must be either True or False."
-msgstr "De waarde moet of True (waar) of False (onwaar) zijn."
+msgstr "De waarde moet of True (Waar) of False (Onwaar) zijn."
#: db/models/fields/__init__.py:397
msgid "This field cannot be null."
@@ -279,7 +274,7 @@ msgstr "Alleen alfabetische karakters zijn toegestaan"
#: core/validators.py:139
msgid "Year must be 1900 or later."
-msgstr "Het jaartal moet 1900 of nieuwer zijn."
+msgstr "Het jaartal moet 1900 of later zijn."
#: core/validators.py:143
#, python-format
@@ -296,20 +291,20 @@ msgstr "Geef een geldig e-mailadres op."
#: core/validators.py:173 core/validators.py:442 forms/__init__.py:667
msgid "No file was submitted. Check the encoding type on the form."
-msgstr "Er was geen bestand verstuurd. Controleer de encoding van het formulier."
+msgstr "Er was geen bestand verstuurd. Controleer het coderings type van het formulier."
#: core/validators.py:177
msgid ""
"Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image."
msgstr ""
-"Bestand ongeldig. Het bestand dat is gegeven is geen afbeelding of was "
+"Bestand ongeldig. Het bestand dat is gegeven is geen afbeelding of is "
"beschadigd."
#: core/validators.py:184
#, python-format
msgid "The URL %s does not point to a valid image."
-msgstr "De URL %s wijst niet naar een afbeelding."
+msgstr "De URL %s wijst niet naar een geldige afbeelding."
#: core/validators.py:188
#, python-format
@@ -321,7 +316,7 @@ msgstr ""
#: core/validators.py:196
#, python-format
msgid "The URL %s does not point to a valid QuickTime video."
-msgstr "De URL %s wijst niet naar een QuickTime video."
+msgstr "De URL %s wijst niet naar een geldige QuickTime video."
#: core/validators.py:200
msgid "A valid URL is required."
@@ -349,18 +344,18 @@ msgstr "Ongeldige URL: %s"
#: core/validators.py:243 core/validators.py:245
#, python-format
msgid "The URL %s is a broken link."
-msgstr "De URL %s is een niet werkende link."
+msgstr "De URL %s is niet een werkende link."
#: core/validators.py:251
msgid "Enter a valid U.S. state abbreviation."
-msgstr "Geef een geldige afkorting van een VS staat."
+msgstr "Geef een geldige afkorting van een staat in de VS."
#: core/validators.py:265
#, python-format
msgid "Watch your mouth! The word %s is not allowed here."
msgid_plural "Watch your mouth! The words %s are not allowed here."
msgstr[0] "Pas op uw taalgebruik! Gebruik van %s niet toegestaan."
-msgstr[1] "Pas op uw taalgebruik! Gebruik van de woorden %s niet toegestaan."
+msgstr[1] "Pas op uw taalgebruik! Gebruik van de woorden %s is niet toegestaan."
#: core/validators.py:272
#, python-format
@@ -373,7 +368,7 @@ msgstr "Voer tenminste één veld in."
#: core/validators.py:300 core/validators.py:311
msgid "Please enter both fields or leave them both empty."
-msgstr "Voer waarden in in beide velden of laat beide leeg."
+msgstr "Voer waarden in beide velden in of laat beide leeg."
#: core/validators.py:318
#, python-format
@@ -450,7 +445,7 @@ msgstr "Zorg ervoor dat het bestand hoogstens %s bytes groot is."
#: core/validators.py:453
msgid "The format for this field is wrong."
-msgstr "Het formaat van dit veld is fout."
+msgstr "Het formaat van dit veld is foutief."
#: core/validators.py:468
msgid "This field is invalid."
@@ -536,7 +531,7 @@ msgid ""
"Your Web browser doesn't appear to have cookies enabled. Cookies are "
"required for logging in."
msgstr ""
-"Het lijkt erop dat uw browser geen cookies accepteerd. Om aan te melden "
+"Het lijkt erop dat uw browser geen cookies accepteert. Om aan te melden "
"moeten cookies worden geaccepteerd."
#: contrib/auth/forms.py:59 contrib/admin/views/decorators.py:10
@@ -645,7 +640,7 @@ msgstr "supergebruiker status"
msgid ""
"Designates that this user has all permissions without explicitly assigning "
"them."
-msgstr "Bepaald dat deze gebruiker alle rechten heeft, zonder deze expliciet toe te wijzen."
+msgstr "Bepaalt dat deze gebruiker alle rechten heeft, zonder deze expliciet toe te wijzen."
#: contrib/auth/models.py:98
msgid "last login"
@@ -685,7 +680,7 @@ msgstr "Rechten"
#: contrib/auth/models.py:113
msgid "Important dates"
-msgstr "Belangrijke data"
+msgstr "Belangrijke datums"
#: contrib/auth/models.py:114
msgid "Groups"
@@ -783,7 +778,7 @@ msgid ""
"Please log in again, because your session has expired. Don't worry: Your "
"submission has been saved."
msgstr ""
-"Uw sessie is verlopen, meldt u opnieuw aan. Maakt u geen zorgen: Uw bijdrage "
+"Uw sessie is verlopen, meld u opnieuw aan. Maakt u zich geen zorgen: Uw bijdrage "
"is opgeslagen."
#: contrib/admin/views/decorators.py:69
@@ -791,7 +786,7 @@ msgid ""
"Looks like your browser isn't configured to accept cookies. Please enable "
"cookies, reload this page, and try again."
msgstr ""
-"Het lijkt erop dat uw browser geen cookies accepteerd. Zet het gebruik van "
+"Het lijkt erop dat uw browser geen cookies accepteert. Zet het gebruik van "
"cookies aan in uw browser, laad deze pagina nogmaals en probeer het opnieuw."
#: contrib/admin/views/decorators.py:83
@@ -928,7 +923,7 @@ msgstr "Model %r niet gevonden in app %r"
#: contrib/admin/views/doc.py:183
#, python-format
msgid "the related `%s.%s` object"
-msgstr "the related `%s.%s` object"
+msgstr "het gerelateerde `%s.%s` object"
#: contrib/admin/views/doc.py:183 contrib/admin/views/doc.py:205
#: contrib/admin/views/doc.py:219 contrib/admin/views/doc.py:224
@@ -938,7 +933,7 @@ msgstr "model:"
#: contrib/admin/views/doc.py:214
#, python-format
msgid "related `%s.%s` objects"
-msgstr "related `%s.%s` objects"
+msgstr "de gerelateerde `%s.%s` objecten"
#: contrib/admin/views/doc.py:219
#, python-format
@@ -948,12 +943,12 @@ msgstr "alle %s"
#: contrib/admin/views/doc.py:224
#, python-format
msgid "number of %s"
-msgstr "nummer van %s"
+msgstr "aantal %s"
#: contrib/admin/views/doc.py:229
#, python-format
msgid "Fields on %s objects"
-msgstr "Velden van %s objects"
+msgstr "Velden van %s objecten"
#: contrib/admin/views/doc.py:291 contrib/admin/views/doc.py:301
#: contrib/admin/views/doc.py:303 contrib/admin/views/doc.py:309
@@ -1139,11 +1134,11 @@ msgid ""
"All of the following related items will be deleted:"
msgstr ""
"Weet u zeker dat u %(object_name)s \"%(escaped_object)s\" wilt verwijderen? Alle "
-"volgende opjecten worden verwijderd:"
+"volgende objecten worden verwijderd:"
#: contrib/admin/templates/admin/delete_confirmation.html:26
msgid "Yes, I'm sure"
-msgstr "Ja, Ik weet het zeker"
+msgstr "Ja, ik weet het zeker"
#: contrib/admin/templates/admin/404.html:4
#: contrib/admin/templates/admin/404.html:8
@@ -1197,7 +1192,7 @@ msgstr "Opslaan en nieuw item"
#: contrib/admin/templates/admin/submit_line.html:6
msgid "Save and continue editing"
-msgstr "Opslaan en bewerk opnieuw"
+msgstr "Opslaan en opnieuw bewerken"
#: contrib/admin/templates/admin/submit_line.html:7
msgid "Save"
@@ -1224,7 +1219,7 @@ msgstr "Wijzigen"
#: contrib/admin/templates/admin/index.html:44
msgid "You don't have permission to edit anything."
-msgstr "U heeft geen rechten om iets te wijzigen"
+msgstr "U heeft geen rechten om iets te wijzigen."
#: contrib/admin/templates/admin/index.html:52
msgid "Recent Actions"
@@ -1380,7 +1375,7 @@ msgstr ""
"de bookmarklet vanuit elke pagina op de site worden gekozen. Let erop dat "
"het soms\n"
"noodzakelijk is dat de computer van waaruit de pagina wordt bekeken intern "
-"is\n"
+"is.\n"
"(Raadpleeg uw systeembeheerder of uw computer zich op het interne netwerk "
"bevind).<p>\n"
@@ -1393,7 +1388,7 @@ msgid ""
"Jumps you from any page to the documentation for the view that generates "
"that page."
msgstr ""
-"Spring vanuit elke pagina naar de documentatie voor de view die gegenereerd "
+"Springt vanuit elke pagina naar de documentatie voor de view die gegenereerd "
"wordt door die pagina"
#: contrib/admin/templates/admin_doc/bookmarklets.html:22
@@ -1406,7 +1401,7 @@ msgid ""
"object."
msgstr ""
"Toont de content-type en unieke ID voor pagina's die een enkel object "
-"voorsteld."
+"voorstellen."
#: contrib/admin/templates/admin_doc/bookmarklets.html:25
msgid "Edit this object (current window)"
@@ -1414,7 +1409,7 @@ msgstr "Bewerk dit object (huidig venster)"
#: contrib/admin/templates/admin_doc/bookmarklets.html:26
msgid "Jumps to the admin page for pages that represent a single object."
-msgstr "Ga naar de beheerpagina voor pagina's die een enkel object weergeven."
+msgstr "Gaat naar de beheerpagina voor pagina's die een enkel object weergeven."
#: contrib/admin/templates/admin_doc/bookmarklets.html:28
msgid "Edit this object (new window)"
@@ -1527,7 +1522,7 @@ msgid ""
"password twice so we can verify you typed it in correctly."
msgstr ""
"Vanwege de beveiliging moet u uw oude en twee keer een nieuw "
-"wachtwoordinvoeren, zodat we kunnen controleren of er geen typefouten zijn "
+"wachtwoord invoeren, zodat we kunnen controleren of er geen typefouten zijn "
"gemaakt."
#: contrib/admin/templates/registration/password_change_form.html:17
@@ -1597,7 +1592,7 @@ msgid ""
"Example: 'flatpages/contact_page.html'. If this isn't provided, the system "
"will use 'flatpages/default.html'."
msgstr ""
-"Voorbeeld: 'flatpages/contact_page'. Als deze niet is opgegeven, dan wordt "
+"Voorbeeld: 'flatpages/contact_page.html'. Als deze niet is opgegeven, dan wordt "
"'flatpages/default.html' gebruikt."
#: contrib/flatpages/models.py:14
@@ -1894,7 +1889,7 @@ msgstr "Een of meerdere verplichte velden zijn niet ingevuld"
#: contrib/comments/views/comments.py:196
#: contrib/comments/views/comments.py:286
msgid "Somebody tampered with the comment form (security violation)"
-msgstr "Iemand heeft het opmerkingenformulier gewijzigd (Beveilingsinbreuk)"
+msgstr "Iemand heeft het opmerkingenformulier gewijzigd (beveilingsinbreuk)"
#: contrib/comments/views/comments.py:206
#: contrib/comments/views/comments.py:292
diff --git a/django/contrib/admin/media/js/urlify.js b/django/contrib/admin/media/js/urlify.js
index 74e6e57e6c..63e626d884 100644
--- a/django/contrib/admin/media/js/urlify.js
+++ b/django/contrib/admin/media/js/urlify.js
@@ -55,6 +55,12 @@ var POLISH_MAP = {
'Ź':'Z', 'Ż':'Z'
}
+var LATVIAN_MAP = {
+ 'ā':'a', 'č':'c', 'ē':'e', 'ģ':'g', 'ī':'i', 'ķ':'k', 'ļ':'l', 'ņ':'n',
+ 'š':'s', 'ū':'u', 'ž':'z', 'Ā':'A', 'Č':'C', 'Ē':'E', 'Ģ':'G', 'Ī':'i',
+ 'Ķ':'k', 'Ļ':'L', 'Ņ':'N', 'Š':'S', 'Ū':'u', 'Ž':'Z'
+}
+
var ALL_DOWNCODE_MAPS=new Array()
ALL_DOWNCODE_MAPS[0]=LATIN_MAP
ALL_DOWNCODE_MAPS[1]=LATIN_SYMBOLS_MAP
@@ -64,6 +70,7 @@ ALL_DOWNCODE_MAPS[4]=RUSSIAN_MAP
ALL_DOWNCODE_MAPS[5]=UKRAINIAN_MAP
ALL_DOWNCODE_MAPS[6]=CZECH_MAP
ALL_DOWNCODE_MAPS[7]=POLISH_MAP
+ALL_DOWNCODE_MAPS[8]=LATVIAN_MAP
var Downcoder = new Object();
Downcoder.Initialize = function()
diff --git a/django/contrib/databrowse/datastructures.py b/django/contrib/databrowse/datastructures.py
index 4b0947eb34..1b32ee2e2e 100644
--- a/django/contrib/databrowse/datastructures.py
+++ b/django/contrib/databrowse/datastructures.py
@@ -12,6 +12,7 @@ from django.utils.safestring import mark_safe
from django.db.models.query import QuerySet
EMPTY_VALUE = '(None)'
+DISPLAY_SIZE = 100
class EasyModel(object):
def __init__(self, site, model):
@@ -93,8 +94,8 @@ class EasyInstance(object):
def __unicode__(self):
val = smart_unicode(self.instance)
- if len(val) > 30:
- return val[:30] + u'...'
+ if len(val) > DISPLAY_SIZE:
+ return val[:DISPLAY_SIZE] + u'...'
return val
def __str__(self):
diff --git a/django/contrib/localflavor/ar/forms.py b/django/contrib/localflavor/ar/forms.py
index 6d86e4e676..6374a48c92 100644
--- a/django/contrib/localflavor/ar/forms.py
+++ b/django/contrib/localflavor/ar/forms.py
@@ -24,18 +24,20 @@ class ARPostalCodeField(RegexField):
See http://www.correoargentino.com.ar/consulta_cpa/home.php
"""
+ default_error_messages = {
+ 'invalid': ugettext("Enter a postal code in the format NNNN or ANNNNAAA."),
+ }
+
def __init__(self, *args, **kwargs):
super(ARPostalCodeField, self).__init__(r'^\d{4}$|^[A-HJ-NP-Za-hj-np-z]\d{4}\D{3}$',
- min_length=4, max_length=8,
- error_message=ugettext("Enter a postal code in the format NNNN or ANNNNAAA."),
- *args, **kwargs)
+ min_length=4, max_length=8, *args, **kwargs)
def clean(self, value):
value = super(ARPostalCodeField, self).clean(value)
if value in EMPTY_VALUES:
return u''
if len(value) not in (4, 8):
- raise ValidationError(ugettext("Enter a postal code in the format NNNN or ANNNNAAA."))
+ raise ValidationError(self.error_messages['invalid'])
if len(value) == 8:
return u'%s%s%s' % (value[0].upper(), value[1:5], value[5:].upper())
return value
@@ -44,6 +46,11 @@ class ARDNIField(CharField):
"""
A field that validates `Documento Nacional de Identidad (DNI) numbers.
"""
+ default_error_messages = {
+ 'invalid': ugettext("This field requires only numbers."),
+ 'max_digits': ugettext("This field requires 7 or 8 digits."),
+ }
+
def __init__(self, *args, **kwargs):
super(ARDNIField, self).__init__(max_length=10, min_length=7, *args,
**kwargs)
@@ -58,10 +65,9 @@ class ARDNIField(CharField):
if not value.isdigit():
value = value.replace('.', '')
if not value.isdigit():
- raise ValidationError(ugettext("This field requires only numbers."))
+ raise ValidationError(self.error_messages['invalid'])
if len(value) not in (7, 8):
- raise ValidationError(
- ugettext("This field requires 7 or 8 digits."))
+ raise ValidationError(self.error_messages['max_digits'])
return value
@@ -70,9 +76,13 @@ class ARCUITField(RegexField):
This field validates a CUIT (Cdigo nico de Identificacin Tributaria). A
CUIT is of the form XX-XXXXXXXX-V. The last digit is a check digit.
"""
+ default_error_messages = {
+ 'invalid': ugettext('Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'),
+ 'checksum': ugettext("Invalid CUIT."),
+ }
+
def __init__(self, *args, **kwargs):
super(ARCUITField, self).__init__(r'^\d{2}-?\d{8}-?\d$',
- error_message=ugettext('Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'),
*args, **kwargs)
def clean(self, value):
@@ -85,7 +95,7 @@ class ARCUITField(RegexField):
return u''
value, cd = self._canon(value)
if self._calc_cd(value) != cd:
- raise ValidationError(ugettext("Invalid CUIT."))
+ raise ValidationError(self.error_messages['checksum'])
return self._format(value, cd)
def _canon(self, cuit):
diff --git a/django/contrib/localflavor/au/forms.py b/django/contrib/localflavor/au/forms.py
index 33e883511b..ada27b69b9 100644
--- a/django/contrib/localflavor/au/forms.py
+++ b/django/contrib/localflavor/au/forms.py
@@ -12,14 +12,20 @@ PHONE_DIGITS_RE = re.compile(r'^(\d{10})$')
class AUPostCodeField(RegexField):
"""Australian post code field."""
+ default_error_messages = {
+ 'invalid': ugettext('Enter a 4 digit post code.'),
+ }
+
def __init__(self, *args, **kwargs):
super(AUPostCodeField, self).__init__(r'^\d{4}$',
- max_length=None, min_length=None,
- error_message=ugettext('Enter a 4 digit post code.'),
- *args, **kwargs)
+ max_length=None, min_length=None, *args, **kwargs)
class AUPhoneNumberField(Field):
"""Australian phone number field."""
+ default_error_messages = {
+ 'invalid': u'Phone numbers must contain 10 digits.',
+ }
+
def clean(self, value):
"""
Validate a phone number. Strips parentheses, whitespace and hyphens.
@@ -31,7 +37,7 @@ class AUPhoneNumberField(Field):
phone_match = PHONE_DIGITS_RE.search(value)
if phone_match:
return u'%s' % phone_match.group(1)
- raise ValidationError(u'Phone numbers must contain 10 digits.')
+ raise ValidationError(self.error_messages['invalid'])
class AUStateSelect(Select):
"""
diff --git a/django/contrib/localflavor/br/forms.py b/django/contrib/localflavor/br/forms.py
index 58bf795292..aa7e3b23fd 100644
--- a/django/contrib/localflavor/br/forms.py
+++ b/django/contrib/localflavor/br/forms.py
@@ -17,13 +17,19 @@ except NameError:
phone_digits_re = re.compile(r'^(\d{2})[-\.]?(\d{4})[-\.]?(\d{4})$')
class BRZipCodeField(RegexField):
+ default_error_messages = {
+ 'invalid': _('Enter a zip code in the format XXXXX-XXX.'),
+ }
+
def __init__(self, *args, **kwargs):
super(BRZipCodeField, self).__init__(r'^\d{5}-\d{3}$',
- max_length=None, min_length=None,
- error_message=_('Enter a zip code in the format XXXXX-XXX.'),
- *args, **kwargs)
+ max_length=None, min_length=None, *args, **kwargs)
class BRPhoneNumberField(Field):
+ default_error_messages = {
+ 'invalid': _('Phone numbers must be in XX-XXXX-XXXX format.'),
+ }
+
def clean(self, value):
super(BRPhoneNumberField, self).clean(value)
if value in EMPTY_VALUES:
@@ -32,7 +38,7 @@ class BRPhoneNumberField(Field):
m = phone_digits_re.search(value)
if m:
return u'%s-%s-%s' % (m.group(1), m.group(2), m.group(3))
- raise ValidationError(_('Phone numbers must be in XX-XXXX-XXXX format.'))
+ raise ValidationError(self.error_messages['invalid'])
class BRStateSelect(Select):
"""
@@ -48,6 +54,9 @@ class BRStateChoiceField(Field):
A choice field that uses a list of Brazilian states as its choices.
"""
widget = Select
+ default_error_messages = {
+ 'invalid': _(u'Select a valid brazilian state. That state is not one of the available states.'),
+ }
def __init__(self, required=True, widget=None, label=None,
initial=None, help_text=None):
@@ -65,9 +74,7 @@ class BRStateChoiceField(Field):
return value
valid_values = set([smart_unicode(k) for k, v in self.widget.choices])
if value not in valid_values:
- raise ValidationError(_(u'Select a valid brazilian state.'
- u' That state is not one'
- u' of the available states.'))
+ raise ValidationError(self.error_messages['invalid'])
return value
def DV_maker(v):
@@ -83,6 +90,12 @@ class BRCPFField(CharField):
More information:
http://en.wikipedia.org/wiki/Cadastro_de_Pessoas_F%C3%ADsicas
"""
+ default_error_messages = {
+ 'invalid': _("Invalid CPF number."),
+ 'max_digits': _("This field requires at most 11 digits or 14 characters."),
+ 'digits_only': _("This field requires only numbers."),
+ }
+
def __init__(self, *args, **kwargs):
super(BRCPFField, self).__init__(max_length=14, min_length=11, *args, **kwargs)
@@ -100,9 +113,9 @@ class BRCPFField(CharField):
try:
int(value)
except ValueError:
- raise ValidationError(_("This field requires only numbers."))
+ raise ValidationError(self.error_messages['digits_only'])
if len(value) != 11:
- raise ValidationError(_("This field requires at most 11 digits or 14 characters."))
+ raise ValidationError(self.error_messages['max_digits'])
orig_dv = value[-2:]
new_1dv = sum([i * int(value[idx]) for idx, i in enumerate(range(10, 1, -1))])
@@ -112,11 +125,17 @@ class BRCPFField(CharField):
new_2dv = DV_maker(new_2dv % 11)
value = value[:-1] + str(new_2dv)
if value[-2:] != orig_dv:
- raise ValidationError(_("Invalid CPF number."))
+ raise ValidationError(self.error_messages['invalid'])
return orig_value
class BRCNPJField(Field):
+ default_error_messages = {
+ 'invalid': _("Invalid CNPJ number."),
+ 'digits_only': _("This field requires only numbers."),
+ 'max_digits': _("This field requires at least 14 digits"),
+ }
+
def clean(self, value):
"""
Value can be either a string in the format XX.XXX.XXX/XXXX-XX or a
@@ -131,10 +150,9 @@ class BRCNPJField(Field):
try:
int(value)
except ValueError:
- raise ValidationError("This field requires only numbers.")
+ raise ValidationError(self.error_messages['digits_only'])
if len(value) != 14:
- raise ValidationError(
- _("This field requires at least 14 digits"))
+ raise ValidationError(self.error_messages['max_digits'])
orig_dv = value[-2:]
new_1dv = sum([i * int(value[idx]) for idx, i in enumerate(range(5, 1, -1) + range(9, 1, -1))])
@@ -144,7 +162,6 @@ class BRCNPJField(Field):
new_2dv = DV_maker(new_2dv % 11)
value = value[:-1] + str(new_2dv)
if value[-2:] != orig_dv:
- raise ValidationError(_("Invalid CNPJ number."))
+ raise ValidationError(self.error_messages['invalid'])
return orig_value
-
diff --git a/django/contrib/localflavor/ca/forms.py b/django/contrib/localflavor/ca/forms.py
index 98f65a5c6c..b40dba8335 100644
--- a/django/contrib/localflavor/ca/forms.py
+++ b/django/contrib/localflavor/ca/forms.py
@@ -1,37 +1,43 @@
-"""
-Canada-specific Form helpers
-"""
-
-from django.newforms import ValidationError
-from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES
-from django.newforms.util import smart_unicode
+"""
+Canada-specific Form helpers
+"""
+
+from django.newforms import ValidationError
+from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES
+from django.newforms.util import smart_unicode
from django.utils.translation import gettext, ugettext
-import re
-
+import re
+
phone_digits_re = re.compile(r'^(?:1-?)?(\d{3})[-\.]?(\d{3})[-\.]?(\d{4})$')
sin_re = re.compile(r"^(\d{3})-(\d{3})-(\d{3})$")
-
-class CAPostalCodeField(RegexField):
- """Canadian postal code field."""
- def __init__(self, *args, **kwargs):
- super(CAPostalCodeField, self).__init__(r'^[ABCEGHJKLMNPRSTVXYZ]\d[A-Z] \d[A-Z]\d$',
- max_length=None, min_length=None,
- error_message=gettext(u'Enter a postal code in the format XXX XXX.'),
- *args, **kwargs)
-
-class CAPhoneNumberField(Field):
- """Canadian phone number field."""
- def clean(self, value):
- """Validate a phone number.
- """
- super(CAPhoneNumberField, self).clean(value)
+
+class CAPostalCodeField(RegexField):
+ """Canadian postal code field."""
+ default_error_messages = {
+ 'invalid': gettext(u'Enter a postal code in the format XXX XXX.'),
+ }
+
+ def __init__(self, *args, **kwargs):
+ super(CAPostalCodeField, self).__init__(r'^[ABCEGHJKLMNPRSTVXYZ]\d[A-Z] \d[A-Z]\d$',
+ max_length=None, min_length=None, *args, **kwargs)
+
+class CAPhoneNumberField(Field):
+ """Canadian phone number field."""
+ default_error_messages = {
+ 'invalid': u'Phone numbers must be in XXX-XXX-XXXX format.',
+ }
+
+ def clean(self, value):
+ """Validate a phone number.
+ """
+ super(CAPhoneNumberField, self).clean(value)
if value in EMPTY_VALUES:
return u''
value = re.sub('(\(|\)|\s+)', '', smart_unicode(value))
m = phone_digits_re.search(value)
if m:
return u'%s-%s-%s' % (m.group(1), m.group(2), m.group(3))
- raise ValidationError(u'Phone numbers must be in XXX-XXX-XXXX format.')
+ raise ValidationError(self.error_messages['invalid'])
class CAProvinceField(Field):
"""
@@ -39,6 +45,10 @@ class CAProvinceField(Field):
It normalizes the input to the standard two-leter postal service
abbreviation for the given province.
"""
+ default_error_messages = {
+ 'invalid': u'Enter a Canadian province or territory.',
+ }
+
def clean(self, value):
from ca_provinces import PROVINCES_NORMALIZED
super(CAProvinceField, self).clean(value)
@@ -53,45 +63,49 @@ class CAProvinceField(Field):
return PROVINCES_NORMALIZED[value.strip().lower()].decode('ascii')
except KeyError:
pass
- raise ValidationError(u'Enter a Canadian province or territory.')
-
-class CAProvinceSelect(Select):
- """
- A Select widget that uses a list of Canadian provinces and
- territories as its choices.
- """
- def __init__(self, attrs=None):
- from ca_provinces import PROVINCE_CHOICES # relative import
+ raise ValidationError(self.error_messages['invalid'])
+
+class CAProvinceSelect(Select):
+ """
+ A Select widget that uses a list of Canadian provinces and
+ territories as its choices.
+ """
+ def __init__(self, attrs=None):
+ from ca_provinces import PROVINCE_CHOICES # relative import
super(CAProvinceSelect, self).__init__(attrs, choices=PROVINCE_CHOICES)
-
+
class CASocialInsuranceNumberField(Field):
"""
A Canadian Social Insurance Number (SIN).
Checks the following rules to determine whether the number is valid:
- * Conforms to the XXX-XXX-XXXX format.
+ * Conforms to the XXX-XXX-XXX format.
* Passes the check digit process "Luhn Algorithm"
See: http://en.wikipedia.org/wiki/Social_Insurance_Number
"""
+ default_error_messages = {
+ 'invalid': ugettext('Enter a valid Canadian Social Insurance number in XXX-XXX-XXX format.'),
+ }
+
def clean(self, value):
super(CASocialInsuranceNumberField, self).clean(value)
if value in EMPTY_VALUES:
return u''
- msg = ugettext('Enter a valid Canadian Social Insurance number in XXX-XXX-XXXX format.')
+
match = re.match(sin_re, value)
if not match:
- raise ValidationError(msg)
-
- number = u'%s-%s-%s' % (match.group(1), match.group(2), match.group(3))
+ raise ValidationError(self.error_messages['invalid'])
+
+ number = u'%s-%s-%s' % (match.group(1), match.group(2), match.group(3))
check_number = u'%s%s%s' % (match.group(1), match.group(2), match.group(3))
if not self.luhn_checksum_is_valid(check_number):
- raise ValidationError(msg)
+ raise ValidationError(self.error_messages['invalid'])
return number
-
+
def luhn_checksum_is_valid(self, number):
"""
- Checks to make sure that the SIN passes a luhn mod-10 checksum
+ Checks to make sure that the SIN passes a luhn mod-10 checksum
See: http://en.wikipedia.org/wiki/Luhn_algorithm
"""
@@ -109,4 +123,4 @@ class CASocialInsuranceNumberField(Field):
sum = sum + digit
- return ( (sum % 10) == 0 ) \ No newline at end of file
+ return ( (sum % 10) == 0 )
diff --git a/django/contrib/localflavor/ch/forms.py b/django/contrib/localflavor/ch/forms.py
index 5e601f5d74..2158fba697 100644
--- a/django/contrib/localflavor/ch/forms.py
+++ b/django/contrib/localflavor/ch/forms.py
@@ -12,11 +12,13 @@ id_re = re.compile(r"^(?P<idnumber>\w{8})(?P<pos9>(\d{1}|<))(?P<checksum>\d{1})$
phone_digits_re = re.compile(r'^0([1-9]{1})\d{8}$')
class CHZipCodeField(RegexField):
+ default_error_messages = {
+ 'invalid': ugettext('Enter a zip code in the format XXXX.'),
+ }
+
def __init__(self, *args, **kwargs):
super(CHZipCodeField, self).__init__(r'^\d{4}$',
- max_length=None, min_length=None,
- error_message=ugettext('Enter a zip code in the format XXXX.'),
- *args, **kwargs)
+ max_length=None, min_length=None, *args, **kwargs)
class CHPhoneNumberField(Field):
"""
@@ -25,6 +27,10 @@ class CHPhoneNumberField(Field):
'0XX.XXX.XX.XX' and '0XXXXXXXXX' validate but are corrected to
'0XX XXX XX XX'.
"""
+ default_error_messages = {
+ 'invalid': 'Phone numbers must be in 0XX XXX XX XX format.',
+ }
+
def clean(self, value):
super(CHPhoneNumberField, self).clean(value)
if value in EMPTY_VALUES:
@@ -33,7 +39,7 @@ class CHPhoneNumberField(Field):
m = phone_digits_re.search(value)
if m:
return u'%s %s %s %s' % (value[0:3], value[3:6], value[6:8], value[8:10])
- raise ValidationError('Phone numbers must be in 0XX XXX XX XX format.')
+ raise ValidationError(self.error_messages['invalid'])
class CHStateSelect(Select):
"""
@@ -54,6 +60,10 @@ class CHIdentityCardNumberField(Field):
Algorithm is documented at http://adi.kousz.ch/artikel/IDCHE.htm
"""
+ default_error_messages = {
+ 'invalid': ugettext('Enter a valid Swiss identity or passport card number in X1234567<0 or 1234567890 format.'),
+ }
+
def has_valid_checksum(self, number):
given_number, given_checksum = number[:-1], number[-1]
new_number = given_number
@@ -87,23 +97,22 @@ class CHIdentityCardNumberField(Field):
def clean(self, value):
super(CHIdentityCardNumberField, self).clean(value)
- error_msg = ugettext('Enter a valid Swiss identity or passport card number in X1234567<0 or 1234567890 format.')
if value in EMPTY_VALUES:
return u''
match = re.match(id_re, value)
if not match:
- raise ValidationError(error_msg)
+ raise ValidationError(self.error_messages['invalid'])
idnumber, pos9, checksum = match.groupdict()['idnumber'], match.groupdict()['pos9'], match.groupdict()['checksum']
if idnumber == '00000000' or \
idnumber == 'A0000000':
- raise ValidationError(error_msg)
+ raise ValidationError(self.error_messages['invalid'])
all_digits = "%s%s%s" % (idnumber, pos9, checksum)
if not self.has_valid_checksum(all_digits):
- raise ValidationError(error_msg)
+ raise ValidationError(self.error_messages['invalid'])
return u'%s%s%s' % (idnumber, pos9, checksum)
diff --git a/django/contrib/localflavor/cl/forms.py b/django/contrib/localflavor/cl/forms.py
index d2d37a3712..2f5ac29b39 100644
--- a/django/contrib/localflavor/cl/forms.py
+++ b/django/contrib/localflavor/cl/forms.py
@@ -25,16 +25,21 @@ class CLRutField(RegexField):
Samples for testing are available from
https://palena.sii.cl/cvc/dte/ee_empresas_emisoras.html
"""
+ default_error_messages = {
+ 'invalid': ugettext('Enter a valid Chilean RUT.'),
+ 'strict': ugettext('Enter a valid Chilean RUT. The format is XX.XXX.XXX-X.'),
+ 'checksum': ugettext('The Chilean RUT is not valid.'),
+ }
+
def __init__(self, *args, **kwargs):
if 'strict' in kwargs:
del kwargs['strict']
super(CLRutField, self).__init__(r'^(\d{1,2}\.)?\d{3}\.\d{3}-[\dkK]$',
- error_message=ugettext('Enter valid a Chilean RUT. The format is XX.XXX.XXX-X.'),
- *args, **kwargs)
+ error_message=self.default_error_messages['strict'], *args, **kwargs)
else:
# In non-strict mode, accept RUTs that validate but do not exist in
# the real world.
- super(CLRutField, self).__init__(r'^[\d\.]{1,11}-?[\dkK]$', error_message=ugettext('Enter valid a Chilean RUT'), *args, **kwargs)
+ super(CLRutField, self).__init__(r'^[\d\.]{1,11}-?[\dkK]$', *args, **kwargs)
def clean(self, value):
"""
@@ -47,7 +52,7 @@ class CLRutField(RegexField):
if self._algorithm(rut) == verificador:
return self._format(rut, verificador)
else:
- raise ValidationError(u'The Chilean RUT is not valid.')
+ raise ValidationError(self.error_messages['checksum'])
def _algorithm(self, rut):
"""
diff --git a/django/contrib/localflavor/de/forms.py b/django/contrib/localflavor/de/forms.py
index 1a0b8587a6..76ce215dca 100644
--- a/django/contrib/localflavor/de/forms.py
+++ b/django/contrib/localflavor/de/forms.py
@@ -10,11 +10,12 @@ import re
id_re = re.compile(r"^(?P<residence>\d{10})(?P<origin>\w{1,3})[-\ ]?(?P<birthday>\d{7})[-\ ]?(?P<validity>\d{7})[-\ ]?(?P<checksum>\d{1})$")
class DEZipCodeField(RegexField):
+ default_error_messages = {
+ 'invalid': ugettext('Enter a zip code in the format XXXXX.'),
+ }
def __init__(self, *args, **kwargs):
super(DEZipCodeField, self).__init__(r'^\d{5}$',
- max_length=None, min_length=None,
- error_message=ugettext('Enter a zip code in the format XXXXX.'),
- *args, **kwargs)
+ max_length=None, min_length=None, *args, **kwargs)
class DEStateSelect(Select):
"""
@@ -36,6 +37,10 @@ class DEIdentityCardNumberField(Field):
Algorithm is documented at http://de.wikipedia.org/wiki/Personalausweis
"""
+ default_error_messages = {
+ 'invalid': ugettext('Enter a valid German identity card number in XXXXXXXXXXX-XXXXXXX-XXXXXXX-X format.'),
+ }
+
def has_valid_checksum(self, number):
given_number, given_checksum = number[:-1], number[-1]
calculated_checksum = 0
@@ -57,23 +62,22 @@ class DEIdentityCardNumberField(Field):
def clean(self, value):
super(DEIdentityCardNumberField, self).clean(value)
- error_msg = ugettext('Enter a valid German identity card number in XXXXXXXXXXX-XXXXXXX-XXXXXXX-X format.')
if value in EMPTY_VALUES:
return u''
match = re.match(id_re, value)
if not match:
- raise ValidationError(error_msg)
+ raise ValidationError(self.error_messages['invalid'])
gd = match.groupdict()
residence, origin = gd['residence'], gd['origin']
birthday, validity, checksum = gd['birthday'], gd['validity'], gd['checksum']
if residence == '0000000000' or birthday == '0000000' or validity == '0000000':
- raise ValidationError(error_msg)
+ raise ValidationError(self.error_messages['invalid'])
all_digits = u"%s%s%s%s" % (residence, birthday, validity, checksum)
if not self.has_valid_checksum(residence) or not self.has_valid_checksum(birthday) or \
not self.has_valid_checksum(validity) or not self.has_valid_checksum(all_digits):
- raise ValidationError(error_msg)
+ raise ValidationError(self.error_messages['invalid'])
return u'%s%s-%s-%s-%s' % (residence, origin, birthday, validity, checksum)
diff --git a/django/contrib/localflavor/es/forms.py b/django/contrib/localflavor/es/forms.py
index 29b41828f6..81186f8b3d 100644
--- a/django/contrib/localflavor/es/forms.py
+++ b/django/contrib/localflavor/es/forms.py
@@ -15,12 +15,14 @@ class ESPostalCodeField(RegexField):
Spanish postal code is a five digits string, with two first digits
between 01 and 52, assigned to provinces code.
"""
+ default_error_messages = {
+ 'invalid': _('Enter a valid postal code in the range and format 01XXX - 52XXX.'),
+ }
+
def __init__(self, *args, **kwargs):
super(ESPostalCodeField, self).__init__(
r'^(0[1-9]|[1-4][0-9]|5[0-2])\d{3}$',
- max_length=None, min_length=None,
- error_message=_('Enter a valid postal code in the range and format 01XXX - 52XXX.'),
- *args, **kwargs)
+ max_length=None, min_length=None, *args, **kwargs)
class ESPhoneNumberField(RegexField):
"""
@@ -33,11 +35,13 @@ class ESPhoneNumberField(RegexField):
TODO: accept and strip characters like dot, hyphen... in phone number
"""
+ default_error_messages = {
+ 'invalid': _('Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.'),
+ }
+
def __init__(self, *args, **kwargs):
super(ESPhoneNumberField, self).__init__(r'^(6|8|9)\d{8}$',
- max_length=None, min_length=None,
- error_message=_('Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.'),
- *args, **kwargs)
+ max_length=None, min_length=None, *args, **kwargs)
class ESIdentityCardNumberField(RegexField):
"""
@@ -58,19 +62,23 @@ class ESIdentityCardNumberField(RegexField):
public, and different authors have different opinions on which ones allows
letters, so both validations are assumed true for all types.
"""
+ default_error_messages = {
+ 'invalid': _('Please enter a valid NIF, NIE, or CIF.'),
+ 'invalid_only_nif': _('Please enter a valid NIF or NIE.'),
+ 'invalid_nif': _('Invalid checksum for NIF.'),
+ 'invalid_nie': _('Invalid checksum for NIE.'),
+ 'invalid_cif': _('Invalid checksum for CIF.'),
+ }
+
def __init__(self, only_nif=False, *args, **kwargs):
self.only_nif = only_nif
self.nif_control = 'TRWAGMYFPDXBNJZSQVHLCKE'
self.cif_control = 'JABCDEFGHI'
self.cif_types = 'ABCDEFGHKLMNPQS'
self.nie_types = 'XT'
- if self.only_nif:
- self.id_types = 'NIF or NIE'
- else:
- self.id_types = 'NIF, NIE, or CIF'
super(ESIdentityCardNumberField, self).__init__(r'^([%s]?)[ -]?(\d+)[ -]?([%s]?)$' % (self.cif_types + self.nie_types + self.cif_types.lower() + self.nie_types.lower(), self.nif_control + self.nif_control.lower()),
max_length=None, min_length=None,
- error_message=_('Please enter a valid %s.' % self.id_types),
+ error_message=self.default_error_messages['invalid%s' % (self.only_nif and '_only_nif' or '')],
*args, **kwargs)
def clean(self, value):
@@ -88,13 +96,13 @@ class ESIdentityCardNumberField(RegexField):
if letter2 == nif_get_checksum(number):
return value
else:
- raise ValidationError, _('Invalid checksum for NIF.')
+ raise ValidationError, self.error_messages['invalid_nif']
elif letter1 in self.nie_types and letter2:
# NIE
if letter2 == nif_get_checksum(number):
return value
else:
- raise ValidationError, _('Invalid checksum for NIE.')
+ raise ValidationError, self.error_messages['invalid_nie']
elif not self.only_nif and letter1 in self.cif_types and len(number) in [7, 8]:
# CIF
if not letter2:
@@ -103,9 +111,9 @@ class ESIdentityCardNumberField(RegexField):
if letter2 in [checksum, self.cif_control[checksum]]:
return value
else:
- raise ValidationError, _('Invalid checksum for CIF.')
+ raise ValidationError, self.error_messages['invalid_cif']
else:
- raise ValidationError, _('Please enter a valid %s.' % self.id_types)
+ raise ValidationError, self.error_messages['invalid']
class ESCCCField(RegexField):
"""
@@ -130,11 +138,14 @@ class ESCCCField(RegexField):
TODO: allow IBAN validation too
"""
+ default_error_messages = {
+ 'invalid': _('Please enter a valid bank account number in format XXXX-XXXX-XX-XXXXXXXXXX.'),
+ 'checksum': _('Invalid checksum for bank account number.'),
+ }
+
def __init__(self, *args, **kwargs):
super(ESCCCField, self).__init__(r'^\d{4}[ -]?\d{4}[ -]?\d{2}[ -]?\d{10}$',
- max_length=None, min_length=None,
- error_message=_('Please enter a valid bank account number in format XXXX-XXXX-XX-XXXXXXXXXX.'),
- *args, **kwargs)
+ max_length=None, min_length=None, *args, **kwargs)
def clean(self, value):
super(ESCCCField, self).clean(value)
@@ -147,7 +158,7 @@ class ESCCCField(RegexField):
if get_checksum('00' + entity + office) + get_checksum(account) == checksum:
return value
else:
- raise ValidationError, _('Invalid checksum for bank account number.')
+ raise ValidationError, self.error_messages['checksum']
class ESRegionSelect(Select):
"""
diff --git a/django/contrib/localflavor/fi/forms.py b/django/contrib/localflavor/fi/forms.py
index 82e024f7b1..a0274da44c 100644
--- a/django/contrib/localflavor/fi/forms.py
+++ b/django/contrib/localflavor/fi/forms.py
@@ -8,11 +8,12 @@ from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES
from django.utils.translation import ugettext
class FIZipCodeField(RegexField):
+ default_error_messages = {
+ 'invalid': ugettext('Enter a zip code in the format XXXXX.'),
+ }
def __init__(self, *args, **kwargs):
super(FIZipCodeField, self).__init__(r'^\d{5}$',
- max_length=None, min_length=None,
- error_message=ugettext('Enter a zip code in the format XXXXX.'),
- *args, **kwargs)
+ max_length=None, min_length=None, *args, **kwargs)
class FIMunicipalitySelect(Select):
"""
@@ -23,6 +24,10 @@ class FIMunicipalitySelect(Select):
super(FIMunicipalitySelect, self).__init__(attrs, choices=MUNICIPALITY_CHOICES)
class FISocialSecurityNumber(Field):
+ default_error_messages = {
+ 'invalid': ugettext('Enter a valid Finnish social security number.'),
+ }
+
def clean(self, value):
super(FISocialSecurityNumber, self).clean(value)
if value in EMPTY_VALUES:
@@ -37,9 +42,9 @@ class FISocialSecurityNumber(Field):
(?P<serial>(\d{3}))
(?P<checksum>[%s])$""" % checkmarks, value, re.VERBOSE | re.IGNORECASE)
if not result:
- raise ValidationError(ugettext('Enter a valid Finnish social security number.'))
+ raise ValidationError(self.error_messages['invalid'])
gd = result.groupdict()
checksum = int(gd['date'] + gd['serial'])
if checkmarks[checksum % len(checkmarks)] == gd['checksum'].upper():
return u'%s' % value.upper()
- raise ValidationError(ugettext('Enter a valid Finnish social security number.'))
+ raise ValidationError(self.error_messages['invalid'])
diff --git a/django/contrib/localflavor/fr/forms.py b/django/contrib/localflavor/fr/forms.py
index cc0a5db259..635b62edd5 100644
--- a/django/contrib/localflavor/fr/forms.py
+++ b/django/contrib/localflavor/fr/forms.py
@@ -11,11 +11,13 @@ import re
phone_digits_re = re.compile(r'^0\d(\s|\.)?(\d{2}(\s|\.)?){3}\d{2}$')
class FRZipCodeField(RegexField):
+ default_error_messages = {
+ 'invalid': ugettext('Enter a zip code in the format XXXXX.'),
+ }
+
def __init__(self, *args, **kwargs):
super(FRZipCodeField, self).__init__(r'^\d{5}$',
- max_length=None, min_length=None,
- error_message=ugettext('Enter a zip code in the format XXXXX.'),
- *args, **kwargs)
+ max_length=None, min_length=None, *args, **kwargs)
class FRPhoneNumberField(Field):
"""
@@ -24,6 +26,10 @@ class FRPhoneNumberField(Field):
'0X.XX.XX.XX.XX' and '0XXXXXXXXX' validate but are corrected to
'0X XX XX XX XX'.
"""
+ default_error_messages = {
+ 'invalid': u'Phone numbers must be in 0X XX XX XX XX format.',
+ }
+
def clean(self, value):
super(FRPhoneNumberField, self).clean(value)
if value in EMPTY_VALUES:
@@ -32,7 +38,7 @@ class FRPhoneNumberField(Field):
m = phone_digits_re.search(value)
if m:
return u'%s %s %s %s %s' % (value[0:2], value[2:4], value[4:6], value[6:8], value[8:10])
- raise ValidationError(u'Phone numbers must be in 0X XX XX XX XX format.')
+ raise ValidationError(self.error_messages['invalid'])
class FRDepartmentSelect(Select):
"""
diff --git a/django/contrib/localflavor/in_/forms.py b/django/contrib/localflavor/in_/forms.py
index 5febc40e67..1cb303d772 100644
--- a/django/contrib/localflavor/in_/forms.py
+++ b/django/contrib/localflavor/in_/forms.py
@@ -10,11 +10,13 @@ import re
class INZipCodeField(RegexField):
+ default_error_messages = {
+ 'invalid': gettext(u'Enter a zip code in the format XXXXXXX.'),
+ }
+
def __init__(self, *args, **kwargs):
super(INZipCodeField, self).__init__(r'^\d{6}$',
- max_length=None, min_length=None,
- error_message=gettext(u'Enter a zip code in the format XXXXXXX.'),
- *args, **kwargs)
+ max_length=None, min_length=None, *args, **kwargs)
class INStateField(Field):
"""
@@ -22,6 +24,10 @@ class INStateField(Field):
abbreviation. It normalizes the input to the standard two-letter vehicle
registration abbreviation for the given state or union territory
"""
+ default_error_messages = {
+ 'invalid': u'Enter a Indian state or territory.',
+ }
+
def clean(self, value):
from in_states import STATES_NORMALIZED
super(INStateField, self).clean(value)
@@ -36,7 +42,7 @@ class INStateField(Field):
return smart_unicode(STATES_NORMALIZED[value.strip().lower()])
except KeyError:
pass
- raise ValidationError(u'Enter a Indian state or territory.')
+ raise ValidationError(self.error_messages['invalid'])
class INStateSelect(Select):
"""
diff --git a/django/contrib/localflavor/is_/forms.py b/django/contrib/localflavor/is_/forms.py
index ef8799e29c..ea7a1e00c3 100644
--- a/django/contrib/localflavor/is_/forms.py
+++ b/django/contrib/localflavor/is_/forms.py
@@ -13,10 +13,14 @@ class ISIdNumberField(RegexField):
Icelandic identification number (kennitala). This is a number every citizen
of Iceland has.
"""
+ default_error_messages = {
+ 'invalid': ugettext('Enter a valid Icelandic identification number. The format is XXXXXX-XXXX.'),
+ 'checksum': ugettext(u'The Icelandic identification number is not valid.'),
+ }
+
def __init__(self, *args, **kwargs):
- error_msg = ugettext('Enter a valid Icelandic identification number. The format is XXXXXX-XXXX.')
kwargs['min_length'],kwargs['max_length'] = 10,11
- super(ISIdNumberField, self).__init__(r'^\d{6}(-| )?\d{4}$', error_message=error_msg, *args, **kwargs)
+ super(ISIdNumberField, self).__init__(r'^\d{6}(-| )?\d{4}$', *args, **kwargs)
def clean(self, value):
value = super(ISIdNumberField, self).clean(value)
@@ -28,7 +32,7 @@ class ISIdNumberField(RegexField):
if self._validate(value):
return self._format(value)
else:
- raise ValidationError(ugettext(u'The Icelandic identification number is not valid.'))
+ raise ValidationError(self.error_messages['checksum'])
def _canonify(self, value):
"""
diff --git a/django/contrib/localflavor/it/forms.py b/django/contrib/localflavor/it/forms.py
index 0b6eecec45..88e65734e4 100644
--- a/django/contrib/localflavor/it/forms.py
+++ b/django/contrib/localflavor/it/forms.py
@@ -10,11 +10,12 @@ from django.contrib.localflavor.it.util import ssn_check_digit, vat_number_check
import re
class ITZipCodeField(RegexField):
+ default_error_messages = {
+ 'invalid': ugettext('Enter a valid zip code.'),
+ }
def __init__(self, *args, **kwargs):
super(ITZipCodeField, self).__init__(r'^\d{5}$',
- max_length=None, min_length=None,
- error_message=ugettext('Enter a valid zip code.'),
- *args, **kwargs)
+ max_length=None, min_length=None, *args, **kwargs)
class ITRegionSelect(Select):
"""
@@ -38,11 +39,13 @@ class ITSocialSecurityNumberField(RegexField):
For reference see http://www.agenziaentrate.it/ and search for
'Informazioni sulla codificazione delle persone fisiche'.
"""
- err_msg = ugettext(u'Enter a valid Social Security number.')
+ default_error_messages = {
+ 'invalid': ugettext(u'Enter a valid Social Security number.'),
+ }
+
def __init__(self, *args, **kwargs):
super(ITSocialSecurityNumberField, self).__init__(r'^\w{3}\s*\w{3}\s*\w{5}\s*\w{5}$',
- max_length=None, min_length=None, error_message=self.err_msg,
- *args, **kwargs)
+ max_length=None, min_length=None, *args, **kwargs)
def clean(self, value):
value = super(ITSocialSecurityNumberField, self).clean(value)
@@ -52,26 +55,29 @@ class ITSocialSecurityNumberField(RegexField):
try:
check_digit = ssn_check_digit(value)
except ValueError:
- raise ValidationError(self.err_msg)
+ raise ValidationError(self.error_messages['invalid'])
if not value[15] == check_digit:
- raise ValidationError(self.err_msg)
+ raise ValidationError(self.error_messages['invalid'])
return value
class ITVatNumberField(Field):
"""
A form field that validates Italian VAT numbers (partita IVA).
"""
+ default_error_messages = {
+ 'invalid': ugettext(u'Enter a valid VAT number.'),
+ }
+
def clean(self, value):
value = super(ITVatNumberField, self).clean(value)
if value == u'':
return value
- err_msg = ugettext(u'Enter a valid VAT number.')
try:
vat_number = int(value)
except ValueError:
- raise ValidationError(err_msg)
+ raise ValidationError(self.error_messages['invalid'])
vat_number = str(vat_number).zfill(11)
check_digit = vat_number_check_digit(vat_number[0:10])
if not vat_number[10] == check_digit:
- raise ValidationError(err_msg)
+ raise ValidationError(self.error_messages['invalid'])
return smart_unicode(vat_number)
diff --git a/django/contrib/localflavor/jp/forms.py b/django/contrib/localflavor/jp/forms.py
index 682ffb8c31..13083aab6e 100644
--- a/django/contrib/localflavor/jp/forms.py
+++ b/django/contrib/localflavor/jp/forms.py
@@ -15,11 +15,13 @@ class JPPostalCodeField(RegexField):
Accepts 7 digits, with or without a hyphen.
"""
+ default_error_messages = {
+ 'invalid': ugettext('Enter a postal code in the format XXXXXXX or XXX-XXXX.'),
+ }
+
def __init__(self, *args, **kwargs):
super(JPPostalCodeField, self).__init__(r'^\d{3}-\d{4}$|^\d{7}$',
- max_length=None, min_length=None,
- error_message=ugettext('Enter a postal code in the format XXXXXXX or XXX-XXXX.'),
- *args, **kwargs)
+ max_length=None, min_length=None, *args, **kwargs)
def clean(self, value):
"""
diff --git a/django/contrib/localflavor/nl/forms.py b/django/contrib/localflavor/nl/forms.py
index e202ab0595..748484926e 100644
--- a/django/contrib/localflavor/nl/forms.py
+++ b/django/contrib/localflavor/nl/forms.py
@@ -17,24 +17,27 @@ class NLZipCodeField(Field):
"""
A Dutch postal code field.
"""
+ default_error_messages = {
+ 'invalid': _('Enter a valid postal code'),
+ }
+
def clean(self, value):
super(NLZipCodeField, self).clean(value)
if value in EMPTY_VALUES:
return u''
-
- msg = _('Enter a valid postal code')
+
value = value.strip().upper().replace(' ', '')
if not pc_re.search(value):
- raise ValidationError(msg)
-
+ raise ValidationError(self.error_messages['invalid'])
+
if int(value[:4]) < 1000:
- raise ValidationError(msg)
-
+ raise ValidationError(self.error_messages['invalid'])
+
return u'%s %s' % (value[:4], value[4:])
class NLProvinceSelect(Select):
"""
- A Select widget that uses a list of provinces of the Netherlands as its
+ A Select widget that uses a list of provinces of the Netherlands as its
choices.
"""
def __init__(self, attrs=None):
@@ -45,48 +48,53 @@ class NLPhoneNumberField(Field):
"""
A Dutch telephone number field.
"""
+ default_error_messages = {
+ 'invalid': _('Enter a valid phone number'),
+ }
+
def clean(self, value):
super(NLPhoneNumberField, self).clean(value)
if value in EMPTY_VALUES:
return u''
-
- msg = _('Enter a valid phone number')
+
phone_nr = re.sub('[\-\s\(\)]', '', smart_unicode(value))
-
+
if len(phone_nr) == 10 and numeric_re.search(phone_nr):
return value
-
+
if phone_nr[:3] == '+31' and len(phone_nr) == 12 and \
numeric_re.search(phone_nr[3:]):
return value
-
- raise ValidationError(msg)
+
+ raise ValidationError(self.error_messages['invalid'])
class NLSoFiNumberField(Field):
"""
A Dutch social security number (SoFi/BSN) field.
-
+
http://nl.wikipedia.org/wiki/Sofinummer
"""
+ default_error_messages = {
+ 'invalid': _('Enter a valid SoFi number'),
+ }
+
def clean(self, value):
super(NLSoFiNumberField, self).clean(value)
if value in EMPTY_VALUES:
return u''
-
- msg = _('Enter a valid SoFi number')
-
+
if not sofi_re.search(value):
- raise ValidationError(msg)
-
+ raise ValidationError(self.error_messages['invalid'])
+
if int(value) == 0:
- raise ValidationError(msg)
-
+ raise ValidationError(self.error_messages['invalid'])
+
checksum = 0
for i in range(9, 1, -1):
checksum += int(value[9-i]) * i
checksum -= int(value[-1])
-
+
if checksum % 11 != 0:
- raise ValidationError(msg)
-
+ raise ValidationError(self.error_messages['invalid'])
+
return value
diff --git a/django/contrib/localflavor/no/forms.py b/django/contrib/localflavor/no/forms.py
index 2de3df14d8..8dc001955d 100644
--- a/django/contrib/localflavor/no/forms.py
+++ b/django/contrib/localflavor/no/forms.py
@@ -8,11 +8,13 @@ from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES
from django.utils.translation import ugettext
class NOZipCodeField(RegexField):
+ default_error_messages = {
+ 'invalid': ugettext('Enter a zip code in the format XXXX.'),
+ }
+
def __init__(self, *args, **kwargs):
super(NOZipCodeField, self).__init__(r'^\d{4}$',
- max_length=None, min_length=None,
- error_message=ugettext('Enter a zip code in the format XXXX.'),
- *args, **kwargs)
+ max_length=None, min_length=None, *args, **kwargs)
class NOMunicipalitySelect(Select):
"""
@@ -27,14 +29,17 @@ class NOSocialSecurityNumber(Field):
"""
Algorithm is documented at http://no.wikipedia.org/wiki/Personnummer
"""
+ default_error_messages = {
+ 'invalid': ugettext(u'Enter a valid Norwegian social security number.'),
+ }
+
def clean(self, value):
super(NOSocialSecurityNumber, self).clean(value)
if value in EMPTY_VALUES:
return u''
- msg = ugettext(u'Enter a valid Norwegian social security number.')
if not re.match(r'^\d{11}$', value):
- raise ValidationError(msg)
+ raise ValidationError(self.error_messages['invalid'])
day = int(value[:2])
month = int(value[2:4])
@@ -52,7 +57,7 @@ class NOSocialSecurityNumber(Field):
if 900 <= inum < 1000 and year2 > 39:
self.birthday = datetime.date(1900+year2, month, day)
except ValueError:
- raise ValidationError(msg)
+ raise ValidationError(self.error_messages['invalid'])
sexnum = int(value[8])
if sexnum % 2 == 0:
@@ -68,9 +73,9 @@ class NOSocialSecurityNumber(Field):
return sum([(a * b) for (a, b) in zip(aval, bval)])
if multiply_reduce(digits, weight_1) % 11 != 0:
- raise ValidationError(msg)
+ raise ValidationError(self.error_messages['invalid'])
if multiply_reduce(digits, weight_2) % 11 != 0:
- raise ValidationError(msg)
+ raise ValidationError(self.error_messages['invalid'])
return value
diff --git a/django/contrib/localflavor/pe/forms.py b/django/contrib/localflavor/pe/forms.py
index b1ae215417..57c9d441f7 100644
--- a/django/contrib/localflavor/pe/forms.py
+++ b/django/contrib/localflavor/pe/forms.py
@@ -19,6 +19,11 @@ class PEDNIField(CharField):
"""
A field that validates `Documento Nacional de IdentidadŽ (DNI) numbers.
"""
+ default_error_messages = {
+ 'invalid': ugettext("This field requires only numbers."),
+ 'max_digits': ugettext("This field requires 8 digits."),
+ }
+
def __init__(self, *args, **kwargs):
super(PEDNIField, self).__init__(max_length=8, min_length=8, *args,
**kwargs)
@@ -31,9 +36,9 @@ class PEDNIField(CharField):
if value in EMPTY_VALUES:
return u''
if not value.isdigit():
- raise ValidationError(ugettext("This field requires only numbers."))
+ raise ValidationError(self.error_messages['invalid'])
if len(value) != 8:
- raise ValidationError(ugettext("This field requires 8 digits."))
+ raise ValidationError(self.error_messages['max_digits'])
return value
@@ -42,6 +47,11 @@ class PERUCField(RegexField):
This field validates a RUC (Registro Unico de Contribuyentes). A RUC is of
the form XXXXXXXXXXX.
"""
+ default_error_messages = {
+ 'invalid': ugettext("This field requires only numbers."),
+ 'max_digits': ugettext("This field requires 11 digits."),
+ }
+
def __init__(self, *args, **kwargs):
super(PERUCField, self).__init__(max_length=11, min_length=11, *args,
**kwargs)
@@ -54,8 +64,8 @@ class PERUCField(RegexField):
if value in EMPTY_VALUES:
return u''
if not value.isdigit():
- raise ValidationError(ugettext("This field requires only numbers."))
+ raise ValidationError(self.error_messages['invalid'])
if len(value) != 11:
- raise ValidationError(ugettext("This field requires 11 digits."))
+ raise ValidationError(self.error_messages['max_digits'])
return value
diff --git a/django/contrib/localflavor/pl/forms.py b/django/contrib/localflavor/pl/forms.py
index 3d1c4234d3..b02dad7025 100644
--- a/django/contrib/localflavor/pl/forms.py
+++ b/django/contrib/localflavor/pl/forms.py
@@ -35,16 +35,19 @@ class PLNationalIdentificationNumberField(RegexField):
The algorithm is documented at http://en.wikipedia.org/wiki/PESEL.
"""
+ default_error_messages = {
+ 'invalid': _(u'National Identification Number consists of 11 digits.'),
+ 'checksum': _(u'Wrong checksum for the National Identification Number.'),
+ }
def __init__(self, *args, **kwargs):
super(PLNationalIdentificationNumberField, self).__init__(r'^\d{11}$',
- max_length=None, min_length=None, error_message=_(u'National Identification Number consists of 11 digits.'),
- *args, **kwargs)
+ max_length=None, min_length=None, *args, **kwargs)
def clean(self,value):
super(PLNationalIdentificationNumberField, self).clean(value)
if not self.has_valid_checksum(value):
- raise ValidationError(_(u'Wrong checksum for the National Identification Number.'))
+ raise ValidationError(self.error_messages['checksum'])
return u'%s' % value
def has_valid_checksum(self, number):
@@ -65,17 +68,20 @@ class PLTaxNumberField(RegexField):
Checksum algorithm based on documentation at
http://wipos.p.lodz.pl/zylla/ut/nip-rego.html
"""
+ default_error_messages = {
+ 'invalid': _(u'Enter a tax number field (NIP) in the format XXX-XXX-XX-XX or XX-XX-XXX-XXX.'),
+ 'checksum': _(u'Wrong checksum for the Tax Number (NIP).'),
+ }
def __init__(self, *args, **kwargs):
super(PLTaxNumberField, self).__init__(r'^\d{3}-\d{3}-\d{2}-\d{2}$|^\d{2}-\d{2}-\d{3}-\d{3}$',
- max_length=None, min_length=None,
- error_message=_(u'Enter a tax number field (NIP) in the format XXX-XXX-XX-XX or XX-XX-XXX-XXX.'), *args, **kwargs)
+ max_length=None, min_length=None, *args, **kwargs)
def clean(self,value):
super(PLTaxNumberField, self).clean(value)
value = re.sub("[-]", "", value)
if not self.has_valid_checksum(value):
- raise ValidationError(_(u'Wrong checksum for the Tax Number (NIP).'))
+ raise ValidationError(self.error_messages['checksum'])
return u'%s' % value
def has_valid_checksum(self, number):
@@ -102,15 +108,19 @@ class PLNationalBusinessRegisterField(RegexField):
The checksum algorithm is documented at http://wipos.p.lodz.pl/zylla/ut/nip-rego.html
"""
+ default_error_messages = {
+ 'invalid': _(u'National Business Register Number (REGON) consists of 7 or 9 digits.'),
+ 'checksum': _(u'Wrong checksum for the National Business Register Number (REGON).'),
+ }
+
def __init__(self, *args, **kwargs):
super(PLNationalBusinessRegisterField, self).__init__(r'^\d{7,9}$',
- max_length=None, min_length=None, error_message=_(u'National Business Register Number (REGON) consists of 7 or 9 digits.'),
- *args, **kwargs)
+ max_length=None, min_length=None, *args, **kwargs)
def clean(self,value):
super(PLNationalBusinessRegisterField, self).clean(value)
if not self.has_valid_checksum(value):
- raise ValidationError(_(u'Wrong checksum for the National Business Register Number (REGON).'))
+ raise ValidationError(self.error_messages['checksum'])
return u'%s' % value
def has_valid_checksum(self, number):
@@ -142,9 +152,10 @@ class PLPostalCodeField(RegexField):
A form field that validates as Polish postal code.
Valid code is XX-XXX where X is digit.
"""
+ default_error_messages = {
+ 'invalid': _(u'Enter a postal code in the format XX-XXX.'),
+ }
+
def __init__(self, *args, **kwargs):
super(PLPostalCodeField, self).__init__(r'^\d{2}-\d{3}$',
- max_length=None, min_length=None,
- error_message=_(u'Enter a postal code in the format XX-XXX.'),
- *args, **kwargs)
-
+ max_length=None, min_length=None, *args, **kwargs)
diff --git a/django/contrib/localflavor/sk/forms.py b/django/contrib/localflavor/sk/forms.py
index 0aa000a0a5..711d70fc2b 100644
--- a/django/contrib/localflavor/sk/forms.py
+++ b/django/contrib/localflavor/sk/forms.py
@@ -26,11 +26,13 @@ class SKPostalCodeField(RegexField):
A form field that validates its input as Slovak postal code.
Valid form is XXXXX or XXX XX, where X represents integer.
"""
+ default_error_messages = {
+ 'invalid': ugettext(u'Enter a postal code in the format XXXXX or XXX XX.'),
+ }
+
def __init__(self, *args, **kwargs):
super(SKPostalCodeField, self).__init__(r'^\d{5}$|^\d{3} \d{2}$',
- max_length=None, min_length=None,
- error_message=ugettext(u'Enter a postal code in the format XXXXX or XXX XX.'),
- *args, **kwargs)
+ max_length=None, min_length=None, *args, **kwargs)
def clean(self, value):
"""
diff --git a/django/contrib/localflavor/uk/forms.py b/django/contrib/localflavor/uk/forms.py
index 2b162230de..52cd7ad232 100644
--- a/django/contrib/localflavor/uk/forms.py
+++ b/django/contrib/localflavor/uk/forms.py
@@ -2,21 +2,39 @@
UK-specific Form helpers
"""
-from django.newforms.fields import RegexField, Select
+import re
+
+from django.newforms.fields import CharField, Select
+from django.newforms import ValidationError
from django.utils.translation import ugettext
-class UKPostcodeField(RegexField):
+class UKPostcodeField(CharField):
"""
A form field that validates its input is a UK postcode.
The regular expression used is sourced from the schema for British Standard
BS7666 address types: http://www.govtalk.gov.uk/gdsc/schemas/bs7666-v2-0.xsd
+
+ The value is uppercased and a space added in the correct place, if required.
"""
- def __init__(self, *args, **kwargs):
- super(UKPostcodeField, self).__init__(r'^(GIR 0AA|[A-PR-UWYZ]([0-9]{1,2}|([A-HIK-Y][0-9](|[0-9]|[ABEHMNPRVWXY]))|[0-9][A-HJKSTUW]) [0-9][ABD-HJLNP-UW-Z]{2})$',
- max_length=None, min_length=None,
- error_message=ugettext(u'Enter a postcode. A space is required between the two postcode parts.'),
- *args, **kwargs)
+ default_error_messages = {
+ 'invalid': ugettext(u'Enter a valid postcode.'),
+ }
+ outcode_pattern = '[A-PR-UWYZ]([0-9]{1,2}|([A-HIK-Y][0-9](|[0-9]|[ABEHMNPRVWXY]))|[0-9][A-HJKSTUW])'
+ incode_pattern = '[0-9][ABD-HJLNP-UW-Z]{2}'
+ postcode_regex = re.compile(r'^(GIR 0AA|%s %s)$' % (outcode_pattern, incode_pattern))
+ space_regex = re.compile(r' *(%s)$' % incode_pattern)
+
+ def clean(self, value):
+ value = super(UKPostcodeField, self).clean(value)
+ if value == u'':
+ return value
+ postcode = value.upper().strip()
+ # Put a single space before the incode (second part).
+ postcode = self.space_regex.sub(r' \1', postcode)
+ if not self.postcode_regex.search(postcode):
+ raise ValidationError(self.default_error_messages['invalid'])
+ return postcode
class UKCountySelect(Select):
"""
diff --git a/django/contrib/localflavor/us/forms.py b/django/contrib/localflavor/us/forms.py
index 259a7f7058..d6aa684cba 100644
--- a/django/contrib/localflavor/us/forms.py
+++ b/django/contrib/localflavor/us/forms.py
@@ -12,13 +12,19 @@ phone_digits_re = re.compile(r'^(?:1-?)?(\d{3})[-\.]?(\d{3})[-\.]?(\d{4})$')
ssn_re = re.compile(r"^(?P<area>\d{3})[-\ ]?(?P<group>\d{2})[-\ ]?(?P<serial>\d{4})$")
class USZipCodeField(RegexField):
+ default_error_messages = {
+ 'invalid': ugettext('Enter a zip code in the format XXXXX or XXXXX-XXXX.'),
+ }
+
def __init__(self, *args, **kwargs):
super(USZipCodeField, self).__init__(r'^\d{5}(?:-\d{4})?$',
- max_length=None, min_length=None,
- error_message=ugettext('Enter a zip code in the format XXXXX or XXXXX-XXXX.'),
- *args, **kwargs)
+ max_length=None, min_length=None, *args, **kwargs)
class USPhoneNumberField(Field):
+ default_error_messages = {
+ 'invalid': u'Phone numbers must be in XXX-XXX-XXXX format.',
+ }
+
def clean(self, value):
super(USPhoneNumberField, self).clean(value)
if value in EMPTY_VALUES:
@@ -27,7 +33,7 @@ class USPhoneNumberField(Field):
m = phone_digits_re.search(value)
if m:
return u'%s-%s-%s' % (m.group(1), m.group(2), m.group(3))
- raise ValidationError(u'Phone numbers must be in XXX-XXX-XXXX format.')
+ raise ValidationError(self.error_messages['invalid'])
class USSocialSecurityNumberField(Field):
"""
@@ -44,28 +50,31 @@ class USSocialSecurityNumberField(Field):
promotional use or distribution (e.g., the Woolworth's number or the
1962 promotional number).
"""
+ default_error_messages = {
+ 'invalid': ugettext('Enter a valid U.S. Social Security number in XXX-XX-XXXX format.'),
+ }
+
def clean(self, value):
super(USSocialSecurityNumberField, self).clean(value)
if value in EMPTY_VALUES:
return u''
- msg = ugettext('Enter a valid U.S. Social Security number in XXX-XX-XXXX format.')
match = re.match(ssn_re, value)
if not match:
- raise ValidationError(msg)
+ raise ValidationError(self.error_messages['invalid'])
area, group, serial = match.groupdict()['area'], match.groupdict()['group'], match.groupdict()['serial']
# First pass: no blocks of all zeroes.
if area == '000' or \
group == '00' or \
serial == '0000':
- raise ValidationError(msg)
+ raise ValidationError(self.error_messages['invalid'])
# Second pass: promotional and otherwise permanently invalid numbers.
if area == '666' or \
(area == '987' and group == '65' and 4320 <= int(serial) <= 4329) or \
value == '078-05-1120' or \
value == '219-09-9999':
- raise ValidationError(msg)
+ raise ValidationError(self.error_messages['invalid'])
return u'%s-%s-%s' % (area, group, serial)
class USStateField(Field):
@@ -74,6 +83,10 @@ class USStateField(Field):
It normalizes the input to the standard two-leter postal service
abbreviation for the given state.
"""
+ default_error_messages = {
+ 'invalid': u'Enter a U.S. state or territory.',
+ }
+
def clean(self, value):
from us_states import STATES_NORMALIZED
super(USStateField, self).clean(value)
@@ -88,7 +101,7 @@ class USStateField(Field):
return STATES_NORMALIZED[value.strip().lower()].decode('ascii')
except KeyError:
pass
- raise ValidationError(u'Enter a U.S. state or territory.')
+ raise ValidationError(self.error_messages['invalid'])
class USStateSelect(Select):
"""
diff --git a/django/contrib/localflavor/za/forms.py b/django/contrib/localflavor/za/forms.py
index b04b7cf6ab..b3613b507d 100644
--- a/django/contrib/localflavor/za/forms.py
+++ b/django/contrib/localflavor/za/forms.py
@@ -16,10 +16,9 @@ class ZAIDField(Field):
using the Luhn checksum, and uses a simlistic (read: not entirely accurate)
check for the birthdate
"""
-
- def __init__(self, *args, **kwargs):
- super(ZAIDField, self).__init__()
- self.error_message = _(u'Enter a valid South African ID number')
+ default_error_messages = {
+ 'invalid': _(u'Enter a valid South African ID number'),
+ }
def clean(self, value):
# strip spaces and dashes
@@ -31,9 +30,9 @@ class ZAIDField(Field):
return u''
match = re.match(id_re, value)
-
+
if not match:
- raise ValidationError(self.error_message)
+ raise ValidationError(self.error_messages['invalid'])
g = match.groupdict()
@@ -43,15 +42,18 @@ class ZAIDField(Field):
# There is no way to guess the century of a ZA ID number
d = date(int(g['yy']) + 2000, int(g['mm']), int(g['dd']))
except ValueError:
- raise ValidationError(self.error_message)
+ raise ValidationError(self.error_messages['invalid'])
if not luhn(value):
- raise ValidationError(self.error_message)
+ raise ValidationError(self.error_messages['invalid'])
return value
class ZAPostCodeField(RegexField):
+ default_error_messages = {
+ 'invalid': _(u'Enter a valid South African postal code'),
+ }
+
def __init__(self, *args, **kwargs):
super(ZAPostCodeField, self).__init__(r'^\d{4}$',
- max_length=None, min_length=None,
- error_message=_(u'Enter a valid South African postal code'))
+ max_length=None, min_length=None)
diff --git a/django/core/mail.py b/django/core/mail.py
index 77ab8007e6..153dcb6e63 100644
--- a/django/core/mail.py
+++ b/django/core/mail.py
@@ -67,42 +67,32 @@ def make_msgid(idstring=None):
class BadHeaderError(ValueError):
pass
+def forbid_multi_line_headers(name, val):
+ "Forbids multi-line headers, to prevent header injection."
+ if '\n' in val or '\r' in val:
+ raise BadHeaderError("Header values can't contain newlines (got %r for header %r)" % (val, name))
+ try:
+ val = force_unicode(val).encode('ascii')
+ except UnicodeEncodeError:
+ if name.lower() in ('to', 'from', 'cc'):
+ result = []
+ for item in val.split(', '):
+ nm, addr = parseaddr(item)
+ nm = str(Header(nm, settings.DEFAULT_CHARSET))
+ result.append(formataddr((nm, str(addr))))
+ val = ', '.join(result)
+ else:
+ val = Header(force_unicode(val), settings.DEFAULT_CHARSET)
+ return name, val
+
class SafeMIMEText(MIMEText):
def __setitem__(self, name, val):
- "Forbids multi-line headers, to prevent header injection."
- if '\n' in val or '\r' in val:
- raise BadHeaderError, "Header values can't contain newlines (got %r for header %r)" % (val, name)
- try:
- val = force_unicode(val).encode('ascii')
- except UnicodeEncodeError:
- if name.lower() in ('to', 'from', 'cc'):
- result = []
- for item in val.split(', '):
- nm, addr = parseaddr(item)
- nm = str(Header(nm, settings.DEFAULT_CHARSET))
- result.append(formataddr((nm, str(addr))))
- val = ', '.join(result)
- else:
- val = Header(force_unicode(val), settings.DEFAULT_CHARSET)
+ name, val = forbid_multi_line_headers(name, val)
MIMEText.__setitem__(self, name, val)
class SafeMIMEMultipart(MIMEMultipart):
def __setitem__(self, name, val):
- "Forbids multi-line headers, to prevent header injection."
- if '\n' in val or '\r' in val:
- raise BadHeaderError, "Header values can't contain newlines (got %r for header %r)" % (val, name)
- try:
- val = force_unicode(val).encode('ascii')
- except UnicodeEncodeError:
- if name.lower() in ('to', 'from', 'cc'):
- result = []
- for item in val.split(', '):
- nm, addr = parseaddr(item)
- nm = str(Header(nm, settings.DEFAULT_CHARSET))
- result.append(formataddr((nm, str(addr))))
- val = ', '.join(result)
- else:
- val = Header(force_unicode(val), settings.DEFAULT_CHARSET)
+ name, val = forbid_multi_line_headers(name, val)
MIMEMultipart.__setitem__(self, name, val)
class SMTPConnection(object):
@@ -209,8 +199,14 @@ class EmailMessage(object):
bytestrings). The SafeMIMEText class will handle any necessary encoding
conversions.
"""
- self.to = to or []
- self.bcc = bcc or []
+ if to:
+ self.to = list(to)
+ else:
+ self.to = []
+ if bcc:
+ self.bcc = list(bcc)
+ else:
+ self.bcc = []
self.from_email = from_email or settings.DEFAULT_FROM_EMAIL
self.subject = subject
self.body = body
diff --git a/django/core/management/base.py b/django/core/management/base.py
index 31c2849075..7b8a3e987f 100644
--- a/django/core/management/base.py
+++ b/django/core/management/base.py
@@ -27,6 +27,8 @@ class BaseCommand(object):
help='The Python path to a settings module, e.g. "myproject.settings.main". If this isn\'t provided, the DJANGO_SETTINGS_MODULE environment variable will be used.'),
make_option('--pythonpath',
help='A directory to add to the Python path, e.g. "/home/djangoprojects/myproject".'),
+ make_option('--traceback', action='store_true',
+ help='Print traceback on exception'),
)
help = ''
args = ''
diff --git a/django/core/management/commands/dumpdata.py b/django/core/management/commands/dumpdata.py
index 7bdcc4271f..2642ae925e 100644
--- a/django/core/management/commands/dumpdata.py
+++ b/django/core/management/commands/dumpdata.py
@@ -1,11 +1,12 @@
from django.core.management.base import BaseCommand, CommandError
+from django.core import serializers
from optparse import make_option
class Command(BaseCommand):
option_list = BaseCommand.option_list + (
make_option('--format', default='json', dest='format',
- help='Specifies the output serialization format for fixtures'),
+ help='Specifies the output serialization format for fixtures.'),
make_option('--indent', default=None, dest='indent', type='int',
help='Specifies the indent level to use when pretty-printing output'),
)
@@ -14,10 +15,10 @@ class Command(BaseCommand):
def handle(self, *app_labels, **options):
from django.db.models import get_app, get_apps, get_models
- from django.core import serializers
format = options.get('format', 'json')
indent = options.get('indent', None)
+ show_traceback = options.get('traceback', False)
if len(app_labels) == 0:
app_list = get_apps()
@@ -26,6 +27,9 @@ class Command(BaseCommand):
# Check that the serialization format exists; this is a shortcut to
# avoid collating all the objects and _then_ failing.
+ if format not in serializers.get_public_serializer_formats():
+ raise CommandError("Unknown serialization format: %s" % format)
+
try:
serializers.get_serializer(format)
except KeyError:
@@ -34,8 +38,10 @@ class Command(BaseCommand):
objects = []
for app in app_list:
for model in get_models(app):
- objects.extend(model.objects.all())
+ objects.extend(model._default_manager.all())
try:
return serializers.serialize(format, objects, indent=indent)
except Exception, e:
+ if show_traceback:
+ raise
raise CommandError("Unable to serialize database: %s" % e)
diff --git a/django/core/management/commands/loaddata.py b/django/core/management/commands/loaddata.py
index 216db09141..fb0325906d 100644
--- a/django/core/management/commands/loaddata.py
+++ b/django/core/management/commands/loaddata.py
@@ -27,6 +27,7 @@ class Command(BaseCommand):
self.style = no_style()
verbosity = int(options.get('verbosity', 1))
+ show_traceback = options.get('traceback', False)
# Keep a count of the installed objects and fixtures
count = [0, 0]
@@ -50,10 +51,10 @@ class Command(BaseCommand):
parts = fixture_label.split('.')
if len(parts) == 1:
fixture_name = fixture_label
- formats = serializers.get_serializer_formats()
+ formats = serializers.get_public_serializer_formats()
else:
fixture_name, format = '.'.join(parts[:-1]), parts[-1]
- if format in serializers.get_serializer_formats():
+ if format in serializers.get_public_serializer_formats():
formats = [format]
else:
formats = []
@@ -98,11 +99,13 @@ class Command(BaseCommand):
label_found = True
except Exception, e:
fixture.close()
+ transaction.rollback()
+ transaction.leave_transaction_management()
+ if show_traceback:
+ raise
sys.stderr.write(
self.style.ERROR("Problem installing fixture '%s': %s\n" %
(full_path, str(e))))
- transaction.rollback()
- transaction.leave_transaction_management()
return
fixture.close()
except:
diff --git a/django/core/management/sql.py b/django/core/management/sql.py
index 49f4a80732..46b8d4a985 100644
--- a/django/core/management/sql.py
+++ b/django/core/management/sql.py
@@ -116,6 +116,7 @@ def sql_delete(app, style):
"Returns a list of the DROP TABLE SQL statements for the given app."
from django.db import connection, models, get_introspection_module
from django.db.backends.util import truncate_name
+ from django.contrib.contenttypes import generic
introspection = get_introspection_module()
# This should work even if a connection isn't available
@@ -179,6 +180,8 @@ def sql_delete(app, style):
for model in app_models:
opts = model._meta
for f in opts.many_to_many:
+ if isinstance(f.rel, generic.GenericRel):
+ continue
if cursor and table_name_converter(f.m2m_db_table()) in table_names:
output.append("%s %s;" % (style.SQL_KEYWORD('DROP TABLE'),
style.SQL_TABLE(qn(f.m2m_db_table()))))
diff --git a/django/core/serializers/__init__.py b/django/core/serializers/__init__.py
index 1e24e2bd22..47dfb0c87a 100644
--- a/django/core/serializers/__init__.py
+++ b/django/core/serializers/__init__.py
@@ -53,6 +53,11 @@ def get_serializer_formats():
_load_serializers()
return _serializers.keys()
+def get_public_serializer_formats():
+ if not _serializers:
+ _load_serializers()
+ return [k for k, v in _serializers.iteritems() if not v.Serializer.internal_use_only]
+
def get_deserializer(format):
if not _serializers:
_load_serializers()
diff --git a/django/core/serializers/base.py b/django/core/serializers/base.py
index ee9e4dd621..3c7dcfa02e 100644
--- a/django/core/serializers/base.py
+++ b/django/core/serializers/base.py
@@ -22,6 +22,10 @@ class Serializer(object):
Abstract serializer base class.
"""
+ # Indicates if the implemented serializer is only available for
+ # internal Django use.
+ internal_use_only = False
+
def serialize(self, queryset, **options):
"""
Serialize a queryset.
diff --git a/django/core/serializers/json.py b/django/core/serializers/json.py
index fa2dca7295..e17b821f52 100644
--- a/django/core/serializers/json.py
+++ b/django/core/serializers/json.py
@@ -20,6 +20,8 @@ class Serializer(PythonSerializer):
"""
Convert a queryset to JSON.
"""
+ internal_use_only = False
+
def end_serialization(self):
self.options.pop('stream', None)
self.options.pop('fields', None)
diff --git a/django/core/serializers/python.py b/django/core/serializers/python.py
index 6fc13d76b5..cedab06be9 100644
--- a/django/core/serializers/python.py
+++ b/django/core/serializers/python.py
@@ -13,7 +13,9 @@ class Serializer(base.Serializer):
"""
Serializes a QuerySet to basic Python objects.
"""
-
+
+ internal_use_only = True
+
def start_serialization(self):
self._current = None
self.objects = []
diff --git a/django/core/serializers/pyyaml.py b/django/core/serializers/pyyaml.py
index 772f34a2e3..4c32a9686f 100644
--- a/django/core/serializers/pyyaml.py
+++ b/django/core/serializers/pyyaml.py
@@ -19,6 +19,8 @@ class Serializer(PythonSerializer):
Convert a queryset to YAML.
"""
+ internal_use_only = False
+
def handle_field(self, obj, field):
# A nasty special case: base YAML doesn't support serialization of time
# types (as opposed to dates or datetimes, which it does support). Since
diff --git a/django/core/servers/basehttp.py b/django/core/servers/basehttp.py
index e98957da2c..05f8756655 100644
--- a/django/core/servers/basehttp.py
+++ b/django/core/servers/basehttp.py
@@ -398,8 +398,20 @@ class ServerHandler(object):
self.bytes_sent += len(data)
# XXX check Content-Length and truncate if too many bytes written?
- self._write(data)
- self._flush()
+
+ # If data is too large, socket will choke, so write chunks no larger
+ # than 32MB at a time.
+ length = len(data)
+ if length > 33554432:
+ offset = 0
+ while offset < length:
+ chunk_size = min(33554432, length)
+ self._write(data[offset:offset+chunk_size])
+ self._flush()
+ offset += chunk_size
+ else:
+ self._write(data)
+ self._flush()
def sendfile(self):
"""Platform-specific file transmission
diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py
index cd830413fc..5dd62432c5 100644
--- a/django/db/backends/oracle/base.py
+++ b/django/db/backends/oracle/base.py
@@ -413,6 +413,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
return self.connection is not None
def _cursor(self, settings):
+ cursor = None
if not self._valid_connection():
if len(settings.DATABASE_HOST.strip()) == 0:
settings.DATABASE_HOST = 'localhost'
@@ -422,16 +423,25 @@ class DatabaseWrapper(BaseDatabaseWrapper):
else:
conn_string = "%s/%s@%s" % (settings.DATABASE_USER, settings.DATABASE_PASSWORD, settings.DATABASE_NAME)
self.connection = Database.connect(conn_string, **self.options)
+ cursor = FormatStylePlaceholderCursor(self.connection)
+ # Set oracle date to ansi date format. This only needs to execute
+ # once when we create a new connection.
+ cursor.execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD' "
+ "NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'")
try:
self.oracle_version = int(self.connection.version.split('.')[0])
except ValueError:
pass
- cursor = FormatStylePlaceholderCursor(self.connection)
+ try:
+ self.connection.stmtcachesize = 20
+ except:
+ # Django docs specify cx_Oracle version 4.3.1 or higher, but
+ # stmtcachesize is available only in 4.3.2 and up.
+ pass
+ if not cursor:
+ cursor = FormatStylePlaceholderCursor(self.connection)
# Default arraysize of 1 is highly sub-optimal.
cursor.arraysize = 100
- # Set oracle date to ansi date format.
- cursor.execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD'")
- cursor.execute("ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'")
return cursor
class FormatStylePlaceholderCursor(Database.Cursor):
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index 61ccc2d85d..e72f5015b7 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -714,7 +714,7 @@ class EmailField(CharField):
class FileField(Field):
def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs):
self.upload_to = upload_to
- kwargs['max_length'] = kwargs.get('max_length', 100)
+ kwargs['max_length'] = kwargs.get('max_length', 100)
Field.__init__(self, verbose_name, name, **kwargs)
def get_db_prep_save(self, value):
@@ -910,6 +910,11 @@ class NullBooleanField(Field):
def get_manipulator_field_objs(self):
return [oldforms.NullBooleanField]
+ def formfield(self, **kwargs):
+ defaults = {'form_class': forms.NullBooleanField}
+ defaults.update(kwargs)
+ return super(NullBooleanField, self).formfield(**defaults)
+
class PhoneNumberField(IntegerField):
def get_manipulator_field_objs(self):
return [oldforms.PhoneNumberField]
@@ -926,11 +931,11 @@ class PhoneNumberField(IntegerField):
class PositiveIntegerField(IntegerField):
def get_manipulator_field_objs(self):
return [oldforms.PositiveIntegerField]
-
+
def formfield(self, **kwargs):
defaults = {'min_value': 0}
defaults.update(kwargs)
- return super(PositiveIntegerField, self).formfield(**defaults)
+ return super(PositiveIntegerField, self).formfield(**defaults)
class PositiveSmallIntegerField(IntegerField):
def get_manipulator_field_objs(self):
@@ -939,7 +944,7 @@ class PositiveSmallIntegerField(IntegerField):
def formfield(self, **kwargs):
defaults = {'min_value': 0}
defaults.update(kwargs)
- return super(PositiveSmallIntegerField, self).formfield(**defaults)
+ return super(PositiveSmallIntegerField, self).formfield(**defaults)
class SlugField(CharField):
def __init__(self, *args, **kwargs):
diff --git a/django/http/__init__.py b/django/http/__init__.py
index 13cc8cea0d..69e9d51204 100644
--- a/django/http/__init__.py
+++ b/django/http/__init__.py
@@ -277,7 +277,20 @@ class HttpResponse(object):
for key, value in self._headers.values()]) \
+ '\n\n' + self.content
+ def _convert_to_ascii(self, *values):
+ "Convert all values to ascii strings"
+ for value in values:
+ if isinstance(value, unicode):
+ try:
+ yield value.encode('us-ascii')
+ except UnicodeError, e:
+ e.reason += ', HTTP response headers must be in US-ASCII format'
+ raise
+ else:
+ yield str(value)
+
def __setitem__(self, header, value):
+ header, value = self._convert_to_ascii(header, value)
self._headers[header.lower()] = (header, value)
def __delitem__(self, header):
diff --git a/django/newforms/models.py b/django/newforms/models.py
index 17fd1cf2e2..fd0087a3b1 100644
--- a/django/newforms/models.py
+++ b/django/newforms/models.py
@@ -84,9 +84,8 @@ def form_for_model(model, form=BaseForm, fields=None,
determining the formfield for a given database field. It's a callable that
takes a database Field instance and returns a form Field instance.
"""
- warn("form_for_model is deprecated, use ModelForm instead.",
- PendingDeprecationWarning,
- stacklevel=3)
+ warn("form_for_model is deprecated. Use ModelForm instead.",
+ PendingDeprecationWarning, stacklevel=3)
opts = model._meta
field_list = []
for f in opts.fields + opts.many_to_many:
@@ -114,9 +113,8 @@ def form_for_instance(instance, form=BaseForm, fields=None,
takes a database Field instance, plus **kwargs, and returns a form Field
instance with the given kwargs (i.e. 'initial').
"""
- warn("form_for_instance is deprecated, use ModelForm instead.",
- PendingDeprecationWarning,
- stacklevel=3)
+ warn("form_for_instance is deprecated. Use ModelForm instead.",
+ PendingDeprecationWarning, stacklevel=3)
model = instance.__class__
opts = model._meta
field_list = []
@@ -149,10 +147,10 @@ def model_to_dict(instance, fields=None, exclude=None):
"""
Returns a dict containing the data in ``instance`` suitable for passing as
a Form's ``initial`` keyword argument.
-
+
``fields`` is an optional list of field names. If provided, only the named
fields will be included in the returned dict.
-
+
``exclude`` is an optional list of field names. If provided, the named
fields will be excluded from the returned dict, even if they are listed in
the ``fields`` argument.
@@ -187,7 +185,7 @@ def fields_for_model(model, fields=None, exclude=None, formfield_callback=lambda
``fields`` is an optional list of field names. If provided, only the named
fields will be included in the returned fields.
-
+
``exclude`` is an optional list of field names. If provided, the named
fields will be excluded from the returned fields, even if they are listed
in the ``fields`` argument.
@@ -214,9 +212,8 @@ class ModelFormOptions(object):
self.exclude = getattr(options, 'exclude', None)
class ModelFormMetaclass(type):
- def __new__(cls, name, bases, attrs):
- # TODO: no way to specify formfield_callback yet, do we need one, or
- # should it be a special case for the admin?
+ def __new__(cls, name, bases, attrs,
+ formfield_callback=lambda f: f.formfield()):
fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)]
fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter))
@@ -253,7 +250,8 @@ class ModelFormMetaclass(type):
base_model = getattr(base_opts, 'model', None)
if base_model and base_model is not opts.model:
raise ImproperlyConfigured('%s defines a different model than its parent.' % name)
- model_fields = fields_for_model(opts.model, opts.fields, opts.exclude)
+ model_fields = fields_for_model(opts.model, opts.fields,
+ opts.exclude, formfield_callback)
# fields declared in base classes override fields from the model
model_fields.update(declared_fields)
attrs['base_fields'] = model_fields
diff --git a/django/template/__init__.py b/django/template/__init__.py
index 9b1868cfce..750c48988d 100644
--- a/django/template/__init__.py
+++ b/django/template/__init__.py
@@ -154,15 +154,12 @@ class StringOrigin(Origin):
class Template(object):
def __init__(self, template_string, origin=None, name='<Unknown Template>'):
- "Compilation stage"
try:
template_string = smart_unicode(template_string)
except UnicodeDecodeError:
raise TemplateEncodingError("Templates can only be constructed from unicode or UTF-8 strings.")
- if settings.TEMPLATE_DEBUG and origin == None:
+ if settings.TEMPLATE_DEBUG and origin is None:
origin = StringOrigin(template_string)
- # Could do some crazy stack-frame stuff to record where this string
- # came from...
self.nodelist = compile_string(template_string, origin)
self.name = name
@@ -177,13 +174,18 @@ class Template(object):
def compile_string(template_string, origin):
"Compiles template_string into NodeList ready for rendering"
- lexer = lexer_factory(template_string, origin)
- parser = parser_factory(lexer.tokenize())
+ if settings.TEMPLATE_DEBUG:
+ from debug import DebugLexer, DebugParser
+ lexer_class, parser_class = DebugLexer, DebugParser
+ else:
+ lexer_class, parser_class = Lexer, Parser
+ lexer = lexer_class(template_string, origin)
+ parser = parser_class(lexer.tokenize())
return parser.parse()
class Token(object):
def __init__(self, token_type, contents):
- "The token_type must be TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK or TOKEN_COMMENT"
+ # token_type must be TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK or TOKEN_COMMENT.
self.token_type, self.contents = token_type, contents
def __str__(self):
@@ -200,7 +202,7 @@ class Lexer(object):
self.origin = origin
def tokenize(self):
- "Return a list of tokens from a given template_string"
+ "Return a list of tokens from a given template_string."
in_tag = False
result = []
for bit in tag_re.split(self.template_string):
@@ -226,30 +228,6 @@ class Lexer(object):
token = Token(TOKEN_TEXT, token_string)
return token
-class DebugLexer(Lexer):
- def __init__(self, template_string, origin):
- super(DebugLexer, self).__init__(template_string, origin)
-
- def tokenize(self):
- "Return a list of tokens from a given template_string"
- result, upto = [], 0
- for match in tag_re.finditer(self.template_string):
- start, end = match.span()
- if start > upto:
- result.append(self.create_token(self.template_string[upto:start], (upto, start), False))
- upto = start
- result.append(self.create_token(self.template_string[start:end], (start, end), True))
- upto = end
- last_bit = self.template_string[upto:]
- if last_bit:
- result.append(self.create_token(last_bit, (upto, upto + len(last_bit)), False))
- return result
-
- def create_token(self, token_string, source, in_tag):
- token = super(DebugLexer, self).create_token(token_string, in_tag)
- token.source = self.origin, source
- return token
-
class Parser(object):
def __init__(self, tokens):
self.tokens = tokens
@@ -319,17 +297,17 @@ class Parser(object):
def exit_command(self):
pass
- def error(self, token, msg ):
+ def error(self, token, msg):
return TemplateSyntaxError(msg)
def empty_variable(self, token):
- raise self.error( token, "Empty variable tag")
+ raise self.error(token, "Empty variable tag")
def empty_block_tag(self, token):
- raise self.error( token, "Empty block tag")
+ raise self.error(token, "Empty block tag")
def invalid_block_tag(self, token, command):
- raise self.error( token, "Invalid block tag: '%s'" % command)
+ raise self.error(token, "Invalid block tag: '%s'" % command)
def unclosed_block_tag(self, parse_until):
raise self.error(None, "Unclosed tags: %s " % ', '.join(parse_until))
@@ -358,57 +336,7 @@ class Parser(object):
if filter_name in self.filters:
return self.filters[filter_name]
else:
- raise TemplateSyntaxError, "Invalid filter: '%s'" % filter_name
-
-class DebugParser(Parser):
- def __init__(self, lexer):
- super(DebugParser, self).__init__(lexer)
- self.command_stack = []
-
- def enter_command(self, command, token):
- self.command_stack.append( (command, token.source) )
-
- def exit_command(self):
- self.command_stack.pop()
-
- def error(self, token, msg):
- return self.source_error(token.source, msg)
-
- def source_error(self, source,msg):
- e = TemplateSyntaxError(msg)
- e.source = source
- return e
-
- def create_nodelist(self):
- return DebugNodeList()
-
- def create_variable_node(self, contents):
- return DebugVariableNode(contents)
-
- def extend_nodelist(self, nodelist, node, token):
- node.source = token.source
- super(DebugParser, self).extend_nodelist(nodelist, node, token)
-
- def unclosed_block_tag(self, parse_until):
- command, source = self.command_stack.pop()
- msg = "Unclosed tag '%s'. Looking for one of: %s " % (command, ', '.join(parse_until))
- raise self.source_error( source, msg)
-
- def compile_function_error(self, token, e):
- if not hasattr(e, 'source'):
- e.source = token.source
-
-def lexer_factory(*args, **kwargs):
- if settings.TEMPLATE_DEBUG:
- return DebugLexer(*args, **kwargs)
- else:
- return Lexer(*args, **kwargs)
-
-def parser_factory(*args, **kwargs):
- if settings.TEMPLATE_DEBUG:
- return DebugParser(*args, **kwargs)
- else:
- return Parser(*args, **kwargs)
+ raise TemplateSyntaxError("Invalid filter: '%s'" % filter_name)
class TokenParser(object):
"""
@@ -426,7 +354,7 @@ class TokenParser(object):
def top(self):
"Overload this method to do the actual parsing and return the result."
- raise NotImplemented
+ raise NotImplementedError()
def more(self):
"Returns True if there is more stuff in the tag."
@@ -435,7 +363,7 @@ class TokenParser(object):
def back(self):
"Undoes the last microparser. Use this for lookahead and backtracking."
if not len(self.backout):
- raise TemplateSyntaxError, "back called without some previous parsing"
+ raise TemplateSyntaxError("back called without some previous parsing")
self.pointer = self.backout.pop()
def tag(self):
@@ -443,7 +371,7 @@ class TokenParser(object):
subject = self.subject
i = self.pointer
if i >= len(subject):
- raise TemplateSyntaxError, "expected another tag, found end of string: %s" % subject
+ raise TemplateSyntaxError("expected another tag, found end of string: %s" % subject)
p = i
while i < len(subject) and subject[i] not in (' ', '\t'):
i += 1
@@ -459,14 +387,14 @@ class TokenParser(object):
subject = self.subject
i = self.pointer
if i >= len(subject):
- raise TemplateSyntaxError, "Searching for value. Expected another value but found end of string: %s" % subject
+ raise TemplateSyntaxError("Searching for value. Expected another value but found end of string: %s" % subject)
if subject[i] in ('"', "'"):
p = i
i += 1
while i < len(subject) and subject[i] != subject[p]:
i += 1
if i >= len(subject):
- raise TemplateSyntaxError, "Searching for value. Unexpected end of string in column %d: %s" % (i, subject)
+ raise TemplateSyntaxError("Searching for value. Unexpected end of string in column %d: %s" % (i, subject))
i += 1
res = subject[p:i]
while i < len(subject) and subject[i] in (' ', '\t'):
@@ -483,7 +411,7 @@ class TokenParser(object):
while i < len(subject) and subject[i] != c:
i += 1
if i >= len(subject):
- raise TemplateSyntaxError, "Searching for value. Unexpected end of string in column %d: %s" % (i, subject)
+ raise TemplateSyntaxError("Searching for value. Unexpected end of string in column %d: %s" % (i, subject))
i += 1
s = subject[p:i]
while i < len(subject) and subject[i] in (' ', '\t'):
@@ -542,8 +470,8 @@ class FilterExpression(object):
for match in matches:
start = match.start()
if upto != start:
- raise TemplateSyntaxError, "Could not parse some characters: %s|%s|%s" % \
- (token[:upto], token[upto:start], token[start:])
+ raise TemplateSyntaxError("Could not parse some characters: %s|%s|%s" % \
+ (token[:upto], token[upto:start], token[start:]))
if var == None:
var, constant, i18n_constant = match.group("var", "constant", "i18n_constant")
if i18n_constant:
@@ -552,9 +480,9 @@ class FilterExpression(object):
var = '"%s"' % constant.replace(r'\"', '"')
upto = match.end()
if var == None:
- raise TemplateSyntaxError, "Could not find variable at start of %s" % token
+ raise TemplateSyntaxError("Could not find variable at start of %s" % token)
elif var.find(VARIABLE_ATTRIBUTE_SEPARATOR + '_') > -1 or var[0] == '_':
- raise TemplateSyntaxError, "Variables and attributes may not begin with underscores: '%s'" % var
+ raise TemplateSyntaxError("Variables and attributes may not begin with underscores: '%s'" % var)
else:
filter_name = match.group("filter_name")
args = []
@@ -570,7 +498,7 @@ class FilterExpression(object):
filters.append( (filter_func,args))
upto = match.end()
if upto != len(token):
- raise TemplateSyntaxError, "Could not parse the remainder: '%s' from '%s'" % (token[upto:], token)
+ raise TemplateSyntaxError("Could not parse the remainder: '%s' from '%s'" % (token[upto:], token))
self.filters = filters
self.var = Variable(var)
@@ -627,7 +555,7 @@ class FilterExpression(object):
provided.pop(0)
except IndexError:
# Not enough
- raise TemplateSyntaxError, "%s requires %d arguments, %d provided" % (name, len(nondefs), plen)
+ raise TemplateSyntaxError("%s requires %d arguments, %d provided" % (name, len(nondefs), plen))
# Defaults can be overridden.
defaults = defaults and list(defaults) or []
@@ -636,7 +564,7 @@ class FilterExpression(object):
defaults.pop(0)
except IndexError:
# Too many.
- raise TemplateSyntaxError, "%s requires %d arguments, %d provided" % (name, len(nondefs), plen)
+ raise TemplateSyntaxError("%s requires %d arguments, %d provided" % (name, len(nondefs), plen))
return True
args_check = staticmethod(args_check)
@@ -816,22 +744,6 @@ class NodeList(list):
def render_node(self, node, context):
return node.render(context)
-class DebugNodeList(NodeList):
- def render_node(self, node, context):
- try:
- result = node.render(context)
- except TemplateSyntaxError, e:
- if not hasattr(e, 'source'):
- e.source = node.source
- raise
- except Exception, e:
- from sys import exc_info
- wrapped = TemplateSyntaxError('Caught an exception while rendering: %s' % e)
- wrapped.source = node.source
- wrapped.exc_info = exc_info()
- raise wrapped
- return result
-
class TextNode(Node):
def __init__(self, s):
self.s = s
@@ -861,21 +773,6 @@ class VariableNode(Node):
else:
return force_unicode(output)
-class DebugVariableNode(VariableNode):
- def render(self, context):
- try:
- output = force_unicode(self.filter_expression.resolve(context))
- except TemplateSyntaxError, e:
- if not hasattr(e, 'source'):
- e.source = self.source
- raise
- except UnicodeDecodeError:
- return ''
- if (context.autoescape and not isinstance(output, SafeData)) or isinstance(output, EscapeData):
- return escape(output)
- else:
- return output
-
def generic_tag_compiler(params, defaults, name, node_class, parser, token):
"Returns a template.Node subclass."
bits = token.split_contents()[1:]
@@ -887,7 +784,7 @@ def generic_tag_compiler(params, defaults, name, node_class, parser, token):
message = "%s takes %s arguments" % (name, bmin)
else:
message = "%s takes between %s and %s arguments" % (name, bmin, bmax)
- raise TemplateSyntaxError, message
+ raise TemplateSyntaxError(message)
return node_class(bits)
class Library(object):
@@ -913,7 +810,7 @@ class Library(object):
self.tags[name] = compile_function
return compile_function
else:
- raise InvalidTemplateLibrary, "Unsupported arguments to Library.tag: (%r, %r)", (name, compile_function)
+ raise InvalidTemplateLibrary("Unsupported arguments to Library.tag: (%r, %r)", (name, compile_function))
def tag_function(self,func):
self.tags[getattr(func, "_decorated_function", func).__name__] = func
@@ -937,7 +834,7 @@ class Library(object):
self.filters[name] = filter_func
return filter_func
else:
- raise InvalidTemplateLibrary, "Unsupported arguments to Library.filter: (%r, %r)", (name, filter_func)
+ raise InvalidTemplateLibrary("Unsupported arguments to Library.filter: (%r, %r)", (name, filter_func))
def filter_function(self, func):
self.filters[getattr(func, "_decorated_function", func).__name__] = func
@@ -966,7 +863,7 @@ class Library(object):
if params[0] == 'context':
params = params[1:]
else:
- raise TemplateSyntaxError, "Any tag function decorated with takes_context=True must have a first argument of 'context'"
+ raise TemplateSyntaxError("Any tag function decorated with takes_context=True must have a first argument of 'context'")
class InclusionNode(Node):
def __init__(self, vars_to_resolve):
@@ -1003,12 +900,12 @@ def get_library(module_name):
try:
mod = __import__(module_name, {}, {}, [''])
except ImportError, e:
- raise InvalidTemplateLibrary, "Could not load template library from %s, %s" % (module_name, e)
+ raise InvalidTemplateLibrary("Could not load template library from %s, %s" % (module_name, e))
try:
lib = mod.register
libraries[module_name] = lib
except AttributeError:
- raise InvalidTemplateLibrary, "Template library %s does not have a variable named 'register'" % module_name
+ raise InvalidTemplateLibrary("Template library %s does not have a variable named 'register'" % module_name)
return lib
def add_to_builtins(module_name):
diff --git a/django/template/context.py b/django/template/context.py
index 0e41a26618..6ba53f340b 100644
--- a/django/template/context.py
+++ b/django/template/context.py
@@ -9,7 +9,6 @@ class ContextPopException(Exception):
class Context(object):
"A stack container for variable context"
-
def __init__(self, dict_=None, autoescape=True):
dict_ = dict_ or {}
self.dicts = [dict_]
@@ -78,11 +77,11 @@ def get_standard_processors():
try:
mod = __import__(module, {}, {}, [attr])
except ImportError, e:
- raise ImproperlyConfigured, 'Error importing request processor module %s: "%s"' % (module, e)
+ raise ImproperlyConfigured('Error importing request processor module %s: "%s"' % (module, e))
try:
func = getattr(mod, attr)
except AttributeError:
- raise ImproperlyConfigured, 'Module "%s" does not define a "%s" callable request processor' % (module, attr)
+ raise ImproperlyConfigured('Module "%s" does not define a "%s" callable request processor' % (module, attr))
processors.append(func)
_standard_context_processors = tuple(processors)
return _standard_context_processors
@@ -102,4 +101,3 @@ class RequestContext(Context):
processors = tuple(processors)
for processor in get_standard_processors() + processors:
self.update(processor(request))
-
diff --git a/django/template/debug.py b/django/template/debug.py
new file mode 100644
index 0000000000..008059c28f
--- /dev/null
+++ b/django/template/debug.py
@@ -0,0 +1,97 @@
+from django.template import Lexer, Parser, tag_re, NodeList, VariableNode, TemplateSyntaxError
+from django.utils.encoding import force_unicode
+from django.utils.html import escape
+from django.utils.safestring import SafeData, EscapeData
+
+class DebugLexer(Lexer):
+ def __init__(self, template_string, origin):
+ super(DebugLexer, self).__init__(template_string, origin)
+
+ def tokenize(self):
+ "Return a list of tokens from a given template_string"
+ result, upto = [], 0
+ for match in tag_re.finditer(self.template_string):
+ start, end = match.span()
+ if start > upto:
+ result.append(self.create_token(self.template_string[upto:start], (upto, start), False))
+ upto = start
+ result.append(self.create_token(self.template_string[start:end], (start, end), True))
+ upto = end
+ last_bit = self.template_string[upto:]
+ if last_bit:
+ result.append(self.create_token(last_bit, (upto, upto + len(last_bit)), False))
+ return result
+
+ def create_token(self, token_string, source, in_tag):
+ token = super(DebugLexer, self).create_token(token_string, in_tag)
+ token.source = self.origin, source
+ return token
+
+class DebugParser(Parser):
+ def __init__(self, lexer):
+ super(DebugParser, self).__init__(lexer)
+ self.command_stack = []
+
+ def enter_command(self, command, token):
+ self.command_stack.append( (command, token.source) )
+
+ def exit_command(self):
+ self.command_stack.pop()
+
+ def error(self, token, msg):
+ return self.source_error(token.source, msg)
+
+ def source_error(self, source,msg):
+ e = TemplateSyntaxError(msg)
+ e.source = source
+ return e
+
+ def create_nodelist(self):
+ return DebugNodeList()
+
+ def create_variable_node(self, contents):
+ return DebugVariableNode(contents)
+
+ def extend_nodelist(self, nodelist, node, token):
+ node.source = token.source
+ super(DebugParser, self).extend_nodelist(nodelist, node, token)
+
+ def unclosed_block_tag(self, parse_until):
+ command, source = self.command_stack.pop()
+ msg = "Unclosed tag '%s'. Looking for one of: %s " % (command, ', '.join(parse_until))
+ raise self.source_error(source, msg)
+
+ def compile_function_error(self, token, e):
+ if not hasattr(e, 'source'):
+ e.source = token.source
+
+class DebugNodeList(NodeList):
+ def render_node(self, node, context):
+ try:
+ result = node.render(context)
+ except TemplateSyntaxError, e:
+ if not hasattr(e, 'source'):
+ e.source = node.source
+ raise
+ except Exception, e:
+ from sys import exc_info
+ wrapped = TemplateSyntaxError('Caught an exception while rendering: %s' % e)
+ wrapped.source = node.source
+ wrapped.exc_info = exc_info()
+ raise wrapped
+ return result
+
+class DebugVariableNode(VariableNode):
+ def render(self, context):
+ try:
+ output = force_unicode(self.filter_expression.resolve(context))
+ except TemplateSyntaxError, e:
+ if not hasattr(e, 'source'):
+ e.source = self.source
+ raise
+ except UnicodeDecodeError:
+ return ''
+ if (context.autoescape and not isinstance(output, SafeData)) or isinstance(output, EscapeData):
+ return escape(output)
+ else:
+ return output
diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py
index 2f211dadb5..5cee0ab244 100644
--- a/django/template/defaultfilters.py
+++ b/django/template/defaultfilters.py
@@ -254,7 +254,7 @@ urlize.is_safe=True
urlize.needs_autoescape = True
urlize = stringfilter(urlize)
-def urlizetrunc(value, limit):
+def urlizetrunc(value, limit, autoescape=None):
"""
Converts URLs into clickable links, truncating URLs to the given character
limit, and adding 'rel=nofollow' attribute to discourage spamming.
@@ -262,8 +262,10 @@ def urlizetrunc(value, limit):
Argument: Length to truncate URLs to.
"""
from django.utils.html import urlize
- return mark_safe(urlize(value, trim_url_limit=int(limit), nofollow=True))
+ return mark_safe(urlize(value, trim_url_limit=int(limit), nofollow=True,
+ autoescape=autoescape))
urlizetrunc.is_safe = True
+urlizetrunc.needs_autoescape = True
urlizetrunc = stringfilter(urlizetrunc)
def wordcount(value):
diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py
index d91e30bbb5..726a37012b 100644
--- a/django/template/defaulttags.py
+++ b/django/template/defaulttags.py
@@ -84,19 +84,16 @@ class FirstOfNode(Node):
return u''
class ForNode(Node):
- def __init__(self, loopvars, sequence, reversed, nodelist_loop):
+ def __init__(self, loopvars, sequence, is_reversed, nodelist_loop):
self.loopvars, self.sequence = loopvars, sequence
- self.reversed = reversed
+ self.is_reversed = is_reversed
self.nodelist_loop = nodelist_loop
def __repr__(self):
- if self.reversed:
- reversed = ' reversed'
- else:
- reversed = ''
+ reversed_text = self.is_reversed and ' reversed' or ''
return "<For Node: for %s in %s, tail_len: %d%s>" % \
(', '.join(self.loopvars), self.sequence, len(self.nodelist_loop),
- reversed)
+ reversed_text)
def __iter__(self):
for node in self.nodelist_loop:
@@ -125,22 +122,23 @@ class ForNode(Node):
if not hasattr(values, '__len__'):
values = list(values)
len_values = len(values)
- if self.reversed:
+ if self.is_reversed:
values = reversed(values)
unpack = len(self.loopvars) > 1
+ # Create a forloop value in the context. We'll update counters on each
+ # iteration just below.
+ loop_dict = context['forloop'] = {'parentloop': parentloop}
for i, item in enumerate(values):
- context['forloop'] = {
- # Shortcuts for current loop iteration number.
- 'counter0': i,
- 'counter': i+1,
- # Reverse counter iteration numbers.
- 'revcounter': len_values - i,
- 'revcounter0': len_values - i - 1,
- # Boolean values designating first and last times through loop.
- 'first': (i == 0),
- 'last': (i == len_values - 1),
- 'parentloop': parentloop,
- }
+ # Shortcuts for current loop iteration number.
+ loop_dict['counter0'] = i
+ loop_dict['counter'] = i+1
+ # Reverse counter iteration numbers.
+ loop_dict['revcounter'] = len_values - i
+ loop_dict['revcounter0'] = len_values - i - 1
+ # Boolean values designating first and last times through loop.
+ loop_dict['first'] = (i == 0)
+ loop_dict['last'] = (i == len_values - 1)
+
if unpack:
# If there are multiple loop variables, unpack the item into
# them.
@@ -619,8 +617,8 @@ def do_for(parser, token):
raise TemplateSyntaxError("'for' statements should have at least four"
" words: %s" % token.contents)
- reversed = bits[-1] == 'reversed'
- in_index = reversed and -3 or -2
+ is_reversed = bits[-1] == 'reversed'
+ in_index = is_reversed and -3 or -2
if bits[in_index] != 'in':
raise TemplateSyntaxError("'for' statements should use the format"
" 'for x in y': %s" % token.contents)
@@ -634,7 +632,7 @@ def do_for(parser, token):
sequence = parser.compile_filter(bits[in_index+1])
nodelist_loop = parser.parse(('endfor',))
parser.delete_first_token()
- return ForNode(loopvars, sequence, reversed, nodelist_loop)
+ return ForNode(loopvars, sequence, is_reversed, nodelist_loop)
do_for = register.tag("for", do_for)
def do_ifequal(parser, token, negate):
@@ -814,7 +812,7 @@ def ssi(parser, token):
Outputs the contents of a given file into the page.
Like a simple "include" tag, the ``ssi`` tag includes the contents
- of another file -- which must be specified using an absolute page --
+ of another file -- which must be specified using an absolute path --
in the current page::
{% ssi /home/html/ljworld.com/includes/right_generic.html %}
@@ -926,20 +924,18 @@ def regroup(parser, token):
{% regroup people|dictsort:"gender" by gender as grouped %}
"""
- firstbits = token.contents.split(None, 3)
- if len(firstbits) != 4:
+ bits = token.contents.split()
+ if len(bits) != 6:
raise TemplateSyntaxError, "'regroup' tag takes five arguments"
- target = parser.compile_filter(firstbits[1])
- if firstbits[2] != 'by':
+ target = parser.compile_filter(bits[1])
+ if bits[2] != 'by':
raise TemplateSyntaxError("second argument to 'regroup' tag must be 'by'")
- lastbits_reversed = firstbits[3][::-1].split(None, 2)
- if lastbits_reversed[1][::-1] != 'as':
+ if bits[4] != 'as':
raise TemplateSyntaxError("next-to-last argument to 'regroup' tag must"
" be 'as'")
- expression = parser.compile_filter(lastbits_reversed[2][::-1])
-
- var_name = lastbits_reversed[0][::-1]
+ expression = parser.compile_filter(bits[3])
+ var_name = bits[5]
return RegroupNode(target, expression, var_name)
regroup = register.tag(regroup)
diff --git a/django/test/utils.py b/django/test/utils.py
index 51ca37e2f0..b1c528873c 100644
--- a/django/test/utils.py
+++ b/django/test/utils.py
@@ -1,4 +1,4 @@
-import sys, time
+import sys, time, os
from django.conf import settings
from django.db import connection, get_creation_module
from django.core import mail
@@ -106,9 +106,32 @@ def create_test_db(verbosity=1, autoclobber=False):
if verbosity >= 1:
print "Creating test database..."
# If we're using SQLite, it's more convenient to test against an
- # in-memory database.
+ # in-memory database. Using the TEST_DATABASE_NAME setting you can still choose
+ # to run on a physical database.
if settings.DATABASE_ENGINE == "sqlite3":
- TEST_DATABASE_NAME = ":memory:"
+ if settings.TEST_DATABASE_NAME and settings.TEST_DATABASE_NAME != ":memory:":
+ TEST_DATABASE_NAME = settings.TEST_DATABASE_NAME
+ # Erase the old test database
+ if verbosity >= 1:
+ print "Destroying old test database..."
+ if os.access(TEST_DATABASE_NAME, os.F_OK):
+ if not autoclobber:
+ confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % TEST_DATABASE_NAME)
+ if autoclobber or confirm == 'yes':
+ try:
+ if verbosity >= 1:
+ print "Destroying old test database..."
+ os.remove(TEST_DATABASE_NAME)
+ except Exception, e:
+ sys.stderr.write("Got an error deleting the old test database: %s\n" % e)
+ sys.exit(2)
+ else:
+ print "Tests cancelled."
+ sys.exit(1)
+ if verbosity >= 1:
+ print "Creating test database..."
+ else:
+ TEST_DATABASE_NAME = ":memory:"
else:
suffix = {
'postgresql': get_postgresql_create_suffix,
@@ -171,17 +194,20 @@ def destroy_test_db(old_database_name, verbosity=1):
creation_module.destroy_test_db(settings, connection, old_database_name, verbosity)
return
- # Unless we're using SQLite, remove the test database to clean up after
- # ourselves. Connect to the previous database (not the test database)
- # to do so, because it's not allowed to delete a database while being
- # connected to it.
if verbosity >= 1:
print "Destroying test database..."
connection.close()
TEST_DATABASE_NAME = settings.DATABASE_NAME
settings.DATABASE_NAME = old_database_name
-
- if settings.DATABASE_ENGINE != "sqlite3":
+ if settings.DATABASE_ENGINE == "sqlite3":
+ if TEST_DATABASE_NAME and TEST_DATABASE_NAME != ":memory:":
+ # Remove the SQLite database file
+ os.remove(TEST_DATABASE_NAME)
+ else:
+ # Remove the test database to clean up after
+ # ourselves. Connect to the previous database (not the test database)
+ # to do so, because it's not allowed to delete a database while being
+ # connected to it.
cursor = connection.cursor()
_set_autocommit(connection)
time.sleep(1) # To avoid "database is being accessed by other users" errors.
diff --git a/django/views/debug.py b/django/views/debug.py
index 18a396d3a6..e3be2a7dcd 100644
--- a/django/views/debug.py
+++ b/django/views/debug.py
@@ -507,7 +507,7 @@ Exception Type: {{ exception_type|escape }} at {{ request.path|escape }}
Exception Value: {{ exception_value|escape }}
</textarea>
<br><br>
- <input type="submit" value="Share this traceback on public Web site">
+ <input type="submit" value="Share this traceback on a public Web site">
</div>
</form>
</div>
diff --git a/django/views/static.py b/django/views/static.py
index 8095fc9231..5a4d3ab55c 100644
--- a/django/views/static.py
+++ b/django/views/static.py
@@ -59,10 +59,11 @@ def serve(request, path, document_root=None, show_indexes=False):
if not was_modified_since(request.META.get('HTTP_IF_MODIFIED_SINCE'),
statobj[stat.ST_MTIME], statobj[stat.ST_SIZE]):
return HttpResponseNotModified()
- mimetype = mimetypes.guess_type(fullpath)[0]
+ mimetype = mimetypes.guess_type(fullpath)[0] or 'application/octet-stream'
contents = open(fullpath, 'rb').read()
response = HttpResponse(contents, mimetype=mimetype)
response["Last-Modified"] = http_date(statobj[stat.ST_MTIME])
+ response["Content-Length"] = len(contents)
return response
DEFAULT_DIRECTORY_INDEX_TEMPLATE = """