1
0
mirror of https://github.com/django/django.git synced 2025-07-05 02:09:13 +00:00

newforms-admin: Merged from trunk up to [6952].

git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@6955 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Joseph Kocherhans 2007-12-19 05:20:55 +00:00
parent 3f494f1823
commit f4c7a0dcb6
96 changed files with 1606 additions and 973 deletions

View File

@ -131,6 +131,7 @@ answer newbie questions, and generally made Django that much better:
Matthew Flanagan <http://wadofstuff.blogspot.com> Matthew Flanagan <http://wadofstuff.blogspot.com>
Eric Floehr <eric@intellovations.com> Eric Floehr <eric@intellovations.com>
Vincent Foley <vfoleybourgon@yahoo.ca> Vincent Foley <vfoleybourgon@yahoo.ca>
Rudolph Froger <rfroger@estrate.nl>
Jorge Gajon <gajon@gajon.org> Jorge Gajon <gajon@gajon.org>
gandalf@owca.info gandalf@owca.info
Marc Garcia <marc.garcia@accopensys.com> Marc Garcia <marc.garcia@accopensys.com>
@ -306,6 +307,7 @@ answer newbie questions, and generally made Django that much better:
Georgi Stanojevski <glisha@gmail.com> Georgi Stanojevski <glisha@gmail.com>
Vasiliy Stavenko <stavenko@gmail.com> Vasiliy Stavenko <stavenko@gmail.com>
Thomas Steinacher <http://www.eggdrop.ch/> Thomas Steinacher <http://www.eggdrop.ch/>
Johan C. Stöver <johan@nilling.nl>
nowell strite nowell strite
Thomas Stromberg <tstromberg@google.com> Thomas Stromberg <tstromberg@google.com>
Sundance Sundance
@ -333,6 +335,7 @@ answer newbie questions, and generally made Django that much better:
tt@gurgle.no tt@gurgle.no
Amit Upadhyay Amit Upadhyay
Geert Vanderkelen Geert Vanderkelen
I.S. van Oostveen <v.oostveen@idca.nl>
viestards.lists@gmail.com viestards.lists@gmail.com
George Vilches <gav@thataddress.com> George Vilches <gav@thataddress.com>
Vlado <vlado@labath.org> Vlado <vlado@labath.org>

View File

@ -52,7 +52,9 @@ class LazySettings(object):
if not settings_module: # If it's set but is an empty string. if not settings_module: # If it's set but is an empty string.
raise KeyError raise KeyError
except 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) self._target = Settings(settings_module)

View File

@ -5,9 +5,9 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: django\n" "Project-Id-Version: django\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2007-11-05 17:33+0100\n" "POT-Creation-Date: 2007-12-02 22:26+0100\n"
"PO-Revision-Date: 2007-11-05 17:34+0100\n" "PO-Revision-Date: 2007-12-02 22:32+0100\n"
"Last-Translator: Marc Garcia <marc.garcia@accopensys.com>\n" "Last-Translator: Marc Fargas <telenieko@telenieko.com>\n"
"Language-Team: <es@li.org>\n" "Language-Team: <es@li.org>\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@ -194,7 +194,7 @@ msgstr "Xinés simplificat"
msgid "Traditional Chinese" msgid "Traditional Chinese"
msgstr "Xinés tradicional" msgstr "Xinés tradicional"
#: contrib/admin/filterspecs.py:42 #: contrib/admin/filterspecs.py:44
#, python-format #, python-format
msgid "" msgid ""
"<h3>By %s:</h3>\n" "<h3>By %s:</h3>\n"
@ -203,71 +203,71 @@ msgstr ""
"<h3>Per %s:</h3>\n" "<h3>Per %s:</h3>\n"
"<ul>\n" "<ul>\n"
#: contrib/admin/filterspecs.py:72 contrib/admin/filterspecs.py:90 #: contrib/admin/filterspecs.py:74 contrib/admin/filterspecs.py:92
#: contrib/admin/filterspecs.py:145 contrib/admin/filterspecs.py:171 #: contrib/admin/filterspecs.py:147 contrib/admin/filterspecs.py:173
msgid "All" msgid "All"
msgstr "Tots" msgstr "Tots"
#: contrib/admin/filterspecs.py:111 #: contrib/admin/filterspecs.py:113
msgid "Any date" msgid "Any date"
msgstr "Qualsevol data" msgstr "Qualsevol data"
#: contrib/admin/filterspecs.py:112 #: contrib/admin/filterspecs.py:114
msgid "Today" msgid "Today"
msgstr "Avui" msgstr "Avui"
#: contrib/admin/filterspecs.py:115 #: contrib/admin/filterspecs.py:117
msgid "Past 7 days" msgid "Past 7 days"
msgstr "Últims 7 dies" msgstr "Últims 7 dies"
#: contrib/admin/filterspecs.py:117 #: contrib/admin/filterspecs.py:119
msgid "This month" msgid "This month"
msgstr "Aquest mes" msgstr "Aquest mes"
#: contrib/admin/filterspecs.py:119 #: contrib/admin/filterspecs.py:121
msgid "This year" msgid "This year"
msgstr "Aquest any" msgstr "Aquest any"
#: contrib/admin/filterspecs.py:145 newforms/widgets.py:221 #: contrib/admin/filterspecs.py:147 newforms/widgets.py:231
#: oldforms/__init__.py:591 #: oldforms/__init__.py:592
msgid "Yes" msgid "Yes"
msgstr "Si" msgstr "Si"
#: contrib/admin/filterspecs.py:145 newforms/widgets.py:221 #: contrib/admin/filterspecs.py:147 newforms/widgets.py:231
#: oldforms/__init__.py:591 #: oldforms/__init__.py:592
msgid "No" msgid "No"
msgstr "No" msgstr "No"
#: contrib/admin/filterspecs.py:152 newforms/widgets.py:221 #: contrib/admin/filterspecs.py:154 newforms/widgets.py:231
#: oldforms/__init__.py:591 #: oldforms/__init__.py:592
msgid "Unknown" msgid "Unknown"
msgstr "Desconegut" msgstr "Desconegut"
#: contrib/admin/models.py:17 #: contrib/admin/models.py:18
msgid "action time" msgid "action time"
msgstr "moment de l'acció" msgstr "moment de l'acció"
#: contrib/admin/models.py:20 #: contrib/admin/models.py:21
msgid "object id" msgid "object id"
msgstr "id del objecte" msgstr "id del objecte"
#: contrib/admin/models.py:21 #: contrib/admin/models.py:22
msgid "object repr" msgid "object repr"
msgstr "'repr' de l'objecte" msgstr "'repr' de l'objecte"
#: contrib/admin/models.py:22 #: contrib/admin/models.py:23
msgid "action flag" msgid "action flag"
msgstr "marca de l'acció" msgstr "marca de l'acció"
#: contrib/admin/models.py:23 #: contrib/admin/models.py:24
msgid "change message" msgid "change message"
msgstr "missatge del canvi" msgstr "missatge del canvi"
#: contrib/admin/models.py:26 #: contrib/admin/models.py:27
msgid "log entry" msgid "log entry"
msgstr "entrada del registre" msgstr "entrada del registre"
#: contrib/admin/models.py:27 #: contrib/admin/models.py:28
msgid "log entries" msgid "log entries"
msgstr "entrades del registre" msgstr "entrades del registre"
@ -469,7 +469,7 @@ msgid "Password:"
msgstr "Contrasenya:" msgstr "Contrasenya:"
#: contrib/admin/templates/admin/login.html:25 #: contrib/admin/templates/admin/login.html:25
#: contrib/admin/views/decorators.py:24 #: contrib/admin/views/decorators.py:25
msgid "Log in" msgid "Log in"
msgstr "Iniciar sessió" msgstr "Iniciar sessió"
@ -769,17 +769,17 @@ msgstr "Actualment:"
msgid "Change:" msgid "Change:"
msgstr "Modificar:" msgstr "Modificar:"
#: contrib/admin/templatetags/admin_list.py:254 #: contrib/admin/templatetags/admin_list.py:257
msgid "All dates" msgid "All dates"
msgstr "Totes les 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 #, python-format
msgid "The %(name)s \"%(obj)s\" was added successfully." msgid "The %(name)s \"%(obj)s\" was added successfully."
msgstr "El/la %(name)s \"%(obj)s\".ha estat agregat/da amb èxit." 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/auth.py:25 contrib/admin/views/main.py:271
#: contrib/admin/views/main.py:354 #: contrib/admin/views/main.py:356
msgid "You may edit it again below." msgid "You may edit it again below."
msgstr "Pot editar-lo de nou abaix." msgstr "Pot editar-lo de nou abaix."
@ -796,7 +796,7 @@ msgstr "Canvi de clau exitós"
msgid "Change password: %s" msgid "Change password: %s"
msgstr "Canviar clau: %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 "" msgid ""
"Please enter a correct username and password. Note that both fields are case-" "Please enter a correct username and password. Note that both fields are case-"
"sensitive." "sensitive."
@ -804,7 +804,7 @@ msgstr ""
"Si us plau, introdueixi un nom d'usuari i contrasenya vàlids. Tingui en " "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." "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 "" msgid ""
"Please log in again, because your session has expired. Don't worry: Your " "Please log in again, because your session has expired. Don't worry: Your "
"submission has been saved." "submission has been saved."
@ -812,7 +812,7 @@ msgstr ""
"Si us plau, identifiquis de nou doncs la seva sessió ha expirat. No es " "Si us plau, identifiquis de nou doncs la seva sessió ha expirat. No es "
"preocupi, el seu enviament està emmagatzemat." "preocupi, el seu enviament està emmagatzemat."
#: contrib/admin/views/decorators.py:69 #: contrib/admin/views/decorators.py:70
msgid "" msgid ""
"Looks like your browser isn't configured to accept cookies. Please enable " "Looks like your browser isn't configured to accept cookies. Please enable "
"cookies, reload this page, and try again." "cookies, reload this page, and try again."
@ -821,247 +821,247 @@ msgstr ""
"'cookies' (galetes). Si us plau, habiliti les 'cookies', recarregui aquesta " "'cookies' (galetes). Si us plau, habiliti les 'cookies', recarregui aquesta "
"pàgina i provi-ho de nou. " "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." msgid "Usernames cannot contain the '@' character."
msgstr "Els noms d'usuari no poden contenir el caracter '@'." msgstr "Els noms d'usuari no poden contenir el caracter '@'."
#: contrib/admin/views/decorators.py:85 #: contrib/admin/views/decorators.py:86
#, python-format #, python-format
msgid "Your e-mail address is not your username. Try '%s' instead." msgid "Your e-mail address is not your username. Try '%s' instead."
msgstr "" msgstr ""
"La seva adreça de correu no és el seu nom d'usuari. Provi '%s' en tot cas." "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:48 contrib/admin/views/doc.py:50
#: contrib/admin/views/doc.py:51 #: contrib/admin/views/doc.py:52
msgid "tag:" msgid "tag:"
msgstr "etiqueta:" msgstr "etiqueta:"
#: contrib/admin/views/doc.py:78 contrib/admin/views/doc.py:80 #: contrib/admin/views/doc.py:79 contrib/admin/views/doc.py:81
#: contrib/admin/views/doc.py:82 #: contrib/admin/views/doc.py:83
msgid "filter:" msgid "filter:"
msgstr "filtre:" msgstr "filtre:"
#: contrib/admin/views/doc.py:136 contrib/admin/views/doc.py:138 #: contrib/admin/views/doc.py:137 contrib/admin/views/doc.py:139
#: contrib/admin/views/doc.py:140 #: contrib/admin/views/doc.py:141
msgid "view:" msgid "view:"
msgstr "vista:" msgstr "vista:"
#: contrib/admin/views/doc.py:165 #: contrib/admin/views/doc.py:166
#, python-format #, python-format
msgid "App %r not found" msgid "App %r not found"
msgstr "La aplicació %r no s'ha pogut trobar" msgstr "La aplicació %r no s'ha pogut trobar"
#: contrib/admin/views/doc.py:172 #: contrib/admin/views/doc.py:173
#, python-format #, python-format
msgid "Model %(name)r not found in app %(label)r" msgid "Model %(name)r not found in app %(label)r"
msgstr "El model %(name)r no s'ha trobat en la aplicació %(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 #, python-format
msgid "the related `%(label)s.%(type)s` object" msgid "the related `%(label)s.%(type)s` object"
msgstr "el objecte relacionat `%(label)s.%(type)s`" 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:185 contrib/admin/views/doc.py:207
#: contrib/admin/views/doc.py:220 contrib/admin/views/doc.py:225 #: contrib/admin/views/doc.py:221 contrib/admin/views/doc.py:226
msgid "model:" msgid "model:"
msgstr "model:" msgstr "model:"
#: contrib/admin/views/doc.py:215 #: contrib/admin/views/doc.py:216
#, python-format #, python-format
msgid "related `%(label)s.%(name)s` objects" msgid "related `%(label)s.%(name)s` objects"
msgstr "objectes relacionats `%(label)s.%(name)s`" msgstr "objectes relacionats `%(label)s.%(name)s`"
#: contrib/admin/views/doc.py:220 #: contrib/admin/views/doc.py:221
#, python-format #, python-format
msgid "all %s" msgid "all %s"
msgstr "tots %s" msgstr "tots %s"
#: contrib/admin/views/doc.py:225 #: contrib/admin/views/doc.py:226
#, python-format #, python-format
msgid "number of %s" msgid "number of %s"
msgstr "nombre de %s" msgstr "nombre de %s"
#: contrib/admin/views/doc.py:230 #: contrib/admin/views/doc.py:231
#, python-format #, python-format
msgid "Fields on %s objects" msgid "Fields on %s objects"
msgstr "Camps en objectes %s" msgstr "Camps en objectes %s"
#: contrib/admin/views/doc.py:292 contrib/admin/views/doc.py:303 #: contrib/admin/views/doc.py:293 contrib/admin/views/doc.py:304
#: contrib/admin/views/doc.py:305 contrib/admin/views/doc.py:311 #: contrib/admin/views/doc.py:306 contrib/admin/views/doc.py:312
#: contrib/admin/views/doc.py:312 contrib/admin/views/doc.py:314 #: contrib/admin/views/doc.py:313 contrib/admin/views/doc.py:315
msgid "Integer" msgid "Integer"
msgstr "Enter" msgstr "Enter"
#: contrib/admin/views/doc.py:293 #: contrib/admin/views/doc.py:294
msgid "Boolean (Either True or False)" msgid "Boolean (Either True or False)"
msgstr "Booleà (Verdader o Fals)" 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 #, python-format
msgid "String (up to %(max_length)s)" msgid "String (up to %(max_length)s)"
msgstr "Cadena (de fins a %(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" msgid "Comma-separated integers"
msgstr "Enters separats per comes" msgstr "Enters separats per comes"
#: contrib/admin/views/doc.py:296 #: contrib/admin/views/doc.py:297
msgid "Date (without time)" msgid "Date (without time)"
msgstr "Data (sense hora)" msgstr "Data (sense hora)"
#: contrib/admin/views/doc.py:297 #: contrib/admin/views/doc.py:298
msgid "Date (with time)" msgid "Date (with time)"
msgstr "Data (amb hora)" msgstr "Data (amb hora)"
#: contrib/admin/views/doc.py:298 #: contrib/admin/views/doc.py:299
msgid "Decimal number" msgid "Decimal number"
msgstr "Número decimal" msgstr "Número decimal"
#: contrib/admin/views/doc.py:299 #: contrib/admin/views/doc.py:300
msgid "E-mail address" msgid "E-mail address"
msgstr "Adreça de correu electrònic" msgstr "Adreça de correu electrònic"
#: contrib/admin/views/doc.py:300 contrib/admin/views/doc.py:301 #: contrib/admin/views/doc.py:301 contrib/admin/views/doc.py:302
#: contrib/admin/views/doc.py:304 #: contrib/admin/views/doc.py:305
msgid "File path" msgid "File path"
msgstr "Ruta del fitxer" msgstr "Ruta del fitxer"
#: contrib/admin/views/doc.py:302 #: contrib/admin/views/doc.py:303
msgid "Floating point number" msgid "Floating point number"
msgstr "Número amb punt de coma flotant" 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" msgid "IP address"
msgstr "Adreça IP" msgstr "Adreça IP"
#: contrib/admin/views/doc.py:308 #: contrib/admin/views/doc.py:309
msgid "Boolean (Either True, False or None)" msgid "Boolean (Either True, False or None)"
msgstr "Booleà (Verdader, Fals o 'None' (cap))" msgstr "Booleà (Verdader, Fals o 'None' (cap))"
#: contrib/admin/views/doc.py:309 #: contrib/admin/views/doc.py:310
msgid "Relation to parent model" msgid "Relation to parent model"
msgstr "Relació amb el model pare" msgstr "Relació amb el model pare"
#: contrib/admin/views/doc.py:310 #: contrib/admin/views/doc.py:311
msgid "Phone number" msgid "Phone number"
msgstr "Número de telèfon" msgstr "Número de telèfon"
#: contrib/admin/views/doc.py:315 #: contrib/admin/views/doc.py:316
msgid "Text" msgid "Text"
msgstr "Texte" msgstr "Texte"
#: contrib/admin/views/doc.py:316 #: contrib/admin/views/doc.py:317
msgid "Time" msgid "Time"
msgstr "Hora" 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" msgid "URL"
msgstr "URL" msgstr "URL"
#: contrib/admin/views/doc.py:318 #: contrib/admin/views/doc.py:319
msgid "U.S. state (two uppercase letters)" msgid "U.S. state (two uppercase letters)"
msgstr "Estat dels E.U.A. (dos lletres majúscules)" 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" msgid "XML text"
msgstr "Texte XML" msgstr "Texte XML"
#: contrib/admin/views/doc.py:345 #: contrib/admin/views/doc.py:346
#, python-format #, python-format
msgid "%s does not appear to be a urlpattern object" msgid "%s does not appear to be a urlpattern object"
msgstr "%s no sembla ser un objecte 'urlpattern'" msgstr "%s no sembla ser un objecte 'urlpattern'"
#: contrib/admin/views/main.py:230 #: contrib/admin/views/main.py:233
msgid "Site administration" msgid "Site administration"
msgstr "Lloc administratiu" 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 #, python-format
msgid "You may add another %s below." msgid "You may add another %s below."
msgstr "Pot afegir un altre %s a baix." msgstr "Pot afegir un altre %s a baix."
#: contrib/admin/views/main.py:296 #: contrib/admin/views/main.py:298
#, python-format #, python-format
msgid "Add %s" msgid "Add %s"
msgstr "Afegir %s" msgstr "Afegir %s"
#: contrib/admin/views/main.py:342 #: contrib/admin/views/main.py:344
#, python-format #, python-format
msgid "Added %s." msgid "Added %s."
msgstr "Agregat %s." msgstr "Agregat %s."
#: contrib/admin/views/main.py:342 contrib/admin/views/main.py:344 #: contrib/admin/views/main.py:344 contrib/admin/views/main.py:346
#: contrib/admin/views/main.py:346 core/validators.py:283 #: contrib/admin/views/main.py:348 core/validators.py:283
#: db/models/manipulators.py:309 #: db/models/manipulators.py:309
msgid "and" msgid "and"
msgstr "i" msgstr "i"
#: contrib/admin/views/main.py:344 #: contrib/admin/views/main.py:346
#, python-format #, python-format
msgid "Changed %s." msgid "Changed %s."
msgstr "Modificat %s." msgstr "Modificat %s."
#: contrib/admin/views/main.py:346 #: contrib/admin/views/main.py:348
#, python-format #, python-format
msgid "Deleted %s." msgid "Deleted %s."
msgstr "Eliminat %s." msgstr "Eliminat %s."
#: contrib/admin/views/main.py:349 #: contrib/admin/views/main.py:351
msgid "No fields changed." msgid "No fields changed."
msgstr "Cap camp canviat." msgstr "Cap camp canviat."
#: contrib/admin/views/main.py:352 #: contrib/admin/views/main.py:354
#, python-format #, python-format
msgid "The %(name)s \"%(obj)s\" was changed successfully." msgid "The %(name)s \"%(obj)s\" was changed successfully."
msgstr "S'ha modificat amb èxist el/la %(name)s \"%(obj)s." 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 #, python-format
msgid "" msgid ""
"The %(name)s \"%(obj)s\" was added successfully. You may edit it again below." "The %(name)s \"%(obj)s\" was added successfully. You may edit it again below."
msgstr "" msgstr ""
"S'ha agregat amb èxit el/la %(name)s \"%(obj)s\". Pot editar-lo de nou abaix." "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 #, python-format
msgid "Change %s" msgid "Change %s"
msgstr "Modificar %s" msgstr "Modificar %s"
#: contrib/admin/views/main.py:483 #: contrib/admin/views/main.py:487
#, python-format #, python-format
msgid "One or more %(fieldname)s in %(name)s: %(obj)s" msgid "One or more %(fieldname)s in %(name)s: %(obj)s"
msgstr "Un o més %(fieldname)s en %(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 #, python-format
msgid "One or more %(fieldname)s in %(name)s:" msgid "One or more %(fieldname)s in %(name)s:"
msgstr "Un o més %(fieldname)s en %(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 #, python-format
msgid "The %(name)s \"%(obj)s\" was deleted successfully." msgid "The %(name)s \"%(obj)s\" was deleted successfully."
msgstr "El/la %(name)s \"%(obj)s\".ha estat eliminat amb èxit." 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?" msgid "Are you sure?"
msgstr "Està segur?" msgstr "Està segur?"
#: contrib/admin/views/main.py:545 #: contrib/admin/views/main.py:549
#, python-format #, python-format
msgid "Change history: %s" msgid "Change history: %s"
msgstr "Modificar històric: %s" msgstr "Modificar històric: %s"
#: contrib/admin/views/main.py:579 #: contrib/admin/views/main.py:583
#, python-format #, python-format
msgid "Select %s" msgid "Select %s"
msgstr "Seleccioni %s" msgstr "Seleccioni %s"
#: contrib/admin/views/main.py:579 #: contrib/admin/views/main.py:583
#, python-format #, python-format
msgid "Select %s to change" msgid "Select %s to change"
msgstr "Seleccioni %s per modificar" msgstr "Seleccioni %s per modificar"
#: contrib/admin/views/main.py:780 #: contrib/admin/views/main.py:784
msgid "Database error" msgid "Database error"
msgstr "Error de/en la base de dades" msgstr "Error de/en la base de dades"
@ -1622,72 +1622,72 @@ msgstr "n"
msgid "rd" msgid "rd"
msgstr "r" msgstr "r"
#: contrib/humanize/templatetags/humanize.py:50 #: contrib/humanize/templatetags/humanize.py:52
#, python-format #, python-format
msgid "%(value).1f million" msgid "%(value).1f million"
msgid_plural "%(value).1f million" msgid_plural "%(value).1f million"
msgstr[0] "%(value).1f milió" msgstr[0] "%(value).1f milió"
msgstr[1] "%(value).1f milions" msgstr[1] "%(value).1f milions"
#: contrib/humanize/templatetags/humanize.py:53 #: contrib/humanize/templatetags/humanize.py:55
#, python-format #, python-format
msgid "%(value).1f billion" msgid "%(value).1f billion"
msgid_plural "%(value).1f billion" msgid_plural "%(value).1f billion"
msgstr[0] "%(value).1f bilió" msgstr[0] "%(value).1f bilió"
msgstr[1] "%(value).1f bilions" msgstr[1] "%(value).1f bilions"
#: contrib/humanize/templatetags/humanize.py:56 #: contrib/humanize/templatetags/humanize.py:58
#, python-format #, python-format
msgid "%(value).1f trillion" msgid "%(value).1f trillion"
msgid_plural "%(value).1f trillion" msgid_plural "%(value).1f trillion"
msgstr[0] "%(value).1f trilió" msgstr[0] "%(value).1f trilió"
msgstr[1] "%(value).1f trilions" msgstr[1] "%(value).1f trilions"
#: contrib/humanize/templatetags/humanize.py:71 #: contrib/humanize/templatetags/humanize.py:74
msgid "one" msgid "one"
msgstr "un" msgstr "un"
#: contrib/humanize/templatetags/humanize.py:71 #: contrib/humanize/templatetags/humanize.py:74
msgid "two" msgid "two"
msgstr "dos" msgstr "dos"
#: contrib/humanize/templatetags/humanize.py:71 #: contrib/humanize/templatetags/humanize.py:74
msgid "three" msgid "three"
msgstr "tres" msgstr "tres"
#: contrib/humanize/templatetags/humanize.py:71 #: contrib/humanize/templatetags/humanize.py:74
msgid "four" msgid "four"
msgstr "cuatre" msgstr "cuatre"
#: contrib/humanize/templatetags/humanize.py:71 #: contrib/humanize/templatetags/humanize.py:74
msgid "five" msgid "five"
msgstr "cinc" msgstr "cinc"
#: contrib/humanize/templatetags/humanize.py:71 #: contrib/humanize/templatetags/humanize.py:74
msgid "six" msgid "six"
msgstr "sis" msgstr "sis"
#: contrib/humanize/templatetags/humanize.py:71 #: contrib/humanize/templatetags/humanize.py:74
msgid "seven" msgid "seven"
msgstr "set" msgstr "set"
#: contrib/humanize/templatetags/humanize.py:71 #: contrib/humanize/templatetags/humanize.py:74
msgid "eight" msgid "eight"
msgstr "vuit" msgstr "vuit"
#: contrib/humanize/templatetags/humanize.py:71 #: contrib/humanize/templatetags/humanize.py:74
msgid "nine" msgid "nine"
msgstr "nou" msgstr "nou"
#: contrib/humanize/templatetags/humanize.py:90 #: contrib/humanize/templatetags/humanize.py:94
msgid "today" msgid "today"
msgstr "avui" msgstr "avui"
#: contrib/humanize/templatetags/humanize.py:92 #: contrib/humanize/templatetags/humanize.py:96
msgid "tomorrow" msgid "tomorrow"
msgstr "demà" msgstr "demà"
#: contrib/humanize/templatetags/humanize.py:94 #: contrib/humanize/templatetags/humanize.py:98
msgid "yesterday" msgid "yesterday"
msgstr "ahir" msgstr "ahir"
@ -3024,6 +3024,50 @@ msgstr ""
"Introdueixi un número vàlid de la Seguretat Social dels E.U.A. en el format " "Introdueixi un número vàlid de la Seguretat Social dels E.U.A. en el format "
"XXX-XX-XXXX." "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 #: contrib/redirects/models.py:7
msgid "redirect from" msgid "redirect from"
msgstr "redreçar des de" msgstr "redreçar des de"
@ -3056,23 +3100,23 @@ msgstr "redreçament"
msgid "redirects" msgid "redirects"
msgstr "redreçaments" msgstr "redreçaments"
#: contrib/sessions/models.py:80 #: contrib/sessions/models.py:46
msgid "session key" msgid "session key"
msgstr "clau de la sessió" msgstr "clau de la sessió"
#: contrib/sessions/models.py:81 #: contrib/sessions/models.py:47
msgid "session data" msgid "session data"
msgstr "dades de la sessió" msgstr "dades de la sessió"
#: contrib/sessions/models.py:82 #: contrib/sessions/models.py:48
msgid "expire date" msgid "expire date"
msgstr "data de caducitat" msgstr "data de caducitat"
#: contrib/sessions/models.py:87 #: contrib/sessions/models.py:53
msgid "session" msgid "session"
msgstr "sessió" msgstr "sessió"
#: contrib/sessions/models.py:88 #: contrib/sessions/models.py:54
msgid "sessions" msgid "sessions"
msgstr "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." msgid "This value can't be comprised solely of digits."
msgstr "Aquest valor no pot contenir només dígits." 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." msgid "Enter a whole number."
msgstr "Introdueixi un número sencer." 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." 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." 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." msgid "Enter a valid e-mail address."
msgstr "Introdueixi una adreça de correu vàlida." msgstr "Introdueixi una adreça de correu vàlida."
#: core/validators.py:182 core/validators.py:474 newforms/fields.py:438 #: core/validators.py:182 core/validators.py:474 newforms/fields.py:432
#: oldforms/__init__.py:686 #: oldforms/__init__.py:687
msgid "No file was submitted. Check the encoding type on the form." msgid "No file was submitted. Check the encoding type on the form."
msgstr "" msgstr ""
"No s'ha enviat cap fitxer. Comprovi el tipus de codificació del formulari." "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 "" msgid ""
"Upload a valid image. The file you uploaded was either not an image or a " "Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image." "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:161 db/models/fields/__init__.py:318
#: db/models/fields/__init__.py:735 db/models/fields/__init__.py:746 #: 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." msgid "This field is required."
msgstr "Aquest camp és obligatori." msgstr "Aquest camp és obligatori."
@ -3484,150 +3528,150 @@ msgstr[1] ""
msgid "Enter a valid value." msgid "Enter a valid value."
msgstr "Introdueixi un valor vàlid." msgstr "Introdueixi un valor vàlid."
#: newforms/fields.py:129 #: newforms/fields.py:123
#, python-format #, python-format
msgid "Ensure this value has at most %(max)d characters (it has %(length)d)." msgid "Ensure this value has at most %(max)d characters (it has %(length)d)."
msgstr "" msgstr ""
"Asseguris de que el valor té com a màxim %(max)d caràcters (en té %(length)" "Asseguris de que el valor té com a màxim %(max)d caràcters (en té %(length)"
"d)." "d)."
#: newforms/fields.py:130 #: newforms/fields.py:124
#, python-format #, python-format
msgid "Ensure this value has at least %(min)d characters (it has %(length)d)." msgid "Ensure this value has at least %(min)d characters (it has %(length)d)."
msgstr "" msgstr ""
"Asseguris de que el valor té com a mínim %(min)d caràcters (en té %(length)" "Asseguris de que el valor té com a mínim %(min)d caràcters (en té %(length)"
"d)." "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 #, python-format
msgid "Ensure this value is less than or equal to %s." msgid "Ensure this value is less than or equal to %s."
msgstr "Aquest valor ha de ser menor o igual a %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 #, python-format
msgid "Ensure this value is greater than or equal to %s." msgid "Ensure this value is greater than or equal to %s."
msgstr "Asseguris de que aquest valor sigui superior o igual a %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." msgid "Enter a number."
msgstr "Introdueixi un número." msgstr "Introdueixi un número."
#: newforms/fields.py:218 #: newforms/fields.py:212
#, python-format #, python-format
msgid "Ensure that there are no more than %s digits in total." 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." 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 #, python-format
msgid "Ensure that there are no more than %s decimal places." msgid "Ensure that there are no more than %s decimal places."
msgstr "Asseguris de que no hi ha més de %s decimals." msgstr "Asseguris de que no hi ha més de %s decimals."
#: newforms/fields.py:220 #: newforms/fields.py:214
#, python-format #, python-format
msgid "Ensure that there are no more than %s digits before the decimal point." 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." 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." msgid "Enter a valid date."
msgstr "Introdueixi una data vàlida." 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." msgid "Enter a valid time."
msgstr "Introdueixi una hora vàlida." msgstr "Introdueixi una hora vàlida."
#: newforms/fields.py:340 #: newforms/fields.py:334
msgid "Enter a valid date/time." msgid "Enter a valid date/time."
msgstr "Introdueixi una data/hora vàlides." msgstr "Introdueixi una data/hora vàlides."
#: newforms/fields.py:439 #: newforms/fields.py:433
msgid "No file was submitted." msgid "No file was submitted."
msgstr "No s'ha enviat cap fitxer." 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." msgid "The submitted file is empty."
msgstr "El fitxer enviat està buit." msgstr "El fitxer enviat està buit."
#: newforms/fields.py:498 #: newforms/fields.py:492
msgid "Enter a valid URL." msgid "Enter a valid URL."
msgstr "Introdueixi una URL vàlida." msgstr "Introdueixi una URL vàlida."
#: newforms/fields.py:499 #: newforms/fields.py:493
msgid "This URL appears to be a broken link." msgid "This URL appears to be a broken link."
msgstr "Aquesta URL sembla ser un enllaç trencat." 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." msgid "Select a valid choice. That choice is not one of the available choices."
msgstr "" msgstr ""
"Esculli una opció vàlida; Aquesta opció no és una de les opcions disponibles." "Esculli una opció vàlida; Aquesta opció no és una de les opcions disponibles."
#: newforms/fields.py:599 #: newforms/fields.py:594
#, python-format #, python-format
msgid "Select a valid choice. %(value)s is not one of the available choices." 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." 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." msgid "Enter a list of values."
msgstr "Introdueixi una llista de valors." msgstr "Introdueixi una llista de valors."
#: newforms/fields.py:753 #: newforms/fields.py:748
msgid "Enter a valid IPv4 address." msgid "Enter a valid IPv4 address."
msgstr "Introdueixi una adreça IPv4 vàlida." msgstr "Introdueixi una adreça IPv4 vàlida."
#: newforms/models.py:221 #: newforms/models.py:216
#, python-format #, python-format
msgid "Select a valid choice. %s is not one of the available choices." 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." 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 #, python-format
msgid "Ensure your text is less than %s character." msgid "Ensure your text is less than %s character."
msgid_plural "Ensure your text is less than %s characters." msgid_plural "Ensure your text is less than %s characters."
msgstr[0] "Asseguris de que el seu texte té menys de %s caracter." 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." 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." msgid "Line breaks are not allowed here."
msgstr "No es permeten salts de línia." 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 #, python-format
msgid "Select a valid choice; '%(data)s' is not in %(choices)s." 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." 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." msgid "Enter a whole number between -32,768 and 32,767."
msgstr "Introdueixi un número enter entre -32,768 i 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." msgid "Enter a positive number."
msgstr "Introdueixi un número positiu." msgstr "Introdueixi un número positiu."
#: oldforms/__init__.py:764 #: oldforms/__init__.py:765
msgid "Enter a whole number between 0 and 32,767." msgid "Enter a whole number between 0 and 32,767."
msgstr "Introdueixi un número entre 0 i 32,767." msgstr "Introdueixi un número entre 0 i 32,767."
#: template/defaultfilters.py:555 #: template/defaultfilters.py:658
msgid "yes,no,maybe" msgid "yes,no,maybe"
msgstr "si,no,potser" msgstr "si,no,potser"
#: template/defaultfilters.py:585 #: template/defaultfilters.py:689
#, python-format #, python-format
msgid "%(size)d byte" msgid "%(size)d byte"
msgid_plural "%(size)d bytes" msgid_plural "%(size)d bytes"
msgstr[0] "%(size)d byte" msgstr[0] "%(size)d byte"
msgstr[1] "%(size)d bytes" msgstr[1] "%(size)d bytes"
#: template/defaultfilters.py:587 #: template/defaultfilters.py:691
#, python-format #, python-format
msgid "%.1f KB" msgid "%.1f KB"
msgstr "%.1f KB" msgstr "%.1f KB"
#: template/defaultfilters.py:589 #: template/defaultfilters.py:693
#, python-format #, python-format
msgid "%.1f MB" msgid "%.1f MB"
msgstr "%.1f MB" msgstr "%.1f MB"
#: template/defaultfilters.py:590 #: template/defaultfilters.py:694
#, python-format #, python-format
msgid "%.1f GB" msgid "%.1f GB"
msgstr "%.1f GB" msgstr "%.1f GB"
@ -3890,23 +3934,23 @@ msgstr "%(number)d %(type)s"
msgid ", %(number)d %(type)s" msgid ", %(number)d %(type)s"
msgstr ", %(number)d %(type)s" msgstr ", %(number)d %(type)s"
#: utils/translation/trans_real.py:395 #: utils/translation/trans_real.py:399
msgid "DATE_FORMAT" msgid "DATE_FORMAT"
msgstr "F j, Y" msgstr "F j, Y"
#: utils/translation/trans_real.py:396 #: utils/translation/trans_real.py:400
msgid "DATETIME_FORMAT" msgid "DATETIME_FORMAT"
msgstr "F j, Y, H:i" msgstr "F j, Y, H:i"
#: utils/translation/trans_real.py:397 #: utils/translation/trans_real.py:401
msgid "TIME_FORMAT" msgid "TIME_FORMAT"
msgstr "H:i" msgstr "H:i"
#: utils/translation/trans_real.py:413 #: utils/translation/trans_real.py:417
msgid "YEAR_MONTH_FORMAT" msgid "YEAR_MONTH_FORMAT"
msgstr "j de/d' F del Y" msgstr "j de/d' F del Y"
#: utils/translation/trans_real.py:414 #: utils/translation/trans_real.py:418
msgid "MONTH_DAY_FORMAT" msgid "MONTH_DAY_FORMAT"
msgstr "j de/d' F del Y" msgstr "j de/d' F del Y"
@ -3924,43 +3968,3 @@ msgstr "El/La %(verbose_name)s s'ha actualtzat amb èxit."
#, python-format #, python-format
msgid "The %(verbose_name)s was deleted." msgid "The %(verbose_name)s was deleted."
msgstr "El %(verbose_name)s s'ha eliminat." 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"

View File

@ -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 #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Django 1.0\n" "Project-Id-Version: Django 1.0\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2006-12-09 15:51+0100\n" "POT-Creation-Date: 2006-12-09 15:51+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: 2007-12-17 21:14+0100\n"
"Last-Translator: Johan C. Stöver <johan@nilling.nl>\n" "Last-Translator: I.S. van Oostveen\n"
"Language-Team: \n" "Language-Team: \n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\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 #: db/models/fields/__init__.py:381
msgid "This value must be either True or False." 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 #: db/models/fields/__init__.py:397
msgid "This field cannot be null." msgid "This field cannot be null."
@ -279,7 +274,7 @@ msgstr "Alleen alfabetische karakters zijn toegestaan"
#: core/validators.py:139 #: core/validators.py:139
msgid "Year must be 1900 or later." 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 #: core/validators.py:143
#, python-format #, 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 #: core/validators.py:173 core/validators.py:442 forms/__init__.py:667
msgid "No file was submitted. Check the encoding type on the form." 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 #: core/validators.py:177
msgid "" msgid ""
"Upload a valid image. The file you uploaded was either not an image or a " "Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image." "corrupted image."
msgstr "" 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." "beschadigd."
#: core/validators.py:184 #: core/validators.py:184
#, python-format #, python-format
msgid "The URL %s does not point to a valid image." 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 #: core/validators.py:188
#, python-format #, python-format
@ -321,7 +316,7 @@ msgstr ""
#: core/validators.py:196 #: core/validators.py:196
#, python-format #, python-format
msgid "The URL %s does not point to a valid QuickTime video." 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 #: core/validators.py:200
msgid "A valid URL is required." msgid "A valid URL is required."
@ -349,18 +344,18 @@ msgstr "Ongeldige URL: %s"
#: core/validators.py:243 core/validators.py:245 #: core/validators.py:243 core/validators.py:245
#, python-format #, python-format
msgid "The URL %s is a broken link." 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 #: core/validators.py:251
msgid "Enter a valid U.S. state abbreviation." 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 #: core/validators.py:265
#, python-format #, python-format
msgid "Watch your mouth! The word %s is not allowed here." msgid "Watch your mouth! The word %s is not allowed here."
msgid_plural "Watch your mouth! The words %s are 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[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 #: core/validators.py:272
#, python-format #, python-format
@ -373,7 +368,7 @@ msgstr "Voer tenminste één veld in."
#: core/validators.py:300 core/validators.py:311 #: core/validators.py:300 core/validators.py:311
msgid "Please enter both fields or leave them both empty." 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 #: core/validators.py:318
#, python-format #, python-format
@ -450,7 +445,7 @@ msgstr "Zorg ervoor dat het bestand hoogstens %s bytes groot is."
#: core/validators.py:453 #: core/validators.py:453
msgid "The format for this field is wrong." 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 #: core/validators.py:468
msgid "This field is invalid." msgid "This field is invalid."
@ -536,7 +531,7 @@ msgid ""
"Your Web browser doesn't appear to have cookies enabled. Cookies are " "Your Web browser doesn't appear to have cookies enabled. Cookies are "
"required for logging in." "required for logging in."
msgstr "" 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." "moeten cookies worden geaccepteerd."
#: contrib/auth/forms.py:59 contrib/admin/views/decorators.py:10 #: contrib/auth/forms.py:59 contrib/admin/views/decorators.py:10
@ -645,7 +640,7 @@ msgstr "supergebruiker status"
msgid "" msgid ""
"Designates that this user has all permissions without explicitly assigning " "Designates that this user has all permissions without explicitly assigning "
"them." "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 #: contrib/auth/models.py:98
msgid "last login" msgid "last login"
@ -685,7 +680,7 @@ msgstr "Rechten"
#: contrib/auth/models.py:113 #: contrib/auth/models.py:113
msgid "Important dates" msgid "Important dates"
msgstr "Belangrijke data" msgstr "Belangrijke datums"
#: contrib/auth/models.py:114 #: contrib/auth/models.py:114
msgid "Groups" msgid "Groups"
@ -783,7 +778,7 @@ msgid ""
"Please log in again, because your session has expired. Don't worry: Your " "Please log in again, because your session has expired. Don't worry: Your "
"submission has been saved." "submission has been saved."
msgstr "" 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." "is opgeslagen."
#: contrib/admin/views/decorators.py:69 #: contrib/admin/views/decorators.py:69
@ -791,7 +786,7 @@ msgid ""
"Looks like your browser isn't configured to accept cookies. Please enable " "Looks like your browser isn't configured to accept cookies. Please enable "
"cookies, reload this page, and try again." "cookies, reload this page, and try again."
msgstr "" 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." "cookies aan in uw browser, laad deze pagina nogmaals en probeer het opnieuw."
#: contrib/admin/views/decorators.py:83 #: contrib/admin/views/decorators.py:83
@ -928,7 +923,7 @@ msgstr "Model %r niet gevonden in app %r"
#: contrib/admin/views/doc.py:183 #: contrib/admin/views/doc.py:183
#, python-format #, python-format
msgid "the related `%s.%s` object" 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:183 contrib/admin/views/doc.py:205
#: contrib/admin/views/doc.py:219 contrib/admin/views/doc.py:224 #: contrib/admin/views/doc.py:219 contrib/admin/views/doc.py:224
@ -938,7 +933,7 @@ msgstr "model:"
#: contrib/admin/views/doc.py:214 #: contrib/admin/views/doc.py:214
#, python-format #, python-format
msgid "related `%s.%s` objects" msgid "related `%s.%s` objects"
msgstr "related `%s.%s` objects" msgstr "de gerelateerde `%s.%s` objecten"
#: contrib/admin/views/doc.py:219 #: contrib/admin/views/doc.py:219
#, python-format #, python-format
@ -948,12 +943,12 @@ msgstr "alle %s"
#: contrib/admin/views/doc.py:224 #: contrib/admin/views/doc.py:224
#, python-format #, python-format
msgid "number of %s" msgid "number of %s"
msgstr "nummer van %s" msgstr "aantal %s"
#: contrib/admin/views/doc.py:229 #: contrib/admin/views/doc.py:229
#, python-format #, python-format
msgid "Fields on %s objects" 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:291 contrib/admin/views/doc.py:301
#: contrib/admin/views/doc.py:303 contrib/admin/views/doc.py:309 #: 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:" "All of the following related items will be deleted:"
msgstr "" msgstr ""
"Weet u zeker dat u %(object_name)s \"%(escaped_object)s\" wilt verwijderen? Alle " "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 #: contrib/admin/templates/admin/delete_confirmation.html:26
msgid "Yes, I'm sure" 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:4
#: contrib/admin/templates/admin/404.html:8 #: contrib/admin/templates/admin/404.html:8
@ -1197,7 +1192,7 @@ msgstr "Opslaan en nieuw item"
#: contrib/admin/templates/admin/submit_line.html:6 #: contrib/admin/templates/admin/submit_line.html:6
msgid "Save and continue editing" msgid "Save and continue editing"
msgstr "Opslaan en bewerk opnieuw" msgstr "Opslaan en opnieuw bewerken"
#: contrib/admin/templates/admin/submit_line.html:7 #: contrib/admin/templates/admin/submit_line.html:7
msgid "Save" msgid "Save"
@ -1224,7 +1219,7 @@ msgstr "Wijzigen"
#: contrib/admin/templates/admin/index.html:44 #: contrib/admin/templates/admin/index.html:44
msgid "You don't have permission to edit anything." 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 #: contrib/admin/templates/admin/index.html:52
msgid "Recent Actions" msgid "Recent Actions"
@ -1380,7 +1375,7 @@ msgstr ""
"de bookmarklet vanuit elke pagina op de site worden gekozen. Let erop dat " "de bookmarklet vanuit elke pagina op de site worden gekozen. Let erop dat "
"het soms\n" "het soms\n"
"noodzakelijk is dat de computer van waaruit de pagina wordt bekeken intern " "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 " "(Raadpleeg uw systeembeheerder of uw computer zich op het interne netwerk "
"bevind).<p>\n" "bevind).<p>\n"
@ -1393,7 +1388,7 @@ msgid ""
"Jumps you from any page to the documentation for the view that generates " "Jumps you from any page to the documentation for the view that generates "
"that page." "that page."
msgstr "" 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" "wordt door die pagina"
#: contrib/admin/templates/admin_doc/bookmarklets.html:22 #: contrib/admin/templates/admin_doc/bookmarklets.html:22
@ -1406,7 +1401,7 @@ msgid ""
"object." "object."
msgstr "" msgstr ""
"Toont de content-type en unieke ID voor pagina's die een enkel object " "Toont de content-type en unieke ID voor pagina's die een enkel object "
"voorsteld." "voorstellen."
#: contrib/admin/templates/admin_doc/bookmarklets.html:25 #: contrib/admin/templates/admin_doc/bookmarklets.html:25
msgid "Edit this object (current window)" msgid "Edit this object (current window)"
@ -1414,7 +1409,7 @@ msgstr "Bewerk dit object (huidig venster)"
#: contrib/admin/templates/admin_doc/bookmarklets.html:26 #: contrib/admin/templates/admin_doc/bookmarklets.html:26
msgid "Jumps to the admin page for pages that represent a single object." 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 #: contrib/admin/templates/admin_doc/bookmarklets.html:28
msgid "Edit this object (new window)" msgid "Edit this object (new window)"
@ -1597,7 +1592,7 @@ msgid ""
"Example: 'flatpages/contact_page.html'. If this isn't provided, the system " "Example: 'flatpages/contact_page.html'. If this isn't provided, the system "
"will use 'flatpages/default.html'." "will use 'flatpages/default.html'."
msgstr "" 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." "'flatpages/default.html' gebruikt."
#: contrib/flatpages/models.py:14 #: 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:196
#: contrib/comments/views/comments.py:286 #: contrib/comments/views/comments.py:286
msgid "Somebody tampered with the comment form (security violation)" 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:206
#: contrib/comments/views/comments.py:292 #: contrib/comments/views/comments.py:292

View File

@ -55,6 +55,12 @@ var POLISH_MAP = {
'Ź':'Z', 'Ż':'Z' 'Ź':'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() var ALL_DOWNCODE_MAPS=new Array()
ALL_DOWNCODE_MAPS[0]=LATIN_MAP ALL_DOWNCODE_MAPS[0]=LATIN_MAP
ALL_DOWNCODE_MAPS[1]=LATIN_SYMBOLS_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[5]=UKRAINIAN_MAP
ALL_DOWNCODE_MAPS[6]=CZECH_MAP ALL_DOWNCODE_MAPS[6]=CZECH_MAP
ALL_DOWNCODE_MAPS[7]=POLISH_MAP ALL_DOWNCODE_MAPS[7]=POLISH_MAP
ALL_DOWNCODE_MAPS[8]=LATVIAN_MAP
var Downcoder = new Object(); var Downcoder = new Object();
Downcoder.Initialize = function() Downcoder.Initialize = function()

View File

@ -114,7 +114,7 @@ def result_headers(cl):
yield {"text": header, yield {"text": header,
"sortable": True, "sortable": True,
"url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}), "url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
"class_attrib": mark_safe((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): def _boolean_icon(field_val):
BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'} BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'}

View File

@ -307,7 +307,7 @@ class AnonymousUser(object):
id = None id = None
username = '' username = ''
is_staff = False is_staff = False
is_active = True is_active = False
is_superuser = False is_superuser = False
_groups = EmptyManager() _groups = EmptyManager()
_user_permissions = EmptyManager() _user_permissions = EmptyManager()

View File

@ -16,9 +16,21 @@ False
>>> u2 = User.objects.create_user('testuser2', 'test2@example.com') >>> u2 = User.objects.create_user('testuser2', 'test2@example.com')
>>> u2.has_usable_password() >>> u2.has_usable_password()
False False
>>> u.is_authenticated()
True
>>> u.is_staff
False
>>> u.is_active
True
>>> a = AnonymousUser() >>> a = AnonymousUser()
>>> a.is_authenticated()
False
>>> a.is_staff >>> a.is_staff
False False
>>> a.is_active
False
>>> a.groups.all() >>> a.groups.all()
[] []
>>> a.user_permissions.all() >>> a.user_permissions.all()

View File

@ -118,7 +118,7 @@ class GenericRelation(RelatedField, Field):
return self.object_id_field_name return self.object_id_field_name
def m2m_reverse_name(self): def m2m_reverse_name(self):
return self.object_id_field_name return self.model._meta.pk.column
def contribute_to_class(self, cls, name): def contribute_to_class(self, cls, name):
super(GenericRelation, self).contribute_to_class(cls, name) super(GenericRelation, self).contribute_to_class(cls, name)

View File

@ -12,6 +12,7 @@ from django.utils.safestring import mark_safe
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
EMPTY_VALUE = '(None)' EMPTY_VALUE = '(None)'
DISPLAY_SIZE = 100
class EasyModel(object): class EasyModel(object):
def __init__(self, site, model): def __init__(self, site, model):
@ -93,8 +94,8 @@ class EasyInstance(object):
def __unicode__(self): def __unicode__(self):
val = smart_unicode(self.instance) val = smart_unicode(self.instance)
if len(val) > 30: if len(val) > DISPLAY_SIZE:
return val[:30] + u'...' return val[:DISPLAY_SIZE] + u'...'
return val return val
def __str__(self): def __str__(self):

View File

@ -24,18 +24,20 @@ class ARPostalCodeField(RegexField):
See http://www.correoargentino.com.ar/consulta_cpa/home.php 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): def __init__(self, *args, **kwargs):
super(ARPostalCodeField, self).__init__(r'^\d{4}$|^[A-HJ-NP-Za-hj-np-z]\d{4}\D{3}$', super(ARPostalCodeField, self).__init__(r'^\d{4}$|^[A-HJ-NP-Za-hj-np-z]\d{4}\D{3}$',
min_length=4, max_length=8, min_length=4, max_length=8, *args, **kwargs)
error_message=ugettext("Enter a postal code in the format NNNN or ANNNNAAA."),
*args, **kwargs)
def clean(self, value): def clean(self, value):
value = super(ARPostalCodeField, self).clean(value) value = super(ARPostalCodeField, self).clean(value)
if value in EMPTY_VALUES: if value in EMPTY_VALUES:
return u'' return u''
if len(value) not in (4, 8): 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: if len(value) == 8:
return u'%s%s%s' % (value[0].upper(), value[1:5], value[5:].upper()) return u'%s%s%s' % (value[0].upper(), value[1:5], value[5:].upper())
return value return value
@ -44,6 +46,11 @@ class ARDNIField(CharField):
""" """
A field that validates `Documento Nacional de Identidad´ (DNI) numbers. 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): def __init__(self, *args, **kwargs):
super(ARDNIField, self).__init__(max_length=10, min_length=7, *args, super(ARDNIField, self).__init__(max_length=10, min_length=7, *args,
**kwargs) **kwargs)
@ -58,10 +65,9 @@ class ARDNIField(CharField):
if not value.isdigit(): if not value.isdigit():
value = value.replace('.', '') value = value.replace('.', '')
if not value.isdigit(): 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): if len(value) not in (7, 8):
raise ValidationError( raise ValidationError(self.error_messages['max_digits'])
ugettext("This field requires 7 or 8 digits."))
return value return value
@ -70,9 +76,13 @@ class ARCUITField(RegexField):
This field validates a CUIT (Código Único de Identificación Tributaria). A This field validates a CUIT (Código Único de Identificación Tributaria). A
CUIT is of the form XX-XXXXXXXX-V. The last digit is a check digit. 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): def __init__(self, *args, **kwargs):
super(ARCUITField, self).__init__(r'^\d{2}-?\d{8}-?\d$', 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) *args, **kwargs)
def clean(self, value): def clean(self, value):
@ -85,7 +95,7 @@ class ARCUITField(RegexField):
return u'' return u''
value, cd = self._canon(value) value, cd = self._canon(value)
if self._calc_cd(value) != cd: if self._calc_cd(value) != cd:
raise ValidationError(ugettext("Invalid CUIT.")) raise ValidationError(self.error_messages['checksum'])
return self._format(value, cd) return self._format(value, cd)
def _canon(self, cuit): def _canon(self, cuit):

View File

@ -12,14 +12,20 @@ PHONE_DIGITS_RE = re.compile(r'^(\d{10})$')
class AUPostCodeField(RegexField): class AUPostCodeField(RegexField):
"""Australian post code field.""" """Australian post code field."""
default_error_messages = {
'invalid': ugettext('Enter a 4 digit post code.'),
}
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(AUPostCodeField, self).__init__(r'^\d{4}$', super(AUPostCodeField, self).__init__(r'^\d{4}$',
max_length=None, min_length=None, max_length=None, min_length=None, *args, **kwargs)
error_message=ugettext('Enter a 4 digit post code.'),
*args, **kwargs)
class AUPhoneNumberField(Field): class AUPhoneNumberField(Field):
"""Australian phone number field.""" """Australian phone number field."""
default_error_messages = {
'invalid': u'Phone numbers must contain 10 digits.',
}
def clean(self, value): def clean(self, value):
""" """
Validate a phone number. Strips parentheses, whitespace and hyphens. Validate a phone number. Strips parentheses, whitespace and hyphens.
@ -31,7 +37,7 @@ class AUPhoneNumberField(Field):
phone_match = PHONE_DIGITS_RE.search(value) phone_match = PHONE_DIGITS_RE.search(value)
if phone_match: if phone_match:
return u'%s' % phone_match.group(1) 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): class AUStateSelect(Select):
""" """

View File

@ -17,13 +17,19 @@ except NameError:
phone_digits_re = re.compile(r'^(\d{2})[-\.]?(\d{4})[-\.]?(\d{4})$') phone_digits_re = re.compile(r'^(\d{2})[-\.]?(\d{4})[-\.]?(\d{4})$')
class BRZipCodeField(RegexField): class BRZipCodeField(RegexField):
default_error_messages = {
'invalid': _('Enter a zip code in the format XXXXX-XXX.'),
}
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(BRZipCodeField, self).__init__(r'^\d{5}-\d{3}$', super(BRZipCodeField, self).__init__(r'^\d{5}-\d{3}$',
max_length=None, min_length=None, max_length=None, min_length=None, *args, **kwargs)
error_message=_('Enter a zip code in the format XXXXX-XXX.'),
*args, **kwargs)
class BRPhoneNumberField(Field): class BRPhoneNumberField(Field):
default_error_messages = {
'invalid': _('Phone numbers must be in XX-XXXX-XXXX format.'),
}
def clean(self, value): def clean(self, value):
super(BRPhoneNumberField, self).clean(value) super(BRPhoneNumberField, self).clean(value)
if value in EMPTY_VALUES: if value in EMPTY_VALUES:
@ -32,7 +38,7 @@ class BRPhoneNumberField(Field):
m = phone_digits_re.search(value) m = phone_digits_re.search(value)
if m: if m:
return u'%s-%s-%s' % (m.group(1), m.group(2), m.group(3)) 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): class BRStateSelect(Select):
""" """
@ -48,6 +54,9 @@ class BRStateChoiceField(Field):
A choice field that uses a list of Brazilian states as its choices. A choice field that uses a list of Brazilian states as its choices.
""" """
widget = Select 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, def __init__(self, required=True, widget=None, label=None,
initial=None, help_text=None): initial=None, help_text=None):
@ -65,9 +74,7 @@ class BRStateChoiceField(Field):
return value return value
valid_values = set([smart_unicode(k) for k, v in self.widget.choices]) valid_values = set([smart_unicode(k) for k, v in self.widget.choices])
if value not in valid_values: if value not in valid_values:
raise ValidationError(_(u'Select a valid brazilian state.' raise ValidationError(self.error_messages['invalid'])
u' That state is not one'
u' of the available states.'))
return value return value
def DV_maker(v): def DV_maker(v):
@ -83,6 +90,12 @@ class BRCPFField(CharField):
More information: More information:
http://en.wikipedia.org/wiki/Cadastro_de_Pessoas_F%C3%ADsicas 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): def __init__(self, *args, **kwargs):
super(BRCPFField, self).__init__(max_length=14, min_length=11, *args, **kwargs) super(BRCPFField, self).__init__(max_length=14, min_length=11, *args, **kwargs)
@ -100,9 +113,9 @@ class BRCPFField(CharField):
try: try:
int(value) int(value)
except ValueError: except ValueError:
raise ValidationError(_("This field requires only numbers.")) raise ValidationError(self.error_messages['digits_only'])
if len(value) != 11: 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:] orig_dv = value[-2:]
new_1dv = sum([i * int(value[idx]) for idx, i in enumerate(range(10, 1, -1))]) 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) new_2dv = DV_maker(new_2dv % 11)
value = value[:-1] + str(new_2dv) value = value[:-1] + str(new_2dv)
if value[-2:] != orig_dv: if value[-2:] != orig_dv:
raise ValidationError(_("Invalid CPF number.")) raise ValidationError(self.error_messages['invalid'])
return orig_value return orig_value
class BRCNPJField(Field): 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): def clean(self, value):
""" """
Value can be either a string in the format XX.XXX.XXX/XXXX-XX or a Value can be either a string in the format XX.XXX.XXX/XXXX-XX or a
@ -131,10 +150,9 @@ class BRCNPJField(Field):
try: try:
int(value) int(value)
except ValueError: except ValueError:
raise ValidationError("This field requires only numbers.") raise ValidationError(self.error_messages['digits_only'])
if len(value) != 14: if len(value) != 14:
raise ValidationError( raise ValidationError(self.error_messages['max_digits'])
_("This field requires at least 14 digits"))
orig_dv = value[-2:] orig_dv = value[-2:]
new_1dv = sum([i * int(value[idx]) for idx, i in enumerate(range(5, 1, -1) + range(9, 1, -1))]) 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) new_2dv = DV_maker(new_2dv % 11)
value = value[:-1] + str(new_2dv) value = value[:-1] + str(new_2dv)
if value[-2:] != orig_dv: if value[-2:] != orig_dv:
raise ValidationError(_("Invalid CNPJ number.")) raise ValidationError(self.error_messages['invalid'])
return orig_value return orig_value

View File

@ -13,14 +13,20 @@ sin_re = re.compile(r"^(\d{3})-(\d{3})-(\d{3})$")
class CAPostalCodeField(RegexField): class CAPostalCodeField(RegexField):
"""Canadian postal code field.""" """Canadian postal code field."""
default_error_messages = {
'invalid': gettext(u'Enter a postal code in the format XXX XXX.'),
}
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(CAPostalCodeField, self).__init__(r'^[ABCEGHJKLMNPRSTVXYZ]\d[A-Z] \d[A-Z]\d$', super(CAPostalCodeField, self).__init__(r'^[ABCEGHJKLMNPRSTVXYZ]\d[A-Z] \d[A-Z]\d$',
max_length=None, min_length=None, max_length=None, min_length=None, *args, **kwargs)
error_message=gettext(u'Enter a postal code in the format XXX XXX.'),
*args, **kwargs)
class CAPhoneNumberField(Field): class CAPhoneNumberField(Field):
"""Canadian phone number field.""" """Canadian phone number field."""
default_error_messages = {
'invalid': u'Phone numbers must be in XXX-XXX-XXXX format.',
}
def clean(self, value): def clean(self, value):
"""Validate a phone number. """Validate a phone number.
""" """
@ -31,7 +37,7 @@ class CAPhoneNumberField(Field):
m = phone_digits_re.search(value) m = phone_digits_re.search(value)
if m: if m:
return u'%s-%s-%s' % (m.group(1), m.group(2), m.group(3)) 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): class CAProvinceField(Field):
""" """
@ -39,6 +45,10 @@ class CAProvinceField(Field):
It normalizes the input to the standard two-leter postal service It normalizes the input to the standard two-leter postal service
abbreviation for the given province. abbreviation for the given province.
""" """
default_error_messages = {
'invalid': u'Enter a Canadian province or territory.',
}
def clean(self, value): def clean(self, value):
from ca_provinces import PROVINCES_NORMALIZED from ca_provinces import PROVINCES_NORMALIZED
super(CAProvinceField, self).clean(value) super(CAProvinceField, self).clean(value)
@ -53,7 +63,7 @@ class CAProvinceField(Field):
return PROVINCES_NORMALIZED[value.strip().lower()].decode('ascii') return PROVINCES_NORMALIZED[value.strip().lower()].decode('ascii')
except KeyError: except KeyError:
pass pass
raise ValidationError(u'Enter a Canadian province or territory.') raise ValidationError(self.error_messages['invalid'])
class CAProvinceSelect(Select): class CAProvinceSelect(Select):
""" """
@ -74,19 +84,23 @@ class CASocialInsuranceNumberField(Field):
* Passes the check digit process "Luhn Algorithm" * Passes the check digit process "Luhn Algorithm"
See: http://en.wikipedia.org/wiki/Social_Insurance_Number 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): def clean(self, value):
super(CASocialInsuranceNumberField, self).clean(value) super(CASocialInsuranceNumberField, self).clean(value)
if value in EMPTY_VALUES: if value in EMPTY_VALUES:
return u'' return u''
msg = ugettext('Enter a valid Canadian Social Insurance number in XXX-XXX-XXXX format.')
match = re.match(sin_re, value) match = re.match(sin_re, value)
if not match: if not match:
raise ValidationError(msg) raise ValidationError(self.error_messages['invalid'])
number = u'%s-%s-%s' % (match.group(1), match.group(2), match.group(3)) 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)) check_number = u'%s%s%s' % (match.group(1), match.group(2), match.group(3))
if not self.luhn_checksum_is_valid(check_number): if not self.luhn_checksum_is_valid(check_number):
raise ValidationError(msg) raise ValidationError(self.error_messages['invalid'])
return number return number
def luhn_checksum_is_valid(self, number): def luhn_checksum_is_valid(self, number):

View File

@ -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}$') phone_digits_re = re.compile(r'^0([1-9]{1})\d{8}$')
class CHZipCodeField(RegexField): class CHZipCodeField(RegexField):
default_error_messages = {
'invalid': ugettext('Enter a zip code in the format XXXX.'),
}
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(CHZipCodeField, self).__init__(r'^\d{4}$', super(CHZipCodeField, self).__init__(r'^\d{4}$',
max_length=None, min_length=None, max_length=None, min_length=None, *args, **kwargs)
error_message=ugettext('Enter a zip code in the format XXXX.'),
*args, **kwargs)
class CHPhoneNumberField(Field): 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' and '0XXXXXXXXX' validate but are corrected to
'0XX XXX XX XX'. '0XX XXX XX XX'.
""" """
default_error_messages = {
'invalid': 'Phone numbers must be in 0XX XXX XX XX format.',
}
def clean(self, value): def clean(self, value):
super(CHPhoneNumberField, self).clean(value) super(CHPhoneNumberField, self).clean(value)
if value in EMPTY_VALUES: if value in EMPTY_VALUES:
@ -33,7 +39,7 @@ class CHPhoneNumberField(Field):
m = phone_digits_re.search(value) m = phone_digits_re.search(value)
if m: if m:
return u'%s %s %s %s' % (value[0:3], value[3:6], value[6:8], value[8:10]) 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): class CHStateSelect(Select):
""" """
@ -54,6 +60,10 @@ class CHIdentityCardNumberField(Field):
Algorithm is documented at http://adi.kousz.ch/artikel/IDCHE.htm 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): def has_valid_checksum(self, number):
given_number, given_checksum = number[:-1], number[-1] given_number, given_checksum = number[:-1], number[-1]
new_number = given_number new_number = given_number
@ -87,23 +97,22 @@ class CHIdentityCardNumberField(Field):
def clean(self, value): def clean(self, value):
super(CHIdentityCardNumberField, self).clean(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: if value in EMPTY_VALUES:
return u'' return u''
match = re.match(id_re, value) match = re.match(id_re, value)
if not match: 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'] idnumber, pos9, checksum = match.groupdict()['idnumber'], match.groupdict()['pos9'], match.groupdict()['checksum']
if idnumber == '00000000' or \ if idnumber == '00000000' or \
idnumber == 'A0000000': idnumber == 'A0000000':
raise ValidationError(error_msg) raise ValidationError(self.error_messages['invalid'])
all_digits = "%s%s%s" % (idnumber, pos9, checksum) all_digits = "%s%s%s" % (idnumber, pos9, checksum)
if not self.has_valid_checksum(all_digits): 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) return u'%s%s%s' % (idnumber, pos9, checksum)

View File

@ -25,16 +25,21 @@ class CLRutField(RegexField):
Samples for testing are available from Samples for testing are available from
https://palena.sii.cl/cvc/dte/ee_empresas_emisoras.html 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): def __init__(self, *args, **kwargs):
if 'strict' in kwargs: if 'strict' in kwargs:
del kwargs['strict'] del kwargs['strict']
super(CLRutField, self).__init__(r'^(\d{1,2}\.)?\d{3}\.\d{3}-[\dkK]$', 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.'), error_message=self.default_error_messages['strict'], *args, **kwargs)
*args, **kwargs)
else: else:
# In non-strict mode, accept RUTs that validate but do not exist in # In non-strict mode, accept RUTs that validate but do not exist in
# the real world. # 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): def clean(self, value):
""" """
@ -47,7 +52,7 @@ class CLRutField(RegexField):
if self._algorithm(rut) == verificador: if self._algorithm(rut) == verificador:
return self._format(rut, verificador) return self._format(rut, verificador)
else: else:
raise ValidationError(u'The Chilean RUT is not valid.') raise ValidationError(self.error_messages['checksum'])
def _algorithm(self, rut): def _algorithm(self, rut):
""" """

View File

@ -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})$") 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): class DEZipCodeField(RegexField):
default_error_messages = {
'invalid': ugettext('Enter a zip code in the format XXXXX.'),
}
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(DEZipCodeField, self).__init__(r'^\d{5}$', super(DEZipCodeField, self).__init__(r'^\d{5}$',
max_length=None, min_length=None, max_length=None, min_length=None, *args, **kwargs)
error_message=ugettext('Enter a zip code in the format XXXXX.'),
*args, **kwargs)
class DEStateSelect(Select): class DEStateSelect(Select):
""" """
@ -36,6 +37,10 @@ class DEIdentityCardNumberField(Field):
Algorithm is documented at http://de.wikipedia.org/wiki/Personalausweis 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): def has_valid_checksum(self, number):
given_number, given_checksum = number[:-1], number[-1] given_number, given_checksum = number[:-1], number[-1]
calculated_checksum = 0 calculated_checksum = 0
@ -57,23 +62,22 @@ class DEIdentityCardNumberField(Field):
def clean(self, value): def clean(self, value):
super(DEIdentityCardNumberField, self).clean(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: if value in EMPTY_VALUES:
return u'' return u''
match = re.match(id_re, value) match = re.match(id_re, value)
if not match: if not match:
raise ValidationError(error_msg) raise ValidationError(self.error_messages['invalid'])
gd = match.groupdict() gd = match.groupdict()
residence, origin = gd['residence'], gd['origin'] residence, origin = gd['residence'], gd['origin']
birthday, validity, checksum = gd['birthday'], gd['validity'], gd['checksum'] birthday, validity, checksum = gd['birthday'], gd['validity'], gd['checksum']
if residence == '0000000000' or birthday == '0000000' or validity == '0000000': 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) 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 \ 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): 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) return u'%s%s-%s-%s-%s' % (residence, origin, birthday, validity, checksum)

View File

@ -15,12 +15,14 @@ class ESPostalCodeField(RegexField):
Spanish postal code is a five digits string, with two first digits Spanish postal code is a five digits string, with two first digits
between 01 and 52, assigned to provinces code. 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): def __init__(self, *args, **kwargs):
super(ESPostalCodeField, self).__init__( super(ESPostalCodeField, self).__init__(
r'^(0[1-9]|[1-4][0-9]|5[0-2])\d{3}$', r'^(0[1-9]|[1-4][0-9]|5[0-2])\d{3}$',
max_length=None, min_length=None, max_length=None, min_length=None, *args, **kwargs)
error_message=_('Enter a valid postal code in the range and format 01XXX - 52XXX.'),
*args, **kwargs)
class ESPhoneNumberField(RegexField): class ESPhoneNumberField(RegexField):
""" """
@ -33,11 +35,13 @@ class ESPhoneNumberField(RegexField):
TODO: accept and strip characters like dot, hyphen... in phone number 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): def __init__(self, *args, **kwargs):
super(ESPhoneNumberField, self).__init__(r'^(6|8|9)\d{8}$', super(ESPhoneNumberField, self).__init__(r'^(6|8|9)\d{8}$',
max_length=None, min_length=None, max_length=None, min_length=None, *args, **kwargs)
error_message=_('Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.'),
*args, **kwargs)
class ESIdentityCardNumberField(RegexField): class ESIdentityCardNumberField(RegexField):
""" """
@ -58,19 +62,23 @@ class ESIdentityCardNumberField(RegexField):
public, and different authors have different opinions on which ones allows public, and different authors have different opinions on which ones allows
letters, so both validations are assumed true for all types. 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): def __init__(self, only_nif=False, *args, **kwargs):
self.only_nif = only_nif self.only_nif = only_nif
self.nif_control = 'TRWAGMYFPDXBNJZSQVHLCKE' self.nif_control = 'TRWAGMYFPDXBNJZSQVHLCKE'
self.cif_control = 'JABCDEFGHI' self.cif_control = 'JABCDEFGHI'
self.cif_types = 'ABCDEFGHKLMNPQS' self.cif_types = 'ABCDEFGHKLMNPQS'
self.nie_types = 'XT' 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()), 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, 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) *args, **kwargs)
def clean(self, value): def clean(self, value):
@ -88,13 +96,13 @@ class ESIdentityCardNumberField(RegexField):
if letter2 == nif_get_checksum(number): if letter2 == nif_get_checksum(number):
return value return value
else: else:
raise ValidationError, _('Invalid checksum for NIF.') raise ValidationError, self.error_messages['invalid_nif']
elif letter1 in self.nie_types and letter2: elif letter1 in self.nie_types and letter2:
# NIE # NIE
if letter2 == nif_get_checksum(number): if letter2 == nif_get_checksum(number):
return value return value
else: 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]: elif not self.only_nif and letter1 in self.cif_types and len(number) in [7, 8]:
# CIF # CIF
if not letter2: if not letter2:
@ -103,9 +111,9 @@ class ESIdentityCardNumberField(RegexField):
if letter2 in [checksum, self.cif_control[checksum]]: if letter2 in [checksum, self.cif_control[checksum]]:
return value return value
else: else:
raise ValidationError, _('Invalid checksum for CIF.') raise ValidationError, self.error_messages['invalid_cif']
else: else:
raise ValidationError, _('Please enter a valid %s.' % self.id_types) raise ValidationError, self.error_messages['invalid']
class ESCCCField(RegexField): class ESCCCField(RegexField):
""" """
@ -130,11 +138,14 @@ class ESCCCField(RegexField):
TODO: allow IBAN validation too 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): def __init__(self, *args, **kwargs):
super(ESCCCField, self).__init__(r'^\d{4}[ -]?\d{4}[ -]?\d{2}[ -]?\d{10}$', super(ESCCCField, self).__init__(r'^\d{4}[ -]?\d{4}[ -]?\d{2}[ -]?\d{10}$',
max_length=None, min_length=None, max_length=None, min_length=None, *args, **kwargs)
error_message=_('Please enter a valid bank account number in format XXXX-XXXX-XX-XXXXXXXXXX.'),
*args, **kwargs)
def clean(self, value): def clean(self, value):
super(ESCCCField, self).clean(value) super(ESCCCField, self).clean(value)
@ -147,7 +158,7 @@ class ESCCCField(RegexField):
if get_checksum('00' + entity + office) + get_checksum(account) == checksum: if get_checksum('00' + entity + office) + get_checksum(account) == checksum:
return value return value
else: else:
raise ValidationError, _('Invalid checksum for bank account number.') raise ValidationError, self.error_messages['checksum']
class ESRegionSelect(Select): class ESRegionSelect(Select):
""" """

View File

@ -8,11 +8,12 @@ from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES
from django.utils.translation import ugettext from django.utils.translation import ugettext
class FIZipCodeField(RegexField): class FIZipCodeField(RegexField):
default_error_messages = {
'invalid': ugettext('Enter a zip code in the format XXXXX.'),
}
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(FIZipCodeField, self).__init__(r'^\d{5}$', super(FIZipCodeField, self).__init__(r'^\d{5}$',
max_length=None, min_length=None, max_length=None, min_length=None, *args, **kwargs)
error_message=ugettext('Enter a zip code in the format XXXXX.'),
*args, **kwargs)
class FIMunicipalitySelect(Select): class FIMunicipalitySelect(Select):
""" """
@ -23,6 +24,10 @@ class FIMunicipalitySelect(Select):
super(FIMunicipalitySelect, self).__init__(attrs, choices=MUNICIPALITY_CHOICES) super(FIMunicipalitySelect, self).__init__(attrs, choices=MUNICIPALITY_CHOICES)
class FISocialSecurityNumber(Field): class FISocialSecurityNumber(Field):
default_error_messages = {
'invalid': ugettext('Enter a valid Finnish social security number.'),
}
def clean(self, value): def clean(self, value):
super(FISocialSecurityNumber, self).clean(value) super(FISocialSecurityNumber, self).clean(value)
if value in EMPTY_VALUES: if value in EMPTY_VALUES:
@ -37,9 +42,9 @@ class FISocialSecurityNumber(Field):
(?P<serial>(\d{3})) (?P<serial>(\d{3}))
(?P<checksum>[%s])$""" % checkmarks, value, re.VERBOSE | re.IGNORECASE) (?P<checksum>[%s])$""" % checkmarks, value, re.VERBOSE | re.IGNORECASE)
if not result: if not result:
raise ValidationError(ugettext('Enter a valid Finnish social security number.')) raise ValidationError(self.error_messages['invalid'])
gd = result.groupdict() gd = result.groupdict()
checksum = int(gd['date'] + gd['serial']) checksum = int(gd['date'] + gd['serial'])
if checkmarks[checksum % len(checkmarks)] == gd['checksum'].upper(): if checkmarks[checksum % len(checkmarks)] == gd['checksum'].upper():
return u'%s' % value.upper() return u'%s' % value.upper()
raise ValidationError(ugettext('Enter a valid Finnish social security number.')) raise ValidationError(self.error_messages['invalid'])

View File

@ -11,11 +11,13 @@ import re
phone_digits_re = re.compile(r'^0\d(\s|\.)?(\d{2}(\s|\.)?){3}\d{2}$') phone_digits_re = re.compile(r'^0\d(\s|\.)?(\d{2}(\s|\.)?){3}\d{2}$')
class FRZipCodeField(RegexField): class FRZipCodeField(RegexField):
default_error_messages = {
'invalid': ugettext('Enter a zip code in the format XXXXX.'),
}
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(FRZipCodeField, self).__init__(r'^\d{5}$', super(FRZipCodeField, self).__init__(r'^\d{5}$',
max_length=None, min_length=None, max_length=None, min_length=None, *args, **kwargs)
error_message=ugettext('Enter a zip code in the format XXXXX.'),
*args, **kwargs)
class FRPhoneNumberField(Field): 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' and '0XXXXXXXXX' validate but are corrected to
'0X XX XX XX XX'. '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): def clean(self, value):
super(FRPhoneNumberField, self).clean(value) super(FRPhoneNumberField, self).clean(value)
if value in EMPTY_VALUES: if value in EMPTY_VALUES:
@ -32,7 +38,7 @@ class FRPhoneNumberField(Field):
m = phone_digits_re.search(value) m = phone_digits_re.search(value)
if m: if m:
return u'%s %s %s %s %s' % (value[0:2], value[2:4], value[4:6], value[6:8], value[8:10]) 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): class FRDepartmentSelect(Select):
""" """

View File

@ -10,11 +10,13 @@ import re
class INZipCodeField(RegexField): class INZipCodeField(RegexField):
default_error_messages = {
'invalid': gettext(u'Enter a zip code in the format XXXXXXX.'),
}
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(INZipCodeField, self).__init__(r'^\d{6}$', super(INZipCodeField, self).__init__(r'^\d{6}$',
max_length=None, min_length=None, max_length=None, min_length=None, *args, **kwargs)
error_message=gettext(u'Enter a zip code in the format XXXXXXX.'),
*args, **kwargs)
class INStateField(Field): class INStateField(Field):
""" """
@ -22,6 +24,10 @@ class INStateField(Field):
abbreviation. It normalizes the input to the standard two-letter vehicle abbreviation. It normalizes the input to the standard two-letter vehicle
registration abbreviation for the given state or union territory registration abbreviation for the given state or union territory
""" """
default_error_messages = {
'invalid': u'Enter a Indian state or territory.',
}
def clean(self, value): def clean(self, value):
from in_states import STATES_NORMALIZED from in_states import STATES_NORMALIZED
super(INStateField, self).clean(value) super(INStateField, self).clean(value)
@ -36,7 +42,7 @@ class INStateField(Field):
return smart_unicode(STATES_NORMALIZED[value.strip().lower()]) return smart_unicode(STATES_NORMALIZED[value.strip().lower()])
except KeyError: except KeyError:
pass pass
raise ValidationError(u'Enter a Indian state or territory.') raise ValidationError(self.error_messages['invalid'])
class INStateSelect(Select): class INStateSelect(Select):
""" """

View File

@ -13,10 +13,14 @@ class ISIdNumberField(RegexField):
Icelandic identification number (kennitala). This is a number every citizen Icelandic identification number (kennitala). This is a number every citizen
of Iceland has. 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): 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 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): def clean(self, value):
value = super(ISIdNumberField, self).clean(value) value = super(ISIdNumberField, self).clean(value)
@ -28,7 +32,7 @@ class ISIdNumberField(RegexField):
if self._validate(value): if self._validate(value):
return self._format(value) return self._format(value)
else: else:
raise ValidationError(ugettext(u'The Icelandic identification number is not valid.')) raise ValidationError(self.error_messages['checksum'])
def _canonify(self, value): def _canonify(self, value):
""" """

View File

@ -10,11 +10,12 @@ from django.contrib.localflavor.it.util import ssn_check_digit, vat_number_check
import re import re
class ITZipCodeField(RegexField): class ITZipCodeField(RegexField):
default_error_messages = {
'invalid': ugettext('Enter a valid zip code.'),
}
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(ITZipCodeField, self).__init__(r'^\d{5}$', super(ITZipCodeField, self).__init__(r'^\d{5}$',
max_length=None, min_length=None, max_length=None, min_length=None, *args, **kwargs)
error_message=ugettext('Enter a valid zip code.'),
*args, **kwargs)
class ITRegionSelect(Select): class ITRegionSelect(Select):
""" """
@ -38,11 +39,13 @@ class ITSocialSecurityNumberField(RegexField):
For reference see http://www.agenziaentrate.it/ and search for For reference see http://www.agenziaentrate.it/ and search for
'Informazioni sulla codificazione delle persone fisiche'. '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): def __init__(self, *args, **kwargs):
super(ITSocialSecurityNumberField, self).__init__(r'^\w{3}\s*\w{3}\s*\w{5}\s*\w{5}$', 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, max_length=None, min_length=None, *args, **kwargs)
*args, **kwargs)
def clean(self, value): def clean(self, value):
value = super(ITSocialSecurityNumberField, self).clean(value) value = super(ITSocialSecurityNumberField, self).clean(value)
@ -52,26 +55,29 @@ class ITSocialSecurityNumberField(RegexField):
try: try:
check_digit = ssn_check_digit(value) check_digit = ssn_check_digit(value)
except ValueError: except ValueError:
raise ValidationError(self.err_msg) raise ValidationError(self.error_messages['invalid'])
if not value[15] == check_digit: if not value[15] == check_digit:
raise ValidationError(self.err_msg) raise ValidationError(self.error_messages['invalid'])
return value return value
class ITVatNumberField(Field): class ITVatNumberField(Field):
""" """
A form field that validates Italian VAT numbers (partita IVA). 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): def clean(self, value):
value = super(ITVatNumberField, self).clean(value) value = super(ITVatNumberField, self).clean(value)
if value == u'': if value == u'':
return value return value
err_msg = ugettext(u'Enter a valid VAT number.')
try: try:
vat_number = int(value) vat_number = int(value)
except ValueError: except ValueError:
raise ValidationError(err_msg) raise ValidationError(self.error_messages['invalid'])
vat_number = str(vat_number).zfill(11) vat_number = str(vat_number).zfill(11)
check_digit = vat_number_check_digit(vat_number[0:10]) check_digit = vat_number_check_digit(vat_number[0:10])
if not vat_number[10] == check_digit: if not vat_number[10] == check_digit:
raise ValidationError(err_msg) raise ValidationError(self.error_messages['invalid'])
return smart_unicode(vat_number) return smart_unicode(vat_number)

View File

@ -15,11 +15,13 @@ class JPPostalCodeField(RegexField):
Accepts 7 digits, with or without a hyphen. 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): def __init__(self, *args, **kwargs):
super(JPPostalCodeField, self).__init__(r'^\d{3}-\d{4}$|^\d{7}$', super(JPPostalCodeField, self).__init__(r'^\d{3}-\d{4}$|^\d{7}$',
max_length=None, min_length=None, max_length=None, min_length=None, *args, **kwargs)
error_message=ugettext('Enter a postal code in the format XXXXXXX or XXX-XXXX.'),
*args, **kwargs)
def clean(self, value): def clean(self, value):
""" """

View File

@ -17,18 +17,21 @@ class NLZipCodeField(Field):
""" """
A Dutch postal code field. A Dutch postal code field.
""" """
default_error_messages = {
'invalid': _('Enter a valid postal code'),
}
def clean(self, value): def clean(self, value):
super(NLZipCodeField, self).clean(value) super(NLZipCodeField, self).clean(value)
if value in EMPTY_VALUES: if value in EMPTY_VALUES:
return u'' return u''
msg = _('Enter a valid postal code')
value = value.strip().upper().replace(' ', '') value = value.strip().upper().replace(' ', '')
if not pc_re.search(value): if not pc_re.search(value):
raise ValidationError(msg) raise ValidationError(self.error_messages['invalid'])
if int(value[:4]) < 1000: if int(value[:4]) < 1000:
raise ValidationError(msg) raise ValidationError(self.error_messages['invalid'])
return u'%s %s' % (value[:4], value[4:]) return u'%s %s' % (value[:4], value[4:])
@ -45,12 +48,15 @@ class NLPhoneNumberField(Field):
""" """
A Dutch telephone number field. A Dutch telephone number field.
""" """
default_error_messages = {
'invalid': _('Enter a valid phone number'),
}
def clean(self, value): def clean(self, value):
super(NLPhoneNumberField, self).clean(value) super(NLPhoneNumberField, self).clean(value)
if value in EMPTY_VALUES: if value in EMPTY_VALUES:
return u'' return u''
msg = _('Enter a valid phone number')
phone_nr = re.sub('[\-\s\(\)]', '', smart_unicode(value)) phone_nr = re.sub('[\-\s\(\)]', '', smart_unicode(value))
if len(phone_nr) == 10 and numeric_re.search(phone_nr): if len(phone_nr) == 10 and numeric_re.search(phone_nr):
@ -60,7 +66,7 @@ class NLPhoneNumberField(Field):
numeric_re.search(phone_nr[3:]): numeric_re.search(phone_nr[3:]):
return value return value
raise ValidationError(msg) raise ValidationError(self.error_messages['invalid'])
class NLSoFiNumberField(Field): class NLSoFiNumberField(Field):
""" """
@ -68,18 +74,20 @@ class NLSoFiNumberField(Field):
http://nl.wikipedia.org/wiki/Sofinummer http://nl.wikipedia.org/wiki/Sofinummer
""" """
default_error_messages = {
'invalid': _('Enter a valid SoFi number'),
}
def clean(self, value): def clean(self, value):
super(NLSoFiNumberField, self).clean(value) super(NLSoFiNumberField, self).clean(value)
if value in EMPTY_VALUES: if value in EMPTY_VALUES:
return u'' return u''
msg = _('Enter a valid SoFi number')
if not sofi_re.search(value): if not sofi_re.search(value):
raise ValidationError(msg) raise ValidationError(self.error_messages['invalid'])
if int(value) == 0: if int(value) == 0:
raise ValidationError(msg) raise ValidationError(self.error_messages['invalid'])
checksum = 0 checksum = 0
for i in range(9, 1, -1): for i in range(9, 1, -1):
@ -87,6 +95,6 @@ class NLSoFiNumberField(Field):
checksum -= int(value[-1]) checksum -= int(value[-1])
if checksum % 11 != 0: if checksum % 11 != 0:
raise ValidationError(msg) raise ValidationError(self.error_messages['invalid'])
return value return value

View File

@ -8,11 +8,13 @@ from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES
from django.utils.translation import ugettext from django.utils.translation import ugettext
class NOZipCodeField(RegexField): class NOZipCodeField(RegexField):
default_error_messages = {
'invalid': ugettext('Enter a zip code in the format XXXX.'),
}
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(NOZipCodeField, self).__init__(r'^\d{4}$', super(NOZipCodeField, self).__init__(r'^\d{4}$',
max_length=None, min_length=None, max_length=None, min_length=None, *args, **kwargs)
error_message=ugettext('Enter a zip code in the format XXXX.'),
*args, **kwargs)
class NOMunicipalitySelect(Select): class NOMunicipalitySelect(Select):
""" """
@ -27,14 +29,17 @@ class NOSocialSecurityNumber(Field):
""" """
Algorithm is documented at http://no.wikipedia.org/wiki/Personnummer 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): def clean(self, value):
super(NOSocialSecurityNumber, self).clean(value) super(NOSocialSecurityNumber, self).clean(value)
if value in EMPTY_VALUES: if value in EMPTY_VALUES:
return u'' return u''
msg = ugettext(u'Enter a valid Norwegian social security number.')
if not re.match(r'^\d{11}$', value): if not re.match(r'^\d{11}$', value):
raise ValidationError(msg) raise ValidationError(self.error_messages['invalid'])
day = int(value[:2]) day = int(value[:2])
month = int(value[2:4]) month = int(value[2:4])
@ -52,7 +57,7 @@ class NOSocialSecurityNumber(Field):
if 900 <= inum < 1000 and year2 > 39: if 900 <= inum < 1000 and year2 > 39:
self.birthday = datetime.date(1900+year2, month, day) self.birthday = datetime.date(1900+year2, month, day)
except ValueError: except ValueError:
raise ValidationError(msg) raise ValidationError(self.error_messages['invalid'])
sexnum = int(value[8]) sexnum = int(value[8])
if sexnum % 2 == 0: if sexnum % 2 == 0:
@ -68,9 +73,9 @@ class NOSocialSecurityNumber(Field):
return sum([(a * b) for (a, b) in zip(aval, bval)]) return sum([(a * b) for (a, b) in zip(aval, bval)])
if multiply_reduce(digits, weight_1) % 11 != 0: 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: if multiply_reduce(digits, weight_2) % 11 != 0:
raise ValidationError(msg) raise ValidationError(self.error_messages['invalid'])
return value return value

View File

@ -19,6 +19,11 @@ class PEDNIField(CharField):
""" """
A field that validates `Documento Nacional de IdentidadŽ (DNI) numbers. 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): def __init__(self, *args, **kwargs):
super(PEDNIField, self).__init__(max_length=8, min_length=8, *args, super(PEDNIField, self).__init__(max_length=8, min_length=8, *args,
**kwargs) **kwargs)
@ -31,9 +36,9 @@ class PEDNIField(CharField):
if value in EMPTY_VALUES: if value in EMPTY_VALUES:
return u'' return u''
if not value.isdigit(): if not value.isdigit():
raise ValidationError(ugettext("This field requires only numbers.")) raise ValidationError(self.error_messages['invalid'])
if len(value) != 8: if len(value) != 8:
raise ValidationError(ugettext("This field requires 8 digits.")) raise ValidationError(self.error_messages['max_digits'])
return value return value
@ -42,6 +47,11 @@ class PERUCField(RegexField):
This field validates a RUC (Registro Unico de Contribuyentes). A RUC is of This field validates a RUC (Registro Unico de Contribuyentes). A RUC is of
the form XXXXXXXXXXX. 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): def __init__(self, *args, **kwargs):
super(PERUCField, self).__init__(max_length=11, min_length=11, *args, super(PERUCField, self).__init__(max_length=11, min_length=11, *args,
**kwargs) **kwargs)
@ -54,8 +64,8 @@ class PERUCField(RegexField):
if value in EMPTY_VALUES: if value in EMPTY_VALUES:
return u'' return u''
if not value.isdigit(): if not value.isdigit():
raise ValidationError(ugettext("This field requires only numbers.")) raise ValidationError(self.error_messages['invalid'])
if len(value) != 11: if len(value) != 11:
raise ValidationError(ugettext("This field requires 11 digits.")) raise ValidationError(self.error_messages['max_digits'])
return value return value

View File

@ -35,16 +35,19 @@ class PLNationalIdentificationNumberField(RegexField):
The algorithm is documented at http://en.wikipedia.org/wiki/PESEL. 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): def __init__(self, *args, **kwargs):
super(PLNationalIdentificationNumberField, self).__init__(r'^\d{11}$', super(PLNationalIdentificationNumberField, self).__init__(r'^\d{11}$',
max_length=None, min_length=None, error_message=_(u'National Identification Number consists of 11 digits.'), max_length=None, min_length=None, *args, **kwargs)
*args, **kwargs)
def clean(self,value): def clean(self,value):
super(PLNationalIdentificationNumberField, self).clean(value) super(PLNationalIdentificationNumberField, self).clean(value)
if not self.has_valid_checksum(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 return u'%s' % value
def has_valid_checksum(self, number): def has_valid_checksum(self, number):
@ -65,17 +68,20 @@ class PLTaxNumberField(RegexField):
Checksum algorithm based on documentation at Checksum algorithm based on documentation at
http://wipos.p.lodz.pl/zylla/ut/nip-rego.html 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): 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}$', 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, max_length=None, min_length=None, *args, **kwargs)
error_message=_(u'Enter a tax number field (NIP) in the format XXX-XXX-XX-XX or XX-XX-XXX-XXX.'), *args, **kwargs)
def clean(self,value): def clean(self,value):
super(PLTaxNumberField, self).clean(value) super(PLTaxNumberField, self).clean(value)
value = re.sub("[-]", "", value) value = re.sub("[-]", "", value)
if not self.has_valid_checksum(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 return u'%s' % value
def has_valid_checksum(self, number): 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 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): def __init__(self, *args, **kwargs):
super(PLNationalBusinessRegisterField, self).__init__(r'^\d{7,9}$', 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.'), max_length=None, min_length=None, *args, **kwargs)
*args, **kwargs)
def clean(self,value): def clean(self,value):
super(PLNationalBusinessRegisterField, self).clean(value) super(PLNationalBusinessRegisterField, self).clean(value)
if not self.has_valid_checksum(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 return u'%s' % value
def has_valid_checksum(self, number): def has_valid_checksum(self, number):
@ -142,9 +152,10 @@ class PLPostalCodeField(RegexField):
A form field that validates as Polish postal code. A form field that validates as Polish postal code.
Valid code is XX-XXX where X is digit. 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): def __init__(self, *args, **kwargs):
super(PLPostalCodeField, self).__init__(r'^\d{2}-\d{3}$', super(PLPostalCodeField, self).__init__(r'^\d{2}-\d{3}$',
max_length=None, min_length=None, max_length=None, min_length=None, *args, **kwargs)
error_message=_(u'Enter a postal code in the format XX-XXX.'),
*args, **kwargs)

View File

@ -26,11 +26,13 @@ class SKPostalCodeField(RegexField):
A form field that validates its input as Slovak postal code. A form field that validates its input as Slovak postal code.
Valid form is XXXXX or XXX XX, where X represents integer. 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): def __init__(self, *args, **kwargs):
super(SKPostalCodeField, self).__init__(r'^\d{5}$|^\d{3} \d{2}$', super(SKPostalCodeField, self).__init__(r'^\d{5}$|^\d{3} \d{2}$',
max_length=None, min_length=None, max_length=None, min_length=None, *args, **kwargs)
error_message=ugettext(u'Enter a postal code in the format XXXXX or XXX XX.'),
*args, **kwargs)
def clean(self, value): def clean(self, value):
""" """

View File

@ -2,21 +2,39 @@
UK-specific Form helpers 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 from django.utils.translation import ugettext
class UKPostcodeField(RegexField): class UKPostcodeField(CharField):
""" """
A form field that validates its input is a UK postcode. A form field that validates its input is a UK postcode.
The regular expression used is sourced from the schema for British Standard 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 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): default_error_messages = {
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})$', 'invalid': ugettext(u'Enter a valid postcode.'),
max_length=None, min_length=None, }
error_message=ugettext(u'Enter a postcode. A space is required between the two postcode parts.'), outcode_pattern = '[A-PR-UWYZ]([0-9]{1,2}|([A-HIK-Y][0-9](|[0-9]|[ABEHMNPRVWXY]))|[0-9][A-HJKSTUW])'
*args, **kwargs) 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): class UKCountySelect(Select):
""" """

View File

@ -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})$") ssn_re = re.compile(r"^(?P<area>\d{3})[-\ ]?(?P<group>\d{2})[-\ ]?(?P<serial>\d{4})$")
class USZipCodeField(RegexField): class USZipCodeField(RegexField):
default_error_messages = {
'invalid': ugettext('Enter a zip code in the format XXXXX or XXXXX-XXXX.'),
}
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(USZipCodeField, self).__init__(r'^\d{5}(?:-\d{4})?$', super(USZipCodeField, self).__init__(r'^\d{5}(?:-\d{4})?$',
max_length=None, min_length=None, max_length=None, min_length=None, *args, **kwargs)
error_message=ugettext('Enter a zip code in the format XXXXX or XXXXX-XXXX.'),
*args, **kwargs)
class USPhoneNumberField(Field): class USPhoneNumberField(Field):
default_error_messages = {
'invalid': u'Phone numbers must be in XXX-XXX-XXXX format.',
}
def clean(self, value): def clean(self, value):
super(USPhoneNumberField, self).clean(value) super(USPhoneNumberField, self).clean(value)
if value in EMPTY_VALUES: if value in EMPTY_VALUES:
@ -27,7 +33,7 @@ class USPhoneNumberField(Field):
m = phone_digits_re.search(value) m = phone_digits_re.search(value)
if m: if m:
return u'%s-%s-%s' % (m.group(1), m.group(2), m.group(3)) 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): class USSocialSecurityNumberField(Field):
""" """
@ -44,28 +50,31 @@ class USSocialSecurityNumberField(Field):
promotional use or distribution (e.g., the Woolworth's number or the promotional use or distribution (e.g., the Woolworth's number or the
1962 promotional number). 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): def clean(self, value):
super(USSocialSecurityNumberField, self).clean(value) super(USSocialSecurityNumberField, self).clean(value)
if value in EMPTY_VALUES: if value in EMPTY_VALUES:
return u'' return u''
msg = ugettext('Enter a valid U.S. Social Security number in XXX-XX-XXXX format.')
match = re.match(ssn_re, value) match = re.match(ssn_re, value)
if not match: if not match:
raise ValidationError(msg) raise ValidationError(self.error_messages['invalid'])
area, group, serial = match.groupdict()['area'], match.groupdict()['group'], match.groupdict()['serial'] area, group, serial = match.groupdict()['area'], match.groupdict()['group'], match.groupdict()['serial']
# First pass: no blocks of all zeroes. # First pass: no blocks of all zeroes.
if area == '000' or \ if area == '000' or \
group == '00' or \ group == '00' or \
serial == '0000': serial == '0000':
raise ValidationError(msg) raise ValidationError(self.error_messages['invalid'])
# Second pass: promotional and otherwise permanently invalid numbers. # Second pass: promotional and otherwise permanently invalid numbers.
if area == '666' or \ if area == '666' or \
(area == '987' and group == '65' and 4320 <= int(serial) <= 4329) or \ (area == '987' and group == '65' and 4320 <= int(serial) <= 4329) or \
value == '078-05-1120' or \ value == '078-05-1120' or \
value == '219-09-9999': value == '219-09-9999':
raise ValidationError(msg) raise ValidationError(self.error_messages['invalid'])
return u'%s-%s-%s' % (area, group, serial) return u'%s-%s-%s' % (area, group, serial)
class USStateField(Field): class USStateField(Field):
@ -74,6 +83,10 @@ class USStateField(Field):
It normalizes the input to the standard two-leter postal service It normalizes the input to the standard two-leter postal service
abbreviation for the given state. abbreviation for the given state.
""" """
default_error_messages = {
'invalid': u'Enter a U.S. state or territory.',
}
def clean(self, value): def clean(self, value):
from us_states import STATES_NORMALIZED from us_states import STATES_NORMALIZED
super(USStateField, self).clean(value) super(USStateField, self).clean(value)
@ -88,7 +101,7 @@ class USStateField(Field):
return STATES_NORMALIZED[value.strip().lower()].decode('ascii') return STATES_NORMALIZED[value.strip().lower()].decode('ascii')
except KeyError: except KeyError:
pass pass
raise ValidationError(u'Enter a U.S. state or territory.') raise ValidationError(self.error_messages['invalid'])
class USStateSelect(Select): class USStateSelect(Select):
""" """

View File

@ -16,10 +16,9 @@ class ZAIDField(Field):
using the Luhn checksum, and uses a simlistic (read: not entirely accurate) using the Luhn checksum, and uses a simlistic (read: not entirely accurate)
check for the birthdate check for the birthdate
""" """
default_error_messages = {
def __init__(self, *args, **kwargs): 'invalid': _(u'Enter a valid South African ID number'),
super(ZAIDField, self).__init__() }
self.error_message = _(u'Enter a valid South African ID number')
def clean(self, value): def clean(self, value):
# strip spaces and dashes # strip spaces and dashes
@ -33,7 +32,7 @@ class ZAIDField(Field):
match = re.match(id_re, value) match = re.match(id_re, value)
if not match: if not match:
raise ValidationError(self.error_message) raise ValidationError(self.error_messages['invalid'])
g = match.groupdict() g = match.groupdict()
@ -43,15 +42,18 @@ class ZAIDField(Field):
# There is no way to guess the century of a ZA ID number # 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'])) d = date(int(g['yy']) + 2000, int(g['mm']), int(g['dd']))
except ValueError: except ValueError:
raise ValidationError(self.error_message) raise ValidationError(self.error_messages['invalid'])
if not luhn(value): if not luhn(value):
raise ValidationError(self.error_message) raise ValidationError(self.error_messages['invalid'])
return value return value
class ZAPostCodeField(RegexField): class ZAPostCodeField(RegexField):
default_error_messages = {
'invalid': _(u'Enter a valid South African postal code'),
}
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(ZAPostCodeField, self).__init__(r'^\d{4}$', super(ZAPostCodeField, self).__init__(r'^\d{4}$',
max_length=None, min_length=None, max_length=None, min_length=None)
error_message=_(u'Enter a valid South African postal code'))

View File

@ -1,14 +1,23 @@
import os import os
import tempfile
from django.conf import settings from django.conf import settings
from django.contrib.sessions.backends.base import SessionBase from django.contrib.sessions.backends.base import SessionBase
from django.core.exceptions import SuspiciousOperation from django.core.exceptions import SuspiciousOperation, ImproperlyConfigured
class SessionStore(SessionBase): class SessionStore(SessionBase):
""" """
Implements a file based session store. Implements a file based session store.
""" """
def __init__(self, session_key=None): 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 self.file_prefix = settings.SESSION_COOKIE_NAME
super(SessionStore, self).__init__(session_key) super(SessionStore, self).__init__(session_key)

View File

@ -1,5 +1,6 @@
r""" r"""
>>> from django.conf import settings
>>> from django.contrib.sessions.backends.db import SessionStore as DatabaseSession >>> 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.cache import SessionStore as CacheSession
>>> from django.contrib.sessions.backends.file import SessionStore as FileSession >>> from django.contrib.sessions.backends.file import SessionStore as FileSession
@ -39,6 +40,13 @@ True
>>> file_session.exists(file_session.session_key) >>> file_session.exists(file_session.session_key)
False 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 = CacheSession()
>>> cache_session.modified >>> cache_session.modified
False False

View File

@ -1,12 +1,12 @@
"File-based cache backend" "File-based cache backend"
import md5
import os, time import os, time
try: try:
import cPickle as pickle import cPickle as pickle
except ImportError: except ImportError:
import pickle import pickle
from django.core.cache.backends.base import BaseCache from django.core.cache.backends.base import BaseCache
from django.utils.http import urlquote_plus
class CacheClass(BaseCache): class CacheClass(BaseCache):
def __init__(self, dir, params): def __init__(self, dir, params):
@ -29,24 +29,10 @@ class CacheClass(BaseCache):
self._createdir() self._createdir()
def add(self, key, value, timeout=None): def add(self, key, value, timeout=None):
fname = self._key_to_file(key) if self.has_key(key):
if timeout is None: return None
timeout = self.default_timeout
try: self.set(key, value, timeout)
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
def get(self, key, default=None): def get(self, key, default=None):
fname = self._key_to_file(key) fname = self._key_to_file(key)
@ -56,7 +42,7 @@ class CacheClass(BaseCache):
now = time.time() now = time.time()
if exp < now: if exp < now:
f.close() f.close()
os.remove(fname) self._delete(fname)
else: else:
return pickle.load(f) return pickle.load(f)
except (IOError, OSError, EOFError, pickle.PickleError): except (IOError, OSError, EOFError, pickle.PickleError):
@ -65,40 +51,74 @@ class CacheClass(BaseCache):
def set(self, key, value, timeout=None): def set(self, key, value, timeout=None):
fname = self._key_to_file(key) fname = self._key_to_file(key)
dirname = os.path.dirname(fname)
if timeout is None: if timeout is None:
timeout = self.default_timeout timeout = self.default_timeout
self._cull()
try: try:
filelist = os.listdir(self._dir) if not os.path.exists(dirname):
except (IOError, OSError): os.makedirs(dirname)
self._createdir()
filelist = []
if len(filelist) > self._max_entries:
self._cull(filelist)
try:
f = open(fname, 'wb') f = open(fname, 'wb')
now = time.time() now = time.time()
pickle.dump(now + timeout, f, 2) pickle.dump(now + timeout, f, pickle.HIGHEST_PROTOCOL)
pickle.dump(value, f, 2) pickle.dump(value, f, pickle.HIGHEST_PROTOCOL)
except (IOError, OSError): except (IOError, OSError):
pass pass
def delete(self, key): def delete(self, key):
try: 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): except (IOError, OSError):
pass pass
def has_key(self, key): 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):
if int(self._num_entries) < self._max_entries:
return
try:
filelist = os.listdir(self._dir)
except (IOError, OSError):
return
def _cull(self, filelist):
if self._cull_frequency == 0: if self._cull_frequency == 0:
doomed = filelist doomed = filelist
else: else:
doomed = [k for (i, k) in enumerate(filelist) if i % self._cull_frequency == 0] doomed = [os.path.join(self._dir, k) for (i, k) in enumerate(filelist) if i % self._cull_frequency == 0]
for fname in doomed:
for topdir in doomed:
try: 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): except (IOError, OSError):
pass pass
@ -109,4 +129,22 @@ class CacheClass(BaseCache):
raise EnvironmentError, "Cache directory '%s' does not exist and could not be created'" % self._dir raise EnvironmentError, "Cache directory '%s' does not exist and could not be created'" % self._dir
def _key_to_file(self, key): 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)

View File

@ -29,44 +29,31 @@ class CacheClass(BaseCache):
self._lock = RWLock() 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): def add(self, key, value, timeout=None):
self._lock.writer_enters() self._lock.writer_enters()
# Python 2.3 and 2.4 don't allow combined try-except-finally blocks.
try: try:
exp = self._expire_info.get(key)
if exp is None or exp <= time.time():
try: try:
self._add(key, pickle.dumps(value), timeout) self._set(key, pickle.dumps(value), timeout)
except pickle.PickleError: except pickle.PickleError:
pass pass
finally: finally:
self._lock.writer_leaves() self._lock.writer_leaves()
def get(self, key, default=None): def get(self, key, default=None):
should_delete = False
self._lock.reader_enters() self._lock.reader_enters()
try: try:
now = time.time()
exp = self._expire_info.get(key) exp = self._expire_info.get(key)
if exp is None: if exp is None:
return default return default
elif exp < now: elif exp > time.time():
should_delete = True
else:
try: try:
return pickle.loads(self._cache[key]) return pickle.loads(self._cache[key])
except pickle.PickleError: except pickle.PickleError:
return default return default
finally: finally:
self._lock.reader_leaves() self._lock.reader_leaves()
if should_delete:
self._lock.writer_enters() self._lock.writer_enters()
try: try:
del self._cache[key] del self._cache[key]
@ -95,7 +82,23 @@ class CacheClass(BaseCache):
self._lock.writer_leaves() self._lock.writer_leaves()
def has_key(self, key): 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): def _cull(self):
if self._cull_frequency == 0: if self._cull_frequency == 0:

View File

@ -10,7 +10,7 @@ from django.core.management.base import BaseCommand, CommandError, handle_defaul
get_version = django.get_version get_version = django.get_version
# A cache of loaded commands, so that call_command # 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 _commands = None
def find_commands(management_dir): def find_commands(management_dir):
@ -29,8 +29,8 @@ def find_commands(management_dir):
def find_management_module(app_name): def find_management_module(app_name):
""" """
Determines the path to the management module for the application named, Determines the path to the management module for the given app_name,
without acutally importing the application or the management module. without actually importing the application or the management module.
Raises ImportError if the management module cannot be found for any reason. 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): def load_command_class(app_name, name):
""" """
Given a command name and an application name, returns the Command 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. (ImportError, AttributeError) are allowed to propagate.
""" """
return getattr(__import__('%s.management.commands.%s' % (app_name, name), return getattr(__import__('%s.management.commands.%s' % (app_name, name),
{}, {}, ['Command']), 'Command')() {}, {}, ['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 Returns a dictionary mapping command names to their callback applications.
those commands can be found. This works by looking for a
management.commands package in django.core, and in each installed This works by looking for a management.commands package in django.core, and
application -- if a commands package exists, all commands in that in each installed application -- if a commands package exists, all commands
package are registered. in that package are registered.
Core commands are always included. If a settings module has been Core commands are always included. If a settings module has been
specified, user-defined commands will also be included, the 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 startapp command), the instantiated module can be placed in the
dictionary in place of the application name. 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. calls.
""" """
global _commands global _commands
if _commands is None: if _commands is None:
_commands = dict([(name, 'django.core') _commands = dict([(name, 'django.core') for name in find_commands(__path__[0])])
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 = []
for app_name in apps: if load_user_commands:
# Get commands from all installed apps.
from django.conf import settings
for app_name in settings.INSTALLED_APPS:
try: try:
path = find_management_module(app_name) path = find_management_module(app_name)
_commands.update(dict([(name, app_name) _commands.update(dict([(name, app_name) for name in find_commands(path)]))
for name in find_commands(path)]))
except ImportError: except ImportError:
pass # No management module - ignore this app pass # No management module -- ignore this app.
# Try to determine the project directory
try:
from django.conf import settings
project_directory = setup_environ(__import__(settings.SETTINGS_MODULE))
except (AttributeError, ImportError):
project_directory = None
if project_directory: if project_directory:
# Remove the "startproject" command from self.commands, because # Remove the "startproject" command from self.commands, because
@ -157,18 +145,18 @@ class ManagementUtility(object):
def __init__(self, argv=None): def __init__(self, argv=None):
self.argv = argv or sys.argv[:] self.argv = argv or sys.argv[:]
self.prog_name = os.path.basename(self.argv[0]) self.prog_name = os.path.basename(self.argv[0])
self.project_directory = None
self.user_commands = False
def main_help_text(self): def main_help_text(self):
""" """
Returns the script's main help text, as a string. Returns the script's main help text, as a string.
""" """
usage = ['%s <subcommand> [options] [args]' % self.prog_name] usage = ['%s <subcommand> [options] [args]' % self.prog_name]
usage.append('Django command line tool,' usage.append('Django command line tool, version %s' % django.get_version())
' version %s' % django.get_version()) usage.append("Type '%s help <subcommand>' for help on a specific subcommand." % self.prog_name)
usage.append("Type '%s help <subcommand>' for help on a specific"
" subcommand." % self.prog_name)
usage.append('Available subcommands:') usage.append('Available subcommands:')
commands = get_commands().keys() commands = get_commands(self.user_commands, self.project_directory).keys()
commands.sort() commands.sort()
for cmd in commands: for cmd in commands:
usage.append(' %s' % cmd) usage.append(' %s' % cmd)
@ -178,18 +166,18 @@ class ManagementUtility(object):
""" """
Tries to fetch the given subcommand, printing a message with the Tries to fetch the given subcommand, printing a message with the
appropriate command called from the command line (usually 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: try:
app_name = get_commands()[subcommand] app_name = get_commands(self.user_commands, self.project_directory)[subcommand]
if isinstance(app_name, BaseCommand): if isinstance(app_name, BaseCommand):
# If the command is already loaded, use it directly. # If the command is already loaded, use it directly.
klass = app_name klass = app_name
else: else:
klass = load_command_class(app_name, subcommand) klass = load_command_class(app_name, subcommand)
except KeyError: except KeyError:
sys.stderr.write("Unknown command: %r\nType '%s help' for" sys.stderr.write("Unknown command: %r\nType '%s help' for usage.\n" % \
" usage.\n" % (subcommand, self.prog_name)) (subcommand, self.prog_name))
sys.exit(1) sys.exit(1)
return klass return klass
@ -201,8 +189,7 @@ class ManagementUtility(object):
# Preprocess options to extract --settings and --pythonpath. # Preprocess options to extract --settings and --pythonpath.
# These options could affect the commands that are available, so they # These options could affect the commands that are available, so they
# must be processed early. # must be processed early.
parser = LaxOptionParser(version=get_version(), parser = LaxOptionParser(version=get_version(), option_list=BaseCommand.option_list)
option_list=BaseCommand.option_list)
try: try:
options, args = parser.parse_args(self.argv) options, args = parser.parse_args(self.argv)
handle_default_options(options) handle_default_options(options)
@ -242,6 +229,8 @@ class ProjectManagementUtility(ManagementUtility):
""" """
def __init__(self, argv, project_directory): def __init__(self, argv, project_directory):
super(ProjectManagementUtility, self).__init__(argv) super(ProjectManagementUtility, self).__init__(argv)
self.project_directory = project_directory
self.user_commands = True
def setup_environ(settings_mod): def setup_environ(settings_mod):
""" """
@ -263,8 +252,7 @@ def setup_environ(settings_mod):
sys.path.pop() sys.path.pop()
# Set DJANGO_SETTINGS_MODULE appropriately. # Set DJANGO_SETTINGS_MODULE appropriately.
os.environ['DJANGO_SETTINGS_MODULE'] = '%s.%s' % (project_name, os.environ['DJANGO_SETTINGS_MODULE'] = '%s.%s' % (project_name, settings_name)
settings_name)
return project_directory return project_directory
def execute_from_command_line(argv=None): def execute_from_command_line(argv=None):

View File

@ -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.'), 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', make_option('--pythonpath',
help='A directory to add to the Python path, e.g. "/home/djangoprojects/myproject".'), 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 = '' help = ''
args = '' args = ''

View File

@ -1,11 +1,12 @@
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand, CommandError
from django.core import serializers
from optparse import make_option from optparse import make_option
class Command(BaseCommand): class Command(BaseCommand):
option_list = BaseCommand.option_list + ( option_list = BaseCommand.option_list + (
make_option('--format', default='json', dest='format', 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', make_option('--indent', default=None, dest='indent', type='int',
help='Specifies the indent level to use when pretty-printing output'), help='Specifies the indent level to use when pretty-printing output'),
) )
@ -14,10 +15,10 @@ class Command(BaseCommand):
def handle(self, *app_labels, **options): def handle(self, *app_labels, **options):
from django.db.models import get_app, get_apps, get_models from django.db.models import get_app, get_apps, get_models
from django.core import serializers
format = options.get('format', 'json') format = options.get('format', 'json')
indent = options.get('indent', None) indent = options.get('indent', None)
show_traceback = options.get('traceback', False)
if len(app_labels) == 0: if len(app_labels) == 0:
app_list = get_apps() app_list = get_apps()
@ -26,6 +27,9 @@ class Command(BaseCommand):
# Check that the serialization format exists; this is a shortcut to # Check that the serialization format exists; this is a shortcut to
# avoid collating all the objects and _then_ failing. # 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: try:
serializers.get_serializer(format) serializers.get_serializer(format)
except KeyError: except KeyError:
@ -34,8 +38,10 @@ class Command(BaseCommand):
objects = [] objects = []
for app in app_list: for app in app_list:
for model in get_models(app): for model in get_models(app):
objects.extend(model.objects.all()) objects.extend(model._default_manager.all())
try: try:
return serializers.serialize(format, objects, indent=indent) return serializers.serialize(format, objects, indent=indent)
except Exception, e: except Exception, e:
if show_traceback:
raise
raise CommandError("Unable to serialize database: %s" % e) raise CommandError("Unable to serialize database: %s" % e)

View File

@ -27,6 +27,7 @@ class Command(BaseCommand):
self.style = no_style() self.style = no_style()
verbosity = int(options.get('verbosity', 1)) verbosity = int(options.get('verbosity', 1))
show_traceback = options.get('traceback', False)
# Keep a count of the installed objects and fixtures # Keep a count of the installed objects and fixtures
count = [0, 0] count = [0, 0]
@ -50,15 +51,15 @@ class Command(BaseCommand):
parts = fixture_label.split('.') parts = fixture_label.split('.')
if len(parts) == 1: if len(parts) == 1:
fixture_name = fixture_label fixture_name = fixture_label
formats = serializers.get_serializer_formats() formats = serializers.get_public_serializer_formats()
else: else:
fixture_name, format = '.'.join(parts[:-1]), parts[-1] 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] formats = [format]
else: else:
formats = [] formats = []
if verbosity > 0: if verbosity > 2:
if formats: if formats:
print "Loading '%s' fixtures..." % fixture_name print "Loading '%s' fixtures..." % fixture_name
else: else:
@ -98,15 +99,17 @@ class Command(BaseCommand):
label_found = True label_found = True
except Exception, e: except Exception, e:
fixture.close() fixture.close()
transaction.rollback()
transaction.leave_transaction_management()
if show_traceback:
raise
sys.stderr.write( sys.stderr.write(
self.style.ERROR("Problem installing fixture '%s': %s\n" % self.style.ERROR("Problem installing fixture '%s': %s\n" %
(full_path, str(e)))) (full_path, str(e))))
transaction.rollback()
transaction.leave_transaction_management()
return return
fixture.close() fixture.close()
except: except:
if verbosity > 1: if verbosity > 2:
print "No %s fixture '%s' in %s." % \ print "No %s fixture '%s' in %s." % \
(format, fixture_name, humanize(fixture_dir)) (format, fixture_name, humanize(fixture_dir))
@ -122,7 +125,7 @@ class Command(BaseCommand):
transaction.leave_transaction_management() transaction.leave_transaction_management()
if count[0] == 0: if count[0] == 0:
if verbosity > 0: if verbosity > 2:
print "No fixtures found." print "No fixtures found."
else: else:
if verbosity > 0: if verbosity > 0:

View File

@ -116,6 +116,7 @@ def sql_delete(app, style):
"Returns a list of the DROP TABLE SQL statements for the given app." "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 import connection, models, get_introspection_module
from django.db.backends.util import truncate_name from django.db.backends.util import truncate_name
from django.contrib.contenttypes import generic
introspection = get_introspection_module() introspection = get_introspection_module()
# This should work even if a connection isn't available # This should work even if a connection isn't available
@ -179,6 +180,8 @@ def sql_delete(app, style):
for model in app_models: for model in app_models:
opts = model._meta opts = model._meta
for f in opts.many_to_many: 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: if cursor and table_name_converter(f.m2m_db_table()) in table_names:
output.append("%s %s;" % (style.SQL_KEYWORD('DROP TABLE'), output.append("%s %s;" % (style.SQL_KEYWORD('DROP TABLE'),
style.SQL_TABLE(qn(f.m2m_db_table())))) style.SQL_TABLE(qn(f.m2m_db_table()))))

View File

@ -53,6 +53,11 @@ def get_serializer_formats():
_load_serializers() _load_serializers()
return _serializers.keys() 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): def get_deserializer(format):
if not _serializers: if not _serializers:
_load_serializers() _load_serializers()

View File

@ -22,6 +22,10 @@ class Serializer(object):
Abstract serializer base class. 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): def serialize(self, queryset, **options):
""" """
Serialize a queryset. Serialize a queryset.

View File

@ -20,6 +20,8 @@ class Serializer(PythonSerializer):
""" """
Convert a queryset to JSON. Convert a queryset to JSON.
""" """
internal_use_only = False
def end_serialization(self): def end_serialization(self):
self.options.pop('stream', None) self.options.pop('stream', None)
self.options.pop('fields', None) self.options.pop('fields', None)

View File

@ -14,6 +14,8 @@ class Serializer(base.Serializer):
Serializes a QuerySet to basic Python objects. Serializes a QuerySet to basic Python objects.
""" """
internal_use_only = True
def start_serialization(self): def start_serialization(self):
self._current = None self._current = None
self.objects = [] self.objects = []

View File

@ -5,6 +5,7 @@ Requires PyYaml (http://pyyaml.org/), but that's checked for in __init__.
""" """
import datetime import datetime
from django.db import models
from django.core.serializers.python import Serializer as PythonSerializer from django.core.serializers.python import Serializer as PythonSerializer
from django.core.serializers.python import Deserializer as PythonDeserializer from django.core.serializers.python import Deserializer as PythonDeserializer
try: try:
@ -17,10 +18,25 @@ class Serializer(PythonSerializer):
""" """
Convert a queryset to YAML. 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): def end_serialization(self):
self.options.pop('stream', None) self.options.pop('stream', None)
self.options.pop('fields', 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): def getvalue(self):
return self.stream.getvalue() return self.stream.getvalue()

View File

@ -398,6 +398,18 @@ class ServerHandler(object):
self.bytes_sent += len(data) self.bytes_sent += len(data)
# XXX check Content-Length and truncate if too many bytes written? # XXX check Content-Length and truncate if too many bytes written?
# 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._write(data)
self._flush() self._flush()

View File

@ -34,8 +34,8 @@ except ImportError, e:
raise # If there's some other error, this must be an error in Django itself. raise # If there's some other error, this must be an error in Django itself.
def _import_database_module(import_path='', module_name=''): def _import_database_module(import_path='', module_name=''):
"""Lazyily import a database module when requested.""" """Lazily import a database module when requested."""
return __import__('%s%s.%s' % (_import_path, settings.DATABASE_ENGINE, module_name), {}, {}, ['']) return __import__('%s%s.%s' % (import_path, settings.DATABASE_ENGINE, module_name), {}, {}, [''])
# We don't want to import the introspect/creation modules unless # We don't want to import the introspect/creation modules unless
# someone asks for 'em, so lazily load them on demmand. # someone asks for 'em, so lazily load them on demmand.

View File

@ -455,6 +455,23 @@ class FormatStylePlaceholderCursor(Database.Cursor):
else: else:
return tuple([smart_str(p, self.charset, True) for p in params]) 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): def execute(self, query, params=None):
if params is None: if params is None:
params = [] params = []
@ -468,6 +485,7 @@ class FormatStylePlaceholderCursor(Database.Cursor):
if query.endswith(';') or query.endswith('/'): if query.endswith(';') or query.endswith('/'):
query = query[:-1] query = query[:-1]
query = smart_str(query, self.charset) % tuple(args) query = smart_str(query, self.charset) % tuple(args)
self._guess_input_sizes([params])
return Database.Cursor.execute(self, query, params) return Database.Cursor.execute(self, query, params)
def executemany(self, query, params=None): def executemany(self, query, params=None):
@ -484,6 +502,7 @@ class FormatStylePlaceholderCursor(Database.Cursor):
query = query[:-1] query = query[:-1]
query = smart_str(query, self.charset) % tuple(args) query = smart_str(query, self.charset) % tuple(args)
new_param_list = [self._format_params(i) for i in params] 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) return Database.Cursor.executemany(self, query, new_param_list)
def fetchone(self): def fetchone(self):

View File

@ -905,9 +905,13 @@ class NullBooleanField(Field):
return [oldforms.NullBooleanField] return [oldforms.NullBooleanField]
def formfield(self, **kwargs): def formfield(self, **kwargs):
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text} defaults = {
'form_class': forms.NullBooleanField,
'required': not self.blank,
'label': capfirst(self.verbose_name),
'help_text': self.help_text}
defaults.update(kwargs) defaults.update(kwargs)
return forms.NullBooleanField(**defaults) return super(NullBooleanField, self).formfield(**defaults)
class PhoneNumberField(IntegerField): class PhoneNumberField(IntegerField):
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):

View File

@ -277,7 +277,20 @@ class HttpResponse(object):
for key, value in self._headers.values()]) \ for key, value in self._headers.values()]) \
+ '\n\n' + self.content + '\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): def __setitem__(self, header, value):
header, value = self._convert_to_ascii(header, value)
self._headers[header.lower()] = (header, value) self._headers[header.lower()] = (header, value)
def __delitem__(self, header): def __delitem__(self, header):
@ -331,7 +344,7 @@ class HttpResponse(object):
chunk = self._iterator.next() chunk = self._iterator.next()
if isinstance(chunk, unicode): if isinstance(chunk, unicode):
chunk = chunk.encode(self._charset) chunk = chunk.encode(self._charset)
return chunk return str(chunk)
def close(self): def close(self):
if hasattr(self._container, 'close'): if hasattr(self._container, 'close'):

View File

@ -533,8 +533,8 @@ class BooleanField(Field):
"""Returns a Python boolean object.""" """Returns a Python boolean object."""
super(BooleanField, self).clean(value) super(BooleanField, self).clean(value)
# Explicitly check for the string 'False', which is what a hidden field # 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 # will submit for False. Because bool("True") == True, we don't need to
# handle that explicitly). # handle that explicitly.
if value == 'False': if value == 'False':
return False return False
return bool(value) return bool(value)

View File

@ -86,9 +86,8 @@ def form_for_model(model, form=BaseForm, fields=None,
determining the formfield for a given database field. It's a callable that determining the formfield for a given database field. It's a callable that
takes a database Field instance and returns a form Field instance. takes a database Field instance and returns a form Field instance.
""" """
warn("form_for_model is deprecated, use ModelForm instead.", warn("form_for_model is deprecated. Use ModelForm instead.",
PendingDeprecationWarning, PendingDeprecationWarning, stacklevel=3)
stacklevel=3)
opts = model._meta opts = model._meta
field_list = [] field_list = []
for f in opts.fields + opts.many_to_many: for f in opts.fields + opts.many_to_many:
@ -116,9 +115,8 @@ def form_for_instance(instance, form=BaseForm, fields=None,
takes a database Field instance, plus **kwargs, and returns a form Field takes a database Field instance, plus **kwargs, and returns a form Field
instance with the given kwargs (i.e. 'initial'). instance with the given kwargs (i.e. 'initial').
""" """
warn("form_for_instance is deprecated, use ModelForm instead.", warn("form_for_instance is deprecated. Use ModelForm instead.",
PendingDeprecationWarning, PendingDeprecationWarning, stacklevel=3)
stacklevel=3)
model = instance.__class__ model = instance.__class__
opts = model._meta opts = model._meta
field_list = [] field_list = []
@ -216,9 +214,8 @@ class ModelFormOptions(object):
self.exclude = getattr(options, 'exclude', None) self.exclude = getattr(options, 'exclude', None)
class ModelFormMetaclass(type): class ModelFormMetaclass(type):
def __new__(cls, name, bases, attrs): def __new__(cls, name, bases, attrs,
# TODO: no way to specify formfield_callback yet, do we need one, or formfield_callback=lambda f: f.formfield()):
# should it be a special case for the admin?
fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)] 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)) fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter))
@ -247,15 +244,16 @@ class ModelFormMetaclass(type):
# If a model is defined, extract form fields from it and add them to base_fields # If a model is defined, extract form fields from it and add them to base_fields
if attrs['_meta'].model is not None: if attrs['_meta'].model is not None:
# Don't allow a subclass to define a Meta model if a parent class has. # Don't allow a subclass to define a different Meta model than a
# Technically the right fields would be generated, but the save # parent class has. Technically the right fields would be generated,
# method will not deal with more than one model. # but the save method will not deal with more than one model.
for base in bases: for base in bases:
base_opts = getattr(base, '_meta', None) base_opts = getattr(base, '_meta', None)
base_model = getattr(base_opts, 'model', None) base_model = getattr(base_opts, 'model', None)
if base_model is not None: if base_model and base_model is not opts.model:
raise ImproperlyConfigured('%s defines more than one model.' % name) raise ImproperlyConfigured('%s defines a different model than its parent.' % name)
model_fields = fields_for_model(opts.model, opts.fields, opts.exclude) model_fields = fields_for_model(opts.model, opts.fields,
opts.exclude, formfield_callback)
# fields declared in base classes override fields from the model # fields declared in base classes override fields from the model
model_fields.update(declared_fields) model_fields.update(declared_fields)
attrs['base_fields'] = model_fields attrs['base_fields'] = model_fields
@ -264,10 +262,15 @@ class ModelFormMetaclass(type):
return type.__new__(cls, name, bases, attrs) return type.__new__(cls, name, bases, attrs)
class BaseModelForm(BaseForm): class BaseModelForm(BaseForm):
def __init__(self, instance, data=None, files=None, auto_id='id_%s', prefix=None, def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
initial=None, error_class=ErrorList, label_suffix=':'): initial=None, error_class=ErrorList, label_suffix=':', instance=None):
self.instance = instance
opts = self._meta opts = self._meta
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) object_data = model_to_dict(instance, opts.fields, opts.exclude)
# if initial was provided, it should override the values from instance # if initial was provided, it should override the values from instance
if initial is not None: if initial is not None:

View File

@ -43,7 +43,11 @@ def stringfilter(func):
def addslashes(value): 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("'", "\\'") return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'")
addslashes.is_safe = True addslashes.is_safe = True
addslashes = stringfilter(addslashes) addslashes = stringfilter(addslashes)
@ -54,6 +58,25 @@ def capfirst(value):
capfirst.is_safe=True capfirst.is_safe=True
capfirst = stringfilter(capfirst) 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): def fix_ampersands(value):
"""Replaces ampersands with ``&amp;`` entities.""" """Replaces ampersands with ``&amp;`` entities."""
from django.utils.html import fix_ampersands from django.utils.html import fix_ampersands
@ -231,7 +254,7 @@ urlize.is_safe=True
urlize.needs_autoescape = True urlize.needs_autoescape = True
urlize = stringfilter(urlize) urlize = stringfilter(urlize)
def urlizetrunc(value, limit): def urlizetrunc(value, limit, autoescape=None):
""" """
Converts URLs into clickable links, truncating URLs to the given character Converts URLs into clickable links, truncating URLs to the given character
limit, and adding 'rel=nofollow' attribute to discourage spamming. limit, and adding 'rel=nofollow' attribute to discourage spamming.
@ -239,8 +262,10 @@ def urlizetrunc(value, limit):
Argument: Length to truncate URLs to. Argument: Length to truncate URLs to.
""" """
from django.utils.html import urlize 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.is_safe = True
urlizetrunc.needs_autoescape = True
urlizetrunc = stringfilter(urlizetrunc) urlizetrunc = stringfilter(urlizetrunc)
def wordcount(value): def wordcount(value):
@ -766,6 +791,7 @@ register.filter(dictsort)
register.filter(dictsortreversed) register.filter(dictsortreversed)
register.filter(divisibleby) register.filter(divisibleby)
register.filter(escape) register.filter(escape)
register.filter(escapejs)
register.filter(filesizeformat) register.filter(filesizeformat)
register.filter(first) register.filter(first)
register.filter(fix_ampersands) register.filter(fix_ampersands)

View File

@ -814,7 +814,7 @@ def ssi(parser, token):
Outputs the contents of a given file into the page. Outputs the contents of a given file into the page.
Like a simple "include" tag, the ``ssi`` tag includes the contents 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:: in the current page::
{% ssi /home/html/ljworld.com/includes/right_generic.html %} {% ssi /home/html/ljworld.com/includes/right_generic.html %}

View File

@ -1,4 +1,4 @@
import sys, time import sys, time, os
from django.conf import settings from django.conf import settings
from django.db import connection, get_creation_module from django.db import connection, get_creation_module
from django.core import mail from django.core import mail
@ -106,8 +106,31 @@ def create_test_db(verbosity=1, autoclobber=False):
if verbosity >= 1: if verbosity >= 1:
print "Creating test database..." print "Creating test database..."
# If we're using SQLite, it's more convenient to test against an # 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": if settings.DATABASE_ENGINE == "sqlite3":
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:" TEST_DATABASE_NAME = ":memory:"
else: else:
suffix = { 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) creation_module.destroy_test_db(settings, connection, old_database_name, verbosity)
return 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: if verbosity >= 1:
print "Destroying test database..." print "Destroying test database..."
connection.close() connection.close()
TEST_DATABASE_NAME = settings.DATABASE_NAME TEST_DATABASE_NAME = settings.DATABASE_NAME
settings.DATABASE_NAME = old_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() cursor = connection.cursor()
_set_autocommit(connection) _set_autocommit(connection)
time.sleep(1) # To avoid "database is being accessed by other users" errors. time.sleep(1) # To avoid "database is being accessed by other users" errors.

View File

@ -1,4 +1,4 @@
"HTML utilities suitable for global use." """HTML utilities suitable for global use."""
import re import re
import string import string
@ -8,11 +8,11 @@ from django.utils.encoding import force_unicode
from django.utils.functional import allow_lazy from django.utils.functional import allow_lazy
from django.utils.http import urlquote from django.utils.http import urlquote
# Configuration for urlize() function # Configuration for urlize() function.
LEADING_PUNCTUATION = ['(', '<', '&lt;'] LEADING_PUNCTUATION = ['(', '<', '&lt;']
TRAILING_PUNCTUATION = ['.', ',', ')', '>', '\n', '&gt;'] 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;'] DOTS = ['&middot;', '*', '\xe2\x80\xa2', '&#149;', '&bull;', '&#8226;']
unencoded_ampersands_re = re.compile(r'&(?!(\w+|#\d+);)') 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 del x # Temporary variable
def escape(html): 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;')) return mark_safe(force_unicode(html).replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;').replace("'", '&#39;'))
escape = allow_lazy(escape, unicode) escape = allow_lazy(escape, unicode)
@ -42,7 +42,7 @@ def conditional_escape(html):
return escape(html) return escape(html)
def linebreaks(value, autoescape=False): 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 value = re.sub(r'\r\n|\r|\n', '\n', force_unicode(value)) # normalize newlines
paras = re.split('\n{2,}', value) paras = re.split('\n{2,}', value)
if autoescape: if autoescape:
@ -53,28 +53,28 @@ def linebreaks(value, autoescape=False):
linebreaks = allow_lazy(linebreaks, unicode) linebreaks = allow_lazy(linebreaks, unicode)
def strip_tags(value): 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)) return re.sub(r'<[^>]*?>', '', force_unicode(value))
strip_tags = allow_lazy(strip_tags) strip_tags = allow_lazy(strip_tags)
def strip_spaces_between_tags(value): 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)) return re.sub(r'>\s+<', '><', force_unicode(value))
strip_spaces_between_tags = allow_lazy(strip_spaces_between_tags, unicode) strip_spaces_between_tags = allow_lazy(strip_spaces_between_tags, unicode)
def strip_entities(value): 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)) return re.sub(r'&(?:\w+|#\d+);', '', force_unicode(value))
strip_entities = allow_lazy(strip_entities, unicode) strip_entities = allow_lazy(strip_entities, unicode)
def fix_ampersands(value): 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)) return unencoded_ampersands_re.sub('&amp;', force_unicode(value))
fix_ampersands = allow_lazy(fix_ampersands, unicode) fix_ampersands = allow_lazy(fix_ampersands, unicode)
def urlize(text, trim_url_limit=None, nofollow=False, autoescape=False): 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 Works on http://, https://, and www. links. Links can have trailing
punctuation (periods, commas, close-parens) and leading punctuation punctuation (periods, commas, close-parens) and leading punctuation

View File

@ -1,6 +1,6 @@
""" """
Utilities for providing backwards compatibility for the maxlength argument, 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 from warnings import warn
@ -15,17 +15,15 @@ def legacy_maxlength(max_length, maxlength):
""" """
Consolidates max_length and maxlength, providing backwards compatibilty Consolidates max_length and maxlength, providing backwards compatibilty
for the legacy "maxlength" argument. for the legacy "maxlength" argument.
If one of max_length or maxlength is given, then that value is returned. If one of max_length or maxlength is given, then that value is returned.
If both are given, a TypeError is raised. If both are given, a TypeError is raised. If maxlength is used at all, a
If maxlength is used at all, a deprecation warning is issued. deprecation warning is issued.
""" """
if maxlength is not None: if maxlength is not None:
warn("maxlength is deprecated, use max_length instead.", warn("maxlength is deprecated. Use max_length instead.", DeprecationWarning, stacklevel=3)
DeprecationWarning,
stacklevel=3)
if max_length is not None: if max_length is not None:
raise TypeError("field can not take both the max_length" raise TypeError("Field cannot take both the max_length argument and the legacy maxlength argument.")
" argument and the legacy maxlength argument.")
max_length = maxlength max_length = maxlength
return max_length return max_length
@ -34,6 +32,7 @@ def remove_maxlength(func):
A decorator to be used on a class's __init__ that provides backwards A decorator to be used on a class's __init__ that provides backwards
compatibilty for the legacy "maxlength" keyword argument, i.e. 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 It does this by changing the passed "maxlength" keyword argument
(if it exists) into a "max_length" 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 Metaclass for providing backwards compatibility support for the
"maxlength" keyword argument. "maxlength" keyword argument.
""" """
def __init__(cls, name, bases, attrs): def __init__(cls, name, bases, attrs):
super(LegacyMaxlength, cls).__init__(name, bases, attrs) super(LegacyMaxlength, cls).__init__(name, bases, attrs)
# Decorate the class's __init__ to remove any maxlength keyword. # Decorate the class's __init__ to remove any maxlength keyword.

View File

@ -1,9 +1,12 @@
import os
import re
import sys
from django.conf import settings from django.conf import settings
from django.template import Template, Context, TemplateDoesNotExist from django.template import Template, Context, TemplateDoesNotExist
from django.utils.html import escape from django.utils.html import escape
from django.http import HttpResponseServerError, HttpResponseNotFound from django.http import HttpResponseServerError, HttpResponseNotFound
from django.utils.encoding import smart_unicode from django.utils.encoding import smart_unicode
import os, re, sys
HIDDEN_SETTINGS = re.compile('SECRET|PASSWORD|PROFANITIES_LIST') HIDDEN_SETTINGS = re.compile('SECRET|PASSWORD|PROFANITIES_LIST')
@ -145,6 +148,7 @@ def technical_500_response(request, exc_type, exc_value, tb):
'sys_executable': sys.executable, 'sys_executable': sys.executable,
'sys_version_info': '%d.%d.%d' % sys.version_info[0:3], 'sys_version_info': '%d.%d.%d' % sys.version_info[0:3],
'django_version_info': get_version(), 'django_version_info': get_version(),
'sys_path' : sys.path,
'template_info': template_info, 'template_info': template_info,
'template_does_not_exist': template_does_not_exist, 'template_does_not_exist': template_does_not_exist,
'loader_debug_info': loader_debug_info, '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"> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en"> <html lang="en">
<head> <head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" /> <meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="robots" content="NONE,NOARCHIVE" /> <meta name="robots" content="NONE,NOARCHIVE">
<title>{{ exception_type }} at {{ request.path|escape }}</title> <title>{{ exception_type }} at {{ request.path|escape }}</title>
<style type="text/css"> <style type="text/css">
html * { padding:0; margin:0; } html * { padding:0; margin:0; }
@ -368,6 +372,10 @@ TECHNICAL_500_TEMPLATE = """
<th>Python Version:</th> <th>Python Version:</th>
<td>{{ sys_version_info }}</td> <td>{{ sys_version_info }}</td>
</tr> </tr>
<tr>
<th>Python Path:</th>
<td>{{ sys_path }}</td>
</tr>
</table> </table>
</div> </div>
{% if unicode_hint %} {% if unicode_hint %}
@ -460,10 +468,10 @@ TECHNICAL_500_TEMPLATE = """
{% endautoescape %} {% endautoescape %}
<form action="http://dpaste.com/" name="pasteform" id="pasteform" method="post"> <form action="http://dpaste.com/" name="pasteform" id="pasteform" method="post">
<div id="pastebinTraceback" class="pastebin"> <div id="pastebinTraceback" class="pastebin">
<input type="hidden" name="language" value="PythonConsole" /> <input type="hidden" name="language" value="PythonConsole">
<input type="hidden" name="title" value="{{ exception_type|escape }} at {{ request.path|escape }}" /> <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="source" value="Django Dpaste Agent">
<input type="hidden" name="poster" value="Django" /> <input type="hidden" name="poster" value="Django">
<textarea name="content" id="traceback_area" cols="140" rows="25"> <textarea name="content" id="traceback_area" cols="140" rows="25">
Environment: Environment:
@ -498,9 +506,9 @@ Traceback:
Exception Type: {{ exception_type|escape }} at {{ request.path|escape }} Exception Type: {{ exception_type|escape }} at {{ request.path|escape }}
Exception Value: {{ exception_value|escape }} Exception Value: {{ exception_value|escape }}
</textarea> </textarea>
<br/><br/> <br><br>
<input type="submit" value="Share this traceback on public Web site">
</div> </div>
<input type="submit" value="Send to DPaste">
</form> </form>
</div> </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"> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en"> <html lang="en">
<head> <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> <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"> <style type="text/css">
html * { padding:0; margin:0; } html * { padding:0; margin:0; }
body * { padding:10px 20px; } body * { padding:10px 20px; }

View File

@ -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'), if not was_modified_since(request.META.get('HTTP_IF_MODIFIED_SINCE'),
statobj[stat.ST_MTIME], statobj[stat.ST_SIZE]): statobj[stat.ST_MTIME], statobj[stat.ST_SIZE]):
return HttpResponseNotModified() return HttpResponseNotModified()
mimetype = mimetypes.guess_type(fullpath)[0] mimetype = mimetypes.guess_type(fullpath)[0] or 'application/octet-stream'
contents = open(fullpath, 'rb').read() contents = open(fullpath, 'rb').read()
response = HttpResponse(contents, mimetype=mimetype) response = HttpResponse(contents, mimetype=mimetype)
response["Last-Modified"] = http_date(statobj[stat.ST_MTIME]) response["Last-Modified"] = http_date(statobj[stat.ST_MTIME])
response["Content-Length"] = len(contents)
return response return response
DEFAULT_DIRECTORY_INDEX_TEMPLATE = """ DEFAULT_DIRECTORY_INDEX_TEMPLATE = """

View File

@ -47,7 +47,11 @@ contenttypes
============ ============
A light framework for hooking into "types" of content, where each installed 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 csrf
==== ====
@ -177,9 +181,13 @@ localflavor
=========== ===========
A collection of various Django snippets that are useful only for a particular 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. contains a ``USZipCodeField`` that you can use to validate U.S. zip codes.
See the `localflavor documentation`_.
.. _localflavor documentation: ../localflavor/
markup markup
====== ======
@ -252,12 +260,12 @@ See the `syndication documentation`_.
webdesign webdesign
========= =========
Helpers and utilties targeted primarily at web designers rather than Helpers and utilties targeted primarily at Web *designers* rather than
web developers. 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 Other add-ons
============= =============

View File

@ -247,8 +247,8 @@ Anonymous users
the ``django.contrib.auth.models.User`` interface, with these differences: the ``django.contrib.auth.models.User`` interface, with these differences:
* ``id`` is always ``None``. * ``id`` is always ``None``.
* ``is_staff`` and ``is_superuser`` are always False. * ``is_staff`` and ``is_superuser`` are always ``False``.
* ``is_active`` is always True. * ``is_active`` is always ``False``.
* ``groups`` and ``user_permissions`` are always empty. * ``groups`` and ``user_permissions`` are always empty.
* ``is_anonymous()`` returns ``True`` instead of ``False``. * ``is_anonymous()`` returns ``True`` instead of ``False``.
* ``is_authenticated()`` returns ``False`` instead of ``True``. * ``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 .. admonition:: Calling ``authenticate()`` first
When you're manually logging a user in, you *must* call 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 sets an attribute on the ``User`` noting which authentication
backend successfully authenticated that user (see the `backends backend successfully authenticated that user (see the `backends
documentation`_ for details), and this information is needed later documentation`_ for details), and this information is needed later

View File

@ -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 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. "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 4. In some cases, a ticket might get moved to the "Someday/Maybe" state.
enhancement request we are willing to consider if a good patch is This means the ticket is an enhancement request that we might consider
written. Such tickets are not high priority. 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 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 the patch. If the patch is complete, it'll be marked as "ready for

View File

@ -44,8 +44,8 @@ Our class looks something like this::
# ... (other possibly useful methods omitted) ... # ... (other possibly useful methods omitted) ...
This is just an ordinary Python class, with nothing Django-specific about it. 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`` We'd like to be able to do things like this in our models (we assume the
attribute on the model is an instance of ``Hand``):: ``hand`` attribute on the model is an instance of ``Hand``)::
example = MyModel.objects.get(pk=1) example = MyModel.objects.get(pk=1)
print example.hand.north 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 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. 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)`` ``get_db_prep_save(self, value)``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -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 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. it would be stored in the model tablespace ``tables`` by default.
The settings.py file supports two additional options to specify **New in the Django development version:** Use the ``DEFAULT_TABLESPACE`` and
default values for the db_tablespace options. This is useful for ``DEFAULT_INDEX_TABLESPACE`` settings to specify default values for the
setting a tablespace for the Django internal apps and other db_tablespace options. These are useful for setting a tablespace for the
contributed applications. These options are ``DEFAULT_TABLESPACE`` built-in Django apps and other applications whose code you cannot control.
and ``DEFAULT_INDEX_TABLESPACE``.
Django does not create the tablespaces for you. Please refer to `Oracle's Django does not create the tablespaces for you. Please refer to `Oracle's
documentation`_ for details on creating and managing tablespaces. documentation`_ for details on creating and managing tablespaces.

View File

@ -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``. 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 --format
~~~~~~~~ ~~~~~~~~

View File

@ -1,6 +1,13 @@
Generating forms for models 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 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`` 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 model, and you want to create a form that lets people submit comments. In this

View File

@ -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 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 database you're using; if you plan to manually create the tables, you
can simply grant Django ``SELECT``, ``INSERT``, ``UPDATE`` and can simply grant Django ``SELECT``, ``INSERT``, ``UPDATE`` and
``DELETE`` permissions. On some databases, Django will need to have ``DELETE`` permissions. On some databases, Django will need
``ALTER TABLE`` privileges during ``syncdb`` (in order to create ``ALTER TABLE`` privileges during ``syncdb`` but won't issue
foreign key constraints properly on databases which do not allow them ``ALTER TABLE`` statements on a table once ``syncdb`` has created it.
to be deferred), but will not issue ``ALTER TABLE`` statements on a
table once ``syncdb`` has finished setting it up.
If you will be using Django's `testing framework`_ with data fixtures, If you're using Django's `testing framework`_ to test database queries,
Django will need permission to create a temporary test database. Django will need permission to create a test database.
.. _PostgreSQL: http://www.postgresql.org/ .. _PostgreSQL: http://www.postgresql.org/
.. _MySQL: http://www.mysql.com/ .. _MySQL: http://www.mysql.com/

View File

@ -2,16 +2,22 @@
The "local flavor" add-ons The "local flavor" add-ons
========================== ==========================
Django comes with assorted pieces of code that are useful only for a particular Following its "batteries included" philosophy, Django comes with assorted
country or culture. These pieces of code are organized as a set of 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.
Inside that package, country- or culture-specific code is organized into
subpackages, named using `ISO 3166 country codes`_. subpackages, named using `ISO 3166 country codes`_.
.. _ISO 3166 country codes: http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm
Most of the ``localflavor`` add-ons are localized form components deriving from 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 newforms_ framework -- for example, a ``USStateField`` that knows how to
the relevant subpackage. For example, a form with a field for French telephone validate U.S. state abbreviations, and a ``FISocialSecurityNumber`` that knows
numbers is created like so:: 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 import newforms as forms
from django.contrib.localflavor import fr from django.contrib.localflavor import fr
@ -19,6 +25,9 @@ numbers is created like so::
class MyForm(forms.Form): class MyForm(forms.Form):
my_french_phone_no = fr.forms.FRPhoneNumberField() my_french_phone_no = fr.forms.FRPhoneNumberField()
Supported countries
===================
Countries currently supported by ``localflavor`` are: Countries currently supported by ``localflavor`` are:
* Argentina_ * Argentina_
@ -45,6 +54,19 @@ Countries currently supported by ``localflavor`` are:
* `United Kingdom`_ * `United Kingdom`_
* `United States of America`_ * `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)`_ .. _Argentina: `Argentina (django.contrib.localflavor.ar)`_
.. _Australia: `Australia (django.contrib.localflavor.au)`_ .. _Australia: `Australia (django.contrib.localflavor.au)`_
.. _Brazil: `Brazil (django.contrib.localflavor.br)`_ .. _Brazil: `Brazil (django.contrib.localflavor.br)`_
@ -68,29 +90,15 @@ Countries currently supported by ``localflavor`` are:
.. _Switzerland: `Switzerland (django.contrib.localflavor.ch)`_ .. _Switzerland: `Switzerland (django.contrib.localflavor.ch)`_
.. _United Kingdom: `United Kingdom (django.contrib.localflavor.uk)`_ .. _United Kingdom: `United Kingdom (django.contrib.localflavor.uk)`_
.. _United States of America: `United States of America (django.contrib.localflavor.us)`_ .. _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/ .. _newforms: ../newforms/
Adding flavors
==============
.. admonition:: Adding a Flavor 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
We'd love to add more of these to Django, so please create a ticket for Unicode objects (``u'mystring'``) for strings, rather than setting the encoding
anything that you've found useful. Please use unicode objects in the file. See any of the existing flavors for examples.
(``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``) Argentina (``django.contrib.localflavor.ar``)
============================================= =============================================
@ -108,7 +116,6 @@ ARProvinceSelect
A ``Select`` widget that uses a list of Argentina's provinces as its choices. A ``Select`` widget that uses a list of Argentina's provinces as its choices.
Australia (``django.contrib.localflavor.au``) Australia (``django.contrib.localflavor.au``)
============================================= =============================================
@ -129,7 +136,6 @@ AUStateSelect
A ``Select`` widget that uses a list of Australian states/territories as its A ``Select`` widget that uses a list of Australian states/territories as its
choices. choices.
Brazil (``django.contrib.localflavor.br``) Brazil (``django.contrib.localflavor.br``)
========================================== ==========================================
@ -151,7 +157,6 @@ BRStateSelect
A ``Select`` widget that uses a list of Brazilian states/territories as its A ``Select`` widget that uses a list of Brazilian states/territories as its
choices. choices.
Canada (``django.contrib.localflavor.ca``) Canada (``django.contrib.localflavor.ca``)
========================================== ==========================================
@ -187,7 +192,6 @@ CAProvinceSelect
A ``Select`` widget that uses a list of Canadian provinces and territories as A ``Select`` widget that uses a list of Canadian provinces and territories as
its choices. its choices.
Chile (``django.contrib.localflavor.cl``) Chile (``django.contrib.localflavor.cl``)
========================================= =========================================
@ -203,7 +207,6 @@ CLRegionSelect
A ``Select`` widget that uses a list of Chilean regions (Regiones) as its A ``Select`` widget that uses a list of Chilean regions (Regiones) as its
choices. choices.
Finland (``django.contrib.localflavor.fi``) Finland (``django.contrib.localflavor.fi``)
=========================================== ===========================================
@ -224,7 +227,6 @@ FIMunicipalitySelect
A ``Select`` widget that uses a list of Finnish municipalities as its A ``Select`` widget that uses a list of Finnish municipalities as its
choices. choices.
France (``django.contrib.localflavor.fr``) France (``django.contrib.localflavor.fr``)
========================================== ==========================================
@ -246,7 +248,6 @@ FRDepartmentSelect
A ``Select`` widget that uses a list of French departments as its choices. A ``Select`` widget that uses a list of French departments as its choices.
Germany (``django.contrib.localflavor.de``) Germany (``django.contrib.localflavor.de``)
=========================================== ===========================================
@ -270,7 +271,6 @@ DEStateSelect
A ``Select`` widget that uses a list of German states as its choices. A ``Select`` widget that uses a list of German states as its choices.
Holland (``django.contrib.localflavor.nl``) Holland (``django.contrib.localflavor.nl``)
=========================================== ===========================================
@ -296,7 +296,6 @@ NLProvinceSelect
A ``Select`` widget that uses a list of Dutch provinces as its list of A ``Select`` widget that uses a list of Dutch provinces as its list of
choices. choices.
Iceland (``django.contrib.localflavor.is_``) Iceland (``django.contrib.localflavor.is_``)
============================================ ============================================
@ -318,7 +317,6 @@ ISPostalCodeSelect
A ``Select`` widget that uses a list of Icelandic postal codes as its A ``Select`` widget that uses a list of Icelandic postal codes as its
choices. choices.
India (``django.contrib.localflavor.in_``) India (``django.contrib.localflavor.in_``)
========================================== ==========================================
@ -341,7 +339,6 @@ INStateSelect
A ``Select`` widget that uses a list of Indian states/territories as its A ``Select`` widget that uses a list of Indian states/territories as its
choices. choices.
Italy (``django.contrib.localflavor.it``) Italy (``django.contrib.localflavor.it``)
========================================= =========================================
@ -374,22 +371,20 @@ ITRegionSelect
A ``Select`` widget that uses a list of Italian regions as its choices. A ``Select`` widget that uses a list of Italian regions as its choices.
Japan (``django.contrib.localflavor.jp``) Japan (``django.contrib.localflavor.jp``)
========================================= =========================================
JPPostalCodeField JPPostalCodeField
----------------- -----------------
A form field that validates input as a Japanese postcode. A form field that validates input as a Japanese postcode. It accepts seven
It accepts seven digits, with or without a hyphen. digits, with or without a hyphen.
JPPrefectureSelect JPPrefectureSelect
------------------ ------------------
A ``Select`` widget that uses a list of Japanese prefectures as its choices. A ``Select`` widget that uses a list of Japanese prefectures as its choices.
Mexico (``django.contrib.localflavor.mx``) Mexico (``django.contrib.localflavor.mx``)
========================================== ==========================================
@ -398,7 +393,6 @@ MXStateSelect
A ``Select`` widget that uses a list of Mexican states as its choices. A ``Select`` widget that uses a list of Mexican states as its choices.
Norway (``django.contrib.localflavor.no``) Norway (``django.contrib.localflavor.no``)
========================================== ==========================================
@ -422,7 +416,6 @@ NOMunicipalitySelect
A ``Select`` widget that uses a list of Norwegian municipalities (fylker) as A ``Select`` widget that uses a list of Norwegian municipalities (fylker) as
its choices. its choices.
Peru (``django.contrib.localflavor.pe``) Peru (``django.contrib.localflavor.pe``)
======================================== ========================================
@ -436,14 +429,13 @@ PERUCField
---------- ----------
A form field that validates input as an RUC (Registro Unico de 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 PEDepartmentSelect
------------------ ------------------
A ``Select`` widget that uses a list of Peruvian Departments as its choices. A ``Select`` widget that uses a list of Peruvian Departments as its choices.
Poland (``django.contrib.localflavor.pl``) Poland (``django.contrib.localflavor.pl``)
========================================== ==========================================
@ -490,7 +482,6 @@ PLVoivodeshipSelect
A ``Select`` widget that uses a list of Polish voivodeships (administrative A ``Select`` widget that uses a list of Polish voivodeships (administrative
provinces) as its choices. provinces) as its choices.
Slovakia (``django.contrib.localflavor.sk``) Slovakia (``django.contrib.localflavor.sk``)
============================================ ============================================
@ -510,7 +501,6 @@ SKRegionSelect
A ``Select`` widget that uses a list of Slovak regions as its choices. A ``Select`` widget that uses a list of Slovak regions as its choices.
South Africa (``django.contrib.localflavor.za``) South Africa (``django.contrib.localflavor.za``)
================================================ ================================================
@ -527,7 +517,6 @@ 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. postcodes must have four digits.
Spain (``django.contrib.localflavor.es``) Spain (``django.contrib.localflavor.es``)
========================================= =========================================
@ -571,7 +560,6 @@ ESRegionSelect
A ``Select`` widget that uses a list of Spanish regions as its choices. A ``Select`` widget that uses a list of Spanish regions as its choices.
Switzerland (``django.contrib.localflavor.ch``) Switzerland (``django.contrib.localflavor.ch``)
=============================================== ===============================================
@ -600,7 +588,6 @@ CHStateSelect
A ``Select`` widget that uses a list of Swiss states as its choices. A ``Select`` widget that uses a list of Swiss states as its choices.
United Kingdom (``django.contrib.localflavor.uk``) 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 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. 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``) United States of America (``django.contrib.localflavor.us``)
============================================================ ============================================================
@ -644,11 +640,11 @@ for the given state.
USZipCodeField 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. XXXXX or XXXXX-XXXX.
USStateSelect 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. choices.

View File

@ -61,17 +61,18 @@ Adds a few conveniences for perfectionists:
settings. settings.
If ``APPEND_SLASH`` is ``True`` and the initial URL doesn't end with a slash, 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 and it is not found in the URLconf, then a new URL is formed by appending a
at the end. If this new URL is found in urlpatterns, then an HTTP-redirect is slash at the end. If this new URL is found in the URLconf, then Django
returned to this new URL; otherwise the initial URL is processed as usual. 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 For example, ``foo.com/bar`` will be redirected to ``foo.com/bar/`` if you
have a valid urlpattern for ``foo.com/bar``, and do have a valid urlpattern don't have a valid URL pattern for ``foo.com/bar`` but *do* have a valid
for ``foo.com/bar/``. pattern for ``foo.com/bar/``.
**New in Django development version:** The behaviour of ``APPEND_SLASH`` has **New in Django development version:** The behavior of ``APPEND_SLASH`` has
changed slightly in the development version (it didn't used to check to see changed slightly in the development version. It didn't used to check whether
if the pattern was matched in the URL patterns). the pattern was matched in the URLconf.
If ``PREPEND_WWW`` is ``True``, URLs that lack a leading "www." will be If ``PREPEND_WWW`` is ``True``, URLs that lack a leading "www." will be
redirected to the same URL with a leading "www." redirected to the same URL with a leading "www."

View File

@ -618,7 +618,7 @@ statement for this field.
**New in Django development version** **New in Django development version**
The name of the database tablespace to use for this field's index, if 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`` ``DEFAULT_INDEX_TABLESPACE`` setting, if set, or the ``db_tablespace``
of the model, if any. If the backend doesn't support tablespaces, this of the model, if any. If the backend doesn't support tablespaces, this
option is ignored. option is ignored.

View File

@ -24,12 +24,11 @@ For example::
... model = Article ... model = Article
# Creating a form to add an article. # Creating a form to add an article.
>>> article = Article() >>> form = ArticleForm()
>>> form = ArticleForm(article)
# Creating a form to change an existing article. # Creating a form to change an existing article.
>>> article = Article.objects.get(pk=1) >>> article = Article.objects.get(pk=1)
>>> form = ArticleForm(article) >>> form = ArticleForm(instance=article)
Field types Field types
----------- -----------
@ -166,18 +165,23 @@ we'll discuss in a moment.)::
The ``save()`` method The ``save()`` method
--------------------- ---------------------
Every form produced by ``ModelForm`` also has a ``save()`` method. This Every form produced by ``ModelForm`` also has a ``save()``
method creates and saves a database object from the data bound to the form. method. This method creates and saves a database object from the data
A subclass of ``ModelForm`` also requires a model instance as the first bound to the form. A subclass of ``ModelForm`` can accept an existing
arument to its constructor. For example:: 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. # Create a form instance from POST data.
>>> a = Article() >>> f = ArticleForm(request.POST)
>>> f = ArticleForm(a, request.POST)
# Save a new Article object from the form's data. # Save a new Article object from the form's data.
>>> new_article = f.save() >>> 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 Note that ``save()`` will raise a ``ValueError`` if the data in the form
doesn't validate -- i.e., ``if form.errors``. 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:: ``save_m2m()`` to save the many-to-many form data. For example::
# Create a form instance with POST data. # Create a form instance with POST data.
>>> a = Author() >>> f = AuthorForm(request.POST)
>>> f = AuthorForm(a, request.POST)
# Create, but don't save the new author instance. # Create, but don't save the new author instance.
>>> new_author = f.save(commit=False) >>> new_author = f.save(commit=False)
@ -277,7 +280,7 @@ model fields:
manually set any extra required fields:: manually set any extra required fields::
instance = Instance(required_field='value') instance = Instance(required_field='value')
form = InstanceForm(instance, request.POST) form = InstanceForm(request.POST, instance=instance)
new_instance = form.save() new_instance = form.save()
instance = form.save(commit=False) 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 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 want that to be represented as a ``DateField`` in your form. But
``ModelForm`` gives you the flexibility of changing the form field type ``ModelForm`` gives you the flexibility of changing the form field type
for a given model field. You do this by declaratively specifying fields like for a given model field. You do this by declaratively specifying fields like

View File

@ -766,7 +766,7 @@ label of each required field::
<form method="post" action=""> <form method="post" action="">
<dl> <dl>
{% for field in form %} {% 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> <dd>{{ field }}</dd>
{% if field.help_text %}<dd>{{ field.help_text }}</dd>{% endif %} {% if field.help_text %}<dd>{{ field.help_text }}</dd>{% endif %}
{% if field.errors %}<dd class="myerrors">{{ field.errors }}</dd>{% endif %} {% if field.errors %}<dd class="myerrors">{{ field.errors }}</dd>{% endif %}

View File

@ -156,6 +156,18 @@ Methods
Returns ``True`` or ``False``, designating whether ``request.GET`` or Returns ``True`` or ``False``, designating whether ``request.GET`` or
``request.POST`` has the given key. ``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()`` ``get_full_path()``
Returns the ``path``, plus an appended query string, if applicable. 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`` ``HttpResponseNotModified``
The constructor doesn't take any arguments. Use this to designate that a 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`` ``HttpResponseBadRequest``
**New in Django development version.** **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 you must create a ``404.html`` template in the root of your
template directory. The default 404 view will use that template template directory. The default 404 view will use that template
for all 404 errors. The default 404 view will pass one variable for all 404 errors. The default 404 view will pass one variable
to the template: ``request_path``, which is the URL which to the template: ``request_path``, which is the URL that resulted
resulted in the 404. 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. view will never be used, and the traceback will be displayed instead.
The 500 (server error) view The 500 (server error) view

View File

@ -99,7 +99,7 @@ It implements the following standard dictionary methods:
* ``items()`` * ``items()``
* ``setdefault()`` * ``setdefault()`` (**New in Django development version**)
It also has these three methods: It also has these three methods:

View File

@ -417,17 +417,21 @@ site manager(s).
DEFAULT_TABLESPACE DEFAULT_TABLESPACE
------------------ ------------------
**New in Django development version**
Default: ``''`` (Empty string) 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. backend supports it.
DEFAULT_INDEX_TABLESPACE DEFAULT_INDEX_TABLESPACE
------------------------ ------------------------
**New in Django development version**
Default: ``''`` (Empty string) 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. one, if the backend supports it.
DISALLOWED_USER_AGENTS DISALLOWED_USER_AGENTS
@ -977,8 +981,13 @@ TEST_DATABASE_NAME
Default: ``None`` Default: ``None``
The name of database to use when running the test suite. If a value of The name of database to use when running the test suite.
``None`` is specified, the test database will use the name ``'test_' + settings.DATABASE_NAME``. See `Testing Django Applications`_.
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/ .. _Testing Django Applications: ../testing/

View File

@ -234,6 +234,7 @@ request to the URL ``/rss/beats/0613/``:
``get_object()`` method, passing it the bits. In this case, bits is ``get_object()`` method, passing it the bits. In this case, bits is
``['0613']``. For a request to ``/rss/beats/0613/foo/bar/``, bits would ``['0613']``. For a request to ``/rss/beats/0613/foo/bar/``, bits would
be ``['0613', 'foo', 'bar']``. be ``['0613', 'foo', 'bar']``.
* ``get_object()`` is responsible for retrieving the given beat, from the * ``get_object()`` is responsible for retrieving the given beat, from the
given ``bits``. In this case, it uses the Django database API to retrieve given ``bits``. In this case, it uses the Django database API to retrieve
the beat. Note that ``get_object()`` should raise 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 raises ``Beat.DoesNotExist`` on failure, and ``Beat.DoesNotExist`` is a
subclass of ``ObjectDoesNotExist``. Raising ``ObjectDoesNotExist`` in subclass of ``ObjectDoesNotExist``. Raising ``ObjectDoesNotExist`` in
``get_object()`` tells Django to produce a 404 error for that request. ``get_object()`` tells Django to produce a 404 error for that request.
* To generate the feed's ``<title>``, ``<link>`` and ``<description>``, * To generate the feed's ``<title>``, ``<link>`` and ``<description>``,
Django uses the ``title()``, ``link()`` and ``description()`` methods. In Django uses the ``title()``, ``link()`` and ``description()`` methods. In
the previous example, they were simple string class attributes, but this 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`` Inside the ``link()`` method, we handle the possibility that ``obj``
might be ``None``, which can occur when the URL isn't fully specified. In 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 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 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 (The ``link()`` method is called very early in the feed generation
process, so is a good place to bail out early). process, so it's a good place to bail out early.)
* Finally, note that ``items()`` in this example also takes the ``obj`` * Finally, note that ``items()`` in this example also takes the ``obj``
argument. The algorithm for ``items`` is the same as described in the argument. The algorithm for ``items`` is the same as described in the

View File

@ -565,17 +565,17 @@ autoescape
**New in Django development version** **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 ``off`` as an argument and that determines whether auto-escaping is in effect
inside the block. inside the block.
When auto-escaping is in effect, all variable content has HTML escaping applied 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 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 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 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 escaping, either by the code that populated the variable, or because it has had
the ``safe`` or ``escape`` filters applied. the ``safe`` or ``escape`` filters applied.
block block
@ -1227,8 +1227,10 @@ Adds the arg to the value.
addslashes 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 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 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. 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 filesizeformat
~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~
@ -1324,36 +1335,36 @@ floatformat
When used without an argument, rounds a floating-point number to one decimal 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: place -- but only if there's a decimal part to be displayed. For example:
======== ======================= ====== ============ =========================== ========
value Template Output ``value`` Template Output
======== ======================= ====== ============ =========================== ========
34.23234 {{ value|floatformat }} 34.2 ``34.23234`` ``{{ value|floatformat }}`` ``34.2``
34.00000 {{ value|floatformat }} 34 ``34.00000`` ``{{ value|floatformat }}`` ``34``
34.26000 {{ value|floatformat }} 34.3 ``34.26000`` ``{{ value|floatformat }}`` ``34.3``
======== ======================= ====== ============ =========================== ========
If used with a numeric integer argument, ``floatformat`` rounds a number to If used with a numeric integer argument, ``floatformat`` rounds a number to
that many decimal places. For example: that many decimal places. For example:
======== ========================= ====== ============ ============================= ==========
value Template Output ``value`` Template Output
======== ========================= ====== ============ ============================= ==========
34.23234 {{ value|floatformat:3 }} 34.232 ``34.23234`` ``{{ value|floatformat:3 }}`` ``34.232``
34.00000 {{ value|floatformat:3 }} 34.000 ``34.00000`` ``{{ value|floatformat:3 }}`` ``34.000``
34.26000 {{ value|floatformat:3 }} 34.260 ``34.26000`` ``{{ value|floatformat:3 }}`` ``34.260``
======== ========================= ====== ============ ============================= ==========
If the argument passed to ``floatformat`` is negative, it will round a number 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 to that many decimal places -- but only if there's a decimal part to be
displayed. For example: displayed. For example:
======== ============================ ====== ============ ================================ ==========
value Template Output ``value`` Template Output
======== ============================ ====== ============ ================================ ==========
34.23234 {{ value|floatformat:"-3" }} 34.232 ``34.23234`` ``{{ value|floatformat:"-3" }}`` ``34.232``
34.00000 {{ value|floatformat:"-3" }} 34 ``34.00000`` ``{{ value|floatformat:"-3" }}`` ``34``
34.26000 {{ value|floatformat:"-3" }} 34.260 ``34.26000`` ``{{ value|floatformat:"-3" }}`` ``34.260``
======== ============================ ====== ============ ================================ ==========
Using ``floatformat`` with no argument is equivalent to using ``floatformat`` Using ``floatformat`` with no argument is equivalent to using ``floatformat``
with an argument of ``-1``. with an argument of ``-1``.

View File

@ -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()`` won't cause an ``AttributeError`` (because integers don't have ``lower()``
methods). methods).
Registering a custom filters Registering custom filters
~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~
Once you've written your filter definition, you need to register it with 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:: your ``Library`` instance, to make it available to Django's template language::

View File

@ -270,27 +270,21 @@ a test case, add the name of the test method to the label::
$ ./manage.py test animals.AnimalTestCase.testFluffyAnimals $ ./manage.py test animals.AnimalTestCase.testFluffyAnimals
Understanding the test output The test database
----------------------------- -----------------
When you run your tests, you'll see a number of messages as the test runner Tests that require a database (namely, model tests) will not use
prepares itself:: your "real" (production) database. A separate, blank database is created
for the tests.
Creating test database... Regardless of whether the tests pass or fail, the test database is destroyed
Creating table myapp_animal when all the tests have been executed.
Creating table myapp_mineral
Loading 'initial_data' fixtures...
No fixtures found.
This tells you that the test runner is creating a test database -- a blank, By default this test database gets its name by prepending ``test_`` to the
from-scratch database that it will use for any tests that happen to require a value of the ``DATABASE_NAME`` setting. When using the SQLite database engine
database (namely, model tests). 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
Don't worry -- the test runner will not touch your "real" (production) different database name, specify the ``TEST_DATABASE_NAME`` setting.
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.
Aside from using a separate database, the test runner will otherwise use all of 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``, 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/ .. _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. Once the test database has been created, Django will run your tests.
If everything goes well, you'll see something like this:: 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 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. 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 Testing tools
============= =============

View File

@ -132,4 +132,8 @@ __test__ = {'API_TESTS':"""
>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()] >>> [(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)] [(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>]
"""} """}

View File

@ -143,7 +143,7 @@ familiar with the mechanics.
... model = Article ... model = Article
Traceback (most recent call last): 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 ArticleForm(ModelForm):
... class Meta: ... class Meta:
@ -155,6 +155,12 @@ Traceback (most recent call last):
... ...
ImproperlyConfigured: BadForm's base classes define more than one model. 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 ####################################################### # Old form_for_x tests #######################################################
@ -167,7 +173,7 @@ ImproperlyConfigured: BadForm's base classes define more than one model.
>>> class CategoryForm(ModelForm): >>> class CategoryForm(ModelForm):
... class Meta: ... class Meta:
... model = Category ... model = Category
>>> f = CategoryForm(Category()) >>> f = CategoryForm()
>>> print f >>> 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_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> <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'] >>> print f['name']
<input id="id_name" type="text" name="name" maxlength="20" /> <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() >>> print f.as_ul()
<li>Name: <input type="text" name="name" maxlength="20" /></li> <li>Name: <input type="text" name="name" maxlength="20" /></li>
<li>Slug: <input type="text" name="slug" maxlength="20" /></li> <li>Slug: <input type="text" name="slug" maxlength="20" /></li>
<li>The URL: <input type="text" name="url" maxlength="40" /></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() >>> f.is_valid()
True True
>>> f.cleaned_data >>> f.cleaned_data
@ -196,7 +202,7 @@ True
>>> Category.objects.all() >>> Category.objects.all()
[<Category: Entertainment>] [<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() >>> f.is_valid()
True True
>>> f.cleaned_data >>> f.cleaned_data
@ -210,7 +216,7 @@ True
If you call save() with commit=False, then it will return an object that 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 hasn't yet been saved to the database. In this case, it's up to you to call
save() on the resulting model instance. 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() >>> f.is_valid()
True True
>>> f.cleaned_data >>> f.cleaned_data
@ -225,7 +231,7 @@ True
[<Category: Entertainment>, <Category: It's a test>, <Category: Third test>] [<Category: Entertainment>, <Category: It's a test>, <Category: Third test>]
If you call save() with invalid data, you'll get a ValueError. 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 >>> f.errors
{'name': [u'This field is required.'], 'slug': [u'This field is required.']} {'name': [u'This field is required.'], 'slug': [u'This field is required.']}
>>> f.cleaned_data >>> f.cleaned_data
@ -236,7 +242,7 @@ AttributeError: 'CategoryForm' object has no attribute 'cleaned_data'
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValueError: The Category could not be created because the data didn't validate. 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() >>> f.save()
Traceback (most recent call last): Traceback (most recent call last):
... ...
@ -253,7 +259,7 @@ fields with the 'choices' attribute are represented by a ChoiceField.
>>> class ArticleForm(ModelForm): >>> class ArticleForm(ModelForm):
... class Meta: ... class Meta:
... model = Article ... model = Article
>>> f = ArticleForm(Article(), auto_id=False) >>> f = ArticleForm(auto_id=False)
>>> print f >>> print f
<tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr> <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> <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: ... class Meta:
... model = Article ... model = Article
... fields = ('headline','pub_date') ... fields = ('headline','pub_date')
>>> f = PartialArticleForm(Article(), auto_id=False) >>> f = PartialArticleForm(auto_id=False)
>>> print f >>> print f
<tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr> <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> <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 RoykoForm(ModelForm):
... class Meta: ... class Meta:
... model = Writer ... model = Writer
>>> f = RoykoForm(w, auto_id=False) >>> f = RoykoForm(auto_id=False, instance=w)
>>> print f >>> 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> <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 TestArticleForm(ModelForm):
... class Meta: ... class Meta:
... model = Article ... model = Article
>>> f = TestArticleForm(art, auto_id=False) >>> f = TestArticleForm(auto_id=False, instance=art)
>>> print f.as_ul() >>> print f.as_ul()
<li>Headline: <input type="text" name="headline" value="Test article" maxlength="50" /></li> <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> <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="2">It&#39;s a test</option>
<option value="3">Third test</option> <option value="3">Third test</option>
</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li> </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() >>> f.is_valid()
True True
>>> test_art = f.save() >>> test_art = f.save()
@ -347,7 +353,7 @@ by specifying a 'fields' argument to form_for_instance.
... class Meta: ... class Meta:
... model = Article ... model = Article
... fields=('headline', 'slug', 'pub_date') ... 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() >>> print f.as_ul()
<li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li> <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> <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 TestArticleForm(ModelForm):
... class Meta: ... class Meta:
... model = Article ... model = Article
>>> f = TestArticleForm(new_art, auto_id=False) >>> f = TestArticleForm(auto_id=False, instance=new_art)
>>> print f.as_ul() >>> print f.as_ul()
<li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li> <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> <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> <option value="3">Third test</option>
</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li> </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', >>> 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']}) ... 'writer': u'1', 'article': u'Hello.', 'categories': [u'1', u'2']}, instance=new_art)
>>> new_art = f.save() >>> new_art = f.save()
>>> new_art.id >>> new_art.id
1 1
@ -403,8 +409,8 @@ Add some categories and test the many-to-many form output.
[<Category: Entertainment>, <Category: It's a test>] [<Category: Entertainment>, <Category: It's a test>]
Now, submit form data with no categories. This deletes the existing categories. 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', >>> f = TestArticleForm({'headline': u'New headline', 'slug': u'new-headline', 'pub_date': u'1988-01-04',
... 'writer': u'1', 'article': u'Hello.'}) ... 'writer': u'1', 'article': u'Hello.'}, instance=new_art)
>>> new_art = f.save() >>> new_art = f.save()
>>> new_art.id >>> new_art.id
1 1
@ -416,7 +422,7 @@ Create a new article, with categories, via the form.
>>> class ArticleForm(ModelForm): >>> class ArticleForm(ModelForm):
... class Meta: ... class Meta:
... model = Article ... 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']}) ... 'writer': u'1', 'article': u'Test.', 'categories': [u'1', u'2']})
>>> new_art = f.save() >>> new_art = f.save()
>>> new_art.id >>> new_art.id
@ -429,7 +435,7 @@ Create a new article, with no categories, via the form.
>>> class ArticleForm(ModelForm): >>> class ArticleForm(ModelForm):
... class Meta: ... class Meta:
... model = Article ... 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.'}) ... 'writer': u'1', 'article': u'Test.'})
>>> new_art = f.save() >>> new_art = f.save()
>>> new_art.id >>> 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 ArticleForm(ModelForm):
... class Meta: ... class Meta:
... model = Article ... 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']}) ... 'writer': u'1', 'article': u'Test.', 'categories': [u'1', u'2']})
>>> new_art = f.save(commit=False) >>> new_art = f.save(commit=False)
@ -474,7 +480,7 @@ existing Category instance.
<Category: Third test> <Category: Third test>
>>> cat.id >>> cat.id
3 3
>>> form = ShortCategory(cat, {'name': 'Third', 'slug': 'third', 'url': '3rd'}) >>> form = ShortCategory({'name': 'Third', 'slug': 'third', 'url': '3rd'}, instance=cat)
>>> form.save() >>> form.save()
<Category: Third> <Category: Third>
>>> Category.objects.get(id=3) >>> Category.objects.get(id=3)
@ -486,7 +492,7 @@ the data in the database when the form is instantiated.
>>> class ArticleForm(ModelForm): >>> class ArticleForm(ModelForm):
... class Meta: ... class Meta:
... model = Article ... model = Article
>>> f = ArticleForm(Article(), auto_id=False) >>> f = ArticleForm(auto_id=False)
>>> print f.as_ul() >>> print f.as_ul()
<li>Headline: <input type="text" name="headline" maxlength="50" /></li> <li>Headline: <input type="text" name="headline" maxlength="50" /></li>
<li>Slug: <input type="text" name="slug" 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 PhoneNumberForm(ModelForm):
... class Meta: ... class Meta:
... model = PhoneNumber ... model = PhoneNumber
>>> f = PhoneNumberForm(PhoneNumber(), {'phone': '(312) 555-1212', 'description': 'Assistance'}) >>> f = PhoneNumberForm({'phone': '(312) 555-1212', 'description': 'Assistance'})
>>> f.is_valid() >>> f.is_valid()
True True
>>> f.cleaned_data >>> f.cleaned_data

View File

@ -218,3 +218,41 @@ None
3.4 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

View File

@ -3,8 +3,8 @@
# Unit tests for cache framework # Unit tests for cache framework
# Uses whatever cache backend is set in the test settings file. # 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.core.cache import cache
from django.utils.cache import patch_vary_headers from django.utils.cache import patch_vary_headers
from django.http import HttpResponse from django.http import HttpResponse
@ -76,10 +76,16 @@ class Cache(unittest.TestCase):
self.assertEqual(cache.get("stuff"), stuff) self.assertEqual(cache.get("stuff"), stuff)
def test_expiration(self): def test_expiration(self):
# expiration cache.set('expire1', 'very quickly', 1)
cache.set('expire', 'very quickly', 1) cache.set('expire2', 'very quickly', 1)
cache.set('expire3', 'very quickly', 1)
time.sleep(2) time.sleep(2)
self.assertEqual(cache.get("expire"), None) 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): def test_unicode(self):
stuff = { stuff = {
@ -92,6 +98,44 @@ class Cache(unittest.TestCase):
cache.set(key, value) cache.set(key, value)
self.assertEqual(cache.get(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): class CacheUtils(unittest.TestCase):
"""TestCase for django.utils.cache functions.""" """TestCase for django.utils.cache functions."""

View File

@ -49,6 +49,18 @@ u'\\\\ : backslashes, too'
>>> capfirst(u'hello world') >>> capfirst(u'hello world')
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') >>> fix_ampersands(u'Jack & Jill & Jeroboam')
u'Jack &amp; Jill &amp; Jeroboam' u'Jack &amp; Jill &amp; Jeroboam'

View File

@ -41,7 +41,7 @@ Strict RUT usage (does not allow imposible values)
>>> rut.clean('11-6') >>> rut.clean('11-6')
Traceback (most recent call last): 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. # valid format, bad verifier.
>>> rut.clean('11.111.111-0') >>> rut.clean('11.111.111-0')
@ -53,17 +53,17 @@ ValidationError: [u'The Chilean RUT is not valid.']
>>> rut.clean('767484100') >>> rut.clean('767484100')
Traceback (most recent call last): 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') >>> rut.clean('78.412.790-7')
u'78.412.790-7' u'78.412.790-7'
>>> rut.clean('8.334.6043') >>> rut.clean('8.334.6043')
Traceback (most recent call last): 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') >>> rut.clean('76793310-K')
Traceback (most recent call last): 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 ######################################################### ## CLRegionSelect #########################################################
>>> from django.contrib.localflavor.cl.forms import CLRegionSelect >>> from django.contrib.localflavor.cl.forms import CLRegionSelect

View File

@ -12,13 +12,15 @@ u'BT32 4PX'
>>> f.clean('GIR 0AA') >>> f.clean('GIR 0AA')
u'GIR 0AA' u'GIR 0AA'
>>> f.clean('BT324PX') >>> f.clean('BT324PX')
Traceback (most recent call last): u'BT32 4PX'
...
ValidationError: [u'Enter a postcode. A space is required between the two postcode parts.']
>>> f.clean('1NV 4L1D') >>> f.clean('1NV 4L1D')
Traceback (most recent call last): 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('1NV4L1D')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid postcode.']
>>> f.clean(None) >>> f.clean(None)
Traceback (most recent call last): Traceback (most recent call last):
... ...
@ -27,7 +29,20 @@ ValidationError: [u'This field is required.']
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValidationError: [u'This field is required.'] 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 = UKPostcodeField(required=False)
>>> f.clean('BT32 4PX') >>> f.clean('BT32 4PX')
u'BT32 4PX' u'BT32 4PX'
@ -36,11 +51,9 @@ u'GIR 0AA'
>>> f.clean('1NV 4L1D') >>> f.clean('1NV 4L1D')
Traceback (most recent call last): 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') >>> f.clean('BT324PX')
Traceback (most recent call last): u'BT32 4PX'
...
ValidationError: [u'Enter a postcode. A space is required between the two postcode parts.']
>>> f.clean(None) >>> f.clean(None)
u'' u''
>>> f.clean('') >>> f.clean('')

View File

@ -391,9 +391,45 @@ u'\ufffd'
>>> q.getlist('foo') >>> q.getlist('foo')
[u'bar', u'\ufffd'] [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__": if __name__ == "__main__":
import doctest import doctest

View File

@ -22,12 +22,12 @@ Don't print out the deprecation warnings during testing.
>>> legacy_maxlength(10, 12) >>> legacy_maxlength(10, 12)
Traceback (most recent call last): 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) >>> legacy_maxlength(0, 10)
Traceback (most recent call last): 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) >>> legacy_maxlength(0, None)
0 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) >>> fields.Field(maxlength=10, max_length=15)
Traceback (most recent call last): 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 # Test max_length
>>> new.max_length >>> new.max_length

View File

@ -11,6 +11,7 @@ class Article(models.Model):
pub_date = models.DateTimeField() pub_date = models.DateTimeField()
status = models.IntegerField(blank=True, null=True, choices=CHOICES) status = models.IntegerField(blank=True, null=True, choices=CHOICES)
misc_data = models.CharField(max_length=100, blank=True) misc_data = models.CharField(max_length=100, blank=True)
article_text = models.TextField()
class Meta: class Meta:
ordering = ('pub_date','headline') ordering = ('pub_date','headline')
@ -41,5 +42,14 @@ Empty strings should be returned as Unicode
>>> a2 = Article.objects.get(pk=a.id) >>> a2 = Article.objects.get(pk=a.id)
>>> a2.misc_data >>> a2.misc_data
u'' 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
""" """
} }

View File

@ -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-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-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-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": "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": '"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-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"), 'filter-wordcount02': ('{{ a|wordcount }} {{ b|wordcount }}', {"a": "a & b", "b": mark_safe("a &amp; b")}, "3 3"),

View File

@ -0,0 +1 @@
An unknown file extension.

View File

@ -13,6 +13,11 @@ class StaticTests(TestCase):
response = self.client.get('/views/site_media/%s' % filename) response = self.client.get('/views/site_media/%s' % filename)
file = open(path.join(media_dir, filename)) file = open(path.join(media_dir, filename))
self.assertEquals(file.read(), response.content) 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): def test_copes_with_empty_path_component(self):
file_name = 'file.txt' file_name = 'file.txt'
@ -20,4 +25,3 @@ class StaticTests(TestCase):
file = open(path.join(media_dir, file_name)) file = open(path.join(media_dir, file_name))
self.assertEquals(file.read(), response.content) self.assertEquals(file.read(), response.content)

View File

@ -93,6 +93,7 @@ def django_tests(verbosity, interactive, test_labels):
old_root_urlconf = settings.ROOT_URLCONF old_root_urlconf = settings.ROOT_URLCONF
old_template_dirs = settings.TEMPLATE_DIRS old_template_dirs = settings.TEMPLATE_DIRS
old_use_i18n = settings.USE_I18N old_use_i18n = settings.USE_I18N
old_login_url = settings.LOGIN_URL
old_language_code = settings.LANGUAGE_CODE old_language_code = settings.LANGUAGE_CODE
old_middleware_classes = settings.MIDDLEWARE_CLASSES 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.TEMPLATE_DIRS = (os.path.join(os.path.dirname(__file__), TEST_TEMPLATE_DIR),)
settings.USE_I18N = True settings.USE_I18N = True
settings.LANGUAGE_CODE = 'en' settings.LANGUAGE_CODE = 'en'
settings.LOGIN_URL = '/accounts/login/'
settings.MIDDLEWARE_CLASSES = ( settings.MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
@ -154,6 +156,7 @@ def django_tests(verbosity, interactive, test_labels):
settings.TEMPLATE_DIRS = old_template_dirs settings.TEMPLATE_DIRS = old_template_dirs
settings.USE_I18N = old_use_i18n settings.USE_I18N = old_use_i18n
settings.LANGUAGE_CODE = old_language_code settings.LANGUAGE_CODE = old_language_code
settings.LOGIN_URL = old_login_url
settings.MIDDLEWARE_CLASSES = old_middleware_classes settings.MIDDLEWARE_CLASSES = old_middleware_classes
if __name__ == "__main__": if __name__ == "__main__":