1
0
mirror of https://github.com/django/django.git synced 2025-07-04 09:49:12 +00:00

gis: Merged revisions 6525-6613 via svnmerge from [repos:django/trunk trunk].

git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@6615 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Justin Bronn 2007-10-26 20:47:20 +00:00
parent f66ee9d006
commit 4ffbddf92d
93 changed files with 11032 additions and 7945 deletions

View File

@ -50,6 +50,7 @@ answer newbie questions, and generally made Django that much better:
Fabrice Aneche <akh@nobugware.com> Fabrice Aneche <akh@nobugware.com>
ant9000@netwise.it ant9000@netwise.it
Florian Apolloner Florian Apolloner
arien <regexbot@gmail.com>
David Ascher <http://ascher.ca/> David Ascher <http://ascher.ca/>
david@kazserve.org david@kazserve.org
Arthur <avandorp@gmail.com> Arthur <avandorp@gmail.com>
@ -169,6 +170,7 @@ answer newbie questions, and generally made Django that much better:
Nagy Károly <charlie@rendszergazda.com> Nagy Károly <charlie@rendszergazda.com>
Ben Dean Kawamura <ben.dean.kawamura@gmail.com> Ben Dean Kawamura <ben.dean.kawamura@gmail.com>
Ian G. Kelly <ian.g.kelly@gmail.com> Ian G. Kelly <ian.g.kelly@gmail.com>
Thomas Kerpe <thomas@kerpe.net>
Ben Khoo <khoobks@westnet.com.au> Ben Khoo <khoobks@westnet.com.au>
Garth Kidd <http://www.deadlybloodyserious.com/> Garth Kidd <http://www.deadlybloodyserious.com/>
kilian <kilian.cavalotti@lip6.fr> kilian <kilian.cavalotti@lip6.fr>
@ -213,6 +215,7 @@ answer newbie questions, and generally made Django that much better:
Jason McBrayer <http://www.carcosa.net/jason/> Jason McBrayer <http://www.carcosa.net/jason/>
mccutchen@gmail.com mccutchen@gmail.com
michael.mcewan@gmail.com michael.mcewan@gmail.com
michal@plovarna.cz
mikko@sorl.net mikko@sorl.net
Slawek Mikula <slawek dot mikula at gmail dot com> Slawek Mikula <slawek dot mikula at gmail dot com>
mitakummaa@gmail.com mitakummaa@gmail.com
@ -256,6 +259,7 @@ answer newbie questions, and generally made Django that much better:
Brian Ray <http://brianray.chipy.org/> Brian Ray <http://brianray.chipy.org/>
remco@diji.biz remco@diji.biz
rhettg@gmail.com rhettg@gmail.com
ricardojbarrios@gmail.com
Matt Riggott Matt Riggott
Henrique Romano <onaiort@gmail.com> Henrique Romano <onaiort@gmail.com>
Armin Ronacher Armin Ronacher
@ -270,6 +274,7 @@ answer newbie questions, and generally made Django that much better:
serbaut@gmail.com serbaut@gmail.com
John Shaffer <jshaffer2112@gmail.com> John Shaffer <jshaffer2112@gmail.com>
Pete Shinners <pete@shinners.org> Pete Shinners <pete@shinners.org>
jason.sidabras@gmail.com
Jozko Skrablin <jozko.skrablin@gmail.com> Jozko Skrablin <jozko.skrablin@gmail.com>
SmileyChris <smileychris@gmail.com> SmileyChris <smileychris@gmail.com>
smurf@smurf.noris.de smurf@smurf.noris.de
@ -324,6 +329,7 @@ answer newbie questions, and generally made Django that much better:
Jakub Wiśniowski <restless.being@gmail.com> Jakub Wiśniowski <restless.being@gmail.com>
Maciej Wiśniowski <pigletto@gmail.com> Maciej Wiśniowski <pigletto@gmail.com>
wojtek wojtek
Jason Yan <tailofthesun@gmail.com>
ye7cakf02@sneakemail.com ye7cakf02@sneakemail.com
ymasuda@ethercube.com ymasuda@ethercube.com
Jarek Zgoda <jarek.zgoda@gmail.com> Jarek Zgoda <jarek.zgoda@gmail.com>

View File

@ -140,13 +140,3 @@ class UserSettingsHolder(object):
settings = LazySettings() settings = LazySettings()
# This function replaces itself with django.utils.translation.gettext() the
# first time it's run. This is necessary because the import of
# django.utils.translation requires a working settings module, and loading it
# from within this file would cause a circular import.
def first_time_gettext(*args):
from django.utils.translation import gettext
__builtins__['_'] = gettext
return gettext(*args)
__builtins__['_'] = first_time_gettext

View File

@ -30,7 +30,7 @@ INTERNAL_IPS = ()
TIME_ZONE = 'America/Chicago' TIME_ZONE = 'America/Chicago'
# Language code for this installation. All choices can be found here: # Language code for this installation. All choices can be found here:
# http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes # http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGE_CODE = 'en-us' LANGUAGE_CODE = 'en-us'
# Languages we provide translations for, out of the box. The language name # Languages we provide translations for, out of the box. The language name
@ -275,6 +275,7 @@ SESSION_COOKIE_NAME = 'sessionid' # Cookie name. This can
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2 # Age of cookie, in seconds (default: 2 weeks). SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2 # Age of cookie, in seconds (default: 2 weeks).
SESSION_COOKIE_DOMAIN = None # A string like ".lawrence.com", or None for standard domain cookie. SESSION_COOKIE_DOMAIN = None # A string like ".lawrence.com", or None for standard domain cookie.
SESSION_COOKIE_SECURE = False # Whether the session cookie should be secure (https:// only). SESSION_COOKIE_SECURE = False # Whether the session cookie should be secure (https:// only).
SESSION_COOKIE_PATH = '/' # The path of the session cookie.
SESSION_SAVE_EVERY_REQUEST = False # Whether to save the session data on every request. SESSION_SAVE_EVERY_REQUEST = False # Whether to save the session data on every request.
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # Whether sessions expire when a user closes his browser. SESSION_EXPIRE_AT_BROWSER_CLOSE = False # Whether sessions expire when a user closes his browser.
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # The module to store session data SESSION_ENGINE = 'django.contrib.sessions.backends.db' # The module to store session data

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -6,12 +6,12 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Django Javascript 1.0\n" "Project-Id-Version: Django Javascript 1.0\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2007-07-14 13:45-0300\n" "POT-Creation-Date: 2007-10-06 14:18-0300\n"
"PO-Revision-Date: 2007-07-14 14:36-0300\n" "PO-Revision-Date: 2007-07-14 14:36-0300\n"
"Last-Translator: Ramiro Morales <rm0@gmx.net>\n" "Last-Translator: Ramiro Morales <rm0@gmx.net>\n"
"Language-Team: Django-I18N <django-i18n@googlegroups.com>\n" "Language-Team: Django-I18N <django-i18n@googlegroups.com>\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: contrib/admin/media/js/SelectFilter2.js:33 #: contrib/admin/media/js/SelectFilter2.js:33
@ -44,8 +44,8 @@ msgstr "Seleccione los items a agregar y haga click en "
msgid "Clear all" msgid "Clear all"
msgstr "Eliminar todos" msgstr "Eliminar todos"
#: contrib/admin/media/js/dateparse.js:32
#: contrib/admin/media/js/calendar.js:24 #: contrib/admin/media/js/calendar.js:24
#: contrib/admin/media/js/dateparse.js:32
msgid "" msgid ""
"January February March April May June July August September October November " "January February March April May June July August September October November "
"December" "December"
@ -53,14 +53,14 @@ msgstr ""
"Enero Febrero Marzo Abril Mayo Junio Julio Agosto Setiembre Octubre " "Enero Febrero Marzo Abril Mayo Junio Julio Agosto Setiembre Octubre "
"Noviembre Diciembre" "Noviembre Diciembre"
#: contrib/admin/media/js/dateparse.js:33
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday"
msgstr "Domingo Lunes Martes Miércoles Jueves Viernes Sábado"
#: contrib/admin/media/js/calendar.js:25 #: contrib/admin/media/js/calendar.js:25
msgid "S M T W T F S" msgid "S M T W T F S"
msgstr "D L M M J V S" msgstr "D L M M J V S"
#: contrib/admin/media/js/dateparse.js:33
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday"
msgstr "Domingo Lunes Martes Miércoles Jueves Viernes Sábado"
#: contrib/admin/media/js/admin/CollapsedFieldsets.js:34 #: contrib/admin/media/js/admin/CollapsedFieldsets.js:34
#: contrib/admin/media/js/admin/CollapsedFieldsets.js:72 #: contrib/admin/media/js/admin/CollapsedFieldsets.js:72
msgid "Show" msgid "Show"

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@ msgstr ""
"Project-Id-Version: django\n" "Project-Id-Version: django\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2007-08-06 23:19+0200\n" "POT-Creation-Date: 2007-08-06 23:19+0200\n"
"PO-Revision-Date: 2007-08-12 23:05+0200\n" "PO-Revision-Date: 2007-10-12 21:22+0200\n"
"Last-Translator: Aljosa Mohorovic <aljosa.mohorovic@gmail.com>\n" "Last-Translator: Aljosa Mohorovic <aljosa.mohorovic@gmail.com>\n"
"Language-Team: Hrvatski jezik\n" "Language-Team: Hrvatski jezik\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@ -28,7 +28,7 @@ msgstr "Unos za ovo polje je obavezan."
#, python-format #, python-format
msgid "Ensure your text is less than %s character." msgid "Ensure your text is less than %s character."
msgid_plural "Ensure your text is less than %s characters." msgid_plural "Ensure your text is less than %s characters."
msgstr[0] "Uneseni tekst mora sadržavati manje od %s znaka." msgstr[0] ""
msgstr[1] "Uneseni tekst mora sadržavati manje od %s znakova." msgstr[1] "Uneseni tekst mora sadržavati manje od %s znakova."
#: oldforms/__init__.py:413 #: oldforms/__init__.py:413
@ -398,62 +398,62 @@ msgstr "URL %s ne prikazuje ispravnu sliku."
#: core/validators.py:196 #: core/validators.py:196
#, python-format #, python-format
msgid "Phone numbers must be in XXX-XXX-XXXX format. \"%s\" is invalid." msgid "Phone numbers must be in XXX-XXX-XXXX format. \"%s\" is invalid."
msgstr "" msgstr "Telefonski brojevi moraju biti u formatu XXX-XXX-XXXX. \"%s\" nije ispravan format."
#: core/validators.py:204 #: core/validators.py:204
#, python-format #, python-format
msgid "The URL %s does not point to a valid QuickTime video." msgid "The URL %s does not point to a valid QuickTime video."
msgstr "" msgstr "URL %s ne vodi na ispravan QuickTime video."
#: core/validators.py:208 #: core/validators.py:208
msgid "A valid URL is required." msgid "A valid URL is required."
msgstr "" msgstr "Ispravan URL je obavezan."
#: core/validators.py:222 #: core/validators.py:222
#, python-format #, python-format
msgid "" msgid ""
"Valid HTML is required. Specific errors are:\n" "Valid HTML is required. Specific errors are:\n"
"%s" "%s"
msgstr "" msgstr "Ispravan HTML je obavezan. Pogreške:<br> %s"
#: core/validators.py:229 #: core/validators.py:229
#, python-format #, python-format
msgid "Badly formed XML: %s" msgid "Badly formed XML: %s"
msgstr "" msgstr "Loše formatiran XML: %s"
#: core/validators.py:246 #: core/validators.py:246
#, python-format #, python-format
msgid "Invalid URL: %s" msgid "Invalid URL: %s"
msgstr "" msgstr "Neispravan URL: %s"
#: core/validators.py:251 core/validators.py:253 #: core/validators.py:251 core/validators.py:253
#, python-format #, python-format
msgid "The URL %s is a broken link." msgid "The URL %s is a broken link."
msgstr "" msgstr "URL %s je neispravan (broken) link."
#: core/validators.py:259 #: core/validators.py:259
msgid "Enter a valid U.S. state abbreviation." msgid "Enter a valid U.S. state abbreviation."
msgstr "" msgstr "Enter a valid U.S. state abbreviation."
#: core/validators.py:273 #: core/validators.py:273
#, python-format #, python-format
msgid "Watch your mouth! The word %s is not allowed here." msgid "Watch your mouth! The word %s is not allowed here."
msgid_plural "Watch your mouth! The words %s are not allowed here." msgid_plural "Watch your mouth! The words %s are not allowed here."
msgstr[0] "" msgstr[0] "Pazite na izražavanje! Riječ %s nije dopuštena."
msgstr[1] "" msgstr[1] "Pazite na izražavanje! Riječi %s nisu dopuštene."
#: core/validators.py:280 #: core/validators.py:280
#, python-format #, python-format
msgid "This field must match the '%s' field." msgid "This field must match the '%s' field."
msgstr "" msgstr "Ovo polje mora biti jednako %s polju."
#: core/validators.py:299 #: core/validators.py:299
msgid "Please enter something for at least one field." msgid "Please enter something for at least one field."
msgstr "" msgstr "Molim unesite nešto bar za jedno polje."
#: core/validators.py:308 core/validators.py:319 #: core/validators.py:308 core/validators.py:319
msgid "Please enter both fields or leave them both empty." msgid "Please enter both fields or leave them both empty."
msgstr "" msgstr "Molim unesite vrijednosti za oba polja ili ostavite oba polja prazna."
#: core/validators.py:327 #: core/validators.py:327
#, python-format #, python-format
@ -463,7 +463,7 @@ msgstr ""
#: core/validators.py:340 #: core/validators.py:340
#, python-format #, python-format
msgid "This field must be given if %(field)s is not %(value)s" msgid "This field must be given if %(field)s is not %(value)s"
msgstr "" msgstr "Ovo polje je obavezno ako je %(field)s različito od %(value)s"
#: core/validators.py:359 #: core/validators.py:359
msgid "Duplicate values are not allowed." msgid "Duplicate values are not allowed."
@ -471,28 +471,27 @@ msgstr ""
#: core/validators.py:374 #: core/validators.py:374
#, python-format #, python-format
#, fuzzy
msgid "This value must be between %(lower)s and %(upper)s." msgid "This value must be between %(lower)s and %(upper)s."
msgstr "i." msgstr "Vrijednost mora biti između %(lower)s i %(upper)s."
#: core/validators.py:376 #: core/validators.py:376
#, python-format #, python-format
msgid "This value must be at least %s." msgid "This value must be at least %s."
msgstr "" msgstr "Vrijednost mora biti bar %s."
#: core/validators.py:378 #: core/validators.py:378
#, python-format #, python-format
msgid "This value must be no more than %s." msgid "This value must be no more than %s."
msgstr "" msgstr "Vrijednost ne može biti veća od %s."
#: core/validators.py:414 #: core/validators.py:414
#, python-format #, python-format
msgid "This value must be a power of %s." msgid "This value must be a power of %s."
msgstr "" msgstr "Ova vrijednost na kvadrat mora biti %s."
#: core/validators.py:424 #: core/validators.py:424
msgid "Please enter a valid decimal number." msgid "Please enter a valid decimal number."
msgstr "" msgstr "Molim unesite ispravan decimalni broj."
#: core/validators.py:431 #: core/validators.py:431
#, python-format #, python-format
@ -1879,7 +1878,6 @@ msgid "Documentation bookmarklets"
msgstr "" msgstr ""
#: contrib/admin/templates/admin_doc/bookmarklets.html:9 #: contrib/admin/templates/admin_doc/bookmarklets.html:9
#, fuzzy
msgid "" msgid ""
"\n" "\n"
"<p class=\"help\">To install bookmarklets, drag the link to your bookmarks\n" "<p class=\"help\">To install bookmarklets, drag the link to your bookmarks\n"
@ -1890,107 +1888,110 @@ msgid ""
"your computer is \"internal\").</p>\n" "your computer is \"internal\").</p>\n"
msgstr "" msgstr ""
"\n" "\n"
"<p class=\"help\"> i</p>" "<p class=\"help\">To install bookmarklets, drag the link to your bookmarks\n"
"toolbar, or right-click the link and add it to your bookmarks. Now you can\n"
"select the bookmarklet from any page in the site. Note that some of these\n"
"bookmarklets require you to be viewing the site from a computer designated\n"
"as \"internal\" (talk to your system administrator if you aren't sure if\n"
"your computer is \"internal\").</p>\n"
#: contrib/admin/templates/admin_doc/bookmarklets.html:19 #: contrib/admin/templates/admin_doc/bookmarklets.html:19
msgid "Documentation for this page" msgid "Documentation for this page"
msgstr "" msgstr "Dokumentacija za ovu stranicu"
#: contrib/admin/templates/admin_doc/bookmarklets.html:20 #: contrib/admin/templates/admin_doc/bookmarklets.html:20
msgid "" msgid ""
"Jumps you from any page to the documentation for the view that generates " "Jumps you from any page to the documentation for the view that generates "
"that page." "that page."
msgstr "" msgstr "Preusmjeri te sa bilo koje stranice na dokumentaciju za taj prikaz (view) koji generira stranicu."
#: contrib/admin/templates/admin_doc/bookmarklets.html:22 #: contrib/admin/templates/admin_doc/bookmarklets.html:22
msgid "Show object ID" msgid "Show object ID"
msgstr "" msgstr "Prikaži ID objekta"
#: contrib/admin/templates/admin_doc/bookmarklets.html:23 #: contrib/admin/templates/admin_doc/bookmarklets.html:23
#, fuzzy
msgid "" msgid ""
"Shows the content-type and unique ID for pages that represent a single " "Shows the content-type and unique ID for pages that represent a single "
"object." "object."
msgstr "i." msgstr "Prikazuje tip sadržaja i jedinstveni ID za stranice koje predstavljaju pojedinačan objekt."
#: contrib/admin/templates/admin_doc/bookmarklets.html:25 #: contrib/admin/templates/admin_doc/bookmarklets.html:25
msgid "Edit this object (current window)" msgid "Edit this object (current window)"
msgstr "" msgstr "Uredi objekt (u trenutno prozoru)"
#: contrib/admin/templates/admin_doc/bookmarklets.html:26 #: contrib/admin/templates/admin_doc/bookmarklets.html:26
msgid "Jumps to the admin page for pages that represent a single object." msgid "Jumps to the admin page for pages that represent a single object."
msgstr "" msgstr "Preusmjeri na admin stranicu za stranice koje predstavljaju pojedinačan objekt."
#: contrib/admin/templates/admin_doc/bookmarklets.html:28 #: contrib/admin/templates/admin_doc/bookmarklets.html:28
msgid "Edit this object (new window)" msgid "Edit this object (new window)"
msgstr "" msgstr "Uredi objekt (novi prozor)"
#: contrib/admin/templates/admin_doc/bookmarklets.html:29 #: contrib/admin/templates/admin_doc/bookmarklets.html:29
msgid "As above, but opens the admin page in a new window." msgid "As above, but opens the admin page in a new window."
msgstr "" msgstr "Isto kao prethodno, ali otvara admin stranicu u novom prozoru."
#: contrib/contenttypes/models.py:37 #: contrib/contenttypes/models.py:37
msgid "python model class name" msgid "python model class name"
msgstr "" msgstr "ime klase (class) python modela"
#: contrib/contenttypes/models.py:40 #: contrib/contenttypes/models.py:40
msgid "content type" msgid "content type"
msgstr "" msgstr "tip sadržaja"
#: contrib/contenttypes/models.py:41 #: contrib/contenttypes/models.py:41
msgid "content types" msgid "content types"
msgstr "" msgstr "tipovi sadržaja"
#: contrib/auth/views.py:41 #: contrib/auth/views.py:41
msgid "Logged out" msgid "Logged out"
msgstr "" msgstr "Niste logirani"
#: contrib/auth/models.py:53 contrib/auth/models.py:73 #: contrib/auth/models.py:53 contrib/auth/models.py:73
msgid "name" msgid "name"
msgstr "" msgstr "ime"
#: contrib/auth/models.py:55 #: contrib/auth/models.py:55
msgid "codename" msgid "codename"
msgstr "" msgstr "kodno ime"
#: contrib/auth/models.py:58 #: contrib/auth/models.py:58
msgid "permission" msgid "permission"
msgstr "" msgstr "privilegija"
#: contrib/auth/models.py:59 contrib/auth/models.py:74 #: contrib/auth/models.py:59 contrib/auth/models.py:74
msgid "permissions" msgid "permissions"
msgstr "" msgstr "privilegije"
#: contrib/auth/models.py:77 #: contrib/auth/models.py:77
msgid "group" msgid "group"
msgstr "" msgstr "grupa"
#: contrib/auth/models.py:78 contrib/auth/models.py:121 #: contrib/auth/models.py:78 contrib/auth/models.py:121
msgid "groups" msgid "groups"
msgstr "" msgstr "grupe"
#: contrib/auth/models.py:111 #: contrib/auth/models.py:111
msgid "username" msgid "username"
msgstr "" msgstr "korisničko ime"
#: contrib/auth/models.py:111 #: contrib/auth/models.py:111
#, fuzzy
msgid "" msgid ""
"Required. 30 characters or fewer. Alphanumeric characters only (letters, " "Required. 30 characters or fewer. Alphanumeric characters only (letters, "
"digits and underscores)." "digits and underscores)."
msgstr "i." msgstr "Obavezno 30 alfanumeričkih znakova ili manje (slova, brojevi i povlaka)."
#: contrib/auth/models.py:112 #: contrib/auth/models.py:112
msgid "first name" msgid "first name"
msgstr "" msgstr "ime"
#: contrib/auth/models.py:113 #: contrib/auth/models.py:113
msgid "last name" msgid "last name"
msgstr "" msgstr "prezime"
#: contrib/auth/models.py:114 #: contrib/auth/models.py:114
msgid "e-mail address" msgid "e-mail address"
msgstr "" msgstr "e-mail adresa"
#: contrib/auth/models.py:115 #: contrib/auth/models.py:115
msgid "password" msgid "password"
@ -2207,7 +2208,7 @@ msgstr ""
#: contrib/localflavor/de/de_states.py:17 #: contrib/localflavor/de/de_states.py:17
msgid "Saxony" msgid "Saxony"
msgstr "" msgstr "Saxony"
#: contrib/localflavor/de/de_states.py:18 #: contrib/localflavor/de/de_states.py:18
msgid "Saxony-Anhalt" msgid "Saxony-Anhalt"
@ -2899,306 +2900,305 @@ msgstr ""
#: contrib/localflavor/sk/sk_districts.py:85 #: contrib/localflavor/sk/sk_districts.py:85
msgid "Ziar nad Hronom" msgid "Ziar nad Hronom"
msgstr "" msgstr "Ziar nad Hronom"
#: contrib/localflavor/sk/sk_districts.py:86 #: contrib/localflavor/sk/sk_districts.py:86
msgid "Zilina" msgid "Zilina"
msgstr "" msgstr "Zilina"
#: contrib/localflavor/sk/forms.py:32 #: contrib/localflavor/sk/forms.py:32
msgid "Enter a postal code in the format XXXXX or XXX XX." msgid "Enter a postal code in the format XXXXX or XXX XX."
msgstr "" msgstr "Unesi ispravan poštanski broj formata XXXXX or XXX XX."
#: contrib/localflavor/cl/forms.py:32 #: contrib/localflavor/cl/forms.py:32
msgid "Enter valid a Chilean RUT. The format is XX.XXX.XXX-X." msgid "Enter valid a Chilean RUT. The format is XX.XXX.XXX-X."
msgstr "" msgstr "Unesi ispravan čileanski RUT formata XX.XXX.XXX-X."
#: contrib/localflavor/cl/forms.py:37 #: contrib/localflavor/cl/forms.py:37
msgid "Enter valid a Chilean RUT" msgid "Enter valid a Chilean RUT"
msgstr "" msgstr "Unesi ispravan čileanski RUT"
#: contrib/localflavor/fi/forms.py:40 contrib/localflavor/fi/forms.py:45 #: contrib/localflavor/fi/forms.py:40 contrib/localflavor/fi/forms.py:45
msgid "Enter a valid Finnish social security number." msgid "Enter a valid Finnish social security number."
msgstr "" msgstr "Unesi ispravan finski broj socijalnog osiguranja."
#: contrib/sessions/models.py:68 #: contrib/sessions/models.py:68
msgid "session key" msgid "session key"
msgstr "" msgstr "session ključ (key)"
#: contrib/sessions/models.py:69 #: contrib/sessions/models.py:69
msgid "session data" msgid "session data"
msgstr "" msgstr "session podaci"
#: contrib/sessions/models.py:70 #: contrib/sessions/models.py:70
msgid "expire date" msgid "expire date"
msgstr "" msgstr "ističe datuma"
#: contrib/sessions/models.py:74 #: contrib/sessions/models.py:74
msgid "session" msgid "session"
msgstr "" msgstr "session"
#: contrib/sessions/models.py:75 #: contrib/sessions/models.py:75
msgid "sessions" msgid "sessions"
msgstr "" msgstr "sessions"
#: contrib/flatpages/models.py:8 #: contrib/flatpages/models.py:8
#, fuzzy
msgid "Example: '/about/contact/'. Make sure to have leading and trailing slashes." msgid "Example: '/about/contact/'. Make sure to have leading and trailing slashes."
msgstr "i." msgstr "Primjer: '/about/contact/'. Provjerite ako imate prvi i preostale slash-eve (/)."
#: contrib/flatpages/models.py:9 #: contrib/flatpages/models.py:9
msgid "title" msgid "title"
msgstr "" msgstr "naslov"
#: contrib/flatpages/models.py:10 #: contrib/flatpages/models.py:10
msgid "content" msgid "content"
msgstr "" msgstr "sadržaj"
#: contrib/flatpages/models.py:11 #: contrib/flatpages/models.py:11
msgid "enable comments" msgid "enable comments"
msgstr "" msgstr "uključi komentare"
#: contrib/flatpages/models.py:12 #: contrib/flatpages/models.py:12
msgid "template name" msgid "template name"
msgstr "" msgstr "ime template-a"
#: contrib/flatpages/models.py:13 #: contrib/flatpages/models.py:13
msgid "" msgid ""
"Example: 'flatpages/contact_page.html'. If this isn't provided, the system " "Example: 'flatpages/contact_page.html'. If this isn't provided, the system "
"will use 'flatpages/default.html'." "will use 'flatpages/default.html'."
msgstr "" msgstr "Primjer: 'flatpages/contact_page.html'. Ako navedeno nije definirano sistem će koristiti 'flatpages/default.html'."
#: contrib/flatpages/models.py:14 #: contrib/flatpages/models.py:14
msgid "registration required" msgid "registration required"
msgstr "" msgstr "registracija obavezna"
#: contrib/flatpages/models.py:14 #: contrib/flatpages/models.py:14
msgid "If this is checked, only logged-in users will be able to view the page." msgid "If this is checked, only logged-in users will be able to view the page."
msgstr "" msgstr "Ako je ovo selektirano samo logirani korisnici moći će vidjeti ovu stranicu."
#: contrib/flatpages/models.py:18 #: contrib/flatpages/models.py:18
msgid "flat page" msgid "flat page"
msgstr "" msgstr "statična stranica"
#: contrib/flatpages/models.py:19 #: contrib/flatpages/models.py:19
msgid "flat pages" msgid "flat pages"
msgstr "" msgstr "statične stranice"
#: utils/dates.py:6 #: utils/dates.py:6
msgid "Monday" msgid "Monday"
msgstr "" msgstr "Ponedjeljak"
#: utils/dates.py:6 #: utils/dates.py:6
msgid "Tuesday" msgid "Tuesday"
msgstr "" msgstr "Utorak"
#: utils/dates.py:6 #: utils/dates.py:6
msgid "Wednesday" msgid "Wednesday"
msgstr "" msgstr "Srijeda"
#: utils/dates.py:6 #: utils/dates.py:6
msgid "Thursday" msgid "Thursday"
msgstr "" msgstr "Četvrtak"
#: utils/dates.py:6 #: utils/dates.py:6
msgid "Friday" msgid "Friday"
msgstr "" msgstr "Petak"
#: utils/dates.py:7 #: utils/dates.py:7
msgid "Saturday" msgid "Saturday"
msgstr "" msgstr "Subota"
#: utils/dates.py:7 #: utils/dates.py:7
msgid "Sunday" msgid "Sunday"
msgstr "" msgstr "Nedjelja"
#: utils/dates.py:10 #: utils/dates.py:10
msgid "Mon" msgid "Mon"
msgstr "" msgstr "Pon"
#: utils/dates.py:10 #: utils/dates.py:10
msgid "Tue" msgid "Tue"
msgstr "" msgstr "Uto"
#: utils/dates.py:10 #: utils/dates.py:10
msgid "Wed" msgid "Wed"
msgstr "" msgstr "Sri"
#: utils/dates.py:10 #: utils/dates.py:10
msgid "Thu" msgid "Thu"
msgstr "" msgstr "Čet"
#: utils/dates.py:10 #: utils/dates.py:10
msgid "Fri" msgid "Fri"
msgstr "" msgstr "Pet"
#: utils/dates.py:11 #: utils/dates.py:11
msgid "Sat" msgid "Sat"
msgstr "" msgstr "Sub"
#: utils/dates.py:11 #: utils/dates.py:11
msgid "Sun" msgid "Sun"
msgstr "" msgstr "Ned"
#: utils/dates.py:18 #: utils/dates.py:18
msgid "January" msgid "January"
msgstr "" msgstr "Siječanj"
#: utils/dates.py:18 #: utils/dates.py:18
msgid "February" msgid "February"
msgstr "" msgstr "Veljača"
#: utils/dates.py:18 utils/dates.py:31 #: utils/dates.py:18 utils/dates.py:31
msgid "March" msgid "March"
msgstr "" msgstr "Ožujak"
#: utils/dates.py:18 utils/dates.py:31 #: utils/dates.py:18 utils/dates.py:31
msgid "April" msgid "April"
msgstr "" msgstr "Travanj"
#: utils/dates.py:18 utils/dates.py:31 #: utils/dates.py:18 utils/dates.py:31
msgid "May" msgid "May"
msgstr "" msgstr "Svibanj"
#: utils/dates.py:18 utils/dates.py:31 #: utils/dates.py:18 utils/dates.py:31
msgid "June" msgid "June"
msgstr "" msgstr "Lipanj"
#: utils/dates.py:19 utils/dates.py:31 #: utils/dates.py:19 utils/dates.py:31
msgid "July" msgid "July"
msgstr "" msgstr "Srpanj"
#: utils/dates.py:19 #: utils/dates.py:19
msgid "August" msgid "August"
msgstr "" msgstr "Kolovoz"
#: utils/dates.py:19 #: utils/dates.py:19
msgid "September" msgid "September"
msgstr "" msgstr "Rujan"
#: utils/dates.py:19 #: utils/dates.py:19
msgid "October" msgid "October"
msgstr "" msgstr "Listopad"
#: utils/dates.py:19 #: utils/dates.py:19
msgid "November" msgid "November"
msgstr "" msgstr "Studeni"
#: utils/dates.py:20 #: utils/dates.py:20
msgid "December" msgid "December"
msgstr "" msgstr "Prosinac"
#: utils/dates.py:23 #: utils/dates.py:23
msgid "jan" msgid "jan"
msgstr "" msgstr "sij."
#: utils/dates.py:23 #: utils/dates.py:23
msgid "feb" msgid "feb"
msgstr "" msgstr "velj."
#: utils/dates.py:23 #: utils/dates.py:23
msgid "mar" msgid "mar"
msgstr "" msgstr "ožu."
#: utils/dates.py:23 #: utils/dates.py:23
msgid "apr" msgid "apr"
msgstr "" msgstr "tra."
#: utils/dates.py:23 #: utils/dates.py:23
msgid "may" msgid "may"
msgstr "" msgstr "svi."
#: utils/dates.py:23 #: utils/dates.py:23
msgid "jun" msgid "jun"
msgstr "" msgstr "lip."
#: utils/dates.py:24 #: utils/dates.py:24
msgid "jul" msgid "jul"
msgstr "" msgstr "srp."
#: utils/dates.py:24 #: utils/dates.py:24
msgid "aug" msgid "aug"
msgstr "" msgstr "kol."
#: utils/dates.py:24 #: utils/dates.py:24
msgid "sep" msgid "sep"
msgstr "" msgstr "ruj."
#: utils/dates.py:24 #: utils/dates.py:24
msgid "oct" msgid "oct"
msgstr "" msgstr "lis."
#: utils/dates.py:24 #: utils/dates.py:24
msgid "nov" msgid "nov"
msgstr "" msgstr "stu."
#: utils/dates.py:24 #: utils/dates.py:24
msgid "dec" msgid "dec"
msgstr "" msgstr "pro."
#: utils/dates.py:31 #: utils/dates.py:31
msgid "Jan." msgid "Jan."
msgstr "" msgstr "Sij."
#: utils/dates.py:31 #: utils/dates.py:31
msgid "Feb." msgid "Feb."
msgstr "" msgstr "Velj."
#: utils/dates.py:32 #: utils/dates.py:32
msgid "Aug." msgid "Aug."
msgstr "" msgstr "Kol."
#: utils/dates.py:32 #: utils/dates.py:32
msgid "Sept." msgid "Sept."
msgstr "" msgstr "Ruj."
#: utils/dates.py:32 #: utils/dates.py:32
msgid "Oct." msgid "Oct."
msgstr "" msgstr "Lis."
#: utils/dates.py:32 #: utils/dates.py:32
msgid "Nov." msgid "Nov."
msgstr "" msgstr "Stu."
#: utils/dates.py:32 #: utils/dates.py:32
msgid "Dec." msgid "Dec."
msgstr "" msgstr "Pro."
#: utils/timesince.py:12 #: utils/timesince.py:12
msgid "year" msgid "year"
msgid_plural "years" msgid_plural "years"
msgstr[0] "" msgstr[0] "godina"
msgstr[1] "" msgstr[1] "godina"
#: utils/timesince.py:13 #: utils/timesince.py:13
msgid "month" msgid "month"
msgid_plural "months" msgid_plural "months"
msgstr[0] "" msgstr[0] "mjesec"
msgstr[1] "" msgstr[1] "mjesec"
#: utils/timesince.py:14 #: utils/timesince.py:14
msgid "week" msgid "week"
msgid_plural "weeks" msgid_plural "weeks"
msgstr[0] "" msgstr[0] "tjedan"
msgstr[1] "" msgstr[1] "tjedan"
#: utils/timesince.py:15 #: utils/timesince.py:15
msgid "day" msgid "day"
msgid_plural "days" msgid_plural "days"
msgstr[0] "" msgstr[0] "dan"
msgstr[1] "" msgstr[1] "dan"
#: utils/timesince.py:16 #: utils/timesince.py:16
msgid "hour" msgid "hour"
msgid_plural "hours" msgid_plural "hours"
msgstr[0] "" msgstr[0] "sati"
msgstr[1] "" msgstr[1] "sati"
#: utils/timesince.py:17 #: utils/timesince.py:17
msgid "minute" msgid "minute"
msgid_plural "minutes" msgid_plural "minutes"
msgstr[0] "" msgstr[0] "minute"
msgstr[1] "" msgstr[1] "minuta"
#: utils/timesince.py:39 #: utils/timesince.py:39
#, python-format #, python-format
@ -3228,7 +3228,7 @@ msgstr ""
#: utils/dateformat.py:97 #: utils/dateformat.py:97
msgid "midnight" msgid "midnight"
msgstr "" msgstr "ponoć"
#: utils/dateformat.py:99 #: utils/dateformat.py:99
msgid "noon" msgid "noon"
@ -3256,7 +3256,7 @@ msgstr ""
#: template/defaultfilters.py:485 #: template/defaultfilters.py:485
msgid "yes,no,maybe" msgid "yes,no,maybe"
msgstr "" msgstr "da,ne,možda"
#: template/defaultfilters.py:514 #: template/defaultfilters.py:514
#, python-format #, python-format

View File

@ -45,7 +45,14 @@ var UKRAINIAN_MAP = {
} }
var CZECH_MAP = { var CZECH_MAP = {
'č':'c', 'ď':'d', 'ě':'e', 'ň': 'n', 'ř':'r', 'š':'s', 'ť':'t', 'ů':'u', 'č':'c', 'ď':'d', 'ě':'e', 'ň': 'n', 'ř':'r', 'š':'s', 'ť':'t', 'ů':'u',
'ž':'z' 'ž':'z', 'Č':'C', 'Ď':'D', 'Ě':'E', 'Ň': 'N', 'Ř':'R', 'Š':'S', 'Ť':'T',
'Ů':'U', 'Ž':'Z'
}
var POLISH_MAP = {
'ą':'a', 'ć':'c', 'ę':'e', 'ł':'l', 'ń':'n', 'ó':'o', 'ś':'s', 'ź':'z',
'ż':'z', 'Ą':'A', 'Ć':'C', 'Ę':'e', 'Ł':'L', 'Ń':'N', 'Ó':'o', 'Ś':'S',
'Ź':'Z', 'Ż':'Z'
} }
var ALL_DOWNCODE_MAPS=new Array() var ALL_DOWNCODE_MAPS=new Array()
@ -56,6 +63,7 @@ ALL_DOWNCODE_MAPS[3]=TURKISH_MAP
ALL_DOWNCODE_MAPS[4]=RUSSIAN_MAP ALL_DOWNCODE_MAPS[4]=RUSSIAN_MAP
ALL_DOWNCODE_MAPS[5]=UKRAINIAN_MAP ALL_DOWNCODE_MAPS[5]=UKRAINIAN_MAP
ALL_DOWNCODE_MAPS[6]=CZECH_MAP ALL_DOWNCODE_MAPS[6]=CZECH_MAP
ALL_DOWNCODE_MAPS[7]=POLISH_MAP
var Downcoder = new Object(); var Downcoder = new Object();
Downcoder.Initialize = function() Downcoder.Initialize = function()

View File

@ -4,7 +4,7 @@ from django.contrib.syndication.feeds import Feed
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
class LatestFreeCommentsFeed(Feed): class LatestFreeCommentsFeed(Feed):
"Feed of latest comments on the current site." """Feed of latest free comments on the current site."""
comments_class = FreeComment comments_class = FreeComment
@ -30,7 +30,7 @@ class LatestFreeCommentsFeed(Feed):
return self.get_query_set()[:40] return self.get_query_set()[:40]
class LatestCommentsFeed(LatestFreeCommentsFeed): class LatestCommentsFeed(LatestFreeCommentsFeed):
"""Feed of latest free comments on the current site""" """Feed of latest comments on the current site."""
comments_class = Comment comments_class = Comment

View File

@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
from django.utils.translation import ugettext_lazy as _
PROVINCE_CHOICES = (
('01', _('Arava')),
('02', _('Albacete')),
('03', _('Alacant')),
('04', _('Almeria')),
('05', _('Avila')),
('06', _('Badajoz')),
('07', _('Illes Balears')),
('08', _('Barcelona')),
('09', _('Burgos')),
('10', _('Caceres')),
('11', _('Cadiz')),
('12', _('Castello')),
('13', _('Ciudad Real')),
('14', _('Cordoba')),
('15', _('A Coruna')),
('16', _('Cuenca')),
('17', _('Girona')),
('18', _('Granada')),
('19', _('Guadalajara')),
('20', _('Guipuzkoa')),
('21', _('Huelva')),
('22', _('Huesca')),
('23', _('Jaen')),
('24', _('Leon')),
('25', _('Lleida')),
('26', _('La Rioja')),
('27', _('Lugo')),
('28', _('Madrid')),
('29', _('Malaga')),
('30', _('Murcia')),
('31', _('Navarre')),
('32', _('Ourense')),
('33', _('Asturias')),
('34', _('Palencia')),
('35', _('Las Palmas')),
('36', _('Pontevedra')),
('37', _('Salamanca')),
('38', _('Santa Cruz de Tenerife')),
('39', _('Cantabria')),
('40', _('Segovia')),
('41', _('Seville')),
('42', _('Soria')),
('43', _('Tarragona')),
('44', _('Teruel')),
('45', _('Toledo')),
('46', _('Valencia')),
('47', _('Valladolid')),
('48', _('Bizkaia')),
('49', _('Zamora')),
('50', _('Zaragoza')),
('51', _('Ceuta')),
('52', _('Melilla')),
)

View File

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
from django.utils.translation import ugettext_lazy as _
REGION_CHOICES = (
('AN', _('Andalusia')),
('AR', _('Aragon')),
('O', _('Principality of Asturias')),
('IB', _('Balearic Islands')),
('PV', _('Basque Country')),
('CN', _('Canary Islands')),
('S', _('Cantabria')),
('CM', _('Castile-La Mancha')),
('CL', _('Castile and Leon')),
('CT', _('Catalonia')),
('EX', _('Extremadura')),
('GA', _('Galicia')),
('LO', _('La Rioja')),
('M', _('Madrid')),
('MU', _('Region of Murcia')),
('NA', _('Foral Community of Navarre')),
('VC', _('Valencian Community')),
)

View File

@ -0,0 +1,173 @@
# -*- coding: utf-8 -*-
"""
Spanish-specific Form helpers
"""
from django.newforms import ValidationError
from django.newforms.fields import RegexField, Select, EMPTY_VALUES
from django.utils.translation import ugettext as _
import re
class ESPostalCodeField(RegexField):
"""
A form field that validates its input as a spanish postal code.
Spanish postal code is a five digits string, with two first digits
between 01 and 52, assigned to provinces code.
"""
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)
class ESPhoneNumberField(RegexField):
"""
A form field that validates its input as a Spanish phone number.
Information numbers are ommited.
Spanish phone numbers are nine digit numbers, where first digit is 6 (for
cell phones), 8 (for special phones), or 9 (for landlines and special
phones)
TODO: accept and strip characters like dot, hyphen... in phone number
"""
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)
class ESIdentityCardNumberField(RegexField):
"""
Spanish NIF/NIE/CIF (Fiscal Identification Number) code.
Validates three diferent formats:
NIF (individuals): 12345678A
CIF (companies): A12345678
NIE (foreigners): X12345678A
according to a couple of simple checksum algorithms.
Value can include a space or hyphen separator between number and letters.
Number length is not checked for NIF (or NIE), old values start with a 1,
and future values can contain digits greater than 8. The CIF control digit
can be a number or a letter depending on company type. Algorithm is not
public, and different authors have different opinions on which ones allows
letters, so both validations are assumed true for all types.
"""
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),
*args, **kwargs)
def clean(self, value):
super(ESIdentityCardNumberField, self).clean(value)
if value in EMPTY_VALUES:
return u''
nif_get_checksum = lambda d: self.nif_control[int(d)%23]
value = value.upper().replace(' ', '').replace('-', '')
m = re.match(r'^([%s]?)[ -]?(\d+)[ -]?([%s]?)$' % (self.cif_types + self.nie_types, self.nif_control), value)
letter1, number, letter2 = m.groups()
if not letter1 and letter2:
# NIF
if letter2 == nif_get_checksum(number):
return value
else:
raise ValidationError, _('Invalid checksum for 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.')
elif not self.only_nif and letter1 in self.cif_types and len(number) in [7, 8]:
# CIF
if not letter2:
number, letter2 = number[:-1], int(number[-1])
checksum = cif_get_checksum(number)
if letter2 in [checksum, self.cif_control[checksum]]:
return value
else:
raise ValidationError, _('Invalid checksum for CIF.')
else:
raise ValidationError, _('Please enter a valid %s.' % self.id_types)
class ESCCCField(RegexField):
"""
A form field that validates its input as a Spanish bank account or CCC
(Codigo Cuenta Cliente).
Spanish CCC is in format EEEE-OOOO-CC-AAAAAAAAAA where:
E = entity
O = office
C = checksum
A = account
It's also valid to use a space as delimiter, or to use no delimiter.
First checksum digit validates entity and office, and last one
validates account. Validation is done multiplying every digit of 10
digit value (with leading 0 if necessary) by number in its position in
string 1, 2, 4, 8, 5, 10, 9, 7, 3, 6. Sum resulting numbers and extract
it from 11. Result is checksum except when 10 then is 1, or when 11
then is 0.
TODO: allow IBAN validation too
"""
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)
def clean(self, value):
super(ESCCCField, self).clean(value)
if value in EMPTY_VALUES:
return u''
control_str = [1, 2, 4, 8, 5, 10, 9, 7, 3, 6]
m = re.match(r'^(\d{4})[ -]?(\d{4})[ -]?(\d{2})[ -]?(\d{10})$', value)
entity, office, checksum, account = m.groups()
get_checksum = lambda d: str(11 - sum([int(digit) * int(control) for digit, control in zip(d, control_str)]) % 11).replace('10', '1').replace('11', '0')
if get_checksum('00' + entity + office) + get_checksum(account) == checksum:
return value
else:
raise ValidationError, _('Invalid checksum for bank account number.')
class ESRegionSelect(Select):
"""
A Select widget that uses a list of spanish regions as its choices.
"""
def __init__(self, attrs=None):
from es_regions import REGION_CHOICES
super(ESRegionSelect, self).__init__(attrs, choices=REGION_CHOICES)
class ESProvinceSelect(Select):
"""
A Select widget that uses a list of spanish provinces as its choices.
"""
def __init__(self, attrs=None):
from es_provinces import PROVINCE_CHOICES
super(ESProvinceSelect, self).__init__(attrs, choices=PROVINCE_CHOICES)
def cif_get_checksum(number):
s1 = sum([int(digit) for pos, digit in enumerate(number) if int(pos) % 2])
s2 = sum([sum([int(unit) for unit in str(int(digit) * 2)]) for pos, digit in enumerate(number) if not int(pos) % 2])
return 10 - ((s1 + s2) % 10)

View File

@ -2,6 +2,8 @@
Polish-specific form helpers Polish-specific form helpers
""" """
import re
from django.newforms import ValidationError from django.newforms import ValidationError
from django.newforms.fields import Select, RegexField from django.newforms.fields import Select, RegexField
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
@ -34,20 +36,6 @@ class PLNationalIdentificationNumberField(RegexField):
The algorithm is documented at http://en.wikipedia.org/wiki/PESEL. The algorithm is documented at http://en.wikipedia.org/wiki/PESEL.
""" """
def has_valid_checksum(self, number):
"""
Calculates a checksum with the provided algorithm.
"""
multiple_table = (1, 3, 7, 9, 1, 3, 7, 9, 1, 3, 1)
result = 0
for i in range(len(number)):
result += int(number[i])*multiple_table[i]
if result % 10 == 0:
return True
else:
return False
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(PLNationalIdentificationNumberField, self).__init__(r'^\d{11}$', super(PLNationalIdentificationNumberField, self).__init__(r'^\d{11}$',
max_length=None, min_length=None, error_message=_(u'National Identification Number consists of 11 digits.'), max_length=None, min_length=None, error_message=_(u'National Identification Number consists of 11 digits.'),
@ -59,17 +47,95 @@ class PLNationalIdentificationNumberField(RegexField):
raise ValidationError(_(u'Wrong checksum for the National Identification Number.')) raise ValidationError(_(u'Wrong checksum for the National Identification Number.'))
return u'%s' % value return u'%s' % value
def has_valid_checksum(self, number):
"""
Calculates a checksum with the provided algorithm.
"""
multiple_table = (1, 3, 7, 9, 1, 3, 7, 9, 1, 3, 1)
result = 0
for i in range(len(number)):
result += int(number[i]) * multiple_table[i]
return result % 10 == 0
class PLTaxNumberField(RegexField): class PLTaxNumberField(RegexField):
""" """
A form field that validates as Polish Tax Number (NIP). A form field that validates as Polish Tax Number (NIP).
Valid forms are: XXX-XXX-YY-YY or XX-XX-YYY-YYY. Valid forms are: XXX-XXX-YY-YY or XX-XX-YYY-YYY.
Checksum algorithm based on documentation at
http://wipos.p.lodz.pl/zylla/ut/nip-rego.html
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(PLTaxNumberField, self).__init__(r'^\d{3}-\d{3}-\d{2}-\d{2}$|^\d{2}-\d{2}-\d{3}-\d{3}$', super(PLTaxNumberField, self).__init__(r'^\d{3}-\d{3}-\d{2}-\d{2}$|^\d{2}-\d{2}-\d{3}-\d{3}$',
max_length=None, min_length=None, max_length=None, min_length=None,
error_message=_(u'Enter a tax number field (NIP) in the format XXX-XXX-XX-XX or XX-XX-XXX-XXX.'), *args, **kwargs) error_message=_(u'Enter a tax number field (NIP) in the format XXX-XXX-XX-XX or XX-XX-XXX-XXX.'), *args, **kwargs)
def clean(self,value):
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).'))
return u'%s' % value
def has_valid_checksum(self, number):
"""
Calculates a checksum with the provided algorithm.
"""
multiple_table = (6, 5, 7, 2, 3, 4, 5, 6, 7)
result = 0
for i in range(len(number)-1):
result += int(number[i]) * multiple_table[i]
result %= 11
if result == int(number[-1]):
return True
else:
return False
class PLNationalBusinessRegisterField(RegexField):
"""
A form field that validated as Polish National Official Business Register Number (REGON)
Valid forms are: 7 or 9 digits number
More on the field: http://www.stat.gov.pl/bip/regon_ENG_HTML.htm
The checksum algorithm is documented at http://wipos.p.lodz.pl/zylla/ut/nip-rego.html
"""
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)
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).'))
return u'%s' % value
def has_valid_checksum(self, number):
"""
Calculates a checksum with the provided algorithm.
"""
multiple_table_7 = (2, 3, 4, 5, 6, 7)
multiple_table_9 = (8, 9, 2, 3, 4, 5, 6, 7)
result = 0
if len(number) == 7:
multiple_table = multiple_table_7
else:
multiple_table = multiple_table_9
for i in range(len(number)-1):
result += int(number[i]) * multiple_table[i]
result %= 11
if result == 10:
result = 0
if result == int(number[-1]):
return True
else:
return False
class PLPostalCodeField(RegexField): class PLPostalCodeField(RegexField):
""" """

View File

@ -48,6 +48,7 @@ class SessionBase(object):
return self._session.get(key, default) return self._session.get(key, default)
def pop(self, key, *args): def pop(self, key, *args):
self.modified = self.modified or key in self._session
return self._session.pop(key, *args) return self._session.pop(key, *args)
def set_test_cookie(self): def set_test_cookie(self):

View File

@ -39,8 +39,10 @@ class SessionMiddleware(object):
# Save the seesion data and refresh the client cookie. # Save the seesion data and refresh the client cookie.
request.session.save() request.session.save()
response.set_cookie(settings.SESSION_COOKIE_NAME, request.session.session_key, response.set_cookie(settings.SESSION_COOKIE_NAME,
max_age=max_age, expires=expires, domain=settings.SESSION_COOKIE_DOMAIN, request.session.session_key, max_age=max_age,
secure=settings.SESSION_COOKIE_SECURE or None) expires=expires, domain=settings.SESSION_COOKIE_DOMAIN,
path=settings.SESSION_COOKIE_PATH,
secure=settings.SESSION_COOKIE_SECURE or None)
return response return response

View File

@ -3,6 +3,7 @@ r"""
>>> from django.contrib.sessions.backends.db import SessionStore as DatabaseSession >>> from django.contrib.sessions.backends.db import SessionStore as DatabaseSession
>>> from django.contrib.sessions.backends.cache import SessionStore as CacheSession >>> from django.contrib.sessions.backends.cache import SessionStore as CacheSession
>>> from django.contrib.sessions.backends.file import SessionStore as FileSession >>> from django.contrib.sessions.backends.file import SessionStore as FileSession
>>> from django.contrib.sessions.backends.base import SessionBase
>>> db_session = DatabaseSession() >>> db_session = DatabaseSession()
>>> db_session.modified >>> db_session.modified
@ -52,6 +53,28 @@ True
>>> cache_session.delete(cache_session.session_key) >>> cache_session.delete(cache_session.session_key)
>>> cache_session.exists(cache_session.session_key) >>> cache_session.exists(cache_session.session_key)
False False
>>> s = SessionBase()
>>> s._session['some key'] = 'exists' # Pre-populate the session with some data
>>> s.accessed = False # Reset to pretend this wasn't accessed previously
>>> s.accessed, s.modified
(False, False)
>>> s.pop('non existant key', 'does not exist')
'does not exist'
>>> s.accessed, s.modified
(True, False)
>>> s.accessed = False # Reset the accessed flag
>>> s.pop('some key')
'exists'
>>> s.accessed, s.modified
(True, True)
>>> s.pop('some key', 'does not exist')
'does not exist'
""" """
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -89,6 +89,7 @@ class Feed(object):
categories = self.__get_dynamic_attr('categories', obj), categories = self.__get_dynamic_attr('categories', obj),
feed_copyright = self.__get_dynamic_attr('feed_copyright', obj), feed_copyright = self.__get_dynamic_attr('feed_copyright', obj),
feed_guid = self.__get_dynamic_attr('feed_guid', obj), feed_guid = self.__get_dynamic_attr('feed_guid', obj),
ttl = self.__get_dynamic_attr('ttl', obj),
) )
try: try:

View File

@ -14,6 +14,14 @@ class BaseCache(object):
timeout = 300 timeout = 300
self.default_timeout = timeout self.default_timeout = timeout
def add(self, key, value, timeout=None):
"""
Set a value in the cache if the key does not already exist. If
timeout is given, that timeout will be used for the key; otherwise
the default cache timeout will be used.
"""
raise NotImplementedError
def get(self, key, default=None): def get(self, key, default=None):
""" """
Fetch a given key from the cache. If the key does not exist, return Fetch a given key from the cache. If the key does not exist, return

View File

@ -38,6 +38,12 @@ class CacheClass(BaseCache):
return pickle.loads(base64.decodestring(row[1])) return pickle.loads(base64.decodestring(row[1]))
def set(self, key, value, timeout=None): def set(self, key, value, timeout=None):
return self._base_set('set', key, value, timeout)
def add(self, key, value, timeout=None):
return self._base_set('add', key, value, timeout)
def _base_set(self, mode, key, value, timeout=None):
if timeout is None: if timeout is None:
timeout = self.default_timeout timeout = self.default_timeout
cursor = connection.cursor() cursor = connection.cursor()
@ -50,7 +56,7 @@ class CacheClass(BaseCache):
encoded = base64.encodestring(pickle.dumps(value, 2)).strip() encoded = base64.encodestring(pickle.dumps(value, 2)).strip()
cursor.execute("SELECT cache_key FROM %s WHERE cache_key = %%s" % self._table, [key]) cursor.execute("SELECT cache_key FROM %s WHERE cache_key = %%s" % self._table, [key])
try: try:
if cursor.fetchone(): if mode == 'set' and cursor.fetchone():
cursor.execute("UPDATE %s SET value = %%s, expires = %%s WHERE cache_key = %%s" % self._table, [encoded, str(exp), key]) cursor.execute("UPDATE %s SET value = %%s, expires = %%s WHERE cache_key = %%s" % self._table, [encoded, str(exp), key])
else: else:
cursor.execute("INSERT INTO %s (cache_key, value, expires) VALUES (%%s, %%s, %%s)" % self._table, [key, encoded, str(exp)]) cursor.execute("INSERT INTO %s (cache_key, value, expires) VALUES (%%s, %%s, %%s)" % self._table, [key, encoded, str(exp)])

View File

@ -6,6 +6,9 @@ class CacheClass(BaseCache):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
pass pass
def add(self, *args, **kwargs):
pass
def get(self, key, default=None): def get(self, key, default=None):
return default return default

View File

@ -17,6 +17,26 @@ class CacheClass(SimpleCacheClass):
del self._cache del self._cache
del self._expire_info del self._expire_info
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
def get(self, key, default=None): def get(self, key, default=None):
fname = self._key_to_file(key) fname = self._key_to_file(key)
try: try:

View File

@ -14,6 +14,13 @@ class CacheClass(SimpleCacheClass):
SimpleCacheClass.__init__(self, host, params) SimpleCacheClass.__init__(self, host, params)
self._lock = RWLock() self._lock = RWLock()
def add(self, key, value, timeout=None):
self._lock.writer_enters()
try:
SimpleCacheClass.add(self, key, value, timeout)
finally:
self._lock.writer_leaves()
def get(self, key, default=None): def get(self, key, default=None):
should_delete = False should_delete = False
self._lock.reader_enters() self._lock.reader_enters()

View File

@ -16,6 +16,9 @@ class CacheClass(BaseCache):
BaseCache.__init__(self, params) BaseCache.__init__(self, params)
self._cache = memcache.Client(server.split(';')) self._cache = memcache.Client(server.split(';'))
def add(self, key, value, timeout=0):
self._cache.add(key.encode('ascii', 'ignore'), value, timeout or self.default_timeout)
def get(self, key, default=None): def get(self, key, default=None):
val = self._cache.get(smart_str(key)) val = self._cache.get(smart_str(key))
if val is None: if val is None:

View File

@ -21,6 +21,15 @@ class CacheClass(BaseCache):
except (ValueError, TypeError): except (ValueError, TypeError):
self._cull_frequency = 3 self._cull_frequency = 3
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 get(self, key, default=None): def get(self, key, default=None):
now = time.time() now = time.time()
exp = self._expire_info.get(key) exp = self._expire_info.get(key)

View File

@ -14,7 +14,7 @@ import os
class ModPythonRequest(http.HttpRequest): class ModPythonRequest(http.HttpRequest):
def __init__(self, req): def __init__(self, req):
self._req = req self._req = req
self.path = force_unicode(req.uri, errors='ignore') self.path = force_unicode(req.uri)
def __repr__(self): def __repr__(self):
# Since this is called as part of error handling, we need to be very # Since this is called as part of error handling, we need to be very
@ -136,6 +136,8 @@ class ModPythonRequest(http.HttpRequest):
method = property(_get_method) method = property(_get_method)
class ModPythonHandler(BaseHandler): class ModPythonHandler(BaseHandler):
request_class = ModPythonRequest
def __call__(self, req): def __call__(self, req):
# mod_python fakes the environ, and thus doesn't process SetEnv. This fixes that # mod_python fakes the environ, and thus doesn't process SetEnv. This fixes that
os.environ.update(req.subprocess_env) os.environ.update(req.subprocess_env)
@ -150,13 +152,16 @@ class ModPythonHandler(BaseHandler):
dispatcher.send(signal=signals.request_started) dispatcher.send(signal=signals.request_started)
try: try:
request = ModPythonRequest(req) try:
response = self.get_response(request) request = self.request_class(req)
except UnicodeDecodeError:
# Apply response middleware response = http.HttpResponseBadRequest()
for middleware_method in self._response_middleware: else:
response = middleware_method(request, response) response = self.get_response(request)
# Apply response middleware
for middleware_method in self._response_middleware:
response = middleware_method(request, response)
finally: finally:
dispatcher.send(signal=signals.request_finished) dispatcher.send(signal=signals.request_finished)

View File

@ -75,7 +75,7 @@ def safe_copyfileobj(fsrc, fdst, length=16*1024, size=0):
class WSGIRequest(http.HttpRequest): class WSGIRequest(http.HttpRequest):
def __init__(self, environ): def __init__(self, environ):
self.environ = environ self.environ = environ
self.path = force_unicode(environ['PATH_INFO'], errors='ignore') self.path = force_unicode(environ['PATH_INFO'])
self.META = environ self.META = environ
self.method = environ['REQUEST_METHOD'].upper() self.method = environ['REQUEST_METHOD'].upper()
@ -165,7 +165,9 @@ class WSGIRequest(http.HttpRequest):
content_length = int(self.environ.get('CONTENT_LENGTH', 0)) content_length = int(self.environ.get('CONTENT_LENGTH', 0))
except ValueError: # if CONTENT_LENGTH was empty string or not an integer except ValueError: # if CONTENT_LENGTH was empty string or not an integer
content_length = 0 content_length = 0
safe_copyfileobj(self.environ['wsgi.input'], buf, size=content_length) if content_length > 0:
safe_copyfileobj(self.environ['wsgi.input'], buf,
size=content_length)
self._raw_post_data = buf.getvalue() self._raw_post_data = buf.getvalue()
buf.close() buf.close()
return self._raw_post_data return self._raw_post_data
@ -179,6 +181,7 @@ class WSGIRequest(http.HttpRequest):
class WSGIHandler(BaseHandler): class WSGIHandler(BaseHandler):
initLock = Lock() initLock = Lock()
request_class = WSGIRequest
def __call__(self, environ, start_response): def __call__(self, environ, start_response):
from django.conf import settings from django.conf import settings
@ -194,13 +197,16 @@ class WSGIHandler(BaseHandler):
dispatcher.send(signal=signals.request_started) dispatcher.send(signal=signals.request_started)
try: try:
request = WSGIRequest(environ) try:
response = self.get_response(request) request = self.request_class(environ)
except UnicodeDecodeError:
# Apply response middleware response = http.HttpResponseBadRequest()
for middleware_method in self._response_middleware: else:
response = middleware_method(request, response) response = self.get_response(request)
# Apply response middleware
for middleware_method in self._response_middleware:
response = middleware_method(request, response)
finally: finally:
dispatcher.send(signal=signals.request_finished) dispatcher.send(signal=signals.request_finished)

View File

@ -73,7 +73,7 @@ class SafeMIMEText(MIMEText):
if '\n' in val or '\r' in val: if '\n' in val or '\r' in val:
raise BadHeaderError, "Header values can't contain newlines (got %r for header %r)" % (val, name) raise BadHeaderError, "Header values can't contain newlines (got %r for header %r)" % (val, name)
try: try:
val = str(force_unicode(val)) val = force_unicode(val).encode('ascii')
except UnicodeEncodeError: except UnicodeEncodeError:
if name.lower() in ('to', 'from', 'cc'): if name.lower() in ('to', 'from', 'cc'):
result = [] result = []
@ -92,7 +92,7 @@ class SafeMIMEMultipart(MIMEMultipart):
if '\n' in val or '\r' in val: if '\n' in val or '\r' in val:
raise BadHeaderError, "Header values can't contain newlines (got %r for header %r)" % (val, name) raise BadHeaderError, "Header values can't contain newlines (got %r for header %r)" % (val, name)
try: try:
val = str(force_unicode(val)) val = force_unicode(val).encode('ascii')
except UnicodeEncodeError: except UnicodeEncodeError:
if name.lower() in ('to', 'from', 'cc'): if name.lower() in ('to', 'from', 'cc'):
result = [] result = []

View File

@ -1,10 +1,11 @@
import django
from django.core.management.base import BaseCommand, CommandError, handle_default_options
from optparse import OptionParser
import os import os
import sys import sys
from optparse import OptionParser
from imp import find_module from imp import find_module
import django
from django.core.management.base import BaseCommand, CommandError, handle_default_options
# For backwards compatibility: get_version() used to be in this module. # For backwards compatibility: get_version() used to be in this module.
get_version = django.get_version get_version = django.get_version
@ -14,18 +15,21 @@ _commands = None
def find_commands(management_dir): def find_commands(management_dir):
""" """
Given a path to a management directory, return a list of all the command names Given a path to a management directory, returns a list of all the command
that are available. Returns an empty list if no commands are defined. names that are available.
Returns an empty list if no commands are defined.
""" """
command_dir = os.path.join(management_dir,'commands') command_dir = os.path.join(management_dir, 'commands')
try: try:
return [f[:-3] for f in os.listdir(command_dir) if not f.startswith('_') and f.endswith('.py')] return [f[:-3] for f in os.listdir(command_dir)
if not f.startswith('_') and f.endswith('.py')]
except OSError: except OSError:
return [] return []
def find_management_module(app_name): def find_management_module(app_name):
""" """
Determine the path to the management module for the application named, Determines the path to the management module for the application named,
without acutally importing the application or the management module. without acutally importing the application or the management module.
Raises ImportError if the management module cannot be found for any reason. Raises ImportError if the management module cannot be found for any reason.
@ -36,7 +40,7 @@ def find_management_module(app_name):
path = None path = None
while parts: while parts:
part = parts.pop() part = parts.pop()
f,path,descr = find_module(part, path and [path] or None) f, path, descr = find_module(part, path and [path] or None)
return path return path
def load_command_class(app_name, name): def load_command_class(app_name, name):
@ -77,7 +81,7 @@ def get_commands(load_user_commands=True, project_directory=None):
_commands = dict([(name, 'django.core') _commands = dict([(name, 'django.core')
for name in find_commands(__path__[0])]) for name in find_commands(__path__[0])])
if load_user_commands: if load_user_commands:
# Get commands from all installed apps # Get commands from all installed apps.
from django.conf import settings from django.conf import settings
for app_name in settings.INSTALLED_APPS: for app_name in settings.INSTALLED_APPS:
try: try:
@ -130,7 +134,7 @@ class LaxOptionParser(OptionParser):
the commands (and thus the options) that are available to the user. the commands (and thus the options) that are available to the user.
""" """
def error(self, msg): def error(self, msg):
pass pass
class ManagementUtility(object): class ManagementUtility(object):
""" """
@ -150,10 +154,13 @@ class ManagementUtility(object):
Returns the script's main help text, as a string. Returns the script's main help text, as a string.
""" """
usage = ['%s <subcommand> [options] [args]' % self.prog_name] usage = ['%s <subcommand> [options] [args]' % self.prog_name]
usage.append('Django command line tool, version %s' % django.get_version()) usage.append('Django command line tool,'
usage.append("Type '%s help <subcommand>' for help on a specific subcommand." % self.prog_name) ' version %s' % django.get_version())
usage.append("Type '%s help <subcommand>' for help on a specific"
" subcommand." % self.prog_name)
usage.append('Available subcommands:') usage.append('Available subcommands:')
commands = get_commands(self.user_commands, self.project_directory).keys() commands = get_commands(self.user_commands,
self.project_directory).keys()
commands.sort() commands.sort()
for cmd in commands: for cmd in commands:
usage.append(' %s' % cmd) usage.append(' %s' % cmd)
@ -166,14 +173,16 @@ class ManagementUtility(object):
django-admin.py or manage.py) if it can't be found. django-admin.py or manage.py) if it can't be found.
""" """
try: try:
app_name = get_commands(self.user_commands, self.project_directory)[subcommand] app_name = get_commands(self.user_commands,
self.project_directory)[subcommand]
if isinstance(app_name, BaseCommand): if isinstance(app_name, BaseCommand):
# If the command is already loaded, use it directly. # If the command is already loaded, use it directly.
klass = app_name klass = app_name
else: else:
klass = load_command_class(app_name, subcommand) klass = load_command_class(app_name, subcommand)
except KeyError: except KeyError:
sys.stderr.write("Unknown command: %r\nType '%s help' for 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) sys.exit(1)
return klass return klass
@ -182,9 +191,9 @@ class ManagementUtility(object):
Given the command-line arguments, this figures out which subcommand is Given the command-line arguments, this figures out which subcommand is
being run, creates a parser appropriate to that command, and runs it. being run, creates a parser appropriate to that command, and runs it.
""" """
# Preprocess options to extract --settings and --pythonpath. These options # Preprocess options to extract --settings and --pythonpath.
# could affect the commands that are available, so they must be processed # These options could affect the commands that are available, so they
# early # must be processed early.
parser = LaxOptionParser(version=get_version(), parser = LaxOptionParser(version=get_version(),
option_list=BaseCommand.option_list) option_list=BaseCommand.option_list)
try: try:
@ -208,7 +217,8 @@ class ManagementUtility(object):
# Special-cases: We want 'django-admin.py --version' and # Special-cases: We want 'django-admin.py --version' and
# 'django-admin.py --help' to work, for backwards compatibility. # 'django-admin.py --help' to work, for backwards compatibility.
elif self.argv[1:] == ['--version']: elif self.argv[1:] == ['--version']:
print django.get_version() # LaxOptionParser already takes care of printing the version.
pass
elif self.argv[1:] == ['--help']: elif self.argv[1:] == ['--help']:
sys.stderr.write(self.main_help_text() + '\n') sys.stderr.write(self.main_help_text() + '\n')
else: else:
@ -230,7 +240,7 @@ class ProjectManagementUtility(ManagementUtility):
def setup_environ(settings_mod): def setup_environ(settings_mod):
""" """
Configure the runtime environment. This can also be used by external Configures the runtime environment. This can also be used by external
scripts wanting to set up a similar environment to manage.py. scripts wanting to set up a similar environment to manage.py.
""" """
# Add this project to sys.path so that it's importable in the conventional # Add this project to sys.path so that it's importable in the conventional
@ -244,7 +254,8 @@ def setup_environ(settings_mod):
sys.path.pop() sys.path.pop()
# Set DJANGO_SETTINGS_MODULE appropriately. # Set DJANGO_SETTINGS_MODULE appropriately.
os.environ['DJANGO_SETTINGS_MODULE'] = '%s.%s' % (project_name, settings_name) os.environ['DJANGO_SETTINGS_MODULE'] = '%s.%s' % (project_name,
settings_name)
return project_directory return project_directory
def execute_from_command_line(argv=None): def execute_from_command_line(argv=None):

View File

@ -35,6 +35,8 @@ def get_validation_errors(outfile, app=None):
for f in opts.fields: for f in opts.fields:
if f.name == 'id' and not f.primary_key and opts.pk.name == 'id': if f.name == 'id' and not f.primary_key and opts.pk.name == 'id':
e.add(opts, '"%s": You can\'t use "id" as a field name, because each model automatically gets an "id" field if none of the fields have primary_key=True. You need to either remove/rename your "id" field or add primary_key=True to a field.' % f.name) e.add(opts, '"%s": You can\'t use "id" as a field name, because each model automatically gets an "id" field if none of the fields have primary_key=True. You need to either remove/rename your "id" field or add primary_key=True to a field.' % f.name)
if f.name.endswith('_'):
e.add(opts, '"%s": Field names cannot end with underscores, because this would lead to ambiguous queryset filters.' % f.name)
if isinstance(f, models.CharField) and f.max_length in (None, 0): if isinstance(f, models.CharField) and f.max_length in (None, 0):
e.add(opts, '"%s": CharFields require a "max_length" attribute.' % f.name) e.add(opts, '"%s": CharFields require a "max_length" attribute.' % f.name)
if isinstance(f, models.DecimalField): if isinstance(f, models.DecimalField):

View File

@ -249,8 +249,9 @@ class RegexURLResolver(object):
except AttributeError: except AttributeError:
try: try:
self._urlconf_module = __import__(self.urlconf_name, {}, {}, ['']) self._urlconf_module = __import__(self.urlconf_name, {}, {}, [''])
except ValueError, e: except Exception, e:
# Invalid urlconf_name, such as "foo.bar." (note trailing period) # Either an invalid urlconf_name, such as "foo.bar.", or some
# kind of problem during the actual import.
raise ImproperlyConfigured, "Error while importing URLconf %r: %s" % (self.urlconf_name, e) raise ImproperlyConfigured, "Error while importing URLconf %r: %s" % (self.urlconf_name, e)
return self._urlconf_module return self._urlconf_module
urlconf_module = property(_get_urlconf_module) urlconf_module = property(_get_urlconf_module)

View File

@ -127,6 +127,27 @@ class BaseDatabaseOperations(object):
""" """
raise NotImplementedError('Full-text search is not implemented for this database backend') raise NotImplementedError('Full-text search is not implemented for this database backend')
def last_executed_query(self, cursor, sql, params):
"""
Returns a string of the query last executed by the given cursor, with
placeholders replaced with actual values.
`sql` is the raw query containing placeholders, and `params` is the
sequence of parameters. These are used by default, but this method
exists for database backends to provide a better implementation
according to their own quoting schemes.
"""
from django.utils.encoding import smart_unicode, force_unicode
# Convert params to contain Unicode values.
to_unicode = lambda s: force_unicode(s, strings_only=True)
if isinstance(params, (list, tuple)):
u_params = tuple([to_unicode(val) for val in params])
else:
u_params = dict([(to_unicode(k), to_unicode(v)) for k, v in params.items()])
return smart_unicode(sql) % u_params
def last_insert_id(self, cursor, table_name, pk_name): def last_insert_id(self, cursor, table_name, pk_name):
""" """
Given a cursor object that has just performed an INSERT statement into Given a cursor object that has just performed an INSERT statement into

View File

@ -5,7 +5,7 @@ Requires psycopg 2: http://initd.org/projects/psycopg2
""" """
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures
from django.db.backends.postgresql.operations import DatabaseOperations from django.db.backends.postgresql.operations import DatabaseOperations as PostgresqlDatabaseOperations
try: try:
import psycopg2 as Database import psycopg2 as Database
import psycopg2.extensions import psycopg2.extensions
@ -21,6 +21,13 @@ psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
class DatabaseFeatures(BaseDatabaseFeatures): class DatabaseFeatures(BaseDatabaseFeatures):
needs_datetime_string_cast = False needs_datetime_string_cast = False
class DatabaseOperations(PostgresqlDatabaseOperations):
def last_executed_query(self, cursor, sql, params):
# With psycopg2, cursor objects have a "query" attribute that is the
# exact query sent to the database. See docs here:
# http://www.initd.org/tracker/psycopg/wiki/psycopg2_documentation#postgresql-status-message-and-executed-query
return cursor.query
class DatabaseWrapper(BaseDatabaseWrapper): class DatabaseWrapper(BaseDatabaseWrapper):
features = DatabaseFeatures() features = DatabaseFeatures()
ops = DatabaseOperations() ops = DatabaseOperations()

View File

@ -1,7 +1,6 @@
import datetime import datetime
import md5 import md5
from time import time from time import time
from django.utils.encoding import smart_unicode, force_unicode
try: try:
import decimal import decimal
@ -11,7 +10,7 @@ except ImportError:
class CursorDebugWrapper(object): class CursorDebugWrapper(object):
def __init__(self, cursor, db): def __init__(self, cursor, db):
self.cursor = cursor self.cursor = cursor
self.db = db self.db = db # Instance of a BaseDatabaseWrapper subclass
def execute(self, sql, params=()): def execute(self, sql, params=()):
start = time() start = time()
@ -19,8 +18,9 @@ class CursorDebugWrapper(object):
return self.cursor.execute(sql, params) return self.cursor.execute(sql, params)
finally: finally:
stop = time() stop = time()
sql = self.db.ops.last_executed_query(self.cursor, sql, params)
self.db.queries.append({ self.db.queries.append({
'sql': smart_unicode(sql) % convert_args(params), 'sql': sql,
'time': "%.3f" % (stop - start), 'time': "%.3f" % (stop - start),
}) })
@ -31,7 +31,7 @@ class CursorDebugWrapper(object):
finally: finally:
stop = time() stop = time()
self.db.queries.append({ self.db.queries.append({
'sql': 'MANY: ' + sql + ' ' + smart_unicode(tuple(param_list)), 'sql': '%s times: %s' % (len(param_list), sql),
'time': "%.3f" % (stop - start), 'time': "%.3f" % (stop - start),
}) })
@ -41,16 +41,6 @@ class CursorDebugWrapper(object):
else: else:
return getattr(self.cursor, attr) return getattr(self.cursor, attr)
def convert_args(args):
"""
Convert sequence or dictionary to contain unicode values.
"""
to_unicode = lambda s: force_unicode(s, strings_only=True)
if isinstance(args, (list, tuple)):
return tuple([to_unicode(val) for val in args])
else:
return dict([(to_unicode(k), to_unicode(v)) for k, v in args.items()])
############################################### ###############################################
# Converters from database (string) to Python # # Converters from database (string) to Python #
############################################### ###############################################

View File

@ -395,6 +395,8 @@ class Field(object):
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text} defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
if self.choices: if self.choices:
defaults['widget'] = forms.Select(choices=self.get_choices()) defaults['widget'] = forms.Select(choices=self.get_choices())
if self.has_default():
defaults['initial'] = self.get_default()
defaults.update(kwargs) defaults.update(kwargs)
return form_class(**defaults) return form_class(**defaults)

View File

@ -1,5 +1,6 @@
"""Refactored "safe reference" from dispatcher.py""" """Refactored "safe reference" from dispatcher.py"""
import weakref, traceback import weakref, traceback
from django.utils.functional import curry
def safeRef(target, onDelete = None): def safeRef(target, onDelete = None):
"""Return a *safe* weak reference to a callable target """Return a *safe* weak reference to a callable target
@ -17,7 +18,7 @@ def safeRef(target, onDelete = None):
# Turn a bound method into a BoundMethodWeakref instance. # Turn a bound method into a BoundMethodWeakref instance.
# Keep track of these instances for lookup by disconnect(). # Keep track of these instances for lookup by disconnect().
assert hasattr(target, 'im_func'), """safeRef target %r has im_self, but no im_func, don't know how to create reference"""%( target,) assert hasattr(target, 'im_func'), """safeRef target %r has im_self, but no im_func, don't know how to create reference"""%( target,)
reference = BoundMethodWeakref( reference = get_bound_method_weakref(
target=target, target=target,
onDelete=onDelete onDelete=onDelete
) )
@ -163,3 +164,75 @@ class BoundMethodWeakref(object):
if function is not None: if function is not None:
return function.__get__(target) return function.__get__(target)
return None return None
class BoundNonDescriptorMethodWeakref(BoundMethodWeakref):
"""A specialized BoundMethodWeakref, for platforms where instance methods
are not descriptors.
It assumes that the function name and the target attribute name are the
same, instead of assuming that the function is a descriptor. This approach
is equally fast, but not 100% reliable because functions can be stored on an
attribute named differenty than the function's name such as in:
class A: pass
def foo(self): return "foo"
A.bar = foo
But this shouldn't be a common use case. So, on platforms where methods
aren't descriptors (such as Jython) this implementation has the advantage
of working in the most cases.
"""
def __init__(self, target, onDelete=None):
"""Return a weak-reference-like instance for a bound method
target -- the instance-method target for the weak
reference, must have im_self and im_func attributes
and be reconstructable via:
target.im_func.__get__( target.im_self )
which is true of built-in instance methods.
onDelete -- optional callback which will be called
when this weak reference ceases to be valid
(i.e. either the object or the function is garbage
collected). Should take a single argument,
which will be passed a pointer to this object.
"""
assert getattr(target.im_self, target.__name__) == target, \
("method %s isn't available as the attribute %s of %s" %
(target, target.__name__, target.im_self))
super(BoundNonDescriptorMethodWeakref, self).__init__(target, onDelete)
def __call__(self):
"""Return a strong reference to the bound method
If the target cannot be retrieved, then will
return None, otherwise returns a bound instance
method for our object and function.
Note:
You may call this method any number of times,
as it does not invalidate the reference.
"""
target = self.weakSelf()
if target is not None:
function = self.weakFunc()
if function is not None:
# Using curry() would be another option, but it erases the
# "signature" of the function. That is, after a function is
# curried, the inspect module can't be used to determine how
# many arguments the function expects, nor what keyword
# arguments it supports, and pydispatcher needs this
# information.
return getattr(target, function.__name__)
return None
def get_bound_method_weakref(target, onDelete):
"""Instantiates the appropiate BoundMethodWeakRef, depending on the details of
the underlying class method implementation"""
if hasattr(target, '__get__'):
# target method is a descriptor, so the default implementation works:
return BoundMethodWeakref(target=target, onDelete=onDelete)
else:
# no luck, use the alternative implementation:
return BoundNonDescriptorMethodWeakref(target=target, onDelete=onDelete)

View File

@ -261,19 +261,23 @@ class HttpResponse(object):
else: else:
self._container = [content] self._container = [content]
self._is_string = True self._is_string = True
self._headers = {'content-type': content_type}
self.cookies = SimpleCookie() self.cookies = SimpleCookie()
if status: if status:
self.status_code = status self.status_code = status
# _headers is a mapping of the lower-case name to the original case of
# the header (required for working with legacy systems) and the header
# value.
self._headers = {'content-type': ('Content-Type', content_type)}
def __str__(self): def __str__(self):
"Full HTTP message, including headers" "Full HTTP message, including headers"
return '\n'.join(['%s: %s' % (key, value) return '\n'.join(['%s: %s' % (key, value)
for key, value in self._headers.items()]) \ for key, value in self._headers.values()]) \
+ '\n\n' + self.content + '\n\n' + self.content
def __setitem__(self, header, value): def __setitem__(self, header, value):
self._headers[header.lower()] = value self._headers[header.lower()] = (header, value)
def __delitem__(self, header): def __delitem__(self, header):
try: try:
@ -282,7 +286,7 @@ class HttpResponse(object):
pass pass
def __getitem__(self, header): def __getitem__(self, header):
return self._headers[header.lower()] return self._headers[header.lower()][1]
def has_header(self, header): def has_header(self, header):
"Case-insensitive check for a header" "Case-insensitive check for a header"
@ -291,10 +295,10 @@ class HttpResponse(object):
__contains__ = has_header __contains__ = has_header
def items(self): def items(self):
return self._headers.items() return self._headers.values()
def get(self, header, alternate): def get(self, header, alternate):
return self._headers.get(header, alternate) return self._headers.get(header.lower(), (None, alternate))[1]
def set_cookie(self, key, value='', max_age=None, expires=None, path='/', domain=None, secure=None): def set_cookie(self, key, value='', max_age=None, expires=None, path='/', domain=None, secure=None):
self.cookies[key] = value self.cookies[key] = value
@ -304,17 +308,13 @@ class HttpResponse(object):
self.cookies[key][var.replace('_', '-')] = val self.cookies[key][var.replace('_', '-')] = val
def delete_cookie(self, key, path='/', domain=None): def delete_cookie(self, key, path='/', domain=None):
self.cookies[key] = '' self.set_cookie(key, max_age=0, path=path, domain=domain,
if path is not None: expires='Thu, 01-Jan-1970 00:00:00 GMT')
self.cookies[key]['path'] = path
if domain is not None:
self.cookies[key]['domain'] = domain
self.cookies[key]['expires'] = 0
self.cookies[key]['max-age'] = 0
def _get_content(self): def _get_content(self):
content = smart_str(''.join(self._container), self._charset) if self.has_header('Content-Encoding'):
return content return ''.join(self._container)
return smart_str(''.join(self._container), self._charset)
def _set_content(self, value): def _set_content(self, value):
self._container = [value] self._container = [value]

View File

@ -1,8 +1,10 @@
import md5
import re
from django.conf import settings from django.conf import settings
from django import http from django import http
from django.core.mail import mail_managers from django.core.mail import mail_managers
import md5 from django.utils.http import urlquote
import re
class CommonMiddleware(object): class CommonMiddleware(object):
""" """
@ -46,9 +48,9 @@ class CommonMiddleware(object):
if new_url != old_url: if new_url != old_url:
# Redirect # Redirect
if new_url[0]: if new_url[0]:
newurl = "%s://%s%s" % (request.is_secure() and 'https' or 'http', new_url[0], new_url[1]) newurl = "%s://%s%s" % (request.is_secure() and 'https' or 'http', new_url[0], urlquote(new_url[1]))
else: else:
newurl = new_url[1] newurl = urlquote(new_url[1])
if request.GET: if request.GET:
newurl += '?' + request.GET.urlencode() newurl += '?' + request.GET.urlencode()
return http.HttpResponsePermanentRedirect(newurl) return http.HttpResponsePermanentRedirect(newurl)

View File

@ -19,8 +19,9 @@ class GZipMiddleware(object):
patch_vary_headers(response, ('Accept-Encoding',)) patch_vary_headers(response, ('Accept-Encoding',))
# Avoid gzipping if we've already got a content-encoding or if the # Avoid gzipping if we've already got a content-encoding or if the
# content-type is Javascript (silly IE...) # content-type is Javascript and the user's browser is IE.
is_js = "javascript" in response.get('Content-Type', '').lower() is_js = ("msie" in request.META.get('HTTP_USER_AGENT', '').lower() and
"javascript" in response.get('Content-Type', '').lower())
if response.has_header('Content-Encoding') or is_js: if response.has_header('Content-Encoding') or is_js:
return response return response

View File

@ -11,7 +11,7 @@ from django.utils.translation import ugettext
from django.utils.encoding import StrAndUnicode, smart_unicode from django.utils.encoding import StrAndUnicode, smart_unicode
from util import ErrorList, ValidationError from util import ErrorList, ValidationError
from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, DateTimeInput
try: try:
from decimal import Decimal, DecimalException from decimal import Decimal, DecimalException
@ -284,6 +284,8 @@ DEFAULT_DATETIME_INPUT_FORMATS = (
) )
class DateTimeField(Field): class DateTimeField(Field):
widget = DateTimeInput
def __init__(self, input_formats=None, *args, **kwargs): def __init__(self, input_formats=None, *args, **kwargs):
super(DateTimeField, self).__init__(*args, **kwargs) super(DateTimeField, self).__init__(*args, **kwargs)
self.input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS self.input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS
@ -300,6 +302,12 @@ class DateTimeField(Field):
return value return value
if isinstance(value, datetime.date): if isinstance(value, datetime.date):
return datetime.datetime(value.year, value.month, value.day) return datetime.datetime(value.year, value.month, value.day)
if isinstance(value, list):
# Input comes from a SplitDateTimeWidget, for example. So, it's two
# components: date and time.
if len(value) != 2:
raise ValidationError(ugettext(u'Enter a valid date/time.'))
value = '%s %s' % tuple(value)
for format in self.input_formats: for format in self.input_formats:
try: try:
return datetime.datetime(*time.strptime(value, format)[:6]) return datetime.datetime(*time.strptime(value, format)[:6])

View File

@ -19,7 +19,7 @@ from util import flatatt
__all__ = ( __all__ = (
'Widget', 'TextInput', 'PasswordInput', 'Widget', 'TextInput', 'PasswordInput',
'HiddenInput', 'MultipleHiddenInput', 'HiddenInput', 'MultipleHiddenInput',
'FileInput', 'Textarea', 'CheckboxInput', 'FileInput', 'DateTimeInput', 'Textarea', 'CheckboxInput',
'Select', 'NullBooleanSelect', 'SelectMultiple', 'RadioSelect', 'Select', 'NullBooleanSelect', 'SelectMultiple', 'RadioSelect',
'CheckboxSelectMultiple', 'MultiWidget', 'SplitDateTimeWidget', 'CheckboxSelectMultiple', 'MultiWidget', 'SplitDateTimeWidget',
) )
@ -151,6 +151,22 @@ class Textarea(Widget):
final_attrs = self.build_attrs(attrs, name=name) final_attrs = self.build_attrs(attrs, name=name)
return u'<textarea%s>%s</textarea>' % (flatatt(final_attrs), escape(value)) return u'<textarea%s>%s</textarea>' % (flatatt(final_attrs), escape(value))
class DateTimeInput(Input):
input_type = 'text'
format = '%Y-%m-%d %H:%M:%S' # '2006-10-25 14:30:59'
def __init__(self, attrs=None, format=None):
super(DateTimeInput, self).__init__(attrs)
if format:
self.format = format
def render(self, name, value, attrs=None):
if value is None:
value = ''
elif hasattr(value, 'strftime'):
value = value.strftime(self.format)
return super(DateTimeInput, self).render(name, value, attrs)
class CheckboxInput(Widget): class CheckboxInput(Widget):
def __init__(self, attrs=None, check_test=bool): def __init__(self, attrs=None, check_test=bool):
super(CheckboxInput, self).__init__(attrs) super(CheckboxInput, self).__init__(attrs)
@ -170,6 +186,13 @@ class CheckboxInput(Widget):
final_attrs['value'] = force_unicode(value) # Only add the 'value' attribute if a value is non-empty. final_attrs['value'] = force_unicode(value) # Only add the 'value' attribute if a value is non-empty.
return u'<input%s />' % flatatt(final_attrs) return u'<input%s />' % flatatt(final_attrs)
def value_from_datadict(self, data, files, name):
if name not in data:
# A missing value means False because HTML form submission does not
# send results for unselected checkboxes.
return False
return super(CheckboxInput, self).value_from_datadict(data, files, name)
class Select(Widget): class Select(Widget):
def __init__(self, attrs=None, choices=()): def __init__(self, attrs=None, choices=()):
super(Select, self).__init__(attrs) super(Select, self).__init__(attrs)
@ -425,5 +448,5 @@ class SplitDateTimeWidget(MultiWidget):
def decompress(self, value): def decompress(self, value):
if value: if value:
return [value.date(), value.time()] return [value.date(), value.time().replace(microsecond=0)]
return [None, None] return [None, None]

View File

@ -517,8 +517,14 @@ def firstof(parser, token):
{% endif %}{% endif %}{% endif %} {% endif %}{% endif %}{% endif %}
but obviously much cleaner! but obviously much cleaner!
You can also use a literal string as a fallback value in case all
passed variables are False::
{% firstof var1 var2 var3 "fallback value" %}
""" """
bits = token.contents.split()[1:] bits = token.split_contents()[1:]
if len(bits) < 1: if len(bits) < 1:
raise TemplateSyntaxError, "'firstof' statement requires at least one argument" raise TemplateSyntaxError, "'firstof' statement requires at least one argument"
return FirstOfNode(bits) return FirstOfNode(bits)
@ -675,7 +681,7 @@ def do_if(parser, token):
{% if athlete_list and coach_list or cheerleader_list %} {% if athlete_list and coach_list or cheerleader_list %}
If you need to combine ``and`` and ``or`` to do advanced logic, just use If you need to combine ``and`` and ``or`` to do advanced logic, just use
nested if tags. For example: nested if tags. For example::
{% if athlete_list %} {% if athlete_list %}
{% if coach_list or cheerleader_list %} {% if coach_list or cheerleader_list %}

View File

@ -0,0 +1,57 @@
from django.template import Library, Node, TemplateSyntaxError
from django.template import resolve_variable
from django.core.cache import cache
from django.utils.encoding import force_unicode
register = Library()
class CacheNode(Node):
def __init__(self, nodelist, expire_time, fragment_name, vary_on):
self.nodelist = nodelist
self.expire_time = expire_time
self.fragment_name = fragment_name
self.vary_on = vary_on
def render(self, context):
# Build a unicode key for this fragment and all vary-on's.
cache_key = u':'.join([self.fragment_name] + \
[force_unicode(resolve_variable(var, context)) for var in self.vary_on])
value = cache.get(cache_key)
if value is None:
value = self.nodelist.render(context)
cache.set(cache_key, value, self.expire_time)
return value
def do_cache(parser, token):
"""
This will cache the contents of a template fragment for a given amount
of time.
Usage::
{% load cache %}
{% cache [expire_time] [fragment_name] %}
.. some expensive processing ..
{% endcache %}
This tag also supports varying by a list of arguments::
{% load cache %}
{% cache [expire_time] [fragment_name] [var1] [var2] .. %}
.. some expensive processing ..
{% endcache %}
Each unique set of arguments will result in a unique cache entry.
"""
nodelist = parser.parse(('endcache',))
parser.delete_first_token()
tokens = token.contents.split()
if len(tokens) < 3:
raise TemplateSyntaxError(u"'%r' tag requires at least 2 arguments." % tokens[0])
try:
expire_time = int(tokens[1])
except ValueError:
raise TemplateSyntaxError(u"First argument to '%r' must be an integer (got '%s')." % (tokens[0], tokens[1]))
return CacheNode(nodelist, expire_time, tokens[2], tokens[3:])
register.tag('cache', do_cache)

View File

@ -1,3 +1,5 @@
import re
from django.template import Node, Variable from django.template import Node, Variable
from django.template import TemplateSyntaxError, TokenParser, Library from django.template import TemplateSyntaxError, TokenParser, Library
from django.template import TOKEN_TEXT, TOKEN_VAR from django.template import TOKEN_TEXT, TOKEN_VAR
@ -68,9 +70,11 @@ class BlockTranslateNode(Node):
count = self.counter.resolve(context) count = self.counter.resolve(context)
context[self.countervar] = count context[self.countervar] = count
plural = self.render_token_list(self.plural) plural = self.render_token_list(self.plural)
result = translation.ungettext(singular, plural, count) % context result = translation.ungettext(singular, plural, count)
else: else:
result = translation.ugettext(singular) % context result = translation.ugettext(singular)
# Escape all isolated '%' before substituting in the context.
result = re.sub('%(?!\()', '%%', result) % context
context.pop() context.pop()
return result return result

View File

@ -6,6 +6,7 @@ from django.core.management import call_command
from django.dispatch import dispatcher from django.dispatch import dispatcher
from django.test import signals from django.test import signals
from django.template import Template from django.template import Template
from django.utils.translation import deactivate
# The prefix to put on the default database name when creating # The prefix to put on the default database name when creating
# the test database. # the test database.
@ -43,7 +44,7 @@ def setup_test_environment():
- Installing the instrumented test renderer - Installing the instrumented test renderer
- Diverting the email sending functions to a test buffer - Diverting the email sending functions to a test buffer
- Setting the active locale to match the LANGUAGE_CODE setting.
""" """
Template.original_render = Template.render Template.original_render = Template.render
Template.render = instrumented_test_render Template.render = instrumented_test_render
@ -53,6 +54,8 @@ def setup_test_environment():
mail.outbox = [] mail.outbox = []
deactivate()
def teardown_test_environment(): def teardown_test_environment():
"""Perform any global post-test teardown. This involves: """Perform any global post-test teardown. This involves:

22
django/utils/checksums.py Normal file
View File

@ -0,0 +1,22 @@
"""
Common checksum routines (used in multiple localflavor/ cases, for example).
"""
__all__ = ['luhn',]
LUHN_ODD_LOOKUP = (0, 2, 4, 6, 8, 1, 3, 5, 7, 9) # sum_of_digits(index * 2)
def luhn(candidate):
"""
Checks a candidate number for validity according to the Luhn
algorithm (used in validation of, for example, credit cards).
Both numeric and string candidates are accepted.
"""
if not isinstance(candidate, basestring):
candidate = str(candidate)
try:
evens = sum([int(c) for c in candidate[-1::-2]])
odds = sum([LUHN_ODD_LOOKUP[int(c)] for c in candidate[-2::-2]])
return ((evens + odds) % 10 == 0)
except ValueError: # Raised if an int conversion fails
return False

View File

@ -46,18 +46,28 @@ class MergeDict(object):
__contains__ = has_key __contains__ = has_key
def copy(self): def copy(self):
""" returns a copy of this object""" """Returns a copy of this object."""
return self.__copy__() return self.__copy__()
class SortedDict(dict): class SortedDict(dict):
"A dictionary that keeps its keys in the order in which they're inserted." """
A dictionary that keeps its keys in the order in which they're inserted.
"""
def __init__(self, data=None): def __init__(self, data=None):
if data is None: data = {} if data is None:
data = {}
dict.__init__(self, data) dict.__init__(self, data)
if isinstance(data, dict): if isinstance(data, dict):
self.keyOrder = data.keys() self.keyOrder = data.keys()
else: else:
self.keyOrder=[key for key, value in data] self.keyOrder = [key for key, value in data]
def __deepcopy__(self,memo):
from copy import deepcopy
obj = self.__class__()
for k, v in self.items():
obj[k] = deepcopy(v, memo)
return obj
def __setitem__(self, key, value): def __setitem__(self, key, value):
dict.__setitem__(self, key, value) dict.__setitem__(self, key, value)
@ -72,6 +82,20 @@ class SortedDict(dict):
for k in self.keyOrder: for k in self.keyOrder:
yield k yield k
def pop(self, k, *args):
result = dict.pop(self, k, *args)
try:
self.keyOrder.remove(k)
except ValueError:
# Key wasn't in the dictionary in the first place. No problem.
pass
return result
def popitem(self):
result = dict.popitem(self)
self.keyOrder.remove(result[0])
return result
def items(self): def items(self):
return zip(self.keyOrder, self.values()) return zip(self.keyOrder, self.values())
@ -102,20 +126,21 @@ class SortedDict(dict):
return dict.setdefault(self, key, default) return dict.setdefault(self, key, default)
def value_for_index(self, index): def value_for_index(self, index):
"Returns the value of the item at the given zero-based index." """Returns the value of the item at the given zero-based index."""
return self[self.keyOrder[index]] return self[self.keyOrder[index]]
def insert(self, index, key, value): def insert(self, index, key, value):
"Inserts the key, value pair before the item with the given index." """Inserts the key, value pair before the item with the given index."""
if key in self.keyOrder: if key in self.keyOrder:
n = self.keyOrder.index(key) n = self.keyOrder.index(key)
del self.keyOrder[n] del self.keyOrder[n]
if n < index: index -= 1 if n < index:
index -= 1
self.keyOrder.insert(index, key) self.keyOrder.insert(index, key)
dict.__setitem__(self, key, value) dict.__setitem__(self, key, value)
def copy(self): def copy(self):
"Returns a copy of this object." """Returns a copy of this object."""
# This way of initializing the copy means it works for subclasses, too. # This way of initializing the copy means it works for subclasses, too.
obj = self.__class__(self) obj = self.__class__(self)
obj.keyOrder = self.keyOrder obj.keyOrder = self.keyOrder
@ -133,7 +158,8 @@ class MultiValueDictKeyError(KeyError):
class MultiValueDict(dict): class MultiValueDict(dict):
""" """
A subclass of dictionary customized to handle multiple values for the same key. A subclass of dictionary customized to handle multiple values for the
same key.
>>> d = MultiValueDict({'name': ['Adrian', 'Simon'], 'position': ['Developer']}) >>> d = MultiValueDict({'name': ['Adrian', 'Simon'], 'position': ['Developer']})
>>> d['name'] >>> d['name']
@ -176,15 +202,17 @@ class MultiValueDict(dict):
def __deepcopy__(self, memo=None): def __deepcopy__(self, memo=None):
import copy import copy
if memo is None: memo = {} if memo is None:
memo = {}
result = self.__class__() result = self.__class__()
memo[id(self)] = result memo[id(self)] = result
for key, value in dict.items(self): for key, value in dict.items(self):
dict.__setitem__(result, copy.deepcopy(key, memo), copy.deepcopy(value, memo)) dict.__setitem__(result, copy.deepcopy(key, memo),
copy.deepcopy(value, memo))
return result return result
def get(self, key, default=None): def get(self, key, default=None):
"Returns the default value if the requested data doesn't exist" """Returns the default value if the requested data doesn't exist."""
try: try:
val = self[key] val = self[key]
except KeyError: except KeyError:
@ -194,7 +222,7 @@ class MultiValueDict(dict):
return val return val
def getlist(self, key): def getlist(self, key):
"Returns an empty list if the requested data doesn't exist" """Returns an empty list if the requested data doesn't exist."""
try: try:
return dict.__getitem__(self, key) return dict.__getitem__(self, key)
except KeyError: except KeyError:
@ -214,7 +242,7 @@ class MultiValueDict(dict):
return self.getlist(key) return self.getlist(key)
def appendlist(self, key, value): def appendlist(self, key, value):
"Appends an item to the internal list associated with key" """Appends an item to the internal list associated with key."""
self.setlistdefault(key, []) self.setlistdefault(key, [])
dict.__setitem__(self, key, self.getlist(key) + [value]) dict.__setitem__(self, key, self.getlist(key) + [value])
@ -226,19 +254,22 @@ class MultiValueDict(dict):
return [(key, self[key]) for key in self.keys()] return [(key, self[key]) for key in self.keys()]
def lists(self): def lists(self):
"Returns a list of (key, list) pairs." """Returns a list of (key, list) pairs."""
return dict.items(self) return dict.items(self)
def values(self): def values(self):
"Returns a list of the last value on every key list." """Returns a list of the last value on every key list."""
return [self[key] for key in self.keys()] return [self[key] for key in self.keys()]
def copy(self): def copy(self):
"Returns a copy of this object." """Returns a copy of this object."""
return self.__deepcopy__() return self.__deepcopy__()
def update(self, *args, **kwargs): def update(self, *args, **kwargs):
"update() extends rather than replaces existing key lists. Also accepts keyword args." """
update() extends rather than replaces existing key lists.
Also accepts keyword args.
"""
if len(args) > 1: if len(args) > 1:
raise TypeError, "update expected at most 1 arguments, got %d" % len(args) raise TypeError, "update expected at most 1 arguments, got %d" % len(args)
if args: if args:
@ -299,4 +330,3 @@ class FileDict(dict):
d = dict(self, content='<omitted>') d = dict(self, content='<omitted>')
return dict.__repr__(d) return dict.__repr__(d)
return dict.__repr__(self) return dict.__repr__(self)

View File

@ -31,7 +31,7 @@ def force_unicode(s, encoding='utf-8', strings_only=False, errors='strict'):
If strings_only is True, don't convert (some) non-string-like objects. If strings_only is True, don't convert (some) non-string-like objects.
""" """
if strings_only and isinstance(s, (types.NoneType, int, long, datetime.datetime, datetime.time, float)): if strings_only and isinstance(s, (types.NoneType, int, long, datetime.datetime, datetime.date, datetime.time, float)):
return s return s
if not isinstance(s, basestring,): if not isinstance(s, basestring,):
if hasattr(s, '__unicode__'): if hasattr(s, '__unicode__'):

View File

@ -45,7 +45,7 @@ class SyndicationFeed(object):
"Base class for all syndication feeds. Subclasses should provide write()" "Base class for all syndication feeds. Subclasses should provide write()"
def __init__(self, title, link, description, language=None, author_email=None, def __init__(self, title, link, description, language=None, author_email=None,
author_name=None, author_link=None, subtitle=None, categories=None, author_name=None, author_link=None, subtitle=None, categories=None,
feed_url=None, feed_copyright=None, feed_guid=None): feed_url=None, feed_copyright=None, feed_guid=None, ttl=None):
to_unicode = lambda s: force_unicode(s, strings_only=True) to_unicode = lambda s: force_unicode(s, strings_only=True)
if categories: if categories:
categories = [force_unicode(c) for c in categories] categories = [force_unicode(c) for c in categories]
@ -62,12 +62,13 @@ class SyndicationFeed(object):
'feed_url': iri_to_uri(feed_url), 'feed_url': iri_to_uri(feed_url),
'feed_copyright': to_unicode(feed_copyright), 'feed_copyright': to_unicode(feed_copyright),
'id': feed_guid or link, 'id': feed_guid or link,
'ttl': ttl,
} }
self.items = [] self.items = []
def add_item(self, title, link, description, author_email=None, def add_item(self, title, link, description, author_email=None,
author_name=None, author_link=None, pubdate=None, comments=None, author_name=None, author_link=None, pubdate=None, comments=None,
unique_id=None, enclosure=None, categories=(), item_copyright=None): unique_id=None, enclosure=None, categories=(), item_copyright=None, ttl=None):
""" """
Adds an item to the feed. All args are expected to be Python Unicode Adds an item to the feed. All args are expected to be Python Unicode
objects except pubdate, which is a datetime.datetime object, and objects except pubdate, which is a datetime.datetime object, and
@ -89,6 +90,7 @@ class SyndicationFeed(object):
'enclosure': enclosure, 'enclosure': enclosure,
'categories': categories or (), 'categories': categories or (),
'item_copyright': to_unicode(item_copyright), 'item_copyright': to_unicode(item_copyright),
'ttl': ttl,
}) })
def num_items(self): def num_items(self):
@ -146,6 +148,8 @@ class RssFeed(SyndicationFeed):
if self.feed['feed_copyright'] is not None: if self.feed['feed_copyright'] is not None:
handler.addQuickElement(u"copyright", self.feed['feed_copyright']) handler.addQuickElement(u"copyright", self.feed['feed_copyright'])
handler.addQuickElement(u"lastBuildDate", rfc2822_date(self.latest_post_date()).decode('ascii')) handler.addQuickElement(u"lastBuildDate", rfc2822_date(self.latest_post_date()).decode('ascii'))
if self.feed['ttl'] is not None:
handler.addQuickElement(u"ttl", self.feed['ttl'])
self.write_items(handler) self.write_items(handler)
self.endChannelElement(handler) self.endChannelElement(handler)
handler.endElement(u"rss") handler.endElement(u"rss")
@ -190,6 +194,8 @@ class Rss201rev2Feed(RssFeed):
handler.addQuickElement(u"comments", item['comments']) handler.addQuickElement(u"comments", item['comments'])
if item['unique_id'] is not None: if item['unique_id'] is not None:
handler.addQuickElement(u"guid", item['unique_id']) handler.addQuickElement(u"guid", item['unique_id'])
if item['ttl'] is not None:
handler.addQuickElement(u"ttl", item['ttl'])
# Enclosure. # Enclosure.
if item['enclosure'] is not None: if item['enclosure'] is not None:

View File

@ -53,7 +53,11 @@ def lazy(func, *resultclasses):
self._delegate_unicode = unicode in resultclasses self._delegate_unicode = unicode in resultclasses
assert not (self._delegate_str and self._delegate_unicode), "Cannot call lazy() with both str and unicode return types." assert not (self._delegate_str and self._delegate_unicode), "Cannot call lazy() with both str and unicode return types."
if self._delegate_unicode: if self._delegate_unicode:
self.__unicode__ = self.__unicode_cast # Each call to lazy() makes a new __proxy__ object, so this
# doesn't interfere with any other lazy() results.
__proxy__.__unicode__ = __proxy__.__unicode_cast
elif self._delegate_str:
__proxy__.__str__ = __proxy__.__str_cast
def __promise__(self, klass, funcname, func): def __promise__(self, klass, funcname, func):
# Builds a wrapper around some magic method and registers that magic # Builds a wrapper around some magic method and registers that magic
@ -72,14 +76,8 @@ def lazy(func, *resultclasses):
def __unicode_cast(self): def __unicode_cast(self):
return self.__func(*self.__args, **self.__kw) return self.__func(*self.__args, **self.__kw)
def __str__(self): def __str_cast(self):
# As __str__ is always a method on the type (class), it is looked return str(self.__func(*self.__args, **self.__kw))
# up (and found) there first. So we can't just assign to it on a
# per-instance basis in __init__.
if self._delegate_str:
return str(self.__func(*self.__args, **self.__kw))
else:
return Promise.__str__(self)
def __cmp__(self, rhs): def __cmp__(self, rhs):
if self._delegate_str: if self._delegate_str:

View File

@ -9,7 +9,8 @@ def urlquote(url, safe='/'):
can safely be used as part of an argument to a subsequent iri_to_uri() call can safely be used as part of an argument to a subsequent iri_to_uri() call
without double-quoting occurring. without double-quoting occurring.
""" """
return force_unicode(urllib.quote(smart_str(url))) return force_unicode(urllib.quote(smart_str(url), safe))
urlquote = allow_lazy(urlquote, unicode) urlquote = allow_lazy(urlquote, unicode)
def urlquote_plus(url, safe=''): def urlquote_plus(url, safe=''):

View File

@ -8,7 +8,7 @@ __all__ = ['gettext', 'gettext_noop', 'gettext_lazy', 'ngettext',
'ngettext_lazy', 'string_concat', 'activate', 'deactivate', 'ngettext_lazy', 'string_concat', 'activate', 'deactivate',
'get_language', 'get_language_bidi', 'get_date_formats', 'get_language', 'get_language_bidi', 'get_date_formats',
'get_partial_date_formats', 'check_for_language', 'to_locale', 'get_partial_date_formats', 'check_for_language', 'to_locale',
'get_language_from_request', 'install', 'templatize', 'ugettext', 'get_language_from_request', 'templatize', 'ugettext',
'ungettext', 'deactivate_all'] 'ungettext', 'deactivate_all']
# Here be dragons, so a short explanation of the logic won't hurt: # Here be dragons, so a short explanation of the logic won't hurt:
@ -96,9 +96,6 @@ def to_locale(language):
def get_language_from_request(request): def get_language_from_request(request):
return real_get_language_from_request(request) return real_get_language_from_request(request)
def install():
return real_install()
def templatize(src): def templatize(src):
return real_templatize(src) return real_templatize(src)

View File

@ -14,7 +14,7 @@ def ungettext(singular, plural, number):
return force_unicode(ngettext(singular, plural, number)) return force_unicode(ngettext(singular, plural, number))
activate = lambda x: None activate = lambda x: None
deactivate = deactivate_all = install = lambda: None deactivate = deactivate_all = lambda: None
get_language = lambda: settings.LANGUAGE_CODE get_language = lambda: settings.LANGUAGE_CODE
get_language_bidi = lambda: settings.LANGUAGE_CODE in settings.LANGUAGES_BIDI get_language_bidi = lambda: settings.LANGUAGE_CODE in settings.LANGUAGES_BIDI
get_date_formats = lambda: (settings.DATE_FORMAT, settings.DATETIME_FORMAT, settings.TIME_FORMAT) get_date_formats = lambda: (settings.DATE_FORMAT, settings.DATETIME_FORMAT, settings.TIME_FORMAT)

View File

@ -1,8 +1,12 @@
"Translation helper functions" "Translation helper functions"
import os, re, sys import locale
import os
import re
import sys
import gettext as gettext_module import gettext as gettext_module
from cStringIO import StringIO from cStringIO import StringIO
from django.utils.encoding import force_unicode from django.utils.encoding import force_unicode
try: try:
@ -25,15 +29,25 @@ _active = {}
# The default translation is based on the settings file. # The default translation is based on the settings file.
_default = None _default = None
# This is a cache for accept-header to translation object mappings to prevent # This is a cache for normalised accept-header languages to prevent multiple
# the accept parser to run multiple times for one user. # file lookups when checking the same locale on repeated requests.
_accepted = {} _accepted = {}
def to_locale(language): # Format of Accept-Language header values. From RFC 2616, section 14.4 and 3.9.
accept_language_re = re.compile(r'''
([A-Za-z]{1,8}(?:-[A-Za-z]{1,8})*|\*) # "en", "en-au", "x-y-z", "*"
(?:;q=(0(?:\.\d{,3})?|1(?:.0{,3})?))? # Optional "q=1.00", "q=0.8"
(?:\s*,\s*|$) # Multiple accepts per header.
''', re.VERBOSE)
def to_locale(language, to_lower=False):
"Turns a language name (en-us) into a locale name (en_US)." "Turns a language name (en-us) into a locale name (en_US)."
p = language.find('-') p = language.find('-')
if p >= 0: if p >= 0:
return language[:p].lower()+'_'+language[p+1:].upper() if to_lower:
return language[:p].lower()+'_'+language[p+1:].lower()
else:
return language[:p].lower()+'_'+language[p+1:].upper()
else: else:
return language.lower() return language.lower()
@ -249,8 +263,10 @@ def catalog():
def do_translate(message, translation_function): def do_translate(message, translation_function):
""" """
Translate 'message' using the given 'translation_function' name -- which Translates 'message' using the given 'translation_function' name -- which
will be either gettext or ugettext. will be either gettext or ugettext. It uses the current thread to find the
translation object to use. If no current translation is activated, the
message will be run through the default translation object.
""" """
global _default, _active global _default, _active
t = _active.get(currentThread(), None) t = _active.get(currentThread(), None)
@ -262,12 +278,6 @@ def do_translate(message, translation_function):
return getattr(_default, translation_function)(message) return getattr(_default, translation_function)(message)
def gettext(message): def gettext(message):
"""
This function will be patched into the builtins module to provide the _
helper function. It will use the current thread as a discriminator to find
the translation object to use. If no current translation is activated, the
message will be run through the default translation object.
"""
return do_translate(message, 'gettext') return do_translate(message, 'gettext')
def ugettext(message): def ugettext(message):
@ -338,46 +348,40 @@ def get_language_from_request(request):
if lang_code in supported and lang_code is not None and check_for_language(lang_code): if lang_code in supported and lang_code is not None and check_for_language(lang_code):
return lang_code return lang_code
lang_code = request.COOKIES.get('django_language', None) lang_code = request.COOKIES.get('django_language')
if lang_code in supported and lang_code is not None and check_for_language(lang_code): if lang_code and lang_code in supported and check_for_language(lang_code):
return lang_code return lang_code
accept = request.META.get('HTTP_ACCEPT_LANGUAGE', None) accept = request.META.get('HTTP_ACCEPT_LANGUAGE', '')
if accept is not None: for lang, unused in parse_accept_lang_header(accept):
if lang == '*':
break
t = _accepted.get(accept, None) # We have a very restricted form for our language files (no encoding
if t is not None: # specifier, since they all must be UTF-8 and only one possible
return t # language each time. So we avoid the overhead of gettext.find() and
# look up the MO file manually.
def _parsed(el): normalized = locale.locale_alias.get(to_locale(lang, True))
p = el.find(';q=') if not normalized:
if p >= 0: continue
lang = el[:p].strip()
order = int(float(el[p+3:].strip())*100)
else:
lang = el
order = 100
p = lang.find('-')
if p >= 0:
mainlang = lang[:p]
else:
mainlang = lang
return (lang, mainlang, order)
langs = [_parsed(el) for el in accept.split(',')] # Remove the default encoding from locale_alias
langs.sort(lambda a,b: -1*cmp(a[2], b[2])) normalized = normalized.split('.')[0]
for lang, mainlang, order in langs: if normalized in _accepted:
if lang in supported or mainlang in supported: # We've seen this locale before and have an MO file for it, so no
langfile = gettext_module.find('django', globalpath, [to_locale(lang)]) # need to check again.
if langfile: return _accepted[normalized]
# reconstruct the actual language from the language
# filename, because otherwise we might incorrectly for lang in (normalized, normalized.split('_')[0]):
# report de_DE if we only have de available, but if lang not in supported:
# did find de_DE because of language normalization continue
lang = langfile[len(globalpath):].split(os.path.sep)[1] langfile = os.path.join(globalpath, lang, 'LC_MESSAGES',
_accepted[accept] = lang 'django.mo')
return lang if os.path.exists(langfile):
_accepted[normalized] = lang
return lang
return settings.LANGUAGE_CODE return settings.LANGUAGE_CODE
@ -414,13 +418,6 @@ def get_partial_date_formats():
month_day_format = settings.MONTH_DAY_FORMAT month_day_format = settings.MONTH_DAY_FORMAT
return year_month_format, month_day_format return year_month_format, month_day_format
def install():
"""
Installs the gettext function as the default translation function under
the name '_'.
"""
__builtins__['_'] = gettext
dot_re = re.compile(r'\S') dot_re = re.compile(r'\S')
def blankout(src, char): def blankout(src, char):
""" """
@ -516,3 +513,23 @@ def templatize(src):
out.write(blankout(t.contents, 'X')) out.write(blankout(t.contents, 'X'))
return out.getvalue() return out.getvalue()
def parse_accept_lang_header(lang_string):
"""
Parses the lang_string, which is the body of an HTTP Accept-Language
header, and returns a list of (lang, q-value), ordered by 'q' values.
Any format errors in lang_string results in an empty list being returned.
"""
result = []
pieces = accept_language_re.split(lang_string)
if pieces[-1]:
return []
for i in range(0, len(pieces) - 1, 3):
first, lang, priority = pieces[i : i + 3]
if first:
return []
priority = priority and float(priority) or 1.0
result.append((lang, priority))
result.sort(lambda x, y: -cmp(x[1], y[1]))
return result

View File

@ -201,16 +201,15 @@ def _get_lines_from_file(filename, lineno, context_lines, loader=None, module_na
if source is None: if source is None:
return None, [], None, [] return None, [], None, []
encoding=None encoding = 'ascii'
for line in source[:2]: for line in source[:2]:
# File coding may be specified (and may not be UTF-8). Match # File coding may be specified. Match pattern from PEP-263
# pattern from PEP-263 (http://www.python.org/dev/peps/pep-0263/) # (http://www.python.org/dev/peps/pep-0263/)
match = re.search(r'coding[:=]\s*([-\w.]+)', line) match = re.search(r'coding[:=]\s*([-\w.]+)', line)
if match: if match:
encoding = match.group(1) encoding = match.group(1)
break break
if encoding: source = [unicode(sline, encoding, 'replace') for sline in source]
source = [unicode(sline, encoding) for sline in source]
lower_bound = max(0, lineno - context_lines) lower_bound = max(0, lineno - context_lines)
upper_bound = lineno + context_lines upper_bound = lineno + context_lines

View File

@ -62,7 +62,7 @@ with the standard ``Auth*`` and ``Require`` directives::
... ...
<Location /exmaple/> <Location /example/>
AuthType Basic AuthType Basic
AuthName "example.com" AuthName "example.com"
**AuthBasicAuthoritative Off** **AuthBasicAuthoritative Off**

View File

@ -119,8 +119,8 @@ in your database that is in the proper format that Django's database-cache
system expects. system expects.
Once you've created that database table, set your ``CACHE_BACKEND`` setting to Once you've created that database table, set your ``CACHE_BACKEND`` setting to
``"db://tablename/"``, where ``tablename`` is the name of the database table. ``"db://tablename"``, where ``tablename`` is the name of the database table.
In this example, the cache table's name is ``my_cache_table``: In this example, the cache table's name is ``my_cache_table``::
CACHE_BACKEND = 'db://my_cache_table' CACHE_BACKEND = 'db://my_cache_table'
@ -228,7 +228,7 @@ entire site. Just add ``'django.middleware.cache.CacheMiddleware'`` to your
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
) )
(The order of ``MIDDLEWARE_CLASSES`` matters. See "Order of MIDDLEWARE_CLASSES" (The order of ``MIDDLEWARE_CLASSES`` matters. See `Order of MIDDLEWARE_CLASSES`_
below.) below.)
Then, add the following required settings to your Django settings file: Then, add the following required settings to your Django settings file:
@ -288,6 +288,36 @@ Or, using Python 2.4's decorator syntax::
above example, the result of the ``slashdot_this()`` view will be cached for 15 above example, the result of the ``slashdot_this()`` view will be cached for 15
minutes. minutes.
Template fragment caching
=========================
If you're after even more control, you can also cache template fragments using
the ``cache`` template tag. To give your template access to this tag, put ``{%
load cache %}`` near the top of your template.
The ``{% cache %}`` template tag caches the contents of the block for a given
amount of time. It takes at least two arguments: the cache timeout, in
seconds, and the name to give the cache fragment. For example::
{% load cache %}
{% cache 500 sidebar %}
.. sidebar ..
{% endcache %}
Sometimes you might want to cache multiple copies of a fragment depending on
some dynamic data that appears inside the fragment. For example you may want a
separate cached copy of the sidebar used in the previous example for every user
of your site. This can be easily achieved by passing additional arguments to
the ``{% cache %}`` template tag to uniquely identify the cache fragment::
{% load cache %}
{% cache 500 sidebar request.user.username %}
.. sidebar for logged in user ..
{% endcache %}
If you need more than one argument to identify the fragment that's fine, simply
pass as many arguments to ``{% cache %}`` as you need!
The low-level cache API The low-level cache API
======================= =======================
@ -326,6 +356,15 @@ get() can take a ``default`` argument::
>>> cache.get('my_key', 'has expired') >>> cache.get('my_key', 'has expired')
'has expired' 'has expired'
To add a key only if it doesn't already exist, there is an add() method. It
takes the same parameters as set(), but will not attempt to update the cache
if the key specified is already present::
>>> cache.set('add_key', 'Initial value')
>>> cache.add('add_key', 'New value')
>>> cache.get('add_key')
'Initial value'
There's also a get_many() interface that only hits the cache once. get_many() There's also a get_many() interface that only hits the cache once. get_many()
returns a dictionary with all the keys you asked for that actually exist in the returns a dictionary with all the keys you asked for that actually exist in the
cache (and haven't expired):: cache (and haven't expired)::
@ -556,8 +595,11 @@ within the ``MIDDLEWARE_CLASSES`` setting, because the cache middleware needs
to know which headers by which to vary the cache storage. Middleware always to know which headers by which to vary the cache storage. Middleware always
adds something to the ``Vary`` response header when it can. adds something to the ``Vary`` response header when it can.
Put the ``CacheMiddleware`` after any middlewares that might add something to Put the ``CacheMiddleware`` *before* any other middleware that might add
the ``Vary`` header. The following middlewares do so: something to the ``Vary`` header (response middleware is applied in reverse
order). The following middleware modules do so:
* ``SessionMiddleware`` adds ``Cookie`` * ``SessionMiddleware`` adds ``Cookie``
* ``GZipMiddleware`` adds ``Accept-Encoding`` * ``GZipMiddleware`` adds ``Accept-Encoding``
* ``LocaleMiddleware`` adds ``Accept-Language``

View File

@ -335,6 +335,10 @@ Please follow these coding standards when writing code for inclusion in Django:
* Unless otherwise specified, follow `PEP 8`_. * Unless otherwise specified, follow `PEP 8`_.
You could use a tool like `pep8.py`_ to check for some problems in this
area, but remember that PEP 8 is only a guide, so respect the style of
the surrounding code as a primary goal.
* Use four spaces for indentation. * Use four spaces for indentation.
* Use underscores, not camelCase, for variable, function and method names * Use underscores, not camelCase, for variable, function and method names
@ -924,5 +928,6 @@ requests for commit access are potential flame-war starters, and will be ignored
.. _`#django`: irc://irc.freenode.net/django .. _`#django`: irc://irc.freenode.net/django
.. _list of tickets with patches: http://code.djangoproject.com/query?status=new&status=assigned&status=reopened&has_patch=1&order=priority .. _list of tickets with patches: http://code.djangoproject.com/query?status=new&status=assigned&status=reopened&has_patch=1&order=priority
.. _PEP 8: http://www.python.org/peps/pep-0008.html .. _PEP 8: http://www.python.org/peps/pep-0008.html
.. _pep8.py: http://svn.browsershots.org/trunk/devtools/pep8/pep8.py
.. _i18n branch: http://code.djangoproject.com/browser/django/branches/i18n .. _i18n branch: http://code.djangoproject.com/browser/django/branches/i18n
.. _`tags/releases`: http://code.djangoproject.com/browser/django/tags/releases .. _`tags/releases`: http://code.djangoproject.com/browser/django/tags/releases

View File

@ -76,6 +76,14 @@ the ``mysql`` backend.
If you are trying to use an older version of MySQL and the ``mysql_old`` If you are trying to use an older version of MySQL and the ``mysql_old``
backend, then 1.2.0 *might* work for you. backend, then 1.2.0 *might* work for you.
.. note::
If you see ``ImportError: cannot import name ImmutableSet`` when trying to
use Django, your MySQLdb installation may contain an outdated ``sets.py``
file that conflicts with the built-in module of the same name from Python
2.4 and later. To fix this, verify that you have installed MySQLdb version
1.2.1p2 or newer, then delete the ``sets.py`` file in the MySQLdb
directory that was left by an earlier version.
.. _MySQLdb: http://sourceforge.net/projects/mysql-python .. _MySQLdb: http://sourceforge.net/projects/mysql-python
Creating your database Creating your database

View File

@ -741,22 +741,25 @@ Customized actions
**New in Django development version** **New in Django development version**
If you want to add an action of your own to ``manage.py``, you can. Applications can register their own actions with ``manage.py``. For example,
Simply add a ``management/commands`` directory to your application. you might want to add a ``manage.py`` action for a Django app that you're
Each python module in that directory will be discovered and registered as distributing.
To do this, just add a ``management/commands`` directory to your application.
Each Python module in that directory will be auto-discovered and registered as
a command that can be executed as an action when you run ``manage.py``:: a command that can be executed as an action when you run ``manage.py``::
/fancy_blog blog/
__init__.py __init__.py
models.py models.py
/management management/
__init__.py __init__.py
/commands commands/
__init__.py __init__.py
explode.py explode.py
views.py views.py
In this example, ``explode`` command will be made available to any project In this example, the ``explode`` command will be made available to any project
that includes the ``fancy_blog`` application in ``settings.INSTALLED_APPS``. that includes the ``fancy_blog`` application in ``settings.INSTALLED_APPS``.
The ``explode.py`` module has only one requirement -- it must define a class The ``explode.py`` module has only one requirement -- it must define a class

View File

@ -48,7 +48,7 @@ with the given URL with a site ID that corresponds to the SITE_ID_ setting.
If it finds a match, it follows this algorithm: If it finds a match, it follows this algorithm:
* If the flatpage has a custom template, it loads that template. Otherwise, * If the flatpage has a custom template, it loads that template. Otherwise,
it loads the template ``flatpages/default``. it loads the template ``flatpages/default.html``.
* It passes that template a single context variable, ``flatpage``, which is * It passes that template a single context variable, ``flatpage``, which is
the flatpage object. It uses RequestContext_ in rendering the template. the flatpage object. It uses RequestContext_ in rendering the template.

View File

@ -63,7 +63,10 @@ installed.
* If you're using MySQL, you'll need MySQLdb_, version 1.2.1p2 or higher. * If you're using MySQL, you'll need MySQLdb_, version 1.2.1p2 or higher.
You will also want to read the database-specific notes for the `MySQL backend`_. You will also want to read the database-specific notes for the `MySQL backend`_.
* If you're using SQLite, you'll need pysqlite_. Use version 2.0.3 or higher. * If you're using SQLite and either Python 2.3 or Python 2.4, you'll need
pysqlite_. Use version 2.0.3 or higher. Python 2.5 ships with an sqlite
wrapper in the standard library, so you don't need to install anything extra
in that case.
* If you're using Oracle, you'll need cx_Oracle_, version 4.3.1 or higher. * If you're using Oracle, you'll need cx_Oracle_, version 4.3.1 or higher.
You will also want to read the database-specific notes for the `Oracle backend`_. You will also want to read the database-specific notes for the `Oracle backend`_.

View File

@ -178,8 +178,9 @@ request, before Django decides which view to execute.
``process_request()`` should return either ``None`` or an ``HttpResponse`` ``process_request()`` should return either ``None`` or an ``HttpResponse``
object. If it returns ``None``, Django will continue processing this request, object. If it returns ``None``, Django will continue processing this request,
executing any other middleware and, then, the appropriate view. If it returns executing any other middleware and, then, the appropriate view. If it returns
an ``HttpResponse`` object, Django won't bother calling ANY other middleware or an ``HttpResponse`` object, Django won't bother calling ANY other request,
the appropriate view; it'll return that ``HttpResponse``. view or exception middleware, or the appropriate view; it'll return that
``HttpResponse``. Response middleware is always called on every response.
process_view process_view
------------ ------------
@ -197,8 +198,9 @@ arguments that will be passed to the view. Neither ``view_args`` nor
return either ``None`` or an ``HttpResponse`` object. If it returns ``None``, return either ``None`` or an ``HttpResponse`` object. If it returns ``None``,
Django will continue processing this request, executing any other Django will continue processing this request, executing any other
``process_view()`` middleware and, then, the appropriate view. If it returns an ``process_view()`` middleware and, then, the appropriate view. If it returns an
``HttpResponse`` object, Django won't bother calling ANY other middleware or ``HttpResponse`` object, Django won't bother calling ANY other request, view
the appropriate view; it'll return that ``HttpResponse``. or exception middleware, or the appropriate view; it'll return that
``HttpResponse``. Response middleware is always called on every response.
process_response process_response
---------------- ----------------
@ -236,7 +238,8 @@ Guidelines
* Feel free to look at Django's available middleware for examples. The * Feel free to look at Django's available middleware for examples. The
core Django middleware classes are in ``django/middleware/`` in the core Django middleware classes are in ``django/middleware/`` in the
Django distribution. The session middleware is in ``django/contrib/sessions``. Django distribution. The session middleware is in
``django/contrib/sessions``.
* If you write a middleware component that you think would be useful to * If you write a middleware component that you think would be useful to
other people, contribute to the community! Let us know, and we'll other people, contribute to the community! Let us know, and we'll

View File

@ -678,7 +678,9 @@ set.
If ``True``, this field must be unique throughout the table. If ``True``, this field must be unique throughout the table.
This is enforced at the database level and at the Django admin-form level. This is enforced at the database level and at the Django admin-form level. If
you try to add save a model with a duplicate value in a ``unique`` field, a
``django.db.IntegrityError`` will be raised by the model's ``save()`` method.
``unique_for_date`` ``unique_for_date``
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~

View File

@ -87,17 +87,19 @@ lived under the ``weblog/`` directory, you would *also* need to add
**parent directories** of anything you import directly must be on the Python **parent directories** of anything you import directly must be on the Python
path. path.
.. caution:: .. note::
If you're using Windows, remember that the path will contain backslashes. If you're using Windows, it is still recommended that you use forward
This string is passed through Python's string parser twice, so you need to slashes in the pathnames, even though Windows normally uses backslashes
escape each backslash **twice**:: for its native separator. Apache knows how to convert from the forward
slash format to the native format, so this approach is portable and easier
to read (it avoids tricky problems with having to double-escape
backslashes).
PythonPath "['c:\\\\path\\\\to\\\\project'] + sys.path" This is valid even on a Windows system::
Or, use raw strings:: PythonPath "['c:/path/to/project'] + sys.path"
PythonPath "[r'c:\\path\\to\\project'] + sys.path"
You can also add directives such as ``PythonAutoReload Off`` for performance. You can also add directives such as ``PythonAutoReload Off`` for performance.
See the `mod_python documentation`_ for a full list of options. See the `mod_python documentation`_ for a full list of options.

View File

@ -100,7 +100,7 @@ Start with this basic ``Form`` subclass, which we'll call ``ContactForm``::
subject = forms.CharField(max_length=100) subject = forms.CharField(max_length=100)
message = forms.CharField() message = forms.CharField()
sender = forms.EmailField() sender = forms.EmailField()
cc_myself = forms.BooleanField() cc_myself = forms.BooleanField(required=False)
A form is composed of ``Field`` objects. In this case, our form has four A form is composed of ``Field`` objects. In this case, our form has four
fields: ``subject``, ``message``, ``sender`` and ``cc_myself``. We'll explain fields: ``subject``, ``message``, ``sender`` and ``cc_myself``. We'll explain
@ -860,6 +860,23 @@ classes::
<li>Instrument: <input type="text" name="instrument" /></li> <li>Instrument: <input type="text" name="instrument" /></li>
<li>Haircut type: <input type="text" name="haircut_type" /></li> <li>Haircut type: <input type="text" name="haircut_type" /></li>
Prefixes for forms
------------------
You can put several Django forms inside one ``<form>`` tag. To give each
``Form`` its own namespace you need to use the ``prefix`` keyword argument::
>>> mother = PersonForm(prefix="mother")
>>> father = PersonForm(prefix="father")
>>> print mother.as_ul()
<li><label for="id_mother-first_name">First name:</label> <input type="text" name="mother-first_name" id="id_mother-first_name" /></li>
<li><label for="id_mother-last_name">Last name:</label> <input type="text" name="mother-last_name" id="id_mother-last_name" /></li>
>>> print father.as_ul()
<li><label for="id_father-first_name">First name:</label> <input type="text" name="father-first_name" id="id_father-first_name" /></li>
<li><label for="id_father-last_name">Last name:</label> <input type="text" name="father-last_name" id="id_father-last_name" /></li>
Fields Fields
====== ======
@ -1043,7 +1060,7 @@ fields. We've specified ``auto_id=False`` to simplify the output::
... subject = forms.CharField(max_length=100, help_text='100 characters max.') ... subject = forms.CharField(max_length=100, help_text='100 characters max.')
... message = forms.CharField() ... message = forms.CharField()
... sender = forms.EmailField(help_text='A valid e-mail address, please.') ... sender = forms.EmailField(help_text='A valid e-mail address, please.')
... cc_myself = forms.BooleanField() ... cc_myself = forms.BooleanField(required=False)
>>> f = HelpTextContactForm(auto_id=False) >>> f = HelpTextContactForm(auto_id=False)
>>> print f.as_table() >>> print f.as_table()
<tr><th>Subject:</th><td><input type="text" name="subject" maxlength="100" /><br />100 characters max.</td></tr> <tr><th>Subject:</th><td><input type="text" name="subject" maxlength="100" /><br />100 characters max.</td></tr>
@ -1122,9 +1139,20 @@ For each field, we describe the default widget used if you don't specify
~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
* Default widget: ``CheckboxInput`` * Default widget: ``CheckboxInput``
* Empty value: ``None`` * Empty value: ``False``
* Normalizes to: A Python ``True`` or ``False`` value. * Normalizes to: A Python ``True`` or ``False`` value.
* Validates nothing (i.e., it never raises a ``ValidationError``). * Validates that the check box is checked (i.e. the value is ``True``) if
the field has ``required=True``.
**New in Django development version:** The empty value for a ``CheckboxInput``
(and hence the standard ``BooleanField``) has changed to return ``False``
instead of ``None`` in the development version.
.. note::
Since all ``Field`` subclasses have ``required=True`` by default, the
validation condition here is important. If you want to include a checkbox
in your form that can be either checked or unchecked, you must remember to
pass in ``required=False`` when creating the ``BooleanField``.
``CharField`` ``CharField``
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
@ -1132,7 +1160,8 @@ For each field, we describe the default widget used if you don't specify
* Default widget: ``TextInput`` * Default widget: ``TextInput``
* Empty value: ``''`` (an empty string) * Empty value: ``''`` (an empty string)
* Normalizes to: A Unicode object. * Normalizes to: A Unicode object.
* Validates nothing, unless ``max_length`` or ``min_length`` is provided. * Validates ``max_length`` or ``min_length``, if they are provided.
Otherwise, all inputs are valid.
Has two optional arguments for validation, ``max_length`` and ``min_length``. Has two optional arguments for validation, ``max_length`` and ``min_length``.
If provided, these arguments ensure that the string is at most or at least the If provided, these arguments ensure that the string is at most or at least the
@ -1172,7 +1201,7 @@ If no ``input_formats`` argument is provided, the default input formats are::
``DateTimeField`` ``DateTimeField``
~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~
* Default widget: ``TextInput`` * Default widget: ``DateTimeInput``
* Empty value: ``None`` * Empty value: ``None``
* Normalizes to: A Python ``datetime.datetime`` object. * Normalizes to: A Python ``datetime.datetime`` object.
* Validates that the given value is either a ``datetime.datetime``, * Validates that the given value is either a ``datetime.datetime``,
@ -1193,6 +1222,9 @@ If no ``input_formats`` argument is provided, the default input formats are::
'%m/%d/%y %H:%M', # '10/25/06 14:30' '%m/%d/%y %H:%M', # '10/25/06 14:30'
'%m/%d/%y', # '10/25/06' '%m/%d/%y', # '10/25/06'
**New in Django development version:** The ``DateTimeField`` used to use a
``TextInput`` widget by default. This has now changed.
``DecimalField`` ``DecimalField``
~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
@ -1508,7 +1540,7 @@ like so::
subject = forms.CharField(max_length=100) subject = forms.CharField(max_length=100)
message = forms.CharField() message = forms.CharField()
senders = MultiEmailField() senders = MultiEmailField()
cc_myself = forms.BooleanField() cc_myself = forms.BooleanField(required=False)
Widgets Widgets
======= =======
@ -1529,6 +1561,7 @@ commonly used groups of widgets:
``MultipleHiddenInput`` Multiple ``<input type='hidden' ...`` ``MultipleHiddenInput`` Multiple ``<input type='hidden' ...``
instances. instances.
``FileInput`` ``<input type='file' ...`` ``FileInput`` ``<input type='file' ...``
``DateTimeInput`` ``<input type='text' ...``
``Textarea`` ``<textarea>...</textarea>`` ``Textarea`` ``<textarea>...</textarea>``
``CheckboxInput`` ``<input type='checkbox' ...`` ``CheckboxInput`` ``<input type='checkbox' ...``
``Select`` ``<select><option ...`` ``Select`` ``<select><option ...``
@ -1542,6 +1575,9 @@ commonly used groups of widgets:
one for the Date, and one for the Time. one for the Date, and one for the Time.
============================ =========================================== ============================ ===========================================
**New in Django development version:** The ``DateTimeInput`` has been added
since the last release.
Specifying widgets Specifying widgets
------------------ ------------------
@ -2033,7 +2069,7 @@ have a ``Message`` model that holds each contact submission. Something like::
subject = models.CharField(max_length=100) subject = models.CharField(max_length=100)
message = models.TextField() message = models.TextField()
sender = models.EmailField() sender = models.EmailField()
cc_myself = models.BooleanField() cc_myself = models.BooleanField(required=False)
You could use this model to create a form (using ``form_for_model()``). You You could use this model to create a form (using ``form_for_model()``). You
could also use existing ``Message`` instances to create a form for editing could also use existing ``Message`` instances to create a form for editing

View File

@ -172,7 +172,7 @@ of the case of the actual model class name.
ADMIN_FOR ADMIN_FOR
--------- ---------
Default: ``()`` (Empty list) Default: ``()`` (Empty tuple)
Used for admin-site settings modules, this should be a tuple of settings Used for admin-site settings modules, this should be a tuple of settings
modules (in the format ``'foo.bar.baz'``) for which this site is an admin. modules (in the format ``'foo.bar.baz'``) for which this site is an admin.
@ -578,6 +578,17 @@ strings for translation, but the translation won't happen at runtime -- so
you'll have to remember to wrap the languages in the *real* ``gettext()`` in you'll have to remember to wrap the languages in the *real* ``gettext()`` in
any code that uses ``LANGUAGES`` at runtime. any code that uses ``LANGUAGES`` at runtime.
LOCALE_PATHS
------------
Default: ``()`` (Empty tuple)
A list of directories where Django looks for translation files.
See the `internationalization docs section`_ explaining the variable and the
default behavior.
.. _internationalization docs section: ../i18n/#using-translations-in-your-own-projects
LOGIN_REDIRECT_URL LOGIN_REDIRECT_URL
------------------ ------------------
@ -773,6 +784,18 @@ Default: ``'sessionid'``
The name of the cookie to use for sessions. This can be whatever you want. The name of the cookie to use for sessions. This can be whatever you want.
See the `session docs`_. See the `session docs`_.
SESSION_COOKIE_PATH
-------------------
**New in Django development version**
Default: ``'/'``
The path set on the session cookie. Should match the URL path of your Django
installation (or be parent of that path). This is useful if you have multiple
Django instances running under the same hostname; they can use different
cookie paths and each instance will only see its own session cookie.
SESSION_COOKIE_SECURE SESSION_COOKIE_SECURE
--------------------- ---------------------

View File

@ -547,6 +547,22 @@ This example illustrates all possible attributes and methods for a ``Feed`` clas
copyright = 'Copyright (c) 2007, Sally Smith' # Hard-coded copyright notice. copyright = 'Copyright (c) 2007, Sally Smith' # Hard-coded copyright notice.
# TTL -- One of the following three is optional. The framework looks
# for them in this order. Ignored for Atom feeds.
def ttl(self, obj):
"""
Takes the object returned by get_object() and returns the feed's
TTL (Time to live) as a normal Python string.
"""
def ttl(self):
"""
Returns the feed's ttl as a normal Python string.
"""
ttl = 600 # Hard-coded Time to live.
# ITEMS -- One of the following three is required. The framework looks # ITEMS -- One of the following three is required. The framework looks
# for them in this order. # for them in this order.

View File

@ -2,13 +2,35 @@
The Django template language: For template authors The Django template language: For template authors
================================================== ==================================================
This document explains the language syntax of the Django template system. If
you're looking for a more technical perspective on how it works and how to
extend it, see `The Django template language: For Python programmers`_.
Django's template language is designed to strike a balance between power and Django's template language is designed to strike a balance between power and
ease. It's designed to feel comfortable to those used to working with HTML. If ease. It's designed to feel comfortable to those used to working with HTML. If
you have any exposure to other text-based template languages, such as Smarty_ you have any exposure to other text-based template languages, such as Smarty_
or CheetahTemplate_, you should feel right at home with Django's templates. or CheetahTemplate_, you should feel right at home with Django's templates.
.. admonition:: Philosophy
If you have a background in programming, or if you're used to languages
like PHP which mix programming code directly into HTML, you'll want to
bear in mind that the Django template system is not simply Python embedded
into HTML. This is by design: the template system is meant to express
presentation, not program logic.
The Django template system provides tags which function similarly to some
programming constructs -- an ``{% if %}`` tag for boolean tests, a ``{%
for %}`` tag for looping, etc. -- but these are not simply executed as the
corresponding Python code, and the template system will not execute
arbitrary Python expressions. Only the tags, filters and syntax listed
below are supported by default (although you can add `your own
extensions`_ to the template language as needed).
.. _`The Django template language: For Python programmers`: ../templates_python/
.. _Smarty: http://smarty.php.net/ .. _Smarty: http://smarty.php.net/
.. _CheetahTemplate: http://www.cheetahtemplate.org/ .. _CheetahTemplate: http://www.cheetahtemplate.org/
.. _your own extensions: ../templates_python/#extending-the-template-system
Templates Templates
========= =========
@ -454,6 +476,11 @@ This is equivalent to::
{{ var3 }} {{ var3 }}
{% endif %}{% endif %}{% endif %} {% endif %}{% endif %}{% endif %}
You can also use a literal string as a fallback value in case all
passed variables are False::
{% firstof var1 var2 var3 "fallback value" %}
for for
~~~ ~~~
@ -1438,12 +1465,3 @@ A collection of template tags that can be useful while designing a website,
such as a generator of Lorem Ipsum text. See the `webdesign documentation`_. such as a generator of Lorem Ipsum text. See the `webdesign documentation`_.
.. _webdesign documentation: ../webdesign/ .. _webdesign documentation: ../webdesign/
Next steps
==========
Read the document `The Django template language: For Python programmers`_ if
you're interested in learning the template system from a technical
perspective -- how it works and how to extend it.
.. _The Django template language\: For Python programmers: ../templates_python/

View File

@ -41,7 +41,7 @@ From the command line, ``cd`` into a directory where you'd like to store your
code, then run the command ``django-admin.py startproject mysite``. This code, then run the command ``django-admin.py startproject mysite``. This
will create a ``mysite`` directory in your current directory. will create a ``mysite`` directory in your current directory.
.. admonition:: Max OS X permissions .. admonition:: Mac OS X permissions
If you're using Mac OS X, you may see the message "permission If you're using Mac OS X, you may see the message "permission
denied" when you try to run ``django-admin.py startproject``. This denied" when you try to run ``django-admin.py startproject``. This

View File

@ -14,6 +14,7 @@ class FieldErrors(models.Model):
choices = models.CharField(max_length=10, choices='bad') choices = models.CharField(max_length=10, choices='bad')
choices2 = models.CharField(max_length=10, choices=[(1,2,3),(1,2,3)]) choices2 = models.CharField(max_length=10, choices=[(1,2,3),(1,2,3)])
index = models.CharField(max_length=10, db_index='bad') index = models.CharField(max_length=10, db_index='bad')
field_ = models.CharField(max_length=10)
class Target(models.Model): class Target(models.Model):
tgt_safe = models.CharField(max_length=10) tgt_safe = models.CharField(max_length=10)
@ -116,6 +117,7 @@ invalid_models.fielderrors: "choices": "choices" should be iterable (e.g., a tup
invalid_models.fielderrors: "choices2": "choices" should be a sequence of two-tuples. invalid_models.fielderrors: "choices2": "choices" should be a sequence of two-tuples.
invalid_models.fielderrors: "choices2": "choices" should be a sequence of two-tuples. invalid_models.fielderrors: "choices2": "choices" should be a sequence of two-tuples.
invalid_models.fielderrors: "index": "db_index" should be either None, True or False. invalid_models.fielderrors: "index": "db_index" should be either None, True or False.
invalid_models.fielderrors: "field_": Field names cannot end with underscores, because this would lead to ambiguous queryset filters.
invalid_models.clash1: Accessor for field 'foreign' clashes with field 'Target.clash1_set'. Add a related_name argument to the definition for 'foreign'. invalid_models.clash1: Accessor for field 'foreign' clashes with field 'Target.clash1_set'. Add a related_name argument to the definition for 'foreign'.
invalid_models.clash1: Accessor for field 'foreign' clashes with related m2m field 'Target.clash1_set'. Add a related_name argument to the definition for 'foreign'. invalid_models.clash1: Accessor for field 'foreign' clashes with related m2m field 'Target.clash1_set'. Add a related_name argument to the definition for 'foreign'.
invalid_models.clash1: Reverse query name for field 'foreign' clashes with field 'Target.clash1'. Add a related_name argument to the definition for 'foreign'. invalid_models.clash1: Reverse query name for field 'foreign' clashes with field 'Target.clash1'. Add a related_name argument to the definition for 'foreign'.

View File

@ -1,4 +1,9 @@
""" try:
set
except NameError:
from sets import Set as set # Python 2.3 fallback
__test__ = {'API_TESTS': """
>>> from django.contrib.auth.models import User, Group, Permission >>> from django.contrib.auth.models import User, Group, Permission
>>> from django.contrib.contenttypes.models import ContentType >>> from django.contrib.contenttypes.models import ContentType
@ -28,10 +33,10 @@ False
# reloading user to purge the _perm_cache # reloading user to purge the _perm_cache
>>> user = User.objects.get(username="test") >>> user = User.objects.get(username="test")
>>> user.get_all_permissions() >>> user.get_all_permissions() == set([u'auth.test'])
set([u'auth.test']) True
>>> user.get_group_permissions() >>> user.get_group_permissions() == set([])
set([]) True
>>> user.has_module_perms("Group") >>> user.has_module_perms("Group")
False False
>>> user.has_module_perms("auth") >>> user.has_module_perms("auth")
@ -43,8 +48,8 @@ True
>>> user.user_permissions.add(perm) >>> user.user_permissions.add(perm)
>>> user.save() >>> user.save()
>>> user = User.objects.get(username="test") >>> user = User.objects.get(username="test")
>>> user.get_all_permissions() >>> user.get_all_permissions() == set([u'auth.test2', u'auth.test', u'auth.test3'])
set([u'auth.test2', u'auth.test', u'auth.test3']) True
>>> user.has_perm('test') >>> user.has_perm('test')
False False
>>> user.has_perm('auth.test') >>> user.has_perm('auth.test')
@ -57,10 +62,11 @@ True
>>> group.save() >>> group.save()
>>> user.groups.add(group) >>> user.groups.add(group)
>>> user = User.objects.get(username="test") >>> user = User.objects.get(username="test")
>>> user.get_all_permissions() >>> exp = set([u'auth.test2', u'auth.test', u'auth.test3', u'auth.test_group'])
set([u'auth.test2', u'auth.test', u'auth.test3', u'auth.test_group']) >>> user.get_all_permissions() == exp
>>> user.get_group_permissions() True
set([u'auth.test_group']) >>> user.get_group_permissions() == set([u'auth.test_group'])
True
>>> user.has_perms(['auth.test3', 'auth.test_group']) >>> user.has_perms(['auth.test3', 'auth.test_group'])
True True
""" """}

View File

@ -19,6 +19,12 @@ class Cache(unittest.TestCase):
cache.set("key", "value") cache.set("key", "value")
self.assertEqual(cache.get("key"), "value") self.assertEqual(cache.get("key"), "value")
def test_add(self):
# test add (only add if key isn't already in cache)
cache.add("addkey1", "value")
cache.add("addkey1", "newvalue")
self.assertEqual(cache.get("addkey1"), "value")
def test_non_existent(self): def test_non_existent(self):
# get with non-existent keys # get with non-existent keys
self.assertEqual(cache.get("does_not_exist"), None) self.assertEqual(cache.get("does_not_exist"), None)

View File

@ -54,6 +54,17 @@
True True
>>> print repr(d) >>> print repr(d)
{'one': 'not one', 'two': 'two', 'three': 'three'} {'one': 'not one', 'two': 'two', 'three': 'three'}
>>> d.pop('one', 'missing')
'not one'
>>> d.pop('one', 'missing')
'missing'
We don't know which item will be popped in popitem(), so we'll just check that
the number of keys has decreased.
>>> l = len(d)
>>> _ = d.popitem()
>>> l - len(d)
1
Init from sequence of tuples Init from sequence of tuples
>>> d = SortedDict(( >>> d = SortedDict((

View File

@ -0,0 +1,343 @@
# -*- coding: utf-8 -*-
# Tests for the contrib/localflavor/ ES form fields.
tests = r"""
# ESPostalCodeField ##############################################################
ESPostalCodeField validates that data is a five-digit spanish postal code.
>>> from django.contrib.localflavor.es.forms import ESPostalCodeField
>>> f = ESPostalCodeField()
>>> f.clean('08028')
u'08028'
>>> f.clean('28080')
u'28080'
>>> f.clean('53001')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
>>> f.clean('0801')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
>>> f.clean('080001')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
>>> f.clean('00999')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
>>> f.clean('08 01')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
>>> f.clean('08A01')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'This field is required.']
>>> f = ESPostalCodeField(required=False)
>>> f.clean('08028')
u'08028'
>>> f.clean('28080')
u'28080'
>>> f.clean('53001')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
>>> f.clean('0801')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
>>> f.clean('080001')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
>>> f.clean('00999')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
>>> f.clean('08 01')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
>>> f.clean('08A01')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid postal code in the range and format 01XXX - 52XXX.']
>>> f.clean('')
u''
# ESPhoneNumberField ##############################################################
ESPhoneNumberField validates that data is a nine-digit spanish phone number.
>>> from django.contrib.localflavor.es.forms import ESPhoneNumberField
>>> f = ESPhoneNumberField()
>>> f.clean('650010101')
u'650010101'
>>> f.clean('931234567')
u'931234567'
>>> f.clean('800123123')
u'800123123'
>>> f.clean('555555555')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.']
>>> f.clean('789789789')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.']
>>> f.clean('99123123')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.']
>>> f.clean('9999123123')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.']
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'This field is required.']
>>> f = ESPhoneNumberField(required=False)
>>> f.clean('650010101')
u'650010101'
>>> f.clean('931234567')
u'931234567'
>>> f.clean('800123123')
u'800123123'
>>> f.clean('555555555')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.']
>>> f.clean('789789789')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.']
>>> f.clean('99123123')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.']
>>> f.clean('9999123123')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX.']
>>> f.clean('')
u''
# ESIdentityCardNumberField ##############################################################
ESIdentityCardNumberField validates that data is a identification spanish code for companies or individuals (CIF, NIF or NIE).
>>> from django.contrib.localflavor.es.forms import ESIdentityCardNumberField
>>> f = ESIdentityCardNumberField()
>>> f.clean('78699688J')
'78699688J'
>>> f.clean('78699688-J')
'78699688J'
>>> f.clean('78699688 J')
'78699688J'
>>> f.clean('78699688 j')
'78699688J'
>>> f.clean('78699688T')
Traceback (most recent call last):
...
ValidationError: [u'Invalid checksum for NIF.']
>>> f.clean('X0901797J')
'X0901797J'
>>> f.clean('X-6124387-Q')
'X6124387Q'
>>> f.clean('X 0012953 G')
'X0012953G'
>>> f.clean('x-3287690-r')
'X3287690R'
>>> f.clean('t-03287690r')
'T03287690R'
>>> f.clean('X-03287690')
Traceback (most recent call last):
...
ValidationError: [u'Please enter a valid NIF, NIE, or CIF.']
>>> f.clean('X-03287690-T')
Traceback (most recent call last):
...
ValidationError: [u'Invalid checksum for NIE.']
>>> f.clean('B38790911')
'B38790911'
>>> f.clean('B-3879091A')
'B3879091A'
>>> f.clean('B 38790917')
Traceback (most recent call last):
...
ValidationError: [u'Invalid checksum for CIF.']
>>> f.clean('B 38790911')
'B38790911'
>>> f.clean('P-3900800-H')
'P3900800H'
>>> f.clean('P 39008008')
'P39008008'
>>> f.clean('C-28795565')
'C28795565'
>>> f.clean('C 2879556E')
'C2879556E'
>>> f.clean('C28795567')
Traceback (most recent call last):
...
ValidationError: [u'Invalid checksum for CIF.']
>>> f.clean('I38790911')
Traceback (most recent call last):
...
ValidationError: [u'Please enter a valid NIF, NIE, or CIF.']
>>> f.clean('78699688-2')
Traceback (most recent call last):
...
ValidationError: [u'Please enter a valid NIF, NIE, or CIF.']
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'This field is required.']
>>> f = ESIdentityCardNumberField(required=False)
>>> f.clean('78699688J')
'78699688J'
>>> f.clean('78699688-J')
'78699688J'
>>> f.clean('78699688 J')
'78699688J'
>>> f.clean('78699688 j')
'78699688J'
>>> f.clean('78699688T')
Traceback (most recent call last):
...
ValidationError: [u'Invalid checksum for NIF.']
>>> f.clean('X0901797J')
'X0901797J'
>>> f.clean('X-6124387-Q')
'X6124387Q'
>>> f.clean('X 0012953 G')
'X0012953G'
>>> f.clean('x-3287690-r')
'X3287690R'
>>> f.clean('t-03287690r')
'T03287690R'
>>> f.clean('X-03287690')
Traceback (most recent call last):
...
ValidationError: [u'Please enter a valid NIF, NIE, or CIF.']
>>> f.clean('X-03287690-T')
Traceback (most recent call last):
...
ValidationError: [u'Invalid checksum for NIE.']
>>> f.clean('B38790911')
'B38790911'
>>> f.clean('B-3879091A')
'B3879091A'
>>> f.clean('B 38790917')
Traceback (most recent call last):
...
ValidationError: [u'Invalid checksum for CIF.']
>>> f.clean('B 38790911')
'B38790911'
>>> f.clean('P-3900800-H')
'P3900800H'
>>> f.clean('P 39008008')
'P39008008'
>>> f.clean('C-28795565')
'C28795565'
>>> f.clean('C 2879556E')
'C2879556E'
>>> f.clean('C28795567')
Traceback (most recent call last):
...
ValidationError: [u'Invalid checksum for CIF.']
>>> f.clean('I38790911')
Traceback (most recent call last):
...
ValidationError: [u'Please enter a valid NIF, NIE, or CIF.']
>>> f.clean('78699688-2')
Traceback (most recent call last):
...
ValidationError: [u'Please enter a valid NIF, NIE, or CIF.']
>>> f.clean('')
u''
# ESCCCField ##############################################################
ESCCCField validates that data is a spanish bank account number (codigo cuenta cliente).
>>> from django.contrib.localflavor.es.forms import ESCCCField
>>> f = ESCCCField()
>>> f.clean('20770338793100254321')
'20770338793100254321'
>>> f.clean('2077 0338 79 3100254321')
'2077 0338 79 3100254321'
>>> f.clean('2077-0338-79-3100254321')
'2077-0338-79-3100254321'
>>> f.clean('2077.0338.79.3100254321')
Traceback (most recent call last):
...
ValidationError: [u'Please enter a valid bank account number in format XXXX-XXXX-XX-XXXXXXXXXX.']
>>> f.clean('2077-0338-78-3100254321')
Traceback (most recent call last):
...
ValidationError: [u'Invalid checksum for bank account number.']
>>> f.clean('2077-0338-89-3100254321')
Traceback (most recent call last):
...
ValidationError: [u'Invalid checksum for bank account number.']
>>> f.clean('2077-03-3879-3100254321')
Traceback (most recent call last):
...
ValidationError: [u'Please enter a valid bank account number in format XXXX-XXXX-XX-XXXXXXXXXX.']
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'This field is required.']
>>> f = ESCCCField(required=False)
>>> f.clean('20770338793100254321')
'20770338793100254321'
>>> f.clean('2077 0338 79 3100254321')
'2077 0338 79 3100254321'
>>> f.clean('2077-0338-79-3100254321')
'2077-0338-79-3100254321'
>>> f.clean('2077.0338.79.3100254321')
Traceback (most recent call last):
...
ValidationError: [u'Please enter a valid bank account number in format XXXX-XXXX-XX-XXXXXXXXXX.']
>>> f.clean('2077-0338-78-3100254321')
Traceback (most recent call last):
...
ValidationError: [u'Invalid checksum for bank account number.']
>>> f.clean('2077-0338-89-3100254321')
Traceback (most recent call last):
...
ValidationError: [u'Invalid checksum for bank account number.']
>>> f.clean('2077-03-3879-3100254321')
Traceback (most recent call last):
...
ValidationError: [u'Please enter a valid bank account number in format XXXX-XXXX-XX-XXXXXXXXXX.']
>>> f.clean('')
u''
# ESRegionSelect ##############################################################
ESRegionSelect is a Select widget that uses a list of Spain regions as its choices.
>>> from django.contrib.localflavor.es.forms import ESRegionSelect
>>> w = ESRegionSelect()
>>> w.render('regions', 'CT')
u'<select name="regions">\n<option value="AN">Andalusia</option>\n<option value="AR">Aragon</option>\n<option value="O">Principality of Asturias</option>\n<option value="IB">Balearic Islands</option>\n<option value="PV">Basque Country</option>\n<option value="CN">Canary Islands</option>\n<option value="S">Cantabria</option>\n<option value="CM">Castile-La Mancha</option>\n<option value="CL">Castile and Leon</option>\n<option value="CT" selected="selected">Catalonia</option>\n<option value="EX">Extremadura</option>\n<option value="GA">Galicia</option>\n<option value="LO">La Rioja</option>\n<option value="M">Madrid</option>\n<option value="MU">Region of Murcia</option>\n<option value="NA">Foral Community of Navarre</option>\n<option value="VC">Valencian Community</option>\n</select>'
# ESProvincenSelect ##############################################################
ESProvinceSelect is a Select widget that uses a list of Spain provinces as its choices.
>>> from django.contrib.localflavor.es.forms import ESProvinceSelect
>>> w = ESProvinceSelect()
>>> w.render('provinces', '08')
u'<select name="provinces">\n<option value="01">Arava</option>\n<option value="02">Albacete</option>\n<option value="03">Alacant</option>\n<option value="04">Almeria</option>\n<option value="05">Avila</option>\n<option value="06">Badajoz</option>\n<option value="07">Illes Balears</option>\n<option value="08" selected="selected">Barcelona</option>\n<option value="09">Burgos</option>\n<option value="10">Caceres</option>\n<option value="11">Cadiz</option>\n<option value="12">Castello</option>\n<option value="13">Ciudad Real</option>\n<option value="14">Cordoba</option>\n<option value="15">A Coruna</option>\n<option value="16">Cuenca</option>\n<option value="17">Girona</option>\n<option value="18">Granada</option>\n<option value="19">Guadalajara</option>\n<option value="20">Guipuzkoa</option>\n<option value="21">Huelva</option>\n<option value="22">Huesca</option>\n<option value="23">Jaen</option>\n<option value="24">Leon</option>\n<option value="25">Lleida</option>\n<option value="26">La Rioja</option>\n<option value="27">Lugo</option>\n<option value="28">Madrid</option>\n<option value="29">Malaga</option>\n<option value="30">Murcia</option>\n<option value="31">Navarre</option>\n<option value="32">Ourense</option>\n<option value="33">Asturias</option>\n<option value="34">Palencia</option>\n<option value="35">Las Palmas</option>\n<option value="36">Pontevedra</option>\n<option value="37">Salamanca</option>\n<option value="38">Santa Cruz de Tenerife</option>\n<option value="39">Cantabria</option>\n<option value="40">Segovia</option>\n<option value="41">Seville</option>\n<option value="42">Soria</option>\n<option value="43">Tarragona</option>\n<option value="44">Teruel</option>\n<option value="45">Toledo</option>\n<option value="46">Valencia</option>\n<option value="47">Valladolid</option>\n<option value="48">Bizkaia</option>\n<option value="49">Zamora</option>\n<option value="50">Zaragoza</option>\n<option value="51">Ceuta</option>\n<option value="52">Melilla</option>\n</select>'
"""

View File

@ -35,10 +35,14 @@ u'41-403'
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValidationError: [u'Enter a tax number field (NIP) in the format XXX-XXX-XX-XX or XX-XX-XXX-XXX.'] ValidationError: [u'Enter a tax number field (NIP) in the format XXX-XXX-XX-XX or XX-XX-XXX-XXX.']
>>> f.clean('43-34-234-323') >>> f.clean('64-62-414-124')
u'43-34-234-323' u'6462414124'
>>> f.clean('433-344-24-23') >>> f.clean('646-241-41-24')
u'433-344-24-23' u'6462414124'
>>> f.clean('646-241-41-23')
Traceback (most recent call last):
...
ValidationError: [u'Wrong checksum for the Tax Number (NIP).']
# PLNationalIdentificationNumberField ############################################ # PLNationalIdentificationNumberField ############################################
@ -58,4 +62,20 @@ ValidationError: [u'National Identification Number consists of 11 digits.']
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValidationError: [u'National Identification Number consists of 11 digits.'] ValidationError: [u'National Identification Number consists of 11 digits.']
# PLNationalBusinessRegisterField ################################################
>>> from django.contrib.localflavor.pl.forms import PLNationalBusinessRegisterField
>>> f = PLNationalBusinessRegisterField()
>>> f.clean('590096454')
u'590096454'
>>> f.clean('590096453')
Traceback (most recent call last):
...
ValidationError: [u'Wrong checksum for the National Business Register Number (REGON).']
>>> f.clean('590096')
Traceback (most recent call last):
...
ValidationError: [u'National Business Register Number (REGON) consists of 7 or 9 digits.']
""" """

View File

@ -1,10 +1,17 @@
import datetime
from django.db import models from django.db import models
class BoundaryModel(models.Model): class BoundaryModel(models.Model):
positive_integer = models.PositiveIntegerField(null=True, blank=True) positive_integer = models.PositiveIntegerField(null=True, blank=True)
class Defaults(models.Model):
name = models.CharField(max_length=256, default='class default value')
def_date = models.DateField(default = datetime.date(1980, 1, 1))
value = models.IntegerField(default=42)
__test__ = {'API_TESTS': """ __test__ = {'API_TESTS': """
>>> from django.newforms import form_for_model >>> from django.newforms import form_for_model, form_for_instance
# Boundary conditions on a PostitiveIntegerField ######################### # Boundary conditions on a PostitiveIntegerField #########################
>>> BoundaryForm = form_for_model(BoundaryModel) >>> BoundaryForm = form_for_model(BoundaryModel)
@ -18,4 +25,25 @@ True
>>> f.is_valid() >>> f.is_valid()
False False
# Formfield initial values ########
If the model has default values for some fields, they are used as the formfield
initial values.
>>> DefaultsForm = form_for_model(Defaults)
>>> DefaultsForm().fields['name'].initial
u'class default value'
>>> DefaultsForm().fields['def_date'].initial
datetime.date(1980, 1, 1)
>>> DefaultsForm().fields['value'].initial
42
In form_for_instance(), the initial values come from the instance's values, not
the model's defaults.
>>> foo_instance = Defaults(name=u'instance value', def_date = datetime.date(1969, 4, 4), value = 12)
>>> InstanceForm = form_for_instance(foo_instance)
>>> InstanceForm().fields['name'].initial
u'instance value'
>>> InstanceForm().fields['def_date'].initial
datetime.date(1969, 4, 4)
>>> InstanceForm().fields['value'].initial
12
"""} """}

View File

@ -9,6 +9,7 @@ from localflavor.ca import tests as localflavor_ca_tests
from localflavor.ch import tests as localflavor_ch_tests from localflavor.ch import tests as localflavor_ch_tests
from localflavor.cl import tests as localflavor_cl_tests from localflavor.cl import tests as localflavor_cl_tests
from localflavor.de import tests as localflavor_de_tests from localflavor.de import tests as localflavor_de_tests
from localflavor.es import tests as localflavor_es_tests
from localflavor.fi import tests as localflavor_fi_tests from localflavor.fi import tests as localflavor_fi_tests
from localflavor.fr import tests as localflavor_fr_tests from localflavor.fr import tests as localflavor_fr_tests
from localflavor.generic import tests as localflavor_generic_tests from localflavor.generic import tests as localflavor_generic_tests
@ -35,6 +36,7 @@ __test__ = {
'localflavor_ch_tests': localflavor_ch_tests, 'localflavor_ch_tests': localflavor_ch_tests,
'localflavor_cl_tests': localflavor_cl_tests, 'localflavor_cl_tests': localflavor_cl_tests,
'localflavor_de_tests': localflavor_de_tests, 'localflavor_de_tests': localflavor_de_tests,
'localflavor_es_tests': localflavor_es_tests,
'localflavor_fi_tests': localflavor_fi_tests, 'localflavor_fi_tests': localflavor_fi_tests,
'localflavor_fr_tests': localflavor_fr_tests, 'localflavor_fr_tests': localflavor_fr_tests,
'localflavor_generic_tests': localflavor_generic_tests, 'localflavor_generic_tests': localflavor_generic_tests,

View File

@ -276,6 +276,12 @@ u'<input type="checkbox" name="greeting" />'
>>> w.render('greeting', None) >>> w.render('greeting', None)
u'<input type="checkbox" name="greeting" />' u'<input type="checkbox" name="greeting" />'
The CheckboxInput widget will return False if the key is not found in the data
dictionary (because HTML form submission doesn't send any result for unchecked
checkboxes).
>>> w.value_from_datadict({}, {}, 'testing')
False
# Select Widget ############################################################### # Select Widget ###############################################################
>>> w = Select() >>> w = Select()
@ -845,4 +851,21 @@ included on both widgets.
>>> w = SplitDateTimeWidget(attrs={'class': 'pretty'}) >>> w = SplitDateTimeWidget(attrs={'class': 'pretty'})
>>> w.render('date', datetime.datetime(2006, 1, 10, 7, 30)) >>> w.render('date', datetime.datetime(2006, 1, 10, 7, 30))
u'<input type="text" class="pretty" value="2006-01-10" name="date_0" /><input type="text" class="pretty" value="07:30:00" name="date_1" />' u'<input type="text" class="pretty" value="2006-01-10" name="date_0" /><input type="text" class="pretty" value="07:30:00" name="date_1" />'
# DateTimeInput ###############################################################
>>> w = DateTimeInput()
>>> w.render('date', None)
u'<input type="text" name="date" />'
>>> d = datetime.datetime(2007, 9, 17, 12, 51, 34, 482548)
>>> print d
2007-09-17 12:51:34.482548
The microseconds are trimmed on display, by default.
>>> w.render('date', d)
u'<input type="text" name="date" value="2007-09-17 12:51:34" />'
>>> w.render('date', datetime.datetime(2007, 9, 17, 12, 51, 34))
u'<input type="text" name="date" value="2007-09-17 12:51:34" />'
>>> w.render('date', datetime.datetime(2007, 9, 17, 12, 51))
u'<input type="text" name="date" value="2007-09-17 12:51:00" />'
""" """

View File

@ -0,0 +1,57 @@
tests = """
>>> from django.utils.translation.trans_real import parse_accept_lang_header
>>> p = parse_accept_lang_header
Good headers.
>>> p('de')
[('de', 1.0)]
>>> p('en-AU')
[('en-AU', 1.0)]
>>> p('*;q=1.00')
[('*', 1.0)]
>>> p('en-AU;q=0.123')
[('en-AU', 0.123)]
>>> p('en-au;q=0.1')
[('en-au', 0.10000000000000001)]
>>> p('en-au;q=1.0')
[('en-au', 1.0)]
>>> p('da, en-gb;q=0.25, en;q=0.5')
[('da', 1.0), ('en', 0.5), ('en-gb', 0.25)]
>>> p('en-au-xx')
[('en-au-xx', 1.0)]
>>> p('de,en-au;q=0.75,en-us;q=0.5,en;q=0.25,es;q=0.125,fa;q=0.125')
[('de', 1.0), ('en-au', 0.75), ('en-us', 0.5), ('en', 0.25), ('es', 0.125), ('fa', 0.125)]
>>> p('*')
[('*', 1.0)]
>>> p('de;q=0.')
[('de', 1.0)]
>>> p('')
[]
Bad headers; should always return [].
>>> p('en-gb;q=1.0000')
[]
>>> p('en;q=0.1234')
[]
>>> p('en;q=.2')
[]
>>> p('abcdefghi-au')
[]
>>> p('**')
[]
>>> p('en,,gb')
[]
>>> p('en-au;q=0.1.0')
[]
>>> p('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXZ,en')
[]
>>> p('da, en-gb;q=0.8, en;q=0.7,#')
[]
>>> p('de;q=2.0')
[]
>>> p('de;q=0.a')
[]
>>> p('')
[]
"""

View File

@ -1,6 +1,7 @@
# coding: utf-8 # coding: utf-8
import misc
ur""" regressions = ur"""
Format string interpolation should work with *_lazy objects. Format string interpolation should work with *_lazy objects.
>>> from django.utils.translation import ugettext_lazy, activate, deactivate, gettext_lazy >>> from django.utils.translation import ugettext_lazy, activate, deactivate, gettext_lazy
@ -39,3 +40,8 @@ unicode(string_concat(...)) should not raise a TypeError - #4796
>>> unicode(django.utils.translation.string_concat("dja", "ngo")) >>> unicode(django.utils.translation.string_concat("dja", "ngo"))
u'django' u'django'
""" """
__test__ = {
'regressions': regressions,
'misc': misc.tests,
}

View File

@ -13,7 +13,7 @@ from datetime import datetime, timedelta
from django import template from django import template
from django.template import loader from django.template import loader
from django.template.loaders import app_directories, filesystem from django.template.loaders import app_directories, filesystem
from django.utils.translation import activate, deactivate, install, ugettext as _ from django.utils.translation import activate, deactivate, ugettext as _
from django.utils.tzinfo import LocalTimezone from django.utils.tzinfo import LocalTimezone
from unicode import unicode_tests from unicode import unicode_tests
@ -341,7 +341,10 @@ class Templates(unittest.TestCase):
'firstof03': ('{% firstof a b c %}', {'a':0,'b':2,'c':0}, '2'), 'firstof03': ('{% firstof a b c %}', {'a':0,'b':2,'c':0}, '2'),
'firstof04': ('{% firstof a b c %}', {'a':0,'b':0,'c':3}, '3'), 'firstof04': ('{% firstof a b c %}', {'a':0,'b':0,'c':3}, '3'),
'firstof05': ('{% firstof a b c %}', {'a':1,'b':2,'c':3}, '1'), 'firstof05': ('{% firstof a b c %}', {'a':1,'b':2,'c':3}, '1'),
'firstof06': ('{% firstof %}', {}, template.TemplateSyntaxError), 'firstof06': ('{% firstof a b c %}', {'b':0,'c':3}, '3'),
'firstof07': ('{% firstof a b "c" %}', {'a':0}, 'c'),
'firstof08': ('{% firstof a b "c and d" %}', {'a':0,'b':0}, 'c and d'),
'firstof09': ('{% firstof %}', {}, template.TemplateSyntaxError),
### FOR TAG ############################################################### ### FOR TAG ###############################################################
'for-tag01': ("{% for val in values %}{{ val }}{% endfor %}", {"values": [1, 2, 3]}, "123"), 'for-tag01': ("{% for val in values %}{{ val }}{% endfor %}", {"values": [1, 2, 3]}, "123"),
@ -802,6 +805,20 @@ class Templates(unittest.TestCase):
'url-fail01' : ('{% url %}', {}, template.TemplateSyntaxError), 'url-fail01' : ('{% url %}', {}, template.TemplateSyntaxError),
'url-fail02' : ('{% url no_such_view %}', {}, ''), 'url-fail02' : ('{% url no_such_view %}', {}, ''),
'url-fail03' : ('{% url regressiontests.templates.views.client no_such_param="value" %}', {}, ''), 'url-fail03' : ('{% url regressiontests.templates.views.client no_such_param="value" %}', {}, ''),
### CACHE TAG ######################################################
'cache01' : ('{% load cache %}{% cache -1 test %}cache01{% endcache %}', {}, 'cache01'),
'cache02' : ('{% load cache %}{% cache -1 test %}cache02{% endcache %}', {}, 'cache02'),
'cache03' : ('{% load cache %}{% cache 2 test %}cache03{% endcache %}', {}, 'cache03'),
'cache04' : ('{% load cache %}{% cache 2 test %}cache04{% endcache %}', {}, 'cache03'),
'cache05' : ('{% load cache %}{% cache 2 test foo %}cache05{% endcache %}', {'foo': 1}, 'cache05'),
'cache06' : ('{% load cache %}{% cache 2 test foo %}cache06{% endcache %}', {'foo': 2}, 'cache06'),
'cache07' : ('{% load cache %}{% cache 2 test foo %}cache06{% endcache %}', {'foo': 1}, 'cache05'),
# Raise exception if we dont have at least 2 args, first one integer.
'cache08' : ('{% load cache %}{% cache %}{% endcache %}', {}, template.TemplateSyntaxError),
'cache09' : ('{% load cache %}{% cache 1 %}{% endcache %}', {}, template.TemplateSyntaxError),
'cache10' : ('{% load cache %}{% cache foo bar %}{% endcache %}', {}, template.TemplateSyntaxError),
} }
# Register our custom template loader. # Register our custom template loader.
@ -827,8 +844,6 @@ class Templates(unittest.TestCase):
expected_invalid_str = 'INVALID' expected_invalid_str = 'INVALID'
for name, vals in tests: for name, vals in tests:
install()
if isinstance(vals[2], tuple): if isinstance(vals[2], tuple):
normal_string_result = vals[2][0] normal_string_result = vals[2][0]
invalid_string_result = vals[2][1] invalid_string_result = vals[2][1]

View File

@ -20,8 +20,12 @@ friends'
>>> from django.utils.http import urlquote, urlquote_plus >>> from django.utils.http import urlquote, urlquote_plus
>>> urlquote(u'Paris & Orl\xe9ans') >>> urlquote(u'Paris & Orl\xe9ans')
u'Paris%20%26%20Orl%C3%A9ans' u'Paris%20%26%20Orl%C3%A9ans'
>>> urlquote(u'Paris & Orl\xe9ans', safe="&")
u'Paris%20&%20Orl%C3%A9ans'
>>> urlquote_plus(u'Paris & Orl\xe9ans') >>> urlquote_plus(u'Paris & Orl\xe9ans')
u'Paris+%26+Orl%C3%A9ans' u'Paris+%26+Orl%C3%A9ans'
>>> urlquote_plus(u'Paris & Orl\xe9ans', safe="&")
u'Paris+&+Orl%C3%A9ans'
### iri_to_uri ########################################################### ### iri_to_uri ###########################################################
>>> from django.utils.encoding import iri_to_uri >>> from django.utils.encoding import iri_to_uri

View File

@ -4,7 +4,7 @@ Tests for django.utils.
from unittest import TestCase from unittest import TestCase
from django.utils import html from django.utils import html, checksums
from timesince import timesince_tests from timesince import timesince_tests
@ -116,6 +116,32 @@ class TestUtilsHtml(TestCase):
for value, output in items: for value, output in items:
self.check_output(f, value, output) self.check_output(f, value, output)
class TestUtilsChecksums(TestCase):
def check_output(self, function, value, output=None):
"""
Check that function(value) equals output. If output is None,
check that function(value) equals value.
"""
if output is None:
output = value
self.assertEqual(function(value), output)
def test_luhn(self):
f = checksums.luhn
items = (
(4111111111111111, True), ('4111111111111111', True),
(4222222222222, True), (378734493671000, True),
(5424000000000015, True), (5555555555554444, True),
(1008, True), ('0000001008', True), ('000000001008', True),
(4012888888881881, True), (1234567890123456789012345678909, True),
(4111111111211111, False), (42222222222224, False),
(100, False), ('100', False), ('0000100', False),
('abc', False), (None, False), (object(), False),
)
for value, output in items:
self.check_output(f, value, output)
__test__ = { __test__ = {
'timesince_tests': timesince_tests, 'timesince_tests': timesince_tests,
} }

View File

@ -93,6 +93,7 @@ def django_tests(verbosity, interactive, test_labels):
old_root_urlconf = settings.ROOT_URLCONF old_root_urlconf = settings.ROOT_URLCONF
old_template_dirs = settings.TEMPLATE_DIRS old_template_dirs = settings.TEMPLATE_DIRS
old_use_i18n = settings.USE_I18N old_use_i18n = settings.USE_I18N
old_language_code = settings.LANGUAGE_CODE
old_middleware_classes = settings.MIDDLEWARE_CLASSES old_middleware_classes = settings.MIDDLEWARE_CLASSES
# Redirect some settings for the duration of these tests. # Redirect some settings for the duration of these tests.
@ -100,6 +101,7 @@ def django_tests(verbosity, interactive, test_labels):
settings.ROOT_URLCONF = 'urls' settings.ROOT_URLCONF = 'urls'
settings.TEMPLATE_DIRS = (os.path.join(os.path.dirname(__file__), TEST_TEMPLATE_DIR),) settings.TEMPLATE_DIRS = (os.path.join(os.path.dirname(__file__), TEST_TEMPLATE_DIR),)
settings.USE_I18N = True settings.USE_I18N = True
settings.LANGUAGE_CODE = 'en'
settings.MIDDLEWARE_CLASSES = ( settings.MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
@ -151,6 +153,7 @@ def django_tests(verbosity, interactive, test_labels):
settings.ROOT_URLCONF = old_root_urlconf settings.ROOT_URLCONF = old_root_urlconf
settings.TEMPLATE_DIRS = old_template_dirs settings.TEMPLATE_DIRS = old_template_dirs
settings.USE_I18N = old_use_i18n settings.USE_I18N = old_use_i18n
settings.LANGUAGE_CODE = old_language_code
settings.MIDDLEWARE_CLASSES = old_middleware_classes settings.MIDDLEWARE_CLASSES = old_middleware_classes
if __name__ == "__main__": if __name__ == "__main__":