summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMalcolm Tredinnick <malcolm.tredinnick@gmail.com>2007-12-19 05:08:37 +0000
committerMalcolm Tredinnick <malcolm.tredinnick@gmail.com>2007-12-19 05:08:37 +0000
commit97091940b1efbc6018133e9f77402c2983fa702f (patch)
treee003a3ec00ab4b0bd0cda7799485cedbf787cd31
parent13d3162aaf7fce11c06d447f397e32b7648ae199 (diff)
queryset-refactor: Merged from trunk up to [6953].
git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@6954 bcc190cf-cafb-0310-a4f2-bffc1f526a37
-rw-r--r--AUTHORS3
-rw-r--r--django/conf/__init__.py4
-rw-r--r--django/conf/locale/ca/LC_MESSAGES/django.mobin61655 -> 62260 bytes
-rw-r--r--django/conf/locale/ca/LC_MESSAGES/django.po392
-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/admin/templatetags/admin_list.py2
-rw-r--r--django/contrib/auth/models.py2
-rw-r--r--django/contrib/auth/tests.py12
-rw-r--r--django/contrib/contenttypes/generic.py42
-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.py100
-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/contrib/sessions/backends/file.py13
-rw-r--r--django/contrib/sessions/tests.py8
-rw-r--r--django/core/cache/backends/filebased.py110
-rw-r--r--django/core/cache/backends/locmem.py59
-rw-r--r--django/core/mail.py10
-rw-r--r--django/core/management/__init__.py78
-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.py17
-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.py18
-rw-r--r--django/core/servers/basehttp.py16
-rw-r--r--django/db/__init__.py4
-rw-r--r--django/db/backends/oracle/base.py19
-rw-r--r--django/db/models/fields/__init__.py13
-rw-r--r--django/http/__init__.py15
-rw-r--r--django/newforms/fields.py4
-rw-r--r--django/newforms/models.py47
-rw-r--r--django/template/defaultfilters.py32
-rw-r--r--django/template/defaulttags.py2
-rw-r--r--django/test/utils.py44
-rw-r--r--django/utils/html.py22
-rw-r--r--django/utils/maxlength.py18
-rw-r--r--django/views/debug.py44
-rw-r--r--django/views/static.py3
-rw-r--r--docs/add_ons.txt20
-rw-r--r--docs/authentication.txt6
-rw-r--r--docs/contributing.txt7
-rw-r--r--docs/custom_model_fields.txt8
-rw-r--r--docs/databases.txt9
-rw-r--r--docs/django-admin.txt7
-rw-r--r--docs/form_for_model.txt7
-rw-r--r--docs/install.txt12
-rw-r--r--docs/localflavor.txt212
-rw-r--r--docs/middleware.txt19
-rw-r--r--docs/model-api.txt2
-rw-r--r--docs/modelforms.txt33
-rw-r--r--docs/newforms.txt2
-rw-r--r--docs/request_response.txt20
-rw-r--r--docs/sessions.txt2
-rw-r--r--docs/settings.txt17
-rw-r--r--docs/syndication_feeds.txt8
-rw-r--r--docs/templates.txt63
-rw-r--r--docs/templates_python.txt4
-rw-r--r--docs/testing.txt49
-rw-r--r--tests/modeltests/generic_relations/models.py30
-rw-r--r--tests/modeltests/model_forms/models.py56
-rw-r--r--tests/modeltests/serializers/models.py38
-rw-r--r--tests/regressiontests/cache/tests.py58
-rw-r--r--tests/regressiontests/defaultfilters/tests.py12
-rw-r--r--tests/regressiontests/forms/localflavor/cl.py8
-rw-r--r--tests/regressiontests/forms/localflavor/uk.py29
-rw-r--r--tests/regressiontests/httpwrappers/tests.py38
-rw-r--r--tests/regressiontests/maxlength/tests.py6
-rw-r--r--tests/regressiontests/model_regress/models.py10
-rw-r--r--tests/regressiontests/templates/filters.py4
-rw-r--r--tests/regressiontests/views/media/file.unknown1
-rw-r--r--tests/regressiontests/views/tests/static.py8
-rwxr-xr-xtests/runtests.py3
97 files changed, 1613 insertions, 973 deletions
diff --git a/AUTHORS b/AUTHORS
index ffb9e87690..5ec60de738 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -130,6 +130,7 @@ answer newbie questions, and generally made Django that much better:
Matthew Flanagan <http://wadofstuff.blogspot.com>
Eric Floehr <eric@intellovations.com>
Vincent Foley <vfoleybourgon@yahoo.ca>
+ Rudolph Froger <rfroger@estrate.nl>
Jorge Gajon <gajon@gajon.org>
gandalf@owca.info
Marc Garcia <marc.garcia@accopensys.com>
@@ -300,6 +301,7 @@ answer newbie questions, and generally made Django that much better:
Georgi Stanojevski <glisha@gmail.com>
Vasiliy Stavenko <stavenko@gmail.com>
Thomas Steinacher <http://www.eggdrop.ch/>
+ Johan C. Stöver <johan@nilling.nl>
nowell strite
Thomas Stromberg <tstromberg@google.com>
Sundance
@@ -327,6 +329,7 @@ answer newbie questions, and generally made Django that much better:
tt@gurgle.no
Amit Upadhyay
Geert Vanderkelen
+ I.S. van Oostveen <v.oostveen@idca.nl>
viestards.lists@gmail.com
George Vilches <gav@thataddress.com>
Vlado <vlado@labath.org>
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/ca/LC_MESSAGES/django.mo b/django/conf/locale/ca/LC_MESSAGES/django.mo
index fdba04abd2..a39a4551bb 100644
--- a/django/conf/locale/ca/LC_MESSAGES/django.mo
+++ b/django/conf/locale/ca/LC_MESSAGES/django.mo
Binary files differ
diff --git a/django/conf/locale/ca/LC_MESSAGES/django.po b/django/conf/locale/ca/LC_MESSAGES/django.po
index 9f561acc5b..2929ea114e 100644
--- a/django/conf/locale/ca/LC_MESSAGES/django.po
+++ b/django/conf/locale/ca/LC_MESSAGES/django.po
@@ -5,9 +5,9 @@ msgid ""
msgstr ""
"Project-Id-Version: django\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2007-11-05 17:33+0100\n"
-"PO-Revision-Date: 2007-11-05 17:34+0100\n"
-"Last-Translator: Marc Garcia <marc.garcia@accopensys.com>\n"
+"POT-Creation-Date: 2007-12-02 22:26+0100\n"
+"PO-Revision-Date: 2007-12-02 22:32+0100\n"
+"Last-Translator: Marc Fargas <telenieko@telenieko.com>\n"
"Language-Team: <es@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -194,7 +194,7 @@ msgstr "Xinés simplificat"
msgid "Traditional Chinese"
msgstr "Xinés tradicional"
-#: contrib/admin/filterspecs.py:42
+#: contrib/admin/filterspecs.py:44
#, python-format
msgid ""
"<h3>By %s:</h3>\n"
@@ -203,71 +203,71 @@ msgstr ""
"<h3>Per %s:</h3>\n"
"<ul>\n"
-#: contrib/admin/filterspecs.py:72 contrib/admin/filterspecs.py:90
-#: contrib/admin/filterspecs.py:145 contrib/admin/filterspecs.py:171
+#: contrib/admin/filterspecs.py:74 contrib/admin/filterspecs.py:92
+#: contrib/admin/filterspecs.py:147 contrib/admin/filterspecs.py:173
msgid "All"
msgstr "Tots"
-#: contrib/admin/filterspecs.py:111
+#: contrib/admin/filterspecs.py:113
msgid "Any date"
msgstr "Qualsevol data"
-#: contrib/admin/filterspecs.py:112
+#: contrib/admin/filterspecs.py:114
msgid "Today"
msgstr "Avui"
-#: contrib/admin/filterspecs.py:115
+#: contrib/admin/filterspecs.py:117
msgid "Past 7 days"
msgstr "Últims 7 dies"
-#: contrib/admin/filterspecs.py:117
+#: contrib/admin/filterspecs.py:119
msgid "This month"
msgstr "Aquest mes"
-#: contrib/admin/filterspecs.py:119
+#: contrib/admin/filterspecs.py:121
msgid "This year"
msgstr "Aquest any"
-#: contrib/admin/filterspecs.py:145 newforms/widgets.py:221
-#: oldforms/__init__.py:591
+#: contrib/admin/filterspecs.py:147 newforms/widgets.py:231
+#: oldforms/__init__.py:592
msgid "Yes"
msgstr "Si"
-#: contrib/admin/filterspecs.py:145 newforms/widgets.py:221
-#: oldforms/__init__.py:591
+#: contrib/admin/filterspecs.py:147 newforms/widgets.py:231
+#: oldforms/__init__.py:592
msgid "No"
msgstr "No"
-#: contrib/admin/filterspecs.py:152 newforms/widgets.py:221
-#: oldforms/__init__.py:591
+#: contrib/admin/filterspecs.py:154 newforms/widgets.py:231
+#: oldforms/__init__.py:592
msgid "Unknown"
msgstr "Desconegut"
-#: contrib/admin/models.py:17
+#: contrib/admin/models.py:18
msgid "action time"
msgstr "moment de l'acció"
-#: contrib/admin/models.py:20
+#: contrib/admin/models.py:21
msgid "object id"
msgstr "id del objecte"
-#: contrib/admin/models.py:21
+#: contrib/admin/models.py:22
msgid "object repr"
msgstr "'repr' de l'objecte"
-#: contrib/admin/models.py:22
+#: contrib/admin/models.py:23
msgid "action flag"
msgstr "marca de l'acció"
-#: contrib/admin/models.py:23
+#: contrib/admin/models.py:24
msgid "change message"
msgstr "missatge del canvi"
-#: contrib/admin/models.py:26
+#: contrib/admin/models.py:27
msgid "log entry"
msgstr "entrada del registre"
-#: contrib/admin/models.py:27
+#: contrib/admin/models.py:28
msgid "log entries"
msgstr "entrades del registre"
@@ -469,7 +469,7 @@ msgid "Password:"
msgstr "Contrasenya:"
#: contrib/admin/templates/admin/login.html:25
-#: contrib/admin/views/decorators.py:24
+#: contrib/admin/views/decorators.py:25
msgid "Log in"
msgstr "Iniciar sessió"
@@ -769,17 +769,17 @@ msgstr "Actualment:"
msgid "Change:"
msgstr "Modificar:"
-#: contrib/admin/templatetags/admin_list.py:254
+#: contrib/admin/templatetags/admin_list.py:257
msgid "All dates"
msgstr "Totes les dates"
-#: contrib/admin/views/auth.py:20 contrib/admin/views/main.py:264
+#: contrib/admin/views/auth.py:20 contrib/admin/views/main.py:267
#, python-format
msgid "The %(name)s \"%(obj)s\" was added successfully."
msgstr "El/la %(name)s \"%(obj)s\".ha estat agregat/da amb èxit."
-#: contrib/admin/views/auth.py:25 contrib/admin/views/main.py:268
-#: contrib/admin/views/main.py:354
+#: contrib/admin/views/auth.py:25 contrib/admin/views/main.py:271
+#: contrib/admin/views/main.py:356
msgid "You may edit it again below."
msgstr "Pot editar-lo de nou abaix."
@@ -796,7 +796,7 @@ msgstr "Canvi de clau exitós"
msgid "Change password: %s"
msgstr "Canviar clau: %s"
-#: contrib/admin/views/decorators.py:10 contrib/auth/forms.py:60
+#: contrib/admin/views/decorators.py:11 contrib/auth/forms.py:60
msgid ""
"Please enter a correct username and password. Note that both fields are case-"
"sensitive."
@@ -804,7 +804,7 @@ msgstr ""
"Si us plau, introdueixi un nom d'usuari i contrasenya vàlids. Tingui en "
"compte que tots dos camps son sensibles a majúscules i minúscules."
-#: contrib/admin/views/decorators.py:62
+#: contrib/admin/views/decorators.py:63
msgid ""
"Please log in again, because your session has expired. Don't worry: Your "
"submission has been saved."
@@ -812,7 +812,7 @@ msgstr ""
"Si us plau, identifiquis de nou doncs la seva sessió ha expirat. No es "
"preocupi, el seu enviament està emmagatzemat."
-#: contrib/admin/views/decorators.py:69
+#: contrib/admin/views/decorators.py:70
msgid ""
"Looks like your browser isn't configured to accept cookies. Please enable "
"cookies, reload this page, and try again."
@@ -821,247 +821,247 @@ msgstr ""
"'cookies' (galetes). Si us plau, habiliti les 'cookies', recarregui aquesta "
"pàgina i provi-ho de nou. "
-#: contrib/admin/views/decorators.py:83
+#: contrib/admin/views/decorators.py:84
msgid "Usernames cannot contain the '@' character."
msgstr "Els noms d'usuari no poden contenir el caracter '@'."
-#: contrib/admin/views/decorators.py:85
+#: contrib/admin/views/decorators.py:86
#, python-format
msgid "Your e-mail address is not your username. Try '%s' instead."
msgstr ""
"La seva adreça de correu no és el seu nom d'usuari. Provi '%s' en tot cas."
-#: contrib/admin/views/doc.py:47 contrib/admin/views/doc.py:49
-#: contrib/admin/views/doc.py:51
+#: contrib/admin/views/doc.py:48 contrib/admin/views/doc.py:50
+#: contrib/admin/views/doc.py:52
msgid "tag:"
msgstr "etiqueta:"
-#: contrib/admin/views/doc.py:78 contrib/admin/views/doc.py:80
-#: contrib/admin/views/doc.py:82
+#: contrib/admin/views/doc.py:79 contrib/admin/views/doc.py:81
+#: contrib/admin/views/doc.py:83
msgid "filter:"
msgstr "filtre:"
-#: contrib/admin/views/doc.py:136 contrib/admin/views/doc.py:138
-#: contrib/admin/views/doc.py:140
+#: contrib/admin/views/doc.py:137 contrib/admin/views/doc.py:139
+#: contrib/admin/views/doc.py:141
msgid "view:"
msgstr "vista:"
-#: contrib/admin/views/doc.py:165
+#: contrib/admin/views/doc.py:166
#, python-format
msgid "App %r not found"
msgstr "La aplicació %r no s'ha pogut trobar"
-#: contrib/admin/views/doc.py:172
+#: contrib/admin/views/doc.py:173
#, python-format
msgid "Model %(name)r not found in app %(label)r"
msgstr "El model %(name)r no s'ha trobat en la aplicació %(label)r"
-#: contrib/admin/views/doc.py:184
+#: contrib/admin/views/doc.py:185
#, python-format
msgid "the related `%(label)s.%(type)s` object"
msgstr "el objecte relacionat `%(label)s.%(type)s`"
-#: contrib/admin/views/doc.py:184 contrib/admin/views/doc.py:206
-#: contrib/admin/views/doc.py:220 contrib/admin/views/doc.py:225
+#: contrib/admin/views/doc.py:185 contrib/admin/views/doc.py:207
+#: contrib/admin/views/doc.py:221 contrib/admin/views/doc.py:226
msgid "model:"
msgstr "model:"
-#: contrib/admin/views/doc.py:215
+#: contrib/admin/views/doc.py:216
#, python-format
msgid "related `%(label)s.%(name)s` objects"
msgstr "objectes relacionats `%(label)s.%(name)s`"
-#: contrib/admin/views/doc.py:220
+#: contrib/admin/views/doc.py:221
#, python-format
msgid "all %s"
msgstr "tots %s"
-#: contrib/admin/views/doc.py:225
+#: contrib/admin/views/doc.py:226
#, python-format
msgid "number of %s"
msgstr "nombre de %s"
-#: contrib/admin/views/doc.py:230
+#: contrib/admin/views/doc.py:231
#, python-format
msgid "Fields on %s objects"
msgstr "Camps en objectes %s"
-#: contrib/admin/views/doc.py:292 contrib/admin/views/doc.py:303
-#: contrib/admin/views/doc.py:305 contrib/admin/views/doc.py:311
-#: contrib/admin/views/doc.py:312 contrib/admin/views/doc.py:314
+#: contrib/admin/views/doc.py:293 contrib/admin/views/doc.py:304
+#: contrib/admin/views/doc.py:306 contrib/admin/views/doc.py:312
+#: contrib/admin/views/doc.py:313 contrib/admin/views/doc.py:315
msgid "Integer"
msgstr "Enter"
-#: contrib/admin/views/doc.py:293
+#: contrib/admin/views/doc.py:294
msgid "Boolean (Either True or False)"
msgstr "Booleà (Verdader o Fals)"
-#: contrib/admin/views/doc.py:294 contrib/admin/views/doc.py:313
+#: contrib/admin/views/doc.py:295 contrib/admin/views/doc.py:314
#, python-format
msgid "String (up to %(max_length)s)"
msgstr "Cadena (de fins a %(max_length)s)"
-#: contrib/admin/views/doc.py:295
+#: contrib/admin/views/doc.py:296
msgid "Comma-separated integers"
msgstr "Enters separats per comes"
-#: contrib/admin/views/doc.py:296
+#: contrib/admin/views/doc.py:297
msgid "Date (without time)"
msgstr "Data (sense hora)"
-#: contrib/admin/views/doc.py:297
+#: contrib/admin/views/doc.py:298
msgid "Date (with time)"
msgstr "Data (amb hora)"
-#: contrib/admin/views/doc.py:298
+#: contrib/admin/views/doc.py:299
msgid "Decimal number"
msgstr "Número decimal"
-#: contrib/admin/views/doc.py:299
+#: contrib/admin/views/doc.py:300
msgid "E-mail address"
msgstr "Adreça de correu electrònic"
-#: contrib/admin/views/doc.py:300 contrib/admin/views/doc.py:301
-#: contrib/admin/views/doc.py:304
+#: contrib/admin/views/doc.py:301 contrib/admin/views/doc.py:302
+#: contrib/admin/views/doc.py:305
msgid "File path"
msgstr "Ruta del fitxer"
-#: contrib/admin/views/doc.py:302
+#: contrib/admin/views/doc.py:303
msgid "Floating point number"
msgstr "Número amb punt de coma flotant"
-#: contrib/admin/views/doc.py:306 contrib/comments/models.py:85
+#: contrib/admin/views/doc.py:307 contrib/comments/models.py:85
msgid "IP address"
msgstr "Adreça IP"
-#: contrib/admin/views/doc.py:308
+#: contrib/admin/views/doc.py:309
msgid "Boolean (Either True, False or None)"
msgstr "Booleà (Verdader, Fals o 'None' (cap))"
-#: contrib/admin/views/doc.py:309
+#: contrib/admin/views/doc.py:310
msgid "Relation to parent model"
msgstr "Relació amb el model pare"
-#: contrib/admin/views/doc.py:310
+#: contrib/admin/views/doc.py:311
msgid "Phone number"
msgstr "Número de telèfon"
-#: contrib/admin/views/doc.py:315
+#: contrib/admin/views/doc.py:316
msgid "Text"
msgstr "Texte"
-#: contrib/admin/views/doc.py:316
+#: contrib/admin/views/doc.py:317
msgid "Time"
msgstr "Hora"
-#: contrib/admin/views/doc.py:317 contrib/flatpages/models.py:7
+#: contrib/admin/views/doc.py:318 contrib/flatpages/models.py:7
msgid "URL"
msgstr "URL"
-#: contrib/admin/views/doc.py:318
+#: contrib/admin/views/doc.py:319
msgid "U.S. state (two uppercase letters)"
msgstr "Estat dels E.U.A. (dos lletres majúscules)"
-#: contrib/admin/views/doc.py:319
+#: contrib/admin/views/doc.py:320
msgid "XML text"
msgstr "Texte XML"
-#: contrib/admin/views/doc.py:345
+#: contrib/admin/views/doc.py:346
#, python-format
msgid "%s does not appear to be a urlpattern object"
msgstr "%s no sembla ser un objecte 'urlpattern'"
-#: contrib/admin/views/main.py:230
+#: contrib/admin/views/main.py:233
msgid "Site administration"
msgstr "Lloc administratiu"
-#: contrib/admin/views/main.py:278 contrib/admin/views/main.py:363
+#: contrib/admin/views/main.py:280 contrib/admin/views/main.py:365
#, python-format
msgid "You may add another %s below."
msgstr "Pot afegir un altre %s a baix."
-#: contrib/admin/views/main.py:296
+#: contrib/admin/views/main.py:298
#, python-format
msgid "Add %s"
msgstr "Afegir %s"
-#: contrib/admin/views/main.py:342
+#: contrib/admin/views/main.py:344
#, python-format
msgid "Added %s."
msgstr "Agregat %s."
-#: contrib/admin/views/main.py:342 contrib/admin/views/main.py:344
-#: contrib/admin/views/main.py:346 core/validators.py:283
+#: contrib/admin/views/main.py:344 contrib/admin/views/main.py:346
+#: contrib/admin/views/main.py:348 core/validators.py:283
#: db/models/manipulators.py:309
msgid "and"
msgstr "i"
-#: contrib/admin/views/main.py:344
+#: contrib/admin/views/main.py:346
#, python-format
msgid "Changed %s."
msgstr "Modificat %s."
-#: contrib/admin/views/main.py:346
+#: contrib/admin/views/main.py:348
#, python-format
msgid "Deleted %s."
msgstr "Eliminat %s."
-#: contrib/admin/views/main.py:349
+#: contrib/admin/views/main.py:351
msgid "No fields changed."
msgstr "Cap camp canviat."
-#: contrib/admin/views/main.py:352
+#: contrib/admin/views/main.py:354
#, python-format
msgid "The %(name)s \"%(obj)s\" was changed successfully."
msgstr "S'ha modificat amb èxist el/la %(name)s \"%(obj)s."
-#: contrib/admin/views/main.py:360
+#: contrib/admin/views/main.py:362
#, python-format
msgid ""
"The %(name)s \"%(obj)s\" was added successfully. You may edit it again below."
msgstr ""
"S'ha agregat amb èxit el/la %(name)s \"%(obj)s\". Pot editar-lo de nou abaix."
-#: contrib/admin/views/main.py:398
+#: contrib/admin/views/main.py:400
#, python-format
msgid "Change %s"
msgstr "Modificar %s"
-#: contrib/admin/views/main.py:483
+#: contrib/admin/views/main.py:487
#, python-format
msgid "One or more %(fieldname)s in %(name)s: %(obj)s"
msgstr "Un o més %(fieldname)s en %(name)s: %(obj)s"
-#: contrib/admin/views/main.py:488
+#: contrib/admin/views/main.py:492
#, python-format
msgid "One or more %(fieldname)s in %(name)s:"
msgstr "Un o més %(fieldname)s en %(name)s:"
-#: contrib/admin/views/main.py:520
+#: contrib/admin/views/main.py:524
#, python-format
msgid "The %(name)s \"%(obj)s\" was deleted successfully."
msgstr "El/la %(name)s \"%(obj)s\".ha estat eliminat amb èxit."
-#: contrib/admin/views/main.py:523
+#: contrib/admin/views/main.py:527
msgid "Are you sure?"
msgstr "Està segur?"
-#: contrib/admin/views/main.py:545
+#: contrib/admin/views/main.py:549
#, python-format
msgid "Change history: %s"
msgstr "Modificar històric: %s"
-#: contrib/admin/views/main.py:579
+#: contrib/admin/views/main.py:583
#, python-format
msgid "Select %s"
msgstr "Seleccioni %s"
-#: contrib/admin/views/main.py:579
+#: contrib/admin/views/main.py:583
#, python-format
msgid "Select %s to change"
msgstr "Seleccioni %s per modificar"
-#: contrib/admin/views/main.py:780
+#: contrib/admin/views/main.py:784
msgid "Database error"
msgstr "Error de/en la base de dades"
@@ -1622,72 +1622,72 @@ msgstr "n"
msgid "rd"
msgstr "r"
-#: contrib/humanize/templatetags/humanize.py:50
+#: contrib/humanize/templatetags/humanize.py:52
#, python-format
msgid "%(value).1f million"
msgid_plural "%(value).1f million"
msgstr[0] "%(value).1f milió"
msgstr[1] "%(value).1f milions"
-#: contrib/humanize/templatetags/humanize.py:53
+#: contrib/humanize/templatetags/humanize.py:55
#, python-format
msgid "%(value).1f billion"
msgid_plural "%(value).1f billion"
msgstr[0] "%(value).1f bilió"
msgstr[1] "%(value).1f bilions"
-#: contrib/humanize/templatetags/humanize.py:56
+#: contrib/humanize/templatetags/humanize.py:58
#, python-format
msgid "%(value).1f trillion"
msgid_plural "%(value).1f trillion"
msgstr[0] "%(value).1f trilió"
msgstr[1] "%(value).1f trilions"
-#: contrib/humanize/templatetags/humanize.py:71
+#: contrib/humanize/templatetags/humanize.py:74
msgid "one"
msgstr "un"
-#: contrib/humanize/templatetags/humanize.py:71
+#: contrib/humanize/templatetags/humanize.py:74
msgid "two"
msgstr "dos"
-#: contrib/humanize/templatetags/humanize.py:71
+#: contrib/humanize/templatetags/humanize.py:74
msgid "three"
msgstr "tres"
-#: contrib/humanize/templatetags/humanize.py:71
+#: contrib/humanize/templatetags/humanize.py:74
msgid "four"
msgstr "cuatre"
-#: contrib/humanize/templatetags/humanize.py:71
+#: contrib/humanize/templatetags/humanize.py:74
msgid "five"
msgstr "cinc"
-#: contrib/humanize/templatetags/humanize.py:71
+#: contrib/humanize/templatetags/humanize.py:74
msgid "six"
msgstr "sis"
-#: contrib/humanize/templatetags/humanize.py:71
+#: contrib/humanize/templatetags/humanize.py:74
msgid "seven"
msgstr "set"
-#: contrib/humanize/templatetags/humanize.py:71
+#: contrib/humanize/templatetags/humanize.py:74
msgid "eight"
msgstr "vuit"
-#: contrib/humanize/templatetags/humanize.py:71
+#: contrib/humanize/templatetags/humanize.py:74
msgid "nine"
msgstr "nou"
-#: contrib/humanize/templatetags/humanize.py:90
+#: contrib/humanize/templatetags/humanize.py:94
msgid "today"
msgstr "avui"
-#: contrib/humanize/templatetags/humanize.py:92
+#: contrib/humanize/templatetags/humanize.py:96
msgid "tomorrow"
msgstr "demà"
-#: contrib/humanize/templatetags/humanize.py:94
+#: contrib/humanize/templatetags/humanize.py:98
msgid "yesterday"
msgstr "ahir"
@@ -3024,6 +3024,50 @@ msgstr ""
"Introdueixi un número vàlid de la Seguretat Social dels E.U.A. en el format "
"XXX-XX-XXXX."
+#: contrib/localflavor/za/forms.py:22
+msgid "Enter a valid South African ID number"
+msgstr "Introdueixi un número d'Identitat Sud Africà valid"
+
+#: contrib/localflavor/za/forms.py:57
+msgid "Enter a valid South African postal code"
+msgstr "Introdueixi un codi postal Sud Africà vàlid."
+
+#: contrib/localflavor/za/za_provinces.py:4
+msgid "Eastern Cape"
+msgstr "Eastern Cape"
+
+#: contrib/localflavor/za/za_provinces.py:5
+msgid "Free State"
+msgstr "Free State"
+
+#: contrib/localflavor/za/za_provinces.py:6
+msgid "Gauteng"
+msgstr "Gauteng"
+
+#: contrib/localflavor/za/za_provinces.py:7
+msgid "KwaZulu-Natal"
+msgstr "KwaZulu-Natal"
+
+#: contrib/localflavor/za/za_provinces.py:8
+msgid "Limpopo"
+msgstr "Limpopo"
+
+#: contrib/localflavor/za/za_provinces.py:9
+msgid "Mpumalanga"
+msgstr "Mpumalanga"
+
+#: contrib/localflavor/za/za_provinces.py:10
+msgid "Northern Cape"
+msgstr "Northern Cape"
+
+#: contrib/localflavor/za/za_provinces.py:11
+msgid "North West"
+msgstr "North West"
+
+#: contrib/localflavor/za/za_provinces.py:12
+msgid "Western Cape"
+msgstr "Western Cape"
+
#: contrib/redirects/models.py:7
msgid "redirect from"
msgstr "redreçar des de"
@@ -3056,23 +3100,23 @@ msgstr "redreçament"
msgid "redirects"
msgstr "redreçaments"
-#: contrib/sessions/models.py:80
+#: contrib/sessions/models.py:46
msgid "session key"
msgstr "clau de la sessió"
-#: contrib/sessions/models.py:81
+#: contrib/sessions/models.py:47
msgid "session data"
msgstr "dades de la sessió"
-#: contrib/sessions/models.py:82
+#: contrib/sessions/models.py:48
msgid "expire date"
msgstr "data de caducitat"
-#: contrib/sessions/models.py:87
+#: contrib/sessions/models.py:53
msgid "session"
msgstr "sessió"
-#: contrib/sessions/models.py:88
+#: contrib/sessions/models.py:54
msgid "sessions"
msgstr "sessions"
@@ -3141,7 +3185,7 @@ msgstr "No s'admeten caracters no numèrics."
msgid "This value can't be comprised solely of digits."
msgstr "Aquest valor no pot contenir només dígits."
-#: core/validators.py:128 newforms/fields.py:157
+#: core/validators.py:128 newforms/fields.py:151
msgid "Enter a whole number."
msgstr "Introdueixi un número sencer."
@@ -3170,17 +3214,17 @@ msgstr "Introdueixi una hora vàlida en el format HH:MM."
msgid "Enter a valid date/time in YYYY-MM-DD HH:MM format."
msgstr "Introdueixi un data/hora vàlida en format YYYY-MM-DD HH:MM."
-#: core/validators.py:170 newforms/fields.py:408
+#: core/validators.py:170 newforms/fields.py:402
msgid "Enter a valid e-mail address."
msgstr "Introdueixi una adreça de correu vàlida."
-#: core/validators.py:182 core/validators.py:474 newforms/fields.py:438
-#: oldforms/__init__.py:686
+#: core/validators.py:182 core/validators.py:474 newforms/fields.py:432
+#: oldforms/__init__.py:687
msgid "No file was submitted. Check the encoding type on the form."
msgstr ""
"No s'ha enviat cap fitxer. Comprovi el tipus de codificació del formulari."
-#: core/validators.py:193 newforms/fields.py:462
+#: core/validators.py:193 newforms/fields.py:456
msgid ""
"Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image."
@@ -3431,7 +3475,7 @@ msgstr "Ja existeix %(optname)s amb auqest %(fieldname)s."
#: db/models/fields/__init__.py:161 db/models/fields/__init__.py:318
#: db/models/fields/__init__.py:735 db/models/fields/__init__.py:746
-#: newforms/fields.py:45 newforms/models.py:211 oldforms/__init__.py:373
+#: newforms/fields.py:45 oldforms/__init__.py:374
msgid "This field is required."
msgstr "Aquest camp és obligatori."
@@ -3484,150 +3528,150 @@ msgstr[1] ""
msgid "Enter a valid value."
msgstr "Introdueixi un valor vàlid."
-#: newforms/fields.py:129
+#: newforms/fields.py:123
#, python-format
msgid "Ensure this value has at most %(max)d characters (it has %(length)d)."
msgstr ""
"Asseguris de que el valor té com a màxim %(max)d caràcters (en té %(length)"
"d)."
-#: newforms/fields.py:130
+#: newforms/fields.py:124
#, python-format
msgid "Ensure this value has at least %(min)d characters (it has %(length)d)."
msgstr ""
"Asseguris de que el valor té com a mínim %(min)d caràcters (en té %(length)"
"d)."
-#: newforms/fields.py:158 newforms/fields.py:187 newforms/fields.py:216
+#: newforms/fields.py:152 newforms/fields.py:181 newforms/fields.py:210
#, python-format
msgid "Ensure this value is less than or equal to %s."
msgstr "Aquest valor ha de ser menor o igual a %s."
-#: newforms/fields.py:159 newforms/fields.py:188 newforms/fields.py:217
+#: newforms/fields.py:153 newforms/fields.py:182 newforms/fields.py:211
#, python-format
msgid "Ensure this value is greater than or equal to %s."
msgstr "Asseguris de que aquest valor sigui superior o igual a %s."
-#: newforms/fields.py:186 newforms/fields.py:215
+#: newforms/fields.py:180 newforms/fields.py:209
msgid "Enter a number."
msgstr "Introdueixi un número."
-#: newforms/fields.py:218
+#: newforms/fields.py:212
#, python-format
msgid "Ensure that there are no more than %s digits in total."
msgstr "Asseguris de que no hi ha més de %s dígits en total."
-#: newforms/fields.py:219
+#: newforms/fields.py:213
#, python-format
msgid "Ensure that there are no more than %s decimal places."
msgstr "Asseguris de que no hi ha més de %s decimals."
-#: newforms/fields.py:220
+#: newforms/fields.py:214
#, python-format
msgid "Ensure that there are no more than %s digits before the decimal point."
msgstr "Asseguris de que no hia ha més de %s dígits decimals."
-#: newforms/fields.py:268 newforms/fields.py:724
+#: newforms/fields.py:262 newforms/fields.py:719
msgid "Enter a valid date."
msgstr "Introdueixi una data vàlida."
-#: newforms/fields.py:301 newforms/fields.py:725
+#: newforms/fields.py:295 newforms/fields.py:720
msgid "Enter a valid time."
msgstr "Introdueixi una hora vàlida."
-#: newforms/fields.py:340
+#: newforms/fields.py:334
msgid "Enter a valid date/time."
msgstr "Introdueixi una data/hora vàlides."
-#: newforms/fields.py:439
+#: newforms/fields.py:433
msgid "No file was submitted."
msgstr "No s'ha enviat cap fitxer."
-#: newforms/fields.py:440 oldforms/__init__.py:688
+#: newforms/fields.py:434 oldforms/__init__.py:689
msgid "The submitted file is empty."
msgstr "El fitxer enviat està buit."
-#: newforms/fields.py:498
+#: newforms/fields.py:492
msgid "Enter a valid URL."
msgstr "Introdueixi una URL vàlida."
-#: newforms/fields.py:499
+#: newforms/fields.py:493
msgid "This URL appears to be a broken link."
msgstr "Aquesta URL sembla ser un enllaç trencat."
-#: newforms/fields.py:560 newforms/models.py:194
+#: newforms/fields.py:555 newforms/models.py:155
msgid "Select a valid choice. That choice is not one of the available choices."
msgstr ""
"Esculli una opció vàlida; Aquesta opció no és una de les opcions disponibles."
-#: newforms/fields.py:599
+#: newforms/fields.py:594
#, python-format
msgid "Select a valid choice. %(value)s is not one of the available choices."
msgstr "Esculli una opció vàlida. %(value)s no és una de les opcions vàlides."
-#: newforms/fields.py:600 newforms/fields.py:662 newforms/models.py:215
+#: newforms/fields.py:595 newforms/fields.py:657 newforms/models.py:215
msgid "Enter a list of values."
msgstr "Introdueixi una llista de valors."
-#: newforms/fields.py:753
+#: newforms/fields.py:748
msgid "Enter a valid IPv4 address."
msgstr "Introdueixi una adreça IPv4 vàlida."
-#: newforms/models.py:221
+#: newforms/models.py:216
#, python-format
msgid "Select a valid choice. %s is not one of the available choices."
msgstr "Esculli una opció vàlida; %s' no és una de les opcions vàlides."
-#: oldforms/__init__.py:408
+#: oldforms/__init__.py:409
#, python-format
msgid "Ensure your text is less than %s character."
msgid_plural "Ensure your text is less than %s characters."
msgstr[0] "Asseguris de que el seu texte té menys de %s caracter."
msgstr[1] "Asseguris de que el seu texte té menys de %s caracters."
-#: oldforms/__init__.py:413
+#: oldforms/__init__.py:414
msgid "Line breaks are not allowed here."
msgstr "No es permeten salts de línia."
-#: oldforms/__init__.py:511 oldforms/__init__.py:585 oldforms/__init__.py:624
+#: oldforms/__init__.py:512 oldforms/__init__.py:586 oldforms/__init__.py:625
#, python-format
msgid "Select a valid choice; '%(data)s' is not in %(choices)s."
msgstr "Esculli una opció vàlida; %(data)s' no està dintre de %(choices)s."
-#: oldforms/__init__.py:744
+#: oldforms/__init__.py:745
msgid "Enter a whole number between -32,768 and 32,767."
msgstr "Introdueixi un número enter entre -32,768 i 32,767."
-#: oldforms/__init__.py:754
+#: oldforms/__init__.py:755
msgid "Enter a positive number."
msgstr "Introdueixi un número positiu."
-#: oldforms/__init__.py:764
+#: oldforms/__init__.py:765
msgid "Enter a whole number between 0 and 32,767."
msgstr "Introdueixi un número entre 0 i 32,767."
-#: template/defaultfilters.py:555
+#: template/defaultfilters.py:658
msgid "yes,no,maybe"
msgstr "si,no,potser"
-#: template/defaultfilters.py:585
+#: template/defaultfilters.py:689
#, python-format
msgid "%(size)d byte"
msgid_plural "%(size)d bytes"
msgstr[0] "%(size)d byte"
msgstr[1] "%(size)d bytes"
-#: template/defaultfilters.py:587
+#: template/defaultfilters.py:691
#, python-format
msgid "%.1f KB"
msgstr "%.1f KB"
-#: template/defaultfilters.py:589
+#: template/defaultfilters.py:693
#, python-format
msgid "%.1f MB"
msgstr "%.1f MB"
-#: template/defaultfilters.py:590
+#: template/defaultfilters.py:694
#, python-format
msgid "%.1f GB"
msgstr "%.1f GB"
@@ -3890,23 +3934,23 @@ msgstr "%(number)d %(type)s"
msgid ", %(number)d %(type)s"
msgstr ", %(number)d %(type)s"
-#: utils/translation/trans_real.py:395
+#: utils/translation/trans_real.py:399
msgid "DATE_FORMAT"
msgstr "F j, Y"
-#: utils/translation/trans_real.py:396
+#: utils/translation/trans_real.py:400
msgid "DATETIME_FORMAT"
msgstr "F j, Y, H:i"
-#: utils/translation/trans_real.py:397
+#: utils/translation/trans_real.py:401
msgid "TIME_FORMAT"
msgstr "H:i"
-#: utils/translation/trans_real.py:413
+#: utils/translation/trans_real.py:417
msgid "YEAR_MONTH_FORMAT"
msgstr "j de/d' F del Y"
-#: utils/translation/trans_real.py:414
+#: utils/translation/trans_real.py:418
msgid "MONTH_DAY_FORMAT"
msgstr "j de/d' F del Y"
@@ -3924,43 +3968,3 @@ msgstr "El/La %(verbose_name)s s'ha actualtzat amb èxit."
#, python-format
msgid "The %(verbose_name)s was deleted."
msgstr "El %(verbose_name)s s'ha eliminat."
-
-#~ msgid ""
-#~ "This comment was posted by a user who has posted fewer than %(count)s "
-#~ "comment:\n"
-#~ "\n"
-#~ "%(text)sThis comment was posted by a user who has posted fewer than %"
-#~ "(count)s comments:\n"
-#~ "\n"
-#~ "%(text)s"
-#~ msgstr ""
-#~ "Aquest comentari el va enviar un usuari que ha enviat menys de %(count)s "
-#~ "comentari:\n"
-#~ "\n"
-#~ "%(text)sAquest comentari el va enviar un usuari que ha enviat menys de %"
-#~ "(count)s comentaris:\n"
-#~ "\n"
-#~ "%(text)s"
-
-#~ msgid "AnonymousUser"
-#~ msgstr "AnonymousUser"
-
-#~ msgid ""
-#~ "Please enter a valid decimal number with a whole part of at most %s digit."
-#~ "Please enter a valid decimal number with a whole part of at most %s "
-#~ "digits."
-#~ msgstr ""
-#~ "Si us plau, introdueixi un número decimal vàlid amb la part entera amb "
-#~ "com a màxim %s dígit.Si us plau, introdueixi un número decimal vàlid amb "
-#~ "la part entera amb com a màxim %s dígits."
-
-#~ msgid ""
-#~ "Please enter a valid decimal number with at most %s decimal place.Please "
-#~ "enter a valid decimal number with at most %s decimal places."
-#~ msgstr ""
-#~ "Si us plau, introdueixi un número decimal vàlid amb no més de %s dígit en "
-#~ "la part decimal.Si us plau, introdueixi un número decimal vàlid amb no "
-#~ "més de %s dígits en la part decimal."
-
-#~ msgid "%d milliseconds"
-#~ msgstr "%d milisegons"
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/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py
index a4e6269b6f..e45b6ed0bb 100644
--- a/django/contrib/admin/templatetags/admin_list.py
+++ b/django/contrib/admin/templatetags/admin_list.py
@@ -114,7 +114,7 @@ def result_headers(cl):
yield {"text": header,
"sortable": True,
"url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
- "class_attrib": (th_classes and ' class="%s"' % ' '.join(th_classes) or '')}
+ "class_attrib": mark_safe(th_classes and ' class="%s"' % ' '.join(th_classes) or '')}
def _boolean_icon(field_val):
BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'}
diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py
index 33f92dc854..4b1590264b 100644
--- a/django/contrib/auth/models.py
+++ b/django/contrib/auth/models.py
@@ -322,7 +322,7 @@ class AnonymousUser(object):
id = None
username = ''
is_staff = False
- is_active = True
+ is_active = False
is_superuser = False
_groups = EmptyManager()
_user_permissions = EmptyManager()
diff --git a/django/contrib/auth/tests.py b/django/contrib/auth/tests.py
index 329049c546..d369ac524c 100644
--- a/django/contrib/auth/tests.py
+++ b/django/contrib/auth/tests.py
@@ -16,9 +16,21 @@ False
>>> u2 = User.objects.create_user('testuser2', 'test2@example.com')
>>> u2.has_usable_password()
False
+
+>>> u.is_authenticated()
+True
+>>> u.is_staff
+False
+>>> u.is_active
+True
+
>>> a = AnonymousUser()
+>>> a.is_authenticated()
+False
>>> a.is_staff
False
+>>> a.is_active
+False
>>> a.groups.all()
[]
>>> a.user_permissions.all()
diff --git a/django/contrib/contenttypes/generic.py b/django/contrib/contenttypes/generic.py
index 63c44ab285..8d32406965 100644
--- a/django/contrib/contenttypes/generic.py
+++ b/django/contrib/contenttypes/generic.py
@@ -16,18 +16,18 @@ class GenericForeignKey(object):
Provides a generic relation to any object through content-type/object-id
fields.
"""
-
+
def __init__(self, ct_field="content_type", fk_field="object_id"):
self.ct_field = ct_field
self.fk_field = fk_field
-
+
def contribute_to_class(self, cls, name):
- # Make sure the fields exist (these raise FieldDoesNotExist,
+ # Make sure the fields exist (these raise FieldDoesNotExist,
# which is a fine error to raise here)
self.name = name
self.model = cls
self.cache_attr = "_%s_cache" % name
-
+
# For some reason I don't totally understand, using weakrefs here doesn't work.
dispatcher.connect(self.instance_pre_init, signal=signals.pre_init, sender=cls, weak=False)
@@ -35,18 +35,18 @@ class GenericForeignKey(object):
setattr(cls, name, self)
def instance_pre_init(self, signal, sender, args, kwargs):
- # Handle initalizing an object with the generic FK instaed of
- # content-type/object-id fields.
+ # Handle initalizing an object with the generic FK instaed of
+ # content-type/object-id fields.
if self.name in kwargs:
value = kwargs.pop(self.name)
kwargs[self.ct_field] = self.get_content_type(value)
kwargs[self.fk_field] = value._get_pk_val()
-
+
def get_content_type(self, obj):
# Convenience function using get_model avoids a circular import when using this model
ContentType = get_model("contenttypes", "contenttype")
return ContentType.objects.get_for_model(obj)
-
+
def __get__(self, instance, instance_type=None):
if instance is None:
raise AttributeError, u"%s must be accessed via instance" % self.name
@@ -77,21 +77,21 @@ class GenericForeignKey(object):
setattr(instance, self.ct_field, ct)
setattr(instance, self.fk_field, fk)
setattr(instance, self.cache_attr, value)
-
+
class GenericRelation(RelatedField, Field):
"""Provides an accessor to generic related objects (i.e. comments)"""
def __init__(self, to, **kwargs):
kwargs['verbose_name'] = kwargs.get('verbose_name', None)
- kwargs['rel'] = GenericRel(to,
+ kwargs['rel'] = GenericRel(to,
related_name=kwargs.pop('related_name', None),
limit_choices_to=kwargs.pop('limit_choices_to', None),
symmetrical=kwargs.pop('symmetrical', True))
-
+
# Override content-type/object-id field names on the related class
self.object_id_field_name = kwargs.pop("object_id_field", "object_id")
- self.content_type_field_name = kwargs.pop("content_type_field", "content_type")
-
+ self.content_type_field_name = kwargs.pop("content_type_field", "content_type")
+
kwargs['blank'] = True
kwargs['editable'] = False
kwargs['serialize'] = False
@@ -116,9 +116,9 @@ class GenericRelation(RelatedField, Field):
def m2m_column_name(self):
return self.object_id_field_name
-
+
def m2m_reverse_name(self):
- return self.object_id_field_name
+ return self.model._meta.pk.column
def contribute_to_class(self, cls, name):
super(GenericRelation, self).contribute_to_class(cls, name)
@@ -131,7 +131,7 @@ class GenericRelation(RelatedField, Field):
def contribute_to_related_class(self, cls, related):
pass
-
+
def set_attributes_from_rel(self):
pass
@@ -142,7 +142,7 @@ class GenericRelation(RelatedField, Field):
# Since we're simulating a ManyToManyField, in effect, best return the
# same db_type as well.
return None
-
+
class ReverseGenericRelatedObjectsDescriptor(object):
"""
This class provides the functionality that makes the related-object
@@ -198,12 +198,12 @@ def create_generic_related_manager(superclass):
Factory function for a manager that subclasses 'superclass' (which is a
Manager) and adds behavior for generic related objects.
"""
-
+
class GenericRelatedObjectManager(superclass):
def __init__(self, model=None, core_filters=None, instance=None, symmetrical=None,
join_table=None, source_col_name=None, target_col_name=None, content_type=None,
content_type_field_name=None, object_id_field_name=None):
-
+
super(GenericRelatedObjectManager, self).__init__()
self.core_filters = core_filters or {}
self.model = model
@@ -217,10 +217,10 @@ def create_generic_related_manager(superclass):
self.content_type_field_name = content_type_field_name
self.object_id_field_name = object_id_field_name
self.pk_val = self.instance._get_pk_val()
-
+
def get_query_set(self):
query = {
- '%s__pk' % self.content_type_field_name : self.content_type.id,
+ '%s__pk' % self.content_type_field_name : self.content_type.id,
'%s__exact' % self.object_id_field_name : self.pk_val,
}
return superclass.get_query_set(self).filter(**query)
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..daeff58acf 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,17 +63,17 @@ 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).
@@ -74,24 +84,28 @@ class CASocialInsuranceNumberField(Field):
* 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-XXXX 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/contrib/sessions/backends/file.py b/django/contrib/sessions/backends/file.py
index 221db5cc60..a8c3c69b10 100644
--- a/django/contrib/sessions/backends/file.py
+++ b/django/contrib/sessions/backends/file.py
@@ -1,14 +1,23 @@
import os
+import tempfile
from django.conf import settings
from django.contrib.sessions.backends.base import SessionBase
-from django.core.exceptions import SuspiciousOperation
+from django.core.exceptions import SuspiciousOperation, ImproperlyConfigured
class SessionStore(SessionBase):
"""
Implements a file based session store.
"""
def __init__(self, session_key=None):
- self.storage_path = settings.SESSION_FILE_PATH
+ self.storage_path = getattr(settings, "SESSION_FILE_PATH", tempfile.gettempdir())
+
+ # Make sure the storage path is valid.
+ if not os.path.isdir(self.storage_path):
+ raise ImproperlyConfigured("The session storage path %r doesn't exist. "\
+ "Please set your SESSION_FILE_PATH setting "\
+ "to an existing directory in which Django "\
+ "can store session data." % self.storage_path)
+
self.file_prefix = settings.SESSION_COOKIE_NAME
super(SessionStore, self).__init__(session_key)
diff --git a/django/contrib/sessions/tests.py b/django/contrib/sessions/tests.py
index 10bec9668b..b2c664ce7b 100644
--- a/django/contrib/sessions/tests.py
+++ b/django/contrib/sessions/tests.py
@@ -1,5 +1,6 @@
r"""
+>>> from django.conf import settings
>>> from django.contrib.sessions.backends.db import SessionStore as DatabaseSession
>>> from django.contrib.sessions.backends.cache import SessionStore as CacheSession
>>> from django.contrib.sessions.backends.file import SessionStore as FileSession
@@ -39,6 +40,13 @@ True
>>> file_session.exists(file_session.session_key)
False
+# Make sure the file backend checks for a good storage dir
+>>> settings.SESSION_FILE_PATH = "/if/this/directory/exists/you/have/a/weird/computer"
+>>> FileSession()
+Traceback (innermost last):
+ ...
+ImproperlyConfigured: The session storage path '/if/this/directory/exists/you/have/a/weird/computer' doesn't exist. Please set your SESSION_FILE_PATH setting to an existing directory in which Django can store session data.
+
>>> cache_session = CacheSession()
>>> cache_session.modified
False
diff --git a/django/core/cache/backends/filebased.py b/django/core/cache/backends/filebased.py
index 72cb877f6f..c1277bf20c 100644
--- a/django/core/cache/backends/filebased.py
+++ b/django/core/cache/backends/filebased.py
@@ -1,12 +1,12 @@
"File-based cache backend"
+import md5
import os, time
try:
import cPickle as pickle
except ImportError:
import pickle
from django.core.cache.backends.base import BaseCache
-from django.utils.http import urlquote_plus
class CacheClass(BaseCache):
def __init__(self, dir, params):
@@ -29,24 +29,10 @@ class CacheClass(BaseCache):
self._createdir()
def add(self, key, value, timeout=None):
- fname = self._key_to_file(key)
- if timeout is None:
- timeout = self.default_timeout
- try:
- filelist = os.listdir(self._dir)
- except (IOError, OSError):
- self._createdir()
- filelist = []
- if len(filelist) > self._max_entries:
- self._cull(filelist)
- if os.path.basename(fname) not in filelist:
- try:
- f = open(fname, 'wb')
- now = time.time()
- pickle.dump(now + timeout, f, 2)
- pickle.dump(value, f, 2)
- except (IOError, OSError):
- pass
+ if self.has_key(key):
+ return None
+
+ self.set(key, value, timeout)
def get(self, key, default=None):
fname = self._key_to_file(key)
@@ -56,7 +42,7 @@ class CacheClass(BaseCache):
now = time.time()
if exp < now:
f.close()
- os.remove(fname)
+ self._delete(fname)
else:
return pickle.load(f)
except (IOError, OSError, EOFError, pickle.PickleError):
@@ -65,40 +51,74 @@ class CacheClass(BaseCache):
def set(self, key, value, timeout=None):
fname = self._key_to_file(key)
+ dirname = os.path.dirname(fname)
+
if timeout is None:
timeout = self.default_timeout
+
+ self._cull()
+
try:
- filelist = os.listdir(self._dir)
- except (IOError, OSError):
- self._createdir()
- filelist = []
- if len(filelist) > self._max_entries:
- self._cull(filelist)
- try:
+ if not os.path.exists(dirname):
+ os.makedirs(dirname)
+
f = open(fname, 'wb')
now = time.time()
- pickle.dump(now + timeout, f, 2)
- pickle.dump(value, f, 2)
+ pickle.dump(now + timeout, f, pickle.HIGHEST_PROTOCOL)
+ pickle.dump(value, f, pickle.HIGHEST_PROTOCOL)
except (IOError, OSError):
pass
def delete(self, key):
try:
- os.remove(self._key_to_file(key))
+ self._delete(self._key_to_file(key))
+ except (IOError, OSError):
+ pass
+
+ def _delete(self, fname):
+ os.remove(fname)
+ try:
+ # Remove the 2 subdirs if they're empty
+ dirname = os.path.dirname(fname)
+ os.rmdir(dirname)
+ os.rmdir(os.path.dirname(dirname))
except (IOError, OSError):
pass
def has_key(self, key):
- return os.path.exists(self._key_to_file(key))
+ fname = self._key_to_file(key)
+ try:
+ f = open(fname, 'rb')
+ exp = pickle.load(f)
+ now = time.time()
+ if exp < now:
+ f.close()
+ self._delete(fname)
+ return False
+ else:
+ return True
+ except (IOError, OSError, EOFError, pickle.PickleError):
+ return False
- def _cull(self, filelist):
+ def _cull(self):
+ if int(self._num_entries) < self._max_entries:
+ return
+
+ try:
+ filelist = os.listdir(self._dir)
+ except (IOError, OSError):
+ return
+
if self._cull_frequency == 0:
doomed = filelist
else:
- doomed = [k for (i, k) in enumerate(filelist) if i % self._cull_frequency == 0]
- for fname in doomed:
+ doomed = [os.path.join(self._dir, k) for (i, k) in enumerate(filelist) if i % self._cull_frequency == 0]
+
+ for topdir in doomed:
try:
- os.remove(os.path.join(self._dir, fname))
+ for root, _, files in os.walk(topdir):
+ for f in files:
+ self._delete(os.path.join(root, f))
except (IOError, OSError):
pass
@@ -109,4 +129,22 @@ class CacheClass(BaseCache):
raise EnvironmentError, "Cache directory '%s' does not exist and could not be created'" % self._dir
def _key_to_file(self, key):
- return os.path.join(self._dir, urlquote_plus(key))
+ """
+ Convert the filename into an md5 string. We'll turn the first couple
+ bits of the path into directory prefixes to be nice to filesystems
+ that have problems with large numbers of files in a directory.
+
+ Thus, a cache key of "foo" gets turnned into a file named
+ ``{cache-dir}ac/bd/18db4cc2f85cedef654fccc4a4d8``.
+ """
+ path = md5.new(key.encode('utf-8')).hexdigest()
+ path = os.path.join(path[:2], path[2:4], path[4:])
+ return os.path.join(self._dir, path)
+
+ def _get_num_entries(self):
+ count = 0
+ for _,_,files in os.walk(self._dir):
+ count += len(files)
+ return count
+ _num_entries = property(_get_num_entries)
+
diff --git a/django/core/cache/backends/locmem.py b/django/core/cache/backends/locmem.py
index 2734cef86a..e8e1e0d450 100644
--- a/django/core/cache/backends/locmem.py
+++ b/django/core/cache/backends/locmem.py
@@ -29,51 +29,38 @@ class CacheClass(BaseCache):
self._lock = RWLock()
- def _add(self, key, value, timeout=None):
- if len(self._cache) >= self._max_entries:
- self._cull()
- if timeout is None:
- timeout = self.default_timeout
- if key not in self._cache.keys():
- self._cache[key] = value
- self._expire_info[key] = time.time() + timeout
-
def add(self, key, value, timeout=None):
self._lock.writer_enters()
- # Python 2.3 and 2.4 don't allow combined try-except-finally blocks.
try:
- try:
- self._add(key, pickle.dumps(value), timeout)
- except pickle.PickleError:
- pass
+ exp = self._expire_info.get(key)
+ if exp is None or exp <= time.time():
+ try:
+ self._set(key, pickle.dumps(value), timeout)
+ except pickle.PickleError:
+ pass
finally:
self._lock.writer_leaves()
def get(self, key, default=None):
- should_delete = False
self._lock.reader_enters()
try:
- now = time.time()
exp = self._expire_info.get(key)
if exp is None:
return default
- elif exp < now:
- should_delete = True
- else:
+ elif exp > time.time():
try:
return pickle.loads(self._cache[key])
except pickle.PickleError:
return default
finally:
self._lock.reader_leaves()
- if should_delete:
- self._lock.writer_enters()
- try:
- del self._cache[key]
- del self._expire_info[key]
- return default
- finally:
- self._lock.writer_leaves()
+ self._lock.writer_enters()
+ try:
+ del self._cache[key]
+ del self._expire_info[key]
+ return default
+ finally:
+ self._lock.writer_leaves()
def _set(self, key, value, timeout=None):
if len(self._cache) >= self._max_entries:
@@ -95,7 +82,23 @@ class CacheClass(BaseCache):
self._lock.writer_leaves()
def has_key(self, key):
- return key in self._cache
+ self._lock.reader_enters()
+ try:
+ exp = self._expire_info.get(key)
+ if exp is None:
+ return False
+ elif exp > time.time():
+ return True
+ finally:
+ self._lock.reader_leaves()
+
+ self._lock.writer_enters()
+ try:
+ del self._cache[key]
+ del self._expire_info[key]
+ return False
+ finally:
+ self._lock.writer_leaves()
def _cull(self):
if self._cull_frequency == 0:
diff --git a/django/core/mail.py b/django/core/mail.py
index 77ab8007e6..e408393d96 100644
--- a/django/core/mail.py
+++ b/django/core/mail.py
@@ -209,8 +209,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/__init__.py b/django/core/management/__init__.py
index ee0eb6fda8..d78e2eda0b 100644
--- a/django/core/management/__init__.py
+++ b/django/core/management/__init__.py
@@ -10,7 +10,7 @@ from django.core.management.base import BaseCommand, CommandError, handle_defaul
get_version = django.get_version
# A cache of loaded commands, so that call_command
-# doesn't have to reload every time it is called
+# doesn't have to reload every time it's called.
_commands = None
def find_commands(management_dir):
@@ -29,8 +29,8 @@ def find_commands(management_dir):
def find_management_module(app_name):
"""
- Determines the path to the management module for the application named,
- without acutally importing the application or the management module.
+ Determines the path to the management module for the given app_name,
+ without actually importing the application or the management module.
Raises ImportError if the management module cannot be found for any reason.
"""
@@ -46,19 +46,19 @@ def find_management_module(app_name):
def load_command_class(app_name, name):
"""
Given a command name and an application name, returns the Command
- class instance. All errors raised by the importation process
+ class instance. All errors raised by the import process
(ImportError, AttributeError) are allowed to propagate.
"""
return getattr(__import__('%s.management.commands.%s' % (app_name, name),
{}, {}, ['Command']), 'Command')()
-def get_commands():
+def get_commands(load_user_commands=True, project_directory=None):
"""
- Returns a dictionary of commands against the application in which
- those commands can be found. This works by looking for a
- management.commands package in django.core, and in each installed
- application -- if a commands package exists, all commands in that
- package are registered.
+ Returns a dictionary mapping command names to their callback applications.
+
+ This works by looking for a management.commands package in django.core, and
+ in each installed application -- if a commands package exists, all commands
+ in that package are registered.
Core commands are always included. If a settings module has been
specified, user-defined commands will also be included, the
@@ -73,34 +73,22 @@ def get_commands():
startapp command), the instantiated module can be placed in the
dictionary in place of the application name.
- The dictionary is cached on the first call, and reused on subsequent
+ The dictionary is cached on the first call and reused on subsequent
calls.
"""
global _commands
if _commands is None:
- _commands = dict([(name, 'django.core')
- for name in find_commands(__path__[0])])
- # Get commands from all installed apps.
- try:
- from django.conf import settings
- apps = settings.INSTALLED_APPS
- except (AttributeError, ImportError):
- apps = []
+ _commands = dict([(name, 'django.core') for name in find_commands(__path__[0])])
- for app_name in apps:
- try:
- path = find_management_module(app_name)
- _commands.update(dict([(name, app_name)
- for name in find_commands(path)]))
- except ImportError:
- pass # No management module - ignore this app
-
- # Try to determine the project directory
- try:
+ if load_user_commands:
+ # Get commands from all installed apps.
from django.conf import settings
- project_directory = setup_environ(__import__(settings.SETTINGS_MODULE))
- except (AttributeError, ImportError):
- project_directory = None
+ for app_name in settings.INSTALLED_APPS:
+ try:
+ path = find_management_module(app_name)
+ _commands.update(dict([(name, app_name) for name in find_commands(path)]))
+ except ImportError:
+ pass # No management module -- ignore this app.
if project_directory:
# Remove the "startproject" command from self.commands, because
@@ -157,18 +145,18 @@ class ManagementUtility(object):
def __init__(self, argv=None):
self.argv = argv or sys.argv[:]
self.prog_name = os.path.basename(self.argv[0])
+ self.project_directory = None
+ self.user_commands = False
def main_help_text(self):
"""
Returns the script's main help text, as a string.
"""
usage = ['%s <subcommand> [options] [args]' % self.prog_name]
- usage.append('Django command line tool,'
- ' version %s' % django.get_version())
- usage.append("Type '%s help <subcommand>' for help on a specific"
- " subcommand." % self.prog_name)
+ usage.append('Django command line tool, version %s' % django.get_version())
+ usage.append("Type '%s help <subcommand>' for help on a specific subcommand." % self.prog_name)
usage.append('Available subcommands:')
- commands = get_commands().keys()
+ commands = get_commands(self.user_commands, self.project_directory).keys()
commands.sort()
for cmd in commands:
usage.append(' %s' % cmd)
@@ -178,18 +166,18 @@ class ManagementUtility(object):
"""
Tries to fetch the given subcommand, printing a message with the
appropriate command called from the command line (usually
- django-admin.py or manage.py) if it can't be found.
+ "django-admin.py" or "manage.py") if it can't be found.
"""
try:
- app_name = get_commands()[subcommand]
+ app_name = get_commands(self.user_commands, self.project_directory)[subcommand]
if isinstance(app_name, BaseCommand):
# If the command is already loaded, use it directly.
klass = app_name
else:
klass = load_command_class(app_name, subcommand)
except KeyError:
- sys.stderr.write("Unknown command: %r\nType '%s help' for"
- " usage.\n" % (subcommand, self.prog_name))
+ sys.stderr.write("Unknown command: %r\nType '%s help' for usage.\n" % \
+ (subcommand, self.prog_name))
sys.exit(1)
return klass
@@ -201,8 +189,7 @@ class ManagementUtility(object):
# Preprocess options to extract --settings and --pythonpath.
# These options could affect the commands that are available, so they
# must be processed early.
- parser = LaxOptionParser(version=get_version(),
- option_list=BaseCommand.option_list)
+ parser = LaxOptionParser(version=get_version(), option_list=BaseCommand.option_list)
try:
options, args = parser.parse_args(self.argv)
handle_default_options(options)
@@ -242,6 +229,8 @@ class ProjectManagementUtility(ManagementUtility):
"""
def __init__(self, argv, project_directory):
super(ProjectManagementUtility, self).__init__(argv)
+ self.project_directory = project_directory
+ self.user_commands = True
def setup_environ(settings_mod):
"""
@@ -263,8 +252,7 @@ def setup_environ(settings_mod):
sys.path.pop()
# Set DJANGO_SETTINGS_MODULE appropriately.
- os.environ['DJANGO_SETTINGS_MODULE'] = '%s.%s' % (project_name,
- settings_name)
+ os.environ['DJANGO_SETTINGS_MODULE'] = '%s.%s' % (project_name, settings_name)
return project_directory
def execute_from_command_line(argv=None):
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 83fc9b8ac9..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,15 +51,15 @@ 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 = []
- if verbosity > 0:
+ if verbosity > 2:
if formats:
print "Loading '%s' fixtures..." % fixture_name
else:
@@ -98,15 +99,17 @@ 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:
- if verbosity > 1:
+ if verbosity > 2:
print "No %s fixture '%s' in %s." % \
(format, fixture_name, humanize(fixture_dir))
@@ -122,7 +125,7 @@ class Command(BaseCommand):
transaction.leave_transaction_management()
if count[0] == 0:
- if verbosity > 0:
+ if verbosity > 2:
print "No fixtures found."
else:
if verbosity > 0:
diff --git a/django/core/management/sql.py b/django/core/management/sql.py
index 43918ef1c2..767a2dbbfb 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 92159dbbe3..4c32a9686f 100644
--- a/django/core/serializers/pyyaml.py
+++ b/django/core/serializers/pyyaml.py
@@ -5,6 +5,7 @@ Requires PyYaml (http://pyyaml.org/), but that's checked for in __init__.
"""
import datetime
+from django.db import models
from django.core.serializers.python import Serializer as PythonSerializer
from django.core.serializers.python import Deserializer as PythonDeserializer
try:
@@ -17,10 +18,25 @@ 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
+ # we want to use the "safe" serializer for better interoperability, we
+ # need to do something with those pesky times. Converting 'em to strings
+ # isn't perfect, but it's better than a "!!python/time" type which would
+ # halt deserialization under any other language.
+ if isinstance(field, models.TimeField) and getattr(obj, field.name) is not None:
+ self._current[field.name] = str(getattr(obj, field.name))
+ else:
+ super(Serializer, self).handle_field(obj, field)
+
def end_serialization(self):
self.options.pop('stream', None)
self.options.pop('fields', None)
- yaml.dump(self.objects, self.stream, **self.options)
+ yaml.safe_dump(self.objects, self.stream, **self.options)
def getvalue(self):
return self.stream.getvalue()
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/__init__.py b/django/db/__init__.py
index e16de07416..95dd36822e 100644
--- a/django/db/__init__.py
+++ b/django/db/__init__.py
@@ -36,8 +36,8 @@ except ImportError, e:
raise # If there's some other error, this must be an error in Django itself.
def _import_database_module(import_path='', module_name=''):
- """Lazyily import a database module when requested."""
- return __import__('%s%s.%s' % (_import_path, settings.DATABASE_ENGINE, module_name), {}, {}, [''])
+ """Lazily import a database module when requested."""
+ return __import__('%s%s.%s' % (import_path, settings.DATABASE_ENGINE, module_name), {}, {}, [''])
# We don't want to import the introspect module unless someone asks for it, so
# lazily load it on demmand.
diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py
index 24fda78427..cd830413fc 100644
--- a/django/db/backends/oracle/base.py
+++ b/django/db/backends/oracle/base.py
@@ -455,6 +455,23 @@ class FormatStylePlaceholderCursor(Database.Cursor):
else:
return tuple([smart_str(p, self.charset, True) for p in params])
+ def _guess_input_sizes(self, params_list):
+ # Mark any string parameter greater than 4000 characters as an NCLOB.
+ if isinstance(params_list[0], dict):
+ sizes = {}
+ iterators = [params.iteritems() for params in params_list]
+ else:
+ sizes = [None] * len(params_list[0])
+ iterators = [enumerate(params) for params in params_list]
+ for iterator in iterators:
+ for key, value in iterator:
+ if isinstance(value, basestring) and len(value) > 4000:
+ sizes[key] = Database.NCLOB
+ if isinstance(sizes, dict):
+ self.setinputsizes(**sizes)
+ else:
+ self.setinputsizes(*sizes)
+
def execute(self, query, params=None):
if params is None:
params = []
@@ -468,6 +485,7 @@ class FormatStylePlaceholderCursor(Database.Cursor):
if query.endswith(';') or query.endswith('/'):
query = query[:-1]
query = smart_str(query, self.charset) % tuple(args)
+ self._guess_input_sizes([params])
return Database.Cursor.execute(self, query, params)
def executemany(self, query, params=None):
@@ -484,6 +502,7 @@ class FormatStylePlaceholderCursor(Database.Cursor):
query = query[:-1]
query = smart_str(query, self.charset) % tuple(args)
new_param_list = [self._format_params(i) for i in params]
+ self._guess_input_sizes(new_param_list)
return Database.Cursor.executemany(self, query, new_param_list)
def fetchone(self):
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index ca92d8ae73..84537bf2dc 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -717,7 +717,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):
@@ -913,6 +913,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]
@@ -929,11 +934,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):
@@ -942,7 +947,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 47f9736ce2..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):
@@ -331,7 +344,7 @@ class HttpResponse(object):
chunk = self._iterator.next()
if isinstance(chunk, unicode):
chunk = chunk.encode(self._charset)
- return chunk
+ return str(chunk)
def close(self):
if hasattr(self._container, 'close'):
diff --git a/django/newforms/fields.py b/django/newforms/fields.py
index 58f65ffde5..3b8f4195b0 100644
--- a/django/newforms/fields.py
+++ b/django/newforms/fields.py
@@ -533,8 +533,8 @@ class BooleanField(Field):
"""Returns a Python boolean object."""
super(BooleanField, self).clean(value)
# Explicitly check for the string 'False', which is what a hidden field
- # will submit for False (since bool("True") == True we don't need to
- # handle that explicitly).
+ # will submit for False. Because bool("True") == True, we don't need to
+ # handle that explicitly.
if value == 'False':
return False
return bool(value)
diff --git a/django/newforms/models.py b/django/newforms/models.py
index 4eb61761a1..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))
@@ -245,15 +242,16 @@ class ModelFormMetaclass(type):
# If a model is defined, extract form fields from it and add them to base_fields
if attrs['_meta'].model is not None:
- # Don't allow a subclass to define a Meta model if a parent class has.
- # Technically the right fields would be generated, but the save
- # method will not deal with more than one model.
+ # Don't allow a subclass to define a different Meta model than a
+ # parent class has. Technically the right fields would be generated,
+ # but the save method will not deal with more than one model.
for base in bases:
base_opts = getattr(base, '_meta', None)
base_model = getattr(base_opts, 'model', None)
- if base_model is not None:
- raise ImproperlyConfigured('%s defines more than one model.' % name)
- model_fields = fields_for_model(opts.model, opts.fields, opts.exclude)
+ 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, formfield_callback)
# fields declared in base classes override fields from the model
model_fields.update(declared_fields)
attrs['base_fields'] = model_fields
@@ -262,11 +260,16 @@ class ModelFormMetaclass(type):
return type.__new__(cls, name, bases, attrs)
class BaseModelForm(BaseForm):
- def __init__(self, instance, data=None, files=None, auto_id='id_%s', prefix=None,
- initial=None, error_class=ErrorList, label_suffix=':'):
- self.instance = instance
+ def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
+ initial=None, error_class=ErrorList, label_suffix=':', instance=None):
opts = self._meta
- object_data = model_to_dict(instance, opts.fields, opts.exclude)
+ if instance is None:
+ # if we didn't get an instance, instantiate a new one
+ self.instance = opts.model()
+ object_data = {}
+ else:
+ self.instance = instance
+ object_data = model_to_dict(instance, opts.fields, opts.exclude)
# if initial was provided, it should override the values from instance
if initial is not None:
object_data.update(initial)
diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py
index ac92bef6cf..5cee0ab244 100644
--- a/django/template/defaultfilters.py
+++ b/django/template/defaultfilters.py
@@ -43,7 +43,11 @@ def stringfilter(func):
def addslashes(value):
- """Adds slashes - useful for passing strings to JavaScript, for example."""
+ """
+ Adds slashes before quotes. Useful for escaping strings in CSV, for
+ example. Less useful for escaping JavaScript; use the ``escapejs``
+ filter instead.
+ """
return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'")
addslashes.is_safe = True
addslashes = stringfilter(addslashes)
@@ -54,6 +58,25 @@ def capfirst(value):
capfirst.is_safe=True
capfirst = stringfilter(capfirst)
+_js_escapes = (
+ ('\\', '\\\\'),
+ ('"', '\\"'),
+ ("'", "\\'"),
+ ('\n', '\\n'),
+ ('\r', '\\r'),
+ ('\b', '\\b'),
+ ('\f', '\\f'),
+ ('\t', '\\t'),
+ ('\v', '\\v'),
+ ('</', '<\\/'),
+)
+def escapejs(value):
+ """Backslash-escapes characters for use in JavaScript strings."""
+ for bad, good in _js_escapes:
+ value = value.replace(bad, good)
+ return value
+escapejs = stringfilter(escapejs)
+
def fix_ampersands(value):
"""Replaces ampersands with ``&amp;`` entities."""
from django.utils.html import fix_ampersands
@@ -231,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.
@@ -239,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):
@@ -766,6 +791,7 @@ register.filter(dictsort)
register.filter(dictsortreversed)
register.filter(divisibleby)
register.filter(escape)
+register.filter(escapejs)
register.filter(filesizeformat)
register.filter(first)
register.filter(fix_ampersands)
diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py
index d91e30bbb5..813e5dbed6 100644
--- a/django/template/defaulttags.py
+++ b/django/template/defaulttags.py
@@ -814,7 +814,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 %}
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/utils/html.py b/django/utils/html.py
index 34bbf7357f..33e2ee3856 100644
--- a/django/utils/html.py
+++ b/django/utils/html.py
@@ -1,4 +1,4 @@
-"HTML utilities suitable for global use."
+"""HTML utilities suitable for global use."""
import re
import string
@@ -8,11 +8,11 @@ from django.utils.encoding import force_unicode
from django.utils.functional import allow_lazy
from django.utils.http import urlquote
-# Configuration for urlize() function
+# Configuration for urlize() function.
LEADING_PUNCTUATION = ['(', '<', '&lt;']
TRAILING_PUNCTUATION = ['.', ',', ')', '>', '\n', '&gt;']
-# list of possible strings used for bullets in bulleted lists
+# List of possible strings used for bullets in bulleted lists.
DOTS = ['&middot;', '*', '\xe2\x80\xa2', '&#149;', '&bull;', '&#8226;']
unencoded_ampersands_re = re.compile(r'&(?!(\w+|#\d+);)')
@@ -28,7 +28,7 @@ trailing_empty_content_re = re.compile(r'(?:<p>(?:&nbsp;|\s|<br \/>)*?</p>\s*)+\
del x # Temporary variable
def escape(html):
- "Return the given HTML with ampersands, quotes and carets encoded."
+ """Returns the given HTML with ampersands, quotes and carets encoded."""
return mark_safe(force_unicode(html).replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;').replace("'", '&#39;'))
escape = allow_lazy(escape, unicode)
@@ -42,7 +42,7 @@ def conditional_escape(html):
return escape(html)
def linebreaks(value, autoescape=False):
- "Converts newlines into <p> and <br />s"
+ """Converts newlines into <p> and <br />s."""
value = re.sub(r'\r\n|\r|\n', '\n', force_unicode(value)) # normalize newlines
paras = re.split('\n{2,}', value)
if autoescape:
@@ -50,31 +50,31 @@ def linebreaks(value, autoescape=False):
else:
paras = [u'<p>%s</p>' % p.strip().replace('\n', '<br />') for p in paras]
return u'\n\n'.join(paras)
-linebreaks = allow_lazy(linebreaks, unicode)
+linebreaks = allow_lazy(linebreaks, unicode)
def strip_tags(value):
- "Return the given HTML with all tags stripped."
+ """Returns the given HTML with all tags stripped."""
return re.sub(r'<[^>]*?>', '', force_unicode(value))
strip_tags = allow_lazy(strip_tags)
def strip_spaces_between_tags(value):
- "Return the given HTML with spaces between tags removed."
+ """Returns the given HTML with spaces between tags removed."""
return re.sub(r'>\s+<', '><', force_unicode(value))
strip_spaces_between_tags = allow_lazy(strip_spaces_between_tags, unicode)
def strip_entities(value):
- "Return the given HTML with all entities (&something;) stripped."
+ """Returns the given HTML with all entities (&something;) stripped."""
return re.sub(r'&(?:\w+|#\d+);', '', force_unicode(value))
strip_entities = allow_lazy(strip_entities, unicode)
def fix_ampersands(value):
- "Return the given HTML with all unencoded ampersands encoded correctly."
+ """Returns the given HTML with all unencoded ampersands encoded correctly."""
return unencoded_ampersands_re.sub('&amp;', force_unicode(value))
fix_ampersands = allow_lazy(fix_ampersands, unicode)
def urlize(text, trim_url_limit=None, nofollow=False, autoescape=False):
"""
- Convert any URLs in text into clickable links.
+ Converts any URLs in text into clickable links.
Works on http://, https://, and www. links. Links can have trailing
punctuation (periods, commas, close-parens) and leading punctuation
diff --git a/django/utils/maxlength.py b/django/utils/maxlength.py
index 1df6a3e93e..a616541f85 100644
--- a/django/utils/maxlength.py
+++ b/django/utils/maxlength.py
@@ -1,6 +1,6 @@
"""
Utilities for providing backwards compatibility for the maxlength argument,
-which has been replaced by max_length, see ticket #2101.
+which has been replaced by max_length. See ticket #2101.
"""
from warnings import warn
@@ -15,17 +15,15 @@ def legacy_maxlength(max_length, maxlength):
"""
Consolidates max_length and maxlength, providing backwards compatibilty
for the legacy "maxlength" argument.
+
If one of max_length or maxlength is given, then that value is returned.
- If both are given, a TypeError is raised.
- If maxlength is used at all, a deprecation warning is issued.
+ If both are given, a TypeError is raised. If maxlength is used at all, a
+ deprecation warning is issued.
"""
if maxlength is not None:
- warn("maxlength is deprecated, use max_length instead.",
- DeprecationWarning,
- stacklevel=3)
+ warn("maxlength is deprecated. Use max_length instead.", DeprecationWarning, stacklevel=3)
if max_length is not None:
- raise TypeError("field can not take both the max_length"
- " argument and the legacy maxlength argument.")
+ raise TypeError("Field cannot take both the max_length argument and the legacy maxlength argument.")
max_length = maxlength
return max_length
@@ -33,7 +31,8 @@ def remove_maxlength(func):
"""
A decorator to be used on a class's __init__ that provides backwards
compatibilty for the legacy "maxlength" keyword argument, i.e.
- name = models.CharField(maxlength=20)
+ name = models.CharField(maxlength=20)
+
It does this by changing the passed "maxlength" keyword argument
(if it exists) into a "max_length" keyword argument.
"""
@@ -58,7 +57,6 @@ class LegacyMaxlength(type):
Metaclass for providing backwards compatibility support for the
"maxlength" keyword argument.
"""
-
def __init__(cls, name, bases, attrs):
super(LegacyMaxlength, cls).__init__(name, bases, attrs)
# Decorate the class's __init__ to remove any maxlength keyword.
diff --git a/django/views/debug.py b/django/views/debug.py
index fba43912bb..18a396d3a6 100644
--- a/django/views/debug.py
+++ b/django/views/debug.py
@@ -1,9 +1,12 @@
+import os
+import re
+import sys
+
from django.conf import settings
from django.template import Template, Context, TemplateDoesNotExist
from django.utils.html import escape
from django.http import HttpResponseServerError, HttpResponseNotFound
from django.utils.encoding import smart_unicode
-import os, re, sys
HIDDEN_SETTINGS = re.compile('SECRET|PASSWORD|PROFANITIES_LIST')
@@ -142,9 +145,10 @@ def technical_500_response(request, exc_type, exc_value, tb):
'request': request,
'request_protocol': request.is_secure() and "https" or "http",
'settings': get_safe_settings(),
- 'sys_executable' : sys.executable,
- 'sys_version_info' : '%d.%d.%d' % sys.version_info[0:3],
- 'django_version_info' : get_version(),
+ 'sys_executable': sys.executable,
+ 'sys_version_info': '%d.%d.%d' % sys.version_info[0:3],
+ 'django_version_info': get_version(),
+ 'sys_path' : sys.path,
'template_info': template_info,
'template_does_not_exist': template_does_not_exist,
'loader_debug_info': loader_debug_info,
@@ -230,8 +234,8 @@ TECHNICAL_500_TEMPLATE = """
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
- <meta http-equiv="content-type" content="text/html; charset=utf-8" />
- <meta name="robots" content="NONE,NOARCHIVE" />
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
+ <meta name="robots" content="NONE,NOARCHIVE">
<title>{{ exception_type }} at {{ request.path|escape }}</title>
<style type="text/css">
html * { padding:0; margin:0; }
@@ -368,6 +372,10 @@ TECHNICAL_500_TEMPLATE = """
<th>Python Version:</th>
<td>{{ sys_version_info }}</td>
</tr>
+ <tr>
+ <th>Python Path:</th>
+ <td>{{ sys_path }}</td>
+ </tr>
</table>
</div>
{% if unicode_hint %}
@@ -460,10 +468,10 @@ TECHNICAL_500_TEMPLATE = """
{% endautoescape %}
<form action="http://dpaste.com/" name="pasteform" id="pasteform" method="post">
<div id="pastebinTraceback" class="pastebin">
- <input type="hidden" name="language" value="PythonConsole" />
- <input type="hidden" name="title" value="{{ exception_type|escape }} at {{ request.path|escape }}" />
- <input type="hidden" name="source" value="Django Dpaste Agent" />
- <input type="hidden" name="poster" value="Django" />
+ <input type="hidden" name="language" value="PythonConsole">
+ <input type="hidden" name="title" value="{{ exception_type|escape }} at {{ request.path|escape }}">
+ <input type="hidden" name="source" value="Django Dpaste Agent">
+ <input type="hidden" name="poster" value="Django">
<textarea name="content" id="traceback_area" cols="140" rows="25">
Environment:
@@ -471,10 +479,10 @@ Request Method: {{ request.META.REQUEST_METHOD }}
Request URL: {{ request_protocol }}://{{ request.META.HTTP_HOST }}{{ request.path|escape }}
Django Version: {{ django_version_info }}
Python Version: {{ sys_version_info }}
-Installed Applications:
- {{ settings.INSTALLED_APPS|pprint }}
-Installed Middleware:
- {{ settings.MIDDLEWARE_CLASSES|pprint }}
+Installed Applications:
+{{ settings.INSTALLED_APPS|pprint }}
+Installed Middleware:
+{{ settings.MIDDLEWARE_CLASSES|pprint }}
{% if template_does_not_exist %}Template Loader Error:
{% if loader_debug_info %}Django tried loading these templates, in this order:
@@ -498,9 +506,9 @@ Traceback:
Exception Type: {{ exception_type|escape }} at {{ request.path|escape }}
Exception Value: {{ exception_value|escape }}
</textarea>
- <br/><br/>
+ <br><br>
+ <input type="submit" value="Share this traceback on public Web site">
</div>
-<input type="submit" value="Send to DPaste">
</form>
</div>
@@ -627,9 +635,9 @@ TECHNICAL_404_TEMPLATE = """
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
- <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Page not found at {{ request.path|escape }}</title>
- <meta name="robots" content="NONE,NOARCHIVE" />
+ <meta name="robots" content="NONE,NOARCHIVE">
<style type="text/css">
html * { padding:0; margin:0; }
body * { padding:10px 20px; }
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 = """
diff --git a/docs/add_ons.txt b/docs/add_ons.txt
index 1763d4f38d..029e314f12 100644
--- a/docs/add_ons.txt
+++ b/docs/add_ons.txt
@@ -47,7 +47,11 @@ contenttypes
============
A light framework for hooking into "types" of content, where each installed
-Django model is a separate content type. This is not yet documented.
+Django model is a separate content type.
+
+See the `contenttypes documentation`_.
+
+.. _contenttypes documentation: ../contenttypes/
csrf
====
@@ -177,9 +181,13 @@ localflavor
===========
A collection of various Django snippets that are useful only for a particular
-country or culture. For example, ``django.contrib.localflavor.usa.forms``
+country or culture. For example, ``django.contrib.localflavor.us.forms``
contains a ``USZipCodeField`` that you can use to validate U.S. zip codes.
+See the `localflavor documentation`_.
+
+.. _localflavor documentation: ../localflavor/
+
markup
======
@@ -252,12 +260,12 @@ See the `syndication documentation`_.
webdesign
=========
-Helpers and utilties targeted primarily at web designers rather than
-web developers.
+Helpers and utilties targeted primarily at Web *designers* rather than
+Web *developers*.
-See the `web design helpers documentation`_.
+See the `Web design helpers documentation`_.
-.. _web design helpers documentation: ../webdesign/
+.. _Web design helpers documentation: ../webdesign/
Other add-ons
=============
diff --git a/docs/authentication.txt b/docs/authentication.txt
index eadf702e88..5134e90267 100644
--- a/docs/authentication.txt
+++ b/docs/authentication.txt
@@ -247,8 +247,8 @@ Anonymous users
the ``django.contrib.auth.models.User`` interface, with these differences:
* ``id`` is always ``None``.
- * ``is_staff`` and ``is_superuser`` are always False.
- * ``is_active`` is always True.
+ * ``is_staff`` and ``is_superuser`` are always ``False``.
+ * ``is_active`` is always ``False``.
* ``groups`` and ``user_permissions`` are always empty.
* ``is_anonymous()`` returns ``True`` instead of ``False``.
* ``is_authenticated()`` returns ``False`` instead of ``True``.
@@ -382,7 +382,7 @@ This example shows how you might use both ``authenticate()`` and ``login()``::
.. admonition:: Calling ``authenticate()`` first
When you're manually logging a user in, you *must* call
- ``authenticate()`` before you call ``login()``; ``authenticate()``
+ ``authenticate()`` before you call ``login()``. ``authenticate()``
sets an attribute on the ``User`` noting which authentication
backend successfully authenticated that user (see the `backends
documentation`_ for details), and this information is needed later
diff --git a/docs/contributing.txt b/docs/contributing.txt
index efc6640d10..37c9196467 100644
--- a/docs/contributing.txt
+++ b/docs/contributing.txt
@@ -254,9 +254,10 @@ Second, note the five triage stages:
3. Once a ticket is ruled to be approved for fixing, it's moved into the
"Accepted" stage. This stage is where all the real work gets done.
- 4. A ticket might be moved to the "Someday/Maybe" state if it's an
- enhancement request we are willing to consider if a good patch is
- written. Such tickets are not high priority.
+ 4. In some cases, a ticket might get moved to the "Someday/Maybe" state.
+ This means the ticket is an enhancement request that we might consider
+ adding to the framework if an excellent patch is submitted. These
+ tickets are not a high priority.
5. If a ticket has an associated patch (see below), a triager will review
the patch. If the patch is complete, it'll be marked as "ready for
diff --git a/docs/custom_model_fields.txt b/docs/custom_model_fields.txt
index 0cf2549746..923b331a95 100644
--- a/docs/custom_model_fields.txt
+++ b/docs/custom_model_fields.txt
@@ -44,8 +44,8 @@ Our class looks something like this::
# ... (other possibly useful methods omitted) ...
This is just an ordinary Python class, with nothing Django-specific about it.
-We'd like to be able to things like this in our models (we assume the ``hand``
-attribute on the model is an instance of ``Hand``)::
+We'd like to be able to do things like this in our models (we assume the
+``hand`` attribute on the model is an instance of ``Hand``)::
example = MyModel.objects.get(pk=1)
print example.hand.north
@@ -382,6 +382,10 @@ database, so we need to be able to process strings and ``Hand`` instances in
Notice that we always return a ``Hand`` instance from this method. That's the
Python object type we want to store in the model's attribute.
+**Remember:** If your custom field needs the ``to_python()`` method to be
+called when it is created, you should be using `The SubfieldBase metaclass`_
+mentioned earlier. Otherwise ``to_python()`` won't be called automatically.
+
``get_db_prep_save(self, value)``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/docs/databases.txt b/docs/databases.txt
index 5ee6aa4304..6832c2b361 100644
--- a/docs/databases.txt
+++ b/docs/databases.txt
@@ -258,11 +258,10 @@ many-to-many table would be stored in the ``indexes`` tablespace. The ``data``
field would also generate an index, but no tablespace for it is specified, so
it would be stored in the model tablespace ``tables`` by default.
-The settings.py file supports two additional options to specify
-default values for the db_tablespace options. This is useful for
-setting a tablespace for the Django internal apps and other
-contributed applications. These options are ``DEFAULT_TABLESPACE``
-and ``DEFAULT_INDEX_TABLESPACE``.
+**New in the Django development version:** Use the ``DEFAULT_TABLESPACE`` and
+``DEFAULT_INDEX_TABLESPACE`` settings to specify default values for the
+db_tablespace options. These are useful for setting a tablespace for the
+built-in Django apps and other applications whose code you cannot control.
Django does not create the tablespaces for you. Please refer to `Oracle's
documentation`_ for details on creating and managing tablespaces.
diff --git a/docs/django-admin.txt b/docs/django-admin.txt
index 2977f9908f..fe43f9586c 100644
--- a/docs/django-admin.txt
+++ b/docs/django-admin.txt
@@ -132,6 +132,13 @@ If no application name is provided, all installed applications will be dumped.
The output of ``dumpdata`` can be used as input for ``loaddata``.
+Note that ``dumpdata`` uses the default manager on the model for selecting the
+records to dump. If you're using a `custom manager`_ as the default manager
+and it filters some of the available records, not all of the objects will be
+dumped.
+
+.. _custom manager: ../model-api/#custom-managers
+
--format
~~~~~~~~
diff --git a/docs/form_for_model.txt b/docs/form_for_model.txt
index 6761c15331..ddca9aae18 100644
--- a/docs/form_for_model.txt
+++ b/docs/form_for_model.txt
@@ -1,6 +1,13 @@
Generating forms for models
===========================
+.. admonition:: Note
+
+ The APIs described in this document have been deprecated. If you're
+ developing new code, use `ModelForms`_ instead.
+
+.. _ModelForms: ../modelforms/
+
If you're building a database-driven app, chances are you'll have forms that
map closely to Django models. For instance, you might have a ``BlogComment``
model, and you want to create a form that lets people submit comments. In this
diff --git a/docs/install.txt b/docs/install.txt
index 7564f640be..331a5ea8e3 100644
--- a/docs/install.txt
+++ b/docs/install.txt
@@ -76,14 +76,12 @@ automatically create database tables for your models, you'll need to
ensure that Django has permission to create and alter tables in the
database you're using; if you plan to manually create the tables, you
can simply grant Django ``SELECT``, ``INSERT``, ``UPDATE`` and
-``DELETE`` permissions. On some databases, Django will need to have
-``ALTER TABLE`` privileges during ``syncdb`` (in order to create
-foreign key constraints properly on databases which do not allow them
-to be deferred), but will not issue ``ALTER TABLE`` statements on a
-table once ``syncdb`` has finished setting it up.
+``DELETE`` permissions. On some databases, Django will need
+``ALTER TABLE`` privileges during ``syncdb`` but won't issue
+``ALTER TABLE`` statements on a table once ``syncdb`` has created it.
-If you will be using Django's `testing framework`_ with data fixtures,
-Django will need permission to create a temporary test database.
+If you're using Django's `testing framework`_ to test database queries,
+Django will need permission to create a test database.
.. _PostgreSQL: http://www.postgresql.org/
.. _MySQL: http://www.mysql.com/
diff --git a/docs/localflavor.txt b/docs/localflavor.txt
index 3eecba57e7..154922ff45 100644
--- a/docs/localflavor.txt
+++ b/docs/localflavor.txt
@@ -2,16 +2,22 @@
The "local flavor" add-ons
==========================
-Django comes with assorted pieces of code that are useful only for a particular
-country or culture. These pieces of code are organized as a set of
-subpackages, named using `ISO 3166 country codes`_.
+Following its "batteries included" philosophy, Django comes with assorted
+pieces of code that are useful for particular countries or cultures. These are
+called the "local flavor" add-ons and live in the ``django.contrib.localflavor``
+package.
-.. _ISO 3166 country codes: http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm
+Inside that package, country- or culture-specific code is organized into
+subpackages, named using `ISO 3166 country codes`_.
Most of the ``localflavor`` add-ons are localized form components deriving from
-the newforms_ framework. To use one of these localized components, just import
-the relevant subpackage. For example, a form with a field for French telephone
-numbers is created like so::
+the newforms_ framework -- for example, a ``USStateField`` that knows how to
+validate U.S. state abbreviations, and a ``FISocialSecurityNumber`` that knows
+how to validate Finnish social security numbers.
+
+To use one of these localized components, just import the relevant subpackage.
+For example, here's how you can create a form with a field representing a
+French telephone number::
from django import newforms as forms
from django.contrib.localflavor import fr
@@ -19,32 +25,48 @@ numbers is created like so::
class MyForm(forms.Form):
my_french_phone_no = fr.forms.FRPhoneNumberField()
+Supported countries
+===================
+
Countries currently supported by ``localflavor`` are:
-* Argentina_
-* Australia_
-* Brazil_
-* Canada_
-* Chile_
-* Finland_
-* France_
-* Germany_
-* Holland_
-* Iceland_
-* India_
-* Italy_
-* Japan_
-* Mexico_
-* Norway_
-* Peru_
-* Poland_
-* Slovakia_
-* `South Africa`_
-* Spain_
-* Switzerland_
-* `United Kingdom`_
-* `United States of America`_
+ * Argentina_
+ * Australia_
+ * Brazil_
+ * Canada_
+ * Chile_
+ * Finland_
+ * France_
+ * Germany_
+ * Holland_
+ * Iceland_
+ * India_
+ * Italy_
+ * Japan_
+ * Mexico_
+ * Norway_
+ * Peru_
+ * Poland_
+ * Slovakia_
+ * `South Africa`_
+ * Spain_
+ * Switzerland_
+ * `United Kingdom`_
+ * `United States of America`_
+
+The ``localflavor`` package also includes a ``generic`` subpackage, containing
+useful code that is not specific to one particular country or culture.
+Currently, it defines date and datetime input fields based on those from
+newforms_, but with non-US default formats. Here's an example of how to use
+them::
+
+ from django import newforms as forms
+ from django.contrib.localflavor import generic
+
+ class MyForm(forms.Form):
+ my_date_field = generic.forms.DateField()
+.. _ISO 3166 country codes: http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm
.. _Argentina: `Argentina (django.contrib.localflavor.ar)`_
.. _Australia: `Australia (django.contrib.localflavor.au)`_
.. _Brazil: `Brazil (django.contrib.localflavor.br)`_
@@ -68,29 +90,15 @@ Countries currently supported by ``localflavor`` are:
.. _Switzerland: `Switzerland (django.contrib.localflavor.ch)`_
.. _United Kingdom: `United Kingdom (django.contrib.localflavor.uk)`_
.. _United States of America: `United States of America (django.contrib.localflavor.us)`_
-
-The ``localflavor`` add-on also includes the ``generic`` subpackage, containing
-useful code that is not specific to one particular country or culture.
-Currently, it defines date and date & time input fields based on those from
-newforms_, but with non-US default formats. Here's an example of how to use
-them::
-
- from django import newforms as forms
- from django.contrib.localflavor import generic
-
- class MyForm(forms.Form):
- my_date_field = generic.forms.DateField()
-
.. _newforms: ../newforms/
+Adding flavors
+==============
-.. admonition:: Adding a Flavor
-
- We'd love to add more of these to Django, so please create a ticket for
- anything that you've found useful. Please use unicode objects
- (``u'mystring'``) for strings, rather than setting the encoding in the file
- (see any of the existing flavors for examples).
-
+We'd love to add more of these to Django, so please `create a ticket`_ with
+any code you'd like to contribute. One thing we ask is that you please use
+Unicode objects (``u'mystring'``) for strings, rather than setting the encoding
+in the file. See any of the existing flavors for examples.
Argentina (``django.contrib.localflavor.ar``)
=============================================
@@ -108,7 +116,6 @@ ARProvinceSelect
A ``Select`` widget that uses a list of Argentina's provinces as its choices.
-
Australia (``django.contrib.localflavor.au``)
=============================================
@@ -129,7 +136,6 @@ AUStateSelect
A ``Select`` widget that uses a list of Australian states/territories as its
choices.
-
Brazil (``django.contrib.localflavor.br``)
==========================================
@@ -151,7 +157,6 @@ BRStateSelect
A ``Select`` widget that uses a list of Brazilian states/territories as its
choices.
-
Canada (``django.contrib.localflavor.ca``)
==========================================
@@ -187,7 +192,6 @@ CAProvinceSelect
A ``Select`` widget that uses a list of Canadian provinces and territories as
its choices.
-
Chile (``django.contrib.localflavor.cl``)
=========================================
@@ -203,7 +207,6 @@ CLRegionSelect
A ``Select`` widget that uses a list of Chilean regions (Regiones) as its
choices.
-
Finland (``django.contrib.localflavor.fi``)
===========================================
@@ -215,7 +218,7 @@ A form field that validates input as a Finnish social security number.
FIZipCodeField
--------------
-A form field that validates input as a Finnish zip code. Valid codes
+A form field that validates input as a Finnish zip code. Valid codes
consist of five digits.
FIMunicipalitySelect
@@ -224,7 +227,6 @@ FIMunicipalitySelect
A ``Select`` widget that uses a list of Finnish municipalities as its
choices.
-
France (``django.contrib.localflavor.fr``)
==========================================
@@ -232,13 +234,13 @@ FRPhoneNumberField
------------------
A form field that validates input as a French local phone number. The
-correct format is 0X XX XX XX XX. 0X.XX.XX.XX.XX and 0XXXXXXXXX validate
+correct format is 0X XX XX XX XX. 0X.XX.XX.XX.XX and 0XXXXXXXXX validate
but are corrected to 0X XX XX XX XX.
FRZipCodeField
--------------
-A form field that validates input as a French zip code. Valid codes
+A form field that validates input as a French zip code. Valid codes
consist of five digits.
FRDepartmentSelect
@@ -246,7 +248,6 @@ FRDepartmentSelect
A ``Select`` widget that uses a list of French departments as its choices.
-
Germany (``django.contrib.localflavor.de``)
===========================================
@@ -254,7 +255,7 @@ DEIdentityCardNumberField
-------------------------
A form field that validates input as a German identity card number
-(Personalausweis_). Valid numbers have the format
+(Personalausweis_). Valid numbers have the format
XXXXXXXXXXX-XXXXXXX-XXXXXXX-X, with no group consisting entirely of zeroes.
.. _Personalausweis: http://de.wikipedia.org/wiki/Personalausweis
@@ -262,7 +263,7 @@ XXXXXXXXXXX-XXXXXXX-XXXXXXX-X, with no group consisting entirely of zeroes.
DEZipCodeField
--------------
-A form field that validates input as a German zip code. Valid codes
+A form field that validates input as a German zip code. Valid codes
consist of five digits.
DEStateSelect
@@ -270,7 +271,6 @@ DEStateSelect
A ``Select`` widget that uses a list of German states as its choices.
-
Holland (``django.contrib.localflavor.nl``)
===========================================
@@ -296,7 +296,6 @@ NLProvinceSelect
A ``Select`` widget that uses a list of Dutch provinces as its list of
choices.
-
Iceland (``django.contrib.localflavor.is_``)
============================================
@@ -304,7 +303,7 @@ ISIdNumberField
---------------
A form field that validates input as an Icelandic identification number
-(kennitala). The format is XXXXXX-XXXX.
+(kennitala). The format is XXXXXX-XXXX.
ISPhoneNumberField
------------------
@@ -318,7 +317,6 @@ ISPostalCodeSelect
A ``Select`` widget that uses a list of Icelandic postal codes as its
choices.
-
India (``django.contrib.localflavor.in_``)
==========================================
@@ -326,7 +324,7 @@ INStateField
------------
A form field that validates input as an Indian state/territory name or
-abbreviation. Input is normalized to the standard two-letter vehicle
+abbreviation. Input is normalized to the standard two-letter vehicle
registration abbreviation for the given state or territory.
INZipCodeField
@@ -341,7 +339,6 @@ INStateSelect
A ``Select`` widget that uses a list of Indian states/territories as its
choices.
-
Italy (``django.contrib.localflavor.it``)
=========================================
@@ -361,7 +358,7 @@ A form field that validates Italian VAT numbers (partita IVA).
ITZipCodeField
--------------
-A form field that validates input as an Italian zip code. Valid codes
+A form field that validates input as an Italian zip code. Valid codes
must have five digits.
ITProvinceSelect
@@ -374,22 +371,20 @@ ITRegionSelect
A ``Select`` widget that uses a list of Italian regions as its choices.
-
Japan (``django.contrib.localflavor.jp``)
=========================================
JPPostalCodeField
-----------------
-A form field that validates input as a Japanese postcode.
-It accepts seven digits, with or without a hyphen.
+A form field that validates input as a Japanese postcode. It accepts seven
+digits, with or without a hyphen.
JPPrefectureSelect
------------------
A ``Select`` widget that uses a list of Japanese prefectures as its choices.
-
Mexico (``django.contrib.localflavor.mx``)
==========================================
@@ -398,7 +393,6 @@ MXStateSelect
A ``Select`` widget that uses a list of Mexican states as its choices.
-
Norway (``django.contrib.localflavor.no``)
==========================================
@@ -413,7 +407,7 @@ A form field that validates input as a Norwegian social security number
NOZipCodeField
--------------
-A form field that validates input as a Norwegian zip code. Valid codes
+A form field that validates input as a Norwegian zip code. Valid codes
have four digits.
NOMunicipalitySelect
@@ -422,7 +416,6 @@ NOMunicipalitySelect
A ``Select`` widget that uses a list of Norwegian municipalities (fylker) as
its choices.
-
Peru (``django.contrib.localflavor.pe``)
========================================
@@ -436,14 +429,13 @@ PERUCField
----------
A form field that validates input as an RUC (Registro Unico de
-Contribuyentes) number. Valid RUC numbers have eleven digits.
+Contribuyentes) number. Valid RUC numbers have 11 digits.
PEDepartmentSelect
------------------
A ``Select`` widget that uses a list of Peruvian Departments as its choices.
-
Poland (``django.contrib.localflavor.pl``)
==========================================
@@ -459,7 +451,7 @@ PLNationalBusinessRegisterField
-------------------------------
A form field that validates input as a Polish National Official Business
-Register Number (REGON_), having either seven or nine digits. The checksum
+Register Number (REGON_), having either seven or nine digits. The checksum
algorithm used for REGONs is documented at
http://wipos.p.lodz.pl/zylla/ut/nip-rego.html.
@@ -468,14 +460,14 @@ http://wipos.p.lodz.pl/zylla/ut/nip-rego.html.
PLPostalCodeField
-----------------
-A form field that validates input as a Polish postal code. The valid format
+A form field that validates input as a Polish postal code. The valid format
is XX-XXX, where X is a digit.
PLTaxNumberField
----------------
-A form field that validates input as a Polish Tax Number (NIP). Valid
-formats are XXX-XXX-XX-XX or XX-XX-XXX-XXX. The checksum algorithm used
+A form field that validates input as a Polish Tax Number (NIP). Valid
+formats are XXX-XXX-XX-XX or XX-XX-XXX-XXX. The checksum algorithm used
for NIPs is documented at http://wipos.p.lodz.pl/zylla/ut/nip-rego.html.
PLAdministrativeUnitSelect
@@ -490,14 +482,13 @@ PLVoivodeshipSelect
A ``Select`` widget that uses a list of Polish voivodeships (administrative
provinces) as its choices.
-
Slovakia (``django.contrib.localflavor.sk``)
============================================
SKPostalCodeField
-----------------
-A form field that validates input as a Slovak postal code. Valid formats
+A form field that validates input as a Slovak postal code. Valid formats
are XXXXX or XXX XX, where X is a digit.
SKDistrictSelect
@@ -510,24 +501,22 @@ SKRegionSelect
A ``Select`` widget that uses a list of Slovak regions as its choices.
-
South Africa (``django.contrib.localflavor.za``)
================================================
ZAIDField
---------
-A form field that validates input as a South African ID number. Validation
+A form field that validates input as a South African ID number. Validation
uses the Luhn checksum and a simplistic (i.e., not entirely accurate) check
for birth date.
ZAPostCodeField
---------------
-A form field that validates input as a South African postcode. Valid
+A form field that validates input as a South African postcode. Valid
postcodes must have four digits.
-
Spain (``django.contrib.localflavor.es``)
=========================================
@@ -541,23 +530,23 @@ ESCCCField
----------
A form field that validates input as a Spanish bank account number (Codigo
-Cuenta Cliente or CCC). A valid CCC number has the format
+Cuenta Cliente or CCC). A valid CCC number has the format
EEEE-OOOO-CC-AAAAAAAAAA, where the E, O, C and A digits denote the entity,
-office, checksum and account, respectively. The first checksum digit
-validates the entity and office. The second checksum digit validates the
-account. It is also valid to use a space as a delimiter, or to use no
+office, checksum and account, respectively. The first checksum digit
+validates the entity and office. The second checksum digit validates the
+account. It is also valid to use a space as a delimiter, or to use no
delimiter.
ESPhoneNumberField
------------------
-A form field that validates input as a Spanish phone number. Valid numbers
+A form field that validates input as a Spanish phone number. Valid numbers
have nine digits, the first of which is 6, 8 or 9.
ESPostalCodeField
-----------------
-A form field that validates input as a Spanish postal code. Valid codes
+A form field that validates input as a Spanish postal code. Valid codes
have five digits, the first two being in the range 01 to 52, representing
the province.
@@ -571,7 +560,6 @@ ESRegionSelect
A ``Select`` widget that uses a list of Spanish regions as its choices.
-
Switzerland (``django.contrib.localflavor.ch``)
===============================================
@@ -585,14 +573,14 @@ have the correct checksums -- see http://adi.kousz.ch/artikel/IDCHE.htm.
CHPhoneNumberField
------------------
-A form field that validates input as a Swiss phone number. The correct
-format is 0XX XXX XX XX. 0XX.XXX.XX.XX and 0XXXXXXXXX validate but are
+A form field that validates input as a Swiss phone number. The correct
+format is 0XX XXX XX XX. 0XX.XXX.XX.XX and 0XXXXXXXXX validate but are
corrected to 0XX XXX XX XX.
CHZipCodeField
--------------
-A form field that validates input as a Swiss zip code. Valid codes
+A form field that validates input as a Swiss zip code. Valid codes
consist of four digits.
CHStateSelect
@@ -600,7 +588,6 @@ CHStateSelect
A ``Select`` widget that uses a list of Swiss states as its choices.
-
United Kingdom (``django.contrib.localflavor.uk``)
==================================================
@@ -611,6 +598,15 @@ A form field that validates input as a UK postcode. The regular
expression used is sourced from the schema for British Standard BS7666
address types at http://www.govtalk.gov.uk/gdsc/schemas/bs7666-v2-0.xsd.
+UKCountySelect
+--------------
+
+A ``Select`` widget that uses a list of UK counties/regions as its choices.
+
+UKNationSelect
+--------------
+
+A ``Select`` widget that uses a list of UK nations as its choices.
United States of America (``django.contrib.localflavor.us``)
============================================================
@@ -626,13 +622,13 @@ USSocialSecurityNumberField
A form field that validates input as a U.S. Social Security Number (SSN).
A valid SSN must obey the following rules:
-* Format of XXX-XX-XXXX
-* No group of digits consisting entirely of zeroes
-* Leading group of digits cannot be 666
-* Number not in promotional block 987-65-4320 through 987-65-4329
-* Number not one known to be invalid due to widespread promotional
- use or distribution (e.g., the Woolworth's number or the 1962
- promotional number)
+ * Format of XXX-XX-XXXX
+ * No group of digits consisting entirely of zeroes
+ * Leading group of digits cannot be 666
+ * Number not in promotional block 987-65-4320 through 987-65-4329
+ * Number not one known to be invalid due to widespread promotional
+ use or distribution (e.g., the Woolworth's number or the 1962
+ promotional number)
USStateField
------------
@@ -644,11 +640,11 @@ for the given state.
USZipCodeField
--------------
-A form field that validates input as a U.S. zip code. Valid formats are
+A form field that validates input as a U.S. ZIP code. Valid formats are
XXXXX or XXXXX-XXXX.
USStateSelect
-------------
-A form Select widget that uses a list of U.S. states/territories as its
+A form ``Select`` widget that uses a list of U.S. states/territories as its
choices.
diff --git a/docs/middleware.txt b/docs/middleware.txt
index d2e2f63877..f2cf18dbdf 100644
--- a/docs/middleware.txt
+++ b/docs/middleware.txt
@@ -61,17 +61,18 @@ Adds a few conveniences for perfectionists:
settings.
If ``APPEND_SLASH`` is ``True`` and the initial URL doesn't end with a slash,
- and it is not found in urlpatterns, a new URL is formed by appending a slash
- at the end. If this new URL is found in urlpatterns, then an HTTP-redirect is
- returned to this new URL; otherwise the initial URL is processed as usual.
+ and it is not found in the URLconf, then a new URL is formed by appending a
+ slash at the end. If this new URL is found in the URLconf, then Django
+ redirects the request to this new URL. Otherwise, the initial URL is
+ processed as usual.
- So ``foo.com/bar`` will be redirected to ``foo.com/bar/`` if you do not
- have a valid urlpattern for ``foo.com/bar``, and do have a valid urlpattern
- for ``foo.com/bar/``.
+ For example, ``foo.com/bar`` will be redirected to ``foo.com/bar/`` if you
+ don't have a valid URL pattern for ``foo.com/bar`` but *do* have a valid
+ pattern for ``foo.com/bar/``.
- **New in Django development version:** The behaviour of ``APPEND_SLASH`` has
- changed slightly in the development version (it didn't used to check to see
- if the pattern was matched in the URL patterns).
+ **New in Django development version:** The behavior of ``APPEND_SLASH`` has
+ changed slightly in the development version. It didn't used to check whether
+ the pattern was matched in the URLconf.
If ``PREPEND_WWW`` is ``True``, URLs that lack a leading "www." will be
redirected to the same URL with a leading "www."
diff --git a/docs/model-api.txt b/docs/model-api.txt
index 590c601d75..3f908ec158 100644
--- a/docs/model-api.txt
+++ b/docs/model-api.txt
@@ -618,7 +618,7 @@ statement for this field.
**New in Django development version**
The name of the database tablespace to use for this field's index, if
-indeed this field is indexed. The default is the project's
+this field is indexed. The default is the project's
``DEFAULT_INDEX_TABLESPACE`` setting, if set, or the ``db_tablespace``
of the model, if any. If the backend doesn't support tablespaces, this
option is ignored.
diff --git a/docs/modelforms.txt b/docs/modelforms.txt
index 8580fe1dbd..0136540bed 100644
--- a/docs/modelforms.txt
+++ b/docs/modelforms.txt
@@ -24,12 +24,11 @@ For example::
... model = Article
# Creating a form to add an article.
- >>> article = Article()
- >>> form = ArticleForm(article)
+ >>> form = ArticleForm()
# Creating a form to change an existing article.
>>> article = Article.objects.get(pk=1)
- >>> form = ArticleForm(article)
+ >>> form = ArticleForm(instance=article)
Field types
-----------
@@ -166,18 +165,23 @@ we'll discuss in a moment.)::
The ``save()`` method
---------------------
-Every form produced by ``ModelForm`` also has a ``save()`` method. This
-method creates and saves a database object from the data bound to the form.
-A subclass of ``ModelForm`` also requires a model instance as the first
-arument to its constructor. For example::
+Every form produced by ``ModelForm`` also has a ``save()``
+method. This method creates and saves a database object from the data
+bound to the form. A subclass of ``ModelForm`` can accept an existing
+model instance as the keyword argument ``instance``; if this is
+supplied, ``save()`` will update that instance. If it's not supplied,
+``save()`` will create a new instance of the specified model::
# Create a form instance from POST data.
- >>> a = Article()
- >>> f = ArticleForm(a, request.POST)
+ >>> f = ArticleForm(request.POST)
# Save a new Article object from the form's data.
>>> new_article = f.save()
+ # Create a form to edit an existing Article.
+ >>> a = Article.objects.get(pk=1)
+ >>> f = ArticleForm(instance=a)
+
Note that ``save()`` will raise a ``ValueError`` if the data in the form
doesn't validate -- i.e., ``if form.errors``.
@@ -201,8 +205,7 @@ you've manually saved the instance produced by the form, you can invoke
``save_m2m()`` to save the many-to-many form data. For example::
# Create a form instance with POST data.
- >>> a = Author()
- >>> f = AuthorForm(a, request.POST)
+ >>> f = AuthorForm(request.POST)
# Create, but don't save the new author instance.
>>> new_author = f.save(commit=False)
@@ -274,10 +277,10 @@ model fields:
any attempt to ``save()`` a ``ModelForm`` with missing fields will fail.
To avoid this failure, you must instantiate your model with initial values
for the missing, but required fields, or use ``save(commit=False)`` and
- manually set anyextra required fields::
+ manually set any extra required fields::
- instance = Instance(requiured_field='value')
- form = InstanceForm(instance, request.POST)
+ instance = Instance(required_field='value')
+ form = InstanceForm(request.POST, instance=instance)
new_instance = form.save()
instance = form.save(commit=False)
@@ -293,7 +296,7 @@ Overriding the default field types
----------------------------------
The default field types, as described in the "Field types" table above, are
-sensible defaults; if you have a ``DateField`` in your model, chances are you'd
+sensible defaults. If you have a ``DateField`` in your model, chances are you'd
want that to be represented as a ``DateField`` in your form. But
``ModelForm`` gives you the flexibility of changing the form field type
for a given model field. You do this by declaratively specifying fields like
diff --git a/docs/newforms.txt b/docs/newforms.txt
index ac2cfb1f72..19f42cb2ee 100644
--- a/docs/newforms.txt
+++ b/docs/newforms.txt
@@ -763,7 +763,7 @@ label of each required field::
<form method="post" action="">
<dl>
{% for field in form %}
- <dt>{{ field.label_tag }}{{ field.label }}{% if field.field.required %}*{% endif %}</dt>
+ <dt>{{ field.label_tag }}{% if field.field.required %}*{% endif %}</dt>
<dd>{{ field }}</dd>
{% if field.help_text %}<dd>{{ field.help_text }}</dd>{% endif %}
{% if field.errors %}<dd class="myerrors">{{ field.errors }}</dd>{% endif %}
diff --git a/docs/request_response.txt b/docs/request_response.txt
index 6fcad8ee3d..e3d794c9ba 100644
--- a/docs/request_response.txt
+++ b/docs/request_response.txt
@@ -156,6 +156,18 @@ Methods
Returns ``True`` or ``False``, designating whether ``request.GET`` or
``request.POST`` has the given key.
+``get_host()``
+ **New in Django development version**
+
+ Returns the originating host of the request using information from the
+ ``HTTP_X_FORWARDED_HOST`` and ``HTTP_HOST`` headers (in that order). If
+ they don't provide a value, the method uses a combination of
+ ``SERVER_NAME`` and ``SERVER_PORT`` as detailed in `PEP 333`_.
+
+ .. _PEP 333: http://www.python.org/dev/peps/pep-0333/
+
+ Example: ``"127.0.0.1:8000"``
+
``get_full_path()``
Returns the ``path``, plus an appended query string, if applicable.
@@ -452,7 +464,7 @@ types of HTTP responses. Like ``HttpResponse``, these subclasses live in
``HttpResponseNotModified``
The constructor doesn't take any arguments. Use this to designate that a
- page hasn't been modified since the user's last request.
+ page hasn't been modified since the user's last request (status code 304).
``HttpResponseBadRequest``
**New in Django development version.**
@@ -560,10 +572,10 @@ Three things to note about 404 views:
you must create a ``404.html`` template in the root of your
template directory. The default 404 view will use that template
for all 404 errors. The default 404 view will pass one variable
- to the template: ``request_path``, which is the URL which
- resulted in the 404.
+ to the template: ``request_path``, which is the URL that resulted
+ in the 404.
- * If ``DEBUG`` is set to ``True`` (in your settings module) then your 404
+ * If ``DEBUG`` is set to ``True`` (in your settings module), then your 404
view will never be used, and the traceback will be displayed instead.
The 500 (server error) view
diff --git a/docs/sessions.txt b/docs/sessions.txt
index eddc3f174a..6355524d2e 100644
--- a/docs/sessions.txt
+++ b/docs/sessions.txt
@@ -99,7 +99,7 @@ It implements the following standard dictionary methods:
* ``items()``
- * ``setdefault()``
+ * ``setdefault()`` (**New in Django development version**)
It also has these three methods:
diff --git a/docs/settings.txt b/docs/settings.txt
index 3157a91dc3..8478e0ce96 100644
--- a/docs/settings.txt
+++ b/docs/settings.txt
@@ -417,17 +417,21 @@ site manager(s).
DEFAULT_TABLESPACE
------------------
+**New in Django development version**
+
Default: ``''`` (Empty string)
-Default tablespace to use for models that do not specify one, if the
+Default tablespace to use for models that don't specify one, if the
backend supports it.
DEFAULT_INDEX_TABLESPACE
------------------------
+**New in Django development version**
+
Default: ``''`` (Empty string)
-Default tablespace to use for indexes on fields that do not specify
+Default tablespace to use for indexes on fields that don't specify
one, if the backend supports it.
DISALLOWED_USER_AGENTS
@@ -977,8 +981,13 @@ TEST_DATABASE_NAME
Default: ``None``
-The name of database to use when running the test suite. If a value of
-``None`` is specified, the test database will use the name ``'test_' + settings.DATABASE_NAME``. See `Testing Django Applications`_.
+The name of database to use when running the test suite.
+
+If the default value (``None``) is used with the SQLite database engine, the
+tests will use a memory resident database. For all other database engines the
+test database will use the name ``'test_' + settings.DATABASE_NAME``.
+
+See `Testing Django Applications`_.
.. _Testing Django Applications: ../testing/
diff --git a/docs/syndication_feeds.txt b/docs/syndication_feeds.txt
index 6017ab6487..b3edf4c008 100644
--- a/docs/syndication_feeds.txt
+++ b/docs/syndication_feeds.txt
@@ -234,6 +234,7 @@ request to the URL ``/rss/beats/0613/``:
``get_object()`` method, passing it the bits. In this case, bits is
``['0613']``. For a request to ``/rss/beats/0613/foo/bar/``, bits would
be ``['0613', 'foo', 'bar']``.
+
* ``get_object()`` is responsible for retrieving the given beat, from the
given ``bits``. In this case, it uses the Django database API to retrieve
the beat. Note that ``get_object()`` should raise
@@ -243,6 +244,7 @@ request to the URL ``/rss/beats/0613/``:
raises ``Beat.DoesNotExist`` on failure, and ``Beat.DoesNotExist`` is a
subclass of ``ObjectDoesNotExist``. Raising ``ObjectDoesNotExist`` in
``get_object()`` tells Django to produce a 404 error for that request.
+
* To generate the feed's ``<title>``, ``<link>`` and ``<description>``,
Django uses the ``title()``, ``link()`` and ``description()`` methods. In
the previous example, they were simple string class attributes, but this
@@ -258,9 +260,9 @@ request to the URL ``/rss/beats/0613/``:
Inside the ``link()`` method, we handle the possibility that ``obj``
might be ``None``, which can occur when the URL isn't fully specified. In
some cases, you might want to do something else in this case, which would
- mean you'd need to check for ``obj`` existing in other methods as well
- (the ``link()`` method is called very early in the feed generation
- process, so is a good place to bail out early).
+ mean you'd need to check for ``obj`` existing in other methods as well.
+ (The ``link()`` method is called very early in the feed generation
+ process, so it's a good place to bail out early.)
* Finally, note that ``items()`` in this example also takes the ``obj``
argument. The algorithm for ``items`` is the same as described in the
diff --git a/docs/templates.txt b/docs/templates.txt
index a32ab27e34..ffda321512 100644
--- a/docs/templates.txt
+++ b/docs/templates.txt
@@ -565,17 +565,17 @@ autoescape
**New in Django development version**
-Control the current auto-escaping behaviour. This tag takes either ``on`` or
+Control the current auto-escaping behavior. This tag takes either ``on`` or
``off`` as an argument and that determines whether auto-escaping is in effect
inside the block.
When auto-escaping is in effect, all variable content has HTML escaping applied
to it before placing the result into the output (but after any filters have
been applied). This is equivalent to manually applying the ``escape`` filter
-attached to each variable.
+to each variable.
-The only exceptions are variables that are already marked as 'safe' from
-escaping, either by the code that populated the variable, or because it has
+The only exceptions are variables that are already marked as "safe" from
+escaping, either by the code that populated the variable, or because it has had
the ``safe`` or ``escape`` filters applied.
block
@@ -1227,8 +1227,10 @@ Adds the arg to the value.
addslashes
~~~~~~~~~~
-Adds slashes. Useful for passing strings to JavaScript, for example.
+Adds slashes before quotes. Useful for escaping strings in CSV, for example.
+**New in Django development version**: for escaping data in JavaScript strings,
+use the `escapejs` filter instead.
capfirst
~~~~~~~~
@@ -1302,6 +1304,15 @@ applied to the result will only result in one round of escaping being done. So
it is safe to use this function even in auto-escaping environments. If you want
multiple escaping passes to be applied, use the ``force_escape`` filter.
+escapejs
+~~~~~~~~
+
+**New in Django development version**
+
+Escapes characters for use in JavaScript strings. This does *not* make the
+string safe for use in HTML, but does protect you from syntax errors when using
+templates to generate JavaScript/JSON.
+
filesizeformat
~~~~~~~~~~~~~~
@@ -1324,36 +1335,36 @@ floatformat
When used without an argument, rounds a floating-point number to one decimal
place -- but only if there's a decimal part to be displayed. For example:
-======== ======================= ======
-value Template Output
-======== ======================= ======
-34.23234 {{ value|floatformat }} 34.2
-34.00000 {{ value|floatformat }} 34
-34.26000 {{ value|floatformat }} 34.3
-======== ======================= ======
+============ =========================== ========
+``value`` Template Output
+============ =========================== ========
+``34.23234`` ``{{ value|floatformat }}`` ``34.2``
+``34.00000`` ``{{ value|floatformat }}`` ``34``
+``34.26000`` ``{{ value|floatformat }}`` ``34.3``
+============ =========================== ========
If used with a numeric integer argument, ``floatformat`` rounds a number to
that many decimal places. For example:
-======== ========================= ======
-value Template Output
-======== ========================= ======
-34.23234 {{ value|floatformat:3 }} 34.232
-34.00000 {{ value|floatformat:3 }} 34.000
-34.26000 {{ value|floatformat:3 }} 34.260
-======== ========================= ======
+============ ============================= ==========
+``value`` Template Output
+============ ============================= ==========
+``34.23234`` ``{{ value|floatformat:3 }}`` ``34.232``
+``34.00000`` ``{{ value|floatformat:3 }}`` ``34.000``
+``34.26000`` ``{{ value|floatformat:3 }}`` ``34.260``
+============ ============================= ==========
If the argument passed to ``floatformat`` is negative, it will round a number
to that many decimal places -- but only if there's a decimal part to be
displayed. For example:
-======== ============================ ======
-value Template Output
-======== ============================ ======
-34.23234 {{ value|floatformat:"-3" }} 34.232
-34.00000 {{ value|floatformat:"-3" }} 34
-34.26000 {{ value|floatformat:"-3" }} 34.260
-======== ============================ ======
+============ ================================ ==========
+``value`` Template Output
+============ ================================ ==========
+``34.23234`` ``{{ value|floatformat:"-3" }}`` ``34.232``
+``34.00000`` ``{{ value|floatformat:"-3" }}`` ``34``
+``34.26000`` ``{{ value|floatformat:"-3" }}`` ``34.260``
+============ ================================ ==========
Using ``floatformat`` with no argument is equivalent to using ``floatformat``
with an argument of ``-1``.
diff --git a/docs/templates_python.txt b/docs/templates_python.txt
index 4865f65331..014a853a41 100644
--- a/docs/templates_python.txt
+++ b/docs/templates_python.txt
@@ -691,8 +691,8 @@ This way, you'll be able to pass, say, an integer to this filter, and it
won't cause an ``AttributeError`` (because integers don't have ``lower()``
methods).
-Registering a custom filters
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Registering custom filters
+~~~~~~~~~~~~~~~~~~~~~~~~~~
Once you've written your filter definition, you need to register it with
your ``Library`` instance, to make it available to Django's template language::
diff --git a/docs/testing.txt b/docs/testing.txt
index 7705380eff..54b8ba9ae6 100644
--- a/docs/testing.txt
+++ b/docs/testing.txt
@@ -270,27 +270,21 @@ a test case, add the name of the test method to the label::
$ ./manage.py test animals.AnimalTestCase.testFluffyAnimals
-Understanding the test output
------------------------------
-
-When you run your tests, you'll see a number of messages as the test runner
-prepares itself::
+The test database
+-----------------
- Creating test database...
- Creating table myapp_animal
- Creating table myapp_mineral
- Loading 'initial_data' fixtures...
- No fixtures found.
+Tests that require a database (namely, model tests) will not use
+your "real" (production) database. A separate, blank database is created
+for the tests.
-This tells you that the test runner is creating a test database -- a blank,
-from-scratch database that it will use for any tests that happen to require a
-database (namely, model tests).
+Regardless of whether the tests pass or fail, the test database is destroyed
+when all the tests have been executed.
-Don't worry -- the test runner will not touch your "real" (production)
-database. It creates a separate database purely for the tests. This test
-database gets its name by prepending ``test_`` to the value of the
-``DATABASE_NAME`` setting. If you want to use a different name, specify the
-``TEST_DATABASE_NAME`` setting.
+By default this test database gets its name by prepending ``test_`` to the
+value of the ``DATABASE_NAME`` setting. When using the SQLite database engine
+the tests will by default use an in-memory database (i.e., the database will be
+created in memory, bypassing the filesystem entirely!). If you want to use a
+different database name, specify the ``TEST_DATABASE_NAME`` setting.
Aside from using a separate database, the test runner will otherwise use all of
the same database settings you have in your settings file: ``DATABASE_ENGINE``,
@@ -306,6 +300,22 @@ settings_ documentation for details of these advanced settings.
.. _settings: ../settings/
+Understanding the test output
+-----------------------------
+
+When you run your tests, you'll see a number of messages as the test runner
+prepares itself. You can control the level of detail of these messages with the
+``verbosity`` option on the command line::
+
+ Creating test database...
+ Creating table myapp_animal
+ Creating table myapp_mineral
+ Loading 'initial_data' fixtures...
+ No fixtures found.
+
+This tells you that the test runner is creating a test database, as described
+in the previous section.
+
Once the test database has been created, Django will run your tests.
If everything goes well, you'll see something like this::
@@ -349,9 +359,6 @@ failed and erroneous tests. If all the tests pass, the return code is 0. This
feature is useful if you're using the test-runner script in a shell script and
need to test for success or failure at that level.
-Regardless of whether the tests pass or fail, the test database is destroyed when
-all the tests have been executed.
-
Testing tools
=============
diff --git a/tests/modeltests/generic_relations/models.py b/tests/modeltests/generic_relations/models.py
index ce1d824ca8..ff86823d07 100644
--- a/tests/modeltests/generic_relations/models.py
+++ b/tests/modeltests/generic_relations/models.py
@@ -18,42 +18,42 @@ class TaggedItem(models.Model):
tag = models.SlugField()
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
-
+
content_object = generic.GenericForeignKey()
-
+
class Meta:
ordering = ["tag"]
-
+
def __unicode__(self):
return self.tag
class Animal(models.Model):
common_name = models.CharField(max_length=150)
latin_name = models.CharField(max_length=150)
-
+
tags = generic.GenericRelation(TaggedItem)
def __unicode__(self):
return self.common_name
-
+
class Vegetable(models.Model):
name = models.CharField(max_length=150)
is_yucky = models.BooleanField(default=True)
-
+
tags = generic.GenericRelation(TaggedItem)
-
+
def __unicode__(self):
return self.name
-
+
class Mineral(models.Model):
name = models.CharField(max_length=150)
hardness = models.PositiveSmallIntegerField()
-
+
# note the lack of an explicit GenericRelation here...
-
+
def __unicode__(self):
return self.name
-
+
__test__ = {'API_TESTS':"""
# Create the world in 7 lines of code...
>>> lion = Animal(common_name="Lion", latin_name="Panthera leo")
@@ -117,13 +117,13 @@ __test__ = {'API_TESTS':"""
>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
[(u'clearish', <ContentType: mineral>, 1), (u'fatty', <ContentType: vegetable>, 2), (u'salty', <ContentType: vegetable>, 2), (u'shiny', <ContentType: animal>, 2)]
-# If Generic Relation is not explicitly defined, any related objects
+# If Generic Relation is not explicitly defined, any related objects
# remain after deletion of the source object.
>>> quartz.delete()
>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
[(u'clearish', <ContentType: mineral>, 1), (u'fatty', <ContentType: vegetable>, 2), (u'salty', <ContentType: vegetable>, 2), (u'shiny', <ContentType: animal>, 2)]
-# If you delete a tag, the objects using the tag are unaffected
+# If you delete a tag, the objects using the tag are unaffected
# (other than losing a tag)
>>> tag = TaggedItem.objects.get(id=1)
>>> tag.delete()
@@ -132,4 +132,8 @@ __test__ = {'API_TESTS':"""
>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
[(u'clearish', <ContentType: mineral>, 1), (u'salty', <ContentType: vegetable>, 2), (u'shiny', <ContentType: animal>, 2)]
+>>> ctype = ContentType.objects.get_for_model(lion)
+>>> Animal.objects.filter(tags__content_type=ctype)
+[<Animal: Platypus>]
+
"""}
diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py
index cd92956270..17c3b3551c 100644
--- a/tests/modeltests/model_forms/models.py
+++ b/tests/modeltests/model_forms/models.py
@@ -143,7 +143,7 @@ familiar with the mechanics.
... model = Article
Traceback (most recent call last):
...
-ImproperlyConfigured: BadForm defines more than one model.
+ImproperlyConfigured: BadForm defines a different model than its parent.
>>> class ArticleForm(ModelForm):
... class Meta:
@@ -155,6 +155,12 @@ Traceback (most recent call last):
...
ImproperlyConfigured: BadForm's base classes define more than one model.
+This one is OK since the subclass specifies the same model as the parent.
+
+>>> class SubCategoryForm(CategoryForm):
+... class Meta:
+... model = Category
+
# Old form_for_x tests #######################################################
@@ -167,7 +173,7 @@ ImproperlyConfigured: BadForm's base classes define more than one model.
>>> class CategoryForm(ModelForm):
... class Meta:
... model = Category
->>> f = CategoryForm(Category())
+>>> f = CategoryForm()
>>> print f
<tr><th><label for="id_name">Name:</label></th><td><input id="id_name" type="text" name="name" maxlength="20" /></td></tr>
<tr><th><label for="id_slug">Slug:</label></th><td><input id="id_slug" type="text" name="slug" maxlength="20" /></td></tr>
@@ -179,13 +185,13 @@ ImproperlyConfigured: BadForm's base classes define more than one model.
>>> print f['name']
<input id="id_name" type="text" name="name" maxlength="20" />
->>> f = CategoryForm(Category(), auto_id=False)
+>>> f = CategoryForm(auto_id=False)
>>> print f.as_ul()
<li>Name: <input type="text" name="name" maxlength="20" /></li>
<li>Slug: <input type="text" name="slug" maxlength="20" /></li>
<li>The URL: <input type="text" name="url" maxlength="40" /></li>
->>> f = CategoryForm(Category(), {'name': 'Entertainment', 'slug': 'entertainment', 'url': 'entertainment'})
+>>> f = CategoryForm({'name': 'Entertainment', 'slug': 'entertainment', 'url': 'entertainment'})
>>> f.is_valid()
True
>>> f.cleaned_data
@@ -196,7 +202,7 @@ True
>>> Category.objects.all()
[<Category: Entertainment>]
->>> f = CategoryForm(Category(), {'name': "It's a test", 'slug': 'its-test', 'url': 'test'})
+>>> f = CategoryForm({'name': "It's a test", 'slug': 'its-test', 'url': 'test'})
>>> f.is_valid()
True
>>> f.cleaned_data
@@ -210,7 +216,7 @@ True
If you call save() with commit=False, then it will return an object that
hasn't yet been saved to the database. In this case, it's up to you to call
save() on the resulting model instance.
->>> f = CategoryForm(Category(), {'name': 'Third test', 'slug': 'third-test', 'url': 'third'})
+>>> f = CategoryForm({'name': 'Third test', 'slug': 'third-test', 'url': 'third'})
>>> f.is_valid()
True
>>> f.cleaned_data
@@ -225,7 +231,7 @@ True
[<Category: Entertainment>, <Category: It's a test>, <Category: Third test>]
If you call save() with invalid data, you'll get a ValueError.
->>> f = CategoryForm(Category(), {'name': '', 'slug': '', 'url': 'foo'})
+>>> f = CategoryForm({'name': '', 'slug': '', 'url': 'foo'})
>>> f.errors
{'name': [u'This field is required.'], 'slug': [u'This field is required.']}
>>> f.cleaned_data
@@ -236,7 +242,7 @@ AttributeError: 'CategoryForm' object has no attribute 'cleaned_data'
Traceback (most recent call last):
...
ValueError: The Category could not be created because the data didn't validate.
->>> f = CategoryForm(Category(), {'name': '', 'slug': '', 'url': 'foo'})
+>>> f = CategoryForm({'name': '', 'slug': '', 'url': 'foo'})
>>> f.save()
Traceback (most recent call last):
...
@@ -253,7 +259,7 @@ fields with the 'choices' attribute are represented by a ChoiceField.
>>> class ArticleForm(ModelForm):
... class Meta:
... model = Article
->>> f = ArticleForm(Article(), auto_id=False)
+>>> f = ArticleForm(auto_id=False)
>>> print f
<tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr>
<tr><th>Slug:</th><td><input type="text" name="slug" maxlength="50" /></td></tr>
@@ -286,7 +292,7 @@ from the form can't provide a value for that field!
... class Meta:
... model = Article
... fields = ('headline','pub_date')
->>> f = PartialArticleForm(Article(), auto_id=False)
+>>> f = PartialArticleForm(auto_id=False)
>>> print f
<tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr>
<tr><th>Pub date:</th><td><input type="text" name="pub_date" /></td></tr>
@@ -298,7 +304,7 @@ current values are inserted as 'initial' data in each Field.
>>> class RoykoForm(ModelForm):
... class Meta:
... model = Writer
->>> f = RoykoForm(w, auto_id=False)
+>>> f = RoykoForm(auto_id=False, instance=w)
>>> print f
<tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="50" /><br />Use both first and last names.</td></tr>
@@ -309,7 +315,7 @@ current values are inserted as 'initial' data in each Field.
>>> class TestArticleForm(ModelForm):
... class Meta:
... model = Article
->>> f = TestArticleForm(art, auto_id=False)
+>>> f = TestArticleForm(auto_id=False, instance=art)
>>> print f.as_ul()
<li>Headline: <input type="text" name="headline" value="Test article" maxlength="50" /></li>
<li>Slug: <input type="text" name="slug" value="test-article" maxlength="50" /></li>
@@ -331,7 +337,7 @@ current values are inserted as 'initial' data in each Field.
<option value="2">It&#39;s a test</option>
<option value="3">Third test</option>
</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
->>> f = TestArticleForm(art, {'headline': u'Test headline', 'slug': 'test-headline', 'pub_date': u'1984-02-06', 'writer': u'1', 'article': 'Hello.'})
+>>> f = TestArticleForm({'headline': u'Test headline', 'slug': 'test-headline', 'pub_date': u'1984-02-06', 'writer': u'1', 'article': 'Hello.'}, instance=art)
>>> f.is_valid()
True
>>> test_art = f.save()
@@ -347,7 +353,7 @@ by specifying a 'fields' argument to form_for_instance.
... class Meta:
... model = Article
... fields=('headline', 'slug', 'pub_date')
->>> f = PartialArticleForm(art, {'headline': u'New headline', 'slug': 'new-headline', 'pub_date': u'1988-01-04'}, auto_id=False)
+>>> f = PartialArticleForm({'headline': u'New headline', 'slug': 'new-headline', 'pub_date': u'1988-01-04'}, auto_id=False, instance=art)
>>> print f.as_ul()
<li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li>
<li>Slug: <input type="text" name="slug" value="new-headline" maxlength="50" /></li>
@@ -370,7 +376,7 @@ Add some categories and test the many-to-many form output.
>>> class TestArticleForm(ModelForm):
... class Meta:
... model = Article
->>> f = TestArticleForm(new_art, auto_id=False)
+>>> f = TestArticleForm(auto_id=False, instance=new_art)
>>> print f.as_ul()
<li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li>
<li>Slug: <input type="text" name="slug" value="new-headline" maxlength="50" /></li>
@@ -393,8 +399,8 @@ Add some categories and test the many-to-many form output.
<option value="3">Third test</option>
</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
->>> f = TestArticleForm(new_art, {'headline': u'New headline', 'slug': u'new-headline', 'pub_date': u'1988-01-04',
-... 'writer': u'1', 'article': u'Hello.', 'categories': [u'1', u'2']})
+>>> f = TestArticleForm({'headline': u'New headline', 'slug': u'new-headline', 'pub_date': u'1988-01-04',
+... 'writer': u'1', 'article': u'Hello.', 'categories': [u'1', u'2']}, instance=new_art)
>>> new_art = f.save()
>>> new_art.id
1
@@ -403,8 +409,8 @@ Add some categories and test the many-to-many form output.
[<Category: Entertainment>, <Category: It's a test>]
Now, submit form data with no categories. This deletes the existing categories.
->>> f = TestArticleForm(new_art, {'headline': u'New headline', 'slug': u'new-headline', 'pub_date': u'1988-01-04',
-... 'writer': u'1', 'article': u'Hello.'})
+>>> f = TestArticleForm({'headline': u'New headline', 'slug': u'new-headline', 'pub_date': u'1988-01-04',
+... 'writer': u'1', 'article': u'Hello.'}, instance=new_art)
>>> new_art = f.save()
>>> new_art.id
1
@@ -416,7 +422,7 @@ Create a new article, with categories, via the form.
>>> class ArticleForm(ModelForm):
... class Meta:
... model = Article
->>> f = ArticleForm(Article(), {'headline': u'The walrus was Paul', 'slug': u'walrus-was-paul', 'pub_date': u'1967-11-01',
+>>> f = ArticleForm({'headline': u'The walrus was Paul', 'slug': u'walrus-was-paul', 'pub_date': u'1967-11-01',
... 'writer': u'1', 'article': u'Test.', 'categories': [u'1', u'2']})
>>> new_art = f.save()
>>> new_art.id
@@ -429,7 +435,7 @@ Create a new article, with no categories, via the form.
>>> class ArticleForm(ModelForm):
... class Meta:
... model = Article
->>> f = ArticleForm(Article(), {'headline': u'The walrus was Paul', 'slug': u'walrus-was-paul', 'pub_date': u'1967-11-01',
+>>> f = ArticleForm({'headline': u'The walrus was Paul', 'slug': u'walrus-was-paul', 'pub_date': u'1967-11-01',
... 'writer': u'1', 'article': u'Test.'})
>>> new_art = f.save()
>>> new_art.id
@@ -443,7 +449,7 @@ The m2m data won't be saved until save_m2m() is invoked on the form.
>>> class ArticleForm(ModelForm):
... class Meta:
... model = Article
->>> f = ArticleForm(Article(), {'headline': u'The walrus was Paul', 'slug': 'walrus-was-paul', 'pub_date': u'1967-11-01',
+>>> f = ArticleForm({'headline': u'The walrus was Paul', 'slug': 'walrus-was-paul', 'pub_date': u'1967-11-01',
... 'writer': u'1', 'article': u'Test.', 'categories': [u'1', u'2']})
>>> new_art = f.save(commit=False)
@@ -474,7 +480,7 @@ existing Category instance.
<Category: Third test>
>>> cat.id
3
->>> form = ShortCategory(cat, {'name': 'Third', 'slug': 'third', 'url': '3rd'})
+>>> form = ShortCategory({'name': 'Third', 'slug': 'third', 'url': '3rd'}, instance=cat)
>>> form.save()
<Category: Third>
>>> Category.objects.get(id=3)
@@ -486,7 +492,7 @@ the data in the database when the form is instantiated.
>>> class ArticleForm(ModelForm):
... class Meta:
... model = Article
->>> f = ArticleForm(Article(), auto_id=False)
+>>> f = ArticleForm(auto_id=False)
>>> print f.as_ul()
<li>Headline: <input type="text" name="headline" maxlength="50" /></li>
<li>Slug: <input type="text" name="slug" maxlength="50" /></li>
@@ -690,7 +696,7 @@ ValidationError: [u'Select a valid choice. 4 is not one of the available choices
>>> class PhoneNumberForm(ModelForm):
... class Meta:
... model = PhoneNumber
->>> f = PhoneNumberForm(PhoneNumber(), {'phone': '(312) 555-1212', 'description': 'Assistance'})
+>>> f = PhoneNumberForm({'phone': '(312) 555-1212', 'description': 'Assistance'})
>>> f.is_valid()
True
>>> f.cleaned_data
diff --git a/tests/modeltests/serializers/models.py b/tests/modeltests/serializers/models.py
index 1c7dbabfd1..0ccc19f895 100644
--- a/tests/modeltests/serializers/models.py
+++ b/tests/modeltests/serializers/models.py
@@ -218,3 +218,41 @@ None
3.4
"""}
+
+try:
+ import yaml
+ __test__['YAML'] = """
+# Create some data:
+
+>>> articles = Article.objects.all().order_by("id")[:2]
+>>> from django.core import serializers
+
+# test if serial
+
+>>> serialized = serializers.serialize("yaml", articles)
+>>> print serialized
+- fields:
+ author: 2
+ categories: [3, 1]
+ headline: Just kidding; I love TV poker
+ pub_date: 2006-06-16 11:00:00
+ model: serializers.article
+ pk: 1
+- fields:
+ author: 1
+ categories: [2, 3]
+ headline: Time to reform copyright
+ pub_date: 2006-06-16 13:00:11
+ model: serializers.article
+ pk: 2
+<BLANKLINE>
+
+>>> obs = list(serializers.deserialize("yaml", serialized))
+>>> for i in obs:
+... print i
+<DeserializedObject: Just kidding; I love TV poker>
+<DeserializedObject: Time to reform copyright>
+
+"""
+except ImportError: pass
+
diff --git a/tests/regressiontests/cache/tests.py b/tests/regressiontests/cache/tests.py
index 9ac2722b0a..f050348c77 100644
--- a/tests/regressiontests/cache/tests.py
+++ b/tests/regressiontests/cache/tests.py
@@ -3,8 +3,8 @@
# Unit tests for cache framework
# Uses whatever cache backend is set in the test settings file.
-import time, unittest
-
+import time
+import unittest
from django.core.cache import cache
from django.utils.cache import patch_vary_headers
from django.http import HttpResponse
@@ -27,7 +27,7 @@ class Cache(unittest.TestCase):
cache.add("addkey1", "value")
cache.add("addkey1", "newvalue")
self.assertEqual(cache.get("addkey1"), "value")
-
+
def test_non_existent(self):
# get with non-existent keys
self.assertEqual(cache.get("does_not_exist"), None)
@@ -76,10 +76,16 @@ class Cache(unittest.TestCase):
self.assertEqual(cache.get("stuff"), stuff)
def test_expiration(self):
- # expiration
- cache.set('expire', 'very quickly', 1)
- time.sleep(2)
- self.assertEqual(cache.get("expire"), None)
+ cache.set('expire1', 'very quickly', 1)
+ cache.set('expire2', 'very quickly', 1)
+ cache.set('expire3', 'very quickly', 1)
+
+ time.sleep(2)
+ self.assertEqual(cache.get("expire1"), None)
+
+ cache.add("expire2", "newvalue")
+ self.assertEqual(cache.get("expire2"), "newvalue")
+ self.assertEqual(cache.has_key("expire3"), False)
def test_unicode(self):
stuff = {
@@ -92,6 +98,44 @@ class Cache(unittest.TestCase):
cache.set(key, value)
self.assertEqual(cache.get(key), value)
+import os
+import md5
+import shutil
+import tempfile
+from django.core.cache.backends.filebased import CacheClass as FileCache
+
+class FileBasedCacheTests(unittest.TestCase):
+ """
+ Specific test cases for the file-based cache.
+ """
+ def setUp(self):
+ self.dirname = tempfile.mktemp()
+ os.mkdir(self.dirname)
+ self.cache = FileCache(self.dirname, {})
+
+ def tearDown(self):
+ shutil.rmtree(self.dirname)
+
+ def test_hashing(self):
+ """Test that keys are hashed into subdirectories correctly"""
+ self.cache.set("foo", "bar")
+ keyhash = md5.new("foo").hexdigest()
+ keypath = os.path.join(self.dirname, keyhash[:2], keyhash[2:4], keyhash[4:])
+ self.assert_(os.path.exists(keypath))
+
+ def test_subdirectory_removal(self):
+ """
+ Make sure that the created subdirectories are correctly removed when empty.
+ """
+ self.cache.set("foo", "bar")
+ keyhash = md5.new("foo").hexdigest()
+ keypath = os.path.join(self.dirname, keyhash[:2], keyhash[2:4], keyhash[4:])
+ self.assert_(os.path.exists(keypath))
+
+ self.cache.delete("foo")
+ self.assert_(not os.path.exists(keypath))
+ self.assert_(not os.path.exists(os.path.dirname(keypath)))
+ self.assert_(not os.path.exists(os.path.dirname(os.path.dirname(keypath))))
class CacheUtils(unittest.TestCase):
"""TestCase for django.utils.cache functions."""
diff --git a/tests/regressiontests/defaultfilters/tests.py b/tests/regressiontests/defaultfilters/tests.py
index bfa03cd6e1..668ecb9d5a 100644
--- a/tests/regressiontests/defaultfilters/tests.py
+++ b/tests/regressiontests/defaultfilters/tests.py
@@ -49,6 +49,18 @@ u'\\\\ : backslashes, too'
>>> capfirst(u'hello world')
u'Hello world'
+>>> escapejs(u'"double quotes" and \'single quotes\'')
+u'\\"double quotes\\" and \\\'single quotes\\\''
+
+>>> escapejs(ur'\ : backslashes, too')
+u'\\\\ : backslashes, too'
+
+>>> escapejs(u'and lots of whitespace: \r\n\t\v\f\b')
+u'and lots of whitespace: \\r\\n\\t\\v\\f\\b'
+
+>>> escapejs(ur'<script>and this</script>')
+u'<script>and this<\\/script>'
+
>>> fix_ampersands(u'Jack & Jill & Jeroboam')
u'Jack &amp; Jill &amp; Jeroboam'
diff --git a/tests/regressiontests/forms/localflavor/cl.py b/tests/regressiontests/forms/localflavor/cl.py
index 407f6610e4..23e7a41902 100644
--- a/tests/regressiontests/forms/localflavor/cl.py
+++ b/tests/regressiontests/forms/localflavor/cl.py
@@ -41,7 +41,7 @@ Strict RUT usage (does not allow imposible values)
>>> rut.clean('11-6')
Traceback (most recent call last):
...
-ValidationError: [u'Enter valid a Chilean RUT. The format is XX.XXX.XXX-X.']
+ValidationError: [u'Enter a valid Chilean RUT. The format is XX.XXX.XXX-X.']
# valid format, bad verifier.
>>> rut.clean('11.111.111-0')
@@ -53,17 +53,17 @@ ValidationError: [u'The Chilean RUT is not valid.']
>>> rut.clean('767484100')
Traceback (most recent call last):
...
-ValidationError: [u'Enter valid a Chilean RUT. The format is XX.XXX.XXX-X.']
+ValidationError: [u'Enter a valid Chilean RUT. The format is XX.XXX.XXX-X.']
>>> rut.clean('78.412.790-7')
u'78.412.790-7'
>>> rut.clean('8.334.6043')
Traceback (most recent call last):
...
-ValidationError: [u'Enter valid a Chilean RUT. The format is XX.XXX.XXX-X.']
+ValidationError: [u'Enter a valid Chilean RUT. The format is XX.XXX.XXX-X.']
>>> rut.clean('76793310-K')
Traceback (most recent call last):
...
-ValidationError: [u'Enter valid a Chilean RUT. The format is XX.XXX.XXX-X.']
+ValidationError: [u'Enter a valid Chilean RUT. The format is XX.XXX.XXX-X.']
## CLRegionSelect #########################################################
>>> from django.contrib.localflavor.cl.forms import CLRegionSelect
diff --git a/tests/regressiontests/forms/localflavor/uk.py b/tests/regressiontests/forms/localflavor/uk.py
index d7848f70a8..258c22e5a9 100644
--- a/tests/regressiontests/forms/localflavor/uk.py
+++ b/tests/regressiontests/forms/localflavor/uk.py
@@ -12,13 +12,15 @@ u'BT32 4PX'
>>> f.clean('GIR 0AA')
u'GIR 0AA'
>>> f.clean('BT324PX')
+u'BT32 4PX'
+>>> f.clean('1NV 4L1D')
Traceback (most recent call last):
...
-ValidationError: [u'Enter a postcode. A space is required between the two postcode parts.']
->>> f.clean('1NV 4L1D')
+ValidationError: [u'Enter a valid postcode.']
+>>> f.clean('1NV4L1D')
Traceback (most recent call last):
...
-ValidationError: [u'Enter a postcode. A space is required between the two postcode parts.']
+ValidationError: [u'Enter a valid postcode.']
>>> f.clean(None)
Traceback (most recent call last):
...
@@ -27,7 +29,20 @@ ValidationError: [u'This field is required.']
Traceback (most recent call last):
...
ValidationError: [u'This field is required.']
-
+>>> f.clean(' so11aa ')
+u'SO1 1AA'
+>>> f.clean(' so1 1aa ')
+u'SO1 1AA'
+>>> f.clean('G2 3wt')
+u'G2 3WT'
+>>> f.clean('EC1A 1BB')
+u'EC1A 1BB'
+>>> f.clean('Ec1a1BB')
+u'EC1A 1BB'
+>>> f.clean(' b0gUS')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid postcode.']
>>> f = UKPostcodeField(required=False)
>>> f.clean('BT32 4PX')
u'BT32 4PX'
@@ -36,11 +51,9 @@ u'GIR 0AA'
>>> f.clean('1NV 4L1D')
Traceback (most recent call last):
...
-ValidationError: [u'Enter a postcode. A space is required between the two postcode parts.']
+ValidationError: [u'Enter a valid postcode.']
>>> f.clean('BT324PX')
-Traceback (most recent call last):
-...
-ValidationError: [u'Enter a postcode. A space is required between the two postcode parts.']
+u'BT32 4PX'
>>> f.clean(None)
u''
>>> f.clean('')
diff --git a/tests/regressiontests/httpwrappers/tests.py b/tests/regressiontests/httpwrappers/tests.py
index 5cfae029bb..31b956a99d 100644
--- a/tests/regressiontests/httpwrappers/tests.py
+++ b/tests/regressiontests/httpwrappers/tests.py
@@ -391,9 +391,45 @@ u'\ufffd'
>>> q.getlist('foo')
[u'bar', u'\ufffd']
+
+######################################
+# HttpResponse with Unicode headers #
+######################################
+
+>>> r = HttpResponse()
+
+If we insert a unicode value it will be converted to an ascii
+string. This makes sure we comply with the HTTP specifications.
+
+>>> r['value'] = u'test value'
+>>> isinstance(r['value'], str)
+True
+
+An error is raised When a unicode object with non-ascii is assigned.
+
+>>> r['value'] = u't\xebst value' # doctest:+ELLIPSIS
+Traceback (most recent call last):
+...
+UnicodeEncodeError: ..., HTTP response headers must be in US-ASCII format
+
+The response also converts unicode keys to strings.
+
+>>> r[u'test'] = 'testing key'
+>>> l = list(r.items())
+>>> l.sort()
+>>> l[1]
+('test', 'testing key')
+
+It will also raise errors for keys with non-ascii data.
+
+>>> r[u't\xebst'] = 'testing key' # doctest:+ELLIPSIS
+Traceback (most recent call last):
+...
+UnicodeEncodeError: ..., HTTP response headers must be in US-ASCII format
+
"""
-from django.http import QueryDict
+from django.http import QueryDict, HttpResponse
if __name__ == "__main__":
import doctest
diff --git a/tests/regressiontests/maxlength/tests.py b/tests/regressiontests/maxlength/tests.py
index 8a5f874c78..c7ed1f91c0 100644
--- a/tests/regressiontests/maxlength/tests.py
+++ b/tests/regressiontests/maxlength/tests.py
@@ -22,12 +22,12 @@ Don't print out the deprecation warnings during testing.
>>> legacy_maxlength(10, 12)
Traceback (most recent call last):
...
-TypeError: field can not take both the max_length argument and the legacy maxlength argument.
+TypeError: Field cannot take both the max_length argument and the legacy maxlength argument.
>>> legacy_maxlength(0, 10)
Traceback (most recent call last):
...
-TypeError: field can not take both the max_length argument and the legacy maxlength argument.
+TypeError: Field cannot take both the max_length argument and the legacy maxlength argument.
>>> legacy_maxlength(0, None)
0
@@ -48,7 +48,7 @@ TypeError: field can not take both the max_length argument and the legacy maxlen
>>> fields.Field(maxlength=10, max_length=15)
Traceback (most recent call last):
...
-TypeError: field can not take both the max_length argument and the legacy maxlength argument.
+TypeError: Field cannot take both the max_length argument and the legacy maxlength argument.
# Test max_length
>>> new.max_length
diff --git a/tests/regressiontests/model_regress/models.py b/tests/regressiontests/model_regress/models.py
index 00c3bc96f0..02e73a5aa9 100644
--- a/tests/regressiontests/model_regress/models.py
+++ b/tests/regressiontests/model_regress/models.py
@@ -11,6 +11,7 @@ class Article(models.Model):
pub_date = models.DateTimeField()
status = models.IntegerField(blank=True, null=True, choices=CHOICES)
misc_data = models.CharField(max_length=100, blank=True)
+ article_text = models.TextField()
class Meta:
ordering = ('pub_date','headline')
@@ -41,5 +42,14 @@ Empty strings should be returned as Unicode
>>> a2 = Article.objects.get(pk=a.id)
>>> a2.misc_data
u''
+
+# TextFields can hold more than 4000 characters (this was broken in Oracle).
+>>> a3 = Article(headline="Really, really big", pub_date=datetime.now())
+>>> a3.article_text = "ABCDE" * 1000
+>>> a3.save()
+>>> a4 = Article.objects.get(pk=a3.id)
+>>> len(a4.article_text)
+5000
+
"""
}
diff --git a/tests/regressiontests/templates/filters.py b/tests/regressiontests/templates/filters.py
index 4175bdbe5f..f38b2cdef1 100644
--- a/tests/regressiontests/templates/filters.py
+++ b/tests/regressiontests/templates/filters.py
@@ -108,8 +108,8 @@ def get_filter_tests():
'filter-urlize05': ('{% autoescape off %}{{ a|urlize }}{% endautoescape %}', {"a": "<script>alert('foo')</script>"}, "<script>alert('foo')</script>"),
'filter-urlize06': ('{{ a|urlize }}', {"a": "<script>alert('foo')</script>"}, '&lt;script&gt;alert(&#39;foo&#39;)&lt;/script&gt;'),
- 'filter-urlizetrunc01': ('{% autoescape off %}{{ a|urlizetrunc:"8" }} {{ b|urlizetrunc:"8" }}{% endautoescape %}', {"a": "http://example.com/x=&y=", "b": mark_safe("http://example.com?x=&y=")}, u'<a href="http://example.com/x=&y=" rel="nofollow">http:...</a> <a href="http://example.com?x=&y=" rel="nofollow">http:...</a>'),
- 'filter-urlizetrunc02': ('{{ a|urlizetrunc:"8" }} {{ b|urlizetrunc:"8" }}', {"a": "http://example.com/x=&y=", "b": mark_safe("http://example.com?x=&y=")}, u'<a href="http://example.com/x=&y=" rel="nofollow">http:...</a> <a href="http://example.com?x=&y=" rel="nofollow">http:...</a>'),
+ 'filter-urlizetrunc01': ('{% autoescape off %}{{ a|urlizetrunc:"8" }} {{ b|urlizetrunc:"8" }}{% endautoescape %}', {"a": '"Unsafe" http://example.com/x=&y=', "b": mark_safe('&quot;Safe&quot; http://example.com?x=&y=')}, u'"Unsafe" <a href="http://example.com/x=&y=" rel="nofollow">http:...</a> &quot;Safe&quot; <a href="http://example.com?x=&y=" rel="nofollow">http:...</a>'),
+ 'filter-urlizetrunc02': ('{{ a|urlizetrunc:"8" }} {{ b|urlizetrunc:"8" }}', {"a": '"Unsafe" http://example.com/x=&y=', "b": mark_safe('&quot;Safe&quot; http://example.com?x=&y=')}, u'&quot;Unsafe&quot; <a href="http://example.com/x=&y=" rel="nofollow">http:...</a> &quot;Safe&quot; <a href="http://example.com?x=&y=" rel="nofollow">http:...</a>'),
'filter-wordcount01': ('{% autoescape off %}{{ a|wordcount }} {{ b|wordcount }}{% endautoescape %}', {"a": "a & b", "b": mark_safe("a &amp; b")}, "3 3"),
'filter-wordcount02': ('{{ a|wordcount }} {{ b|wordcount }}', {"a": "a & b", "b": mark_safe("a &amp; b")}, "3 3"),
diff --git a/tests/regressiontests/views/media/file.unknown b/tests/regressiontests/views/media/file.unknown
new file mode 100644
index 0000000000..77dcda8970
--- /dev/null
+++ b/tests/regressiontests/views/media/file.unknown
@@ -0,0 +1 @@
+An unknown file extension.
diff --git a/tests/regressiontests/views/tests/static.py b/tests/regressiontests/views/tests/static.py
index c731b249e8..d7e87d19d2 100644
--- a/tests/regressiontests/views/tests/static.py
+++ b/tests/regressiontests/views/tests/static.py
@@ -13,11 +13,15 @@ class StaticTests(TestCase):
response = self.client.get('/views/site_media/%s' % filename)
file = open(path.join(media_dir, filename))
self.assertEquals(file.read(), response.content)
+ self.assertEquals(len(response.content), int(response['Content-Length']))
+
+ def test_unknown_mime_type(self):
+ response = self.client.get('/views/site_media/file.unknown')
+ self.assertEquals('application/octet-stream', response['Content-Type'])
def test_copes_with_empty_path_component(self):
file_name = 'file.txt'
response = self.client.get('/views/site_media//%s' % file_name)
file = open(path.join(media_dir, file_name))
self.assertEquals(file.read(), response.content)
-
-
+
diff --git a/tests/runtests.py b/tests/runtests.py
index 2d3b737cec..599916ab23 100755
--- a/tests/runtests.py
+++ b/tests/runtests.py
@@ -93,6 +93,7 @@ def django_tests(verbosity, interactive, test_labels):
old_root_urlconf = settings.ROOT_URLCONF
old_template_dirs = settings.TEMPLATE_DIRS
old_use_i18n = settings.USE_I18N
+ old_login_url = settings.LOGIN_URL
old_language_code = settings.LANGUAGE_CODE
old_middleware_classes = settings.MIDDLEWARE_CLASSES
@@ -102,6 +103,7 @@ def django_tests(verbosity, interactive, test_labels):
settings.TEMPLATE_DIRS = (os.path.join(os.path.dirname(__file__), TEST_TEMPLATE_DIR),)
settings.USE_I18N = True
settings.LANGUAGE_CODE = 'en'
+ settings.LOGIN_URL = '/accounts/login/'
settings.MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
@@ -153,6 +155,7 @@ def django_tests(verbosity, interactive, test_labels):
settings.TEMPLATE_DIRS = old_template_dirs
settings.USE_I18N = old_use_i18n
settings.LANGUAGE_CODE = old_language_code
+ settings.LOGIN_URL = old_login_url
settings.MIDDLEWARE_CLASSES = old_middleware_classes
if __name__ == "__main__":