diff --git a/AUTHORS b/AUTHORS index 79ac9e1b42..10a03b920c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -131,6 +131,7 @@ answer newbie questions, and generally made Django that much better: Matthew Flanagan Eric Floehr Vincent Foley + Rudolph Froger Jorge Gajon gandalf@owca.info Marc Garcia @@ -306,6 +307,7 @@ answer newbie questions, and generally made Django that much better: Georgi Stanojevski Vasiliy Stavenko Thomas Steinacher + Johan C. Stöver nowell strite Thomas Stromberg Sundance @@ -333,6 +335,7 @@ answer newbie questions, and generally made Django that much better: tt@gurgle.no Amit Upadhyay Geert Vanderkelen + I.S. van Oostveen viestards.lists@gmail.com George Vilches Vlado diff --git a/django/conf/__init__.py b/django/conf/__init__.py index c24e87e6ed..61d9ff7536 100644 --- a/django/conf/__init__.py +++ b/django/conf/__init__.py @@ -52,7 +52,9 @@ class LazySettings(object): if not settings_module: # If it's set but is an empty string. raise KeyError except KeyError: - raise ImportError, "Environment variable %s is undefined so settings cannot be imported." % ENVIRONMENT_VARIABLE # NOTE: This is arguably an EnvironmentError, but that causes problems with Python's interactive help + # NOTE: This is arguably an EnvironmentError, but that causes + # problems with Python's interactive help. + raise ImportError("Settings cannot be imported, because environment variable %s is undefined." % ENVIRONMENT_VARIABLE) self._target = Settings(settings_module) diff --git a/django/conf/locale/ca/LC_MESSAGES/django.mo b/django/conf/locale/ca/LC_MESSAGES/django.mo index fdba04abd2..a39a4551bb 100644 Binary files a/django/conf/locale/ca/LC_MESSAGES/django.mo and b/django/conf/locale/ca/LC_MESSAGES/django.mo differ diff --git a/django/conf/locale/ca/LC_MESSAGES/django.po b/django/conf/locale/ca/LC_MESSAGES/django.po index 9f561acc5b..2929ea114e 100644 --- a/django/conf/locale/ca/LC_MESSAGES/django.po +++ b/django/conf/locale/ca/LC_MESSAGES/django.po @@ -5,9 +5,9 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2007-11-05 17:33+0100\n" -"PO-Revision-Date: 2007-11-05 17:34+0100\n" -"Last-Translator: Marc Garcia \n" +"POT-Creation-Date: 2007-12-02 22:26+0100\n" +"PO-Revision-Date: 2007-12-02 22:32+0100\n" +"Last-Translator: Marc Fargas \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -194,7 +194,7 @@ msgstr "Xinés simplificat" msgid "Traditional Chinese" msgstr "Xinés tradicional" -#: contrib/admin/filterspecs.py:42 +#: contrib/admin/filterspecs.py:44 #, python-format msgid "" "

By %s:

\n" @@ -203,71 +203,71 @@ msgstr "" "

Per %s:

\n" "
    \n" -#: contrib/admin/filterspecs.py:72 contrib/admin/filterspecs.py:90 -#: contrib/admin/filterspecs.py:145 contrib/admin/filterspecs.py:171 +#: contrib/admin/filterspecs.py:74 contrib/admin/filterspecs.py:92 +#: contrib/admin/filterspecs.py:147 contrib/admin/filterspecs.py:173 msgid "All" msgstr "Tots" -#: contrib/admin/filterspecs.py:111 +#: contrib/admin/filterspecs.py:113 msgid "Any date" msgstr "Qualsevol data" -#: contrib/admin/filterspecs.py:112 +#: contrib/admin/filterspecs.py:114 msgid "Today" msgstr "Avui" -#: contrib/admin/filterspecs.py:115 +#: contrib/admin/filterspecs.py:117 msgid "Past 7 days" msgstr "Últims 7 dies" -#: contrib/admin/filterspecs.py:117 +#: contrib/admin/filterspecs.py:119 msgid "This month" msgstr "Aquest mes" -#: contrib/admin/filterspecs.py:119 +#: contrib/admin/filterspecs.py:121 msgid "This year" msgstr "Aquest any" -#: contrib/admin/filterspecs.py:145 newforms/widgets.py:221 -#: oldforms/__init__.py:591 +#: contrib/admin/filterspecs.py:147 newforms/widgets.py:231 +#: oldforms/__init__.py:592 msgid "Yes" msgstr "Si" -#: contrib/admin/filterspecs.py:145 newforms/widgets.py:221 -#: oldforms/__init__.py:591 +#: contrib/admin/filterspecs.py:147 newforms/widgets.py:231 +#: oldforms/__init__.py:592 msgid "No" msgstr "No" -#: contrib/admin/filterspecs.py:152 newforms/widgets.py:221 -#: oldforms/__init__.py:591 +#: contrib/admin/filterspecs.py:154 newforms/widgets.py:231 +#: oldforms/__init__.py:592 msgid "Unknown" msgstr "Desconegut" -#: contrib/admin/models.py:17 +#: contrib/admin/models.py:18 msgid "action time" msgstr "moment de l'acció" -#: contrib/admin/models.py:20 +#: contrib/admin/models.py:21 msgid "object id" msgstr "id del objecte" -#: contrib/admin/models.py:21 +#: contrib/admin/models.py:22 msgid "object repr" msgstr "'repr' de l'objecte" -#: contrib/admin/models.py:22 +#: contrib/admin/models.py:23 msgid "action flag" msgstr "marca de l'acció" -#: contrib/admin/models.py:23 +#: contrib/admin/models.py:24 msgid "change message" msgstr "missatge del canvi" -#: contrib/admin/models.py:26 +#: contrib/admin/models.py:27 msgid "log entry" msgstr "entrada del registre" -#: contrib/admin/models.py:27 +#: contrib/admin/models.py:28 msgid "log entries" msgstr "entrades del registre" @@ -469,7 +469,7 @@ msgid "Password:" msgstr "Contrasenya:" #: contrib/admin/templates/admin/login.html:25 -#: contrib/admin/views/decorators.py:24 +#: contrib/admin/views/decorators.py:25 msgid "Log in" msgstr "Iniciar sessió" @@ -769,17 +769,17 @@ msgstr "Actualment:" msgid "Change:" msgstr "Modificar:" -#: contrib/admin/templatetags/admin_list.py:254 +#: contrib/admin/templatetags/admin_list.py:257 msgid "All dates" msgstr "Totes les dates" -#: contrib/admin/views/auth.py:20 contrib/admin/views/main.py:264 +#: contrib/admin/views/auth.py:20 contrib/admin/views/main.py:267 #, python-format msgid "The %(name)s \"%(obj)s\" was added successfully." msgstr "El/la %(name)s \"%(obj)s\".ha estat agregat/da amb èxit." -#: contrib/admin/views/auth.py:25 contrib/admin/views/main.py:268 -#: contrib/admin/views/main.py:354 +#: contrib/admin/views/auth.py:25 contrib/admin/views/main.py:271 +#: contrib/admin/views/main.py:356 msgid "You may edit it again below." msgstr "Pot editar-lo de nou abaix." @@ -796,7 +796,7 @@ msgstr "Canvi de clau exitós" msgid "Change password: %s" msgstr "Canviar clau: %s" -#: contrib/admin/views/decorators.py:10 contrib/auth/forms.py:60 +#: contrib/admin/views/decorators.py:11 contrib/auth/forms.py:60 msgid "" "Please enter a correct username and password. Note that both fields are case-" "sensitive." @@ -804,7 +804,7 @@ msgstr "" "Si us plau, introdueixi un nom d'usuari i contrasenya vàlids. Tingui en " "compte que tots dos camps son sensibles a majúscules i minúscules." -#: contrib/admin/views/decorators.py:62 +#: contrib/admin/views/decorators.py:63 msgid "" "Please log in again, because your session has expired. Don't worry: Your " "submission has been saved." @@ -812,7 +812,7 @@ msgstr "" "Si us plau, identifiquis de nou doncs la seva sessió ha expirat. No es " "preocupi, el seu enviament està emmagatzemat." -#: contrib/admin/views/decorators.py:69 +#: contrib/admin/views/decorators.py:70 msgid "" "Looks like your browser isn't configured to accept cookies. Please enable " "cookies, reload this page, and try again." @@ -821,247 +821,247 @@ msgstr "" "'cookies' (galetes). Si us plau, habiliti les 'cookies', recarregui aquesta " "pàgina i provi-ho de nou. " -#: contrib/admin/views/decorators.py:83 +#: contrib/admin/views/decorators.py:84 msgid "Usernames cannot contain the '@' character." msgstr "Els noms d'usuari no poden contenir el caracter '@'." -#: contrib/admin/views/decorators.py:85 +#: contrib/admin/views/decorators.py:86 #, python-format msgid "Your e-mail address is not your username. Try '%s' instead." msgstr "" "La seva adreça de correu no és el seu nom d'usuari. Provi '%s' en tot cas." -#: contrib/admin/views/doc.py:47 contrib/admin/views/doc.py:49 -#: contrib/admin/views/doc.py:51 +#: contrib/admin/views/doc.py:48 contrib/admin/views/doc.py:50 +#: contrib/admin/views/doc.py:52 msgid "tag:" msgstr "etiqueta:" -#: contrib/admin/views/doc.py:78 contrib/admin/views/doc.py:80 -#: contrib/admin/views/doc.py:82 +#: contrib/admin/views/doc.py:79 contrib/admin/views/doc.py:81 +#: contrib/admin/views/doc.py:83 msgid "filter:" msgstr "filtre:" -#: contrib/admin/views/doc.py:136 contrib/admin/views/doc.py:138 -#: contrib/admin/views/doc.py:140 +#: contrib/admin/views/doc.py:137 contrib/admin/views/doc.py:139 +#: contrib/admin/views/doc.py:141 msgid "view:" msgstr "vista:" -#: contrib/admin/views/doc.py:165 +#: contrib/admin/views/doc.py:166 #, python-format msgid "App %r not found" msgstr "La aplicació %r no s'ha pogut trobar" -#: contrib/admin/views/doc.py:172 +#: contrib/admin/views/doc.py:173 #, python-format msgid "Model %(name)r not found in app %(label)r" msgstr "El model %(name)r no s'ha trobat en la aplicació %(label)r" -#: contrib/admin/views/doc.py:184 +#: contrib/admin/views/doc.py:185 #, python-format msgid "the related `%(label)s.%(type)s` object" msgstr "el objecte relacionat `%(label)s.%(type)s`" -#: contrib/admin/views/doc.py:184 contrib/admin/views/doc.py:206 -#: contrib/admin/views/doc.py:220 contrib/admin/views/doc.py:225 +#: contrib/admin/views/doc.py:185 contrib/admin/views/doc.py:207 +#: contrib/admin/views/doc.py:221 contrib/admin/views/doc.py:226 msgid "model:" msgstr "model:" -#: contrib/admin/views/doc.py:215 +#: contrib/admin/views/doc.py:216 #, python-format msgid "related `%(label)s.%(name)s` objects" msgstr "objectes relacionats `%(label)s.%(name)s`" -#: contrib/admin/views/doc.py:220 +#: contrib/admin/views/doc.py:221 #, python-format msgid "all %s" msgstr "tots %s" -#: contrib/admin/views/doc.py:225 +#: contrib/admin/views/doc.py:226 #, python-format msgid "number of %s" msgstr "nombre de %s" -#: contrib/admin/views/doc.py:230 +#: contrib/admin/views/doc.py:231 #, python-format msgid "Fields on %s objects" msgstr "Camps en objectes %s" -#: contrib/admin/views/doc.py:292 contrib/admin/views/doc.py:303 -#: contrib/admin/views/doc.py:305 contrib/admin/views/doc.py:311 -#: contrib/admin/views/doc.py:312 contrib/admin/views/doc.py:314 +#: contrib/admin/views/doc.py:293 contrib/admin/views/doc.py:304 +#: contrib/admin/views/doc.py:306 contrib/admin/views/doc.py:312 +#: contrib/admin/views/doc.py:313 contrib/admin/views/doc.py:315 msgid "Integer" msgstr "Enter" -#: contrib/admin/views/doc.py:293 +#: contrib/admin/views/doc.py:294 msgid "Boolean (Either True or False)" msgstr "Booleà (Verdader o Fals)" -#: contrib/admin/views/doc.py:294 contrib/admin/views/doc.py:313 +#: contrib/admin/views/doc.py:295 contrib/admin/views/doc.py:314 #, python-format msgid "String (up to %(max_length)s)" msgstr "Cadena (de fins a %(max_length)s)" -#: contrib/admin/views/doc.py:295 +#: contrib/admin/views/doc.py:296 msgid "Comma-separated integers" msgstr "Enters separats per comes" -#: contrib/admin/views/doc.py:296 +#: contrib/admin/views/doc.py:297 msgid "Date (without time)" msgstr "Data (sense hora)" -#: contrib/admin/views/doc.py:297 +#: contrib/admin/views/doc.py:298 msgid "Date (with time)" msgstr "Data (amb hora)" -#: contrib/admin/views/doc.py:298 +#: contrib/admin/views/doc.py:299 msgid "Decimal number" msgstr "Número decimal" -#: contrib/admin/views/doc.py:299 +#: contrib/admin/views/doc.py:300 msgid "E-mail address" msgstr "Adreça de correu electrònic" -#: contrib/admin/views/doc.py:300 contrib/admin/views/doc.py:301 -#: contrib/admin/views/doc.py:304 +#: contrib/admin/views/doc.py:301 contrib/admin/views/doc.py:302 +#: contrib/admin/views/doc.py:305 msgid "File path" msgstr "Ruta del fitxer" -#: contrib/admin/views/doc.py:302 +#: contrib/admin/views/doc.py:303 msgid "Floating point number" msgstr "Número amb punt de coma flotant" -#: contrib/admin/views/doc.py:306 contrib/comments/models.py:85 +#: contrib/admin/views/doc.py:307 contrib/comments/models.py:85 msgid "IP address" msgstr "Adreça IP" -#: contrib/admin/views/doc.py:308 +#: contrib/admin/views/doc.py:309 msgid "Boolean (Either True, False or None)" msgstr "Booleà (Verdader, Fals o 'None' (cap))" -#: contrib/admin/views/doc.py:309 +#: contrib/admin/views/doc.py:310 msgid "Relation to parent model" msgstr "Relació amb el model pare" -#: contrib/admin/views/doc.py:310 +#: contrib/admin/views/doc.py:311 msgid "Phone number" msgstr "Número de telèfon" -#: contrib/admin/views/doc.py:315 +#: contrib/admin/views/doc.py:316 msgid "Text" msgstr "Texte" -#: contrib/admin/views/doc.py:316 +#: contrib/admin/views/doc.py:317 msgid "Time" msgstr "Hora" -#: contrib/admin/views/doc.py:317 contrib/flatpages/models.py:7 +#: contrib/admin/views/doc.py:318 contrib/flatpages/models.py:7 msgid "URL" msgstr "URL" -#: contrib/admin/views/doc.py:318 +#: contrib/admin/views/doc.py:319 msgid "U.S. state (two uppercase letters)" msgstr "Estat dels E.U.A. (dos lletres majúscules)" -#: contrib/admin/views/doc.py:319 +#: contrib/admin/views/doc.py:320 msgid "XML text" msgstr "Texte XML" -#: contrib/admin/views/doc.py:345 +#: contrib/admin/views/doc.py:346 #, python-format msgid "%s does not appear to be a urlpattern object" msgstr "%s no sembla ser un objecte 'urlpattern'" -#: contrib/admin/views/main.py:230 +#: contrib/admin/views/main.py:233 msgid "Site administration" msgstr "Lloc administratiu" -#: contrib/admin/views/main.py:278 contrib/admin/views/main.py:363 +#: contrib/admin/views/main.py:280 contrib/admin/views/main.py:365 #, python-format msgid "You may add another %s below." msgstr "Pot afegir un altre %s a baix." -#: contrib/admin/views/main.py:296 +#: contrib/admin/views/main.py:298 #, python-format msgid "Add %s" msgstr "Afegir %s" -#: contrib/admin/views/main.py:342 +#: contrib/admin/views/main.py:344 #, python-format msgid "Added %s." msgstr "Agregat %s." -#: contrib/admin/views/main.py:342 contrib/admin/views/main.py:344 -#: contrib/admin/views/main.py:346 core/validators.py:283 +#: contrib/admin/views/main.py:344 contrib/admin/views/main.py:346 +#: contrib/admin/views/main.py:348 core/validators.py:283 #: db/models/manipulators.py:309 msgid "and" msgstr "i" -#: contrib/admin/views/main.py:344 +#: contrib/admin/views/main.py:346 #, python-format msgid "Changed %s." msgstr "Modificat %s." -#: contrib/admin/views/main.py:346 +#: contrib/admin/views/main.py:348 #, python-format msgid "Deleted %s." msgstr "Eliminat %s." -#: contrib/admin/views/main.py:349 +#: contrib/admin/views/main.py:351 msgid "No fields changed." msgstr "Cap camp canviat." -#: contrib/admin/views/main.py:352 +#: contrib/admin/views/main.py:354 #, python-format msgid "The %(name)s \"%(obj)s\" was changed successfully." msgstr "S'ha modificat amb èxist el/la %(name)s \"%(obj)s." -#: contrib/admin/views/main.py:360 +#: contrib/admin/views/main.py:362 #, python-format msgid "" "The %(name)s \"%(obj)s\" was added successfully. You may edit it again below." msgstr "" "S'ha agregat amb èxit el/la %(name)s \"%(obj)s\". Pot editar-lo de nou abaix." -#: contrib/admin/views/main.py:398 +#: contrib/admin/views/main.py:400 #, python-format msgid "Change %s" msgstr "Modificar %s" -#: contrib/admin/views/main.py:483 +#: contrib/admin/views/main.py:487 #, python-format msgid "One or more %(fieldname)s in %(name)s: %(obj)s" msgstr "Un o més %(fieldname)s en %(name)s: %(obj)s" -#: contrib/admin/views/main.py:488 +#: contrib/admin/views/main.py:492 #, python-format msgid "One or more %(fieldname)s in %(name)s:" msgstr "Un o més %(fieldname)s en %(name)s:" -#: contrib/admin/views/main.py:520 +#: contrib/admin/views/main.py:524 #, python-format msgid "The %(name)s \"%(obj)s\" was deleted successfully." msgstr "El/la %(name)s \"%(obj)s\".ha estat eliminat amb èxit." -#: contrib/admin/views/main.py:523 +#: contrib/admin/views/main.py:527 msgid "Are you sure?" msgstr "Està segur?" -#: contrib/admin/views/main.py:545 +#: contrib/admin/views/main.py:549 #, python-format msgid "Change history: %s" msgstr "Modificar històric: %s" -#: contrib/admin/views/main.py:579 +#: contrib/admin/views/main.py:583 #, python-format msgid "Select %s" msgstr "Seleccioni %s" -#: contrib/admin/views/main.py:579 +#: contrib/admin/views/main.py:583 #, python-format msgid "Select %s to change" msgstr "Seleccioni %s per modificar" -#: contrib/admin/views/main.py:780 +#: contrib/admin/views/main.py:784 msgid "Database error" msgstr "Error de/en la base de dades" @@ -1622,72 +1622,72 @@ msgstr "n" msgid "rd" msgstr "r" -#: contrib/humanize/templatetags/humanize.py:50 +#: contrib/humanize/templatetags/humanize.py:52 #, python-format msgid "%(value).1f million" msgid_plural "%(value).1f million" msgstr[0] "%(value).1f milió" msgstr[1] "%(value).1f milions" -#: contrib/humanize/templatetags/humanize.py:53 +#: contrib/humanize/templatetags/humanize.py:55 #, python-format msgid "%(value).1f billion" msgid_plural "%(value).1f billion" msgstr[0] "%(value).1f bilió" msgstr[1] "%(value).1f bilions" -#: contrib/humanize/templatetags/humanize.py:56 +#: contrib/humanize/templatetags/humanize.py:58 #, python-format msgid "%(value).1f trillion" msgid_plural "%(value).1f trillion" msgstr[0] "%(value).1f trilió" msgstr[1] "%(value).1f trilions" -#: contrib/humanize/templatetags/humanize.py:71 +#: contrib/humanize/templatetags/humanize.py:74 msgid "one" msgstr "un" -#: contrib/humanize/templatetags/humanize.py:71 +#: contrib/humanize/templatetags/humanize.py:74 msgid "two" msgstr "dos" -#: contrib/humanize/templatetags/humanize.py:71 +#: contrib/humanize/templatetags/humanize.py:74 msgid "three" msgstr "tres" -#: contrib/humanize/templatetags/humanize.py:71 +#: contrib/humanize/templatetags/humanize.py:74 msgid "four" msgstr "cuatre" -#: contrib/humanize/templatetags/humanize.py:71 +#: contrib/humanize/templatetags/humanize.py:74 msgid "five" msgstr "cinc" -#: contrib/humanize/templatetags/humanize.py:71 +#: contrib/humanize/templatetags/humanize.py:74 msgid "six" msgstr "sis" -#: contrib/humanize/templatetags/humanize.py:71 +#: contrib/humanize/templatetags/humanize.py:74 msgid "seven" msgstr "set" -#: contrib/humanize/templatetags/humanize.py:71 +#: contrib/humanize/templatetags/humanize.py:74 msgid "eight" msgstr "vuit" -#: contrib/humanize/templatetags/humanize.py:71 +#: contrib/humanize/templatetags/humanize.py:74 msgid "nine" msgstr "nou" -#: contrib/humanize/templatetags/humanize.py:90 +#: contrib/humanize/templatetags/humanize.py:94 msgid "today" msgstr "avui" -#: contrib/humanize/templatetags/humanize.py:92 +#: contrib/humanize/templatetags/humanize.py:96 msgid "tomorrow" msgstr "demà" -#: contrib/humanize/templatetags/humanize.py:94 +#: contrib/humanize/templatetags/humanize.py:98 msgid "yesterday" msgstr "ahir" @@ -3024,6 +3024,50 @@ msgstr "" "Introdueixi un número vàlid de la Seguretat Social dels E.U.A. en el format " "XXX-XX-XXXX." +#: contrib/localflavor/za/forms.py:22 +msgid "Enter a valid South African ID number" +msgstr "Introdueixi un número d'Identitat Sud Africà valid" + +#: contrib/localflavor/za/forms.py:57 +msgid "Enter a valid South African postal code" +msgstr "Introdueixi un codi postal Sud Africà vàlid." + +#: contrib/localflavor/za/za_provinces.py:4 +msgid "Eastern Cape" +msgstr "Eastern Cape" + +#: contrib/localflavor/za/za_provinces.py:5 +msgid "Free State" +msgstr "Free State" + +#: contrib/localflavor/za/za_provinces.py:6 +msgid "Gauteng" +msgstr "Gauteng" + +#: contrib/localflavor/za/za_provinces.py:7 +msgid "KwaZulu-Natal" +msgstr "KwaZulu-Natal" + +#: contrib/localflavor/za/za_provinces.py:8 +msgid "Limpopo" +msgstr "Limpopo" + +#: contrib/localflavor/za/za_provinces.py:9 +msgid "Mpumalanga" +msgstr "Mpumalanga" + +#: contrib/localflavor/za/za_provinces.py:10 +msgid "Northern Cape" +msgstr "Northern Cape" + +#: contrib/localflavor/za/za_provinces.py:11 +msgid "North West" +msgstr "North West" + +#: contrib/localflavor/za/za_provinces.py:12 +msgid "Western Cape" +msgstr "Western Cape" + #: contrib/redirects/models.py:7 msgid "redirect from" msgstr "redreçar des de" @@ -3056,23 +3100,23 @@ msgstr "redreçament" msgid "redirects" msgstr "redreçaments" -#: contrib/sessions/models.py:80 +#: contrib/sessions/models.py:46 msgid "session key" msgstr "clau de la sessió" -#: contrib/sessions/models.py:81 +#: contrib/sessions/models.py:47 msgid "session data" msgstr "dades de la sessió" -#: contrib/sessions/models.py:82 +#: contrib/sessions/models.py:48 msgid "expire date" msgstr "data de caducitat" -#: contrib/sessions/models.py:87 +#: contrib/sessions/models.py:53 msgid "session" msgstr "sessió" -#: contrib/sessions/models.py:88 +#: contrib/sessions/models.py:54 msgid "sessions" msgstr "sessions" @@ -3141,7 +3185,7 @@ msgstr "No s'admeten caracters no numèrics." msgid "This value can't be comprised solely of digits." msgstr "Aquest valor no pot contenir només dígits." -#: core/validators.py:128 newforms/fields.py:157 +#: core/validators.py:128 newforms/fields.py:151 msgid "Enter a whole number." msgstr "Introdueixi un número sencer." @@ -3170,17 +3214,17 @@ msgstr "Introdueixi una hora vàlida en el format HH:MM." msgid "Enter a valid date/time in YYYY-MM-DD HH:MM format." msgstr "Introdueixi un data/hora vàlida en format YYYY-MM-DD HH:MM." -#: core/validators.py:170 newforms/fields.py:408 +#: core/validators.py:170 newforms/fields.py:402 msgid "Enter a valid e-mail address." msgstr "Introdueixi una adreça de correu vàlida." -#: core/validators.py:182 core/validators.py:474 newforms/fields.py:438 -#: oldforms/__init__.py:686 +#: core/validators.py:182 core/validators.py:474 newforms/fields.py:432 +#: oldforms/__init__.py:687 msgid "No file was submitted. Check the encoding type on the form." msgstr "" "No s'ha enviat cap fitxer. Comprovi el tipus de codificació del formulari." -#: core/validators.py:193 newforms/fields.py:462 +#: core/validators.py:193 newforms/fields.py:456 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." @@ -3431,7 +3475,7 @@ msgstr "Ja existeix %(optname)s amb auqest %(fieldname)s." #: db/models/fields/__init__.py:161 db/models/fields/__init__.py:318 #: db/models/fields/__init__.py:735 db/models/fields/__init__.py:746 -#: newforms/fields.py:45 newforms/models.py:211 oldforms/__init__.py:373 +#: newforms/fields.py:45 oldforms/__init__.py:374 msgid "This field is required." msgstr "Aquest camp és obligatori." @@ -3484,150 +3528,150 @@ msgstr[1] "" msgid "Enter a valid value." msgstr "Introdueixi un valor vàlid." -#: newforms/fields.py:129 +#: newforms/fields.py:123 #, python-format msgid "Ensure this value has at most %(max)d characters (it has %(length)d)." msgstr "" "Asseguris de que el valor té com a màxim %(max)d caràcters (en té %(length)" "d)." -#: newforms/fields.py:130 +#: newforms/fields.py:124 #, python-format msgid "Ensure this value has at least %(min)d characters (it has %(length)d)." msgstr "" "Asseguris de que el valor té com a mínim %(min)d caràcters (en té %(length)" "d)." -#: newforms/fields.py:158 newforms/fields.py:187 newforms/fields.py:216 +#: newforms/fields.py:152 newforms/fields.py:181 newforms/fields.py:210 #, python-format msgid "Ensure this value is less than or equal to %s." msgstr "Aquest valor ha de ser menor o igual a %s." -#: newforms/fields.py:159 newforms/fields.py:188 newforms/fields.py:217 +#: newforms/fields.py:153 newforms/fields.py:182 newforms/fields.py:211 #, python-format msgid "Ensure this value is greater than or equal to %s." msgstr "Asseguris de que aquest valor sigui superior o igual a %s." -#: newforms/fields.py:186 newforms/fields.py:215 +#: newforms/fields.py:180 newforms/fields.py:209 msgid "Enter a number." msgstr "Introdueixi un número." -#: newforms/fields.py:218 +#: newforms/fields.py:212 #, python-format msgid "Ensure that there are no more than %s digits in total." msgstr "Asseguris de que no hi ha més de %s dígits en total." -#: newforms/fields.py:219 +#: newforms/fields.py:213 #, python-format msgid "Ensure that there are no more than %s decimal places." msgstr "Asseguris de que no hi ha més de %s decimals." -#: newforms/fields.py:220 +#: newforms/fields.py:214 #, python-format msgid "Ensure that there are no more than %s digits before the decimal point." msgstr "Asseguris de que no hia ha més de %s dígits decimals." -#: newforms/fields.py:268 newforms/fields.py:724 +#: newforms/fields.py:262 newforms/fields.py:719 msgid "Enter a valid date." msgstr "Introdueixi una data vàlida." -#: newforms/fields.py:301 newforms/fields.py:725 +#: newforms/fields.py:295 newforms/fields.py:720 msgid "Enter a valid time." msgstr "Introdueixi una hora vàlida." -#: newforms/fields.py:340 +#: newforms/fields.py:334 msgid "Enter a valid date/time." msgstr "Introdueixi una data/hora vàlides." -#: newforms/fields.py:439 +#: newforms/fields.py:433 msgid "No file was submitted." msgstr "No s'ha enviat cap fitxer." -#: newforms/fields.py:440 oldforms/__init__.py:688 +#: newforms/fields.py:434 oldforms/__init__.py:689 msgid "The submitted file is empty." msgstr "El fitxer enviat està buit." -#: newforms/fields.py:498 +#: newforms/fields.py:492 msgid "Enter a valid URL." msgstr "Introdueixi una URL vàlida." -#: newforms/fields.py:499 +#: newforms/fields.py:493 msgid "This URL appears to be a broken link." msgstr "Aquesta URL sembla ser un enllaç trencat." -#: newforms/fields.py:560 newforms/models.py:194 +#: newforms/fields.py:555 newforms/models.py:155 msgid "Select a valid choice. That choice is not one of the available choices." msgstr "" "Esculli una opció vàlida; Aquesta opció no és una de les opcions disponibles." -#: newforms/fields.py:599 +#: newforms/fields.py:594 #, python-format msgid "Select a valid choice. %(value)s is not one of the available choices." msgstr "Esculli una opció vàlida. %(value)s no és una de les opcions vàlides." -#: newforms/fields.py:600 newforms/fields.py:662 newforms/models.py:215 +#: newforms/fields.py:595 newforms/fields.py:657 newforms/models.py:215 msgid "Enter a list of values." msgstr "Introdueixi una llista de valors." -#: newforms/fields.py:753 +#: newforms/fields.py:748 msgid "Enter a valid IPv4 address." msgstr "Introdueixi una adreça IPv4 vàlida." -#: newforms/models.py:221 +#: newforms/models.py:216 #, python-format msgid "Select a valid choice. %s is not one of the available choices." msgstr "Esculli una opció vàlida; %s' no és una de les opcions vàlides." -#: oldforms/__init__.py:408 +#: oldforms/__init__.py:409 #, python-format msgid "Ensure your text is less than %s character." msgid_plural "Ensure your text is less than %s characters." msgstr[0] "Asseguris de que el seu texte té menys de %s caracter." msgstr[1] "Asseguris de que el seu texte té menys de %s caracters." -#: oldforms/__init__.py:413 +#: oldforms/__init__.py:414 msgid "Line breaks are not allowed here." msgstr "No es permeten salts de línia." -#: oldforms/__init__.py:511 oldforms/__init__.py:585 oldforms/__init__.py:624 +#: oldforms/__init__.py:512 oldforms/__init__.py:586 oldforms/__init__.py:625 #, python-format msgid "Select a valid choice; '%(data)s' is not in %(choices)s." msgstr "Esculli una opció vàlida; %(data)s' no està dintre de %(choices)s." -#: oldforms/__init__.py:744 +#: oldforms/__init__.py:745 msgid "Enter a whole number between -32,768 and 32,767." msgstr "Introdueixi un número enter entre -32,768 i 32,767." -#: oldforms/__init__.py:754 +#: oldforms/__init__.py:755 msgid "Enter a positive number." msgstr "Introdueixi un número positiu." -#: oldforms/__init__.py:764 +#: oldforms/__init__.py:765 msgid "Enter a whole number between 0 and 32,767." msgstr "Introdueixi un número entre 0 i 32,767." -#: template/defaultfilters.py:555 +#: template/defaultfilters.py:658 msgid "yes,no,maybe" msgstr "si,no,potser" -#: template/defaultfilters.py:585 +#: template/defaultfilters.py:689 #, python-format msgid "%(size)d byte" msgid_plural "%(size)d bytes" msgstr[0] "%(size)d byte" msgstr[1] "%(size)d bytes" -#: template/defaultfilters.py:587 +#: template/defaultfilters.py:691 #, python-format msgid "%.1f KB" msgstr "%.1f KB" -#: template/defaultfilters.py:589 +#: template/defaultfilters.py:693 #, python-format msgid "%.1f MB" msgstr "%.1f MB" -#: template/defaultfilters.py:590 +#: template/defaultfilters.py:694 #, python-format msgid "%.1f GB" msgstr "%.1f GB" @@ -3890,23 +3934,23 @@ msgstr "%(number)d %(type)s" msgid ", %(number)d %(type)s" msgstr ", %(number)d %(type)s" -#: utils/translation/trans_real.py:395 +#: utils/translation/trans_real.py:399 msgid "DATE_FORMAT" msgstr "F j, Y" -#: utils/translation/trans_real.py:396 +#: utils/translation/trans_real.py:400 msgid "DATETIME_FORMAT" msgstr "F j, Y, H:i" -#: utils/translation/trans_real.py:397 +#: utils/translation/trans_real.py:401 msgid "TIME_FORMAT" msgstr "H:i" -#: utils/translation/trans_real.py:413 +#: utils/translation/trans_real.py:417 msgid "YEAR_MONTH_FORMAT" msgstr "j de/d' F del Y" -#: utils/translation/trans_real.py:414 +#: utils/translation/trans_real.py:418 msgid "MONTH_DAY_FORMAT" msgstr "j de/d' F del Y" @@ -3924,43 +3968,3 @@ msgstr "El/La %(verbose_name)s s'ha actualtzat amb èxit." #, python-format msgid "The %(verbose_name)s was deleted." msgstr "El %(verbose_name)s s'ha eliminat." - -#~ msgid "" -#~ "This comment was posted by a user who has posted fewer than %(count)s " -#~ "comment:\n" -#~ "\n" -#~ "%(text)sThis comment was posted by a user who has posted fewer than %" -#~ "(count)s comments:\n" -#~ "\n" -#~ "%(text)s" -#~ msgstr "" -#~ "Aquest comentari el va enviar un usuari que ha enviat menys de %(count)s " -#~ "comentari:\n" -#~ "\n" -#~ "%(text)sAquest comentari el va enviar un usuari que ha enviat menys de %" -#~ "(count)s comentaris:\n" -#~ "\n" -#~ "%(text)s" - -#~ msgid "AnonymousUser" -#~ msgstr "AnonymousUser" - -#~ msgid "" -#~ "Please enter a valid decimal number with a whole part of at most %s digit." -#~ "Please enter a valid decimal number with a whole part of at most %s " -#~ "digits." -#~ msgstr "" -#~ "Si us plau, introdueixi un número decimal vàlid amb la part entera amb " -#~ "com a màxim %s dígit.Si us plau, introdueixi un número decimal vàlid amb " -#~ "la part entera amb com a màxim %s dígits." - -#~ msgid "" -#~ "Please enter a valid decimal number with at most %s decimal place.Please " -#~ "enter a valid decimal number with at most %s decimal places." -#~ msgstr "" -#~ "Si us plau, introdueixi un número decimal vàlid amb no més de %s dígit en " -#~ "la part decimal.Si us plau, introdueixi un número decimal vàlid amb no " -#~ "més de %s dígits en la part decimal." - -#~ msgid "%d milliseconds" -#~ msgstr "%d milisegons" diff --git a/django/conf/locale/nl/LC_MESSAGES/django.mo b/django/conf/locale/nl/LC_MESSAGES/django.mo index 4c70c28ff4..b49be6a3e3 100644 Binary files a/django/conf/locale/nl/LC_MESSAGES/django.mo and b/django/conf/locale/nl/LC_MESSAGES/django.mo differ diff --git a/django/conf/locale/nl/LC_MESSAGES/django.po b/django/conf/locale/nl/LC_MESSAGES/django.po index 9aaeef8af9..b8c516b72d 100644 --- a/django/conf/locale/nl/LC_MESSAGES/django.po +++ b/django/conf/locale/nl/LC_MESSAGES/django.po @@ -1,17 +1,12 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the django package. -# Johan C. Stöver , 2005. -# Rudolph Froger , 2006. -# + #, fuzzy msgid "" msgstr "" "Project-Id-Version: Django 1.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2006-12-09 15:51+0100\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Johan C. Stöver \n" +"PO-Revision-Date: 2007-12-17 21:14+0100\n" +"Last-Translator: I.S. van Oostveen\n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" @@ -69,7 +64,7 @@ msgstr "De waarde moet een geheel getal zijn." #: db/models/fields/__init__.py:381 msgid "This value must be either True or False." -msgstr "De waarde moet of True (waar) of False (onwaar) zijn." +msgstr "De waarde moet of True (Waar) of False (Onwaar) zijn." #: db/models/fields/__init__.py:397 msgid "This field cannot be null." @@ -279,7 +274,7 @@ msgstr "Alleen alfabetische karakters zijn toegestaan" #: core/validators.py:139 msgid "Year must be 1900 or later." -msgstr "Het jaartal moet 1900 of nieuwer zijn." +msgstr "Het jaartal moet 1900 of later zijn." #: core/validators.py:143 #, python-format @@ -296,20 +291,20 @@ msgstr "Geef een geldig e-mailadres op." #: core/validators.py:173 core/validators.py:442 forms/__init__.py:667 msgid "No file was submitted. Check the encoding type on the form." -msgstr "Er was geen bestand verstuurd. Controleer de encoding van het formulier." +msgstr "Er was geen bestand verstuurd. Controleer het coderings type van het formulier." #: core/validators.py:177 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -"Bestand ongeldig. Het bestand dat is gegeven is geen afbeelding of was " +"Bestand ongeldig. Het bestand dat is gegeven is geen afbeelding of is " "beschadigd." #: core/validators.py:184 #, python-format msgid "The URL %s does not point to a valid image." -msgstr "De URL %s wijst niet naar een afbeelding." +msgstr "De URL %s wijst niet naar een geldige afbeelding." #: core/validators.py:188 #, python-format @@ -321,7 +316,7 @@ msgstr "" #: core/validators.py:196 #, python-format msgid "The URL %s does not point to a valid QuickTime video." -msgstr "De URL %s wijst niet naar een QuickTime video." +msgstr "De URL %s wijst niet naar een geldige QuickTime video." #: core/validators.py:200 msgid "A valid URL is required." @@ -349,18 +344,18 @@ msgstr "Ongeldige URL: %s" #: core/validators.py:243 core/validators.py:245 #, python-format msgid "The URL %s is a broken link." -msgstr "De URL %s is een niet werkende link." +msgstr "De URL %s is niet een werkende link." #: core/validators.py:251 msgid "Enter a valid U.S. state abbreviation." -msgstr "Geef een geldige afkorting van een VS staat." +msgstr "Geef een geldige afkorting van een staat in de VS." #: core/validators.py:265 #, python-format msgid "Watch your mouth! The word %s is not allowed here." msgid_plural "Watch your mouth! The words %s are not allowed here." msgstr[0] "Pas op uw taalgebruik! Gebruik van %s niet toegestaan." -msgstr[1] "Pas op uw taalgebruik! Gebruik van de woorden %s niet toegestaan." +msgstr[1] "Pas op uw taalgebruik! Gebruik van de woorden %s is niet toegestaan." #: core/validators.py:272 #, python-format @@ -373,7 +368,7 @@ msgstr "Voer tenminste één veld in." #: core/validators.py:300 core/validators.py:311 msgid "Please enter both fields or leave them both empty." -msgstr "Voer waarden in in beide velden of laat beide leeg." +msgstr "Voer waarden in beide velden in of laat beide leeg." #: core/validators.py:318 #, python-format @@ -450,7 +445,7 @@ msgstr "Zorg ervoor dat het bestand hoogstens %s bytes groot is." #: core/validators.py:453 msgid "The format for this field is wrong." -msgstr "Het formaat van dit veld is fout." +msgstr "Het formaat van dit veld is foutief." #: core/validators.py:468 msgid "This field is invalid." @@ -536,7 +531,7 @@ msgid "" "Your Web browser doesn't appear to have cookies enabled. Cookies are " "required for logging in." msgstr "" -"Het lijkt erop dat uw browser geen cookies accepteerd. Om aan te melden " +"Het lijkt erop dat uw browser geen cookies accepteert. Om aan te melden " "moeten cookies worden geaccepteerd." #: contrib/auth/forms.py:59 contrib/admin/views/decorators.py:10 @@ -645,7 +640,7 @@ msgstr "supergebruiker status" msgid "" "Designates that this user has all permissions without explicitly assigning " "them." -msgstr "Bepaald dat deze gebruiker alle rechten heeft, zonder deze expliciet toe te wijzen." +msgstr "Bepaalt dat deze gebruiker alle rechten heeft, zonder deze expliciet toe te wijzen." #: contrib/auth/models.py:98 msgid "last login" @@ -685,7 +680,7 @@ msgstr "Rechten" #: contrib/auth/models.py:113 msgid "Important dates" -msgstr "Belangrijke data" +msgstr "Belangrijke datums" #: contrib/auth/models.py:114 msgid "Groups" @@ -783,7 +778,7 @@ msgid "" "Please log in again, because your session has expired. Don't worry: Your " "submission has been saved." msgstr "" -"Uw sessie is verlopen, meldt u opnieuw aan. Maakt u geen zorgen: Uw bijdrage " +"Uw sessie is verlopen, meld u opnieuw aan. Maakt u zich geen zorgen: Uw bijdrage " "is opgeslagen." #: contrib/admin/views/decorators.py:69 @@ -791,7 +786,7 @@ msgid "" "Looks like your browser isn't configured to accept cookies. Please enable " "cookies, reload this page, and try again." msgstr "" -"Het lijkt erop dat uw browser geen cookies accepteerd. Zet het gebruik van " +"Het lijkt erop dat uw browser geen cookies accepteert. Zet het gebruik van " "cookies aan in uw browser, laad deze pagina nogmaals en probeer het opnieuw." #: contrib/admin/views/decorators.py:83 @@ -928,7 +923,7 @@ msgstr "Model %r niet gevonden in app %r" #: contrib/admin/views/doc.py:183 #, python-format msgid "the related `%s.%s` object" -msgstr "the related `%s.%s` object" +msgstr "het gerelateerde `%s.%s` object" #: contrib/admin/views/doc.py:183 contrib/admin/views/doc.py:205 #: contrib/admin/views/doc.py:219 contrib/admin/views/doc.py:224 @@ -938,7 +933,7 @@ msgstr "model:" #: contrib/admin/views/doc.py:214 #, python-format msgid "related `%s.%s` objects" -msgstr "related `%s.%s` objects" +msgstr "de gerelateerde `%s.%s` objecten" #: contrib/admin/views/doc.py:219 #, python-format @@ -948,12 +943,12 @@ msgstr "alle %s" #: contrib/admin/views/doc.py:224 #, python-format msgid "number of %s" -msgstr "nummer van %s" +msgstr "aantal %s" #: contrib/admin/views/doc.py:229 #, python-format msgid "Fields on %s objects" -msgstr "Velden van %s objects" +msgstr "Velden van %s objecten" #: contrib/admin/views/doc.py:291 contrib/admin/views/doc.py:301 #: contrib/admin/views/doc.py:303 contrib/admin/views/doc.py:309 @@ -1139,11 +1134,11 @@ msgid "" "All of the following related items will be deleted:" msgstr "" "Weet u zeker dat u %(object_name)s \"%(escaped_object)s\" wilt verwijderen? Alle " -"volgende opjecten worden verwijderd:" +"volgende objecten worden verwijderd:" #: contrib/admin/templates/admin/delete_confirmation.html:26 msgid "Yes, I'm sure" -msgstr "Ja, Ik weet het zeker" +msgstr "Ja, ik weet het zeker" #: contrib/admin/templates/admin/404.html:4 #: contrib/admin/templates/admin/404.html:8 @@ -1197,7 +1192,7 @@ msgstr "Opslaan en nieuw item" #: contrib/admin/templates/admin/submit_line.html:6 msgid "Save and continue editing" -msgstr "Opslaan en bewerk opnieuw" +msgstr "Opslaan en opnieuw bewerken" #: contrib/admin/templates/admin/submit_line.html:7 msgid "Save" @@ -1224,7 +1219,7 @@ msgstr "Wijzigen" #: contrib/admin/templates/admin/index.html:44 msgid "You don't have permission to edit anything." -msgstr "U heeft geen rechten om iets te wijzigen" +msgstr "U heeft geen rechten om iets te wijzigen." #: contrib/admin/templates/admin/index.html:52 msgid "Recent Actions" @@ -1380,7 +1375,7 @@ msgstr "" "de bookmarklet vanuit elke pagina op de site worden gekozen. Let erop dat " "het soms\n" "noodzakelijk is dat de computer van waaruit de pagina wordt bekeken intern " -"is\n" +"is.\n" "(Raadpleeg uw systeembeheerder of uw computer zich op het interne netwerk " "bevind).

    \n" @@ -1393,7 +1388,7 @@ msgid "" "Jumps you from any page to the documentation for the view that generates " "that page." msgstr "" -"Spring vanuit elke pagina naar de documentatie voor de view die gegenereerd " +"Springt vanuit elke pagina naar de documentatie voor de view die gegenereerd " "wordt door die pagina" #: contrib/admin/templates/admin_doc/bookmarklets.html:22 @@ -1406,7 +1401,7 @@ msgid "" "object." msgstr "" "Toont de content-type en unieke ID voor pagina's die een enkel object " -"voorsteld." +"voorstellen." #: contrib/admin/templates/admin_doc/bookmarklets.html:25 msgid "Edit this object (current window)" @@ -1414,7 +1409,7 @@ msgstr "Bewerk dit object (huidig venster)" #: contrib/admin/templates/admin_doc/bookmarklets.html:26 msgid "Jumps to the admin page for pages that represent a single object." -msgstr "Ga naar de beheerpagina voor pagina's die een enkel object weergeven." +msgstr "Gaat naar de beheerpagina voor pagina's die een enkel object weergeven." #: contrib/admin/templates/admin_doc/bookmarklets.html:28 msgid "Edit this object (new window)" @@ -1527,7 +1522,7 @@ msgid "" "password twice so we can verify you typed it in correctly." msgstr "" "Vanwege de beveiliging moet u uw oude en twee keer een nieuw " -"wachtwoordinvoeren, zodat we kunnen controleren of er geen typefouten zijn " +"wachtwoord invoeren, zodat we kunnen controleren of er geen typefouten zijn " "gemaakt." #: contrib/admin/templates/registration/password_change_form.html:17 @@ -1597,7 +1592,7 @@ msgid "" "Example: 'flatpages/contact_page.html'. If this isn't provided, the system " "will use 'flatpages/default.html'." msgstr "" -"Voorbeeld: 'flatpages/contact_page'. Als deze niet is opgegeven, dan wordt " +"Voorbeeld: 'flatpages/contact_page.html'. Als deze niet is opgegeven, dan wordt " "'flatpages/default.html' gebruikt." #: contrib/flatpages/models.py:14 @@ -1894,7 +1889,7 @@ msgstr "Een of meerdere verplichte velden zijn niet ingevuld" #: contrib/comments/views/comments.py:196 #: contrib/comments/views/comments.py:286 msgid "Somebody tampered with the comment form (security violation)" -msgstr "Iemand heeft het opmerkingenformulier gewijzigd (Beveilingsinbreuk)" +msgstr "Iemand heeft het opmerkingenformulier gewijzigd (beveilingsinbreuk)" #: contrib/comments/views/comments.py:206 #: contrib/comments/views/comments.py:292 diff --git a/django/contrib/admin/media/js/urlify.js b/django/contrib/admin/media/js/urlify.js index 74e6e57e6c..63e626d884 100644 --- a/django/contrib/admin/media/js/urlify.js +++ b/django/contrib/admin/media/js/urlify.js @@ -55,6 +55,12 @@ var POLISH_MAP = { 'Ź':'Z', 'Ż':'Z' } +var LATVIAN_MAP = { + 'ā':'a', 'č':'c', 'ē':'e', 'ģ':'g', 'ī':'i', 'ķ':'k', 'ļ':'l', 'ņ':'n', + 'š':'s', 'ū':'u', 'ž':'z', 'Ā':'A', 'Č':'C', 'Ē':'E', 'Ģ':'G', 'Ī':'i', + 'Ķ':'k', 'Ļ':'L', 'Ņ':'N', 'Š':'S', 'Ū':'u', 'Ž':'Z' +} + var ALL_DOWNCODE_MAPS=new Array() ALL_DOWNCODE_MAPS[0]=LATIN_MAP ALL_DOWNCODE_MAPS[1]=LATIN_SYMBOLS_MAP @@ -64,6 +70,7 @@ ALL_DOWNCODE_MAPS[4]=RUSSIAN_MAP ALL_DOWNCODE_MAPS[5]=UKRAINIAN_MAP ALL_DOWNCODE_MAPS[6]=CZECH_MAP ALL_DOWNCODE_MAPS[7]=POLISH_MAP +ALL_DOWNCODE_MAPS[8]=LATVIAN_MAP var Downcoder = new Object(); Downcoder.Initialize = function() diff --git a/django/contrib/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py index 2db43270d9..17579064e0 100644 --- a/django/contrib/admin/templatetags/admin_list.py +++ b/django/contrib/admin/templatetags/admin_list.py @@ -114,7 +114,7 @@ def result_headers(cl): yield {"text": header, "sortable": True, "url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}), - "class_attrib": 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): BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'} diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py index 22e0078744..eb811cb389 100644 --- a/django/contrib/auth/models.py +++ b/django/contrib/auth/models.py @@ -307,7 +307,7 @@ class AnonymousUser(object): id = None username = '' is_staff = False - is_active = True + is_active = False is_superuser = False _groups = EmptyManager() _user_permissions = EmptyManager() diff --git a/django/contrib/auth/tests.py b/django/contrib/auth/tests.py index 329049c546..d369ac524c 100644 --- a/django/contrib/auth/tests.py +++ b/django/contrib/auth/tests.py @@ -16,9 +16,21 @@ False >>> u2 = User.objects.create_user('testuser2', 'test2@example.com') >>> u2.has_usable_password() False + +>>> u.is_authenticated() +True +>>> u.is_staff +False +>>> u.is_active +True + >>> a = AnonymousUser() +>>> a.is_authenticated() +False >>> a.is_staff False +>>> a.is_active +False >>> a.groups.all() [] >>> a.user_permissions.all() diff --git a/django/contrib/contenttypes/generic.py b/django/contrib/contenttypes/generic.py index b738a269ec..e496ed1af8 100644 --- a/django/contrib/contenttypes/generic.py +++ b/django/contrib/contenttypes/generic.py @@ -16,18 +16,18 @@ class GenericForeignKey(object): Provides a generic relation to any object through content-type/object-id fields. """ - + def __init__(self, ct_field="content_type", fk_field="object_id"): self.ct_field = ct_field self.fk_field = fk_field - + def contribute_to_class(self, cls, name): - # Make sure the fields exist (these raise FieldDoesNotExist, + # Make sure the fields exist (these raise FieldDoesNotExist, # which is a fine error to raise here) self.name = name self.model = cls self.cache_attr = "_%s_cache" % name - + # For some reason I don't totally understand, using weakrefs here doesn't work. dispatcher.connect(self.instance_pre_init, signal=signals.pre_init, sender=cls, weak=False) @@ -35,18 +35,18 @@ class GenericForeignKey(object): setattr(cls, name, self) def instance_pre_init(self, signal, sender, args, kwargs): - # Handle initalizing an object with the generic FK instaed of - # content-type/object-id fields. + # Handle initalizing an object with the generic FK instaed of + # content-type/object-id fields. if self.name in kwargs: value = kwargs.pop(self.name) kwargs[self.ct_field] = self.get_content_type(value) kwargs[self.fk_field] = value._get_pk_val() - + def get_content_type(self, obj): # Convenience function using get_model avoids a circular import when using this model ContentType = get_model("contenttypes", "contenttype") return ContentType.objects.get_for_model(obj) - + def __get__(self, instance, instance_type=None): if instance is None: raise AttributeError, u"%s must be accessed via instance" % self.name @@ -77,21 +77,21 @@ class GenericForeignKey(object): setattr(instance, self.ct_field, ct) setattr(instance, self.fk_field, fk) setattr(instance, self.cache_attr, value) - + class GenericRelation(RelatedField, Field): """Provides an accessor to generic related objects (i.e. comments)""" def __init__(self, to, **kwargs): kwargs['verbose_name'] = kwargs.get('verbose_name', None) - kwargs['rel'] = GenericRel(to, + kwargs['rel'] = GenericRel(to, related_name=kwargs.pop('related_name', None), limit_choices_to=kwargs.pop('limit_choices_to', None), symmetrical=kwargs.pop('symmetrical', True)) - + # Override content-type/object-id field names on the related class self.object_id_field_name = kwargs.pop("object_id_field", "object_id") - self.content_type_field_name = kwargs.pop("content_type_field", "content_type") - + self.content_type_field_name = kwargs.pop("content_type_field", "content_type") + kwargs['blank'] = True kwargs['editable'] = False kwargs['serialize'] = False @@ -116,9 +116,9 @@ class GenericRelation(RelatedField, Field): def m2m_column_name(self): return self.object_id_field_name - + def m2m_reverse_name(self): - return self.object_id_field_name + return self.model._meta.pk.column def contribute_to_class(self, cls, name): super(GenericRelation, self).contribute_to_class(cls, name) @@ -131,13 +131,13 @@ class GenericRelation(RelatedField, Field): def contribute_to_related_class(self, cls, related): pass - + def set_attributes_from_rel(self): pass def get_internal_type(self): return "ManyToManyField" - + class ReverseGenericRelatedObjectsDescriptor(object): """ This class provides the functionality that makes the related-object @@ -193,12 +193,12 @@ def create_generic_related_manager(superclass): Factory function for a manager that subclasses 'superclass' (which is a Manager) and adds behavior for generic related objects. """ - + class GenericRelatedObjectManager(superclass): def __init__(self, model=None, core_filters=None, instance=None, symmetrical=None, join_table=None, source_col_name=None, target_col_name=None, content_type=None, content_type_field_name=None, object_id_field_name=None): - + super(GenericRelatedObjectManager, self).__init__() self.core_filters = core_filters or {} self.model = model @@ -212,10 +212,10 @@ def create_generic_related_manager(superclass): self.content_type_field_name = content_type_field_name self.object_id_field_name = object_id_field_name self.pk_val = self.instance._get_pk_val() - + def get_query_set(self): query = { - '%s__pk' % self.content_type_field_name : self.content_type.id, + '%s__pk' % self.content_type_field_name : self.content_type.id, '%s__exact' % self.object_id_field_name : self.pk_val, } return superclass.get_query_set(self).filter(**query) diff --git a/django/contrib/databrowse/datastructures.py b/django/contrib/databrowse/datastructures.py index 4b0947eb34..1b32ee2e2e 100644 --- a/django/contrib/databrowse/datastructures.py +++ b/django/contrib/databrowse/datastructures.py @@ -12,6 +12,7 @@ from django.utils.safestring import mark_safe from django.db.models.query import QuerySet EMPTY_VALUE = '(None)' +DISPLAY_SIZE = 100 class EasyModel(object): def __init__(self, site, model): @@ -93,8 +94,8 @@ class EasyInstance(object): def __unicode__(self): val = smart_unicode(self.instance) - if len(val) > 30: - return val[:30] + u'...' + if len(val) > DISPLAY_SIZE: + return val[:DISPLAY_SIZE] + u'...' return val def __str__(self): diff --git a/django/contrib/localflavor/ar/forms.py b/django/contrib/localflavor/ar/forms.py index 6d86e4e676..6374a48c92 100644 --- a/django/contrib/localflavor/ar/forms.py +++ b/django/contrib/localflavor/ar/forms.py @@ -24,18 +24,20 @@ class ARPostalCodeField(RegexField): See http://www.correoargentino.com.ar/consulta_cpa/home.php """ + default_error_messages = { + 'invalid': ugettext("Enter a postal code in the format NNNN or ANNNNAAA."), + } + def __init__(self, *args, **kwargs): super(ARPostalCodeField, self).__init__(r'^\d{4}$|^[A-HJ-NP-Za-hj-np-z]\d{4}\D{3}$', - min_length=4, max_length=8, - error_message=ugettext("Enter a postal code in the format NNNN or ANNNNAAA."), - *args, **kwargs) + min_length=4, max_length=8, *args, **kwargs) def clean(self, value): value = super(ARPostalCodeField, self).clean(value) if value in EMPTY_VALUES: return u'' if len(value) not in (4, 8): - raise ValidationError(ugettext("Enter a postal code in the format NNNN or ANNNNAAA.")) + raise ValidationError(self.error_messages['invalid']) if len(value) == 8: return u'%s%s%s' % (value[0].upper(), value[1:5], value[5:].upper()) return value @@ -44,6 +46,11 @@ class ARDNIField(CharField): """ A field that validates `Documento Nacional de Identidad (DNI) numbers. """ + default_error_messages = { + 'invalid': ugettext("This field requires only numbers."), + 'max_digits': ugettext("This field requires 7 or 8 digits."), + } + def __init__(self, *args, **kwargs): super(ARDNIField, self).__init__(max_length=10, min_length=7, *args, **kwargs) @@ -58,10 +65,9 @@ class ARDNIField(CharField): if not value.isdigit(): value = value.replace('.', '') if not value.isdigit(): - raise ValidationError(ugettext("This field requires only numbers.")) + raise ValidationError(self.error_messages['invalid']) if len(value) not in (7, 8): - raise ValidationError( - ugettext("This field requires 7 or 8 digits.")) + raise ValidationError(self.error_messages['max_digits']) return value @@ -70,9 +76,13 @@ class ARCUITField(RegexField): This field validates a CUIT (Cdigo nico de Identificacin Tributaria). A CUIT is of the form XX-XXXXXXXX-V. The last digit is a check digit. """ + default_error_messages = { + 'invalid': ugettext('Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'), + 'checksum': ugettext("Invalid CUIT."), + } + def __init__(self, *args, **kwargs): super(ARCUITField, self).__init__(r'^\d{2}-?\d{8}-?\d$', - error_message=ugettext('Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'), *args, **kwargs) def clean(self, value): @@ -85,7 +95,7 @@ class ARCUITField(RegexField): return u'' value, cd = self._canon(value) if self._calc_cd(value) != cd: - raise ValidationError(ugettext("Invalid CUIT.")) + raise ValidationError(self.error_messages['checksum']) return self._format(value, cd) def _canon(self, cuit): diff --git a/django/contrib/localflavor/au/forms.py b/django/contrib/localflavor/au/forms.py index 33e883511b..ada27b69b9 100644 --- a/django/contrib/localflavor/au/forms.py +++ b/django/contrib/localflavor/au/forms.py @@ -12,14 +12,20 @@ PHONE_DIGITS_RE = re.compile(r'^(\d{10})$') class AUPostCodeField(RegexField): """Australian post code field.""" + default_error_messages = { + 'invalid': ugettext('Enter a 4 digit post code.'), + } + def __init__(self, *args, **kwargs): super(AUPostCodeField, self).__init__(r'^\d{4}$', - max_length=None, min_length=None, - error_message=ugettext('Enter a 4 digit post code.'), - *args, **kwargs) + max_length=None, min_length=None, *args, **kwargs) class AUPhoneNumberField(Field): """Australian phone number field.""" + default_error_messages = { + 'invalid': u'Phone numbers must contain 10 digits.', + } + def clean(self, value): """ Validate a phone number. Strips parentheses, whitespace and hyphens. @@ -31,7 +37,7 @@ class AUPhoneNumberField(Field): phone_match = PHONE_DIGITS_RE.search(value) if phone_match: return u'%s' % phone_match.group(1) - raise ValidationError(u'Phone numbers must contain 10 digits.') + raise ValidationError(self.error_messages['invalid']) class AUStateSelect(Select): """ diff --git a/django/contrib/localflavor/br/forms.py b/django/contrib/localflavor/br/forms.py index 58bf795292..aa7e3b23fd 100644 --- a/django/contrib/localflavor/br/forms.py +++ b/django/contrib/localflavor/br/forms.py @@ -17,13 +17,19 @@ except NameError: phone_digits_re = re.compile(r'^(\d{2})[-\.]?(\d{4})[-\.]?(\d{4})$') class BRZipCodeField(RegexField): + default_error_messages = { + 'invalid': _('Enter a zip code in the format XXXXX-XXX.'), + } + def __init__(self, *args, **kwargs): super(BRZipCodeField, self).__init__(r'^\d{5}-\d{3}$', - max_length=None, min_length=None, - error_message=_('Enter a zip code in the format XXXXX-XXX.'), - *args, **kwargs) + max_length=None, min_length=None, *args, **kwargs) class BRPhoneNumberField(Field): + default_error_messages = { + 'invalid': _('Phone numbers must be in XX-XXXX-XXXX format.'), + } + def clean(self, value): super(BRPhoneNumberField, self).clean(value) if value in EMPTY_VALUES: @@ -32,7 +38,7 @@ class BRPhoneNumberField(Field): m = phone_digits_re.search(value) if m: return u'%s-%s-%s' % (m.group(1), m.group(2), m.group(3)) - raise ValidationError(_('Phone numbers must be in XX-XXXX-XXXX format.')) + raise ValidationError(self.error_messages['invalid']) class BRStateSelect(Select): """ @@ -48,6 +54,9 @@ class BRStateChoiceField(Field): A choice field that uses a list of Brazilian states as its choices. """ widget = Select + default_error_messages = { + 'invalid': _(u'Select a valid brazilian state. That state is not one of the available states.'), + } def __init__(self, required=True, widget=None, label=None, initial=None, help_text=None): @@ -65,9 +74,7 @@ class BRStateChoiceField(Field): return value valid_values = set([smart_unicode(k) for k, v in self.widget.choices]) if value not in valid_values: - raise ValidationError(_(u'Select a valid brazilian state.' - u' That state is not one' - u' of the available states.')) + raise ValidationError(self.error_messages['invalid']) return value def DV_maker(v): @@ -83,6 +90,12 @@ class BRCPFField(CharField): More information: http://en.wikipedia.org/wiki/Cadastro_de_Pessoas_F%C3%ADsicas """ + default_error_messages = { + 'invalid': _("Invalid CPF number."), + 'max_digits': _("This field requires at most 11 digits or 14 characters."), + 'digits_only': _("This field requires only numbers."), + } + def __init__(self, *args, **kwargs): super(BRCPFField, self).__init__(max_length=14, min_length=11, *args, **kwargs) @@ -100,9 +113,9 @@ class BRCPFField(CharField): try: int(value) except ValueError: - raise ValidationError(_("This field requires only numbers.")) + raise ValidationError(self.error_messages['digits_only']) if len(value) != 11: - raise ValidationError(_("This field requires at most 11 digits or 14 characters.")) + raise ValidationError(self.error_messages['max_digits']) orig_dv = value[-2:] new_1dv = sum([i * int(value[idx]) for idx, i in enumerate(range(10, 1, -1))]) @@ -112,11 +125,17 @@ class BRCPFField(CharField): new_2dv = DV_maker(new_2dv % 11) value = value[:-1] + str(new_2dv) if value[-2:] != orig_dv: - raise ValidationError(_("Invalid CPF number.")) + raise ValidationError(self.error_messages['invalid']) return orig_value class BRCNPJField(Field): + default_error_messages = { + 'invalid': _("Invalid CNPJ number."), + 'digits_only': _("This field requires only numbers."), + 'max_digits': _("This field requires at least 14 digits"), + } + def clean(self, value): """ Value can be either a string in the format XX.XXX.XXX/XXXX-XX or a @@ -131,10 +150,9 @@ class BRCNPJField(Field): try: int(value) except ValueError: - raise ValidationError("This field requires only numbers.") + raise ValidationError(self.error_messages['digits_only']) if len(value) != 14: - raise ValidationError( - _("This field requires at least 14 digits")) + raise ValidationError(self.error_messages['max_digits']) orig_dv = value[-2:] new_1dv = sum([i * int(value[idx]) for idx, i in enumerate(range(5, 1, -1) + range(9, 1, -1))]) @@ -144,7 +162,6 @@ class BRCNPJField(Field): new_2dv = DV_maker(new_2dv % 11) value = value[:-1] + str(new_2dv) if value[-2:] != orig_dv: - raise ValidationError(_("Invalid CNPJ number.")) + raise ValidationError(self.error_messages['invalid']) return orig_value - diff --git a/django/contrib/localflavor/ca/forms.py b/django/contrib/localflavor/ca/forms.py index 98f65a5c6c..daeff58acf 100644 --- a/django/contrib/localflavor/ca/forms.py +++ b/django/contrib/localflavor/ca/forms.py @@ -1,37 +1,43 @@ -""" -Canada-specific Form helpers -""" - -from django.newforms import ValidationError -from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES -from django.newforms.util import smart_unicode +""" +Canada-specific Form helpers +""" + +from django.newforms import ValidationError +from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES +from django.newforms.util import smart_unicode from django.utils.translation import gettext, ugettext -import re - +import re + phone_digits_re = re.compile(r'^(?:1-?)?(\d{3})[-\.]?(\d{3})[-\.]?(\d{4})$') sin_re = re.compile(r"^(\d{3})-(\d{3})-(\d{3})$") - -class CAPostalCodeField(RegexField): - """Canadian postal code field.""" - def __init__(self, *args, **kwargs): - super(CAPostalCodeField, self).__init__(r'^[ABCEGHJKLMNPRSTVXYZ]\d[A-Z] \d[A-Z]\d$', - max_length=None, min_length=None, - error_message=gettext(u'Enter a postal code in the format XXX XXX.'), - *args, **kwargs) - -class CAPhoneNumberField(Field): - """Canadian phone number field.""" - def clean(self, value): - """Validate a phone number. - """ - super(CAPhoneNumberField, self).clean(value) + +class CAPostalCodeField(RegexField): + """Canadian postal code field.""" + default_error_messages = { + 'invalid': gettext(u'Enter a postal code in the format XXX XXX.'), + } + + def __init__(self, *args, **kwargs): + super(CAPostalCodeField, self).__init__(r'^[ABCEGHJKLMNPRSTVXYZ]\d[A-Z] \d[A-Z]\d$', + max_length=None, min_length=None, *args, **kwargs) + +class CAPhoneNumberField(Field): + """Canadian phone number field.""" + default_error_messages = { + 'invalid': u'Phone numbers must be in XXX-XXX-XXXX format.', + } + + def clean(self, value): + """Validate a phone number. + """ + super(CAPhoneNumberField, self).clean(value) if value in EMPTY_VALUES: return u'' value = re.sub('(\(|\)|\s+)', '', smart_unicode(value)) m = phone_digits_re.search(value) if m: return u'%s-%s-%s' % (m.group(1), m.group(2), m.group(3)) - raise ValidationError(u'Phone numbers must be in XXX-XXX-XXXX format.') + raise ValidationError(self.error_messages['invalid']) class CAProvinceField(Field): """ @@ -39,6 +45,10 @@ class CAProvinceField(Field): It normalizes the input to the standard two-leter postal service abbreviation for the given province. """ + default_error_messages = { + 'invalid': u'Enter a Canadian province or territory.', + } + def clean(self, value): from ca_provinces import PROVINCES_NORMALIZED super(CAProvinceField, self).clean(value) @@ -53,17 +63,17 @@ class CAProvinceField(Field): return PROVINCES_NORMALIZED[value.strip().lower()].decode('ascii') except KeyError: pass - raise ValidationError(u'Enter a Canadian province or territory.') - -class CAProvinceSelect(Select): - """ - A Select widget that uses a list of Canadian provinces and - territories as its choices. - """ - def __init__(self, attrs=None): - from ca_provinces import PROVINCE_CHOICES # relative import + raise ValidationError(self.error_messages['invalid']) + +class CAProvinceSelect(Select): + """ + A Select widget that uses a list of Canadian provinces and + territories as its choices. + """ + def __init__(self, attrs=None): + from ca_provinces import PROVINCE_CHOICES # relative import super(CAProvinceSelect, self).__init__(attrs, choices=PROVINCE_CHOICES) - + class CASocialInsuranceNumberField(Field): """ A Canadian Social Insurance Number (SIN). @@ -74,24 +84,28 @@ class CASocialInsuranceNumberField(Field): * Passes the check digit process "Luhn Algorithm" See: http://en.wikipedia.org/wiki/Social_Insurance_Number """ + default_error_messages = { + 'invalid': ugettext('Enter a valid Canadian Social Insurance number in XXX-XXX-XXXX format.'), + } + def clean(self, value): super(CASocialInsuranceNumberField, self).clean(value) if value in EMPTY_VALUES: return u'' - msg = ugettext('Enter a valid Canadian Social Insurance number in XXX-XXX-XXXX format.') + match = re.match(sin_re, value) if not match: - raise ValidationError(msg) - - number = u'%s-%s-%s' % (match.group(1), match.group(2), match.group(3)) + raise ValidationError(self.error_messages['invalid']) + + number = u'%s-%s-%s' % (match.group(1), match.group(2), match.group(3)) check_number = u'%s%s%s' % (match.group(1), match.group(2), match.group(3)) if not self.luhn_checksum_is_valid(check_number): - raise ValidationError(msg) + raise ValidationError(self.error_messages['invalid']) return number - + def luhn_checksum_is_valid(self, number): """ - Checks to make sure that the SIN passes a luhn mod-10 checksum + Checks to make sure that the SIN passes a luhn mod-10 checksum See: http://en.wikipedia.org/wiki/Luhn_algorithm """ @@ -109,4 +123,4 @@ class CASocialInsuranceNumberField(Field): sum = sum + digit - return ( (sum % 10) == 0 ) \ No newline at end of file + return ( (sum % 10) == 0 ) diff --git a/django/contrib/localflavor/ch/forms.py b/django/contrib/localflavor/ch/forms.py index 5e601f5d74..2158fba697 100644 --- a/django/contrib/localflavor/ch/forms.py +++ b/django/contrib/localflavor/ch/forms.py @@ -12,11 +12,13 @@ id_re = re.compile(r"^(?P\w{8})(?P(\d{1}|<))(?P\d{1})$ phone_digits_re = re.compile(r'^0([1-9]{1})\d{8}$') class CHZipCodeField(RegexField): + default_error_messages = { + 'invalid': ugettext('Enter a zip code in the format XXXX.'), + } + def __init__(self, *args, **kwargs): super(CHZipCodeField, self).__init__(r'^\d{4}$', - max_length=None, min_length=None, - error_message=ugettext('Enter a zip code in the format XXXX.'), - *args, **kwargs) + max_length=None, min_length=None, *args, **kwargs) class CHPhoneNumberField(Field): """ @@ -25,6 +27,10 @@ class CHPhoneNumberField(Field): '0XX.XXX.XX.XX' and '0XXXXXXXXX' validate but are corrected to '0XX XXX XX XX'. """ + default_error_messages = { + 'invalid': 'Phone numbers must be in 0XX XXX XX XX format.', + } + def clean(self, value): super(CHPhoneNumberField, self).clean(value) if value in EMPTY_VALUES: @@ -33,7 +39,7 @@ class CHPhoneNumberField(Field): m = phone_digits_re.search(value) if m: return u'%s %s %s %s' % (value[0:3], value[3:6], value[6:8], value[8:10]) - raise ValidationError('Phone numbers must be in 0XX XXX XX XX format.') + raise ValidationError(self.error_messages['invalid']) class CHStateSelect(Select): """ @@ -54,6 +60,10 @@ class CHIdentityCardNumberField(Field): Algorithm is documented at http://adi.kousz.ch/artikel/IDCHE.htm """ + default_error_messages = { + 'invalid': ugettext('Enter a valid Swiss identity or passport card number in X1234567<0 or 1234567890 format.'), + } + def has_valid_checksum(self, number): given_number, given_checksum = number[:-1], number[-1] new_number = given_number @@ -87,23 +97,22 @@ class CHIdentityCardNumberField(Field): def clean(self, value): super(CHIdentityCardNumberField, self).clean(value) - error_msg = ugettext('Enter a valid Swiss identity or passport card number in X1234567<0 or 1234567890 format.') if value in EMPTY_VALUES: return u'' match = re.match(id_re, value) if not match: - raise ValidationError(error_msg) + raise ValidationError(self.error_messages['invalid']) idnumber, pos9, checksum = match.groupdict()['idnumber'], match.groupdict()['pos9'], match.groupdict()['checksum'] if idnumber == '00000000' or \ idnumber == 'A0000000': - raise ValidationError(error_msg) + raise ValidationError(self.error_messages['invalid']) all_digits = "%s%s%s" % (idnumber, pos9, checksum) if not self.has_valid_checksum(all_digits): - raise ValidationError(error_msg) + raise ValidationError(self.error_messages['invalid']) return u'%s%s%s' % (idnumber, pos9, checksum) diff --git a/django/contrib/localflavor/cl/forms.py b/django/contrib/localflavor/cl/forms.py index d2d37a3712..2f5ac29b39 100644 --- a/django/contrib/localflavor/cl/forms.py +++ b/django/contrib/localflavor/cl/forms.py @@ -25,16 +25,21 @@ class CLRutField(RegexField): Samples for testing are available from https://palena.sii.cl/cvc/dte/ee_empresas_emisoras.html """ + default_error_messages = { + 'invalid': ugettext('Enter a valid Chilean RUT.'), + 'strict': ugettext('Enter a valid Chilean RUT. The format is XX.XXX.XXX-X.'), + 'checksum': ugettext('The Chilean RUT is not valid.'), + } + def __init__(self, *args, **kwargs): if 'strict' in kwargs: del kwargs['strict'] super(CLRutField, self).__init__(r'^(\d{1,2}\.)?\d{3}\.\d{3}-[\dkK]$', - error_message=ugettext('Enter valid a Chilean RUT. The format is XX.XXX.XXX-X.'), - *args, **kwargs) + error_message=self.default_error_messages['strict'], *args, **kwargs) else: # In non-strict mode, accept RUTs that validate but do not exist in # the real world. - super(CLRutField, self).__init__(r'^[\d\.]{1,11}-?[\dkK]$', error_message=ugettext('Enter valid a Chilean RUT'), *args, **kwargs) + super(CLRutField, self).__init__(r'^[\d\.]{1,11}-?[\dkK]$', *args, **kwargs) def clean(self, value): """ @@ -47,7 +52,7 @@ class CLRutField(RegexField): if self._algorithm(rut) == verificador: return self._format(rut, verificador) else: - raise ValidationError(u'The Chilean RUT is not valid.') + raise ValidationError(self.error_messages['checksum']) def _algorithm(self, rut): """ diff --git a/django/contrib/localflavor/de/forms.py b/django/contrib/localflavor/de/forms.py index 1a0b8587a6..76ce215dca 100644 --- a/django/contrib/localflavor/de/forms.py +++ b/django/contrib/localflavor/de/forms.py @@ -10,11 +10,12 @@ import re id_re = re.compile(r"^(?P\d{10})(?P\w{1,3})[-\ ]?(?P\d{7})[-\ ]?(?P\d{7})[-\ ]?(?P\d{1})$") class DEZipCodeField(RegexField): + default_error_messages = { + 'invalid': ugettext('Enter a zip code in the format XXXXX.'), + } def __init__(self, *args, **kwargs): super(DEZipCodeField, self).__init__(r'^\d{5}$', - max_length=None, min_length=None, - error_message=ugettext('Enter a zip code in the format XXXXX.'), - *args, **kwargs) + max_length=None, min_length=None, *args, **kwargs) class DEStateSelect(Select): """ @@ -36,6 +37,10 @@ class DEIdentityCardNumberField(Field): Algorithm is documented at http://de.wikipedia.org/wiki/Personalausweis """ + default_error_messages = { + 'invalid': ugettext('Enter a valid German identity card number in XXXXXXXXXXX-XXXXXXX-XXXXXXX-X format.'), + } + def has_valid_checksum(self, number): given_number, given_checksum = number[:-1], number[-1] calculated_checksum = 0 @@ -57,23 +62,22 @@ class DEIdentityCardNumberField(Field): def clean(self, value): super(DEIdentityCardNumberField, self).clean(value) - error_msg = ugettext('Enter a valid German identity card number in XXXXXXXXXXX-XXXXXXX-XXXXXXX-X format.') if value in EMPTY_VALUES: return u'' match = re.match(id_re, value) if not match: - raise ValidationError(error_msg) + raise ValidationError(self.error_messages['invalid']) gd = match.groupdict() residence, origin = gd['residence'], gd['origin'] birthday, validity, checksum = gd['birthday'], gd['validity'], gd['checksum'] if residence == '0000000000' or birthday == '0000000' or validity == '0000000': - raise ValidationError(error_msg) + raise ValidationError(self.error_messages['invalid']) all_digits = u"%s%s%s%s" % (residence, birthday, validity, checksum) if not self.has_valid_checksum(residence) or not self.has_valid_checksum(birthday) or \ not self.has_valid_checksum(validity) or not self.has_valid_checksum(all_digits): - raise ValidationError(error_msg) + raise ValidationError(self.error_messages['invalid']) return u'%s%s-%s-%s-%s' % (residence, origin, birthday, validity, checksum) diff --git a/django/contrib/localflavor/es/forms.py b/django/contrib/localflavor/es/forms.py index 29b41828f6..81186f8b3d 100644 --- a/django/contrib/localflavor/es/forms.py +++ b/django/contrib/localflavor/es/forms.py @@ -15,12 +15,14 @@ class ESPostalCodeField(RegexField): Spanish postal code is a five digits string, with two first digits between 01 and 52, assigned to provinces code. """ + default_error_messages = { + 'invalid': _('Enter a valid postal code in the range and format 01XXX - 52XXX.'), + } + def __init__(self, *args, **kwargs): super(ESPostalCodeField, self).__init__( r'^(0[1-9]|[1-4][0-9]|5[0-2])\d{3}$', - max_length=None, min_length=None, - error_message=_('Enter a valid postal code in the range and format 01XXX - 52XXX.'), - *args, **kwargs) + max_length=None, min_length=None, *args, **kwargs) class ESPhoneNumberField(RegexField): """ @@ -33,11 +35,13 @@ class ESPhoneNumberField(RegexField): TODO: accept and strip characters like dot, hyphen... in phone number """ + default_error_messages = { + 'invalid': _('Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.'), + } + def __init__(self, *args, **kwargs): super(ESPhoneNumberField, self).__init__(r'^(6|8|9)\d{8}$', - max_length=None, min_length=None, - error_message=_('Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.'), - *args, **kwargs) + max_length=None, min_length=None, *args, **kwargs) class ESIdentityCardNumberField(RegexField): """ @@ -58,19 +62,23 @@ class ESIdentityCardNumberField(RegexField): public, and different authors have different opinions on which ones allows letters, so both validations are assumed true for all types. """ + default_error_messages = { + 'invalid': _('Please enter a valid NIF, NIE, or CIF.'), + 'invalid_only_nif': _('Please enter a valid NIF or NIE.'), + 'invalid_nif': _('Invalid checksum for NIF.'), + 'invalid_nie': _('Invalid checksum for NIE.'), + 'invalid_cif': _('Invalid checksum for CIF.'), + } + def __init__(self, only_nif=False, *args, **kwargs): self.only_nif = only_nif self.nif_control = 'TRWAGMYFPDXBNJZSQVHLCKE' self.cif_control = 'JABCDEFGHI' self.cif_types = 'ABCDEFGHKLMNPQS' self.nie_types = 'XT' - if self.only_nif: - self.id_types = 'NIF or NIE' - else: - self.id_types = 'NIF, NIE, or CIF' super(ESIdentityCardNumberField, self).__init__(r'^([%s]?)[ -]?(\d+)[ -]?([%s]?)$' % (self.cif_types + self.nie_types + self.cif_types.lower() + self.nie_types.lower(), self.nif_control + self.nif_control.lower()), max_length=None, min_length=None, - error_message=_('Please enter a valid %s.' % self.id_types), + error_message=self.default_error_messages['invalid%s' % (self.only_nif and '_only_nif' or '')], *args, **kwargs) def clean(self, value): @@ -88,13 +96,13 @@ class ESIdentityCardNumberField(RegexField): if letter2 == nif_get_checksum(number): return value else: - raise ValidationError, _('Invalid checksum for NIF.') + raise ValidationError, self.error_messages['invalid_nif'] elif letter1 in self.nie_types and letter2: # NIE if letter2 == nif_get_checksum(number): return value else: - raise ValidationError, _('Invalid checksum for NIE.') + raise ValidationError, self.error_messages['invalid_nie'] elif not self.only_nif and letter1 in self.cif_types and len(number) in [7, 8]: # CIF if not letter2: @@ -103,9 +111,9 @@ class ESIdentityCardNumberField(RegexField): if letter2 in [checksum, self.cif_control[checksum]]: return value else: - raise ValidationError, _('Invalid checksum for CIF.') + raise ValidationError, self.error_messages['invalid_cif'] else: - raise ValidationError, _('Please enter a valid %s.' % self.id_types) + raise ValidationError, self.error_messages['invalid'] class ESCCCField(RegexField): """ @@ -130,11 +138,14 @@ class ESCCCField(RegexField): TODO: allow IBAN validation too """ + default_error_messages = { + 'invalid': _('Please enter a valid bank account number in format XXXX-XXXX-XX-XXXXXXXXXX.'), + 'checksum': _('Invalid checksum for bank account number.'), + } + def __init__(self, *args, **kwargs): super(ESCCCField, self).__init__(r'^\d{4}[ -]?\d{4}[ -]?\d{2}[ -]?\d{10}$', - max_length=None, min_length=None, - error_message=_('Please enter a valid bank account number in format XXXX-XXXX-XX-XXXXXXXXXX.'), - *args, **kwargs) + max_length=None, min_length=None, *args, **kwargs) def clean(self, value): super(ESCCCField, self).clean(value) @@ -147,7 +158,7 @@ class ESCCCField(RegexField): if get_checksum('00' + entity + office) + get_checksum(account) == checksum: return value else: - raise ValidationError, _('Invalid checksum for bank account number.') + raise ValidationError, self.error_messages['checksum'] class ESRegionSelect(Select): """ diff --git a/django/contrib/localflavor/fi/forms.py b/django/contrib/localflavor/fi/forms.py index 82e024f7b1..a0274da44c 100644 --- a/django/contrib/localflavor/fi/forms.py +++ b/django/contrib/localflavor/fi/forms.py @@ -8,11 +8,12 @@ from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES from django.utils.translation import ugettext class FIZipCodeField(RegexField): + default_error_messages = { + 'invalid': ugettext('Enter a zip code in the format XXXXX.'), + } def __init__(self, *args, **kwargs): super(FIZipCodeField, self).__init__(r'^\d{5}$', - max_length=None, min_length=None, - error_message=ugettext('Enter a zip code in the format XXXXX.'), - *args, **kwargs) + max_length=None, min_length=None, *args, **kwargs) class FIMunicipalitySelect(Select): """ @@ -23,6 +24,10 @@ class FIMunicipalitySelect(Select): super(FIMunicipalitySelect, self).__init__(attrs, choices=MUNICIPALITY_CHOICES) class FISocialSecurityNumber(Field): + default_error_messages = { + 'invalid': ugettext('Enter a valid Finnish social security number.'), + } + def clean(self, value): super(FISocialSecurityNumber, self).clean(value) if value in EMPTY_VALUES: @@ -37,9 +42,9 @@ class FISocialSecurityNumber(Field): (?P(\d{3})) (?P[%s])$""" % checkmarks, value, re.VERBOSE | re.IGNORECASE) if not result: - raise ValidationError(ugettext('Enter a valid Finnish social security number.')) + raise ValidationError(self.error_messages['invalid']) gd = result.groupdict() checksum = int(gd['date'] + gd['serial']) if checkmarks[checksum % len(checkmarks)] == gd['checksum'].upper(): return u'%s' % value.upper() - raise ValidationError(ugettext('Enter a valid Finnish social security number.')) + raise ValidationError(self.error_messages['invalid']) diff --git a/django/contrib/localflavor/fr/forms.py b/django/contrib/localflavor/fr/forms.py index cc0a5db259..635b62edd5 100644 --- a/django/contrib/localflavor/fr/forms.py +++ b/django/contrib/localflavor/fr/forms.py @@ -11,11 +11,13 @@ import re phone_digits_re = re.compile(r'^0\d(\s|\.)?(\d{2}(\s|\.)?){3}\d{2}$') class FRZipCodeField(RegexField): + default_error_messages = { + 'invalid': ugettext('Enter a zip code in the format XXXXX.'), + } + def __init__(self, *args, **kwargs): super(FRZipCodeField, self).__init__(r'^\d{5}$', - max_length=None, min_length=None, - error_message=ugettext('Enter a zip code in the format XXXXX.'), - *args, **kwargs) + max_length=None, min_length=None, *args, **kwargs) class FRPhoneNumberField(Field): """ @@ -24,6 +26,10 @@ class FRPhoneNumberField(Field): '0X.XX.XX.XX.XX' and '0XXXXXXXXX' validate but are corrected to '0X XX XX XX XX'. """ + default_error_messages = { + 'invalid': u'Phone numbers must be in 0X XX XX XX XX format.', + } + def clean(self, value): super(FRPhoneNumberField, self).clean(value) if value in EMPTY_VALUES: @@ -32,7 +38,7 @@ class FRPhoneNumberField(Field): m = phone_digits_re.search(value) if m: return u'%s %s %s %s %s' % (value[0:2], value[2:4], value[4:6], value[6:8], value[8:10]) - raise ValidationError(u'Phone numbers must be in 0X XX XX XX XX format.') + raise ValidationError(self.error_messages['invalid']) class FRDepartmentSelect(Select): """ diff --git a/django/contrib/localflavor/in_/forms.py b/django/contrib/localflavor/in_/forms.py index 5febc40e67..1cb303d772 100644 --- a/django/contrib/localflavor/in_/forms.py +++ b/django/contrib/localflavor/in_/forms.py @@ -10,11 +10,13 @@ import re class INZipCodeField(RegexField): + default_error_messages = { + 'invalid': gettext(u'Enter a zip code in the format XXXXXXX.'), + } + def __init__(self, *args, **kwargs): super(INZipCodeField, self).__init__(r'^\d{6}$', - max_length=None, min_length=None, - error_message=gettext(u'Enter a zip code in the format XXXXXXX.'), - *args, **kwargs) + max_length=None, min_length=None, *args, **kwargs) class INStateField(Field): """ @@ -22,6 +24,10 @@ class INStateField(Field): abbreviation. It normalizes the input to the standard two-letter vehicle registration abbreviation for the given state or union territory """ + default_error_messages = { + 'invalid': u'Enter a Indian state or territory.', + } + def clean(self, value): from in_states import STATES_NORMALIZED super(INStateField, self).clean(value) @@ -36,7 +42,7 @@ class INStateField(Field): return smart_unicode(STATES_NORMALIZED[value.strip().lower()]) except KeyError: pass - raise ValidationError(u'Enter a Indian state or territory.') + raise ValidationError(self.error_messages['invalid']) class INStateSelect(Select): """ diff --git a/django/contrib/localflavor/is_/forms.py b/django/contrib/localflavor/is_/forms.py index ef8799e29c..ea7a1e00c3 100644 --- a/django/contrib/localflavor/is_/forms.py +++ b/django/contrib/localflavor/is_/forms.py @@ -13,10 +13,14 @@ class ISIdNumberField(RegexField): Icelandic identification number (kennitala). This is a number every citizen of Iceland has. """ + default_error_messages = { + 'invalid': ugettext('Enter a valid Icelandic identification number. The format is XXXXXX-XXXX.'), + 'checksum': ugettext(u'The Icelandic identification number is not valid.'), + } + def __init__(self, *args, **kwargs): - error_msg = ugettext('Enter a valid Icelandic identification number. The format is XXXXXX-XXXX.') kwargs['min_length'],kwargs['max_length'] = 10,11 - super(ISIdNumberField, self).__init__(r'^\d{6}(-| )?\d{4}$', error_message=error_msg, *args, **kwargs) + super(ISIdNumberField, self).__init__(r'^\d{6}(-| )?\d{4}$', *args, **kwargs) def clean(self, value): value = super(ISIdNumberField, self).clean(value) @@ -28,7 +32,7 @@ class ISIdNumberField(RegexField): if self._validate(value): return self._format(value) else: - raise ValidationError(ugettext(u'The Icelandic identification number is not valid.')) + raise ValidationError(self.error_messages['checksum']) def _canonify(self, value): """ diff --git a/django/contrib/localflavor/it/forms.py b/django/contrib/localflavor/it/forms.py index 0b6eecec45..88e65734e4 100644 --- a/django/contrib/localflavor/it/forms.py +++ b/django/contrib/localflavor/it/forms.py @@ -10,11 +10,12 @@ from django.contrib.localflavor.it.util import ssn_check_digit, vat_number_check import re class ITZipCodeField(RegexField): + default_error_messages = { + 'invalid': ugettext('Enter a valid zip code.'), + } def __init__(self, *args, **kwargs): super(ITZipCodeField, self).__init__(r'^\d{5}$', - max_length=None, min_length=None, - error_message=ugettext('Enter a valid zip code.'), - *args, **kwargs) + max_length=None, min_length=None, *args, **kwargs) class ITRegionSelect(Select): """ @@ -38,11 +39,13 @@ class ITSocialSecurityNumberField(RegexField): For reference see http://www.agenziaentrate.it/ and search for 'Informazioni sulla codificazione delle persone fisiche'. """ - err_msg = ugettext(u'Enter a valid Social Security number.') + default_error_messages = { + 'invalid': ugettext(u'Enter a valid Social Security number.'), + } + def __init__(self, *args, **kwargs): super(ITSocialSecurityNumberField, self).__init__(r'^\w{3}\s*\w{3}\s*\w{5}\s*\w{5}$', - max_length=None, min_length=None, error_message=self.err_msg, - *args, **kwargs) + max_length=None, min_length=None, *args, **kwargs) def clean(self, value): value = super(ITSocialSecurityNumberField, self).clean(value) @@ -52,26 +55,29 @@ class ITSocialSecurityNumberField(RegexField): try: check_digit = ssn_check_digit(value) except ValueError: - raise ValidationError(self.err_msg) + raise ValidationError(self.error_messages['invalid']) if not value[15] == check_digit: - raise ValidationError(self.err_msg) + raise ValidationError(self.error_messages['invalid']) return value class ITVatNumberField(Field): """ A form field that validates Italian VAT numbers (partita IVA). """ + default_error_messages = { + 'invalid': ugettext(u'Enter a valid VAT number.'), + } + def clean(self, value): value = super(ITVatNumberField, self).clean(value) if value == u'': return value - err_msg = ugettext(u'Enter a valid VAT number.') try: vat_number = int(value) except ValueError: - raise ValidationError(err_msg) + raise ValidationError(self.error_messages['invalid']) vat_number = str(vat_number).zfill(11) check_digit = vat_number_check_digit(vat_number[0:10]) if not vat_number[10] == check_digit: - raise ValidationError(err_msg) + raise ValidationError(self.error_messages['invalid']) return smart_unicode(vat_number) diff --git a/django/contrib/localflavor/jp/forms.py b/django/contrib/localflavor/jp/forms.py index 682ffb8c31..13083aab6e 100644 --- a/django/contrib/localflavor/jp/forms.py +++ b/django/contrib/localflavor/jp/forms.py @@ -15,11 +15,13 @@ class JPPostalCodeField(RegexField): Accepts 7 digits, with or without a hyphen. """ + default_error_messages = { + 'invalid': ugettext('Enter a postal code in the format XXXXXXX or XXX-XXXX.'), + } + def __init__(self, *args, **kwargs): super(JPPostalCodeField, self).__init__(r'^\d{3}-\d{4}$|^\d{7}$', - max_length=None, min_length=None, - error_message=ugettext('Enter a postal code in the format XXXXXXX or XXX-XXXX.'), - *args, **kwargs) + max_length=None, min_length=None, *args, **kwargs) def clean(self, value): """ diff --git a/django/contrib/localflavor/nl/forms.py b/django/contrib/localflavor/nl/forms.py index e202ab0595..748484926e 100644 --- a/django/contrib/localflavor/nl/forms.py +++ b/django/contrib/localflavor/nl/forms.py @@ -17,24 +17,27 @@ class NLZipCodeField(Field): """ A Dutch postal code field. """ + default_error_messages = { + 'invalid': _('Enter a valid postal code'), + } + def clean(self, value): super(NLZipCodeField, self).clean(value) if value in EMPTY_VALUES: return u'' - - msg = _('Enter a valid postal code') + value = value.strip().upper().replace(' ', '') if not pc_re.search(value): - raise ValidationError(msg) - + raise ValidationError(self.error_messages['invalid']) + if int(value[:4]) < 1000: - raise ValidationError(msg) - + raise ValidationError(self.error_messages['invalid']) + return u'%s %s' % (value[:4], value[4:]) class NLProvinceSelect(Select): """ - A Select widget that uses a list of provinces of the Netherlands as its + A Select widget that uses a list of provinces of the Netherlands as its choices. """ def __init__(self, attrs=None): @@ -45,48 +48,53 @@ class NLPhoneNumberField(Field): """ A Dutch telephone number field. """ + default_error_messages = { + 'invalid': _('Enter a valid phone number'), + } + def clean(self, value): super(NLPhoneNumberField, self).clean(value) if value in EMPTY_VALUES: return u'' - - msg = _('Enter a valid phone number') + phone_nr = re.sub('[\-\s\(\)]', '', smart_unicode(value)) - + if len(phone_nr) == 10 and numeric_re.search(phone_nr): return value - + if phone_nr[:3] == '+31' and len(phone_nr) == 12 and \ numeric_re.search(phone_nr[3:]): return value - - raise ValidationError(msg) + + raise ValidationError(self.error_messages['invalid']) class NLSoFiNumberField(Field): """ A Dutch social security number (SoFi/BSN) field. - + http://nl.wikipedia.org/wiki/Sofinummer """ + default_error_messages = { + 'invalid': _('Enter a valid SoFi number'), + } + def clean(self, value): super(NLSoFiNumberField, self).clean(value) if value in EMPTY_VALUES: return u'' - - msg = _('Enter a valid SoFi number') - + if not sofi_re.search(value): - raise ValidationError(msg) - + raise ValidationError(self.error_messages['invalid']) + if int(value) == 0: - raise ValidationError(msg) - + raise ValidationError(self.error_messages['invalid']) + checksum = 0 for i in range(9, 1, -1): checksum += int(value[9-i]) * i checksum -= int(value[-1]) - + if checksum % 11 != 0: - raise ValidationError(msg) - + raise ValidationError(self.error_messages['invalid']) + return value diff --git a/django/contrib/localflavor/no/forms.py b/django/contrib/localflavor/no/forms.py index 2de3df14d8..8dc001955d 100644 --- a/django/contrib/localflavor/no/forms.py +++ b/django/contrib/localflavor/no/forms.py @@ -8,11 +8,13 @@ from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES from django.utils.translation import ugettext class NOZipCodeField(RegexField): + default_error_messages = { + 'invalid': ugettext('Enter a zip code in the format XXXX.'), + } + def __init__(self, *args, **kwargs): super(NOZipCodeField, self).__init__(r'^\d{4}$', - max_length=None, min_length=None, - error_message=ugettext('Enter a zip code in the format XXXX.'), - *args, **kwargs) + max_length=None, min_length=None, *args, **kwargs) class NOMunicipalitySelect(Select): """ @@ -27,14 +29,17 @@ class NOSocialSecurityNumber(Field): """ Algorithm is documented at http://no.wikipedia.org/wiki/Personnummer """ + default_error_messages = { + 'invalid': ugettext(u'Enter a valid Norwegian social security number.'), + } + def clean(self, value): super(NOSocialSecurityNumber, self).clean(value) if value in EMPTY_VALUES: return u'' - msg = ugettext(u'Enter a valid Norwegian social security number.') if not re.match(r'^\d{11}$', value): - raise ValidationError(msg) + raise ValidationError(self.error_messages['invalid']) day = int(value[:2]) month = int(value[2:4]) @@ -52,7 +57,7 @@ class NOSocialSecurityNumber(Field): if 900 <= inum < 1000 and year2 > 39: self.birthday = datetime.date(1900+year2, month, day) except ValueError: - raise ValidationError(msg) + raise ValidationError(self.error_messages['invalid']) sexnum = int(value[8]) if sexnum % 2 == 0: @@ -68,9 +73,9 @@ class NOSocialSecurityNumber(Field): return sum([(a * b) for (a, b) in zip(aval, bval)]) if multiply_reduce(digits, weight_1) % 11 != 0: - raise ValidationError(msg) + raise ValidationError(self.error_messages['invalid']) if multiply_reduce(digits, weight_2) % 11 != 0: - raise ValidationError(msg) + raise ValidationError(self.error_messages['invalid']) return value diff --git a/django/contrib/localflavor/pe/forms.py b/django/contrib/localflavor/pe/forms.py index b1ae215417..57c9d441f7 100644 --- a/django/contrib/localflavor/pe/forms.py +++ b/django/contrib/localflavor/pe/forms.py @@ -19,6 +19,11 @@ class PEDNIField(CharField): """ A field that validates `Documento Nacional de IdentidadŽ (DNI) numbers. """ + default_error_messages = { + 'invalid': ugettext("This field requires only numbers."), + 'max_digits': ugettext("This field requires 8 digits."), + } + def __init__(self, *args, **kwargs): super(PEDNIField, self).__init__(max_length=8, min_length=8, *args, **kwargs) @@ -31,9 +36,9 @@ class PEDNIField(CharField): if value in EMPTY_VALUES: return u'' if not value.isdigit(): - raise ValidationError(ugettext("This field requires only numbers.")) + raise ValidationError(self.error_messages['invalid']) if len(value) != 8: - raise ValidationError(ugettext("This field requires 8 digits.")) + raise ValidationError(self.error_messages['max_digits']) return value @@ -42,6 +47,11 @@ class PERUCField(RegexField): This field validates a RUC (Registro Unico de Contribuyentes). A RUC is of the form XXXXXXXXXXX. """ + default_error_messages = { + 'invalid': ugettext("This field requires only numbers."), + 'max_digits': ugettext("This field requires 11 digits."), + } + def __init__(self, *args, **kwargs): super(PERUCField, self).__init__(max_length=11, min_length=11, *args, **kwargs) @@ -54,8 +64,8 @@ class PERUCField(RegexField): if value in EMPTY_VALUES: return u'' if not value.isdigit(): - raise ValidationError(ugettext("This field requires only numbers.")) + raise ValidationError(self.error_messages['invalid']) if len(value) != 11: - raise ValidationError(ugettext("This field requires 11 digits.")) + raise ValidationError(self.error_messages['max_digits']) return value diff --git a/django/contrib/localflavor/pl/forms.py b/django/contrib/localflavor/pl/forms.py index 3d1c4234d3..b02dad7025 100644 --- a/django/contrib/localflavor/pl/forms.py +++ b/django/contrib/localflavor/pl/forms.py @@ -35,16 +35,19 @@ class PLNationalIdentificationNumberField(RegexField): The algorithm is documented at http://en.wikipedia.org/wiki/PESEL. """ + default_error_messages = { + 'invalid': _(u'National Identification Number consists of 11 digits.'), + 'checksum': _(u'Wrong checksum for the National Identification Number.'), + } def __init__(self, *args, **kwargs): super(PLNationalIdentificationNumberField, self).__init__(r'^\d{11}$', - max_length=None, min_length=None, error_message=_(u'National Identification Number consists of 11 digits.'), - *args, **kwargs) + max_length=None, min_length=None, *args, **kwargs) def clean(self,value): super(PLNationalIdentificationNumberField, self).clean(value) if not self.has_valid_checksum(value): - raise ValidationError(_(u'Wrong checksum for the National Identification Number.')) + raise ValidationError(self.error_messages['checksum']) return u'%s' % value def has_valid_checksum(self, number): @@ -65,17 +68,20 @@ class PLTaxNumberField(RegexField): Checksum algorithm based on documentation at http://wipos.p.lodz.pl/zylla/ut/nip-rego.html """ + default_error_messages = { + 'invalid': _(u'Enter a tax number field (NIP) in the format XXX-XXX-XX-XX or XX-XX-XXX-XXX.'), + 'checksum': _(u'Wrong checksum for the Tax Number (NIP).'), + } def __init__(self, *args, **kwargs): super(PLTaxNumberField, self).__init__(r'^\d{3}-\d{3}-\d{2}-\d{2}$|^\d{2}-\d{2}-\d{3}-\d{3}$', - max_length=None, min_length=None, - error_message=_(u'Enter a tax number field (NIP) in the format XXX-XXX-XX-XX or XX-XX-XXX-XXX.'), *args, **kwargs) + max_length=None, min_length=None, *args, **kwargs) def clean(self,value): super(PLTaxNumberField, self).clean(value) value = re.sub("[-]", "", value) if not self.has_valid_checksum(value): - raise ValidationError(_(u'Wrong checksum for the Tax Number (NIP).')) + raise ValidationError(self.error_messages['checksum']) return u'%s' % value def has_valid_checksum(self, number): @@ -102,15 +108,19 @@ class PLNationalBusinessRegisterField(RegexField): The checksum algorithm is documented at http://wipos.p.lodz.pl/zylla/ut/nip-rego.html """ + default_error_messages = { + 'invalid': _(u'National Business Register Number (REGON) consists of 7 or 9 digits.'), + 'checksum': _(u'Wrong checksum for the National Business Register Number (REGON).'), + } + def __init__(self, *args, **kwargs): super(PLNationalBusinessRegisterField, self).__init__(r'^\d{7,9}$', - max_length=None, min_length=None, error_message=_(u'National Business Register Number (REGON) consists of 7 or 9 digits.'), - *args, **kwargs) + max_length=None, min_length=None, *args, **kwargs) def clean(self,value): super(PLNationalBusinessRegisterField, self).clean(value) if not self.has_valid_checksum(value): - raise ValidationError(_(u'Wrong checksum for the National Business Register Number (REGON).')) + raise ValidationError(self.error_messages['checksum']) return u'%s' % value def has_valid_checksum(self, number): @@ -142,9 +152,10 @@ class PLPostalCodeField(RegexField): A form field that validates as Polish postal code. Valid code is XX-XXX where X is digit. """ + default_error_messages = { + 'invalid': _(u'Enter a postal code in the format XX-XXX.'), + } + def __init__(self, *args, **kwargs): super(PLPostalCodeField, self).__init__(r'^\d{2}-\d{3}$', - max_length=None, min_length=None, - error_message=_(u'Enter a postal code in the format XX-XXX.'), - *args, **kwargs) - + max_length=None, min_length=None, *args, **kwargs) diff --git a/django/contrib/localflavor/sk/forms.py b/django/contrib/localflavor/sk/forms.py index 0aa000a0a5..711d70fc2b 100644 --- a/django/contrib/localflavor/sk/forms.py +++ b/django/contrib/localflavor/sk/forms.py @@ -26,11 +26,13 @@ class SKPostalCodeField(RegexField): A form field that validates its input as Slovak postal code. Valid form is XXXXX or XXX XX, where X represents integer. """ + default_error_messages = { + 'invalid': ugettext(u'Enter a postal code in the format XXXXX or XXX XX.'), + } + def __init__(self, *args, **kwargs): super(SKPostalCodeField, self).__init__(r'^\d{5}$|^\d{3} \d{2}$', - max_length=None, min_length=None, - error_message=ugettext(u'Enter a postal code in the format XXXXX or XXX XX.'), - *args, **kwargs) + max_length=None, min_length=None, *args, **kwargs) def clean(self, value): """ diff --git a/django/contrib/localflavor/uk/forms.py b/django/contrib/localflavor/uk/forms.py index 2b162230de..52cd7ad232 100644 --- a/django/contrib/localflavor/uk/forms.py +++ b/django/contrib/localflavor/uk/forms.py @@ -2,21 +2,39 @@ UK-specific Form helpers """ -from django.newforms.fields import RegexField, Select +import re + +from django.newforms.fields import CharField, Select +from django.newforms import ValidationError from django.utils.translation import ugettext -class UKPostcodeField(RegexField): +class UKPostcodeField(CharField): """ A form field that validates its input is a UK postcode. The regular expression used is sourced from the schema for British Standard BS7666 address types: http://www.govtalk.gov.uk/gdsc/schemas/bs7666-v2-0.xsd + + The value is uppercased and a space added in the correct place, if required. """ - def __init__(self, *args, **kwargs): - super(UKPostcodeField, self).__init__(r'^(GIR 0AA|[A-PR-UWYZ]([0-9]{1,2}|([A-HIK-Y][0-9](|[0-9]|[ABEHMNPRVWXY]))|[0-9][A-HJKSTUW]) [0-9][ABD-HJLNP-UW-Z]{2})$', - max_length=None, min_length=None, - error_message=ugettext(u'Enter a postcode. A space is required between the two postcode parts.'), - *args, **kwargs) + default_error_messages = { + 'invalid': ugettext(u'Enter a valid postcode.'), + } + outcode_pattern = '[A-PR-UWYZ]([0-9]{1,2}|([A-HIK-Y][0-9](|[0-9]|[ABEHMNPRVWXY]))|[0-9][A-HJKSTUW])' + incode_pattern = '[0-9][ABD-HJLNP-UW-Z]{2}' + postcode_regex = re.compile(r'^(GIR 0AA|%s %s)$' % (outcode_pattern, incode_pattern)) + space_regex = re.compile(r' *(%s)$' % incode_pattern) + + def clean(self, value): + value = super(UKPostcodeField, self).clean(value) + if value == u'': + return value + postcode = value.upper().strip() + # Put a single space before the incode (second part). + postcode = self.space_regex.sub(r' \1', postcode) + if not self.postcode_regex.search(postcode): + raise ValidationError(self.default_error_messages['invalid']) + return postcode class UKCountySelect(Select): """ diff --git a/django/contrib/localflavor/us/forms.py b/django/contrib/localflavor/us/forms.py index 259a7f7058..d6aa684cba 100644 --- a/django/contrib/localflavor/us/forms.py +++ b/django/contrib/localflavor/us/forms.py @@ -12,13 +12,19 @@ phone_digits_re = re.compile(r'^(?:1-?)?(\d{3})[-\.]?(\d{3})[-\.]?(\d{4})$') ssn_re = re.compile(r"^(?P\d{3})[-\ ]?(?P\d{2})[-\ ]?(?P\d{4})$") class USZipCodeField(RegexField): + default_error_messages = { + 'invalid': ugettext('Enter a zip code in the format XXXXX or XXXXX-XXXX.'), + } + def __init__(self, *args, **kwargs): super(USZipCodeField, self).__init__(r'^\d{5}(?:-\d{4})?$', - max_length=None, min_length=None, - error_message=ugettext('Enter a zip code in the format XXXXX or XXXXX-XXXX.'), - *args, **kwargs) + max_length=None, min_length=None, *args, **kwargs) class USPhoneNumberField(Field): + default_error_messages = { + 'invalid': u'Phone numbers must be in XXX-XXX-XXXX format.', + } + def clean(self, value): super(USPhoneNumberField, self).clean(value) if value in EMPTY_VALUES: @@ -27,7 +33,7 @@ class USPhoneNumberField(Field): m = phone_digits_re.search(value) if m: return u'%s-%s-%s' % (m.group(1), m.group(2), m.group(3)) - raise ValidationError(u'Phone numbers must be in XXX-XXX-XXXX format.') + raise ValidationError(self.error_messages['invalid']) class USSocialSecurityNumberField(Field): """ @@ -44,28 +50,31 @@ class USSocialSecurityNumberField(Field): promotional use or distribution (e.g., the Woolworth's number or the 1962 promotional number). """ + default_error_messages = { + 'invalid': ugettext('Enter a valid U.S. Social Security number in XXX-XX-XXXX format.'), + } + def clean(self, value): super(USSocialSecurityNumberField, self).clean(value) if value in EMPTY_VALUES: return u'' - msg = ugettext('Enter a valid U.S. Social Security number in XXX-XX-XXXX format.') match = re.match(ssn_re, value) if not match: - raise ValidationError(msg) + raise ValidationError(self.error_messages['invalid']) area, group, serial = match.groupdict()['area'], match.groupdict()['group'], match.groupdict()['serial'] # First pass: no blocks of all zeroes. if area == '000' or \ group == '00' or \ serial == '0000': - raise ValidationError(msg) + raise ValidationError(self.error_messages['invalid']) # Second pass: promotional and otherwise permanently invalid numbers. if area == '666' or \ (area == '987' and group == '65' and 4320 <= int(serial) <= 4329) or \ value == '078-05-1120' or \ value == '219-09-9999': - raise ValidationError(msg) + raise ValidationError(self.error_messages['invalid']) return u'%s-%s-%s' % (area, group, serial) class USStateField(Field): @@ -74,6 +83,10 @@ class USStateField(Field): It normalizes the input to the standard two-leter postal service abbreviation for the given state. """ + default_error_messages = { + 'invalid': u'Enter a U.S. state or territory.', + } + def clean(self, value): from us_states import STATES_NORMALIZED super(USStateField, self).clean(value) @@ -88,7 +101,7 @@ class USStateField(Field): return STATES_NORMALIZED[value.strip().lower()].decode('ascii') except KeyError: pass - raise ValidationError(u'Enter a U.S. state or territory.') + raise ValidationError(self.error_messages['invalid']) class USStateSelect(Select): """ diff --git a/django/contrib/localflavor/za/forms.py b/django/contrib/localflavor/za/forms.py index b04b7cf6ab..b3613b507d 100644 --- a/django/contrib/localflavor/za/forms.py +++ b/django/contrib/localflavor/za/forms.py @@ -16,10 +16,9 @@ class ZAIDField(Field): using the Luhn checksum, and uses a simlistic (read: not entirely accurate) check for the birthdate """ - - def __init__(self, *args, **kwargs): - super(ZAIDField, self).__init__() - self.error_message = _(u'Enter a valid South African ID number') + default_error_messages = { + 'invalid': _(u'Enter a valid South African ID number'), + } def clean(self, value): # strip spaces and dashes @@ -31,9 +30,9 @@ class ZAIDField(Field): return u'' match = re.match(id_re, value) - + if not match: - raise ValidationError(self.error_message) + raise ValidationError(self.error_messages['invalid']) g = match.groupdict() @@ -43,15 +42,18 @@ class ZAIDField(Field): # There is no way to guess the century of a ZA ID number d = date(int(g['yy']) + 2000, int(g['mm']), int(g['dd'])) except ValueError: - raise ValidationError(self.error_message) + raise ValidationError(self.error_messages['invalid']) if not luhn(value): - raise ValidationError(self.error_message) + raise ValidationError(self.error_messages['invalid']) return value class ZAPostCodeField(RegexField): + default_error_messages = { + 'invalid': _(u'Enter a valid South African postal code'), + } + def __init__(self, *args, **kwargs): super(ZAPostCodeField, self).__init__(r'^\d{4}$', - max_length=None, min_length=None, - error_message=_(u'Enter a valid South African postal code')) + max_length=None, min_length=None) diff --git a/django/contrib/sessions/backends/file.py b/django/contrib/sessions/backends/file.py index 221db5cc60..a8c3c69b10 100644 --- a/django/contrib/sessions/backends/file.py +++ b/django/contrib/sessions/backends/file.py @@ -1,14 +1,23 @@ import os +import tempfile from django.conf import settings from django.contrib.sessions.backends.base import SessionBase -from django.core.exceptions import SuspiciousOperation +from django.core.exceptions import SuspiciousOperation, ImproperlyConfigured class SessionStore(SessionBase): """ Implements a file based session store. """ def __init__(self, session_key=None): - self.storage_path = settings.SESSION_FILE_PATH + self.storage_path = getattr(settings, "SESSION_FILE_PATH", tempfile.gettempdir()) + + # Make sure the storage path is valid. + if not os.path.isdir(self.storage_path): + raise ImproperlyConfigured("The session storage path %r doesn't exist. "\ + "Please set your SESSION_FILE_PATH setting "\ + "to an existing directory in which Django "\ + "can store session data." % self.storage_path) + self.file_prefix = settings.SESSION_COOKIE_NAME super(SessionStore, self).__init__(session_key) diff --git a/django/contrib/sessions/tests.py b/django/contrib/sessions/tests.py index 10bec9668b..b2c664ce7b 100644 --- a/django/contrib/sessions/tests.py +++ b/django/contrib/sessions/tests.py @@ -1,5 +1,6 @@ r""" +>>> from django.conf import settings >>> from django.contrib.sessions.backends.db import SessionStore as DatabaseSession >>> from django.contrib.sessions.backends.cache import SessionStore as CacheSession >>> from django.contrib.sessions.backends.file import SessionStore as FileSession @@ -39,6 +40,13 @@ True >>> file_session.exists(file_session.session_key) False +# Make sure the file backend checks for a good storage dir +>>> settings.SESSION_FILE_PATH = "/if/this/directory/exists/you/have/a/weird/computer" +>>> FileSession() +Traceback (innermost last): + ... +ImproperlyConfigured: The session storage path '/if/this/directory/exists/you/have/a/weird/computer' doesn't exist. Please set your SESSION_FILE_PATH setting to an existing directory in which Django can store session data. + >>> cache_session = CacheSession() >>> cache_session.modified False diff --git a/django/core/cache/backends/filebased.py b/django/core/cache/backends/filebased.py index 72cb877f6f..c1277bf20c 100644 --- a/django/core/cache/backends/filebased.py +++ b/django/core/cache/backends/filebased.py @@ -1,12 +1,12 @@ "File-based cache backend" +import md5 import os, time try: import cPickle as pickle except ImportError: import pickle from django.core.cache.backends.base import BaseCache -from django.utils.http import urlquote_plus class CacheClass(BaseCache): def __init__(self, dir, params): @@ -29,24 +29,10 @@ class CacheClass(BaseCache): self._createdir() def add(self, key, value, timeout=None): - fname = self._key_to_file(key) - if timeout is None: - timeout = self.default_timeout - try: - filelist = os.listdir(self._dir) - except (IOError, OSError): - self._createdir() - filelist = [] - if len(filelist) > self._max_entries: - self._cull(filelist) - if os.path.basename(fname) not in filelist: - try: - f = open(fname, 'wb') - now = time.time() - pickle.dump(now + timeout, f, 2) - pickle.dump(value, f, 2) - except (IOError, OSError): - pass + if self.has_key(key): + return None + + self.set(key, value, timeout) def get(self, key, default=None): fname = self._key_to_file(key) @@ -56,7 +42,7 @@ class CacheClass(BaseCache): now = time.time() if exp < now: f.close() - os.remove(fname) + self._delete(fname) else: return pickle.load(f) except (IOError, OSError, EOFError, pickle.PickleError): @@ -65,40 +51,74 @@ class CacheClass(BaseCache): def set(self, key, value, timeout=None): fname = self._key_to_file(key) + dirname = os.path.dirname(fname) + if timeout is None: timeout = self.default_timeout + + self._cull() + try: - filelist = os.listdir(self._dir) - except (IOError, OSError): - self._createdir() - filelist = [] - if len(filelist) > self._max_entries: - self._cull(filelist) - try: + if not os.path.exists(dirname): + os.makedirs(dirname) + f = open(fname, 'wb') now = time.time() - pickle.dump(now + timeout, f, 2) - pickle.dump(value, f, 2) + pickle.dump(now + timeout, f, pickle.HIGHEST_PROTOCOL) + pickle.dump(value, f, pickle.HIGHEST_PROTOCOL) except (IOError, OSError): pass def delete(self, key): try: - os.remove(self._key_to_file(key)) + self._delete(self._key_to_file(key)) + except (IOError, OSError): + pass + + def _delete(self, fname): + os.remove(fname) + try: + # Remove the 2 subdirs if they're empty + dirname = os.path.dirname(fname) + os.rmdir(dirname) + os.rmdir(os.path.dirname(dirname)) except (IOError, OSError): pass def has_key(self, key): - return os.path.exists(self._key_to_file(key)) + fname = self._key_to_file(key) + try: + f = open(fname, 'rb') + exp = pickle.load(f) + now = time.time() + if exp < now: + f.close() + self._delete(fname) + return False + else: + return True + except (IOError, OSError, EOFError, pickle.PickleError): + return False - def _cull(self, filelist): + def _cull(self): + if int(self._num_entries) < self._max_entries: + return + + try: + filelist = os.listdir(self._dir) + except (IOError, OSError): + return + if self._cull_frequency == 0: doomed = filelist else: - doomed = [k for (i, k) in enumerate(filelist) if i % self._cull_frequency == 0] - for fname in doomed: + doomed = [os.path.join(self._dir, k) for (i, k) in enumerate(filelist) if i % self._cull_frequency == 0] + + for topdir in doomed: try: - os.remove(os.path.join(self._dir, fname)) + for root, _, files in os.walk(topdir): + for f in files: + self._delete(os.path.join(root, f)) except (IOError, OSError): pass @@ -109,4 +129,22 @@ class CacheClass(BaseCache): raise EnvironmentError, "Cache directory '%s' does not exist and could not be created'" % self._dir def _key_to_file(self, key): - return os.path.join(self._dir, urlquote_plus(key)) + """ + Convert the filename into an md5 string. We'll turn the first couple + bits of the path into directory prefixes to be nice to filesystems + that have problems with large numbers of files in a directory. + + Thus, a cache key of "foo" gets turnned into a file named + ``{cache-dir}ac/bd/18db4cc2f85cedef654fccc4a4d8``. + """ + path = md5.new(key.encode('utf-8')).hexdigest() + path = os.path.join(path[:2], path[2:4], path[4:]) + return os.path.join(self._dir, path) + + def _get_num_entries(self): + count = 0 + for _,_,files in os.walk(self._dir): + count += len(files) + return count + _num_entries = property(_get_num_entries) + diff --git a/django/core/cache/backends/locmem.py b/django/core/cache/backends/locmem.py index 2734cef86a..e8e1e0d450 100644 --- a/django/core/cache/backends/locmem.py +++ b/django/core/cache/backends/locmem.py @@ -29,51 +29,38 @@ class CacheClass(BaseCache): self._lock = RWLock() - def _add(self, key, value, timeout=None): - if len(self._cache) >= self._max_entries: - self._cull() - if timeout is None: - timeout = self.default_timeout - if key not in self._cache.keys(): - self._cache[key] = value - self._expire_info[key] = time.time() + timeout - def add(self, key, value, timeout=None): self._lock.writer_enters() - # Python 2.3 and 2.4 don't allow combined try-except-finally blocks. try: - try: - self._add(key, pickle.dumps(value), timeout) - except pickle.PickleError: - pass + exp = self._expire_info.get(key) + if exp is None or exp <= time.time(): + try: + self._set(key, pickle.dumps(value), timeout) + except pickle.PickleError: + pass finally: self._lock.writer_leaves() def get(self, key, default=None): - should_delete = False self._lock.reader_enters() try: - now = time.time() exp = self._expire_info.get(key) if exp is None: return default - elif exp < now: - should_delete = True - else: + elif exp > time.time(): try: return pickle.loads(self._cache[key]) except pickle.PickleError: return default finally: self._lock.reader_leaves() - if should_delete: - self._lock.writer_enters() - try: - del self._cache[key] - del self._expire_info[key] - return default - finally: - self._lock.writer_leaves() + self._lock.writer_enters() + try: + del self._cache[key] + del self._expire_info[key] + return default + finally: + self._lock.writer_leaves() def _set(self, key, value, timeout=None): if len(self._cache) >= self._max_entries: @@ -95,7 +82,23 @@ class CacheClass(BaseCache): self._lock.writer_leaves() def has_key(self, key): - return key in self._cache + self._lock.reader_enters() + try: + exp = self._expire_info.get(key) + if exp is None: + return False + elif exp > time.time(): + return True + finally: + self._lock.reader_leaves() + + self._lock.writer_enters() + try: + del self._cache[key] + del self._expire_info[key] + return False + finally: + self._lock.writer_leaves() def _cull(self): if self._cull_frequency == 0: diff --git a/django/core/management/__init__.py b/django/core/management/__init__.py index ee0eb6fda8..d78e2eda0b 100644 --- a/django/core/management/__init__.py +++ b/django/core/management/__init__.py @@ -10,7 +10,7 @@ from django.core.management.base import BaseCommand, CommandError, handle_defaul get_version = django.get_version # A cache of loaded commands, so that call_command -# doesn't have to reload every time it is called +# doesn't have to reload every time it's called. _commands = None def find_commands(management_dir): @@ -29,8 +29,8 @@ def find_commands(management_dir): def find_management_module(app_name): """ - Determines the path to the management module for the application named, - without acutally importing the application or the management module. + Determines the path to the management module for the given app_name, + without actually importing the application or the management module. Raises ImportError if the management module cannot be found for any reason. """ @@ -46,19 +46,19 @@ def find_management_module(app_name): def load_command_class(app_name, name): """ Given a command name and an application name, returns the Command - class instance. All errors raised by the importation process + class instance. All errors raised by the import process (ImportError, AttributeError) are allowed to propagate. """ return getattr(__import__('%s.management.commands.%s' % (app_name, name), {}, {}, ['Command']), 'Command')() -def get_commands(): +def get_commands(load_user_commands=True, project_directory=None): """ - Returns a dictionary of commands against the application in which - those commands can be found. This works by looking for a - management.commands package in django.core, and in each installed - application -- if a commands package exists, all commands in that - package are registered. + Returns a dictionary mapping command names to their callback applications. + + This works by looking for a management.commands package in django.core, and + in each installed application -- if a commands package exists, all commands + in that package are registered. Core commands are always included. If a settings module has been specified, user-defined commands will also be included, the @@ -73,34 +73,22 @@ def get_commands(): startapp command), the instantiated module can be placed in the dictionary in place of the application name. - The dictionary is cached on the first call, and reused on subsequent + The dictionary is cached on the first call and reused on subsequent calls. """ global _commands if _commands is None: - _commands = dict([(name, 'django.core') - for name in find_commands(__path__[0])]) - # Get commands from all installed apps. - try: - from django.conf import settings - apps = settings.INSTALLED_APPS - except (AttributeError, ImportError): - apps = [] + _commands = dict([(name, 'django.core') for name in find_commands(__path__[0])]) - for app_name in apps: - try: - path = find_management_module(app_name) - _commands.update(dict([(name, app_name) - for name in find_commands(path)])) - except ImportError: - pass # No management module - ignore this app - - # Try to determine the project directory - try: + if load_user_commands: + # Get commands from all installed apps. from django.conf import settings - project_directory = setup_environ(__import__(settings.SETTINGS_MODULE)) - except (AttributeError, ImportError): - project_directory = None + for app_name in settings.INSTALLED_APPS: + try: + path = find_management_module(app_name) + _commands.update(dict([(name, app_name) for name in find_commands(path)])) + except ImportError: + pass # No management module -- ignore this app. if project_directory: # Remove the "startproject" command from self.commands, because @@ -157,18 +145,18 @@ class ManagementUtility(object): def __init__(self, argv=None): self.argv = argv or sys.argv[:] self.prog_name = os.path.basename(self.argv[0]) + self.project_directory = None + self.user_commands = False def main_help_text(self): """ Returns the script's main help text, as a string. """ usage = ['%s [options] [args]' % self.prog_name] - usage.append('Django command line tool,' - ' version %s' % django.get_version()) - usage.append("Type '%s help ' for help on a specific" - " subcommand." % self.prog_name) + usage.append('Django command line tool, version %s' % django.get_version()) + usage.append("Type '%s help ' for help on a specific subcommand." % self.prog_name) usage.append('Available subcommands:') - commands = get_commands().keys() + commands = get_commands(self.user_commands, self.project_directory).keys() commands.sort() for cmd in commands: usage.append(' %s' % cmd) @@ -178,18 +166,18 @@ class ManagementUtility(object): """ Tries to fetch the given subcommand, printing a message with the appropriate command called from the command line (usually - django-admin.py or manage.py) if it can't be found. + "django-admin.py" or "manage.py") if it can't be found. """ try: - app_name = get_commands()[subcommand] + app_name = get_commands(self.user_commands, self.project_directory)[subcommand] if isinstance(app_name, BaseCommand): # If the command is already loaded, use it directly. klass = app_name else: klass = load_command_class(app_name, subcommand) except KeyError: - sys.stderr.write("Unknown command: %r\nType '%s help' for" - " usage.\n" % (subcommand, self.prog_name)) + sys.stderr.write("Unknown command: %r\nType '%s help' for usage.\n" % \ + (subcommand, self.prog_name)) sys.exit(1) return klass @@ -201,8 +189,7 @@ class ManagementUtility(object): # Preprocess options to extract --settings and --pythonpath. # These options could affect the commands that are available, so they # must be processed early. - parser = LaxOptionParser(version=get_version(), - option_list=BaseCommand.option_list) + parser = LaxOptionParser(version=get_version(), option_list=BaseCommand.option_list) try: options, args = parser.parse_args(self.argv) handle_default_options(options) @@ -242,6 +229,8 @@ class ProjectManagementUtility(ManagementUtility): """ def __init__(self, argv, project_directory): super(ProjectManagementUtility, self).__init__(argv) + self.project_directory = project_directory + self.user_commands = True def setup_environ(settings_mod): """ @@ -263,8 +252,7 @@ def setup_environ(settings_mod): sys.path.pop() # Set DJANGO_SETTINGS_MODULE appropriately. - os.environ['DJANGO_SETTINGS_MODULE'] = '%s.%s' % (project_name, - settings_name) + os.environ['DJANGO_SETTINGS_MODULE'] = '%s.%s' % (project_name, settings_name) return project_directory def execute_from_command_line(argv=None): diff --git a/django/core/management/base.py b/django/core/management/base.py index 31c2849075..7b8a3e987f 100644 --- a/django/core/management/base.py +++ b/django/core/management/base.py @@ -27,6 +27,8 @@ class BaseCommand(object): help='The Python path to a settings module, e.g. "myproject.settings.main". If this isn\'t provided, the DJANGO_SETTINGS_MODULE environment variable will be used.'), make_option('--pythonpath', help='A directory to add to the Python path, e.g. "/home/djangoprojects/myproject".'), + make_option('--traceback', action='store_true', + help='Print traceback on exception'), ) help = '' args = '' diff --git a/django/core/management/commands/dumpdata.py b/django/core/management/commands/dumpdata.py index 7bdcc4271f..2642ae925e 100644 --- a/django/core/management/commands/dumpdata.py +++ b/django/core/management/commands/dumpdata.py @@ -1,11 +1,12 @@ from django.core.management.base import BaseCommand, CommandError +from django.core import serializers from optparse import make_option class Command(BaseCommand): option_list = BaseCommand.option_list + ( make_option('--format', default='json', dest='format', - help='Specifies the output serialization format for fixtures'), + help='Specifies the output serialization format for fixtures.'), make_option('--indent', default=None, dest='indent', type='int', help='Specifies the indent level to use when pretty-printing output'), ) @@ -14,10 +15,10 @@ class Command(BaseCommand): def handle(self, *app_labels, **options): from django.db.models import get_app, get_apps, get_models - from django.core import serializers format = options.get('format', 'json') indent = options.get('indent', None) + show_traceback = options.get('traceback', False) if len(app_labels) == 0: app_list = get_apps() @@ -26,6 +27,9 @@ class Command(BaseCommand): # Check that the serialization format exists; this is a shortcut to # avoid collating all the objects and _then_ failing. + if format not in serializers.get_public_serializer_formats(): + raise CommandError("Unknown serialization format: %s" % format) + try: serializers.get_serializer(format) except KeyError: @@ -34,8 +38,10 @@ class Command(BaseCommand): objects = [] for app in app_list: for model in get_models(app): - objects.extend(model.objects.all()) + objects.extend(model._default_manager.all()) try: return serializers.serialize(format, objects, indent=indent) except Exception, e: + if show_traceback: + raise raise CommandError("Unable to serialize database: %s" % e) diff --git a/django/core/management/commands/loaddata.py b/django/core/management/commands/loaddata.py index 83fc9b8ac9..fb0325906d 100644 --- a/django/core/management/commands/loaddata.py +++ b/django/core/management/commands/loaddata.py @@ -27,6 +27,7 @@ class Command(BaseCommand): self.style = no_style() verbosity = int(options.get('verbosity', 1)) + show_traceback = options.get('traceback', False) # Keep a count of the installed objects and fixtures count = [0, 0] @@ -50,15 +51,15 @@ class Command(BaseCommand): parts = fixture_label.split('.') if len(parts) == 1: fixture_name = fixture_label - formats = serializers.get_serializer_formats() + formats = serializers.get_public_serializer_formats() else: fixture_name, format = '.'.join(parts[:-1]), parts[-1] - if format in serializers.get_serializer_formats(): + if format in serializers.get_public_serializer_formats(): formats = [format] else: formats = [] - if verbosity > 0: + if verbosity > 2: if formats: print "Loading '%s' fixtures..." % fixture_name else: @@ -98,15 +99,17 @@ class Command(BaseCommand): label_found = True except Exception, e: fixture.close() + transaction.rollback() + transaction.leave_transaction_management() + if show_traceback: + raise sys.stderr.write( self.style.ERROR("Problem installing fixture '%s': %s\n" % (full_path, str(e)))) - transaction.rollback() - transaction.leave_transaction_management() return fixture.close() except: - if verbosity > 1: + if verbosity > 2: print "No %s fixture '%s' in %s." % \ (format, fixture_name, humanize(fixture_dir)) @@ -122,7 +125,7 @@ class Command(BaseCommand): transaction.leave_transaction_management() if count[0] == 0: - if verbosity > 0: + if verbosity > 2: print "No fixtures found." else: if verbosity > 0: diff --git a/django/core/management/sql.py b/django/core/management/sql.py index 43918ef1c2..767a2dbbfb 100644 --- a/django/core/management/sql.py +++ b/django/core/management/sql.py @@ -116,6 +116,7 @@ def sql_delete(app, style): "Returns a list of the DROP TABLE SQL statements for the given app." from django.db import connection, models, get_introspection_module from django.db.backends.util import truncate_name + from django.contrib.contenttypes import generic introspection = get_introspection_module() # This should work even if a connection isn't available @@ -179,6 +180,8 @@ def sql_delete(app, style): for model in app_models: opts = model._meta for f in opts.many_to_many: + if isinstance(f.rel, generic.GenericRel): + continue if cursor and table_name_converter(f.m2m_db_table()) in table_names: output.append("%s %s;" % (style.SQL_KEYWORD('DROP TABLE'), style.SQL_TABLE(qn(f.m2m_db_table())))) diff --git a/django/core/serializers/__init__.py b/django/core/serializers/__init__.py index 1e24e2bd22..47dfb0c87a 100644 --- a/django/core/serializers/__init__.py +++ b/django/core/serializers/__init__.py @@ -53,6 +53,11 @@ def get_serializer_formats(): _load_serializers() return _serializers.keys() +def get_public_serializer_formats(): + if not _serializers: + _load_serializers() + return [k for k, v in _serializers.iteritems() if not v.Serializer.internal_use_only] + def get_deserializer(format): if not _serializers: _load_serializers() diff --git a/django/core/serializers/base.py b/django/core/serializers/base.py index ee9e4dd621..3c7dcfa02e 100644 --- a/django/core/serializers/base.py +++ b/django/core/serializers/base.py @@ -22,6 +22,10 @@ class Serializer(object): Abstract serializer base class. """ + # Indicates if the implemented serializer is only available for + # internal Django use. + internal_use_only = False + def serialize(self, queryset, **options): """ Serialize a queryset. diff --git a/django/core/serializers/json.py b/django/core/serializers/json.py index fa2dca7295..e17b821f52 100644 --- a/django/core/serializers/json.py +++ b/django/core/serializers/json.py @@ -20,6 +20,8 @@ class Serializer(PythonSerializer): """ Convert a queryset to JSON. """ + internal_use_only = False + def end_serialization(self): self.options.pop('stream', None) self.options.pop('fields', None) diff --git a/django/core/serializers/python.py b/django/core/serializers/python.py index 6fc13d76b5..cedab06be9 100644 --- a/django/core/serializers/python.py +++ b/django/core/serializers/python.py @@ -13,7 +13,9 @@ class Serializer(base.Serializer): """ Serializes a QuerySet to basic Python objects. """ - + + internal_use_only = True + def start_serialization(self): self._current = None self.objects = [] diff --git a/django/core/serializers/pyyaml.py b/django/core/serializers/pyyaml.py index 92159dbbe3..4c32a9686f 100644 --- a/django/core/serializers/pyyaml.py +++ b/django/core/serializers/pyyaml.py @@ -5,6 +5,7 @@ Requires PyYaml (http://pyyaml.org/), but that's checked for in __init__. """ import datetime +from django.db import models from django.core.serializers.python import Serializer as PythonSerializer from django.core.serializers.python import Deserializer as PythonDeserializer try: @@ -17,10 +18,25 @@ class Serializer(PythonSerializer): """ Convert a queryset to YAML. """ + + internal_use_only = False + + def handle_field(self, obj, field): + # A nasty special case: base YAML doesn't support serialization of time + # types (as opposed to dates or datetimes, which it does support). Since + # we want to use the "safe" serializer for better interoperability, we + # need to do something with those pesky times. Converting 'em to strings + # isn't perfect, but it's better than a "!!python/time" type which would + # halt deserialization under any other language. + if isinstance(field, models.TimeField) and getattr(obj, field.name) is not None: + self._current[field.name] = str(getattr(obj, field.name)) + else: + super(Serializer, self).handle_field(obj, field) + def end_serialization(self): self.options.pop('stream', None) self.options.pop('fields', None) - yaml.dump(self.objects, self.stream, **self.options) + yaml.safe_dump(self.objects, self.stream, **self.options) def getvalue(self): return self.stream.getvalue() diff --git a/django/core/servers/basehttp.py b/django/core/servers/basehttp.py index e98957da2c..05f8756655 100644 --- a/django/core/servers/basehttp.py +++ b/django/core/servers/basehttp.py @@ -398,8 +398,20 @@ class ServerHandler(object): self.bytes_sent += len(data) # XXX check Content-Length and truncate if too many bytes written? - self._write(data) - self._flush() + + # If data is too large, socket will choke, so write chunks no larger + # than 32MB at a time. + length = len(data) + if length > 33554432: + offset = 0 + while offset < length: + chunk_size = min(33554432, length) + self._write(data[offset:offset+chunk_size]) + self._flush() + offset += chunk_size + else: + self._write(data) + self._flush() def sendfile(self): """Platform-specific file transmission diff --git a/django/db/__init__.py b/django/db/__init__.py index d4ea1403bd..8f75e0d7b8 100644 --- a/django/db/__init__.py +++ b/django/db/__init__.py @@ -34,8 +34,8 @@ except ImportError, e: raise # If there's some other error, this must be an error in Django itself. def _import_database_module(import_path='', module_name=''): - """Lazyily import a database module when requested.""" - return __import__('%s%s.%s' % (_import_path, settings.DATABASE_ENGINE, module_name), {}, {}, ['']) + """Lazily import a database module when requested.""" + return __import__('%s%s.%s' % (import_path, settings.DATABASE_ENGINE, module_name), {}, {}, ['']) # We don't want to import the introspect/creation modules unless # someone asks for 'em, so lazily load them on demmand. diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py index 24fda78427..cd830413fc 100644 --- a/django/db/backends/oracle/base.py +++ b/django/db/backends/oracle/base.py @@ -455,6 +455,23 @@ class FormatStylePlaceholderCursor(Database.Cursor): else: return tuple([smart_str(p, self.charset, True) for p in params]) + def _guess_input_sizes(self, params_list): + # Mark any string parameter greater than 4000 characters as an NCLOB. + if isinstance(params_list[0], dict): + sizes = {} + iterators = [params.iteritems() for params in params_list] + else: + sizes = [None] * len(params_list[0]) + iterators = [enumerate(params) for params in params_list] + for iterator in iterators: + for key, value in iterator: + if isinstance(value, basestring) and len(value) > 4000: + sizes[key] = Database.NCLOB + if isinstance(sizes, dict): + self.setinputsizes(**sizes) + else: + self.setinputsizes(*sizes) + def execute(self, query, params=None): if params is None: params = [] @@ -468,6 +485,7 @@ class FormatStylePlaceholderCursor(Database.Cursor): if query.endswith(';') or query.endswith('/'): query = query[:-1] query = smart_str(query, self.charset) % tuple(args) + self._guess_input_sizes([params]) return Database.Cursor.execute(self, query, params) def executemany(self, query, params=None): @@ -484,6 +502,7 @@ class FormatStylePlaceholderCursor(Database.Cursor): query = query[:-1] query = smart_str(query, self.charset) % tuple(args) new_param_list = [self._format_params(i) for i in params] + self._guess_input_sizes(new_param_list) return Database.Cursor.executemany(self, query, new_param_list) def fetchone(self): diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index f8a3f18d25..0ea4eae6e7 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -708,7 +708,7 @@ class EmailField(CharField): class FileField(Field): def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs): self.upload_to = upload_to - kwargs['max_length'] = kwargs.get('max_length', 100) + kwargs['max_length'] = kwargs.get('max_length', 100) Field.__init__(self, verbose_name, name, **kwargs) def get_db_prep_save(self, value): @@ -905,9 +905,13 @@ class NullBooleanField(Field): return [oldforms.NullBooleanField] 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) - return forms.NullBooleanField(**defaults) + return super(NullBooleanField, self).formfield(**defaults) class PhoneNumberField(IntegerField): def get_manipulator_field_objs(self): @@ -925,11 +929,11 @@ class PhoneNumberField(IntegerField): class PositiveIntegerField(IntegerField): def get_manipulator_field_objs(self): return [oldforms.PositiveIntegerField] - + def formfield(self, **kwargs): defaults = {'min_value': 0} defaults.update(kwargs) - return super(PositiveIntegerField, self).formfield(**defaults) + return super(PositiveIntegerField, self).formfield(**defaults) class PositiveSmallIntegerField(IntegerField): def get_manipulator_field_objs(self): @@ -938,7 +942,7 @@ class PositiveSmallIntegerField(IntegerField): def formfield(self, **kwargs): defaults = {'min_value': 0} defaults.update(kwargs) - return super(PositiveSmallIntegerField, self).formfield(**defaults) + return super(PositiveSmallIntegerField, self).formfield(**defaults) class SlugField(CharField): def __init__(self, *args, **kwargs): diff --git a/django/http/__init__.py b/django/http/__init__.py index 47f9736ce2..69e9d51204 100644 --- a/django/http/__init__.py +++ b/django/http/__init__.py @@ -277,7 +277,20 @@ class HttpResponse(object): for key, value in self._headers.values()]) \ + '\n\n' + self.content + def _convert_to_ascii(self, *values): + "Convert all values to ascii strings" + for value in values: + if isinstance(value, unicode): + try: + yield value.encode('us-ascii') + except UnicodeError, e: + e.reason += ', HTTP response headers must be in US-ASCII format' + raise + else: + yield str(value) + def __setitem__(self, header, value): + header, value = self._convert_to_ascii(header, value) self._headers[header.lower()] = (header, value) def __delitem__(self, header): @@ -331,7 +344,7 @@ class HttpResponse(object): chunk = self._iterator.next() if isinstance(chunk, unicode): chunk = chunk.encode(self._charset) - return chunk + return str(chunk) def close(self): if hasattr(self._container, 'close'): diff --git a/django/newforms/fields.py b/django/newforms/fields.py index 58f65ffde5..3b8f4195b0 100644 --- a/django/newforms/fields.py +++ b/django/newforms/fields.py @@ -533,8 +533,8 @@ class BooleanField(Field): """Returns a Python boolean object.""" super(BooleanField, self).clean(value) # Explicitly check for the string 'False', which is what a hidden field - # will submit for False (since bool("True") == True we don't need to - # handle that explicitly). + # will submit for False. Because bool("True") == True, we don't need to + # handle that explicitly. if value == 'False': return False return bool(value) diff --git a/django/newforms/models.py b/django/newforms/models.py index 3c9b43da20..e0f2cde5d4 100644 --- a/django/newforms/models.py +++ b/django/newforms/models.py @@ -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 takes a database Field instance and returns a form Field instance. """ - warn("form_for_model is deprecated, use ModelForm instead.", - PendingDeprecationWarning, - stacklevel=3) + warn("form_for_model is deprecated. Use ModelForm instead.", + PendingDeprecationWarning, stacklevel=3) opts = model._meta field_list = [] for f in opts.fields + opts.many_to_many: @@ -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 instance with the given kwargs (i.e. 'initial'). """ - warn("form_for_instance is deprecated, use ModelForm instead.", - PendingDeprecationWarning, - stacklevel=3) + warn("form_for_instance is deprecated. Use ModelForm instead.", + PendingDeprecationWarning, stacklevel=3) model = instance.__class__ opts = model._meta field_list = [] @@ -151,10 +149,10 @@ def model_to_dict(instance, fields=None, exclude=None): """ Returns a dict containing the data in ``instance`` suitable for passing as a Form's ``initial`` keyword argument. - + ``fields`` is an optional list of field names. If provided, only the named fields will be included in the returned dict. - + ``exclude`` is an optional list of field names. If provided, the named fields will be excluded from the returned dict, even if they are listed in the ``fields`` argument. @@ -189,7 +187,7 @@ def fields_for_model(model, fields=None, exclude=None, formfield_callback=lambda ``fields`` is an optional list of field names. If provided, only the named fields will be included in the returned fields. - + ``exclude`` is an optional list of field names. If provided, the named fields will be excluded from the returned fields, even if they are listed in the ``fields`` argument. @@ -216,9 +214,8 @@ class ModelFormOptions(object): self.exclude = getattr(options, 'exclude', None) class ModelFormMetaclass(type): - def __new__(cls, name, bases, attrs): - # TODO: no way to specify formfield_callback yet, do we need one, or - # should it be a special case for the admin? + def __new__(cls, name, bases, attrs, + formfield_callback=lambda f: f.formfield()): fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)] fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter)) @@ -247,15 +244,16 @@ class ModelFormMetaclass(type): # If a model is defined, extract form fields from it and add them to base_fields if attrs['_meta'].model is not None: - # Don't allow a subclass to define a Meta model if a parent class has. - # Technically the right fields would be generated, but the save - # method will not deal with more than one model. + # Don't allow a subclass to define a different Meta model than a + # parent class has. Technically the right fields would be generated, + # but the save method will not deal with more than one model. for base in bases: base_opts = getattr(base, '_meta', None) base_model = getattr(base_opts, 'model', None) - if base_model is not None: - raise ImproperlyConfigured('%s defines more than one model.' % name) - model_fields = fields_for_model(opts.model, opts.fields, opts.exclude) + if base_model and base_model is not opts.model: + raise ImproperlyConfigured('%s defines a different model than its parent.' % name) + model_fields = fields_for_model(opts.model, opts.fields, + opts.exclude, formfield_callback) # fields declared in base classes override fields from the model model_fields.update(declared_fields) attrs['base_fields'] = model_fields @@ -264,11 +262,16 @@ class ModelFormMetaclass(type): return type.__new__(cls, name, bases, attrs) class BaseModelForm(BaseForm): - def __init__(self, instance, data=None, files=None, auto_id='id_%s', prefix=None, - initial=None, error_class=ErrorList, label_suffix=':'): - self.instance = instance + def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, + initial=None, error_class=ErrorList, label_suffix=':', instance=None): opts = self._meta - object_data = model_to_dict(instance, opts.fields, opts.exclude) + if instance is None: + # if we didn't get an instance, instantiate a new one + self.instance = opts.model() + object_data = {} + else: + self.instance = instance + object_data = model_to_dict(instance, opts.fields, opts.exclude) # if initial was provided, it should override the values from instance if initial is not None: object_data.update(initial) diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py index ac92bef6cf..5cee0ab244 100644 --- a/django/template/defaultfilters.py +++ b/django/template/defaultfilters.py @@ -43,7 +43,11 @@ def stringfilter(func): def addslashes(value): - """Adds slashes - useful for passing strings to JavaScript, for example.""" + """ + Adds slashes before quotes. Useful for escaping strings in CSV, for + example. Less useful for escaping JavaScript; use the ``escapejs`` + filter instead. + """ return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'") addslashes.is_safe = True addslashes = stringfilter(addslashes) @@ -54,6 +58,25 @@ def capfirst(value): capfirst.is_safe=True capfirst = stringfilter(capfirst) +_js_escapes = ( + ('\\', '\\\\'), + ('"', '\\"'), + ("'", "\\'"), + ('\n', '\\n'), + ('\r', '\\r'), + ('\b', '\\b'), + ('\f', '\\f'), + ('\t', '\\t'), + ('\v', '\\v'), + ('= 1: print "Creating test database..." # If we're using SQLite, it's more convenient to test against an - # in-memory database. + # in-memory database. Using the TEST_DATABASE_NAME setting you can still choose + # to run on a physical database. if settings.DATABASE_ENGINE == "sqlite3": - TEST_DATABASE_NAME = ":memory:" + if settings.TEST_DATABASE_NAME and settings.TEST_DATABASE_NAME != ":memory:": + TEST_DATABASE_NAME = settings.TEST_DATABASE_NAME + # Erase the old test database + if verbosity >= 1: + print "Destroying old test database..." + if os.access(TEST_DATABASE_NAME, os.F_OK): + if not autoclobber: + confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % TEST_DATABASE_NAME) + if autoclobber or confirm == 'yes': + try: + if verbosity >= 1: + print "Destroying old test database..." + os.remove(TEST_DATABASE_NAME) + except Exception, e: + sys.stderr.write("Got an error deleting the old test database: %s\n" % e) + sys.exit(2) + else: + print "Tests cancelled." + sys.exit(1) + if verbosity >= 1: + print "Creating test database..." + else: + TEST_DATABASE_NAME = ":memory:" else: suffix = { 'postgresql': get_postgresql_create_suffix, @@ -171,17 +194,20 @@ def destroy_test_db(old_database_name, verbosity=1): creation_module.destroy_test_db(settings, connection, old_database_name, verbosity) return - # Unless we're using SQLite, remove the test database to clean up after - # ourselves. Connect to the previous database (not the test database) - # to do so, because it's not allowed to delete a database while being - # connected to it. if verbosity >= 1: print "Destroying test database..." connection.close() TEST_DATABASE_NAME = settings.DATABASE_NAME settings.DATABASE_NAME = old_database_name - - if settings.DATABASE_ENGINE != "sqlite3": + if settings.DATABASE_ENGINE == "sqlite3": + if TEST_DATABASE_NAME and TEST_DATABASE_NAME != ":memory:": + # Remove the SQLite database file + os.remove(TEST_DATABASE_NAME) + else: + # Remove the test database to clean up after + # ourselves. Connect to the previous database (not the test database) + # to do so, because it's not allowed to delete a database while being + # connected to it. cursor = connection.cursor() _set_autocommit(connection) time.sleep(1) # To avoid "database is being accessed by other users" errors. diff --git a/django/utils/html.py b/django/utils/html.py index 34bbf7357f..33e2ee3856 100644 --- a/django/utils/html.py +++ b/django/utils/html.py @@ -1,4 +1,4 @@ -"HTML utilities suitable for global use." +"""HTML utilities suitable for global use.""" import re import string @@ -8,11 +8,11 @@ from django.utils.encoding import force_unicode from django.utils.functional import allow_lazy from django.utils.http import urlquote -# Configuration for urlize() function +# Configuration for urlize() function. LEADING_PUNCTUATION = ['(', '<', '<'] TRAILING_PUNCTUATION = ['.', ',', ')', '>', '\n', '>'] -# list of possible strings used for bullets in bulleted lists +# List of possible strings used for bullets in bulleted lists. DOTS = ['·', '*', '\xe2\x80\xa2', '•', '•', '•'] unencoded_ampersands_re = re.compile(r'&(?!(\w+|#\d+);)') @@ -28,7 +28,7 @@ trailing_empty_content_re = re.compile(r'(?:

    (?: |\s|
    )*?

    \s*)+\ del x # Temporary variable def escape(html): - "Return the given HTML with ampersands, quotes and carets encoded." + """Returns the given HTML with ampersands, quotes and carets encoded.""" return mark_safe(force_unicode(html).replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"').replace("'", ''')) escape = allow_lazy(escape, unicode) @@ -42,7 +42,7 @@ def conditional_escape(html): return escape(html) def linebreaks(value, autoescape=False): - "Converts newlines into

    and
    s" + """Converts newlines into

    and
    s.""" value = re.sub(r'\r\n|\r|\n', '\n', force_unicode(value)) # normalize newlines paras = re.split('\n{2,}', value) if autoescape: @@ -50,31 +50,31 @@ def linebreaks(value, autoescape=False): else: paras = [u'

    %s

    ' % p.strip().replace('\n', '
    ') for p in paras] return u'\n\n'.join(paras) -linebreaks = allow_lazy(linebreaks, unicode) +linebreaks = allow_lazy(linebreaks, unicode) def strip_tags(value): - "Return the given HTML with all tags stripped." + """Returns the given HTML with all tags stripped.""" return re.sub(r'<[^>]*?>', '', force_unicode(value)) strip_tags = allow_lazy(strip_tags) def strip_spaces_between_tags(value): - "Return the given HTML with spaces between tags removed." + """Returns the given HTML with spaces between tags removed.""" return re.sub(r'>\s+<', '><', force_unicode(value)) strip_spaces_between_tags = allow_lazy(strip_spaces_between_tags, unicode) def strip_entities(value): - "Return the given HTML with all entities (&something;) stripped." + """Returns the given HTML with all entities (&something;) stripped.""" return re.sub(r'&(?:\w+|#\d+);', '', force_unicode(value)) strip_entities = allow_lazy(strip_entities, unicode) def fix_ampersands(value): - "Return the given HTML with all unencoded ampersands encoded correctly." + """Returns the given HTML with all unencoded ampersands encoded correctly.""" return unencoded_ampersands_re.sub('&', force_unicode(value)) fix_ampersands = allow_lazy(fix_ampersands, unicode) def urlize(text, trim_url_limit=None, nofollow=False, autoescape=False): """ - Convert any URLs in text into clickable links. + Converts any URLs in text into clickable links. Works on http://, https://, and www. links. Links can have trailing punctuation (periods, commas, close-parens) and leading punctuation diff --git a/django/utils/maxlength.py b/django/utils/maxlength.py index 1df6a3e93e..a616541f85 100644 --- a/django/utils/maxlength.py +++ b/django/utils/maxlength.py @@ -1,6 +1,6 @@ """ Utilities for providing backwards compatibility for the maxlength argument, -which has been replaced by max_length, see ticket #2101. +which has been replaced by max_length. See ticket #2101. """ from warnings import warn @@ -15,17 +15,15 @@ def legacy_maxlength(max_length, maxlength): """ Consolidates max_length and maxlength, providing backwards compatibilty for the legacy "maxlength" argument. + If one of max_length or maxlength is given, then that value is returned. - If both are given, a TypeError is raised. - If maxlength is used at all, a deprecation warning is issued. + If both are given, a TypeError is raised. If maxlength is used at all, a + deprecation warning is issued. """ if maxlength is not None: - warn("maxlength is deprecated, use max_length instead.", - DeprecationWarning, - stacklevel=3) + warn("maxlength is deprecated. Use max_length instead.", DeprecationWarning, stacklevel=3) if max_length is not None: - raise TypeError("field can not take both the max_length" - " argument and the legacy maxlength argument.") + raise TypeError("Field cannot take both the max_length argument and the legacy maxlength argument.") max_length = maxlength return max_length @@ -33,7 +31,8 @@ def remove_maxlength(func): """ A decorator to be used on a class's __init__ that provides backwards compatibilty for the legacy "maxlength" keyword argument, i.e. - name = models.CharField(maxlength=20) + name = models.CharField(maxlength=20) + It does this by changing the passed "maxlength" keyword argument (if it exists) into a "max_length" keyword argument. """ @@ -58,7 +57,6 @@ class LegacyMaxlength(type): Metaclass for providing backwards compatibility support for the "maxlength" keyword argument. """ - def __init__(cls, name, bases, attrs): super(LegacyMaxlength, cls).__init__(name, bases, attrs) # Decorate the class's __init__ to remove any maxlength keyword. diff --git a/django/views/debug.py b/django/views/debug.py index fba43912bb..18a396d3a6 100644 --- a/django/views/debug.py +++ b/django/views/debug.py @@ -1,9 +1,12 @@ +import os +import re +import sys + from django.conf import settings from django.template import Template, Context, TemplateDoesNotExist from django.utils.html import escape from django.http import HttpResponseServerError, HttpResponseNotFound from django.utils.encoding import smart_unicode -import os, re, sys HIDDEN_SETTINGS = re.compile('SECRET|PASSWORD|PROFANITIES_LIST') @@ -142,9 +145,10 @@ def technical_500_response(request, exc_type, exc_value, tb): 'request': request, 'request_protocol': request.is_secure() and "https" or "http", 'settings': get_safe_settings(), - 'sys_executable' : sys.executable, - 'sys_version_info' : '%d.%d.%d' % sys.version_info[0:3], - 'django_version_info' : get_version(), + 'sys_executable': sys.executable, + 'sys_version_info': '%d.%d.%d' % sys.version_info[0:3], + 'django_version_info': get_version(), + 'sys_path' : sys.path, 'template_info': template_info, 'template_does_not_exist': template_does_not_exist, 'loader_debug_info': loader_debug_info, @@ -230,8 +234,8 @@ TECHNICAL_500_TEMPLATE = """ - - + + {{ exception_type }} at {{ request.path|escape }}