summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdrian Holovaty <adrian@holovaty.com>2006-11-24 04:17:58 +0000
committerAdrian Holovaty <adrian@holovaty.com>2006-11-24 04:17:58 +0000
commit1b54fc3abace9b5a9752c2d950222c487b960899 (patch)
treea601c2962f93b2ee860362c796f68dda8ebca452
parent47fca880e3d7bd19744b6c77ebb4667225308a7b (diff)
downloaddjango-1b54fc3abace9b5a9752c2d950222c487b960899.tar.gz
per-object-permissions: Merged to trunk [4095]
git-svn-id: http://code.djangoproject.com/svn/django/branches/per-object-permissions@4096 bcc190cf-cafb-0310-a4f2-bffc1f526a37
-rw-r--r--AUTHORS4
-rw-r--r--django/conf/__init__.py4
-rw-r--r--django/conf/global_settings.py5
-rw-r--r--django/conf/locale/de/LC_MESSAGES/django.mobin37729 -> 38123 bytes
-rw-r--r--django/conf/locale/de/LC_MESSAGES/django.po222
-rw-r--r--django/conf/locale/el/LC_MESSAGES/django.mobin8187 -> 15668 bytes
-rw-r--r--django/conf/locale/el/LC_MESSAGES/django.po264
-rw-r--r--django/conf/locale/el/LC_MESSAGES/djangojs.mobin0 -> 1810 bytes
-rw-r--r--django/conf/locale/el/LC_MESSAGES/djangojs.po109
-rw-r--r--django/conf/locale/es_AR/LC_MESSAGES/django.mobin36332 -> 37293 bytes
-rw-r--r--django/conf/locale/es_AR/LC_MESSAGES/django.po258
-rw-r--r--django/conf/locale/es_AR/LC_MESSAGES/djangojs.mobin1566 -> 1576 bytes
-rw-r--r--django/conf/locale/es_AR/LC_MESSAGES/djangojs.po34
-rw-r--r--django/conf/locale/pt_BR/LC_MESSAGES/django.mobin27702 -> 28462 bytes
-rw-r--r--django/conf/locale/pt_BR/LC_MESSAGES/django.po181
-rw-r--r--django/conf/locale/pt_BR/LC_MESSAGES/djangojs.mobin1530 -> 1537 bytes
-rw-r--r--django/conf/locale/pt_BR/LC_MESSAGES/djangojs.po12
-rw-r--r--django/contrib/admin/media/js/admin/DateTimeShortcuts.js10
-rw-r--r--django/contrib/admin/media/js/core.js8
-rw-r--r--django/contrib/admin/media/js/dateparse.js2
-rw-r--r--django/contrib/admin/templatetags/admin_modify.py2
-rw-r--r--django/contrib/admin/views/doc.py8
-rw-r--r--django/contrib/admin/views/template.py2
-rw-r--r--django/contrib/auth/__init__.py2
-rw-r--r--django/contrib/comments/models.py2
-rw-r--r--django/contrib/sitemaps/templates/sitemap.xml2
-rw-r--r--django/contrib/sitemaps/templates/sitemap_index.xml2
-rw-r--r--django/core/cache/__init__.py2
-rw-r--r--django/core/handlers/base.py2
-rw-r--r--django/core/handlers/wsgi.py3
-rw-r--r--django/core/mail.py10
-rw-r--r--django/core/management.py14
-rw-r--r--django/core/paginator.py75
-rw-r--r--django/core/serializers/__init__.py2
-rw-r--r--django/core/serializers/base.py10
-rw-r--r--django/core/serializers/python.py2
-rw-r--r--django/core/serializers/xml_serializer.py6
-rw-r--r--django/core/servers/fastcgi.py16
-rw-r--r--django/core/urlresolvers.py15
-rw-r--r--django/core/validators.py55
-rw-r--r--django/core/xheaders.py2
-rw-r--r--django/db/__init__.py12
-rw-r--r--django/db/backends/ado_mssql/base.py2
-rw-r--r--django/db/backends/dummy/base.py3
-rw-r--r--django/db/backends/mysql/base.py4
-rw-r--r--django/db/backends/oracle/base.py7
-rw-r--r--django/db/backends/postgresql/base.py5
-rw-r--r--django/db/backends/postgresql_psycopg2/base.py5
-rw-r--r--django/db/backends/sqlite3/base.py12
-rw-r--r--django/db/backends/util.py2
-rw-r--r--django/db/models/base.py18
-rw-r--r--django/db/models/fields/__init__.py15
-rw-r--r--django/db/models/loading.py2
-rw-r--r--django/db/models/manipulators.py4
-rw-r--r--django/forms/__init__.py9
-rw-r--r--django/middleware/gzip.py1
-rw-r--r--django/newforms/__init__.py28
-rw-r--r--django/newforms/fields.py290
-rw-r--r--django/newforms/forms.py197
-rw-r--r--django/newforms/util.py57
-rw-r--r--django/newforms/widgets.py153
-rw-r--r--django/template/__init__.py9
-rw-r--r--django/template/context.py2
-rw-r--r--django/template/defaultfilters.py6
-rw-r--r--django/template/defaulttags.py51
-rw-r--r--django/template/loaders/app_directories.py4
-rw-r--r--django/templatetags/__init__.py2
-rw-r--r--django/test/simple.py2
-rw-r--r--django/views/debug.py2
-rw-r--r--django/views/generic/simple.py12
-rw-r--r--docs/authentication.txt2
-rw-r--r--docs/forms.txt9
-rw-r--r--docs/generic_views.txt10
-rw-r--r--docs/modpython.txt5
-rw-r--r--docs/settings.txt18
-rw-r--r--docs/sitemaps.txt19
-rw-r--r--docs/static_files.txt4
-rw-r--r--docs/syndication_feeds.txt18
-rw-r--r--docs/templates.txt33
-rw-r--r--docs/templates_python.txt2
-rw-r--r--docs/testing.txt2
-rw-r--r--docs/tutorial04.txt4
-rw-r--r--tests/modeltests/pagination/models.py13
-rw-r--r--tests/regressiontests/forms/__init__.py0
-rw-r--r--tests/regressiontests/forms/models.py0
-rw-r--r--tests/regressiontests/forms/tests.py1242
-rw-r--r--tests/regressiontests/templates/tests.py15
87 files changed, 3022 insertions, 637 deletions
diff --git a/AUTHORS b/AUTHORS
index 83eaa63927..f49210d303 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -51,6 +51,7 @@ answer newbie questions, and generally made Django that much better:
Jiri Barton
Ned Batchelder <http://www.nedbatchelder.com/>
Shannon -jj Behrens <http://jjinux.blogspot.com/>
+ Esdras Beleza <linux@esdrasbeleza.com>
James Bennett
Paul Bissex <http://e-scribe.com/>
Simon Blanchard
@@ -91,6 +92,7 @@ answer newbie questions, and generally made Django that much better:
Kieran Holland <http://www.kieranholland.com>
Robert Rock Howard <http://djangomojo.com/>
Jason Huggins <http://www.jrandolph.com/blog/>
+ Baurzhan Ismagulov <ibr@radix50.net>
jcrasta@gmail.com
Michael Josephson <http://www.sdjournal.com/>
jpellerin@gmail.com
@@ -112,6 +114,7 @@ answer newbie questions, and generally made Django that much better:
limodou
mattmcc
Martin Maney <http://www.chipy.org/Martin_Maney>
+ masonsimon+django@gmail.com
Manuzhai
Petar Marić
mark@junklight.com
@@ -159,6 +162,7 @@ answer newbie questions, and generally made Django that much better:
Amit Upadhyay
Geert Vanderkelen
Milton Waddams
+ wam-djangobug@wamber.net
Dan Watson <http://theidioteque.net/>
Rachel Willmer <http://www.willmer.com/kb/>
Gary Wilson <gary.wilson@gmail.com>
diff --git a/django/conf/__init__.py b/django/conf/__init__.py
index 1a04bbfb02..daf5ad766a 100644
--- a/django/conf/__init__.py
+++ b/django/conf/__init__.py
@@ -77,7 +77,7 @@ class Settings(object):
self.SETTINGS_MODULE = settings_module
try:
- mod = __import__(self.SETTINGS_MODULE, '', '', [''])
+ mod = __import__(self.SETTINGS_MODULE, {}, {}, [''])
except ImportError, e:
raise EnvironmentError, "Could not import settings '%s' (Is it on sys.path? Does it have syntax errors?): %s" % (self.SETTINGS_MODULE, e)
@@ -97,7 +97,7 @@ class Settings(object):
new_installed_apps = []
for app in self.INSTALLED_APPS:
if app.endswith('.*'):
- appdir = os.path.dirname(__import__(app[:-2], '', '', ['']).__file__)
+ appdir = os.path.dirname(__import__(app[:-2], {}, {}, ['']).__file__)
for d in os.listdir(appdir):
if d.isalpha() and os.path.isdir(os.path.join(appdir, d)):
new_installed_apps.append('%s.%s' % (app[:-2], d))
diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py
index 69c775cdec..36fee9ec6d 100644
--- a/django/conf/global_settings.py
+++ b/django/conf/global_settings.py
@@ -101,6 +101,7 @@ DATABASE_USER = '' # Not used with sqlite3.
DATABASE_PASSWORD = '' # Not used with sqlite3.
DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3.
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
+DATABASE_OPTIONS = {} # Set to empty dictionary for default.
# Host for sending e-mail.
EMAIL_HOST = 'localhost'
@@ -228,6 +229,10 @@ MONTH_DAY_FORMAT = 'F j'
# Hint: you really don't!
TRANSACTIONS_MANAGED = False
+# The User-Agent string to use when checking for URL validity through the
+# isExistingURL validator.
+URL_VALIDATOR_USER_AGENT = "Django/0.96pre (http://www.djangoproject.com)"
+
##############
# MIDDLEWARE #
##############
diff --git a/django/conf/locale/de/LC_MESSAGES/django.mo b/django/conf/locale/de/LC_MESSAGES/django.mo
index aef68099d3..0dd2b26dff 100644
--- a/django/conf/locale/de/LC_MESSAGES/django.mo
+++ b/django/conf/locale/de/LC_MESSAGES/django.mo
Binary files differ
diff --git a/django/conf/locale/de/LC_MESSAGES/django.po b/django/conf/locale/de/LC_MESSAGES/django.po
index 614ea69247..c5e3f4282a 100644
--- a/django/conf/locale/de/LC_MESSAGES/django.po
+++ b/django/conf/locale/de/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Django 1.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2006-09-25 16:04+0200\n"
+"POT-Creation-Date: 2006-11-15 18:35+0100\n"
"PO-Revision-Date: 2005-10-08 00:03+0200\n"
"Last-Translator: Georg Bauer <gb@bofh.ms>\n"
"MIME-Version: 1.0\n"
@@ -452,7 +452,7 @@ msgid ""
"Looks like your browser isn't configured to accept cookies. Please enable "
"cookies, reload this page, and try again."
msgstr ""
-"Es sieht danach aus, das der Browser keine Cookies akzeptiert. Bitte im "
+"Es sieht danach aus, dass der Browser keine Cookies akzeptiert. Bitte im "
"Browser Cookies aktivieren und diese Seite neu laden."
#: contrib/admin/views/decorators.py:83
@@ -470,13 +470,13 @@ msgstr ""
msgid "Site administration"
msgstr "Website Verwaltung"
-#: contrib/admin/views/main.py:257 contrib/admin/views/auth.py:17
+#: contrib/admin/views/main.py:257 contrib/admin/views/auth.py:18
#, python-format
msgid "The %(name)s \"%(obj)s\" was added successfully."
msgstr "%(name)s \"%(obj)s\" wurde erfolgreich hinzugefügt."
#: contrib/admin/views/main.py:261 contrib/admin/views/main.py:347
-#: contrib/admin/views/auth.py:22
+#: contrib/admin/views/auth.py:23
msgid "You may edit it again below."
msgstr "Das Element kann jetzt weiter geändert werden."
@@ -496,7 +496,7 @@ msgid "Added %s."
msgstr "%s hinzugefügt."
#: contrib/admin/views/main.py:335 contrib/admin/views/main.py:337
-#: contrib/admin/views/main.py:339
+#: contrib/admin/views/main.py:339 db/models/manipulators.py:306
msgid "and"
msgstr "und"
@@ -702,7 +702,7 @@ msgstr "XML Text"
msgid "%s does not appear to be a urlpattern object"
msgstr "%s ist scheinbar kein urlpattern Objekt"
-#: contrib/admin/views/auth.py:28
+#: contrib/admin/views/auth.py:29
msgid "Add user"
msgstr "Benutzer zufügen"
@@ -861,10 +861,6 @@ msgstr "Keine vorhanden"
msgid "Add %(name)s"
msgstr "%(name)s zufügen"
-#: contrib/admin/templates/admin/login.html:22
-msgid "Have you <a href=\"/password_reset/\">forgotten your password</a>?"
-msgstr "Haben Sie <a href=\"/password_reset/\">ihr Passwort vergessen</a>?"
-
#: contrib/admin/templates/admin/base.html:25
msgid "Welcome,"
msgstr "Willkommen,"
@@ -891,7 +887,7 @@ msgid ""
"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? "
"All of the following related items will be deleted:"
msgstr ""
-"Sind Sie sicher, das Sie %(object_name)s \"%(escaped_object)s\" löschen "
+"Sind Sie sicher, dass Sie %(object_name)s \"%(escaped_object)s\" löschen "
"wollen? Es werden zusätzlich die folgenden abhängigen Daten mit gelöscht:"
#: contrib/admin/templates/admin/delete_confirmation.html:26
@@ -1061,7 +1057,7 @@ msgid ""
"password twice so we can verify you typed it in correctly."
msgstr ""
"Bitte geben Sie aus Sicherheitsgründen erst Ihr altes Kennwort und darunter "
-"dann zweimal (um sicherzustellen, das Sie es korrekt eingegeben haben) das "
+"dann zweimal (um sicherzustellen, dass Sie es korrekt eingegeben haben) das "
"neue Kennwort ein."
#: contrib/admin/templates/registration/password_change_form.html:17
@@ -1087,7 +1083,7 @@ msgstr "Sie erhalten diese Mail, weil Sie ein neues Kennwort"
#: contrib/admin/templates/registration/password_reset_email.html:3
#, python-format
msgid "for your user account at %(site_name)s"
-msgstr "für ihren Benutzer bei %(site_name)s angefordert haben."
+msgstr "für Ihren Benutzer bei %(site_name)s angefordert haben."
#: contrib/admin/templates/registration/password_reset_email.html:5
#, python-format
@@ -1104,7 +1100,7 @@ msgstr "Ihr Benutzername, falls Sie ihn vergessen haben:"
#: contrib/admin/templates/registration/password_reset_email.html:13
msgid "Thanks for using our site!"
-msgstr "Vielen Dank, das Sie unsere Seiten benutzen!"
+msgstr "Vielen Dank, dass Sie unsere Seiten benutzen!"
#: contrib/admin/templates/registration/password_reset_email.html:15
#, python-format
@@ -1367,7 +1363,7 @@ msgid ""
"Designates that this user has all permissions without explicitly assigning "
"them."
msgstr ""
-"Bestimmt, das dieser Benutzer alle Berechtigungen hat, ohne diese einzeln "
+"Bestimmt, dass dieser Benutzer alle Berechtigungen hat, ohne diese einzeln "
"zuweisen zu müssen."
#: contrib/auth/models.py:98
@@ -1414,10 +1410,18 @@ msgstr "Wichtige Daten"
msgid "Groups"
msgstr "Gruppen"
-#: contrib/auth/models.py:256
+#: contrib/auth/models.py:258
msgid "message"
msgstr "Mitteilung"
+#: contrib/auth/forms.py:16
+msgid "The two password fields didn't match."
+msgstr "Die zwei Passwörter sind nicht gleich."
+
+#: contrib/auth/forms.py:24
+msgid "A user with that username already exists."
+msgstr "Ein Benutzer mit diesem Namen existiert bereits."
+
#: contrib/auth/forms.py:52
msgid ""
"Your Web browser doesn't appear to have cookies enabled. Cookies are "
@@ -1432,10 +1436,10 @@ msgstr "Dieser Benutzer ist inaktiv."
#: contrib/auth/forms.py:84
msgid ""
-"That e-mail address doesn't have an associated user acount. Are you sure "
+"That e-mail address doesn't have an associated user account. Are you sure "
"you've registered?"
msgstr ""
-"Die Email-Adresse hat keinen Benutzer zugeordnet. Sicher, das die Adresse "
+"Die Email-Adresse hat keinen Benutzer zugeordnet. Sicher, dass die Adresse "
"hier angemeldet ist?"
#: contrib/auth/forms.py:116
@@ -1783,58 +1787,62 @@ msgid "Norwegian"
msgstr "Norwegisch"
#: conf/global_settings.py:59
+msgid "Polish"
+msgstr "Polnisch"
+
+#: conf/global_settings.py:60
msgid "Brazilian"
msgstr "Brasilianisch"
-#: conf/global_settings.py:60
+#: conf/global_settings.py:61
msgid "Romanian"
msgstr "Rumänisch"
-#: conf/global_settings.py:61
+#: conf/global_settings.py:62
msgid "Russian"
msgstr "Russisch"
-#: conf/global_settings.py:62
+#: conf/global_settings.py:63
msgid "Slovak"
msgstr "Slowakisch"
-#: conf/global_settings.py:63
+#: conf/global_settings.py:64
msgid "Slovenian"
msgstr "Slowenisch"
-#: conf/global_settings.py:64
+#: conf/global_settings.py:65
msgid "Serbian"
msgstr "Serbisch"
-#: conf/global_settings.py:65
+#: conf/global_settings.py:66
msgid "Swedish"
msgstr "Schwedisch"
-#: conf/global_settings.py:66
+#: conf/global_settings.py:67
msgid "Tamil"
msgstr "Tamilisch"
-#: conf/global_settings.py:67
+#: conf/global_settings.py:68
msgid "Turkish"
msgstr "Türkisch"
-#: conf/global_settings.py:68
+#: conf/global_settings.py:69
msgid "Ukrainian"
msgstr "Ukrainisch"
-#: conf/global_settings.py:69
+#: conf/global_settings.py:70
msgid "Simplified Chinese"
msgstr "Vereinfachtes Chinesisch"
-#: conf/global_settings.py:70
+#: conf/global_settings.py:71
msgid "Traditional Chinese"
msgstr "Traditionelles Chinesisch"
-#: core/validators.py:63
+#: core/validators.py:64
msgid "This value must contain only letters, numbers and underscores."
msgstr "Dieser Wert darf nur Buchstaben, Ziffern und Unterstriche enthalten."
-#: core/validators.py:67
+#: core/validators.py:68
msgid ""
"This value must contain only letters, numbers, underscores, dashes or "
"slashes."
@@ -1842,85 +1850,85 @@ msgstr ""
"Dieser Wert darf nur Buchstaben, Ziffern, Unterstriche und Schrägstriche "
"enthalten."
-#: core/validators.py:71
+#: core/validators.py:72
msgid "This value must contain only letters, numbers, underscores or hyphens."
msgstr ""
"Dieser Wert darf nur Buchstaben, Ziffern, Unterstriche und Bindestriche "
"enthalten."
-#: core/validators.py:75
+#: core/validators.py:76
msgid "Uppercase letters are not allowed here."
msgstr "Großbuchstaben sind hier nicht erlaubt."
-#: core/validators.py:79
+#: core/validators.py:80
msgid "Lowercase letters are not allowed here."
msgstr "Kleinbuchstaben sind hier nicht erlaubt."
-#: core/validators.py:86
+#: core/validators.py:87
msgid "Enter only digits separated by commas."
msgstr "Hier sind nur durch Komma getrennte Ziffern erlaubt."
-#: core/validators.py:98
+#: core/validators.py:99
msgid "Enter valid e-mail addresses separated by commas."
msgstr "Bitte mit Komma getrennte, gültige eMail-Adressen eingeben."
-#: core/validators.py:102
+#: core/validators.py:103
msgid "Please enter a valid IP address."
msgstr "Bitte eine gültige IP-Adresse eingeben."
-#: core/validators.py:106
+#: core/validators.py:107
msgid "Empty values are not allowed here."
msgstr "Dieses Feld darf nicht leer sein."
-#: core/validators.py:110
+#: core/validators.py:111
msgid "Non-numeric characters aren't allowed here."
msgstr "Nichtnumerische Zeichen sind hier nicht erlaubt."
-#: core/validators.py:114
+#: core/validators.py:115
msgid "This value can't be comprised solely of digits."
msgstr "Dieser Wert darf nicht nur aus Ziffern bestehen."
-#: core/validators.py:119
+#: core/validators.py:120
msgid "Enter a whole number."
msgstr "Bitte eine ganze Zahl eingeben."
-#: core/validators.py:123
+#: core/validators.py:124
msgid "Only alphabetical characters are allowed here."
msgstr "Nur alphabetische Zeichen sind hier erlaubt."
-#: core/validators.py:138
+#: core/validators.py:139
msgid "Year must be 1900 or later."
msgstr "Das Jahr muss 1900 oder später sein."
-#: core/validators.py:142
+#: core/validators.py:143
#, python-format
msgid "Invalid date: %s."
msgstr "Ungültiges Datum: %s"
-#: core/validators.py:146 db/models/fields/__init__.py:415
+#: core/validators.py:147 db/models/fields/__init__.py:424
msgid "Enter a valid date in YYYY-MM-DD format."
msgstr "Bitte ein gültiges Datum im Format JJJJ-MM-TT eingeben."
-#: core/validators.py:151
+#: core/validators.py:152
msgid "Enter a valid time in HH:MM format."
msgstr "Bitte eine gültige Zeit im Format SS:MM eingeben."
-#: core/validators.py:155 db/models/fields/__init__.py:477
+#: core/validators.py:156 db/models/fields/__init__.py:488
msgid "Enter a valid date/time in YYYY-MM-DD HH:MM format."
msgstr ""
"Bitte eine gültige Datums- und Zeitangabe im Format JJJJ-MM-TT SS:MM "
"eingeben."
-#: core/validators.py:160
+#: core/validators.py:161
msgid "Enter a valid e-mail address."
msgstr "Bitte eine gültige eMail-Adresse eingeben"
-#: core/validators.py:172 core/validators.py:401 forms/__init__.py:661
+#: core/validators.py:173 core/validators.py:442 forms/__init__.py:667
msgid "No file was submitted. Check the encoding type on the form."
msgstr ""
"Es wurde keine Datei geschickt. Eventuell ist das Formular-Encoding falsch."
-#: core/validators.py:176
+#: core/validators.py:177
msgid ""
"Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image."
@@ -1928,27 +1936,27 @@ msgstr ""
"Bitte ein Bild hochladen. Die Datei, die hochgeladen wurde, ist kein Bild "
"oder ist defekt."
-#: core/validators.py:183
+#: core/validators.py:184
#, python-format
msgid "The URL %s does not point to a valid image."
msgstr "Die URL %s zeigt nicht auf ein gültiges Bild."
-#: core/validators.py:187
+#: core/validators.py:188
#, python-format
msgid "Phone numbers must be in XXX-XXX-XXXX format. \"%s\" is invalid."
msgstr ""
"Telefonnummern müssen im Format XXX-XXX-XXXX sein. \"%s\" ist ungültig."
-#: core/validators.py:195
+#: core/validators.py:196
#, python-format
msgid "The URL %s does not point to a valid QuickTime video."
msgstr "Die URL %s zeigt nicht auf ein gültiges QuickTime video."
-#: core/validators.py:199
+#: core/validators.py:200
msgid "A valid URL is required."
msgstr "Eine gültige URL ist hier verlangt."
-#: core/validators.py:213
+#: core/validators.py:214
#, python-format
msgid ""
"Valid HTML is required. Specific errors are:\n"
@@ -1957,71 +1965,83 @@ msgstr ""
"Bitte gültiges HTML eingeben. Fehler sind:\n"
"%s"
-#: core/validators.py:220
+#: core/validators.py:221
#, python-format
msgid "Badly formed XML: %s"
msgstr "Ungültiges XML: %s"
-#: core/validators.py:230
+#: core/validators.py:238
#, python-format
msgid "Invalid URL: %s"
msgstr "Ungültige URL: %s"
-#: core/validators.py:234 core/validators.py:236
+#: core/validators.py:243 core/validators.py:245
#, python-format
msgid "The URL %s is a broken link."
msgstr "Die URL %s funktioniert nicht."
-#: core/validators.py:242
+#: core/validators.py:251
msgid "Enter a valid U.S. state abbreviation."
msgstr "Bitte eine gültige Abkürzung für einen US-Staat eingeben."
-#: core/validators.py:256
+#: core/validators.py:265
#, python-format
msgid "Watch your mouth! The word %s is not allowed here."
msgid_plural "Watch your mouth! The words %s are not allowed here."
msgstr[0] "Keine Schimpfworte! Das Wort %s ist hier nicht gern gesehen!"
msgstr[1] "Keine Schimpfworte! Die Wörter %s sind hier nicht gern gesehen!"
-#: core/validators.py:263
+#: core/validators.py:272
#, python-format
msgid "This field must match the '%s' field."
msgstr "Dieses Feld muss zum Feld '%s' passen."
-#: core/validators.py:282
+#: core/validators.py:291
msgid "Please enter something for at least one field."
msgstr "Bitte mindestens eines der Felder ausfüllen."
-#: core/validators.py:291 core/validators.py:302
+#: core/validators.py:300 core/validators.py:311
msgid "Please enter both fields or leave them both empty."
msgstr "Bitte entweder beide Felder ausfüllen, oder beide leer lassen."
-#: core/validators.py:309
+#: core/validators.py:318
#, python-format
msgid "This field must be given if %(field)s is %(value)s"
msgstr ""
"Dieses Feld muss gefüllt sein, wenn Feld %(field)s den Wert %(value)s hat."
-#: core/validators.py:321
+#: core/validators.py:330
#, python-format
msgid "This field must be given if %(field)s is not %(value)s"
msgstr ""
"Dieses Feld muss gefüllt sein, wenn Feld %(field)s nicht %(value)s ist."
-#: core/validators.py:340
+#: core/validators.py:349
msgid "Duplicate values are not allowed."
msgstr "Doppelte Werte sind hier nicht erlaubt."
-#: core/validators.py:363
+#: core/validators.py:364
+msgid "This value must be between %s and %s."
+msgstr "Dieser Wert muss zwischen %s und %s sein."
+
+#: core/validators.py:366
+msgid "This value must be at least %s."
+msgstr "Dieser Wert muss mindestens %s sein."
+
+#: core/validators.py:368
+msgid "This value must be no more than %s."
+msgstr "Dieser Wert darf maximal %s sein."
+
+#: core/validators.py:404
#, python-format
msgid "This value must be a power of %s."
msgstr "Dieser Wert muss eine Potenz von %s sein."
-#: core/validators.py:374
+#: core/validators.py:415
msgid "Please enter a valid decimal number."
msgstr "Bitte eine gültige Dezimalzahl eingeben."
-#: core/validators.py:378
+#: core/validators.py:419
#, python-format
msgid "Please enter a valid decimal number with at most %s total digit."
msgid_plural ""
@@ -2029,7 +2049,7 @@ msgid_plural ""
msgstr[0] "Bitte eine gültige Dezimalzahl mit maximal %s Ziffer eingeben."
msgstr[1] "Bitte eine gültige Dezimalzahl mit maximal %s Ziffern eingeben."
-#: core/validators.py:381
+#: core/validators.py:422
#, python-format
msgid ""
"Please enter a valid decimal number with a whole part of at most %s digit."
@@ -2038,7 +2058,7 @@ msgid_plural ""
msgstr[0] "Bitte eine gültige Dezimalzahl mit maximal %s Ziffer eingeben."
msgstr[1] "Bitte eine gültige Dezimalzahl mit maximal %s Ziffern eingeben."
-#: core/validators.py:384
+#: core/validators.py:425
#, python-format
msgid "Please enter a valid decimal number with at most %s decimal place."
msgid_plural ""
@@ -2048,39 +2068,39 @@ msgstr[0] ""
msgstr[1] ""
"Bitte eine gültige Dezimalzahl mit maximal %s Dezimalstellen eingeben."
-#: core/validators.py:394
+#: core/validators.py:435
#, python-format
msgid "Make sure your uploaded file is at least %s bytes big."
msgstr ""
"Bitte sicherstellen, daß die hochgeladene Datei mindestens %s Bytes gross "
"ist."
-#: core/validators.py:395
+#: core/validators.py:436
#, python-format
msgid "Make sure your uploaded file is at most %s bytes big."
msgstr ""
"Bitte sicherstellen, daß die hochgeladene Datei maximal %s Bytes gross ist."
-#: core/validators.py:412
+#: core/validators.py:453
msgid "The format for this field is wrong."
msgstr "Das Format für dieses Feld ist falsch."
-#: core/validators.py:427
+#: core/validators.py:468
msgid "This field is invalid."
msgstr "Dieses Feld ist ungültig."
-#: core/validators.py:463
+#: core/validators.py:504
#, python-format
msgid "Could not retrieve anything from %s."
msgstr "Konnte nichts von %s empfangen."
-#: core/validators.py:466
+#: core/validators.py:507
#, python-format
msgid ""
"The URL %(url)s returned the invalid Content-Type header '%(contenttype)s'."
msgstr "Die URL %(url)s lieferte den falschen Content-Type '%(contenttype)s'."
-#: core/validators.py:499
+#: core/validators.py:540
#, python-format
msgid ""
"Please close the unclosed %(tag)s tag from line %(line)s. (Line starts with "
@@ -2089,7 +2109,7 @@ msgstr ""
"Bitte das ungeschlossene %(tag)s Tag in Zeile %(line)s schließen. Die Zeile "
"beginnt mit \"%(start)s\"."
-#: core/validators.py:503
+#: core/validators.py:544
#, python-format
msgid ""
"Some text starting on line %(line)s is not allowed in that context. (Line "
@@ -2098,7 +2118,7 @@ msgstr ""
"In Zeile %(line)s ist Text, der nicht in dem Kontext erlaubt ist. Die Zeile "
"beginnt mit \"%(start)s\"."
-#: core/validators.py:508
+#: core/validators.py:549
#, python-format
msgid ""
"\"%(attr)s\" on line %(line)s is an invalid attribute. (Line starts with \"%"
@@ -2107,7 +2127,7 @@ msgstr ""
"Das Attribute %(attr)s in Zeile %(line)s ist ungültig. Die Zeile beginnt mit "
"\"%(start)s\"."
-#: core/validators.py:513
+#: core/validators.py:554
#, python-format
msgid ""
"\"<%(tag)s>\" on line %(line)s is an invalid tag. (Line starts with \"%"
@@ -2116,7 +2136,7 @@ msgstr ""
"<%(tag)s> in Zeile %(line)s ist ungültig. Die Zeile beginnt mit \"%(start)s"
"\"."
-#: core/validators.py:517
+#: core/validators.py:558
#, python-format
msgid ""
"A tag on line %(line)s is missing one or more required attributes. (Line "
@@ -2125,7 +2145,7 @@ msgstr ""
"Ein Tag in Zeile %(line)s hat eines oder mehrere Pflichtattribute nicht. Die "
"Zeile beginnt mit \"%(start)s\"."
-#: core/validators.py:522
+#: core/validators.py:563
#, python-format
msgid ""
"The \"%(attr)s\" attribute on line %(line)s has an invalid value. (Line "
@@ -2149,37 +2169,37 @@ msgstr "%(verbose_name)s wurde erfolgreich aktualisiert."
msgid "The %(verbose_name)s was deleted."
msgstr "%(verbose_name)s wurde gelöscht"
-#: db/models/manipulators.py:302
+#: db/models/manipulators.py:305
#, python-format
msgid "%(object)s with this %(type)s already exists for the given %(field)s."
msgstr ""
"Ein '%(object)s' in dieser '%(type)s' existiert bereits für dieses '%(field)"
"s'."
-#: db/models/fields/__init__.py:40
+#: db/models/fields/__init__.py:41
#, python-format
msgid "%(optname)s with this %(fieldname)s already exists."
msgstr "Ein '%(optname)s' mit diesem '%(fieldname)s' existiert bereits."
-#: db/models/fields/__init__.py:114 db/models/fields/__init__.py:265
-#: db/models/fields/__init__.py:551 db/models/fields/__init__.py:562
-#: forms/__init__.py:346
+#: db/models/fields/__init__.py:115 db/models/fields/__init__.py:266
+#: db/models/fields/__init__.py:569 db/models/fields/__init__.py:580
+#: forms/__init__.py:352
msgid "This field is required."
msgstr "Dieses Feld ist zwingend."
-#: db/models/fields/__init__.py:340
+#: db/models/fields/__init__.py:349
msgid "This value must be an integer."
msgstr "Dieser Wert muss eine Ganzzahl sein."
-#: db/models/fields/__init__.py:372
+#: db/models/fields/__init__.py:381
msgid "This value must be either True or False."
msgstr "Dieser Wert muss wahr oder falsch sein."
-#: db/models/fields/__init__.py:388
+#: db/models/fields/__init__.py:397
msgid "This field cannot be null."
msgstr "Dieses Feld darf nicht leer sein."
-#: db/models/fields/__init__.py:571
+#: db/models/fields/__init__.py:589
msgid "Enter a valid filename."
msgstr "Bitte einen gültigen Dateinamen eingeben"
@@ -2209,36 +2229,36 @@ msgstr[0] ""
msgstr[1] ""
"Bitte gültige IDs für %(self)s eingeben. Die Werte %(value)r sind ungültig."
-#: forms/__init__.py:381
+#: forms/__init__.py:387
#, python-format
msgid "Ensure your text is less than %s character."
msgid_plural "Ensure your text is less than %s characters."
-msgstr[0] "Bitte sicherstellen, das der Text weniger als %s Zeichen hat."
-msgstr[1] "Bitte sicherstellen, das der Text weniger als %s Zeichen hat."
+msgstr[0] "Bitte sicherstellen, dass der Text weniger als %s Zeichen hat."
+msgstr[1] "Bitte sicherstellen, dass der Text weniger als %s Zeichen hat."
-#: forms/__init__.py:386
+#: forms/__init__.py:392
msgid "Line breaks are not allowed here."
msgstr "Zeilenumbrüche sind hier nicht erlaubt."
-#: forms/__init__.py:487 forms/__init__.py:560 forms/__init__.py:599
+#: forms/__init__.py:493 forms/__init__.py:566 forms/__init__.py:605
#, python-format
msgid "Select a valid choice; '%(data)s' is not in %(choices)s."
msgstr ""
"Bitte eine gültige Auswahl treffen; '%(data)s' ist nicht in %(choices)s."
-#: forms/__init__.py:663
+#: forms/__init__.py:669
msgid "The submitted file is empty."
msgstr "Die ausgewählte Datei ist leer."
-#: forms/__init__.py:719
+#: forms/__init__.py:725
msgid "Enter a whole number between -32,768 and 32,767."
msgstr "Bitte eine ganze Zahl zwischen -32.768 und 32.767 eingeben."
-#: forms/__init__.py:729
+#: forms/__init__.py:735
msgid "Enter a positive number."
msgstr "Bitte eine ganze, positive Zahl eingeben."
-#: forms/__init__.py:739
+#: forms/__init__.py:745
msgid "Enter a whole number between 0 and 32,767."
msgstr "Bitte eine ganze Zahl zwischen 0 und 32.767 eingeben."
diff --git a/django/conf/locale/el/LC_MESSAGES/django.mo b/django/conf/locale/el/LC_MESSAGES/django.mo
index 1c95d6b5d9..4a7d8e41f0 100644
--- a/django/conf/locale/el/LC_MESSAGES/django.mo
+++ b/django/conf/locale/el/LC_MESSAGES/django.mo
Binary files differ
diff --git a/django/conf/locale/el/LC_MESSAGES/django.po b/django/conf/locale/el/LC_MESSAGES/django.po
index 173b300d04..06099eb9da 100644
--- a/django/conf/locale/el/LC_MESSAGES/django.po
+++ b/django/conf/locale/el/LC_MESSAGES/django.po
@@ -107,6 +107,11 @@ msgid ""
"\n"
"http://%(domain)s%(url)s"
msgstr ""
+"Σχόλιο από τον/την %(user)s την %(date)s\n"
+"\n"
+"%(comment)s\n"
+"\n"
+"http://%(domain)s%(url)s"
#: contrib/comments/models.py:168
msgid "person's name"
@@ -141,11 +146,11 @@ msgstr "ημεÏομηνία βαθμολογίας"
#: contrib/comments/models.py:237
#, fuzzy
msgid "karma score"
-msgstr "βαθμολογία"
+msgstr "karma"
#: contrib/comments/models.py:238
msgid "karma scores"
-msgstr ""
+msgstr "karma"
#: contrib/comments/models.py:242
#, python-format
@@ -159,7 +164,7 @@ msgid ""
"\n"
"%(text)s"
msgstr ""
-"Αυτο το σχόλιο σημειώθει απο %(χÏήστη)ες\n"
+"Αυτο το σχόλιο σημειώθηκε απο %(χÏήστη)ες\n"
"\n"
"%(κείμενο)α"
@@ -245,7 +250,7 @@ msgstr ""
#: contrib/comments/views/comments.py:193
#: contrib/comments/views/comments.py:284
msgid "One or more of the required fields wasn't submitted"
-msgstr "Ένα ή πεÏισσότεÏα από τα απαιτοÏμενα πεδία δεν υποβλίθει"
+msgstr "Ένα ή πεÏισσότεÏα από τα απαιτοÏμενα πεδία δεν υπεβλήθει"
#: contrib/comments/views/comments.py:197
#: contrib/comments/views/comments.py:286
@@ -268,7 +273,7 @@ msgstr ""
#: contrib/comments/templates/comments/form.html:8
#: contrib/admin/templates/admin/login.html:17
msgid "Username:"
-msgstr ""
+msgstr "Όνομα χÏήστη:"
#: contrib/comments/templates/comments/form.html:6
#: contrib/admin/templates/admin/login.html:20
@@ -298,21 +303,21 @@ msgstr "Ξεχάσατε τον κωδικό σας;"
#: contrib/admin/templates/admin_doc/index.html:4
#: contrib/admin/templates/admin_doc/model_index.html:5
msgid "Log out"
-msgstr ""
+msgstr "ΑποσÏνδεση"
#: contrib/comments/templates/comments/form.html:12
msgid "Ratings"
-msgstr ""
+msgstr "Βαθμολογίες"
#: contrib/comments/templates/comments/form.html:12
#: contrib/comments/templates/comments/form.html:23
msgid "Required"
-msgstr ""
+msgstr "ΑπαÏαίτητο"
#: contrib/comments/templates/comments/form.html:12
#: contrib/comments/templates/comments/form.html:23
msgid "Optional"
-msgstr ""
+msgstr "ΠÏοαιÏετικό"
#: contrib/comments/templates/comments/form.html:23
msgid "Post a photo"
@@ -326,11 +331,11 @@ msgstr "Σχόλιο:"
#: contrib/comments/templates/comments/form.html:32
#: contrib/comments/templates/comments/freeform.html:9
msgid "Preview comment"
-msgstr ""
+msgstr "ΠÏοεπισκόπηση σχολίου"
#: contrib/comments/templates/comments/freeform.html:4
msgid "Your name:"
-msgstr ""
+msgstr "Το όνομα σας:"
#: contrib/admin/filterspecs.py:40
#, python-format
@@ -338,15 +343,17 @@ msgid ""
"<h3>By %s:</h3>\n"
"<ul>\n"
msgstr ""
+"<h3>Από %s:</h3>\n"
+"<ul>\n"
#: contrib/admin/filterspecs.py:70 contrib/admin/filterspecs.py:88
#: contrib/admin/filterspecs.py:143
msgid "All"
-msgstr ""
+msgstr "Όλα"
#: contrib/admin/filterspecs.py:109
msgid "Any date"
-msgstr ""
+msgstr "Όλες οι ημεÏομηνίες"
#: contrib/admin/filterspecs.py:110
msgid "Today"
@@ -354,7 +361,7 @@ msgstr "ΣήμεÏα"
#: contrib/admin/filterspecs.py:113
msgid "Past 7 days"
-msgstr ""
+msgstr "Τις Ï€ÏοηγοÏμενες 7 ημέÏες"
#: contrib/admin/filterspecs.py:115
msgid "This month"
@@ -374,7 +381,7 @@ msgstr "Όχι"
#: contrib/admin/filterspecs.py:150
msgid "Unknown"
-msgstr "’γνωστο"
+msgstr "Άγνωστο"
#: contrib/admin/models.py:16
msgid "action time"
@@ -441,7 +448,7 @@ msgstr ""
#: contrib/admin/views/decorators.py:82
msgid "Usernames cannot contain the '@' character."
-msgstr "Τα ονόματα των ΧÏηστών δεν μποÏόυν να πεÏιέχουν τον χαÏακτήÏα '@'."
+msgstr "Τα ονόματα των χÏηστών δεν μποÏόυν να πεÏιέχουν τον χαÏακτήÏα '@'."
#: contrib/admin/views/decorators.py:84
#, python-format
@@ -452,16 +459,16 @@ msgstr ""
#: contrib/admin/views/main.py:226
msgid "Site administration"
-msgstr "ΔιαχείÏιση του Î”Î¹Î±Î´Ï…ÎºÏ„Î¹Î±ÎºÎ¿Ï Ï‡ÏŽÏου"
+msgstr "ΔιαχείÏιση του Î”Î¹Î±Î´Î¹ÎºÏ„Ï…Î±ÎºÎ¿Ï Ï‡ÏŽÏου"
#: contrib/admin/views/main.py:260
#, python-format
msgid "The %(name)s \"%(obj)s\" was added successfully."
-msgstr ""
+msgstr "Το %(name)s \"%(obj)s\" αποθηκεÏτηκε επιτυχώς."
#: contrib/admin/views/main.py:264 contrib/admin/views/main.py:348
msgid "You may edit it again below."
-msgstr ""
+msgstr "ΜποÏείτε να το επεξεÏγαστείτε ξανα παÏακάτω."
#: contrib/admin/views/main.py:272 contrib/admin/views/main.py:357
#, python-format
@@ -471,12 +478,12 @@ msgstr "ΜποÏείτε να Ï€Ïοσθέσετε ακόμα ένα %s απο Î
#: contrib/admin/views/main.py:290
#, python-format
msgid "Add %s"
-msgstr ""
+msgstr "ΠÏοσθήκη %s"
#: contrib/admin/views/main.py:336
#, python-format
msgid "Added %s."
-msgstr ""
+msgstr "ΠÏοστέθηκε %s."
#: contrib/admin/views/main.py:336 contrib/admin/views/main.py:338
#: contrib/admin/views/main.py:340
@@ -486,32 +493,33 @@ msgstr "και"
#: contrib/admin/views/main.py:338
#, python-format
msgid "Changed %s."
-msgstr ""
+msgstr "ΕπεξεÏγάσθηκε %s."
#: contrib/admin/views/main.py:340
#, python-format
msgid "Deleted %s."
-msgstr ""
+msgstr "ΔιεγÏάφη %s."
#: contrib/admin/views/main.py:343
msgid "No fields changed."
-msgstr ""
+msgstr "Κανένα πεδίο δεν άλλαξε."
#: contrib/admin/views/main.py:346
#, python-format
msgid "The %(name)s \"%(obj)s\" was changed successfully."
-msgstr ""
+msgstr "Το %(name)s \"%(obj)s\" επεξεÏγάσθηκε επιτυχώς."
#: contrib/admin/views/main.py:354
#, python-format
msgid ""
"The %(name)s \"%(obj)s\" was added successfully. You may edit it again below."
msgstr ""
+"Το %(name)s \"%(obj)s\" αποθηκεÏθηκε επιτυχώς. ΜποÏείτε να το επεξεÏγαστείτε πάλι παÏακάτω."
#: contrib/admin/views/main.py:392
#, python-format
msgid "Change %s"
-msgstr ""
+msgstr "Αλλαγή %s"
#: contrib/admin/views/main.py:470
#, python-format
@@ -535,17 +543,17 @@ msgstr "Είστε σίγουÏος;"
#: contrib/admin/views/main.py:533
#, python-format
msgid "Change history: %s"
-msgstr ""
+msgstr "ΙστοÏικό Αλλαγών: %s"
#: contrib/admin/views/main.py:565
#, python-format
msgid "Select %s"
-msgstr ""
+msgstr "Επιλογή %s"
#: contrib/admin/views/main.py:565
#, python-format
msgid "Select %s to change"
-msgstr ""
+msgstr "Επιλέξτε %s Ï€Ïος αλλαγή"
#: contrib/admin/views/doc.py:277 contrib/admin/views/doc.py:286
#: contrib/admin/views/doc.py:288 contrib/admin/views/doc.py:294
@@ -555,7 +563,7 @@ msgstr "ΑκέÏαιος"
#: contrib/admin/views/doc.py:278
msgid "Boolean (Either True or False)"
-msgstr "Boolean (Είτε Αλήθεια ή Ψέμα)"
+msgstr "Boolean (Είτε Αληθές ή Ψέυδές)"
#: contrib/admin/views/doc.py:279 contrib/admin/views/doc.py:296
#, python-format
@@ -580,7 +588,7 @@ msgstr "ΗλεκτÏονική διεÏθυνση"
#: contrib/admin/views/doc.py:284 contrib/admin/views/doc.py:287
msgid "File path"
-msgstr ""
+msgstr "Τοποθεσία ΑÏχείου"
#: contrib/admin/views/doc.py:285
msgid "Decimal number"
@@ -592,7 +600,7 @@ msgstr ""
#: contrib/admin/views/doc.py:292
msgid "Relation to parent model"
-msgstr ""
+msgstr "Σχέση με το γονεϊκό μοντέλο"
#: contrib/admin/views/doc.py:293
msgid "Phone number"
@@ -627,7 +635,7 @@ msgstr ""
#: contrib/admin/templates/registration/password_change_form.html:3
#: contrib/admin/templates/admin_doc/bookmarklets.html:3
msgid "Documentation"
-msgstr ""
+msgstr "ΤεκμηÏίωση"
#: contrib/admin/templates/admin/object_history.html:3
#: contrib/admin/templates/admin/change_list.html:5
@@ -667,11 +675,11 @@ msgstr "Home"
#: contrib/admin/templates/admin/object_history.html:5
#: contrib/admin/templates/admin/change_form.html:20
msgid "History"
-msgstr ""
+msgstr "ΙστοÏικό"
#: contrib/admin/templates/admin/object_history.html:18
msgid "Date/time"
-msgstr ""
+msgstr "ΗμεÏομηνία/ÎÏα"
#: contrib/admin/templates/admin/object_history.html:19
msgid "User"
@@ -679,7 +687,7 @@ msgstr "ΧÏήστης"
#: contrib/admin/templates/admin/object_history.html:20
msgid "Action"
-msgstr ""
+msgstr "ΔÏάση"
#: contrib/admin/templates/admin/object_history.html:26
msgid "DATE_WITH_TIME_FULL"
@@ -697,19 +705,19 @@ msgstr "ΔιαχειÏιστής ιστοσελίδας Django"
#: contrib/admin/templates/admin/base_site.html:7
msgid "Django administration"
-msgstr ""
+msgstr "ΔιαχείÏιση Django"
#: contrib/admin/templates/admin/500.html:4
msgid "Server error"
-msgstr ""
+msgstr "Σφάλμα Διακομιστή"
#: contrib/admin/templates/admin/500.html:6
msgid "Server error (500)"
-msgstr ""
+msgstr "Σφάλμα Διακομιστή (500)"
#: contrib/admin/templates/admin/500.html:9
msgid "Server Error <em>(500)</em>"
-msgstr ""
+msgstr "Σφάλμα Διακομιστή <em>(500)</em>"
#: contrib/admin/templates/admin/500.html:10
msgid ""
@@ -720,7 +728,7 @@ msgstr ""
#: contrib/admin/templates/admin/404.html:4
#: contrib/admin/templates/admin/404.html:8
msgid "Page not found"
-msgstr ""
+msgstr "Η σελίδα δε βÏέθηκε."
#: contrib/admin/templates/admin/404.html:10
msgid "We're sorry, but the requested page could not be found."
@@ -729,24 +737,24 @@ msgstr ""
#: contrib/admin/templates/admin/index.html:17
#, python-format
msgid "Models available in the %(name)s application."
-msgstr ""
+msgstr "Διαθέσιμα μοντέλα στην εφαÏμογή %(name)s."
#: contrib/admin/templates/admin/index.html:28
#: contrib/admin/templates/admin/change_form.html:15
msgid "Add"
-msgstr ""
+msgstr "ΠÏοσθήκη"
#: contrib/admin/templates/admin/index.html:34
msgid "Change"
-msgstr ""
+msgstr "ΕπεξεÏγασία"
#: contrib/admin/templates/admin/index.html:44
msgid "You don't have permission to edit anything."
-msgstr ""
+msgstr "Δεν έχετε άδεια να επεξεÏγαστείτε τίποτα."
#: contrib/admin/templates/admin/index.html:52
msgid "Recent Actions"
-msgstr ""
+msgstr "ΠÏόσφατες ΠÏάξεις"
#: contrib/admin/templates/admin/index.html:53
msgid "My Actions"
@@ -754,16 +762,16 @@ msgstr "Οι Ï€Ïάξεις μου"
#: contrib/admin/templates/admin/index.html:57
msgid "None available"
-msgstr ""
+msgstr "Κανένα διαθέσιμο"
#: contrib/admin/templates/admin/change_list.html:11
#, python-format
msgid "Add %(name)s"
-msgstr ""
+msgstr "ΠÏοσθήκη %(name)s"
#: contrib/admin/templates/admin/login.html:22
msgid "Have you <a href=\"/password_reset/\">forgotten your password</a>?"
-msgstr ""
+msgstr "<a href=\"/password_reset/\">Ξεχάσατε τον κωδικό σας;</a> "
#: contrib/admin/templates/admin/base.html:23
msgid "Welcome,"
@@ -772,7 +780,7 @@ msgstr "ΚαλωσήÏθατε,"
#: contrib/admin/templates/admin/delete_confirmation.html:9
#: contrib/admin/templates/admin/submit_line.html:3
msgid "Delete"
-msgstr ""
+msgstr "ΔιαγÏαφή"
#: contrib/admin/templates/admin/delete_confirmation.html:14
#, python-format
@@ -800,37 +808,37 @@ msgstr ""
#: contrib/admin/templates/admin/search_form.html:8
msgid "Go"
-msgstr ""
+msgstr "Πήγαινε"
#: contrib/admin/templates/admin/change_form.html:21
msgid "View on site"
-msgstr ""
+msgstr "ΠÏοβολή στην ιστοσελίδα"
#: contrib/admin/templates/admin/change_form.html:30
msgid "Please correct the error below."
msgid_plural "Please correct the errors below."
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "ΠαÏακαλώ διοÏθώστε το παÏακάτω λάθος."
+msgstr[1] "ΠαÏακαλώ διοÏθώστε τα παÏακάτω λάθη."
#: contrib/admin/templates/admin/change_form.html:48
msgid "Ordering"
-msgstr ""
+msgstr "ΣειÏά"
#: contrib/admin/templates/admin/change_form.html:51
msgid "Order:"
-msgstr ""
+msgstr "ΣειÏά:"
#: contrib/admin/templates/admin/submit_line.html:4
msgid "Save as new"
-msgstr ""
+msgstr "Αποθήκευση καινοÏÏιου"
#: contrib/admin/templates/admin/submit_line.html:5
msgid "Save and add another"
-msgstr ""
+msgstr "Αποθήκευση και Ï€Ïοσθήκη καινοÏÏιου."
#: contrib/admin/templates/admin/submit_line.html:6
msgid "Save and continue editing"
-msgstr ""
+msgstr "Αποθήκευση και συνέχεια επεξεÏγασίας"
#: contrib/admin/templates/admin/submit_line.html:7
msgid "Save"
@@ -841,12 +849,12 @@ msgstr "Αποθήκευση"
#: contrib/admin/templates/registration/password_change_form.html:6
#: contrib/admin/templates/registration/password_change_form.html:10
msgid "Password change"
-msgstr ""
+msgstr "Αλλαγή ΚωδικοÏ"
#: contrib/admin/templates/registration/password_change_done.html:6
#: contrib/admin/templates/registration/password_change_done.html:10
msgid "Password change successful"
-msgstr ""
+msgstr "Αλλαγή ÎºÏ‰Î´Î¹ÎºÎ¿Ï ÎµÏ€Î¹Ï„Ï…Ï‡Î®Ï‚"
#: contrib/admin/templates/registration/password_change_done.html:12
msgid "Your password was changed."
@@ -857,7 +865,7 @@ msgstr "Ο κωδίκός σας άλλαξε."
#: contrib/admin/templates/registration/password_reset_form.html:10
#: contrib/admin/templates/registration/password_reset_done.html:4
msgid "Password reset"
-msgstr ""
+msgstr "ΕπαναφοÏά κωδικοÏ"
#: contrib/admin/templates/registration/password_reset_form.html:12
msgid ""
@@ -867,11 +875,11 @@ msgstr ""
#: contrib/admin/templates/registration/password_reset_form.html:16
msgid "E-mail address:"
-msgstr ""
+msgstr "E-mail διεÏθυνση:"
#: contrib/admin/templates/registration/password_reset_form.html:16
msgid "Reset my password"
-msgstr ""
+msgstr "ΕπαναφοÏά του ÎºÏ‰Î´Î¹ÎºÎ¿Ï Î¼Î¿Ï…"
#: contrib/admin/templates/registration/logged_out.html:8
msgid "Thanks for spending some quality time with the Web site today."
@@ -880,7 +888,7 @@ msgstr ""
#: contrib/admin/templates/registration/logged_out.html:10
msgid "Log in again"
-msgstr ""
+msgstr "Εισαγωγή ξανά"
#: contrib/admin/templates/registration/password_reset_done.html:6
#: contrib/admin/templates/registration/password_reset_done.html:10
@@ -909,11 +917,11 @@ msgstr "Îέος κωδικός:"
#: contrib/admin/templates/registration/password_change_form.html:21
msgid "Confirm password:"
-msgstr ""
+msgstr "Επιβεβαίωση κωδικοÏ"
#: contrib/admin/templates/registration/password_change_form.html:23
msgid "Change my password"
-msgstr ""
+msgstr "Αλλαγή του ÎºÏ‰Î´Î¹ÎºÎ¿Ï Î¼Î¿Ï…"
#: contrib/admin/templates/registration/password_reset_email.html:2
msgid "You're receiving this e-mail because you requested a password reset"
@@ -1003,19 +1011,19 @@ msgstr ""
#: contrib/admin/templates/widget/date_time.html:3
msgid "Date:"
-msgstr ""
+msgstr "Ημ/νία:"
#: contrib/admin/templates/widget/date_time.html:4
msgid "Time:"
-msgstr ""
+msgstr "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ÎÏα:"
#: contrib/admin/templates/widget/file.html:2
msgid "Currently:"
-msgstr ""
+msgstr "ΤÏέχον:"
#: contrib/admin/templates/widget/file.html:3
msgid "Change:"
-msgstr ""
+msgstr "Αλλαγή:"
#: contrib/redirects/models.py:7
msgid "redirect from"
@@ -1271,192 +1279,192 @@ msgstr "P"
#: utils/dates.py:6
msgid "Monday"
-msgstr ""
+msgstr "ΔευτέÏα"
#: utils/dates.py:6
msgid "Tuesday"
-msgstr ""
+msgstr "ΤÏίτη"
#: utils/dates.py:6
msgid "Wednesday"
-msgstr ""
+msgstr "ΤετάÏτη"
#: utils/dates.py:6
msgid "Thursday"
-msgstr ""
+msgstr "Πέμπτη"
#: utils/dates.py:6
msgid "Friday"
-msgstr ""
+msgstr "ΠαÏασκευή"
#: utils/dates.py:7
msgid "Saturday"
-msgstr ""
+msgstr "Σάββατο"
#: utils/dates.py:7
msgid "Sunday"
-msgstr ""
+msgstr "ΚυÏιακή"
#: utils/dates.py:14
msgid "January"
-msgstr ""
+msgstr "ΙανουάÏιος"
#: utils/dates.py:14
msgid "February"
-msgstr ""
+msgstr "ΦεβÏουάÏιος"
#: utils/dates.py:14 utils/dates.py:27
msgid "March"
-msgstr ""
+msgstr "ΜάÏτιος"
#: utils/dates.py:14 utils/dates.py:27
msgid "April"
-msgstr ""
+msgstr "ΑπÏίλιος"
#: utils/dates.py:14 utils/dates.py:27
msgid "May"
-msgstr ""
+msgstr "Μάιος"
#: utils/dates.py:14 utils/dates.py:27
msgid "June"
-msgstr ""
+msgstr "ΙοÏνιος"
#: utils/dates.py:15 utils/dates.py:27
msgid "July"
-msgstr ""
+msgstr "ΙοÏλιος"
#: utils/dates.py:15
msgid "August"
-msgstr ""
+msgstr "ΑÏγουστος"
#: utils/dates.py:15
msgid "September"
-msgstr ""
+msgstr "ΣεπτέμβÏιος"
#: utils/dates.py:15
msgid "October"
-msgstr ""
+msgstr "ΟκτώβÏιος"
#: utils/dates.py:15
msgid "November"
-msgstr ""
+msgstr "ÎοέμβÏιος"
#: utils/dates.py:16
msgid "December"
-msgstr ""
+msgstr "ΔεκέμβÏιος"
#: utils/dates.py:19
#, fuzzy
msgid "jan"
-msgstr "και"
+msgstr "Ιαν"
#: utils/dates.py:19
msgid "feb"
-msgstr ""
+msgstr "Φεβ"
#: utils/dates.py:19
msgid "mar"
-msgstr ""
+msgstr "ΜάÏ"
#: utils/dates.py:19
msgid "apr"
-msgstr ""
+msgstr "ΑπÏ"
#: utils/dates.py:19
msgid "may"
-msgstr ""
+msgstr "Μάι"
#: utils/dates.py:19
msgid "jun"
-msgstr ""
+msgstr "ΙοÏν"
#: utils/dates.py:20
msgid "jul"
-msgstr ""
+msgstr "ΙοÏλ"
#: utils/dates.py:20
msgid "aug"
-msgstr ""
+msgstr "ΑÏγ"
#: utils/dates.py:20
msgid "sep"
-msgstr ""
+msgstr "Σεπ"
#: utils/dates.py:20
msgid "oct"
-msgstr ""
+msgstr "Οκτ"
#: utils/dates.py:20
msgid "nov"
-msgstr ""
+msgstr "Îοέ"
#: utils/dates.py:20
msgid "dec"
-msgstr ""
+msgstr "Δεκ"
#: utils/dates.py:27
msgid "Jan."
-msgstr ""
+msgstr "Ιάν."
#: utils/dates.py:27
msgid "Feb."
-msgstr ""
+msgstr "Φεβ."
#: utils/dates.py:28
msgid "Aug."
-msgstr ""
+msgstr "ΑÏγ."
#: utils/dates.py:28
msgid "Sept."
-msgstr ""
+msgstr "Σεπτ."
#: utils/dates.py:28
msgid "Oct."
-msgstr ""
+msgstr "Οκτ."
#: utils/dates.py:28
msgid "Nov."
-msgstr ""
+msgstr "Îοέ."
#: utils/dates.py:28
msgid "Dec."
-msgstr ""
+msgstr "Δεκ."
#: utils/timesince.py:12
msgid "year"
msgid_plural "years"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "χÏόνος"
+msgstr[1] "χÏόνια"
#: utils/timesince.py:13
msgid "month"
msgid_plural "months"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "μήνας"
+msgstr[1] "μήνες"
#: utils/timesince.py:14
msgid "week"
msgid_plural "weeks"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "εβδομάδα"
+msgstr[1] "εβδομάδες"
#: utils/timesince.py:15
msgid "day"
msgid_plural "days"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "ημέÏα"
+msgstr[1] "ημέÏες"
#: utils/timesince.py:16
msgid "hour"
msgid_plural "hours"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "ÏŽÏα"
+msgstr[1] "ÏŽÏες"
#: utils/timesince.py:17
msgid "minute"
msgid_plural "minutes"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "λεπτό"
+msgstr[1] "λεπτά"
#: conf/global_settings.py:37
msgid "Bengali"
@@ -1630,7 +1638,7 @@ msgstr ""
#: core/validators.py:136
msgid "Enter a valid e-mail address."
-msgstr ""
+msgstr "Εισάγετε ένα σωστό e-mail."
#: core/validators.py:148
msgid ""
@@ -1745,12 +1753,12 @@ msgstr[1] ""
#: core/validators.py:362
#, python-format
msgid "Make sure your uploaded file is at least %s bytes big."
-msgstr "ΣιγουÏευτείτε ότι το αÏχείου που ανεβάζετε είναι %s bytes τουλάχιστον."
+msgstr "ΣιγουÏευτείτε ότι το αÏχείο που ανεβάζετε είναι %s bytes τουλάχιστον."
#: core/validators.py:363
#, python-format
msgid "Make sure your uploaded file is at most %s bytes big."
-msgstr ""
+msgstr "ΣιγουÏευτείτε ότι το αÏχείο που ανεβάζετε έχει μέγεθος μέχÏι %s bytes."
#: core/validators.py:376
msgid "The format for this field is wrong."
@@ -1827,7 +1835,7 @@ msgstr ""
#: db/models/fields/__init__.py:542 db/models/fields/__init__.py:553
#: forms/__init__.py:346
msgid "This field is required."
-msgstr ""
+msgstr "Αυτό το πεδίο είναι απαÏαίτητο"
#: db/models/fields/__init__.py:337
msgid "This value must be an integer."
@@ -1836,12 +1844,12 @@ msgstr ""
#: db/models/fields/__init__.py:369
#, fuzzy
msgid "This value must be either True or False."
-msgstr "Boolean (Είτε Αλήθεια ή Ψέμα)"
+msgstr "Boolean (Είτε Αληθές ή Ψευδές)"
#: db/models/fields/__init__.py:385
#, fuzzy
msgid "This field cannot be null."
-msgstr "Αυτό το πεδίο είναι άκυÏο"
+msgstr "Αυτό το πεδίο δεν μποÏεί να είναι κενό (null)"
#: db/models/fields/__init__.py:562
msgid "Enter a valid filename."
@@ -1850,7 +1858,7 @@ msgstr "Εισάγετε ένα έγκυÏο όνομα αÏχείου"
#: db/models/fields/related.py:43
#, python-format
msgid "Please enter a valid %s."
-msgstr ""
+msgstr "ΠαÏακαλώ εισάγετε ένα/μία έγκυÏο/η %s"
#: db/models/fields/related.py:579
#, fuzzy
diff --git a/django/conf/locale/el/LC_MESSAGES/djangojs.mo b/django/conf/locale/el/LC_MESSAGES/djangojs.mo
new file mode 100644
index 0000000000..7d43b315fe
--- /dev/null
+++ b/django/conf/locale/el/LC_MESSAGES/djangojs.mo
Binary files differ
diff --git a/django/conf/locale/el/LC_MESSAGES/djangojs.po b/django/conf/locale/el/LC_MESSAGES/djangojs.po
new file mode 100644
index 0000000000..545f9f8601
--- /dev/null
+++ b/django/conf/locale/el/LC_MESSAGES/djangojs.po
@@ -0,0 +1,109 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) 2006 and beyond.
+# This file is distributed under the same license as the Django package.
+# Orestis Markou <orestis@orestis.gr>, 2006.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2005-12-09 11:51+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Orestis Markou <orestis@orestis.gr>\n"
+"Language-Team: Greek\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: contrib/admin/media/js/SelectFilter2.js:33
+#, perl-format
+msgid "Available %s"
+msgstr "Διαθέσιμο %s"
+
+#: contrib/admin/media/js/SelectFilter2.js:41
+msgid "Choose all"
+msgstr "Επιλογή Όλων"
+
+#: contrib/admin/media/js/SelectFilter2.js:46
+msgid "Add"
+msgstr "ΠÏοσθήκη"
+
+#: contrib/admin/media/js/SelectFilter2.js:48
+msgid "Remove"
+msgstr "ΑφαίÏεση"
+
+#: contrib/admin/media/js/SelectFilter2.js:53
+#, perl-format
+msgid "Chosen %s"
+msgstr "Επιλεχθέντα %s"
+
+#: contrib/admin/media/js/SelectFilter2.js:54
+msgid "Select your choice(s) and click "
+msgstr "Επιλέξτε και κάντε κλικ."
+
+#: contrib/admin/media/js/SelectFilter2.js:59
+msgid "Clear all"
+msgstr "ΚαθαÏισμός όλων"
+
+#: contrib/admin/media/js/dateparse.js:26
+#: contrib/admin/media/js/calendar.js:24
+msgid ""
+"January February March April May June July August September October November "
+"December"
+msgstr "ΙανουάÏιος ΦεβÏουάÏιος ΜάÏτιος ΑπÏίλιος Μάιος ΙοÏνιος ΙοÏλιος ΑÏγουστος ΣεπτέμβÏιος ΟκτώβÏιος ÎοέμβÏιος "
+"ΔεκέμβÏιος"
+
+#: contrib/admin/media/js/dateparse.js:27
+msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday"
+msgstr "ΚυÏιακή ΔευτέÏα ΤÏίτη ΤετάÏτη Πέμπτη ΠαÏασκευή Σάββατο"
+
+#: contrib/admin/media/js/calendar.js:25
+msgid "S M T W T F S"
+msgstr "Κ Δ Τ Τ Π Π Σ"
+
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:45
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:80
+msgid "Now"
+msgstr "ΤώÏα"
+
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:48
+msgid "Clock"
+msgstr "Ρολόι"
+
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:77
+msgid "Choose a time"
+msgstr "Διαλέξτε ÏŽÏα"
+
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:81
+msgid "Midnight"
+msgstr "Μεσάνυχτα"
+
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:82
+msgid "6 a.m."
+msgstr "6 π.μ."
+
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:83
+msgid "Noon"
+msgstr "ΜεσημέÏι"
+
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:87
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:168
+msgid "Cancel"
+msgstr "ΆκυÏο"
+
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:111
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:162
+msgid "Today"
+msgstr "ΣήμεÏα"
+
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:114
+msgid "Calendar"
+msgstr "ΗμεÏολόγιο"
+
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:160
+msgid "Yesterday"
+msgstr "Χθες"
+
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:164
+msgid "Tomorrow"
+msgstr "ΑÏÏιο"
diff --git a/django/conf/locale/es_AR/LC_MESSAGES/django.mo b/django/conf/locale/es_AR/LC_MESSAGES/django.mo
index dd96bf99ce..a247bb2385 100644
--- a/django/conf/locale/es_AR/LC_MESSAGES/django.mo
+++ b/django/conf/locale/es_AR/LC_MESSAGES/django.mo
Binary files differ
diff --git a/django/conf/locale/es_AR/LC_MESSAGES/django.po b/django/conf/locale/es_AR/LC_MESSAGES/django.po
index e5169e9c4a..36cae2f7f2 100644
--- a/django/conf/locale/es_AR/LC_MESSAGES/django.po
+++ b/django/conf/locale/es_AR/LC_MESSAGES/django.po
@@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: django\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2006-08-18 18:57-0300\n"
-"PO-Revision-Date: 2006-08-21 18:06-0300\n"
+"POT-Creation-Date: 2006-11-05 19:57-0300\n"
+"PO-Revision-Date: 2006-11-05 20:00-0300\n"
"Last-Translator: Ramiro Morales <rm0@gmx.net>\n"
"Language-Team: Spanish <es@li.org>\n"
"MIME-Version: 1.0\n"
@@ -165,10 +165,18 @@ msgstr "Fechas importantes"
msgid "Groups"
msgstr "Grupos"
-#: contrib/auth/models.py:256
+#: contrib/auth/models.py:258
msgid "message"
msgstr "mensaje"
+#: contrib/auth/forms.py:16
+msgid "The two password fields didn't match."
+msgstr "Los dos campos de contraseñas no coinciden entre si."
+
+#: contrib/auth/forms.py:24
+msgid "A user with that username already exists."
+msgstr "Ya existe un usuario con ese nombre."
+
#: contrib/auth/forms.py:52
msgid ""
"Your Web browser doesn't appear to have cookies enabled. Cookies are "
@@ -189,6 +197,24 @@ msgstr ""
msgid "This account is inactive."
msgstr "Esta cuenta está inactiva"
+#: contrib/auth/forms.py:84
+msgid ""
+"That e-mail address doesn't have an associated user account. Are you sure "
+"you've registered?"
+msgstr ""
+"Esa dirección de e-mail no está asociada a ninguna cuenta de usuario. ¿Está "
+"seguro de que ya se ha registrado?"
+
+#: contrib/auth/forms.py:116
+msgid "The two 'new password' fields didn't match."
+msgstr "Los dos campos 'nueva contraseña' no coinciden entre si."
+
+#: contrib/auth/forms.py:123
+msgid "Your old password was entered incorrectly. Please enter it again."
+msgstr ""
+"La antigua contraseña ingresada es incorrecta. Por favor ingrésela "
+"nuevamente."
+
#: contrib/redirects/models.py:7
msgid "redirect from"
msgstr "redirigir desde"
@@ -754,13 +780,13 @@ msgstr ""
msgid "Site administration"
msgstr "Sitio administrativo"
-#: contrib/admin/views/main.py:257 contrib/admin/views/auth.py:14
+#: contrib/admin/views/main.py:257 contrib/admin/views/auth.py:18
#, python-format
msgid "The %(name)s \"%(obj)s\" was added successfully."
-msgstr "Se agregó con éxito el %(name)s \"%(obj)s\"."
+msgstr "Se agregó con éxito %(name)s \"%(obj)s\"."
#: contrib/admin/views/main.py:261 contrib/admin/views/main.py:347
-#: contrib/admin/views/auth.py:19
+#: contrib/admin/views/auth.py:23
msgid "You may edit it again below."
msgstr "Puede modificarlo nuevamente abajo."
@@ -780,7 +806,7 @@ msgid "Added %s."
msgstr "Agregado %s."
#: contrib/admin/views/main.py:335 contrib/admin/views/main.py:337
-#: contrib/admin/views/main.py:339
+#: contrib/admin/views/main.py:339 db/models/manipulators.py:306
msgid "and"
msgstr "y"
@@ -801,15 +827,14 @@ msgstr "No ha modificado ningún campo."
#: contrib/admin/views/main.py:345
#, python-format
msgid "The %(name)s \"%(obj)s\" was changed successfully."
-msgstr "Se modificó con éxito el %(name)s \"%(obj)s."
+msgstr "Se modificó con éxito %(name)s \"%(obj)s."
#: contrib/admin/views/main.py:353
#, python-format
msgid ""
"The %(name)s \"%(obj)s\" was added successfully. You may edit it again below."
msgstr ""
-"Se agregó con éxito el %(name)s \"%(obj)s. Puede modificarlo nuevamente "
-"abajo."
+"Se agregó con éxito %(name)s \"%(obj)s. Puede modificarlo nuevamente abajo."
#: contrib/admin/views/main.py:391
#, python-format
@@ -829,7 +854,7 @@ msgstr "Uno o más %(fieldname)s en %(name)s:"
#: contrib/admin/views/main.py:511
#, python-format
msgid "The %(name)s \"%(obj)s\" was deleted successfully."
-msgstr "Se eliminó con éxito el %(name)s \"%(obj)s\"."
+msgstr "Se eliminó con éxito %(name)s \"%(obj)s\"."
#: contrib/admin/views/main.py:514
msgid "Are you sure?"
@@ -850,7 +875,7 @@ msgstr "Seleccione %s"
msgid "Select %s to change"
msgstr "Seleccione %s a modificar"
-#: contrib/admin/views/main.py:756
+#: contrib/admin/views/main.py:758
msgid "Database error"
msgstr "Error de base de datos"
@@ -977,12 +1002,12 @@ msgstr "Estado de los EEUU (dos letras mayúsculas)"
msgid "XML text"
msgstr "Texto XML"
-#: contrib/admin/views/doc.py:339
+#: contrib/admin/views/doc.py:343
#, python-format
msgid "%s does not appear to be a urlpattern object"
msgstr "%s no parece ser un objeto urlpattern"
-#: contrib/admin/views/auth.py:25
+#: contrib/admin/views/auth.py:29
msgid "Add user"
msgstr "Agregar usuario"
@@ -1181,10 +1206,6 @@ msgstr "Página no encontrada"
msgid "We're sorry, but the requested page could not be found."
msgstr "Lo sentimos, pero no se encuentra la página solicitada."
-#: contrib/admin/templates/admin/login.html:22
-msgid "Have you <a href=\"/password_reset/\">forgotten your password</a>?"
-msgstr "¿Ha <a href=\"/password_reset/\">olvidado su contraseña</a>?"
-
#: contrib/admin/templates/admin/filters.html:4
msgid "Filter"
msgstr "Filtrar"
@@ -1726,123 +1747,134 @@ msgid "Argentinean Spanish"
msgstr "Español Argentino"
#: conf/global_settings.py:49
+msgid "Finnish"
+msgstr "Finlandés"
+
+#: conf/global_settings.py:50
msgid "French"
msgstr "Francés"
-#: conf/global_settings.py:50
+#: conf/global_settings.py:51
msgid "Galician"
msgstr "Gallego"
-#: conf/global_settings.py:51
+#: conf/global_settings.py:52
msgid "Hungarian"
msgstr "Húngaro"
-#: conf/global_settings.py:52
+#: conf/global_settings.py:53
msgid "Hebrew"
msgstr "Hebreo"
-#: conf/global_settings.py:53
+#: conf/global_settings.py:54
msgid "Icelandic"
msgstr "Islandés"
-#: conf/global_settings.py:54
+#: conf/global_settings.py:55
msgid "Italian"
msgstr "Italiano"
-#: conf/global_settings.py:55
+#: conf/global_settings.py:56
msgid "Japanese"
msgstr "Japonés"
-#: conf/global_settings.py:56
+#: conf/global_settings.py:57
msgid "Dutch"
msgstr "Holandés"
-#: conf/global_settings.py:57
+#: conf/global_settings.py:58
msgid "Norwegian"
msgstr "Noruego"
-#: conf/global_settings.py:58
+#: conf/global_settings.py:59
+msgid "Polish"
+msgstr "Polaco"
+
+#: conf/global_settings.py:60
msgid "Brazilian"
msgstr "Brasileño"
-#: conf/global_settings.py:59
+#: conf/global_settings.py:61
msgid "Romanian"
msgstr "Rumano"
-#: conf/global_settings.py:60
+#: conf/global_settings.py:62
msgid "Russian"
msgstr "Ruso"
-#: conf/global_settings.py:61
+#: conf/global_settings.py:63
msgid "Slovak"
msgstr "Eslovaco"
-#: conf/global_settings.py:62
+#: conf/global_settings.py:64
msgid "Slovenian"
msgstr "Esloveno"
-#: conf/global_settings.py:63
+#: conf/global_settings.py:65
msgid "Serbian"
msgstr "Serbio"
-#: conf/global_settings.py:64
+#: conf/global_settings.py:66
msgid "Swedish"
msgstr "Sueco"
-#: conf/global_settings.py:65
+#: conf/global_settings.py:67
msgid "Tamil"
msgstr "Tamil"
-#: conf/global_settings.py:66
+#: conf/global_settings.py:68
+msgid "Turkish"
+msgstr "Turco"
+
+#: conf/global_settings.py:69
msgid "Ukrainian"
msgstr "Ucraniano"
-#: conf/global_settings.py:67
+#: conf/global_settings.py:70
msgid "Simplified Chinese"
msgstr "Chino simplificado"
-#: conf/global_settings.py:68
+#: conf/global_settings.py:71
msgid "Traditional Chinese"
msgstr "Chino tradicional"
-#: db/models/manipulators.py:302
+#: db/models/manipulators.py:305
#, python-format
msgid "%(object)s with this %(type)s already exists for the given %(field)s."
-msgstr ""
-"Ya existen %(object)s con este %(type)s para el %(field)s especificado."
+msgstr "Ya existe un(a) %(object)s con este/a %(type)s para %(field)s."
-#: db/models/fields/__init__.py:40
+#: db/models/fields/__init__.py:41
#, python-format
msgid "%(optname)s with this %(fieldname)s already exists."
msgstr "Ya existe %(optname)s con este %(fieldname)s."
-#: db/models/fields/__init__.py:114 db/models/fields/__init__.py:265
-#: db/models/fields/__init__.py:551 db/models/fields/__init__.py:562
-#: forms/__init__.py:346
+#: db/models/fields/__init__.py:115 db/models/fields/__init__.py:266
+#: db/models/fields/__init__.py:569 db/models/fields/__init__.py:580
+#: forms/__init__.py:347
msgid "This field is required."
msgstr "Este campo es obligatorio."
-#: db/models/fields/__init__.py:340
+#: db/models/fields/__init__.py:349
msgid "This value must be an integer."
msgstr "Este valor debe ser un número entero."
-#: db/models/fields/__init__.py:372
+#: db/models/fields/__init__.py:381
msgid "This value must be either True or False."
msgstr "Este valor debe ser True o False."
-#: db/models/fields/__init__.py:388
+#: db/models/fields/__init__.py:397
msgid "This field cannot be null."
msgstr "Este campo no puede ser nulo."
-#: db/models/fields/__init__.py:415 core/validators.py:127
+#: db/models/fields/__init__.py:424 core/validators.py:146
msgid "Enter a valid date in YYYY-MM-DD format."
msgstr "Introduzca una fecha válida en formato AAAA-MM-DD."
-#: db/models/fields/__init__.py:477 core/validators.py:135
+#: db/models/fields/__init__.py:488 core/validators.py:155
msgid "Enter a valid date/time in YYYY-MM-DD HH:MM format."
msgstr "Introduzca una fecha/hora válida en formato YYYY-MM-DD HH:MM."
-#: db/models/fields/__init__.py:571
+#: db/models/fields/__init__.py:589
msgid "Enter a valid filename."
msgstr "Introduzca un nombre de achivo válido"
@@ -1859,7 +1891,8 @@ msgstr " Separe múltiples IDs con comas."
msgid ""
"Hold down \"Control\", or \"Command\" on a Mac, to select more than one."
msgstr ""
-"Pulse \"Control\", o \"Command\" en un Mac, para seleccionar más de uno."
+"Mantenga presionada \"Control\" (\"Command\" en un Mac) para seleccionar más "
+"de uno."
#: db/models/fields/related.py:664
#, python-format
@@ -1873,42 +1906,42 @@ msgstr[1] ""
"Por favor, introduzca IDs de %(self)s válidos. Los valores %(value)r no son "
"válidos."
-#: forms/__init__.py:381
+#: forms/__init__.py:382
#, python-format
msgid "Ensure your text is less than %s character."
msgid_plural "Ensure your text is less than %s characters."
msgstr[0] "Asegúrese de que su texto tiene menos de %s carácter."
msgstr[1] "Asegúrese de que su texto tiene menos de %s caracteres."
-#: forms/__init__.py:386
+#: forms/__init__.py:387
msgid "Line breaks are not allowed here."
msgstr "No se permiten saltos de línea."
-#: forms/__init__.py:487 forms/__init__.py:560 forms/__init__.py:599
+#: forms/__init__.py:488 forms/__init__.py:561 forms/__init__.py:600
#, python-format
msgid "Select a valid choice; '%(data)s' is not in %(choices)s."
msgstr "Seleccione una opción válida; '%(data)s' no está en %(choices)s."
-#: forms/__init__.py:661 core/validators.py:151 core/validators.py:379
+#: forms/__init__.py:662 core/validators.py:172 core/validators.py:401
msgid "No file was submitted. Check the encoding type on the form."
msgstr ""
"No se envió un archivo. Verifique el tipo de codificación en el formulario."
-#: forms/__init__.py:663
+#: forms/__init__.py:664
msgid "The submitted file is empty."
msgstr "El archivo enviado está vacío."
-#: forms/__init__.py:719
+#: forms/__init__.py:720
msgid "Enter a whole number between -32,768 and 32,767."
-msgstr "Introduzca un número entero entre -32,768 y 32,767."
+msgstr "Introduzca un número entero entre -32.768 y 32.767."
-#: forms/__init__.py:729
+#: forms/__init__.py:730
msgid "Enter a positive number."
msgstr "Introduzca un número positivo."
-#: forms/__init__.py:739
+#: forms/__init__.py:740
msgid "Enter a whole number between 0 and 32,767."
-msgstr "Introduzca un número entero entre 0 y 32,767."
+msgstr "Introduzca un número entero entre 0 y 32.767."
#: core/validators.py:63
msgid "This value must contain only letters, numbers and underscores."
@@ -1919,8 +1952,13 @@ msgid ""
"This value must contain only letters, numbers, underscores, dashes or "
"slashes."
msgstr ""
-"Este valor debe contener sólo letras, números, guiones bajos, barras (/) o "
-"slashes."
+"Este valor debe contener sólo letras, números, guiones bajos, guiones o "
+"barras (/)"
+
+#: core/validators.py:71
+msgid "This value must contain only letters, numbers, underscores or hyphens."
+msgstr ""
+"Este valor debe contener sólo letras, números, guiones bajos o guiones."
#: core/validators.py:75
msgid "Uppercase letters are not allowed here."
@@ -1962,15 +2000,24 @@ msgstr "Introduzca un número entero."
msgid "Only alphabetical characters are allowed here."
msgstr "Sólo se admiten caracteres alfabéticos."
-#: core/validators.py:131
+#: core/validators.py:138
+msgid "Year must be 1900 or later."
+msgstr "El año debe ser 1900 o posterior."
+
+#: core/validators.py:142
+#, python-format
+msgid "Invalid date: %s."
+msgstr "Fecha no válida: %s."
+
+#: core/validators.py:151
msgid "Enter a valid time in HH:MM format."
msgstr "Introduzca una hora válida en formato HH:MM."
-#: core/validators.py:139
+#: core/validators.py:160
msgid "Enter a valid e-mail address."
msgstr "Introduzca una dirección de correo electrónico válida"
-#: core/validators.py:155
+#: core/validators.py:176
msgid ""
"Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image."
@@ -1978,28 +2025,28 @@ msgstr ""
"Envíe una imagen válida. El archivo que ha enviado no era una imagen o se "
"trataba de una imagen corrupta."
-#: core/validators.py:162
+#: core/validators.py:183
#, python-format
msgid "The URL %s does not point to a valid image."
msgstr "La URL %s no apunta a una imagen válida."
-#: core/validators.py:166
+#: core/validators.py:187
#, python-format
msgid "Phone numbers must be in XXX-XXX-XXXX format. \"%s\" is invalid."
msgstr ""
-"Los números de teléfono deben guardar el formato XXX-XXX-XXXX format. \"%s\" "
-"no es válido."
+"Los números telefónicos deben respetar el formato XXX-XXX-XXXX. \"%s\" no es "
+"válido."
-#: core/validators.py:174
+#: core/validators.py:195
#, python-format
msgid "The URL %s does not point to a valid QuickTime video."
msgstr "La URL %s no apunta a un vídeo QuickTime válido."
-#: core/validators.py:178
+#: core/validators.py:199
msgid "A valid URL is required."
msgstr "Se precisa una URL válida."
-#: core/validators.py:192
+#: core/validators.py:213
#, python-format
msgid ""
"Valid HTML is required. Specific errors are:\n"
@@ -2008,69 +2055,69 @@ msgstr ""
"Se precisa HTML válido. Los errores específicos son:\n"
"%s"
-#: core/validators.py:199
+#: core/validators.py:220
#, python-format
msgid "Badly formed XML: %s"
msgstr "XML mal formado: %s"
-#: core/validators.py:209
+#: core/validators.py:230
#, python-format
msgid "Invalid URL: %s"
msgstr "URL no válida: %s"
-#: core/validators.py:213 core/validators.py:215
+#: core/validators.py:234 core/validators.py:236
#, python-format
msgid "The URL %s is a broken link."
msgstr "La URL %s es un enlace roto."
-#: core/validators.py:221
+#: core/validators.py:242
msgid "Enter a valid U.S. state abbreviation."
msgstr "Introduzca una abreviatura válida de estado de los EEUU."
-#: core/validators.py:236
+#: core/validators.py:256
#, python-format
msgid "Watch your mouth! The word %s is not allowed here."
msgid_plural "Watch your mouth! The words %s are not allowed here."
msgstr[0] "¡Vigila tu boca! Aquí no admitimos la palabra %s."
msgstr[1] "¡Vigila tu boca! Aquí no admitimos las palabras %s."
-#: core/validators.py:243
+#: core/validators.py:263
#, python-format
msgid "This field must match the '%s' field."
msgstr "Este campo debe concordar con el campo '%s'."
-#: core/validators.py:262
+#: core/validators.py:282
msgid "Please enter something for at least one field."
msgstr "Por favor, introduzca algo en al menos un campo."
-#: core/validators.py:271 core/validators.py:282
+#: core/validators.py:291 core/validators.py:302
msgid "Please enter both fields or leave them both empty."
msgstr "Por favor, rellene ambos campos o deje ambos vacíos."
-#: core/validators.py:289
+#: core/validators.py:309
#, python-format
msgid "This field must be given if %(field)s is %(value)s"
msgstr "Se debe proporcionar este campo si %(field)s es %(value)s"
-#: core/validators.py:301
+#: core/validators.py:321
#, python-format
msgid "This field must be given if %(field)s is not %(value)s"
msgstr "Se debe proporcionar este campo si %(field)s no es %(value)s"
-#: core/validators.py:320
+#: core/validators.py:340
msgid "Duplicate values are not allowed."
msgstr "No se admiten valores duplicados."
-#: core/validators.py:343
+#: core/validators.py:363
#, python-format
msgid "This value must be a power of %s."
msgstr "Este valor debe ser una potencia de %s."
-#: core/validators.py:354
+#: core/validators.py:374
msgid "Please enter a valid decimal number."
msgstr "Por favor, introduzca un número decimal válido."
-#: core/validators.py:356
+#: core/validators.py:378
#, python-format
msgid "Please enter a valid decimal number with at most %s total digit."
msgid_plural ""
@@ -2082,7 +2129,7 @@ msgstr[1] ""
"Por favor, introduzca un número decimal válido con un maximo de %s dígitos "
"en total."
-#: core/validators.py:359
+#: core/validators.py:381
#, python-format
msgid ""
"Please enter a valid decimal number with a whole part of at most %s digit."
@@ -2095,7 +2142,7 @@ msgstr[1] ""
"Por favor, introduzca un número decimal válido con un máximo de %s dígitos "
"enteros."
-#: core/validators.py:362
+#: core/validators.py:384
#, python-format
msgid "Please enter a valid decimal number with at most %s decimal place."
msgid_plural ""
@@ -2107,30 +2154,30 @@ msgstr[1] ""
"Por favor, introduzca un número decimal válido con un máximo de %s "
"posiciones decimales."
-#: core/validators.py:372
+#: core/validators.py:394
#, python-format
msgid "Make sure your uploaded file is at least %s bytes big."
msgstr "Asegúrese de que el archivo que envía tiene al menos %s bytes."
-#: core/validators.py:373
+#: core/validators.py:395
#, python-format
msgid "Make sure your uploaded file is at most %s bytes big."
msgstr "Asegúrese de que el archivo que envía tiene como máximo %s bytes."
-#: core/validators.py:390
+#: core/validators.py:412
msgid "The format for this field is wrong."
msgstr "El formato de este campo es incorrecto."
-#: core/validators.py:405
+#: core/validators.py:427
msgid "This field is invalid."
msgstr "Este campo no es válido."
-#: core/validators.py:441
+#: core/validators.py:463
#, python-format
msgid "Could not retrieve anything from %s."
msgstr "No pude obtener nada de %s."
-#: core/validators.py:444
+#: core/validators.py:466
#, python-format
msgid ""
"The URL %(url)s returned the invalid Content-Type header '%(contenttype)s'."
@@ -2138,7 +2185,7 @@ msgstr ""
"La URL %(url)s devolvió la cabecera Content-Type '%(contenttype)s', que no "
"es válida."
-#: core/validators.py:477
+#: core/validators.py:499
#, python-format
msgid ""
"Please close the unclosed %(tag)s tag from line %(line)s. (Line starts with "
@@ -2147,7 +2194,7 @@ msgstr ""
"Por favor, cierre la etiqueta %(tag)s de la línea %(line)s. (La línea "
"empieza por \"%(start)s\".)"
-#: core/validators.py:481
+#: core/validators.py:503
#, python-format
msgid ""
"Some text starting on line %(line)s is not allowed in that context. (Line "
@@ -2156,7 +2203,7 @@ msgstr ""
"Parte del texto que comienza en la línea %(line)s no está permitido en ese "
"contexto. (La línea empieza por \"%(start)s\".)"
-#: core/validators.py:486
+#: core/validators.py:508
#, python-format
msgid ""
"\"%(attr)s\" on line %(line)s is an invalid attribute. (Line starts with \"%"
@@ -2165,7 +2212,7 @@ msgstr ""
"El \"%(attr)s\" de la línea %(line)s no es un atributo válido. (La línea "
"empieza por \"%(start)s\".)"
-#: core/validators.py:491
+#: core/validators.py:513
#, python-format
msgid ""
"\"<%(tag)s>\" on line %(line)s is an invalid tag. (Line starts with \"%"
@@ -2174,7 +2221,7 @@ msgstr ""
"La \"<%(tag)s>\" de la línea %(line)s no es una etiqueta válida. (La línea "
"empieza por \"%(start)s\".)"
-#: core/validators.py:495
+#: core/validators.py:517
#, python-format
msgid ""
"A tag on line %(line)s is missing one or more required attributes. (Line "
@@ -2183,7 +2230,7 @@ msgstr ""
"A una etiqueta de la línea %(line)s le faltan uno o más atributos "
"requeridos. (La línea empieza por \"%(start)s\".)"
-#: core/validators.py:500
+#: core/validators.py:522
#, python-format
msgid ""
"The \"%(attr)s\" attribute on line %(line)s has an invalid value. (Line "
@@ -2199,17 +2246,20 @@ msgstr "si,no,tal vez"
#: views/generic/create_update.py:43
#, python-format
msgid "The %(verbose_name)s was created successfully."
-msgstr "Se creó con éxito el %(verbose_name)."
+msgstr "Se creó con éxito %(verbose_name)."
#: views/generic/create_update.py:117
#, python-format
msgid "The %(verbose_name)s was updated successfully."
-msgstr "Se actualizó con éxito el %(verbose_name)s."
+msgstr "Se actualizó con éxito %(verbose_name)s."
#: views/generic/create_update.py:184
#, python-format
msgid "The %(verbose_name)s was deleted."
-msgstr "Se eliminó el %(verbose_name)s."
+msgstr "Se eliminó %(verbose_name)s."
+
+#~ msgid "Have you <a href=\"/password_reset/\">forgotten your password</a>?"
+#~ msgstr "¿Ha <a href=\"/password_reset/\">olvidado su contraseña</a>?"
#~ msgid "%(content_type_name)s"
#~ msgstr "tipos de contenido"
diff --git a/django/conf/locale/es_AR/LC_MESSAGES/djangojs.mo b/django/conf/locale/es_AR/LC_MESSAGES/djangojs.mo
index f24c9a12d0..3f89e3e33f 100644
--- a/django/conf/locale/es_AR/LC_MESSAGES/djangojs.mo
+++ b/django/conf/locale/es_AR/LC_MESSAGES/djangojs.mo
Binary files differ
diff --git a/django/conf/locale/es_AR/LC_MESSAGES/djangojs.po b/django/conf/locale/es_AR/LC_MESSAGES/djangojs.po
index 49d3856d3c..1865de9450 100644
--- a/django/conf/locale/es_AR/LC_MESSAGES/djangojs.po
+++ b/django/conf/locale/es_AR/LC_MESSAGES/djangojs.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Django JavaScript 1.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2006-06-19 12:15-0300\n"
+"POT-Creation-Date: 2006-09-25 15:09-0300\n"
"PO-Revision-Date: 2006-05-16 10:20-0300\n"
"Last-Translator: Ramiro Morales <rm0@gmx.net>\n"
"MIME-Version: 1.0\n"
@@ -39,7 +39,7 @@ msgstr "%s elegidos"
#: contrib/admin/media/js/SelectFilter2.js:54
msgid "Select your choice(s) and click "
-msgstr "Haga sus elecciones y haga click en "
+msgstr "Seleccione los items a agregar y haga click en "
#: contrib/admin/media/js/SelectFilter2.js:59
msgid "Clear all"
@@ -51,7 +51,7 @@ msgid ""
"January February March April May June July August September October November "
"December"
msgstr ""
-"Enero Febrero Marzo Abril Mayo Junio Julio Agosto Septiembre Octubre "
+"Enero Febrero Marzo Abril Mayo Junio Julio Agosto Setiembre Octubre "
"Noviembre Diciembre"
#: contrib/admin/media/js/dateparse.js:33
@@ -71,49 +71,49 @@ msgstr "Mostrar"
msgid "Hide"
msgstr "Ocultar"
-#: contrib/admin/media/js/admin/DateTimeShortcuts.js:45
-#: contrib/admin/media/js/admin/DateTimeShortcuts.js:89
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:47
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:81
msgid "Now"
msgstr "Ahora"
-#: contrib/admin/media/js/admin/DateTimeShortcuts.js:48
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:51
msgid "Clock"
msgstr "Reloj"
-#: contrib/admin/media/js/admin/DateTimeShortcuts.js:86
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:78
msgid "Choose a time"
msgstr "Elija una hora"
-#: contrib/admin/media/js/admin/DateTimeShortcuts.js:90
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:82
msgid "Midnight"
msgstr "Medianoche"
-#: contrib/admin/media/js/admin/DateTimeShortcuts.js:91
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:83
msgid "6 a.m."
msgstr "6 a.m."
-#: contrib/admin/media/js/admin/DateTimeShortcuts.js:92
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:84
msgid "Noon"
msgstr "Mediodía"
-#: contrib/admin/media/js/admin/DateTimeShortcuts.js:96
-#: contrib/admin/media/js/admin/DateTimeShortcuts.js:187
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:88
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:183
msgid "Cancel"
msgstr "Cancelar"
-#: contrib/admin/media/js/admin/DateTimeShortcuts.js:120
-#: contrib/admin/media/js/admin/DateTimeShortcuts.js:181
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:128
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:177
msgid "Today"
msgstr "Hoy"
-#: contrib/admin/media/js/admin/DateTimeShortcuts.js:123
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:132
msgid "Calendar"
msgstr "Calendario"
-#: contrib/admin/media/js/admin/DateTimeShortcuts.js:179
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:175
msgid "Yesterday"
msgstr "Ayer"
-#: contrib/admin/media/js/admin/DateTimeShortcuts.js:183
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:179
msgid "Tomorrow"
msgstr "Mañana"
diff --git a/django/conf/locale/pt_BR/LC_MESSAGES/django.mo b/django/conf/locale/pt_BR/LC_MESSAGES/django.mo
index 3df145a0d3..7f506f972f 100644
--- a/django/conf/locale/pt_BR/LC_MESSAGES/django.mo
+++ b/django/conf/locale/pt_BR/LC_MESSAGES/django.mo
Binary files differ
diff --git a/django/conf/locale/pt_BR/LC_MESSAGES/django.po b/django/conf/locale/pt_BR/LC_MESSAGES/django.po
index 337027d267..bc955f29c4 100644
--- a/django/conf/locale/pt_BR/LC_MESSAGES/django.po
+++ b/django/conf/locale/pt_BR/LC_MESSAGES/django.po
@@ -2,14 +2,15 @@
# Copyright (C) 2006 THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# João Marcus Christ <joaoma@gmail.com>, 2006.
+# Carlos Eduardo de Paula <carlosedp@gmail.com>, 2006.
#
msgid ""
msgstr ""
"Project-Id-Version: django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2006-05-16 10:11+0200\n"
-"PO-Revision-Date: 2006-01-23 19:54-0200\n"
-"Last-Translator: João Marcus Christ <joaoma@gmail.com>\n"
+"PO-Revision-Date: 2006-11-01 17:45-0300\n"
+"Last-Translator: Carlos Eduardo de Paula <carlosedp@gmail.com>\n"
"Language-Team: Português do Brasil <pt-br@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -86,12 +87,12 @@ msgid ""
"removed\" message will be displayed instead."
msgstr ""
"Selecione esta opção se o comentário é inapropriado. Uma mensagem \"Este "
-"comentário foi removido\" será mostrada no lugar."
+"comentário foi removido\" a mensagem será mostrada no lugar."
#: contrib/comments/models.py:91
#, fuzzy
msgid "comments"
-msgstr "comentário"
+msgstr "comentários"
#: contrib/comments/models.py:131 contrib/comments/models.py:207
msgid "Content object"
@@ -150,7 +151,7 @@ msgstr "Pontuação de Karma"
#: contrib/comments/models.py:238
#, fuzzy
msgid "karma scores"
-msgstr "Pontuação de Karma"
+msgstr "Pontuações de Karma"
#: contrib/comments/models.py:242
#, python-format
@@ -170,17 +171,17 @@ msgstr ""
#: contrib/comments/models.py:265
msgid "flag date"
-msgstr "marca de data"
+msgstr "flag de data"
#: contrib/comments/models.py:268
#, fuzzy
msgid "user flag"
-msgstr "Flag de usuário"
+msgstr "flag de usuário"
#: contrib/comments/models.py:269
#, fuzzy
msgid "user flags"
-msgstr "Flags de usuário"
+msgstr "flags de usuário"
#: contrib/comments/models.py:273
#, python-format
@@ -189,22 +190,22 @@ msgstr "Flag por %r"
#: contrib/comments/models.py:278
msgid "deletion date"
-msgstr "data de apagamento"
+msgstr "data de exclusão"
#: contrib/comments/models.py:280
#, fuzzy
msgid "moderator deletion"
-msgstr "Apagamento feito por moderador"
+msgstr "Exclusão feita pelo moderador"
#: contrib/comments/models.py:281
#, fuzzy
msgid "moderator deletions"
-msgstr "Apagamentos feitos por moderador"
+msgstr "Exclusões feitas pelo moderador"
#: contrib/comments/models.py:285
#, python-format
msgid "Moderator deletion by %r"
-msgstr "Apagamento feito pelo moderador %r"
+msgstr "Exclusao feita pelo moderador %r"
#: contrib/comments/views/karma.py:19
msgid "Anonymous users cannot vote"
@@ -237,12 +238,12 @@ msgid_plural ""
"\n"
"%(text)s"
msgstr[0] ""
-"Este comentário foi feito por um usuário esboço:\n"
-"\n"
+"Este comentário foi feito por um usuário que postou menos de %(count)s "
+"comentário:\n"
"%(text)s"
msgstr[1] ""
-"Este comentário foi feito por um usuário esboço:\n"
-"\n"
+"Este comentário foi feito por um usuário que postou menos de %(count)s "
+"comentários:\n"
"%(text)s"
#: contrib/comments/views/comments.py:117
@@ -252,7 +253,7 @@ msgid ""
"\n"
"%(text)s"
msgstr ""
-"Este comentário foi feito por um usuário esboço:\n"
+"Este comentário foi feito por um usuário incompleto:\n"
"\n"
"%(text)s"
@@ -299,7 +300,7 @@ msgstr "Senha:"
#: contrib/comments/templates/comments/form.html:6
#, fuzzy
msgid "Forgotten your password?"
-msgstr "Alterar minha senha"
+msgstr "Esqueceu sua senha?"
#: contrib/comments/templates/comments/form.html:8
#: contrib/admin/templates/admin/object_history.html:3
@@ -325,21 +326,21 @@ msgstr "Encerrar sessão"
#: contrib/comments/templates/comments/form.html:12
#, fuzzy
msgid "Ratings"
-msgstr "avaliação #1"
+msgstr "Avaliações"
#: contrib/comments/templates/comments/form.html:12
#: contrib/comments/templates/comments/form.html:23
msgid "Required"
-msgstr ""
+msgstr "Requerido"
#: contrib/comments/templates/comments/form.html:12
#: contrib/comments/templates/comments/form.html:23
msgid "Optional"
-msgstr ""
+msgstr "Opcional"
#: contrib/comments/templates/comments/form.html:23
msgid "Post a photo"
-msgstr ""
+msgstr "Postar uma foto"
#: contrib/comments/templates/comments/form.html:27
#: contrib/comments/templates/comments/freeform.html:5
@@ -351,12 +352,12 @@ msgstr "Comentário"
#: contrib/comments/templates/comments/freeform.html:9
#, fuzzy
msgid "Preview comment"
-msgstr "Comentário livre"
+msgstr "Pré visualizar comentário"
#: contrib/comments/templates/comments/freeform.html:4
#, fuzzy
msgid "Your name:"
-msgstr "usuário"
+msgstr "Seu nome:"
#: contrib/admin/filterspecs.py:40
#, python-format
@@ -443,7 +444,7 @@ msgid ""
"sensitive."
msgstr ""
"Por favor entre usuário e senha corretos. Note que ambos os "
-"camposdiferenciam maiúsculas e minúsculas."
+"campos diferenciam maiúsculas e minúsculas."
#: contrib/admin/views/decorators.py:23
#: contrib/admin/templates/admin/login.html:25
@@ -468,7 +469,7 @@ msgstr ""
#: contrib/admin/views/decorators.py:82
msgid "Usernames cannot contain the '@' character."
-msgstr "Nomes de usuário não podem conter o caracter '@'."
+msgstr "Nomes de usuário não podem conter o caractere '@'."
#: contrib/admin/views/decorators.py:84
#, python-format
@@ -553,7 +554,7 @@ msgstr "Um(a) ou mais %(fieldname)s em %(name)s:"
#: contrib/admin/views/main.py:508
#, python-format
msgid "The %(name)s \"%(obj)s\" was deleted successfully."
-msgstr "O(A) %(name)s \"%(obj)s\" foi adicionado com sucesso."
+msgstr "O(A) %(name)s \"%(obj)s\" foi excluído com sucesso."
#: contrib/admin/views/main.py:511
msgid "Are you sure?"
@@ -746,7 +747,7 @@ msgid ""
"mail and should be fixed shortly. Thanks for your patience."
msgstr ""
"Houve um erro. Este foi reportado aos administradores do site através d e-"
-"mail e deve ser consertado em breve. Obrigado pela compreensão."
+"mail e deve ser corrigido em breve. Obrigado pela compreensão."
#: contrib/admin/templates/admin/404.html:4
#: contrib/admin/templates/admin/404.html:8
@@ -760,7 +761,7 @@ msgstr "Desculpe, mas a página requisitada não pode ser encontrada."
#: contrib/admin/templates/admin/index.html:17
#, python-format
msgid "Models available in the %(name)s application."
-msgstr ""
+msgstr "Modelos disponíveis na aplicação %(name)s"
#: contrib/admin/templates/admin/index.html:28
#: contrib/admin/templates/admin/change_form.html:15
@@ -794,7 +795,7 @@ msgstr "Adicionar %(name)s"
#: contrib/admin/templates/admin/login.html:22
msgid "Have you <a href=\"/password_reset/\">forgotten your password</a>?"
-msgstr "Você <a href=\"/password_reset/\"esqueceu a senha</a>?"
+msgstr "Você <a href=\"/password_reset/\">esqueceu sua senha</a>?"
#: contrib/admin/templates/admin/base.html:23
msgid "Welcome,"
@@ -836,7 +837,7 @@ msgstr "Por %(title)s "
#: contrib/admin/templates/admin/search_form.html:8
msgid "Go"
-msgstr ""
+msgstr "Ir"
#: contrib/admin/templates/admin/change_form.html:21
msgid "View on site"
@@ -847,7 +848,7 @@ msgstr "Ver no site"
msgid "Please correct the error below."
msgid_plural "Please correct the errors below."
msgstr[0] "Por favor, corrija o erro abaixo."
-msgstr[1] "Por favor, corrija o erro abaixo."
+msgstr[1] "Por favor, corrija os erros abaixo."
#: contrib/admin/templates/admin/change_form.html:48
msgid "Ordering"
@@ -930,8 +931,8 @@ msgid ""
"We've e-mailed a new password to the e-mail address you submitted. You "
"should be receiving it shortly."
msgstr ""
-"Nós enviamos uma nova senha para o e-mail que você informou. Você deve estar "
-"recebendo uma mensagem em breve."
+"Nós enviamos uma nova senha para o e-mail que você informou. Você deverá "
+"receber uma mensagem em breve."
#: contrib/admin/templates/registration/password_change_form.html:12
msgid ""
@@ -973,7 +974,7 @@ msgstr "Sua nova senha é: %(new_password)s"
#: contrib/admin/templates/registration/password_reset_email.html:7
msgid "Feel free to change this password by going to this page:"
-msgstr "Sinta-se livre para alterar esta senha visitando esta página:"
+msgstr "Sinta-se a vontade para alterar esta senha visitando esta página:"
#: contrib/admin/templates/registration/password_reset_email.html:11
msgid "Your username, in case you've forgotten:"
@@ -1040,17 +1041,17 @@ msgstr ""
#: contrib/admin/templates/admin_doc/bookmarklets.html:25
msgid "Edit this object (current window)"
-msgstr "Edita este objeto (janela atual)"
+msgstr "Editar este objeto (janela atual)"
#: contrib/admin/templates/admin_doc/bookmarklets.html:26
msgid "Jumps to the admin page for pages that represent a single object."
msgstr ""
-"Vai para a página de administração para páginas que representam um objeto "
+"Vai para a página de administração que representam um objeto "
"único."
#: contrib/admin/templates/admin_doc/bookmarklets.html:28
msgid "Edit this object (new window)"
-msgstr "Edita este objeto (nova janela)"
+msgstr "Editar este objeto (nova janela)"
#: contrib/admin/templates/admin_doc/bookmarklets.html:29
msgid "As above, but opens the admin page in a new window."
@@ -1135,7 +1136,7 @@ msgstr ""
#: contrib/flatpages/models.py:14
msgid "registration required"
-msgstr "é obrigatório registrar"
+msgstr "registro obrigatório"
#: contrib/flatpages/models.py:14
msgid "If this is checked, only logged-in users will be able to view the page."
@@ -1160,22 +1161,22 @@ msgstr "nome código"
#: contrib/auth/models.py:17
#, fuzzy
msgid "permission"
-msgstr "Permissão"
+msgstr "permissão"
#: contrib/auth/models.py:18 contrib/auth/models.py:27
#, fuzzy
msgid "permissions"
-msgstr "Permissões"
+msgstr "permissões"
#: contrib/auth/models.py:29
#, fuzzy
msgid "group"
-msgstr "Grupo"
+msgstr "grupo"
#: contrib/auth/models.py:30 contrib/auth/models.py:65
#, fuzzy
msgid "groups"
-msgstr "Grupos"
+msgstr "grupos"
#: contrib/auth/models.py:55
msgid "username"
@@ -1236,17 +1237,17 @@ msgstr ""
#: contrib/auth/models.py:67
#, fuzzy
msgid "user permissions"
-msgstr "Permissões"
+msgstr "permissões do usuário"
#: contrib/auth/models.py:70
#, fuzzy
msgid "user"
-msgstr "Usuário"
+msgstr "usuário"
#: contrib/auth/models.py:71
#, fuzzy
msgid "users"
-msgstr "Usuários"
+msgstr "usuários"
#: contrib/auth/models.py:76
msgid "Personal info"
@@ -1267,7 +1268,7 @@ msgstr "Grupos"
#: contrib/auth/models.py:219
#, fuzzy
msgid "message"
-msgstr "Mensagem"
+msgstr "mensagem"
#: contrib/auth/forms.py:30
msgid ""
@@ -1275,7 +1276,7 @@ msgid ""
"required for logging in."
msgstr ""
"Seu navegador Web não parece estar com os cookies habilitados. Cookies são "
-"requeridos para acesssar."
+"requeridos para acessar."
#: contrib/contenttypes/models.py:25
#, fuzzy
@@ -1417,52 +1418,52 @@ msgstr "Dezembro"
#: utils/dates.py:19
#, fuzzy
msgid "jan"
-msgstr "e"
+msgstr "jan"
#: utils/dates.py:19
msgid "feb"
-msgstr ""
+msgstr "fev"
#: utils/dates.py:19
msgid "mar"
-msgstr ""
+msgstr "mar"
#: utils/dates.py:19
msgid "apr"
-msgstr ""
+msgstr "abr"
#: utils/dates.py:19
#, fuzzy
msgid "may"
-msgstr "dia"
+msgstr "mai"
#: utils/dates.py:19
msgid "jun"
-msgstr ""
+msgstr "jun"
#: utils/dates.py:20
msgid "jul"
-msgstr ""
+msgstr "jul"
#: utils/dates.py:20
msgid "aug"
-msgstr ""
+msgstr "ago"
#: utils/dates.py:20
msgid "sep"
-msgstr ""
+msgstr "set"
#: utils/dates.py:20
msgid "oct"
-msgstr ""
+msgstr "out"
#: utils/dates.py:20
msgid "nov"
-msgstr ""
+msgstr "nov"
#: utils/dates.py:20
msgid "dec"
-msgstr ""
+msgstr "dez"
#: utils/dates.py:27
msgid "Jan."
@@ -1497,45 +1498,45 @@ msgstr "Dez."
msgid "year"
msgid_plural "years"
msgstr[0] "ano"
-msgstr[1] "ano"
+msgstr[1] "anos"
#: utils/timesince.py:13
#, fuzzy
msgid "month"
msgid_plural "months"
msgstr[0] "mês"
-msgstr[1] "mês"
+msgstr[1] "meses"
#: utils/timesince.py:14
msgid "week"
msgid_plural "weeks"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "semana"
+msgstr[1] "semanas"
#: utils/timesince.py:15
#, fuzzy
msgid "day"
msgid_plural "days"
msgstr[0] "dia"
-msgstr[1] "dia"
+msgstr[1] "dias"
#: utils/timesince.py:16
#, fuzzy
msgid "hour"
msgid_plural "hours"
msgstr[0] "hora"
-msgstr[1] "hora"
+msgstr[1] "horas"
#: utils/timesince.py:17
#, fuzzy
msgid "minute"
msgid_plural "minutes"
msgstr[0] "minuto"
-msgstr[1] "minuto"
+msgstr[1] "minutos"
#: conf/global_settings.py:37
msgid "Bengali"
-msgstr ""
+msgstr "Bengalês"
#: conf/global_settings.py:38
msgid "Czech"
@@ -1548,7 +1549,7 @@ msgstr ""
#: conf/global_settings.py:40
#, fuzzy
msgid "Danish"
-msgstr "Espanhol"
+msgstr "Dinamarquês"
#: conf/global_settings.py:41
msgid "German"
@@ -1556,7 +1557,7 @@ msgstr "Alemão"
#: conf/global_settings.py:42
msgid "Greek"
-msgstr ""
+msgstr "Grego"
#: conf/global_settings.py:43
msgid "English"
@@ -1576,11 +1577,11 @@ msgstr "Galiciano"
#: conf/global_settings.py:47
msgid "Hungarian"
-msgstr ""
+msgstr "Húngaro"
#: conf/global_settings.py:48
msgid "Hebrew"
-msgstr ""
+msgstr "Hebraico"
#: conf/global_settings.py:49
msgid "Icelandic"
@@ -1604,7 +1605,7 @@ msgstr "Norueguês"
#: conf/global_settings.py:54
msgid "Brazilian"
-msgstr "Brazileiro"
+msgstr "Brasileiro"
#: conf/global_settings.py:55
msgid "Romanian"
@@ -1621,7 +1622,7 @@ msgstr "Eslovaco"
#: conf/global_settings.py:58
#, fuzzy
msgid "Slovenian"
-msgstr "Eslovaco"
+msgstr "Esloveno"
#: conf/global_settings.py:59
msgid "Serbian"
@@ -1634,7 +1635,7 @@ msgstr "Sueco"
#: conf/global_settings.py:61
#, fuzzy
msgid "Ukrainian"
-msgstr "Brazileiro"
+msgstr "Ucraniano"
#: conf/global_settings.py:62
msgid "Simplified Chinese"
@@ -1689,7 +1690,7 @@ msgstr "Este valor não pode conter apenas dígitos."
#: core/validators.py:116
msgid "Enter a whole number."
-msgstr "Informe um número inteiro."
+msgstr "Informe um número completo."
#: core/validators.py:120
msgid "Only alphabetical characters are allowed here."
@@ -1772,7 +1773,7 @@ msgstr "Informe uma abreviação válida de nome de um estado dos EUA."
msgid "Watch your mouth! The word %s is not allowed here."
msgid_plural "Watch your mouth! The words %s are not allowed here."
msgstr[0] "Lave sua boca! A palavra %s não é permitida aqui."
-msgstr[1] "Lave sua boca! A palavra %s não é permitida aqui."
+msgstr[1] "Lave sua boca! As palavras %s não são permitidas aqui."
#: core/validators.py:236
#, python-format
@@ -1808,14 +1809,14 @@ msgstr "Este valor deve ser uma potência de %s."
#: core/validators.py:347
msgid "Please enter a valid decimal number."
-msgstr "Informe um número decimal."
+msgstr "Informe um número decimal válido."
#: core/validators.py:349
#, fuzzy, python-format
msgid "Please enter a valid decimal number with at most %s total digit."
msgid_plural ""
"Please enter a valid decimal number with at most %s total digits."
-msgstr[0] "Por favor entre com um número decimal com no máximo %s digitos."
+msgstr[0] "Por favor entre com um número decimal com no máximo %s digito."
msgstr[1] "Por favor entre com um número decimal com no máximo %s digitos."
#: core/validators.py:352
@@ -1824,7 +1825,7 @@ msgid "Please enter a valid decimal number with at most %s decimal place."
msgid_plural ""
"Please enter a valid decimal number with at most %s decimal places."
msgstr[0] "Informe um número decimal com no máximo %s casa decimal."
-msgstr[1] "Informe um número decimal com no máximo %s casa decimal."
+msgstr[1] "Informe um número decimal com no máximo %s casas decimais."
#: core/validators.py:362
#, python-format
@@ -1930,17 +1931,17 @@ msgstr "Este campo é requerido."
#: db/models/fields/__init__.py:337
#, fuzzy
msgid "This value must be an integer."
-msgstr "Este valor deve ser uma potência de %s."
+msgstr "Este valor deve ser um inteiro."
#: db/models/fields/__init__.py:369
#, fuzzy
msgid "This value must be either True or False."
-msgstr "Este valor deve ser uma potência de %s."
+msgstr "Este valor deve ser Verdadeiro ou Falso."
#: db/models/fields/__init__.py:385
#, fuzzy
msgid "This field cannot be null."
-msgstr "Este campo é inválido."
+msgstr "Este campo não pode ser nulo."
#: db/models/fields/__init__.py:562
msgid "Enter a valid filename."
@@ -1954,14 +1955,14 @@ msgstr "Por favor informe um %s válido."
#: db/models/fields/related.py:579
#, fuzzy
msgid "Separate multiple IDs with commas."
-msgstr " Separe IDs múltiplos com vírgulas."
+msgstr "Separe IDs múltiplos com vírgulas."
#: db/models/fields/related.py:581
#, fuzzy
msgid ""
"Hold down \"Control\", or \"Command\" on a Mac, to select more than one."
msgstr ""
-" Mantenha pressionado \"Control\", ou \"Command\" num Mac para selecionar "
+" Mantenha pressionado \"Control\", ou \"Command\" no Mac para selecionar "
"mais de uma opção."
#: db/models/fields/related.py:625
@@ -1972,18 +1973,18 @@ msgid_plural ""
msgstr[0] ""
"Por favor, entre IDs válidos para %(self)s. O valor %(value)r é inválido."
msgstr[1] ""
-"Por favor, entre IDs válidos para %(self)s. O valor %(value)r é inválido."
+"Por favor, entre IDs válidos para %(self)s. Os valores %(value)r são inválidos."
#: forms/__init__.py:380
#, fuzzy, python-format
msgid "Ensure your text is less than %s character."
msgid_plural "Ensure your text is less than %s characters."
-msgstr[0] "Certifique-se de que seu texto tenha menos que %s caracteres."
+msgstr[0] "Certifique-se de que seu texto tenha menos que %s caractere."
msgstr[1] "Certifique-se de que seu texto tenha menos que %s caracteres."
#: forms/__init__.py:385
msgid "Line breaks are not allowed here."
-msgstr "Não são permitidas múltiplas linhas aqui."
+msgstr "Não são permitidas quebras de linha aqui."
#: forms/__init__.py:480 forms/__init__.py:551 forms/__init__.py:589
#, python-format
@@ -2037,7 +2038,7 @@ msgstr "sim,não,talvez"
#~ "\n"
#~ "%(text)s"
#~ msgstr ""
-#~ "Este comentário foi envidao por um usuário que enviou menos de %(count)s "
+#~ "Este comentário foi enviado por um usuário que enviou menos de %(count)s "
#~ "comentário:\n"
#~ "\n"
#~ "%(text)sEste comentário foi enviado por um usuário que enviou menos de %"
@@ -2047,4 +2048,4 @@ msgstr "sim,não,talvez"
#, fuzzy
#~ msgid "count"
-#~ msgstr "conteúdo"
+#~ msgstr "contagem"
diff --git a/django/conf/locale/pt_BR/LC_MESSAGES/djangojs.mo b/django/conf/locale/pt_BR/LC_MESSAGES/djangojs.mo
index f8c53203c9..31a2b1b3c0 100644
--- a/django/conf/locale/pt_BR/LC_MESSAGES/djangojs.mo
+++ b/django/conf/locale/pt_BR/LC_MESSAGES/djangojs.mo
Binary files differ
diff --git a/django/conf/locale/pt_BR/LC_MESSAGES/djangojs.po b/django/conf/locale/pt_BR/LC_MESSAGES/djangojs.po
index 44aa1d86ec..299fc6526a 100644
--- a/django/conf/locale/pt_BR/LC_MESSAGES/djangojs.po
+++ b/django/conf/locale/pt_BR/LC_MESSAGES/djangojs.po
@@ -1,15 +1,15 @@
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# Português do Brasil translation of django.
+# Copyright (C) 2006 THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# Carlos Eduardo de Paula <carlosedp@gmail.com>, 2006.
#
msgid ""
msgstr ""
"Project-Id-Version: django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2005-12-09 11:51+0100\n"
-"PO-Revision-Date: 2006-01-23 19:54-0200\n"
-"Last-Translator: João Marcus Christ <joaoma@gmail.com>\n"
+"PO-Revision-Date: 2006-11-01 17:45-0300\n"
+"Last-Translator: Carlos Eduardo de Paula <carlosedp@gmail.com>\n"
"Language-Team: Português do Brasil <pt-br@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -51,7 +51,7 @@ msgid ""
"January February March April May June July August September October November "
"December"
msgstr ""
-"Janeiro Fevereiro Março Abrio Maio Junho Julho Agosto Setembro Outubro Novembro "
+"Janeiro Fevereiro Março Abril Maio Junho Julho Agosto Setembro Outubro Novembro "
"Dezembro"
#: contrib/admin/media/js/dateparse.js:27
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday"
diff --git a/django/contrib/admin/media/js/admin/DateTimeShortcuts.js b/django/contrib/admin/media/js/admin/DateTimeShortcuts.js
index 77c536b865..b1504fc819 100644
--- a/django/contrib/admin/media/js/admin/DateTimeShortcuts.js
+++ b/django/contrib/admin/media/js/admin/DateTimeShortcuts.js
@@ -44,7 +44,7 @@ var DateTimeShortcuts = {
var shortcuts_span = document.createElement('span');
inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling);
var now_link = document.createElement('a');
- now_link.setAttribute('href', "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().getHourMinute());");
+ now_link.setAttribute('href', "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().getHourMinuteSecond());");
now_link.appendChild(document.createTextNode(gettext('Now')));
var clock_link = document.createElement('a');
clock_link.setAttribute('href', 'javascript:DateTimeShortcuts.openClock(' + num + ');');
@@ -80,10 +80,10 @@ var DateTimeShortcuts = {
quickElement('h2', clock_box, gettext('Choose a time'));
time_list = quickElement('ul', clock_box, '');
time_list.className = 'timelist';
- quickElement("a", quickElement("li", time_list, ""), gettext("Now"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().getHourMinute());")
- quickElement("a", quickElement("li", time_list, ""), gettext("Midnight"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", '00:00');")
- quickElement("a", quickElement("li", time_list, ""), gettext("6 a.m."), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", '06:00');")
- quickElement("a", quickElement("li", time_list, ""), gettext("Noon"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", '12:00');")
+ quickElement("a", quickElement("li", time_list, ""), gettext("Now"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().getHourMinuteSecond());")
+ quickElement("a", quickElement("li", time_list, ""), gettext("Midnight"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", '00:00:00');")
+ quickElement("a", quickElement("li", time_list, ""), gettext("6 a.m."), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", '06:00:00');")
+ quickElement("a", quickElement("li", time_list, ""), gettext("Noon"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", '12:00:00');")
cancel_p = quickElement('p', clock_box, '');
cancel_p.className = 'calendar-cancel';
diff --git a/django/contrib/admin/media/js/core.js b/django/contrib/admin/media/js/core.js
index d35bd29c1c..a17ac8a4d2 100644
--- a/django/contrib/admin/media/js/core.js
+++ b/django/contrib/admin/media/js/core.js
@@ -119,6 +119,10 @@ Date.prototype.getTwoDigitMinute = function() {
return (this.getMinutes() < 10) ? '0' + this.getMinutes() : this.getMinutes();
}
+Date.prototype.getTwoDigitSecond = function() {
+ return (this.getSeconds() < 10) ? '0' + this.getSeconds() : this.getSeconds();
+}
+
Date.prototype.getISODate = function() {
return this.getCorrectYear() + '-' + this.getTwoDigitMonth() + '-' + this.getTwoDigitDate();
}
@@ -127,6 +131,10 @@ Date.prototype.getHourMinute = function() {
return this.getTwoDigitHour() + ':' + this.getTwoDigitMinute();
}
+Date.prototype.getHourMinuteSecond = function() {
+ return this.getTwoDigitHour() + ':' + this.getTwoDigitMinute() + ':' + this.getTwoDigitSecond();
+}
+
// ----------------------------------------------------------------------------
// String object extensions
// ----------------------------------------------------------------------------
diff --git a/django/contrib/admin/media/js/dateparse.js b/django/contrib/admin/media/js/dateparse.js
index 51821c78e5..e1c870e146 100644
--- a/django/contrib/admin/media/js/dateparse.js
+++ b/django/contrib/admin/media/js/dateparse.js
@@ -169,8 +169,8 @@ var dateParsePatterns = [
handler: function(bits) {
var d = new Date();
d.setYear(parseInt(bits[1]));
- d.setDate(parseInt(bits[3], 10));
d.setMonth(parseInt(bits[2], 10) - 1);
+ d.setDate(parseInt(bits[3], 10));
return d;
}
},
diff --git a/django/contrib/admin/templatetags/admin_modify.py b/django/contrib/admin/templatetags/admin_modify.py
index bb0d06e38a..1ad34dda0a 100644
--- a/django/contrib/admin/templatetags/admin_modify.py
+++ b/django/contrib/admin/templatetags/admin_modify.py
@@ -187,8 +187,8 @@ def output_all(form_fields):
output_all = register.simple_tag(output_all)
def auto_populated_field_script(auto_pop_fields, change = False):
+ t = []
for field in auto_pop_fields:
- t = []
if change:
t.append('document.getElementById("id_%s")._changed = true;' % field.name)
else:
diff --git a/django/contrib/admin/views/doc.py b/django/contrib/admin/views/doc.py
index 435d76f276..4b592acebd 100644
--- a/django/contrib/admin/views/doc.py
+++ b/django/contrib/admin/views/doc.py
@@ -98,13 +98,13 @@ def view_index(request):
return missing_docutils_page(request)
if settings.ADMIN_FOR:
- settings_modules = [__import__(m, '', '', ['']) for m in settings.ADMIN_FOR]
+ settings_modules = [__import__(m, {}, {}, ['']) for m in settings.ADMIN_FOR]
else:
settings_modules = [settings]
views = []
for settings_mod in settings_modules:
- urlconf = __import__(settings_mod.ROOT_URLCONF, '', '', [''])
+ urlconf = __import__(settings_mod.ROOT_URLCONF, {}, {}, [''])
view_functions = extract_views_from_urlpatterns(urlconf.urlpatterns)
if Site._meta.installed:
site_obj = Site.objects.get(pk=settings_mod.SITE_ID)
@@ -127,7 +127,7 @@ def view_detail(request, view):
mod, func = urlresolvers.get_mod_func(view)
try:
- view_func = getattr(__import__(mod, '', '', ['']), func)
+ view_func = getattr(__import__(mod, {}, {}, ['']), func)
except (ImportError, AttributeError):
raise Http404
title, body, metadata = utils.parse_docstring(view_func.__doc__)
@@ -235,7 +235,7 @@ model_detail = staff_member_required(model_detail)
def template_detail(request, template):
templates = []
for site_settings_module in settings.ADMIN_FOR:
- settings_mod = __import__(site_settings_module, '', '', [''])
+ settings_mod = __import__(site_settings_module, {}, {}, [''])
if Site._meta.installed:
site_obj = Site.objects.get(pk=settings_mod.SITE_ID)
else:
diff --git a/django/contrib/admin/views/template.py b/django/contrib/admin/views/template.py
index 1684870842..93d110b045 100644
--- a/django/contrib/admin/views/template.py
+++ b/django/contrib/admin/views/template.py
@@ -14,7 +14,7 @@ def template_validator(request):
# get a dict of {site_id : settings_module} for the validator
settings_modules = {}
for mod in settings.ADMIN_FOR:
- settings_module = __import__(mod, '', '', [''])
+ settings_module = __import__(mod, {}, {}, [''])
settings_modules[settings_module.SITE_ID] = settings_module
manipulator = TemplateValidator(settings_modules)
new_data, errors = {}, {}
diff --git a/django/contrib/auth/__init__.py b/django/contrib/auth/__init__.py
index a0097a01ed..dd3b8152e6 100644
--- a/django/contrib/auth/__init__.py
+++ b/django/contrib/auth/__init__.py
@@ -9,7 +9,7 @@ def load_backend(path):
i = path.rfind('.')
module, attr = path[:i], path[i+1:]
try:
- mod = __import__(module, '', '', [attr])
+ mod = __import__(module, {}, {}, [attr])
except ImportError, e:
raise ImproperlyConfigured, 'Error importing authentication backend %s: "%s"' % (module, e)
try:
diff --git a/django/contrib/comments/models.py b/django/contrib/comments/models.py
index a8aff1cfb3..90a84baaff 100644
--- a/django/contrib/comments/models.py
+++ b/django/contrib/comments/models.py
@@ -34,7 +34,7 @@ class CommentManager(models.Manager):
"""
Given a rating_string, this returns a tuple of (rating_range, options).
>>> s = "scale:1-10|First_category|Second_category"
- >>> get_rating_options(s)
+ >>> Comment.objects.get_rating_options(s)
([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ['First category', 'Second category'])
"""
rating_range, options = rating_string.split('|', 1)
diff --git a/django/contrib/sitemaps/templates/sitemap.xml b/django/contrib/sitemaps/templates/sitemap.xml
index ad24c045d4..16d9a0bbe0 100644
--- a/django/contrib/sitemaps/templates/sitemap.xml
+++ b/django/contrib/sitemaps/templates/sitemap.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<urlset xmlns="http://www.google.com/schemas/sitemap/0.84">
+<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
{% spaceless %}
{% for url in urlset %}
<url>
diff --git a/django/contrib/sitemaps/templates/sitemap_index.xml b/django/contrib/sitemaps/templates/sitemap_index.xml
index c89b192ecc..9f39bb879f 100644
--- a/django/contrib/sitemaps/templates/sitemap_index.xml
+++ b/django/contrib/sitemaps/templates/sitemap_index.xml
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
-<sitemapindex xmlns="http://www.google.com/schemas/sitemap/0.84">
+<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemapindex/0.9">
{% for location in sitemaps %}<sitemap><loc>{{ location|escape }}</loc></sitemap>{% endfor %}
</sitemapindex>
diff --git a/django/core/cache/__init__.py b/django/core/cache/__init__.py
index 17008c4637..6da8e883b9 100644
--- a/django/core/cache/__init__.py
+++ b/django/core/cache/__init__.py
@@ -48,7 +48,7 @@ def get_cache(backend_uri):
if host.endswith('/'):
host = host[:-1]
- cache_class = getattr(__import__('django.core.cache.backends.%s' % BACKENDS[scheme], '', '', ['']), 'CacheClass')
+ cache_class = getattr(__import__('django.core.cache.backends.%s' % BACKENDS[scheme], {}, {}, ['']), 'CacheClass')
return cache_class(host, params)
cache = get_cache(settings.CACHE_BACKEND)
diff --git a/django/core/handlers/base.py b/django/core/handlers/base.py
index 213c528952..c1403ea4fa 100644
--- a/django/core/handlers/base.py
+++ b/django/core/handlers/base.py
@@ -26,7 +26,7 @@ class BaseHandler(object):
raise exceptions.ImproperlyConfigured, '%s isn\'t a middleware module' % middleware_path
mw_module, mw_classname = middleware_path[:dot], middleware_path[dot+1:]
try:
- mod = __import__(mw_module, '', '', [''])
+ mod = __import__(mw_module, {}, {}, [''])
except ImportError, e:
raise exceptions.ImproperlyConfigured, 'Error importing middleware %s: "%s"' % (mw_module, e)
try:
diff --git a/django/core/handlers/wsgi.py b/django/core/handlers/wsgi.py
index 2998bd31f6..7dc1a4a5eb 100644
--- a/django/core/handlers/wsgi.py
+++ b/django/core/handlers/wsgi.py
@@ -157,7 +157,8 @@ class WSGIRequest(http.HttpRequest):
return self._raw_post_data
except AttributeError:
buf = StringIO()
- content_length = int(self.environ['CONTENT_LENGTH'])
+ # CONTENT_LENGTH might be absent if POST doesn't have content at all (lighttpd)
+ content_length = int(self.environ.get('CONTENT_LENGTH', 0))
safe_copyfileobj(self.environ['wsgi.input'], buf, size=content_length)
self._raw_post_data = buf.getvalue()
buf.close()
diff --git a/django/core/mail.py b/django/core/mail.py
index da4cacbe29..a5af6e610f 100644
--- a/django/core/mail.py
+++ b/django/core/mail.py
@@ -4,6 +4,11 @@ from django.conf import settings
from email.MIMEText import MIMEText
from email.Header import Header
import smtplib, rfc822
+import socket
+import time
+import random
+
+DNS_NAME = socket.getfqdn() # Cache the hostname
class BadHeaderError(ValueError):
pass
@@ -51,6 +56,11 @@ def send_mass_mail(datatuple, fail_silently=False, auth_user=settings.EMAIL_HOST
msg['To'] = ', '.join(recipient_list)
msg['Date'] = rfc822.formatdate()
try:
+ random_bits = str(random.getrandbits(64))
+ except AttributeError: # Python 2.3 doesn't have random.getrandbits().
+ random_bits = ''.join([random.choice('1234567890') for i in range(19)])
+ msg['Message-ID'] = "<%d.%s@%s>" % (time.time(), random_bits, DNS_NAME)
+ try:
server.sendmail(from_email, recipient_list, msg.as_string())
num_sent += 1
except:
diff --git a/django/core/management.py b/django/core/management.py
index 75b79f1565..d1a97c4a53 100644
--- a/django/core/management.py
+++ b/django/core/management.py
@@ -32,6 +32,7 @@ class dummy: pass
style = dummy()
style.ERROR = termcolors.make_style(fg='red', opts=('bold',))
style.ERROR_OUTPUT = termcolors.make_style(fg='red', opts=('bold',))
+style.NOTICE = termcolors.make_style(fg='red')
style.SQL_FIELD = termcolors.make_style(fg='green', opts=('bold',))
style.SQL_COLTYPE = termcolors.make_style(fg='green')
style.SQL_KEYWORD = termcolors.make_style(fg='yellow')
@@ -348,6 +349,8 @@ def get_sql_initial_data_for_model(model):
if os.path.exists(sql_file):
fp = open(sql_file, 'U')
for statement in statements.split(fp.read()):
+ # Remove any comments from the file
+ statement = re.sub(r"--.*[\n\Z]", "", statement)
if statement.strip():
output.append(statement + ";")
fp.close()
@@ -445,7 +448,7 @@ def syncdb(verbosity=1, interactive=True):
# dispatcher events.
for app_name in settings.INSTALLED_APPS:
try:
- __import__(app_name + '.management', '', '', [''])
+ __import__(app_name + '.management', {}, {}, [''])
except ImportError:
pass
@@ -696,7 +699,10 @@ def _start_helper(app_or_project, name, directory, other_name=''):
fp_new.write(fp_old.read().replace('{{ %s_name }}' % app_or_project, name).replace('{{ %s_name }}' % other, other_name))
fp_old.close()
fp_new.close()
- shutil.copymode(path_old, path_new)
+ try:
+ shutil.copymode(path_old, path_new)
+ except OSError:
+ sys.stderr.write(style.NOTICE("Notice: Couldn't set permission bits on %s. You're probably using an uncommon filesystem setup. No problem.\n" % path_new))
def startproject(project_name, directory):
"Creates a Django project for the given project_name in the given directory."
@@ -1226,7 +1232,7 @@ def test(app_labels, verbosity=1):
test_module_name = '.'.join(test_path[:-1])
else:
test_module_name = '.'
- test_module = __import__(test_module_name, [],[],test_path[-1])
+ test_module = __import__(test_module_name, {}, {}, test_path[-1])
test_runner = getattr(test_module, test_path[-1])
test_runner(app_list, verbosity)
@@ -1415,7 +1421,7 @@ def setup_environ(settings_mod):
project_directory = os.path.dirname(settings_mod.__file__)
project_name = os.path.basename(project_directory)
sys.path.append(os.path.join(project_directory, '..'))
- project_module = __import__(project_name, '', '', [''])
+ project_module = __import__(project_name, {}, {}, [''])
sys.path.pop()
# Set DJANGO_SETTINGS_MODULE appropriately.
diff --git a/django/core/paginator.py b/django/core/paginator.py
index 026fe0a675..380808a3dd 100644
--- a/django/core/paginator.py
+++ b/django/core/paginator.py
@@ -1,54 +1,46 @@
-from math import ceil
-
class InvalidPage(Exception):
pass
class ObjectPaginator(object):
"""
- This class makes pagination easy. Feed it a QuerySet, plus the number of
- objects you want on each page. Then read the hits and pages properties to
+ This class makes pagination easy. Feed it a QuerySet or list, plus the number
+ of objects you want on each page. Then read the hits and pages properties to
see how many pages it involves. Call get_page with a page number (starting
at 0) to get back a list of objects for that page.
Finally, check if a page number has a next/prev page using
has_next_page(page_number) and has_previous_page(page_number).
+
+ Use orphans to avoid small final pages. For example:
+ 13 records, num_per_page=10, orphans=2 --> pages==2, len(self.get_page(0))==10
+ 12 records, num_per_page=10, orphans=2 --> pages==1, len(self.get_page(0))==12
"""
- def __init__(self, query_set, num_per_page):
+ def __init__(self, query_set, num_per_page, orphans=0):
self.query_set = query_set
self.num_per_page = num_per_page
- self._hits, self._pages = None, None
- self._has_next = {} # Caches page_number -> has_next_boolean
+ self.orphans = orphans
+ self._hits = self._pages = None
- def get_page(self, page_number):
+ def validate_page_number(self, page_number):
try:
page_number = int(page_number)
except ValueError:
raise InvalidPage
- if page_number < 0:
+ if page_number < 0 or page_number > self.pages - 1:
raise InvalidPage
+ return page_number
- # Retrieve one extra record, and check for the existence of that extra
- # record to determine whether there's a next page.
- limit = self.num_per_page + 1
- offset = page_number * self.num_per_page
-
- object_list = list(self.query_set[offset:offset+limit])
-
- if not object_list:
- raise InvalidPage
-
- self._has_next[page_number] = (len(object_list) > self.num_per_page)
- return object_list[:self.num_per_page]
+ def get_page(self, page_number):
+ page_number = self.validate_page_number(page_number)
+ bottom = page_number * self.num_per_page
+ top = bottom + self.num_per_page
+ if top + self.orphans >= self.hits:
+ top = self.hits
+ return self.query_set[bottom:top]
def has_next_page(self, page_number):
"Does page $page_number have a 'next' page?"
- if not self._has_next.has_key(page_number):
- if self._pages is None:
- offset = (page_number + 1) * self.num_per_page
- self._has_next[page_number] = len(self.query_set[offset:offset+1]) > 0
- else:
- self._has_next[page_number] = page_number < (self.pages - 1)
- return self._has_next[page_number]
+ return page_number < self.pages - 1
def has_previous_page(self, page_number):
return page_number > 0
@@ -58,8 +50,7 @@ class ObjectPaginator(object):
Returns the 1-based index of the first object on the given page,
relative to total objects found (hits).
"""
- if page_number == 0:
- return 1
+ page_number = self.validate_page_number(page_number)
return (self.num_per_page * page_number) + 1
def last_on_page(self, page_number):
@@ -67,20 +58,30 @@ class ObjectPaginator(object):
Returns the 1-based index of the last object on the given page,
relative to total objects found (hits).
"""
- if page_number == 0 and self.num_per_page >= self._hits:
- return self._hits
- elif page_number == (self._pages - 1) and (page_number + 1) * self.num_per_page > self._hits:
- return self._hits
- return (page_number + 1) * self.num_per_page
+ page_number = self.validate_page_number(page_number)
+ page_number += 1 # 1-base
+ if page_number == self.pages:
+ return self.hits
+ return page_number * self.num_per_page
def _get_hits(self):
if self._hits is None:
- self._hits = self.query_set.count()
+ # Try .count() or fall back to len().
+ try:
+ self._hits = int(self.query_set.count())
+ except (AttributeError, TypeError, ValueError):
+ # AttributeError if query_set has no object count.
+ # TypeError if query_set.count() required arguments.
+ # ValueError if int() fails.
+ self._hits = len(self.query_set)
return self._hits
def _get_pages(self):
if self._pages is None:
- self._pages = int(ceil(self.hits / float(self.num_per_page)))
+ hits = (self.hits - 1 - self.orphans)
+ if hits < 1:
+ hits = 0
+ self._pages = hits // self.num_per_page + 1
return self._pages
hits = property(_get_hits)
diff --git a/django/core/serializers/__init__.py b/django/core/serializers/__init__.py
index 75e087ee1b..a1268321f2 100644
--- a/django/core/serializers/__init__.py
+++ b/django/core/serializers/__init__.py
@@ -29,7 +29,7 @@ _serializers = {}
def register_serializer(format, serializer_module):
"""Register a new serializer by passing in a module name."""
- module = __import__(serializer_module, '', '', [''])
+ module = __import__(serializer_module, {}, {}, [''])
_serializers[format] = module
def unregister_serializer(format):
diff --git a/django/core/serializers/base.py b/django/core/serializers/base.py
index fb293c7c13..5b0acdc480 100644
--- a/django/core/serializers/base.py
+++ b/django/core/serializers/base.py
@@ -28,6 +28,7 @@ class Serializer(object):
self.options = options
self.stream = options.get("stream", StringIO())
+ self.selected_fields = options.get("fields")
self.start_serialization()
for obj in queryset:
@@ -36,11 +37,14 @@ class Serializer(object):
if field is obj._meta.pk:
continue
elif field.rel is None:
- self.handle_field(obj, field)
+ if self.selected_fields is None or field.attname in self.selected_fields:
+ self.handle_field(obj, field)
else:
- self.handle_fk_field(obj, field)
+ if self.selected_fields is None or field.attname[:-3] in self.selected_fields:
+ self.handle_fk_field(obj, field)
for field in obj._meta.many_to_many:
- self.handle_m2m_field(obj, field)
+ if self.selected_fields is None or field.attname in self.selected_fields:
+ self.handle_m2m_field(obj, field)
self.end_object(obj)
self.end_serialization()
return self.getvalue()
diff --git a/django/core/serializers/python.py b/django/core/serializers/python.py
index 4181bc7f2b..859816c226 100644
--- a/django/core/serializers/python.py
+++ b/django/core/serializers/python.py
@@ -76,7 +76,7 @@ def Deserializer(object_list, **options):
m2m_data[field.name] = field.rel.to._default_manager.in_bulk(field_value).values()
# Handle FK fields
- elif field.rel and isinstance(field.rel, models.ManyToOneRel):
+ elif field.rel and isinstance(field.rel, models.ManyToOneRel) and field_value is not None:
try:
data[field.name] = field.rel.to._default_manager.get(pk=field_value)
except field.rel.to.DoesNotExist:
diff --git a/django/core/serializers/xml_serializer.py b/django/core/serializers/xml_serializer.py
index 09fff408cf..512b8c6176 100644
--- a/django/core/serializers/xml_serializer.py
+++ b/django/core/serializers/xml_serializer.py
@@ -166,7 +166,11 @@ class Deserializer(base.Deserializer):
# If it doesn't exist, set the field to None (which might trigger
# validation error, but that's expected).
RelatedModel = self._get_model_from_node(node, "to")
- return RelatedModel.objects.get(pk=getInnerText(node).strip().encode(self.encoding))
+ # Check if there is a child node named 'None', returning None if so.
+ if len(node.childNodes) == 1 and node.childNodes[0].nodeName == 'None':
+ return None
+ else:
+ return RelatedModel.objects.get(pk=getInnerText(node).strip().encode(self.encoding))
def _handle_m2m_field_node(self, node):
"""
diff --git a/django/core/servers/fastcgi.py b/django/core/servers/fastcgi.py
index c6507fe173..fccb7bf087 100644
--- a/django/core/servers/fastcgi.py
+++ b/django/core/servers/fastcgi.py
@@ -31,9 +31,11 @@ Optional Fcgi settings: (setting=value)
port=PORTNUM port to listen on.
socket=FILE UNIX socket to listen on.
method=IMPL prefork or threaded (default prefork)
- maxspare=NUMBER max number of spare processes to keep running.
- minspare=NUMBER min number of spare processes to prefork.
- maxchildren=NUMBER hard limit number of processes in prefork mode.
+ maxrequests=NUMBER number of requests a child handles before it is
+ killed and a new child is forked (0 = no limit).
+ maxspare=NUMBER max number of spare processes / threads
+ minspare=NUMBER min number of spare processes / threads.
+ maxchildren=NUMBER hard limit number of processes / threads
daemonize=BOOL whether to detach from terminal.
pidfile=FILE write the spawned process-id to this file.
workdir=DIRECTORY change to this directory when daemonizing
@@ -66,6 +68,7 @@ FASTCGI_OPTIONS = {
'maxspare': 5,
'minspare': 2,
'maxchildren': 50,
+ 'maxrequests': 0,
}
def fastcgi_help(message=None):
@@ -103,10 +106,15 @@ def runfastcgi(argset=[], **kwargs):
'maxSpare': int(options["maxspare"]),
'minSpare': int(options["minspare"]),
'maxChildren': int(options["maxchildren"]),
+ 'maxRequests': int(options["maxrequests"]),
}
elif options['method'] in ('thread', 'threaded'):
from flup.server.fcgi import WSGIServer
- wsgi_opts = {}
+ wsgi_opts = {
+ 'maxSpare': int(options["maxspare"]),
+ 'minSpare': int(options["minspare"]),
+ 'maxThreads': int(options["maxchildren"]),
+ }
else:
return fastcgi_help("ERROR: Implementation must be one of prefork or thread.")
diff --git a/django/core/urlresolvers.py b/django/core/urlresolvers.py
index 45705cb223..93c9c30cca 100644
--- a/django/core/urlresolvers.py
+++ b/django/core/urlresolvers.py
@@ -21,7 +21,10 @@ class NoReverseMatch(Exception):
def get_mod_func(callback):
# Converts 'django.views.news.stories.story_detail' to
# ['django.views.news.stories', 'story_detail']
- dot = callback.rindex('.')
+ try:
+ dot = callback.rindex('.')
+ except ValueError:
+ return callback, ''
return callback[:dot], callback[dot+1:]
def reverse_helper(regex, *args, **kwargs):
@@ -119,7 +122,7 @@ class RegexURLPattern(object):
return self._callback
mod_name, func_name = get_mod_func(self._callback_str)
try:
- self._callback = getattr(__import__(mod_name, '', '', ['']), func_name)
+ self._callback = getattr(__import__(mod_name, {}, {}, ['']), func_name)
except ImportError, e:
raise ViewDoesNotExist, "Could not import %s. Error was: %s" % (mod_name, str(e))
except AttributeError, e:
@@ -130,7 +133,7 @@ class RegexURLPattern(object):
def reverse(self, viewname, *args, **kwargs):
mod_name, func_name = get_mod_func(viewname)
try:
- lookup_view = getattr(__import__(mod_name, '', '', ['']), func_name)
+ lookup_view = getattr(__import__(mod_name, {}, {}, ['']), func_name)
except (ImportError, AttributeError):
raise NoReverseMatch
if lookup_view != self.callback:
@@ -171,7 +174,7 @@ class RegexURLResolver(object):
return self._urlconf_module
except AttributeError:
try:
- self._urlconf_module = __import__(self.urlconf_name, '', '', [''])
+ self._urlconf_module = __import__(self.urlconf_name, {}, {}, [''])
except ValueError, e:
# Invalid urlconf_name, such as "foo.bar." (note trailing period)
raise ImproperlyConfigured, "Error while importing URLconf %r: %s" % (self.urlconf_name, e)
@@ -186,7 +189,7 @@ class RegexURLResolver(object):
callback = getattr(self.urlconf_module, 'handler%s' % view_type)
mod_name, func_name = get_mod_func(callback)
try:
- return getattr(__import__(mod_name, '', '', ['']), func_name), {}
+ return getattr(__import__(mod_name, {}, {}, ['']), func_name), {}
except (ImportError, AttributeError), e:
raise ViewDoesNotExist, "Tried %s. Error was: %s" % (callback, str(e))
@@ -200,7 +203,7 @@ class RegexURLResolver(object):
if not callable(lookup_view):
mod_name, func_name = get_mod_func(lookup_view)
try:
- lookup_view = getattr(__import__(mod_name, '', '', ['']), func_name)
+ lookup_view = getattr(__import__(mod_name, {}, {}, ['']), func_name)
except (ImportError, AttributeError):
raise NoReverseMatch
for pattern in self.urlconf_module.urlpatterns:
diff --git a/django/core/validators.py b/django/core/validators.py
index 4c3f59143e..a2f3dc3bc3 100644
--- a/django/core/validators.py
+++ b/django/core/validators.py
@@ -8,6 +8,7 @@ validator will *always* be run, regardless of whether its associated
form field is required.
"""
+import urllib2
from django.conf import settings
from django.utils.translation import gettext, gettext_lazy, ngettext
from django.utils.functional import Promise, lazy
@@ -223,18 +224,26 @@ def isWellFormedXmlFragment(field_data, all_data):
isWellFormedXml('<root>%s</root>' % field_data, all_data)
def isExistingURL(field_data, all_data):
- import urllib2
try:
- u = urllib2.urlopen(field_data)
+ headers = {
+ "Accept" : "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5",
+ "Accept-Language" : "en-us,en;q=0.5",
+ "Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7",
+ "Connection" : "close",
+ "User-Agent": settings.URL_VALIDATOR_USER_AGENT
+ }
+ req = urllib2.Request(field_data,None, headers)
+ u = urllib2.urlopen(req)
except ValueError:
- raise ValidationError, gettext("Invalid URL: %s") % field_data
+ raise ValidationError, _("Invalid URL: %s") % field_data
except urllib2.HTTPError, e:
# 401s are valid; they just mean authorization is required.
- if e.code not in ('401',):
- raise ValidationError, gettext("The URL %s is a broken link.") % field_data
+ # 301 and 302 are redirects; they just mean look somewhere else.
+ if str(e.code) not in ('401','301','302'):
+ raise ValidationError, _("The URL %s is a broken link.") % field_data
except: # urllib2.URLError, httplib.InvalidURL, etc.
- raise ValidationError, gettext("The URL %s is a broken link.") % field_data
-
+ raise ValidationError, _("The URL %s is a broken link.") % field_data
+
def isValidUSState(field_data, all_data):
"Checks that the given string is a valid two-letter U.S. state abbreviation"
states = ['AA', 'AE', 'AK', 'AL', 'AP', 'AR', 'AS', 'AZ', 'CA', 'CO', 'CT', 'DC', 'DE', 'FL', 'FM', 'GA', 'GU', 'HI', 'IA', 'ID', 'IL', 'IN', 'KS', 'KY', 'LA', 'MA', 'MD', 'ME', 'MH', 'MI', 'MN', 'MO', 'MP', 'MS', 'MT', 'NC', 'ND', 'NE', 'NH', 'NJ', 'NM', 'NV', 'NY', 'OH', 'OK', 'OR', 'PA', 'PR', 'PW', 'RI', 'SC', 'SD', 'TN', 'TX', 'UT', 'VA', 'VI', 'VT', 'WA', 'WI', 'WV', 'WY']
@@ -344,6 +353,38 @@ class UniqueAmongstFieldsWithPrefix(object):
if field_name != self.field_name and value == field_data:
raise ValidationError, self.error_message
+class NumberIsInRange(object):
+ """
+ Validator that tests if a value is in a range (inclusive).
+ """
+ def __init__(self, lower=None, upper=None, error_message=''):
+ self.lower, self.upper = lower, upper
+ if not error_message:
+ if lower and upper:
+ self.error_message = gettext("This value must be between %s and %s.") % (lower, upper)
+ elif lower:
+ self.error_message = gettext("This value must be at least %s.") % lower
+ elif upper:
+ self.error_message = gettext("This value must be no more than %s.") % upper
+ else:
+ self.error_message = error_message
+
+ def __call__(self, field_data, all_data):
+ # Try to make the value numeric. If this fails, we assume another
+ # validator will catch the problem.
+ try:
+ val = float(field_data)
+ except ValueError:
+ return
+
+ # Now validate
+ if self.lower and self.upper and (val < self.lower or val > self.upper):
+ raise ValidationError(self.error_message)
+ elif self.lower and val < self.lower:
+ raise ValidationError(self.error_message)
+ elif self.upper and val > self.upper:
+ raise ValidationError(self.error_message)
+
class IsAPowerOf(object):
"""
>>> v = IsAPowerOf(2)
diff --git a/django/core/xheaders.py b/django/core/xheaders.py
index 69f6115839..3beb930158 100644
--- a/django/core/xheaders.py
+++ b/django/core/xheaders.py
@@ -17,6 +17,6 @@ def populate_xheaders(request, response, model, object_id):
or if the request is from a logged in staff member.
"""
from django.conf import settings
- if request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS or (request.user.is_authenticated() and request.user.is_staff):
+ if request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS or (hasattr(request, 'user') and request.user.is_authenticated() and request.user.is_staff):
response['X-Object-Type'] = "%s.%s" % (model._meta.app_label, model._meta.object_name.lower())
response['X-Object-Id'] = str(object_id)
diff --git a/django/db/__init__.py b/django/db/__init__.py
index f21ae6a727..4176b5aa79 100644
--- a/django/db/__init__.py
+++ b/django/db/__init__.py
@@ -8,7 +8,7 @@ if not settings.DATABASE_ENGINE:
settings.DATABASE_ENGINE = 'dummy'
try:
- backend = __import__('django.db.backends.%s.base' % settings.DATABASE_ENGINE, '', '', [''])
+ backend = __import__('django.db.backends.%s.base' % settings.DATABASE_ENGINE, {}, {}, [''])
except ImportError, e:
# The database backend wasn't found. Display a helpful error message
# listing all possible database backends.
@@ -18,16 +18,16 @@ except ImportError, e:
available_backends = [f for f in os.listdir(backend_dir) if not f.startswith('_') and not f.startswith('.') and not f.endswith('.py') and not f.endswith('.pyc')]
available_backends.sort()
if settings.DATABASE_ENGINE not in available_backends:
- raise ImproperlyConfigured, "%r isn't an available database backend. vailable options are: %s" % \
+ raise ImproperlyConfigured, "%r isn't an available database backend. Available options are: %s" % \
(settings.DATABASE_ENGINE, ", ".join(map(repr, available_backends)))
else:
raise # If there's some other error, this must be an error in Django itself.
-get_introspection_module = lambda: __import__('django.db.backends.%s.introspection' % settings.DATABASE_ENGINE, '', '', [''])
-get_creation_module = lambda: __import__('django.db.backends.%s.creation' % settings.DATABASE_ENGINE, '', '', [''])
-runshell = lambda: __import__('django.db.backends.%s.client' % settings.DATABASE_ENGINE, '', '', ['']).runshell()
+get_introspection_module = lambda: __import__('django.db.backends.%s.introspection' % settings.DATABASE_ENGINE, {}, {}, [''])
+get_creation_module = lambda: __import__('django.db.backends.%s.creation' % settings.DATABASE_ENGINE, {}, {}, [''])
+runshell = lambda: __import__('django.db.backends.%s.client' % settings.DATABASE_ENGINE, {}, {}, ['']).runshell()
-connection = backend.DatabaseWrapper()
+connection = backend.DatabaseWrapper(**settings.DATABASE_OPTIONS)
DatabaseError = backend.DatabaseError
# Register an event that closes the database connection
diff --git a/django/db/backends/ado_mssql/base.py b/django/db/backends/ado_mssql/base.py
index afe2d19981..72d2fe083e 100644
--- a/django/db/backends/ado_mssql/base.py
+++ b/django/db/backends/ado_mssql/base.py
@@ -55,7 +55,7 @@ except ImportError:
from django.utils._threading_local import local
class DatabaseWrapper(local):
- def __init__(self):
+ def __init__(self, **kwargs):
self.connection = None
self.queries = []
diff --git a/django/db/backends/dummy/base.py b/django/db/backends/dummy/base.py
index 985fe96469..f98afc48bb 100644
--- a/django/db/backends/dummy/base.py
+++ b/django/db/backends/dummy/base.py
@@ -20,6 +20,9 @@ class DatabaseWrapper:
_commit = complain
_rollback = complain
+ def __init__(self, **kwargs):
+ pass
+
def close(self):
pass # close()
diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py
index 04e5814988..e7e060e6c2 100644
--- a/django/db/backends/mysql/base.py
+++ b/django/db/backends/mysql/base.py
@@ -65,10 +65,11 @@ except ImportError:
from django.utils._threading_local import local
class DatabaseWrapper(local):
- def __init__(self):
+ def __init__(self, **kwargs):
self.connection = None
self.queries = []
self.server_version = None
+ self.options = kwargs
def _valid_connection(self):
if self.connection is not None:
@@ -95,6 +96,7 @@ class DatabaseWrapper(local):
kwargs['host'] = settings.DATABASE_HOST
if settings.DATABASE_PORT:
kwargs['port'] = int(settings.DATABASE_PORT)
+ kwargs.update(self.options)
self.connection = Database.connect(**kwargs)
cursor = self.connection.cursor()
if self.connection.get_server_info() >= '4.1':
diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py
index dfe2df11dc..3a13f39546 100644
--- a/django/db/backends/oracle/base.py
+++ b/django/db/backends/oracle/base.py
@@ -21,9 +21,10 @@ except ImportError:
from django.utils._threading_local import local
class DatabaseWrapper(local):
- def __init__(self):
+ def __init__(self, **kwargs):
self.connection = None
self.queries = []
+ self.options = kwargs
def _valid_connection(self):
return self.connection is not None
@@ -35,10 +36,10 @@ class DatabaseWrapper(local):
settings.DATABASE_HOST = 'localhost'
if len(settings.DATABASE_PORT.strip()) != 0:
dsn = Database.makedsn(settings.DATABASE_HOST, int(settings.DATABASE_PORT), settings.DATABASE_NAME)
- self.connection = Database.connect(settings.DATABASE_USER, settings.DATABASE_PASSWORD, dsn)
+ self.connection = Database.connect(settings.DATABASE_USER, settings.DATABASE_PASSWORD, dsn, **self.options)
else:
conn_string = "%s/%s@%s" % (settings.DATABASE_USER, settings.DATABASE_PASSWORD, settings.DATABASE_NAME)
- self.connection = Database.connect(conn_string)
+ self.connection = Database.connect(conn_string, **self.options)
return FormatStylePlaceholderCursor(self.connection)
def _commit(self):
diff --git a/django/db/backends/postgresql/base.py b/django/db/backends/postgresql/base.py
index 5355781e81..1e48e9c3fa 100644
--- a/django/db/backends/postgresql/base.py
+++ b/django/db/backends/postgresql/base.py
@@ -21,9 +21,10 @@ except ImportError:
from django.utils._threading_local import local
class DatabaseWrapper(local):
- def __init__(self):
+ def __init__(self, **kwargs):
self.connection = None
self.queries = []
+ self.options = kwargs
def cursor(self):
from django.conf import settings
@@ -40,7 +41,7 @@ class DatabaseWrapper(local):
conn_string += " host=%s" % settings.DATABASE_HOST
if settings.DATABASE_PORT:
conn_string += " port=%s" % settings.DATABASE_PORT
- self.connection = Database.connect(conn_string)
+ self.connection = Database.connect(conn_string, **self.options)
self.connection.set_isolation_level(1) # make transactions transparent to all cursors
cursor = self.connection.cursor()
cursor.execute("SET TIME ZONE %s", [settings.TIME_ZONE])
diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py
index 4c835d89fc..04322332dc 100644
--- a/django/db/backends/postgresql_psycopg2/base.py
+++ b/django/db/backends/postgresql_psycopg2/base.py
@@ -21,9 +21,10 @@ except ImportError:
from django.utils._threading_local import local
class DatabaseWrapper(local):
- def __init__(self):
+ def __init__(self, **kwargs):
self.connection = None
self.queries = []
+ self.options = kwargs
def cursor(self):
from django.conf import settings
@@ -40,7 +41,7 @@ class DatabaseWrapper(local):
conn_string += " host=%s" % settings.DATABASE_HOST
if settings.DATABASE_PORT:
conn_string += " port=%s" % settings.DATABASE_PORT
- self.connection = Database.connect(conn_string)
+ self.connection = Database.connect(conn_string, **self.options)
self.connection.set_isolation_level(1) # make transactions transparent to all cursors
cursor = self.connection.cursor()
cursor.tzinfo_factory = None
diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py
index 3fe7eb43ac..891320160f 100644
--- a/django/db/backends/sqlite3/base.py
+++ b/django/db/backends/sqlite3/base.py
@@ -42,16 +42,20 @@ except ImportError:
from django.utils._threading_local import local
class DatabaseWrapper(local):
- def __init__(self):
+ def __init__(self, **kwargs):
self.connection = None
self.queries = []
+ self.options = kwargs
def cursor(self):
from django.conf import settings
if self.connection is None:
- self.connection = Database.connect(settings.DATABASE_NAME,
- detect_types=Database.PARSE_DECLTYPES | Database.PARSE_COLNAMES)
-
+ kwargs = {
+ 'database': settings.DATABASE_NAME,
+ 'detect_types': Database.PARSE_DECLTYPES | Database.PARSE_COLNAMES,
+ }
+ kwargs.update(self.options)
+ self.connection = Database.connect(**kwargs)
# Register extract and date_trunc functions.
self.connection.create_function("django_extract", 2, _sqlite_extract)
self.connection.create_function("django_date_trunc", 2, _sqlite_date_trunc)
diff --git a/django/db/backends/util.py b/django/db/backends/util.py
index 3ec1b41485..d8f86fef4f 100644
--- a/django/db/backends/util.py
+++ b/django/db/backends/util.py
@@ -17,7 +17,7 @@ class CursorDebugWrapper(object):
if not isinstance(params, (tuple, dict)):
params = tuple(params)
self.db.queries.append({
- 'sql': sql % tuple(params),
+ 'sql': sql % params,
'time': "%.3f" % (stop - start),
})
diff --git a/django/db/models/base.py b/django/db/models/base.py
index ffc28f71c9..70569a2561 100644
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -381,24 +381,6 @@ class Model(object):
setattr(self, cachename, get_image_dimensions(filename))
return getattr(self, cachename)
- # Handles setting many-to-many related objects.
- # Example: Album.set_songs()
- def _set_related_many_to_many(self, rel_class, rel_field, id_list):
- id_list = map(int, id_list) # normalize to integers
- rel = rel_field.rel.to
- m2m_table = rel_field.m2m_db_table()
- this_id = self._get_pk_val()
- cursor = connection.cursor()
- cursor.execute("DELETE FROM %s WHERE %s = %%s" % \
- (backend.quote_name(m2m_table),
- backend.quote_name(rel_field.m2m_column_name())), [this_id])
- sql = "INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \
- (backend.quote_name(m2m_table),
- backend.quote_name(rel_field.m2m_column_name()),
- backend.quote_name(rel_field.m2m_reverse_name()))
- cursor.executemany(sql, [(this_id, i) for i in id_list])
- transaction.commit_unless_managed()
-
############################################
# HELPER FUNCTIONS (CURRIED MODEL METHODS) #
############################################
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index d82f38527d..bc1eaf0a6a 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -457,7 +457,9 @@ class DateField(Field):
def get_db_prep_save(self, value):
# Casts dates into string format for entry into database.
- if value is not None:
+ if isinstance(value, datetime.datetime):
+ value = value.date().strftime('%Y-%m-%d')
+ elif isinstance(value, datetime.date):
value = value.strftime('%Y-%m-%d')
return Field.get_db_prep_save(self, value)
@@ -487,12 +489,19 @@ class DateTimeField(DateField):
def get_db_prep_save(self, value):
# Casts dates into string format for entry into database.
- if value is not None:
+ if isinstance(value, datetime.datetime):
# MySQL will throw a warning if microseconds are given, because it
# doesn't support microseconds.
if settings.DATABASE_ENGINE == 'mysql' and hasattr(value, 'microsecond'):
value = value.replace(microsecond=0)
value = str(value)
+ elif isinstance(value, datetime.date):
+ # MySQL will throw a warning if microseconds are given, because it
+ # doesn't support microseconds.
+ if settings.DATABASE_ENGINE == 'mysql' and hasattr(value, 'microsecond'):
+ value = datetime.datetime(value.year, value.month, value.day, microsecond=0)
+ value = str(value)
+
return Field.get_db_prep_save(self, value)
def get_db_prep_lookup(self, lookup_type, value):
@@ -576,7 +585,7 @@ class FileField(Field):
# If the raw path is passed in, validate it's under the MEDIA_ROOT.
def isWithinMediaRoot(field_data, all_data):
f = os.path.abspath(os.path.join(settings.MEDIA_ROOT, field_data))
- if not f.startswith(os.path.normpath(settings.MEDIA_ROOT)):
+ if not f.startswith(os.path.abspath(os.path.normpath(settings.MEDIA_ROOT))):
raise validators.ValidationError, _("Enter a valid filename.")
field_list[1].validator_list.append(isWithinMediaRoot)
return field_list
diff --git a/django/db/models/loading.py b/django/db/models/loading.py
index 22f83bfd78..f4aff2438b 100644
--- a/django/db/models/loading.py
+++ b/django/db/models/loading.py
@@ -48,7 +48,7 @@ def get_app(app_label, emptyOK=False):
def load_app(app_name):
"Loads the app with the provided fully qualified name, and returns the model module."
global _app_list
- mod = __import__(app_name, '', '', ['models'])
+ mod = __import__(app_name, {}, {}, ['models'])
if not hasattr(mod, 'models'):
return None
if mod.models not in _app_list:
diff --git a/django/db/models/manipulators.py b/django/db/models/manipulators.py
index 875a5e3552..d0d438de1c 100644
--- a/django/db/models/manipulators.py
+++ b/django/db/models/manipulators.py
@@ -292,7 +292,7 @@ def manipulator_validator_unique_together(field_name_list, opts, self, field_dat
# This is really not going to work for fields that have different
# form fields, e.g. DateTime.
# This validation needs to occur after html2python to be effective.
- field_val = all_data.get(f.attname, None)
+ field_val = all_data.get(f.name, None)
if field_val is None:
# This will be caught by another validator, assuming the field
# doesn't have blank=True.
@@ -309,7 +309,7 @@ def manipulator_validator_unique_together(field_name_list, opts, self, field_dat
pass
else:
raise validators.ValidationError, _("%(object)s with this %(type)s already exists for the given %(field)s.") % \
- {'object': capfirst(opts.verbose_name), 'type': field_list[0].verbose_name, 'field': get_text_list([f.verbose_name for f in field_list[1:]], 'and')}
+ {'object': capfirst(opts.verbose_name), 'type': field_list[0].verbose_name, 'field': get_text_list([f.verbose_name for f in field_list[1:]], _('and'))}
def manipulator_validator_unique_for_date(from_field, date_field, opts, lookup_type, self, field_data, all_data):
from django.db.models.fields.related import ManyToOneRel
diff --git a/django/forms/__init__.py b/django/forms/__init__.py
index 5f47059f03..0b9ac05edb 100644
--- a/django/forms/__init__.py
+++ b/django/forms/__init__.py
@@ -108,8 +108,13 @@ class FormWrapper(object):
This allows dictionary-style lookups of formfields. It also handles feeding
prepopulated data and validation error messages to the formfield objects.
"""
- def __init__(self, manipulator, data, error_dict, edit_inline=True):
- self.manipulator, self.data = manipulator, data
+ def __init__(self, manipulator, data=None, error_dict=None, edit_inline=True):
+ self.manipulator = manipulator
+ if data is None:
+ data = {}
+ if error_dict is None:
+ error_dict = {}
+ self.data = data
self.error_dict = error_dict
self._inline_collections = None
self.edit_inline = edit_inline
diff --git a/django/middleware/gzip.py b/django/middleware/gzip.py
index 7d860abdb1..a7c74481d0 100644
--- a/django/middleware/gzip.py
+++ b/django/middleware/gzip.py
@@ -25,4 +25,5 @@ class GZipMiddleware(object):
response.content = compress_string(response.content)
response['Content-Encoding'] = 'gzip'
+ response['Content-Length'] = str(len(response.content))
return response
diff --git a/django/newforms/__init__.py b/django/newforms/__init__.py
new file mode 100644
index 0000000000..2a472d7b39
--- /dev/null
+++ b/django/newforms/__init__.py
@@ -0,0 +1,28 @@
+"""
+Django validation and HTML form handling.
+
+TODO:
+ Default value for field
+ Field labels
+ Nestable Forms
+ FatalValidationError -- short-circuits all other validators on a form
+ ValidationWarning
+ "This form field requires foo.js" and form.js_includes()
+"""
+
+from util import ValidationError
+from widgets import *
+from fields import *
+from forms import Form
+
+##########################
+# DATABASE API SHORTCUTS #
+##########################
+
+def form_for_model(model):
+ "Returns a Form instance for the given Django model class."
+ raise NotImplementedError
+
+def form_for_fields(field_list):
+ "Returns a Form instance for the given list of Django database field instances."
+ raise NotImplementedError
diff --git a/django/newforms/fields.py b/django/newforms/fields.py
new file mode 100644
index 0000000000..40fc18bd3e
--- /dev/null
+++ b/django/newforms/fields.py
@@ -0,0 +1,290 @@
+"""
+Field classes
+"""
+
+from util import ValidationError, DEFAULT_ENCODING, smart_unicode
+from widgets import TextInput, CheckboxInput, Select, SelectMultiple
+import datetime
+import re
+import time
+
+__all__ = (
+ 'Field', 'CharField', 'IntegerField',
+ 'DEFAULT_DATE_INPUT_FORMATS', 'DateField',
+ 'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField',
+ 'RegexField', 'EmailField', 'URLField', 'BooleanField',
+ 'ChoiceField', 'MultipleChoiceField',
+ 'ComboField',
+)
+
+# These values, if given to to_python(), will trigger the self.required check.
+EMPTY_VALUES = (None, '')
+
+try:
+ set # Only available in Python 2.4+
+except NameError:
+ from sets import Set as set # Python 2.3 fallback
+
+class Field(object):
+ widget = TextInput # Default widget to use when rendering this type of Field.
+
+ # Tracks each time a Field instance is created. Used to retain order.
+ creation_counter = 0
+
+ def __init__(self, required=True, widget=None):
+ self.required = required
+ widget = widget or self.widget
+ if isinstance(widget, type):
+ widget = widget()
+ self.widget = widget
+
+ # Increase the creation counter, and save our local copy.
+ self.creation_counter = Field.creation_counter
+ Field.creation_counter += 1
+
+ def clean(self, value):
+ """
+ Validates the given value and returns its "cleaned" value as an
+ appropriate Python object.
+
+ Raises ValidationError for any errors.
+ """
+ if self.required and value in EMPTY_VALUES:
+ raise ValidationError(u'This field is required.')
+ return value
+
+class CharField(Field):
+ def __init__(self, max_length=None, min_length=None, required=True, widget=None):
+ Field.__init__(self, required, widget)
+ self.max_length, self.min_length = max_length, min_length
+
+ def clean(self, value):
+ "Validates max_length and min_length. Returns a Unicode object."
+ Field.clean(self, value)
+ if value in EMPTY_VALUES: value = u''
+ value = smart_unicode(value)
+ if self.max_length is not None and len(value) > self.max_length:
+ raise ValidationError(u'Ensure this value has at most %d characters.' % self.max_length)
+ if self.min_length is not None and len(value) < self.min_length:
+ raise ValidationError(u'Ensure this value has at least %d characters.' % self.min_length)
+ return value
+
+class IntegerField(Field):
+ def clean(self, value):
+ """
+ Validates that int() can be called on the input. Returns the result
+ of int().
+ """
+ super(IntegerField, self).clean(value)
+ try:
+ return int(value)
+ except (ValueError, TypeError):
+ raise ValidationError(u'Enter a whole number.')
+
+DEFAULT_DATE_INPUT_FORMATS = (
+ '%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06'
+ '%b %d %Y', '%b %d, %Y', # 'Oct 25 2006', 'Oct 25, 2006'
+ '%d %b %Y', '%d %b, %Y', # '25 Oct 2006', '25 Oct, 2006'
+ '%B %d %Y', '%B %d, %Y', # 'October 25 2006', 'October 25, 2006'
+ '%d %B %Y', '%d %B, %Y', # '25 October 2006', '25 October, 2006'
+)
+
+class DateField(Field):
+ def __init__(self, input_formats=None, required=True, widget=None):
+ Field.__init__(self, required, widget)
+ self.input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS
+
+ def clean(self, value):
+ """
+ Validates that the input can be converted to a date. Returns a Python
+ datetime.date object.
+ """
+ Field.clean(self, value)
+ if value in EMPTY_VALUES:
+ return None
+ if isinstance(value, datetime.datetime):
+ return value.date()
+ if isinstance(value, datetime.date):
+ return value
+ for format in self.input_formats:
+ try:
+ return datetime.date(*time.strptime(value, format)[:3])
+ except ValueError:
+ continue
+ raise ValidationError(u'Enter a valid date.')
+
+DEFAULT_DATETIME_INPUT_FORMATS = (
+ '%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59'
+ '%Y-%m-%d %H:%M', # '2006-10-25 14:30'
+ '%Y-%m-%d', # '2006-10-25'
+ '%m/%d/%Y %H:%M:%S', # '10/25/2006 14:30:59'
+ '%m/%d/%Y %H:%M', # '10/25/2006 14:30'
+ '%m/%d/%Y', # '10/25/2006'
+ '%m/%d/%y %H:%M:%S', # '10/25/06 14:30:59'
+ '%m/%d/%y %H:%M', # '10/25/06 14:30'
+ '%m/%d/%y', # '10/25/06'
+)
+
+class DateTimeField(Field):
+ def __init__(self, input_formats=None, required=True, widget=None):
+ Field.__init__(self, required, widget)
+ self.input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS
+
+ def clean(self, value):
+ """
+ Validates that the input can be converted to a datetime. Returns a
+ Python datetime.datetime object.
+ """
+ Field.clean(self, value)
+ if value in EMPTY_VALUES:
+ return None
+ if isinstance(value, datetime.datetime):
+ return value
+ if isinstance(value, datetime.date):
+ return datetime.datetime(value.year, value.month, value.day)
+ for format in self.input_formats:
+ try:
+ return datetime.datetime(*time.strptime(value, format)[:6])
+ except ValueError:
+ continue
+ raise ValidationError(u'Enter a valid date/time.')
+
+class RegexField(Field):
+ def __init__(self, regex, error_message=None, required=True, widget=None):
+ """
+ regex can be either a string or a compiled regular expression object.
+ error_message is an optional error message to use, if
+ 'Enter a valid value' is too generic for you.
+ """
+ Field.__init__(self, required, widget)
+ if isinstance(regex, basestring):
+ regex = re.compile(regex)
+ self.regex = regex
+ self.error_message = error_message or u'Enter a valid value.'
+
+ def clean(self, value):
+ """
+ Validates that the input matches the regular expression. Returns a
+ Unicode object.
+ """
+ Field.clean(self, value)
+ if value in EMPTY_VALUES: value = u''
+ value = smart_unicode(value)
+ if not self.regex.search(value):
+ raise ValidationError(self.error_message)
+ return value
+
+email_re = re.compile(
+ r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*" # dot-atom
+ r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-011\013\014\016-\177])*"' # quoted-string
+ r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain
+
+class EmailField(RegexField):
+ def __init__(self, required=True, widget=None):
+ RegexField.__init__(self, email_re, u'Enter a valid e-mail address.', required, widget)
+
+url_re = re.compile(
+ r'^https?://' # http:// or https://
+ r'(?:[A-Z0-9-]+\.)+[A-Z]{2,6}' # domain
+ r'(?::\d+)?' # optional port
+ r'(?:/?|/\S+)$', re.IGNORECASE)
+
+try:
+ from django.conf import settings
+ URL_VALIDATOR_USER_AGENT = settings.URL_VALIDATOR_USER_AGENT
+except ImportError:
+ # It's OK if Django settings aren't configured.
+ URL_VALIDATOR_USER_AGENT = 'Django (http://www.djangoproject.com/)'
+
+class URLField(RegexField):
+ def __init__(self, required=True, verify_exists=False, widget=None,
+ validator_user_agent=URL_VALIDATOR_USER_AGENT):
+ RegexField.__init__(self, url_re, u'Enter a valid URL.', required, widget)
+ self.verify_exists = verify_exists
+ self.user_agent = validator_user_agent
+
+ def clean(self, value):
+ value = RegexField.clean(self, value)
+ if self.verify_exists:
+ import urllib2
+ from django.conf import settings
+ headers = {
+ "Accept": "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5",
+ "Accept-Language": "en-us,en;q=0.5",
+ "Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7",
+ "Connection": "close",
+ "User-Agent": self.user_agent,
+ }
+ try:
+ req = urllib2.Request(value, None, headers)
+ u = urllib2.urlopen(req)
+ except ValueError:
+ raise ValidationError(u'Enter a valid URL.')
+ except: # urllib2.URLError, httplib.InvalidURL, etc.
+ raise ValidationError(u'This URL appears to be a broken link.')
+ return value
+
+class BooleanField(Field):
+ widget = CheckboxInput
+
+ def clean(self, value):
+ "Returns a Python boolean object."
+ Field.clean(self, value)
+ return bool(value)
+
+class ChoiceField(Field):
+ def __init__(self, choices=(), required=True, widget=Select):
+ if isinstance(widget, type):
+ widget = widget(choices=choices)
+ Field.__init__(self, required, widget)
+ self.choices = choices
+
+ def clean(self, value):
+ """
+ Validates that the input is in self.choices.
+ """
+ value = Field.clean(self, value)
+ if value in EMPTY_VALUES: value = u''
+ value = smart_unicode(value)
+ valid_values = set([str(k) for k, v in self.choices])
+ if value not in valid_values:
+ raise ValidationError(u'Select a valid choice. %s is not one of the available choices.' % value)
+ return value
+
+class MultipleChoiceField(ChoiceField):
+ def __init__(self, choices=(), required=True, widget=SelectMultiple):
+ ChoiceField.__init__(self, choices, required, widget)
+
+ def clean(self, value):
+ """
+ Validates that the input is a list or tuple.
+ """
+ if not isinstance(value, (list, tuple)):
+ raise ValidationError(u'Enter a list of values.')
+ if self.required and not value:
+ raise ValidationError(u'This field is required.')
+ new_value = []
+ for val in value:
+ val = smart_unicode(val)
+ new_value.append(val)
+ # Validate that each value in the value list is in self.choices.
+ valid_values = set([k for k, v in self.choices])
+ for val in new_value:
+ if val not in valid_values:
+ raise ValidationError(u'Select a valid choice. %s is not one of the available choices.' % val)
+ return new_value
+
+class ComboField(Field):
+ def __init__(self, fields=(), required=True, widget=None):
+ Field.__init__(self, required, widget)
+ self.fields = fields
+
+ def clean(self, value):
+ """
+ Validates the given value against all of self.fields, which is a
+ list of Field instances.
+ """
+ Field.clean(self, value)
+ for field in self.fields:
+ value = field.clean(value)
+ return value
diff --git a/django/newforms/forms.py b/django/newforms/forms.py
new file mode 100644
index 0000000000..b8264fb691
--- /dev/null
+++ b/django/newforms/forms.py
@@ -0,0 +1,197 @@
+"""
+Form classes
+"""
+
+from django.utils.datastructures import SortedDict
+from fields import Field
+from widgets import TextInput, Textarea
+from util import ErrorDict, ErrorList, ValidationError
+
+NON_FIELD_ERRORS = '__all__'
+
+def pretty_name(name):
+ "Converts 'first_name' to 'First name'"
+ name = name[0].upper() + name[1:]
+ return name.replace('_', ' ')
+
+class SortedDictFromList(SortedDict):
+ "A dictionary that keeps its keys in the order in which they're inserted."
+ # This is different than django.utils.datastructures.SortedDict, because
+ # this takes a list/tuple as the argument to __init__().
+ def __init__(self, data=None):
+ if data is None: data = []
+ self.keyOrder = [d[0] for d in data]
+ dict.__init__(self, dict(data))
+
+class DeclarativeFieldsMetaclass(type):
+ "Metaclass that converts Field attributes to a dictionary called 'fields'."
+ def __new__(cls, name, bases, attrs):
+ fields = [(name, attrs.pop(name)) for name, obj in attrs.items() if isinstance(obj, Field)]
+ fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter))
+ attrs['fields'] = SortedDictFromList(fields)
+ return type.__new__(cls, name, bases, attrs)
+
+class Form(object):
+ "A collection of Fields, plus their associated data."
+ __metaclass__ = DeclarativeFieldsMetaclass
+
+ def __init__(self, data=None, auto_id=False): # TODO: prefix stuff
+ self.data = data or {}
+ self.auto_id = auto_id
+ self.clean_data = None # Stores the data after clean() has been called.
+ self.__errors = None # Stores the errors after clean() has been called.
+
+ def __str__(self):
+ return self.as_table()
+
+ def __iter__(self):
+ for name, field in self.fields.items():
+ yield BoundField(self, field, name)
+
+ def __getitem__(self, name):
+ "Returns a BoundField with the given name."
+ try:
+ field = self.fields[name]
+ except KeyError:
+ raise KeyError('Key %r not found in Form' % name)
+ return BoundField(self, field, name)
+
+ def clean(self):
+ if self.__errors is None:
+ self.full_clean()
+ return self.clean_data
+
+ def errors(self):
+ "Returns an ErrorDict for self.data"
+ if self.__errors is None:
+ self.full_clean()
+ return self.__errors
+
+ def is_valid(self):
+ """
+ Returns True if the form has no errors. Otherwise, False. This exists
+ solely for convenience, so client code can use positive logic rather
+ than confusing negative logic ("if not form.errors()").
+ """
+ return not bool(self.errors())
+
+ def as_table(self):
+ "Returns this form rendered as HTML <tr>s -- excluding the <table></table>."
+ return u'\n'.join(['<tr><td>%s:</td><td>%s</td></tr>' % (pretty_name(name), BoundField(self, field, name)) for name, field in self.fields.items()])
+
+ def as_ul(self):
+ "Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
+ return u'\n'.join(['<li>%s: %s</li>' % (pretty_name(name), BoundField(self, field, name)) for name, field in self.fields.items()])
+
+ def as_table_with_errors(self):
+ "Returns this form rendered as HTML <tr>s, with errors."
+ output = []
+ if self.errors().get(NON_FIELD_ERRORS):
+ # Errors not corresponding to a particular field are displayed at the top.
+ output.append('<tr><td colspan="2"><ul>%s</ul></td></tr>' % '\n'.join(['<li>%s</li>' % e for e in self.errors()[NON_FIELD_ERRORS]]))
+ for name, field in self.fields.items():
+ bf = BoundField(self, field, name)
+ if bf.errors:
+ output.append('<tr><td colspan="2"><ul>%s</ul></td></tr>' % '\n'.join(['<li>%s</li>' % e for e in bf.errors]))
+ output.append('<tr><td>%s:</td><td>%s</td></tr>' % (pretty_name(name), bf))
+ return u'\n'.join(output)
+
+ def as_ul_with_errors(self):
+ "Returns this form rendered as HTML <li>s, with errors."
+ output = []
+ if self.errors().get(NON_FIELD_ERRORS):
+ # Errors not corresponding to a particular field are displayed at the top.
+ output.append('<li><ul>%s</ul></li>' % '\n'.join(['<li>%s</li>' % e for e in self.errors()[NON_FIELD_ERRORS]]))
+ for name, field in self.fields.items():
+ bf = BoundField(self, field, name)
+ line = '<li>'
+ if bf.errors:
+ line += '<ul>%s</ul>' % '\n'.join(['<li>%s</li>' % e for e in bf.errors])
+ line += '%s: %s</li>' % (pretty_name(name), bf)
+ output.append(line)
+ return u'\n'.join(output)
+
+ def full_clean(self):
+ """
+ Cleans all of self.data and populates self.__errors and self.clean_data.
+ """
+ self.clean_data = {}
+ errors = ErrorDict()
+ for name, field in self.fields.items():
+ value = self.data.get(name, None)
+ try:
+ value = field.clean(value)
+ self.clean_data[name] = value
+ if hasattr(self, 'clean_%s' % name):
+ value = getattr(self, 'clean_%s' % name)()
+ self.clean_data[name] = value
+ except ValidationError, e:
+ errors[name] = e.messages
+ try:
+ self.clean_data = self.clean()
+ except ValidationError, e:
+ errors[NON_FIELD_ERRORS] = e.messages
+ if errors:
+ self.clean_data = None
+ self.__errors = errors
+
+ def clean(self):
+ """
+ Hook for doing any extra form-wide cleaning after Field.clean() been
+ called on every field.
+ """
+ return self.clean_data
+
+class BoundField(object):
+ "A Field plus data"
+ def __init__(self, form, field, name):
+ self._form = form
+ self._field = field
+ self._name = name
+
+ def __str__(self):
+ "Renders this field as an HTML widget."
+ # Use the 'widget' attribute on the field to determine which type
+ # of HTML widget to use.
+ return self.as_widget(self._field.widget)
+
+ def _errors(self):
+ """
+ Returns an ErrorList for this field. Returns an empty ErrorList
+ if there are none.
+ """
+ try:
+ return self._form.errors()[self._name]
+ except KeyError:
+ return ErrorList()
+ errors = property(_errors)
+
+ def as_widget(self, widget, attrs=None):
+ attrs = attrs or {}
+ auto_id = self.auto_id
+ if not attrs.has_key('id') and not widget.attrs.has_key('id') and auto_id:
+ attrs['id'] = auto_id
+ return widget.render(self._name, self._form.data.get(self._name, None), attrs=attrs)
+
+ def as_text(self, attrs=None):
+ """
+ Returns a string of HTML for representing this as an <input type="text">.
+ """
+ return self.as_widget(TextInput(), attrs)
+
+ def as_textarea(self, attrs=None):
+ "Returns a string of HTML for representing this as a <textarea>."
+ return self.as_widget(Textarea(), attrs)
+
+ def _auto_id(self):
+ """
+ Calculates and returns the ID attribute for this BoundField, if the
+ associated Form has specified auto_id. Returns an empty string otherwise.
+ """
+ auto_id = self._form.auto_id
+ if auto_id and '%s' in str(auto_id):
+ return str(auto_id) % self._name
+ elif auto_id:
+ return self._name
+ return ''
+ auto_id = property(_auto_id)
diff --git a/django/newforms/util.py b/django/newforms/util.py
new file mode 100644
index 0000000000..a5cc4932ea
--- /dev/null
+++ b/django/newforms/util.py
@@ -0,0 +1,57 @@
+# Default encoding for input byte strings.
+DEFAULT_ENCODING = 'utf-8' # TODO: First look at django.conf.settings, then fall back to this.
+
+def smart_unicode(s):
+ if not isinstance(s, basestring):
+ s = unicode(str(s))
+ elif not isinstance(s, unicode):
+ s = unicode(s, DEFAULT_ENCODING)
+ return s
+
+class ErrorDict(dict):
+ """
+ A collection of errors that knows how to display itself in various formats.
+
+ The dictionary keys are the field names, and the values are the errors.
+ """
+ def __str__(self):
+ return self.as_ul()
+
+ def as_ul(self):
+ if not self: return u''
+ return u'<ul class="errorlist">%s</ul>' % ''.join([u'<li>%s%s</li>' % (k, v) for k, v in self.items()])
+
+ def as_text(self):
+ return u'\n'.join([u'* %s\n%s' % (k, u'\n'.join([u' * %s' % i for i in v])) for k, v in self.items()])
+
+class ErrorList(list):
+ """
+ A collection of errors that knows how to display itself in various formats.
+ """
+ def __str__(self):
+ return self.as_ul()
+
+ def as_ul(self):
+ if not self: return u''
+ return u'<ul class="errorlist">%s</ul>' % ''.join([u'<li>%s</li>' % e for e in self])
+
+ def as_text(self):
+ if not self: return u''
+ return u'\n'.join([u'* %s' % e for e in self])
+
+class ValidationError(Exception):
+ def __init__(self, message):
+ "ValidationError can be passed a string or a list."
+ if isinstance(message, list):
+ self.messages = ErrorList([smart_unicode(msg) for msg in message])
+ else:
+ assert isinstance(message, basestring), ("%s should be a basestring" % repr(message))
+ message = smart_unicode(message)
+ self.messages = ErrorList([message])
+
+ def __str__(self):
+ # This is needed because, without a __str__(), printing an exception
+ # instance would result in this:
+ # AttributeError: ValidationError instance has no attribute 'args'
+ # See http://www.python.org/doc/current/tut/node10.html#handling
+ return repr(self.messages)
diff --git a/django/newforms/widgets.py b/django/newforms/widgets.py
new file mode 100644
index 0000000000..318c76e55d
--- /dev/null
+++ b/django/newforms/widgets.py
@@ -0,0 +1,153 @@
+"""
+HTML Widget classes
+"""
+
+__all__ = (
+ 'Widget', 'TextInput', 'PasswordInput', 'HiddenInput', 'FileInput',
+ 'Textarea', 'CheckboxInput',
+ 'Select', 'SelectMultiple', 'RadioSelect',
+)
+
+from util import smart_unicode
+from django.utils.html import escape
+from itertools import chain
+
+try:
+ set # Only available in Python 2.4+
+except NameError:
+ from sets import Set as set # Python 2.3 fallback
+
+# Converts a dictionary to a single string with key="value", XML-style with
+# a leading space. Assumes keys do not need to be XML-escaped.
+flatatt = lambda attrs: u''.join([u' %s="%s"' % (k, escape(v)) for k, v in attrs.items()])
+
+class Widget(object):
+ requires_data_list = False # Determines whether render()'s 'value' argument should be a list.
+ def __init__(self, attrs=None):
+ self.attrs = attrs or {}
+
+ def render(self, name, value):
+ raise NotImplementedError
+
+ def build_attrs(self, extra_attrs=None, **kwargs):
+ attrs = dict(self.attrs, **kwargs)
+ if extra_attrs:
+ attrs.update(extra_attrs)
+ return attrs
+
+class Input(Widget):
+ """
+ Base class for all <input> widgets (except type='checkbox' and
+ type='radio', which are special).
+ """
+ input_type = None # Subclasses must define this.
+ def render(self, name, value, attrs=None):
+ if value is None: value = ''
+ final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
+ if value != '': final_attrs['value'] = smart_unicode(value) # Only add the 'value' attribute if a value is non-empty.
+ return u'<input%s />' % flatatt(final_attrs)
+
+class TextInput(Input):
+ input_type = 'text'
+
+class PasswordInput(Input):
+ input_type = 'password'
+
+class HiddenInput(Input):
+ input_type = 'hidden'
+
+class FileInput(Input):
+ input_type = 'file'
+
+class Textarea(Widget):
+ def render(self, name, value, attrs=None):
+ if value is None: value = ''
+ value = smart_unicode(value)
+ final_attrs = self.build_attrs(attrs, name=name)
+ return u'<textarea%s>%s</textarea>' % (flatatt(final_attrs), escape(value))
+
+class CheckboxInput(Widget):
+ def render(self, name, value, attrs=None):
+ final_attrs = self.build_attrs(attrs, type='checkbox', name=name)
+ if value: final_attrs['checked'] = 'checked'
+ return u'<input%s />' % flatatt(final_attrs)
+
+class Select(Widget):
+ def __init__(self, attrs=None, choices=()):
+ # choices can be any iterable
+ self.attrs = attrs or {}
+ self.choices = choices
+
+ def render(self, name, value, attrs=None, choices=()):
+ if value is None: value = ''
+ final_attrs = self.build_attrs(attrs, name=name)
+ output = [u'<select%s>' % flatatt(final_attrs)]
+ str_value = smart_unicode(value) # Normalize to string.
+ for option_value, option_label in chain(self.choices, choices):
+ option_value = smart_unicode(option_value)
+ selected_html = (option_value == str_value) and u' selected="selected"' or ''
+ output.append(u'<option value="%s"%s>%s</option>' % (escape(option_value), selected_html, escape(smart_unicode(option_label))))
+ output.append(u'</select>')
+ return u'\n'.join(output)
+
+class SelectMultiple(Widget):
+ requires_data_list = True
+ def __init__(self, attrs=None, choices=()):
+ # choices can be any iterable
+ self.attrs = attrs or {}
+ self.choices = choices
+
+ def render(self, name, value, attrs=None, choices=()):
+ if value is None: value = []
+ final_attrs = self.build_attrs(attrs, name=name)
+ output = [u'<select multiple="multiple"%s>' % flatatt(final_attrs)]
+ str_values = set([smart_unicode(v) for v in value]) # Normalize to strings.
+ for option_value, option_label in chain(self.choices, choices):
+ option_value = smart_unicode(option_value)
+ selected_html = (option_value in str_values) and ' selected="selected"' or ''
+ output.append(u'<option value="%s"%s>%s</option>' % (escape(option_value), selected_html, escape(smart_unicode(option_label))))
+ output.append(u'</select>')
+ return u'\n'.join(output)
+
+class RadioInput(object):
+ "An object used by RadioFieldRenderer that represents a single <input type='radio'>."
+ def __init__(self, name, value, attrs, choice):
+ self.name, self.value = name, value
+ self.attrs = attrs or {}
+ self.choice_value, self.choice_label = choice
+
+ def __str__(self):
+ return u'<label>%s %s</label>' % (self.tag(), self.choice_label)
+
+ def is_checked(self):
+ return self.value == smart_unicode(self.choice_value)
+
+ def tag(self):
+ final_attrs = dict(self.attrs, type='radio', name=self.name, value=self.choice_value)
+ if self.is_checked():
+ final_attrs['checked'] = 'checked'
+ return u'<input%s />' % flatatt(final_attrs)
+
+class RadioFieldRenderer(object):
+ "An object used by RadioSelect to enable customization of radio widgets."
+ def __init__(self, name, value, attrs, choices):
+ self.name, self.value, self.attrs = name, value, attrs
+ self.choices = choices
+
+ def __iter__(self):
+ for choice in self.choices:
+ yield RadioInput(self.name, self.value, self.attrs, choice)
+
+ def __str__(self):
+ "Outputs a <ul> for this set of radio fields."
+ return u'<ul>\n%s\n</ul>' % u'\n'.join([u'<li>%s</li>' % w for w in self])
+
+class RadioSelect(Select):
+ def render(self, name, value, attrs=None, choices=()):
+ "Returns a RadioFieldRenderer instance rather than a Unicode string."
+ if value is None: value = ''
+ str_value = smart_unicode(value) # Normalize to string.
+ return RadioFieldRenderer(name, str_value, attrs, list(chain(self.choices, choices)))
+
+class CheckboxSelectMultiple(Widget):
+ pass
diff --git a/django/template/__init__.py b/django/template/__init__.py
index 60526b663d..5affafeba9 100644
--- a/django/template/__init__.py
+++ b/django/template/__init__.py
@@ -868,8 +868,11 @@ class Library(object):
dict = func(*args)
if not getattr(self, 'nodelist', False):
- from django.template.loader import get_template
- t = get_template(file_name)
+ from django.template.loader import get_template, select_template
+ if hasattr(file_name, '__iter__'):
+ t = select_template(file_name)
+ else:
+ t = get_template(file_name)
self.nodelist = t.nodelist
return self.nodelist.render(context_class(dict))
@@ -883,7 +886,7 @@ def get_library(module_name):
lib = libraries.get(module_name, None)
if not lib:
try:
- mod = __import__(module_name, '', '', [''])
+ mod = __import__(module_name, {}, {}, [''])
except ImportError, e:
raise InvalidTemplateLibrary, "Could not load template library from %s, %s" % (module_name, e)
try:
diff --git a/django/template/context.py b/django/template/context.py
index 6d9efdc7ec..ba23e95ab7 100644
--- a/django/template/context.py
+++ b/django/template/context.py
@@ -69,7 +69,7 @@ def get_standard_processors():
i = path.rfind('.')
module, attr = path[:i], path[i+1:]
try:
- mod = __import__(module, '', '', [attr])
+ mod = __import__(module, {}, {}, [attr])
except ImportError, e:
raise ImproperlyConfigured, 'Error importing request processor module %s: "%s"' % (module, e)
try:
diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py
index cf1d3d5f6d..969ef7b28b 100644
--- a/django/template/defaultfilters.py
+++ b/django/template/defaultfilters.py
@@ -421,7 +421,11 @@ def filesizeformat(bytes):
Format the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB, 102
bytes, etc).
"""
- bytes = float(bytes)
+ try:
+ bytes = float(bytes)
+ except TypeError:
+ return "0 bytes"
+
if bytes < 1024:
return "%d byte%s" % (bytes, bytes != 1 and 's' or '')
if bytes < 1024 * 1024:
diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py
index 6144267c1a..3f3f4bda56 100644
--- a/django/template/defaulttags.py
+++ b/django/template/defaulttags.py
@@ -124,17 +124,27 @@ class ForNode(Node):
return nodelist.render(context)
class IfChangedNode(Node):
- def __init__(self, nodelist):
+ def __init__(self, nodelist, *varlist):
self.nodelist = nodelist
self._last_seen = None
+ self._varlist = varlist
def render(self, context):
if context.has_key('forloop') and context['forloop']['first']:
self._last_seen = None
- content = self.nodelist.render(context)
- if content != self._last_seen:
+ try:
+ if self._varlist:
+ # Consider multiple parameters.
+ # This automatically behaves like a OR evaluation of the multiple variables.
+ compare_to = [resolve_variable(var, context) for var in self._varlist]
+ else:
+ compare_to = self.nodelist.render(context)
+ except VariableDoesNotExist:
+ compare_to = None
+
+ if compare_to != self._last_seen:
firstloop = (self._last_seen == None)
- self._last_seen = content
+ self._last_seen = compare_to
context.push()
context['ifchanged'] = {'firstloop': firstloop}
content = self.nodelist.render(context)
@@ -634,23 +644,34 @@ def ifchanged(parser, token):
"""
Check if a value has changed from the last iteration of a loop.
- The 'ifchanged' block tag is used within a loop. It checks its own rendered
- contents against its previous state and only displays its content if the
- value has changed::
+ The 'ifchanged' block tag is used within a loop. It has two possible uses.
- <h1>Archive for {{ year }}</h1>
+ 1. Checks its own rendered contents against its previous state and only
+ displays the content if it has changed. For example, this displays a list of
+ days, only displaying the month if it changes::
- {% for date in days %}
- {% ifchanged %}<h3>{{ date|date:"F" }}</h3>{% endifchanged %}
- <a href="{{ date|date:"M/d"|lower }}/">{{ date|date:"j" }}</a>
- {% endfor %}
+ <h1>Archive for {{ year }}</h1>
+
+ {% for date in days %}
+ {% ifchanged %}<h3>{{ date|date:"F" }}</h3>{% endifchanged %}
+ <a href="{{ date|date:"M/d"|lower }}/">{{ date|date:"j" }}</a>
+ {% endfor %}
+
+ 2. If given a variable, check whether that variable has changed. For example, the
+ following shows the date every time it changes, but only shows the hour if both
+ the hour and the date have changed::
+
+ {% for date in days %}
+ {% ifchanged date.date %} {{ date.date }} {% endifchanged %}
+ {% ifchanged date.hour date.date %}
+ {{ date.hour }}
+ {% endifchanged %}
+ {% endfor %}
"""
bits = token.contents.split()
- if len(bits) != 1:
- raise TemplateSyntaxError, "'ifchanged' tag takes no arguments"
nodelist = parser.parse(('endifchanged',))
parser.delete_first_token()
- return IfChangedNode(nodelist)
+ return IfChangedNode(nodelist, *bits[1:])
ifchanged = register.tag(ifchanged)
#@register.tag
diff --git a/django/template/loaders/app_directories.py b/django/template/loaders/app_directories.py
index 8a9bfef4b6..c4e91df929 100644
--- a/django/template/loaders/app_directories.py
+++ b/django/template/loaders/app_directories.py
@@ -15,9 +15,9 @@ for app in settings.INSTALLED_APPS:
m, a = app[:i], app[i+1:]
try:
if a is None:
- mod = __import__(m, '', '', [])
+ mod = __import__(m, {}, {}, [])
else:
- mod = getattr(__import__(m, '', '', [a]), a)
+ mod = getattr(__import__(m, {}, {}, [a]), a)
except ImportError, e:
raise ImproperlyConfigured, 'ImportError %s: %s' % (app, e.args[0])
template_dir = os.path.join(os.path.dirname(mod.__file__), 'templates')
diff --git a/django/templatetags/__init__.py b/django/templatetags/__init__.py
index 62374577ea..9204535abb 100644
--- a/django/templatetags/__init__.py
+++ b/django/templatetags/__init__.py
@@ -2,6 +2,6 @@ from django.conf import settings
for a in settings.INSTALLED_APPS:
try:
- __path__.extend(__import__(a + '.templatetags', '', '', ['']).__path__)
+ __path__.extend(__import__(a + '.templatetags', {}, {}, ['']).__path__)
except ImportError:
pass
diff --git a/django/test/simple.py b/django/test/simple.py
index 628fa464d2..88e6b49925 100644
--- a/django/test/simple.py
+++ b/django/test/simple.py
@@ -28,7 +28,7 @@ def build_suite(app_module):
# models module
try:
app_path = app_module.__name__.split('.')[:-1]
- test_module = __import__('.'.join(app_path + [TEST_MODULE]), [], [], TEST_MODULE)
+ test_module = __import__('.'.join(app_path + [TEST_MODULE]), {}, {}, TEST_MODULE)
suite.addTest(unittest.defaultTestLoader.loadTestsFromModule(test_module))
try:
diff --git a/django/views/debug.py b/django/views/debug.py
index a7f44d17b3..77b6c2fac2 100644
--- a/django/views/debug.py
+++ b/django/views/debug.py
@@ -75,7 +75,7 @@ def technical_500_response(request, exc_type, exc_value, tb):
loader_debug_info = []
for loader in template_source_loaders:
try:
- source_list_func = getattr(__import__(loader.__module__, '', '', ['get_template_sources']), 'get_template_sources')
+ source_list_func = getattr(__import__(loader.__module__, {}, {}, ['get_template_sources']), 'get_template_sources')
# NOTE: This assumes exc_value is the name of the template that
# the loader attempted to load.
template_list = [{'name': t, 'exists': os.path.exists(t)} \
diff --git a/django/views/generic/simple.py b/django/views/generic/simple.py
index 4571ef8605..355bd25ef8 100644
--- a/django/views/generic/simple.py
+++ b/django/views/generic/simple.py
@@ -2,12 +2,18 @@ from django.shortcuts import render_to_response
from django.template import RequestContext
from django.http import HttpResponse, HttpResponsePermanentRedirect, HttpResponseGone
-def direct_to_template(request, template, **kwargs):
+def direct_to_template(request, template, extra_context={}, **kwargs):
"""
Render a given template with any extra URL parameters in the context as
``{{ params }}``.
"""
- return render_to_response(template, {'params' : kwargs}, context_instance=RequestContext(request))
+ dictionary = {'params': kwargs}
+ for key, value in extra_context.items():
+ if callable(value):
+ dictionary[key] = value()
+ else:
+ dictionary[key] = value
+ return render_to_response(template, dictionary, context_instance=RequestContext(request))
def redirect_to(request, url, **kwargs):
"""
@@ -18,7 +24,7 @@ def redirect_to(request, url, **kwargs):
``/foo/<id>/`` to ``/bar/<id>/``, you could use the following URLconf::
urlpatterns = patterns('',
- ('^foo/(?p<id>\d+)/$', 'django.views.generic.simple.redirect_to', {'url' : '/bar/%(id)s/'}),
+ ('^foo/(?P<id>\d+)/$', 'django.views.generic.simple.redirect_to', {'url' : '/bar/%(id)s/'}),
)
If the given url is ``None``, a HttpResponseGone (410) will be issued.
diff --git a/docs/authentication.txt b/docs/authentication.txt
index 2a61ec82b5..08565e13e1 100644
--- a/docs/authentication.txt
+++ b/docs/authentication.txt
@@ -745,7 +745,7 @@ messages are made available in the `template context`_ as the template variable
{% if messages %}
<ul>
{% for message in messages %}
- <li>{{ message.message }}</li>
+ <li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
diff --git a/docs/forms.txt b/docs/forms.txt
index 4a4ba37289..ff192a4717 100644
--- a/docs/forms.txt
+++ b/docs/forms.txt
@@ -610,6 +610,15 @@ fails. If no message is passed in, a default message is used.
string "123" is less than the string "2", for example. If you don't want
string comparison here, you will need to write your own validator.
+``NumberIsInRange``
+ Takes two boundary numbers, ``lower`` and ``upper``, and checks that the
+ field is greater than ``lower`` (if given) and less than ``upper`` (if
+ given).
+
+ Both checks are inclusive. That is, ``NumberIsInRange(10, 20)`` will allow
+ values of both 10 and 20. This validator only checks numeric values
+ (e.g., float and integer values).
+
``IsAPowerOf``
Takes an integer argument and when called as a validator, checks that the
field being validated is a power of the integer.
diff --git a/docs/generic_views.txt b/docs/generic_views.txt
index 99a9b7cf6b..1736770a20 100644
--- a/docs/generic_views.txt
+++ b/docs/generic_views.txt
@@ -92,6 +92,14 @@ which is a dictionary of the parameters captured in the URL.
* ``template``: The full name of a template to use.
+**Optional arguments:**
+
+ * ``extra_context``: A dictionary of values to add to the template
+ context. By default, this is an empty dictionary. If a value in the
+ dictionary is callable, the generic view will call it
+ just before rendering the template. (**This is new in the
+ Django development version.**)
+
**Example:**
Given the following URL patterns::
@@ -171,7 +179,7 @@ a date in the *future* are not included unless you set ``allow_future`` to
template. By default, it's ``django.template.loader``.
* ``extra_context``: A dictionary of values to add to the template
- context. By default, this is an empty dictionary.
+ context. By default, this is an empty dictionary. If a value in the
dictionary is callable, the generic view will call it
just before rendering the template.
diff --git a/docs/modpython.txt b/docs/modpython.txt
index b88874d3d3..5177abf728 100644
--- a/docs/modpython.txt
+++ b/docs/modpython.txt
@@ -43,6 +43,11 @@ This tells Apache: "Use mod_python for any URL at or under '/mysite/', using the
Django mod_python handler." It passes the value of ``DJANGO_SETTINGS_MODULE``
so mod_python knows which settings to use.
+Note that we're using the ``<Location>`` directive, not the ``<Directory>``
+directive. The latter is used for pointing at places on your filesystem,
+whereas ``<Location>`` points at places in the URL structure of a Web site.
+``<Directory>`` would be meaningless here.
+
Also, if you've manually altered your ``PYTHONPATH`` to put your Django project
on it, you'll need to tell mod_python::
diff --git a/docs/settings.txt b/docs/settings.txt
index 272b20f753..ff1e2eeca2 100644
--- a/docs/settings.txt
+++ b/docs/settings.txt
@@ -265,6 +265,14 @@ Default: ``''`` (Empty string)
The name of the database to use. For SQLite, it's the full path to the database
file.
+DATABASE_OPTIONS
+----------------
+
+Default: ``{}`` (Empty dictionary)
+
+Extra parameters to use when connecting to the database. Consult backend
+module's document for available keywords.
+
DATABASE_PASSWORD
-----------------
@@ -814,6 +822,16 @@ manual configuration option (see below), Django will *not* touch the ``TZ``
environment variable, and it'll be up to you to ensure your processes are
running in the correct environment.
+URL_VALIDATOR_USER_AGENT
+------------------------
+
+Default: ``Django/<version> (http://www.djangoproject.com/)``
+
+The string to use as the ``User-Agent`` header when checking to see if URLs
+exist (see the ``verify_exists`` option on URLField_).
+
+.. URLField: ../model_api/#urlfield
+
USE_ETAGS
---------
diff --git a/docs/sitemaps.txt b/docs/sitemaps.txt
index fec65572f2..7414567b16 100644
--- a/docs/sitemaps.txt
+++ b/docs/sitemaps.txt
@@ -5,9 +5,9 @@ The sitemap framework
**New in Django development version**.
Django comes with a high-level sitemap-generating framework that makes
-creating `Google Sitemap`_ XML files easy.
+creating sitemap_ XML files easy.
-.. _Google Sitemap: http://www.google.com/webmasters/sitemaps/docs/en/protocol.html
+.. _sitemap: http://www.sitemaps.org/
Overview
========
@@ -55,11 +55,12 @@ URLconf_:
This tells Django to build a sitemap when a client accesses ``/sitemap.xml``.
-The name of the sitemap file is not important, but the location is. Google will
-only index links in your sitemap for the current URL level and below. For
-instance, if ``sitemap.xml`` lives in your root directory, it may reference any
-URL in your site. However, if your sitemap lives at ``/content/sitemap.xml``,
-it may only reference URLs that begin with ``/content/``.
+The name of the sitemap file is not important, but the location is. Search
+engines will only index links in your sitemap for the current URL level and
+below. For instance, if ``sitemap.xml`` lives in your root directory, it may
+reference any URL in your site. However, if your sitemap lives at
+``/content/sitemap.xml``, it may only reference URLs that begin with
+``/content/``.
The sitemap view takes an extra, required argument: ``{'sitemaps': sitemaps}``.
``sitemaps`` should be a dictionary that maps a short section label (e.g.,
@@ -199,9 +200,9 @@ If it's an attribute, its value should be either a string or float representing
the priority of *every* object returned by ``items()``.
Example values for ``priority``: ``0.4``, ``1.0``. The default priority of a
-page is ``0.5``. See Google's documentation for more documentation.
+page is ``0.5``. See the `sitemaps.org documentation`_ for more.
-.. _Google's documentation: http://www.google.com/webmasters/sitemaps/docs/en/protocol.html
+.. _sitemaps.org documentation: http://www.sitemaps.org/protocol.html#prioritydef
Shortcuts
=========
diff --git a/docs/static_files.txt b/docs/static_files.txt
index d8d90e52d5..55380a659b 100644
--- a/docs/static_files.txt
+++ b/docs/static_files.txt
@@ -31,7 +31,7 @@ How to do it
Just put this in your URLconf_::
- (r'^site_media/(.*)$', 'django.views.static.serve', {'document_root': '/path/to/media'}),
+ (r'^site_media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': '/path/to/media'}),
...where ``site_media`` is the URL where your media will be rooted, and
``/path/to/media`` is the filesystem root for your media.
@@ -60,7 +60,7 @@ listings for directories.
Example::
- (r'^site_media/(.*)$', 'django.views.static.serve', {'document_root': '/path/to/media', 'show_indexes': True}),
+ (r'^site_media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': '/path/to/media', 'show_indexes': True}),
You can customize the index view by creating a template called
``static/directory_index``. That template gets two objects in its context:
diff --git a/docs/syndication_feeds.txt b/docs/syndication_feeds.txt
index 225b67eb02..59a9022d9b 100644
--- a/docs/syndication_feeds.txt
+++ b/docs/syndication_feeds.txt
@@ -95,7 +95,7 @@ latest five news items::
from django.contrib.syndication.feeds import Feed
from chicagocrime.models import NewsItem
- class SiteNewsFeed(Feed):
+ class LatestEntries(Feed):
title = "Chicagocrime.org site news"
link = "/sitenews/"
description = "Updates on changes and additions to chicagocrime.org."
@@ -120,14 +120,14 @@ One thing's left to do. In an RSS feed, each ``<item>`` has a ``<title>``,
put into those elements.
* To specify the contents of ``<title>`` and ``<description>``, create
- `Django templates`_ called ``feeds/sitenews_title.html`` and
- ``feeds/sitenews_description.html``, where ``sitenews`` is the ``slug``
+ `Django templates`_ called ``feeds/latest_title.html`` and
+ ``feeds/latest_description.html``, where ``latest`` is the ``slug``
specified in the URLconf for the given feed. Note the ``.html`` extension
is required. The RSS system renders that template for each item, passing
it two template context variables:
* ``{{ obj }}`` -- The current object (one of whichever objects you
- returned in ``items()``).
+ returned in ``items()``).
* ``{{ site }}`` -- A ``django.models.core.sites.Site`` object
representing the current site. This is useful for
``{{ site.domain }}`` or ``{{ site.name }}``.
@@ -145,6 +145,16 @@ put into those elements.
Both ``get_absolute_url()`` and ``item_link()`` should return the item's
URL as a normal Python string.
+ * For the LatestEntries example above, we could have very simple feed templates:
+
+ * latest_title.html::
+
+ {{ obj.title }}
+
+ * latest_description.html::
+
+ {{ obj.description }}
+
.. _chicagocrime.org: http://www.chicagocrime.org/
.. _object-relational mapper: http://www.djangoproject.com/documentation/db_api/
.. _Django templates: http://www.djangoproject.com/documentation/templates/
diff --git a/docs/templates.txt b/docs/templates.txt
index 92d4b53660..b4cc47b9f3 100644
--- a/docs/templates.txt
+++ b/docs/templates.txt
@@ -473,7 +473,7 @@ block are output::
In the above, if ``athlete_list`` is not empty, the number of athletes will be
displayed by the ``{{ athlete_list|length }}`` variable.
-As you can see, the ``if`` tag can take an option ``{% else %}`` clause that
+As you can see, the ``if`` tag can take an optional ``{% else %}`` clause that
will be displayed if the test fails.
``if`` tags may use ``and``, ``or`` or ``not`` to test a number of variables or
@@ -525,16 +525,29 @@ ifchanged
Check if a value has changed from the last iteration of a loop.
-The ``ifchanged`` block tag is used within a loop. It checks its own rendered
-contents against its previous state and only displays its content if the value
-has changed::
+The 'ifchanged' block tag is used within a loop. It has two possible uses.
- <h1>Archive for {{ year }}</h1>
+1. Checks its own rendered contents against its previous state and only
+ displays the content if it has changed. For example, this displays a list of
+ days, only displaying the month if it changes::
- {% for day in days %}
- {% ifchanged %}<h3>{{ day|date:"F" }}</h3>{% endifchanged %}
- <a href="{{ day|date:"M/d"|lower }}/">{{ day|date:"j" }}</a>
- {% endfor %}
+ <h1>Archive for {{ year }}</h1>
+
+ {% for date in days %}
+ {% ifchanged %}<h3>{{ date|date:"F" }}</h3>{% endifchanged %}
+ <a href="{{ date|date:"M/d"|lower }}/">{{ date|date:"j" }}</a>
+ {% endfor %}
+
+2. **New in Django development version.** If given a variable, check whether that
+ variable has changed. For example, the following shows the date every time it
+ changes, but only shows the hour if both the hour and the date has changed::
+
+ {% for date in days %}
+ {% ifchanged date.date %} {{ date.date }} {% endifchanged %}
+ {% ifchanged date.hour date.date %}
+ {{ date.hour }}
+ {% endifchanged %}
+ {% endfor %}
ifequal
~~~~~~~
@@ -558,7 +571,7 @@ The arguments can be hard-coded strings, so the following is valid::
It is only possible to compare an argument to template variables or strings.
You cannot check for equality with Python objects such as ``True`` or
``False``. If you need to test if something is true or false, use the ``if``
-and ``ifnot`` tags instead.
+tag instead.
ifnotequal
~~~~~~~~~~
diff --git a/docs/templates_python.txt b/docs/templates_python.txt
index ae2582d7b8..7aeed935b9 100644
--- a/docs/templates_python.txt
+++ b/docs/templates_python.txt
@@ -321,7 +321,7 @@ Note::
def some_view(request):
# ...
- return render_to_response('my_template'html',
+ return render_to_response('my_template.html',
my_data_dictionary,
context_instance=RequestContext(request))
diff --git a/docs/testing.txt b/docs/testing.txt
index 19eef9f071..68eff07788 100644
--- a/docs/testing.txt
+++ b/docs/testing.txt
@@ -10,7 +10,7 @@ used to validate that code behaves as expected. When refactoring or
modifying code, tests serve as a guide to ensure that behavior hasn't
changed unexpectedly as a result of the refactor.
-Testing an web application is a complex task, as there are many
+Testing a web application is a complex task, as there are many
components of a web application that must be validated and tested. To
help you test your application, Django provides a test execution
framework, and range of utilities that can be used to stimulate and
diff --git a/docs/tutorial04.txt b/docs/tutorial04.txt
index c5e2ea3cea..49ed649cff 100644
--- a/docs/tutorial04.txt
+++ b/docs/tutorial04.txt
@@ -192,13 +192,13 @@ objects" and "display a detail page for a particular type of object."
``object_id`` for the generic views.
By default, the ``object_detail`` generic view uses a template called
-``<app name>/<module name>_detail.html``. In our case, it'll use the template
+``<app name>/<model name>_detail.html``. In our case, it'll use the template
``"polls/poll_detail.html"``. Thus, rename your ``polls/detail.html`` template to
``polls/poll_detail.html``, and change the ``render_to_response()`` line in
``vote()``.
Similarly, the ``object_list`` generic view uses a template called
-``<app name>/<module name>_list.html``. Thus, rename ``polls/index.html`` to
+``<app name>/<model name>_list.html``. Thus, rename ``polls/index.html`` to
``polls/poll_list.html``.
Because we have more than one entry in the URLconf that uses ``object_detail``
diff --git a/tests/modeltests/pagination/models.py b/tests/modeltests/pagination/models.py
index ea2385dc79..3319b5cafa 100644
--- a/tests/modeltests/pagination/models.py
+++ b/tests/modeltests/pagination/models.py
@@ -64,4 +64,17 @@ True
>>> paginator.last_on_page(1)
9
+# Add a few more records to test out the orphans feature.
+>>> for x in range(10, 13):
+... Article(headline="Article %s" % x, pub_date=datetime(2006, 10, 6)).save()
+
+# With orphans set to 3 and 10 items per page, we should get all 12 items on a single page:
+>>> paginator = ObjectPaginator(Article.objects.all(), 10, orphans=3)
+>>> paginator.pages
+1
+
+# With orphans only set to 1, we should get two pages:
+>>> paginator = ObjectPaginator(Article.objects.all(), 10, orphans=1)
+>>> paginator.pages
+2
"""}
diff --git a/tests/regressiontests/forms/__init__.py b/tests/regressiontests/forms/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/regressiontests/forms/__init__.py
diff --git a/tests/regressiontests/forms/models.py b/tests/regressiontests/forms/models.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/regressiontests/forms/models.py
diff --git a/tests/regressiontests/forms/tests.py b/tests/regressiontests/forms/tests.py
new file mode 100644
index 0000000000..73fa3c27bf
--- /dev/null
+++ b/tests/regressiontests/forms/tests.py
@@ -0,0 +1,1242 @@
+# -*- coding: utf-8 -*-
+r"""
+>>> from django.newforms import *
+>>> import datetime
+>>> import re
+
+# TextInput Widget ############################################################
+
+>>> w = TextInput()
+>>> w.render('email', '')
+u'<input type="text" name="email" />'
+>>> w.render('email', None)
+u'<input type="text" name="email" />'
+>>> w.render('email', 'test@example.com')
+u'<input type="text" name="email" value="test@example.com" />'
+>>> w.render('email', 'some "quoted" & ampersanded value')
+u'<input type="text" name="email" value="some &quot;quoted&quot; &amp; ampersanded value" />'
+>>> w.render('email', 'test@example.com', attrs={'class': 'fun'})
+u'<input type="text" name="email" value="test@example.com" class="fun" />'
+
+# Note that doctest in Python 2.4 (and maybe 2.5?) doesn't support non-ascii
+# characters in output, so we're displaying the repr() here.
+>>> w.render('email', 'Å ÄĆŽćžšđ', attrs={'class': 'fun'})
+u'<input type="text" name="email" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" class="fun" />'
+
+You can also pass 'attrs' to the constructor:
+>>> w = TextInput(attrs={'class': 'fun'})
+>>> w.render('email', '')
+u'<input type="text" class="fun" name="email" />'
+>>> w.render('email', 'foo@example.com')
+u'<input type="text" class="fun" value="foo@example.com" name="email" />'
+
+'attrs' passed to render() get precedence over those passed to the constructor:
+>>> w = TextInput(attrs={'class': 'pretty'})
+>>> w.render('email', '', attrs={'class': 'special'})
+u'<input type="text" class="special" name="email" />'
+
+# PasswordInput Widget ############################################################
+
+>>> w = PasswordInput()
+>>> w.render('email', '')
+u'<input type="password" name="email" />'
+>>> w.render('email', None)
+u'<input type="password" name="email" />'
+>>> w.render('email', 'test@example.com')
+u'<input type="password" name="email" value="test@example.com" />'
+>>> w.render('email', 'some "quoted" & ampersanded value')
+u'<input type="password" name="email" value="some &quot;quoted&quot; &amp; ampersanded value" />'
+>>> w.render('email', 'test@example.com', attrs={'class': 'fun'})
+u'<input type="password" name="email" value="test@example.com" class="fun" />'
+
+You can also pass 'attrs' to the constructor:
+>>> w = PasswordInput(attrs={'class': 'fun'})
+>>> w.render('email', '')
+u'<input type="password" class="fun" name="email" />'
+>>> w.render('email', 'foo@example.com')
+u'<input type="password" class="fun" value="foo@example.com" name="email" />'
+
+'attrs' passed to render() get precedence over those passed to the constructor:
+>>> w = PasswordInput(attrs={'class': 'pretty'})
+>>> w.render('email', '', attrs={'class': 'special'})
+u'<input type="password" class="special" name="email" />'
+
+>>> w.render('email', 'Å ÄĆŽćžšđ', attrs={'class': 'fun'})
+u'<input type="password" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />'
+
+# HiddenInput Widget ############################################################
+
+>>> w = HiddenInput()
+>>> w.render('email', '')
+u'<input type="hidden" name="email" />'
+>>> w.render('email', None)
+u'<input type="hidden" name="email" />'
+>>> w.render('email', 'test@example.com')
+u'<input type="hidden" name="email" value="test@example.com" />'
+>>> w.render('email', 'some "quoted" & ampersanded value')
+u'<input type="hidden" name="email" value="some &quot;quoted&quot; &amp; ampersanded value" />'
+>>> w.render('email', 'test@example.com', attrs={'class': 'fun'})
+u'<input type="hidden" name="email" value="test@example.com" class="fun" />'
+
+You can also pass 'attrs' to the constructor:
+>>> w = HiddenInput(attrs={'class': 'fun'})
+>>> w.render('email', '')
+u'<input type="hidden" class="fun" name="email" />'
+>>> w.render('email', 'foo@example.com')
+u'<input type="hidden" class="fun" value="foo@example.com" name="email" />'
+
+'attrs' passed to render() get precedence over those passed to the constructor:
+>>> w = HiddenInput(attrs={'class': 'pretty'})
+>>> w.render('email', '', attrs={'class': 'special'})
+u'<input type="hidden" class="special" name="email" />'
+
+>>> w.render('email', 'Å ÄĆŽćžšđ', attrs={'class': 'fun'})
+u'<input type="hidden" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />'
+
+'attrs' passed to render() get precedence over those passed to the constructor:
+>>> w = HiddenInput(attrs={'class': 'pretty'})
+>>> w.render('email', '', attrs={'class': 'special'})
+u'<input type="hidden" class="special" name="email" />'
+
+# FileInput Widget ############################################################
+
+>>> w = FileInput()
+>>> w.render('email', '')
+u'<input type="file" name="email" />'
+>>> w.render('email', None)
+u'<input type="file" name="email" />'
+>>> w.render('email', 'test@example.com')
+u'<input type="file" name="email" value="test@example.com" />'
+>>> w.render('email', 'some "quoted" & ampersanded value')
+u'<input type="file" name="email" value="some &quot;quoted&quot; &amp; ampersanded value" />'
+>>> w.render('email', 'test@example.com', attrs={'class': 'fun'})
+u'<input type="file" name="email" value="test@example.com" class="fun" />'
+
+You can also pass 'attrs' to the constructor:
+>>> w = FileInput(attrs={'class': 'fun'})
+>>> w.render('email', '')
+u'<input type="file" class="fun" name="email" />'
+>>> w.render('email', 'foo@example.com')
+u'<input type="file" class="fun" value="foo@example.com" name="email" />'
+
+>>> w.render('email', 'Å ÄĆŽćžšđ', attrs={'class': 'fun'})
+u'<input type="file" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />'
+
+# Textarea Widget #############################################################
+
+>>> w = Textarea()
+>>> w.render('msg', '')
+u'<textarea name="msg"></textarea>'
+>>> w.render('msg', None)
+u'<textarea name="msg"></textarea>'
+>>> w.render('msg', 'value')
+u'<textarea name="msg">value</textarea>'
+>>> w.render('msg', 'some "quoted" & ampersanded value')
+u'<textarea name="msg">some &quot;quoted&quot; &amp; ampersanded value</textarea>'
+>>> w.render('msg', 'value', attrs={'class': 'pretty'})
+u'<textarea name="msg" class="pretty">value</textarea>'
+
+You can also pass 'attrs' to the constructor:
+>>> w = Textarea(attrs={'class': 'pretty'})
+>>> w.render('msg', '')
+u'<textarea class="pretty" name="msg"></textarea>'
+>>> w.render('msg', 'example')
+u'<textarea class="pretty" name="msg">example</textarea>'
+
+'attrs' passed to render() get precedence over those passed to the constructor:
+>>> w = Textarea(attrs={'class': 'pretty'})
+>>> w.render('msg', '', attrs={'class': 'special'})
+u'<textarea class="special" name="msg"></textarea>'
+
+>>> w.render('msg', 'Å ÄĆŽćžšđ', attrs={'class': 'fun'})
+u'<textarea class="fun" name="msg">\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111</textarea>'
+
+# CheckboxInput Widget ########################################################
+
+>>> w = CheckboxInput()
+>>> w.render('is_cool', '')
+u'<input type="checkbox" name="is_cool" />'
+>>> w.render('is_cool', False)
+u'<input type="checkbox" name="is_cool" />'
+>>> w.render('is_cool', True)
+u'<input checked="checked" type="checkbox" name="is_cool" />'
+>>> w.render('is_cool', False, attrs={'class': 'pretty'})
+u'<input type="checkbox" name="is_cool" class="pretty" />'
+
+You can also pass 'attrs' to the constructor:
+>>> w = CheckboxInput(attrs={'class': 'pretty'})
+>>> w.render('is_cool', '')
+u'<input type="checkbox" class="pretty" name="is_cool" />'
+
+'attrs' passed to render() get precedence over those passed to the constructor:
+>>> w = CheckboxInput(attrs={'class': 'pretty'})
+>>> w.render('is_cool', '', attrs={'class': 'special'})
+u'<input type="checkbox" class="special" name="is_cool" />'
+
+# Select Widget ###############################################################
+
+>>> w = Select()
+>>> print w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
+<select name="beatle">
+<option value="J" selected="selected">John</option>
+<option value="P">Paul</option>
+<option value="G">George</option>
+<option value="R">Ringo</option>
+</select>
+
+If the value is None, none of the options are selected:
+>>> print w.render('beatle', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
+<select name="beatle">
+<option value="J">John</option>
+<option value="P">Paul</option>
+<option value="G">George</option>
+<option value="R">Ringo</option>
+</select>
+
+If the value corresponds to a label (but not to an option value), none of the options are selected:
+>>> print w.render('beatle', 'John', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
+<select name="beatle">
+<option value="J">John</option>
+<option value="P">Paul</option>
+<option value="G">George</option>
+<option value="R">Ringo</option>
+</select>
+
+The value is compared to its str():
+>>> print w.render('num', 2, choices=[('1', '1'), ('2', '2'), ('3', '3')])
+<select name="num">
+<option value="1">1</option>
+<option value="2" selected="selected">2</option>
+<option value="3">3</option>
+</select>
+>>> print w.render('num', '2', choices=[(1, 1), (2, 2), (3, 3)])
+<select name="num">
+<option value="1">1</option>
+<option value="2" selected="selected">2</option>
+<option value="3">3</option>
+</select>
+>>> print w.render('num', 2, choices=[(1, 1), (2, 2), (3, 3)])
+<select name="num">
+<option value="1">1</option>
+<option value="2" selected="selected">2</option>
+<option value="3">3</option>
+</select>
+
+The 'choices' argument can be any iterable:
+>>> def get_choices():
+... for i in range(5):
+... yield (i, i)
+>>> print w.render('num', 2, choices=get_choices())
+<select name="num">
+<option value="0">0</option>
+<option value="1">1</option>
+<option value="2" selected="selected">2</option>
+<option value="3">3</option>
+<option value="4">4</option>
+</select>
+
+You can also pass 'choices' to the constructor:
+>>> w = Select(choices=[(1, 1), (2, 2), (3, 3)])
+>>> print w.render('num', 2)
+<select name="num">
+<option value="1">1</option>
+<option value="2" selected="selected">2</option>
+<option value="3">3</option>
+</select>
+
+If 'choices' is passed to both the constructor and render(), then they'll both be in the output:
+>>> print w.render('num', 2, choices=[(4, 4), (5, 5)])
+<select name="num">
+<option value="1">1</option>
+<option value="2" selected="selected">2</option>
+<option value="3">3</option>
+<option value="4">4</option>
+<option value="5">5</option>
+</select>
+
+>>> w.render('email', 'Å ÄĆŽćžšđ', choices=[('Å ÄĆŽćžšđ', 'Å ÄabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')])
+u'<select name="email">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" selected="selected">\u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</option>\n<option value="\u0107\u017e\u0161\u0111">abc\u0107\u017e\u0161\u0111</option>\n</select>'
+
+# SelectMultiple Widget #######################################################
+
+>>> w = SelectMultiple()
+>>> print w.render('beatles', ['J'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
+<select multiple="multiple" name="beatles">
+<option value="J" selected="selected">John</option>
+<option value="P">Paul</option>
+<option value="G">George</option>
+<option value="R">Ringo</option>
+</select>
+>>> print w.render('beatles', ['J', 'P'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
+<select multiple="multiple" name="beatles">
+<option value="J" selected="selected">John</option>
+<option value="P" selected="selected">Paul</option>
+<option value="G">George</option>
+<option value="R">Ringo</option>
+</select>
+>>> print w.render('beatles', ['J', 'P', 'R'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
+<select multiple="multiple" name="beatles">
+<option value="J" selected="selected">John</option>
+<option value="P" selected="selected">Paul</option>
+<option value="G">George</option>
+<option value="R" selected="selected">Ringo</option>
+</select>
+
+If the value is None, none of the options are selected:
+>>> print w.render('beatles', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
+<select multiple="multiple" name="beatles">
+<option value="J">John</option>
+<option value="P">Paul</option>
+<option value="G">George</option>
+<option value="R">Ringo</option>
+</select>
+
+If the value corresponds to a label (but not to an option value), none of the options are selected:
+>>> print w.render('beatles', ['John'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
+<select multiple="multiple" name="beatles">
+<option value="J">John</option>
+<option value="P">Paul</option>
+<option value="G">George</option>
+<option value="R">Ringo</option>
+</select>
+
+If multiple values are given, but some of them are not valid, the valid ones are selected:
+>>> print w.render('beatles', ['J', 'G', 'foo'], choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
+<select multiple="multiple" name="beatles">
+<option value="J" selected="selected">John</option>
+<option value="P">Paul</option>
+<option value="G" selected="selected">George</option>
+<option value="R">Ringo</option>
+</select>
+
+The value is compared to its str():
+>>> print w.render('nums', [2], choices=[('1', '1'), ('2', '2'), ('3', '3')])
+<select multiple="multiple" name="nums">
+<option value="1">1</option>
+<option value="2" selected="selected">2</option>
+<option value="3">3</option>
+</select>
+>>> print w.render('nums', ['2'], choices=[(1, 1), (2, 2), (3, 3)])
+<select multiple="multiple" name="nums">
+<option value="1">1</option>
+<option value="2" selected="selected">2</option>
+<option value="3">3</option>
+</select>
+>>> print w.render('nums', [2], choices=[(1, 1), (2, 2), (3, 3)])
+<select multiple="multiple" name="nums">
+<option value="1">1</option>
+<option value="2" selected="selected">2</option>
+<option value="3">3</option>
+</select>
+
+The 'choices' argument can be any iterable:
+>>> def get_choices():
+... for i in range(5):
+... yield (i, i)
+>>> print w.render('nums', [2], choices=get_choices())
+<select multiple="multiple" name="nums">
+<option value="0">0</option>
+<option value="1">1</option>
+<option value="2" selected="selected">2</option>
+<option value="3">3</option>
+<option value="4">4</option>
+</select>
+
+You can also pass 'choices' to the constructor:
+>>> w = SelectMultiple(choices=[(1, 1), (2, 2), (3, 3)])
+>>> print w.render('nums', [2])
+<select multiple="multiple" name="nums">
+<option value="1">1</option>
+<option value="2" selected="selected">2</option>
+<option value="3">3</option>
+</select>
+
+If 'choices' is passed to both the constructor and render(), then they'll both be in the output:
+>>> print w.render('nums', [2], choices=[(4, 4), (5, 5)])
+<select multiple="multiple" name="nums">
+<option value="1">1</option>
+<option value="2" selected="selected">2</option>
+<option value="3">3</option>
+<option value="4">4</option>
+<option value="5">5</option>
+</select>
+
+>>> w.render('nums', ['Å ÄĆŽćžšđ'], choices=[('Å ÄĆŽćžšđ', 'Å ÄabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')])
+u'<select multiple="multiple" name="nums">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" selected="selected">\u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</option>\n<option value="\u0107\u017e\u0161\u0111">abc\u0107\u017e\u0161\u0111</option>\n</select>'
+
+# RadioSelect Widget ##########################################################
+
+>>> w = RadioSelect()
+>>> print w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
+<ul>
+<li><label><input checked="checked" type="radio" name="beatle" value="J" /> John</label></li>
+<li><label><input type="radio" name="beatle" value="P" /> Paul</label></li>
+<li><label><input type="radio" name="beatle" value="G" /> George</label></li>
+<li><label><input type="radio" name="beatle" value="R" /> Ringo</label></li>
+</ul>
+
+If the value is None, none of the options are checked:
+>>> print w.render('beatle', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
+<ul>
+<li><label><input type="radio" name="beatle" value="J" /> John</label></li>
+<li><label><input type="radio" name="beatle" value="P" /> Paul</label></li>
+<li><label><input type="radio" name="beatle" value="G" /> George</label></li>
+<li><label><input type="radio" name="beatle" value="R" /> Ringo</label></li>
+</ul>
+
+If the value corresponds to a label (but not to an option value), none of the options are checked:
+>>> print w.render('beatle', 'John', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
+<ul>
+<li><label><input type="radio" name="beatle" value="J" /> John</label></li>
+<li><label><input type="radio" name="beatle" value="P" /> Paul</label></li>
+<li><label><input type="radio" name="beatle" value="G" /> George</label></li>
+<li><label><input type="radio" name="beatle" value="R" /> Ringo</label></li>
+</ul>
+
+The value is compared to its str():
+>>> print w.render('num', 2, choices=[('1', '1'), ('2', '2'), ('3', '3')])
+<ul>
+<li><label><input type="radio" name="num" value="1" /> 1</label></li>
+<li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li>
+<li><label><input type="radio" name="num" value="3" /> 3</label></li>
+</ul>
+>>> print w.render('num', '2', choices=[(1, 1), (2, 2), (3, 3)])
+<ul>
+<li><label><input type="radio" name="num" value="1" /> 1</label></li>
+<li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li>
+<li><label><input type="radio" name="num" value="3" /> 3</label></li>
+</ul>
+>>> print w.render('num', 2, choices=[(1, 1), (2, 2), (3, 3)])
+<ul>
+<li><label><input type="radio" name="num" value="1" /> 1</label></li>
+<li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li>
+<li><label><input type="radio" name="num" value="3" /> 3</label></li>
+</ul>
+
+The 'choices' argument can be any iterable:
+>>> def get_choices():
+... for i in range(5):
+... yield (i, i)
+>>> print w.render('num', 2, choices=get_choices())
+<ul>
+<li><label><input type="radio" name="num" value="0" /> 0</label></li>
+<li><label><input type="radio" name="num" value="1" /> 1</label></li>
+<li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li>
+<li><label><input type="radio" name="num" value="3" /> 3</label></li>
+<li><label><input type="radio" name="num" value="4" /> 4</label></li>
+</ul>
+
+You can also pass 'choices' to the constructor:
+>>> w = RadioSelect(choices=[(1, 1), (2, 2), (3, 3)])
+>>> print w.render('num', 2)
+<ul>
+<li><label><input type="radio" name="num" value="1" /> 1</label></li>
+<li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li>
+<li><label><input type="radio" name="num" value="3" /> 3</label></li>
+</ul>
+
+If 'choices' is passed to both the constructor and render(), then they'll both be in the output:
+>>> print w.render('num', 2, choices=[(4, 4), (5, 5)])
+<ul>
+<li><label><input type="radio" name="num" value="1" /> 1</label></li>
+<li><label><input checked="checked" type="radio" name="num" value="2" /> 2</label></li>
+<li><label><input type="radio" name="num" value="3" /> 3</label></li>
+<li><label><input type="radio" name="num" value="4" /> 4</label></li>
+<li><label><input type="radio" name="num" value="5" /> 5</label></li>
+</ul>
+
+The render() method returns a RadioFieldRenderer object, whose str() is a <ul>.
+You can manipulate that object directly to customize the way the RadioSelect
+is rendered.
+>>> w = RadioSelect()
+>>> r = w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
+>>> for inp in r:
+... print inp
+<label><input checked="checked" type="radio" name="beatle" value="J" /> John</label>
+<label><input type="radio" name="beatle" value="P" /> Paul</label>
+<label><input type="radio" name="beatle" value="G" /> George</label>
+<label><input type="radio" name="beatle" value="R" /> Ringo</label>
+>>> for inp in r:
+... print '%s<br />' % inp
+<label><input checked="checked" type="radio" name="beatle" value="J" /> John</label><br />
+<label><input type="radio" name="beatle" value="P" /> Paul</label><br />
+<label><input type="radio" name="beatle" value="G" /> George</label><br />
+<label><input type="radio" name="beatle" value="R" /> Ringo</label><br />
+>>> for inp in r:
+... print '<p>%s %s</p>' % (inp.tag(), inp.choice_label)
+<p><input checked="checked" type="radio" name="beatle" value="J" /> John</p>
+<p><input type="radio" name="beatle" value="P" /> Paul</p>
+<p><input type="radio" name="beatle" value="G" /> George</p>
+<p><input type="radio" name="beatle" value="R" /> Ringo</p>
+>>> for inp in r:
+... print '%s %s %s %s %s' % (inp.name, inp.value, inp.choice_value, inp.choice_label, inp.is_checked())
+beatle J J John True
+beatle J P Paul False
+beatle J G George False
+beatle J R Ringo False
+
+# CharField ###################################################################
+
+>>> f = CharField(required=False)
+>>> f.clean(1)
+u'1'
+>>> f.clean('hello')
+u'hello'
+>>> f.clean(None)
+u''
+>>> f.clean([1, 2, 3])
+u'[1, 2, 3]'
+
+CharField accepts an optional max_length parameter:
+>>> f = CharField(max_length=10, required=False)
+>>> f.clean('')
+u''
+>>> f.clean('12345')
+u'12345'
+>>> f.clean('1234567890')
+u'1234567890'
+>>> f.clean('1234567890a')
+Traceback (most recent call last):
+...
+ValidationError: [u'Ensure this value has at most 10 characters.']
+
+CharField accepts an optional min_length parameter:
+>>> f = CharField(min_length=10, required=False)
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'Ensure this value has at least 10 characters.']
+>>> f.clean('12345')
+Traceback (most recent call last):
+...
+ValidationError: [u'Ensure this value has at least 10 characters.']
+>>> f.clean('1234567890')
+u'1234567890'
+>>> f.clean('1234567890a')
+u'1234567890a'
+
+# IntegerField ################################################################
+
+>>> f = IntegerField()
+>>> f.clean('1')
+1
+>>> isinstance(f.clean('1'), int)
+True
+>>> f.clean('23')
+23
+>>> f.clean('a')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a whole number.']
+>>> f.clean('1 ')
+1
+>>> f.clean(' 1')
+1
+>>> f.clean(' 1 ')
+1
+>>> f.clean('1a')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a whole number.']
+
+# DateField ###################################################################
+
+>>> import datetime
+>>> f = DateField()
+>>> f.clean(datetime.date(2006, 10, 25))
+datetime.date(2006, 10, 25)
+>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30))
+datetime.date(2006, 10, 25)
+>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59))
+datetime.date(2006, 10, 25)
+>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59, 200))
+datetime.date(2006, 10, 25)
+>>> f.clean('2006-10-25')
+datetime.date(2006, 10, 25)
+>>> f.clean('10/25/2006')
+datetime.date(2006, 10, 25)
+>>> f.clean('10/25/06')
+datetime.date(2006, 10, 25)
+>>> f.clean('Oct 25 2006')
+datetime.date(2006, 10, 25)
+>>> f.clean('October 25 2006')
+datetime.date(2006, 10, 25)
+>>> f.clean('October 25, 2006')
+datetime.date(2006, 10, 25)
+>>> f.clean('25 October 2006')
+datetime.date(2006, 10, 25)
+>>> f.clean('25 October, 2006')
+datetime.date(2006, 10, 25)
+>>> f.clean('2006-4-31')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid date.']
+>>> f.clean('200a-10-25')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid date.']
+>>> f.clean('25/10/06')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid date.']
+>>> f.clean(None)
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+
+>>> f = DateField(required=False)
+>>> f.clean(None)
+>>> repr(f.clean(None))
+'None'
+>>> f.clean('')
+>>> repr(f.clean(''))
+'None'
+
+DateField accepts an optional input_formats parameter:
+>>> f = DateField(input_formats=['%Y %m %d'])
+>>> f.clean(datetime.date(2006, 10, 25))
+datetime.date(2006, 10, 25)
+>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30))
+datetime.date(2006, 10, 25)
+>>> f.clean('2006 10 25')
+datetime.date(2006, 10, 25)
+
+The input_formats parameter overrides all default input formats,
+so the default formats won't work unless you specify them:
+>>> f.clean('2006-10-25')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid date.']
+>>> f.clean('10/25/2006')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid date.']
+>>> f.clean('10/25/06')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid date.']
+
+# DateTimeField ###############################################################
+
+>>> import datetime
+>>> f = DateTimeField()
+>>> f.clean(datetime.date(2006, 10, 25))
+datetime.datetime(2006, 10, 25, 0, 0)
+>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30))
+datetime.datetime(2006, 10, 25, 14, 30)
+>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59))
+datetime.datetime(2006, 10, 25, 14, 30, 59)
+>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59, 200))
+datetime.datetime(2006, 10, 25, 14, 30, 59, 200)
+>>> f.clean('2006-10-25 14:30:45')
+datetime.datetime(2006, 10, 25, 14, 30, 45)
+>>> f.clean('2006-10-25 14:30:00')
+datetime.datetime(2006, 10, 25, 14, 30)
+>>> f.clean('2006-10-25 14:30')
+datetime.datetime(2006, 10, 25, 14, 30)
+>>> f.clean('2006-10-25')
+datetime.datetime(2006, 10, 25, 0, 0)
+>>> f.clean('10/25/2006 14:30:45')
+datetime.datetime(2006, 10, 25, 14, 30, 45)
+>>> f.clean('10/25/2006 14:30:00')
+datetime.datetime(2006, 10, 25, 14, 30)
+>>> f.clean('10/25/2006 14:30')
+datetime.datetime(2006, 10, 25, 14, 30)
+>>> f.clean('10/25/2006')
+datetime.datetime(2006, 10, 25, 0, 0)
+>>> f.clean('10/25/06 14:30:45')
+datetime.datetime(2006, 10, 25, 14, 30, 45)
+>>> f.clean('10/25/06 14:30:00')
+datetime.datetime(2006, 10, 25, 14, 30)
+>>> f.clean('10/25/06 14:30')
+datetime.datetime(2006, 10, 25, 14, 30)
+>>> f.clean('10/25/06')
+datetime.datetime(2006, 10, 25, 0, 0)
+>>> f.clean('hello')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid date/time.']
+>>> f.clean('2006-10-25 4:30 p.m.')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid date/time.']
+
+DateField accepts an optional input_formats parameter:
+>>> f = DateTimeField(input_formats=['%Y %m %d %I:%M %p'])
+>>> f.clean(datetime.date(2006, 10, 25))
+datetime.datetime(2006, 10, 25, 0, 0)
+>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30))
+datetime.datetime(2006, 10, 25, 14, 30)
+>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59))
+datetime.datetime(2006, 10, 25, 14, 30, 59)
+>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59, 200))
+datetime.datetime(2006, 10, 25, 14, 30, 59, 200)
+>>> f.clean('2006 10 25 2:30 PM')
+datetime.datetime(2006, 10, 25, 14, 30)
+
+The input_formats parameter overrides all default input formats,
+so the default formats won't work unless you specify them:
+>>> f.clean('2006-10-25 14:30:45')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid date/time.']
+
+# RegexField ##################################################################
+
+>>> f = RegexField('^\d[A-F]\d$')
+>>> f.clean('2A2')
+u'2A2'
+>>> f.clean('3F3')
+u'3F3'
+>>> f.clean('3G3')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid value.']
+>>> f.clean(' 2A2')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid value.']
+>>> f.clean('2A2 ')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid value.']
+
+Alternatively, RegexField can take a compiled regular expression:
+>>> f = RegexField(re.compile('^\d[A-F]\d$'))
+>>> f.clean('2A2')
+u'2A2'
+>>> f.clean('3F3')
+u'3F3'
+>>> f.clean('3G3')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid value.']
+>>> f.clean(' 2A2')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid value.']
+>>> f.clean('2A2 ')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid value.']
+
+RegexField takes an optional error_message argument:
+>>> f = RegexField('^\d\d\d\d$', 'Enter a four-digit number.')
+>>> f.clean('1234')
+u'1234'
+>>> f.clean('123')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a four-digit number.']
+>>> f.clean('abcd')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a four-digit number.']
+
+# EmailField ##################################################################
+
+>>> f = EmailField()
+>>> f.clean('person@example.com')
+u'person@example.com'
+>>> f.clean('foo')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid e-mail address.']
+>>> f.clean('foo@')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid e-mail address.']
+>>> f.clean('foo@bar')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid e-mail address.']
+
+# URLField ##################################################################
+
+>>> f = URLField()
+>>> f.clean('http://example.com')
+u'http://example.com'
+>>> f.clean('http://www.example.com')
+u'http://www.example.com'
+>>> f.clean('foo')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid URL.']
+>>> f.clean('example.com')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid URL.']
+>>> f.clean('http://')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid URL.']
+>>> f.clean('http://example')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid URL.']
+>>> f.clean('http://example.')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid URL.']
+>>> f.clean('http://.com')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid URL.']
+
+URLField takes an optional verify_exists parameter, which is False by default.
+This verifies that the URL is live on the Internet and doesn't return a 404 or 500:
+>>> f = URLField(verify_exists=True)
+>>> f.clean('http://www.google.com') # This will fail if there's no Internet connection
+u'http://www.google.com'
+>>> f.clean('http://example')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid URL.']
+>>> f.clean('http://www.jfoiwjfoi23jfoijoaijfoiwjofiwjefewl.com') # bad domain
+Traceback (most recent call last):
+...
+ValidationError: [u'This URL appears to be a broken link.']
+>>> f.clean('http://google.com/we-love-microsoft.html') # good domain, bad page
+Traceback (most recent call last):
+...
+ValidationError: [u'This URL appears to be a broken link.']
+
+# BooleanField ################################################################
+
+>>> f = BooleanField()
+>>> f.clean(True)
+True
+>>> f.clean(False)
+False
+>>> f.clean(1)
+True
+>>> f.clean(0)
+False
+>>> f.clean('Django rocks')
+True
+
+# ChoiceField #################################################################
+
+>>> f = ChoiceField(choices=[('1', '1'), ('2', '2')])
+>>> f.clean(1)
+u'1'
+>>> f.clean('1')
+u'1'
+>>> f.clean(None)
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+>>> f.clean('3')
+Traceback (most recent call last):
+...
+ValidationError: [u'Select a valid choice. 3 is not one of the available choices.']
+
+>>> f = ChoiceField(choices=[('J', 'John'), ('P', 'Paul')])
+>>> f.clean('J')
+u'J'
+>>> f.clean('John')
+Traceback (most recent call last):
+...
+ValidationError: [u'Select a valid choice. John is not one of the available choices.']
+
+# MultipleChoiceField #########################################################
+
+>>> f = MultipleChoiceField(choices=[('1', '1'), ('2', '2')])
+>>> f.clean([1])
+[u'1']
+>>> f.clean(['1'])
+[u'1']
+>>> f.clean(['1', '2'])
+[u'1', u'2']
+>>> f.clean([1, '2'])
+[u'1', u'2']
+>>> f.clean((1, '2'))
+[u'1', u'2']
+>>> f.clean('hello')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a list of values.']
+>>> f.clean([])
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+>>> f.clean(())
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+>>> f.clean(['3'])
+Traceback (most recent call last):
+...
+ValidationError: [u'Select a valid choice. 3 is not one of the available choices.']
+
+# ComboField ##################################################################
+
+ComboField takes a list of fields that should be used to validate a value,
+in that order:
+>>> f = ComboField(fields=[CharField(max_length=20), EmailField()])
+>>> f.clean('test@example.com')
+u'test@example.com'
+>>> f.clean('longemailaddress@example.com')
+Traceback (most recent call last):
+...
+ValidationError: [u'Ensure this value has at most 20 characters.']
+>>> f.clean('not an e-mail')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid e-mail address.']
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+>>> f.clean(None)
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+
+# Form ########################################################################
+
+>>> class Person(Form):
+... first_name = CharField()
+... last_name = CharField()
+... birthday = DateField()
+>>> p = Person()
+>>> print p
+<tr><td>First name:</td><td><input type="text" name="first_name" /></td></tr>
+<tr><td>Last name:</td><td><input type="text" name="last_name" /></td></tr>
+<tr><td>Birthday:</td><td><input type="text" name="birthday" /></td></tr>
+>>> print p.as_table()
+<tr><td>First name:</td><td><input type="text" name="first_name" /></td></tr>
+<tr><td>Last name:</td><td><input type="text" name="last_name" /></td></tr>
+<tr><td>Birthday:</td><td><input type="text" name="birthday" /></td></tr>
+>>> print p.as_ul()
+<li>First name: <input type="text" name="first_name" /></li>
+<li>Last name: <input type="text" name="last_name" /></li>
+<li>Birthday: <input type="text" name="birthday" /></li>
+>>> print p.as_table_with_errors()
+<tr><td colspan="2"><ul><li>This field is required.</li></ul></td></tr>
+<tr><td>First name:</td><td><input type="text" name="first_name" /></td></tr>
+<tr><td colspan="2"><ul><li>This field is required.</li></ul></td></tr>
+<tr><td>Last name:</td><td><input type="text" name="last_name" /></td></tr>
+<tr><td colspan="2"><ul><li>This field is required.</li></ul></td></tr>
+<tr><td>Birthday:</td><td><input type="text" name="birthday" /></td></tr>
+>>> print p.as_ul_with_errors()
+<li><ul><li>This field is required.</li></ul>First name: <input type="text" name="first_name" /></li>
+<li><ul><li>This field is required.</li></ul>Last name: <input type="text" name="last_name" /></li>
+<li><ul><li>This field is required.</li></ul>Birthday: <input type="text" name="birthday" /></li>
+
+>>> p = Person({'first_name': u'John', 'last_name': u'Lennon', 'birthday': u'1940-10-9'})
+>>> p.errors()
+{}
+>>> p.is_valid()
+True
+>>> p.errors().as_ul()
+u''
+>>> p.errors().as_text()
+u''
+>>> p.clean()
+{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
+>>> print p['first_name']
+<input type="text" name="first_name" value="John" />
+>>> print p['last_name']
+<input type="text" name="last_name" value="Lennon" />
+>>> print p['birthday']
+<input type="text" name="birthday" value="1940-10-9" />
+>>> for boundfield in p:
+... print boundfield
+<input type="text" name="first_name" value="John" />
+<input type="text" name="last_name" value="Lennon" />
+<input type="text" name="birthday" value="1940-10-9" />
+>>> print p
+<tr><td>First name:</td><td><input type="text" name="first_name" value="John" /></td></tr>
+<tr><td>Last name:</td><td><input type="text" name="last_name" value="Lennon" /></td></tr>
+<tr><td>Birthday:</td><td><input type="text" name="birthday" value="1940-10-9" /></td></tr>
+
+>>> p = Person({'last_name': u'Lennon'})
+>>> p.errors()
+{'first_name': [u'This field is required.'], 'birthday': [u'This field is required.']}
+>>> p.is_valid()
+False
+>>> p.errors().as_ul()
+u'<ul class="errorlist"><li>first_name<ul class="errorlist"><li>This field is required.</li></ul></li><li>birthday<ul class="errorlist"><li>This field is required.</li></ul></li></ul>'
+>>> print p.errors().as_text()
+* first_name
+ * This field is required.
+* birthday
+ * This field is required.
+>>> p.clean()
+>>> repr(p.clean())
+'None'
+>>> p['first_name'].errors
+[u'This field is required.']
+>>> p['first_name'].errors.as_ul()
+u'<ul class="errorlist"><li>This field is required.</li></ul>'
+>>> p['first_name'].errors.as_text()
+u'* This field is required.'
+
+>>> p = Person()
+>>> print p['first_name']
+<input type="text" name="first_name" />
+>>> print p['last_name']
+<input type="text" name="last_name" />
+>>> print p['birthday']
+<input type="text" name="birthday" />
+
+"auto_id" tells the Form to add an "id" attribute to each form element.
+If it's a string that contains '%s', Django will use that as a format string
+into which the field's name will be inserted.
+>>> p = Person(auto_id='id_%s')
+>>> print p.as_ul()
+<li>First name: <input type="text" name="first_name" id="id_first_name" /></li>
+<li>Last name: <input type="text" name="last_name" id="id_last_name" /></li>
+<li>Birthday: <input type="text" name="birthday" id="id_birthday" /></li>
+
+If auto_id is any True value whose str() does not contain '%s', the "id"
+attribute will be the name of the field.
+>>> p = Person(auto_id=True)
+>>> print p.as_ul()
+<li>First name: <input type="text" name="first_name" id="first_name" /></li>
+<li>Last name: <input type="text" name="last_name" id="last_name" /></li>
+<li>Birthday: <input type="text" name="birthday" id="birthday" /></li>
+
+If auto_id is any False value, an "id" attribute won't be output unless it
+was manually entered.
+>>> p = Person(auto_id=False)
+>>> print p.as_ul()
+<li>First name: <input type="text" name="first_name" /></li>
+<li>Last name: <input type="text" name="last_name" /></li>
+<li>Birthday: <input type="text" name="birthday" /></li>
+
+In this example, auto_id is False, but the "id" attribute for the "first_name"
+field is given.
+>>> class PersonNew(Form):
+... first_name = CharField(widget=TextInput(attrs={'id': 'first_name_id'}))
+... last_name = CharField()
+... birthday = DateField()
+>>> p = PersonNew(auto_id=False)
+>>> print p.as_ul()
+<li>First name: <input type="text" id="first_name_id" name="first_name" /></li>
+<li>Last name: <input type="text" name="last_name" /></li>
+<li>Birthday: <input type="text" name="birthday" /></li>
+
+If the "id" attribute is specified in the Form and auto_id is True, the "id"
+attribute in the Form gets precedence.
+>>> p = PersonNew(auto_id=True)
+>>> print p.as_ul()
+<li>First name: <input type="text" id="first_name_id" name="first_name" /></li>
+<li>Last name: <input type="text" name="last_name" id="last_name" /></li>
+<li>Birthday: <input type="text" name="birthday" id="birthday" /></li>
+
+>>> class SignupForm(Form):
+... email = EmailField()
+... get_spam = BooleanField()
+>>> f = SignupForm()
+>>> print f['email']
+<input type="text" name="email" />
+>>> print f['get_spam']
+<input type="checkbox" name="get_spam" />
+
+>>> f = SignupForm({'email': 'test@example.com', 'get_spam': True})
+>>> print f['email']
+<input type="text" name="email" value="test@example.com" />
+>>> print f['get_spam']
+<input checked="checked" type="checkbox" name="get_spam" />
+
+Any Field can have a Widget class passed to its constructor:
+>>> class ContactForm(Form):
+... subject = CharField()
+... message = CharField(widget=Textarea)
+>>> f = ContactForm()
+>>> print f['subject']
+<input type="text" name="subject" />
+>>> print f['message']
+<textarea name="message"></textarea>
+
+as_textarea() and as_text() are shortcuts for changing the output widget type:
+>>> f['subject'].as_textarea()
+u'<textarea name="subject"></textarea>'
+>>> f['message'].as_text()
+u'<input type="text" name="message" />'
+
+The 'widget' parameter to a Field can also be an instance:
+>>> class ContactForm(Form):
+... subject = CharField()
+... message = CharField(widget=Textarea(attrs={'rows': 80, 'cols': 20}))
+>>> f = ContactForm()
+>>> print f['message']
+<textarea rows="80" cols="20" name="message"></textarea>
+
+Instance-level attrs are *not* carried over to as_textarea() and as_text():
+>>> f['message'].as_text()
+u'<input type="text" name="message" />'
+>>> f = ContactForm({'subject': 'Hello', 'message': 'I love you.'})
+>>> f['subject'].as_textarea()
+u'<textarea name="subject">Hello</textarea>'
+>>> f['message'].as_text()
+u'<input type="text" name="message" value="I love you." />'
+
+For a form with a <select>, use ChoiceField:
+>>> class FrameworkForm(Form):
+... name = CharField()
+... language = ChoiceField(choices=[('P', 'Python'), ('J', 'Java')])
+>>> f = FrameworkForm()
+>>> print f['language']
+<select name="language">
+<option value="P">Python</option>
+<option value="J">Java</option>
+</select>
+>>> f = FrameworkForm({'name': 'Django', 'language': 'P'})
+>>> print f['language']
+<select name="language">
+<option value="P" selected="selected">Python</option>
+<option value="J">Java</option>
+</select>
+
+MultipleChoiceField is a special case, as its data is required to be a list:
+>>> class SongForm(Form):
+... name = CharField()
+... composers = MultipleChoiceField()
+>>> f = SongForm()
+>>> print f['composers']
+<select multiple="multiple" name="composers">
+</select>
+>>> class SongForm(Form):
+... name = CharField()
+... composers = MultipleChoiceField(choices=[('J', 'John Lennon'), ('P', 'Paul McCartney')])
+>>> f = SongForm()
+>>> print f['composers']
+<select multiple="multiple" name="composers">
+<option value="J">John Lennon</option>
+<option value="P">Paul McCartney</option>
+</select>
+>>> f = SongForm({'name': 'Yesterday', 'composers': ['P']})
+>>> print f['name']
+<input type="text" name="name" value="Yesterday" />
+>>> print f['composers']
+<select multiple="multiple" name="composers">
+<option value="J">John Lennon</option>
+<option value="P" selected="selected">Paul McCartney</option>
+</select>
+
+There are a couple of ways to do multiple-field validation. If you want the
+validation message to be associated with a particular field, implement the
+clean_XXX() method on the Form, where XXX is the field name. As in
+Field.clean(), the clean_XXX() method should return the cleaned value:
+>>> class UserRegistration(Form):
+... username = CharField(max_length=10)
+... password1 = CharField(widget=PasswordInput)
+... password2 = CharField(widget=PasswordInput)
+... def clean_password2(self):
+... if self.clean_data.get('password1') and self.clean_data.get('password2') and self.clean_data['password1'] != self.clean_data['password2']:
+... raise ValidationError(u'Please make sure your passwords match.')
+... return self.clean_data['password2']
+>>> f = UserRegistration()
+>>> f.errors()
+{'username': [u'This field is required.'], 'password1': [u'This field is required.'], 'password2': [u'This field is required.']}
+>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'bar'})
+>>> f.errors()
+{'password2': [u'Please make sure your passwords match.']}
+>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'})
+>>> f.errors()
+{}
+>>> f.clean()
+{'username': u'adrian', 'password1': u'foo', 'password2': u'foo'}
+
+Another way of doing multiple-field validation is by implementing the
+Form's clean() method. If you do this, any ValidationError raised by that
+method will not be associated with a particular field; it will have a
+special-case association with the field named '__all__'. Note that
+Form.clean() still needs to return a dictionary of all clean data:
+>>> class UserRegistration(Form):
+... username = CharField(max_length=10)
+... password1 = CharField(widget=PasswordInput)
+... password2 = CharField(widget=PasswordInput)
+... def clean(self):
+... if self.clean_data.get('password1') and self.clean_data.get('password2') and self.clean_data['password1'] != self.clean_data['password2']:
+... raise ValidationError(u'Please make sure your passwords match.')
+... return self.clean_data
+>>> f = UserRegistration()
+>>> print f.as_table()
+<tr><td>Username:</td><td><input type="text" name="username" /></td></tr>
+<tr><td>Password1:</td><td><input type="password" name="password1" /></td></tr>
+<tr><td>Password2:</td><td><input type="password" name="password2" /></td></tr>
+>>> f.errors()
+{'username': [u'This field is required.'], 'password1': [u'This field is required.'], 'password2': [u'This field is required.']}
+>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'bar'})
+>>> f.errors()
+{'__all__': [u'Please make sure your passwords match.']}
+>>> print f.as_table()
+<tr><td>Username:</td><td><input type="text" name="username" value="adrian" /></td></tr>
+<tr><td>Password1:</td><td><input type="password" name="password1" value="foo" /></td></tr>
+<tr><td>Password2:</td><td><input type="password" name="password2" value="bar" /></td></tr>
+>>> print f.as_table_with_errors()
+<tr><td colspan="2"><ul><li>Please make sure your passwords match.</li></ul></td></tr>
+<tr><td>Username:</td><td><input type="text" name="username" value="adrian" /></td></tr>
+<tr><td>Password1:</td><td><input type="password" name="password1" value="foo" /></td></tr>
+<tr><td>Password2:</td><td><input type="password" name="password2" value="bar" /></td></tr>
+>>> print f.as_ul_with_errors()
+<li><ul><li>Please make sure your passwords match.</li></ul></li>
+<li>Username: <input type="text" name="username" value="adrian" /></li>
+<li>Password1: <input type="password" name="password1" value="foo" /></li>
+<li>Password2: <input type="password" name="password2" value="bar" /></li>
+>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'})
+>>> f.errors()
+{}
+>>> f.clean()
+{'username': u'adrian', 'password1': u'foo', 'password2': u'foo'}
+
+It's possible to construct a Form dynamically by adding to the self.fields
+dictionary in __init__(). Don't forget to call Form.__init__() within the
+subclass' __init__().
+>>> class Person(Form):
+... first_name = CharField()
+... last_name = CharField()
+... def __init__(self):
+... super(Person, self).__init__()
+... self.fields['birthday'] = DateField()
+>>> p = Person()
+>>> print p
+<tr><td>First name:</td><td><input type="text" name="first_name" /></td></tr>
+<tr><td>Last name:</td><td><input type="text" name="last_name" /></td></tr>
+<tr><td>Birthday:</td><td><input type="text" name="birthday" /></td></tr>
+
+A Form's fields are displayed in the same order in which they were defined.
+>>> class TestForm(Form):
+... field1 = CharField()
+... field2 = CharField()
+... field3 = CharField()
+... field4 = CharField()
+... field5 = CharField()
+... field6 = CharField()
+... field7 = CharField()
+... field8 = CharField()
+... field9 = CharField()
+... field10 = CharField()
+... field11 = CharField()
+... field12 = CharField()
+... field13 = CharField()
+... field14 = CharField()
+>>> p = TestForm()
+>>> print p
+<tr><td>Field1:</td><td><input type="text" name="field1" /></td></tr>
+<tr><td>Field2:</td><td><input type="text" name="field2" /></td></tr>
+<tr><td>Field3:</td><td><input type="text" name="field3" /></td></tr>
+<tr><td>Field4:</td><td><input type="text" name="field4" /></td></tr>
+<tr><td>Field5:</td><td><input type="text" name="field5" /></td></tr>
+<tr><td>Field6:</td><td><input type="text" name="field6" /></td></tr>
+<tr><td>Field7:</td><td><input type="text" name="field7" /></td></tr>
+<tr><td>Field8:</td><td><input type="text" name="field8" /></td></tr>
+<tr><td>Field9:</td><td><input type="text" name="field9" /></td></tr>
+<tr><td>Field10:</td><td><input type="text" name="field10" /></td></tr>
+<tr><td>Field11:</td><td><input type="text" name="field11" /></td></tr>
+<tr><td>Field12:</td><td><input type="text" name="field12" /></td></tr>
+<tr><td>Field13:</td><td><input type="text" name="field13" /></td></tr>
+<tr><td>Field14:</td><td><input type="text" name="field14" /></td></tr>
+"""
+
+if __name__ == "__main__":
+ import doctest
+ doctest.testmod()
diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py
index bcb9c66254..3c31bb0604 100644
--- a/tests/regressiontests/templates/tests.py
+++ b/tests/regressiontests/templates/tests.py
@@ -328,6 +328,21 @@ class Templates(unittest.TestCase):
'ifchanged05': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}', { 'num': (1, 1, 1), 'numx': (1, 2, 3)}, '1123123123'),
'ifchanged06': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}', { 'num': (1, 1, 1), 'numx': (2, 2, 2)}, '1222'),
'ifchanged07': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% for y in numy %}{% ifchanged %}{{ y }}{% endifchanged %}{% endfor %}{% endfor %}{% endfor %}', { 'num': (1, 1, 1), 'numx': (2, 2, 2), 'numy': (3, 3, 3)}, '1233323332333'),
+
+ # Test one parameter given to ifchanged.
+ 'ifchanged-param01': ('{% for n in num %}{% ifchanged n %}..{% endifchanged %}{{ n }}{% endfor %}', { 'num': (1,2,3) }, '..1..2..3'),
+ 'ifchanged-param02': ('{% for n in num %}{% for x in numx %}{% ifchanged n %}..{% endifchanged %}{{ x }}{% endfor %}{% endfor %}', { 'num': (1,2,3), 'numx': (5,6,7) }, '..567..567..567'),
+
+ # Test multiple parameters to ifchanged.
+ 'ifchanged-param03': ('{% for n in num %}{{ n }}{% for x in numx %}{% ifchanged x n %}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}', { 'num': (1,1,2), 'numx': (5,6,6) }, '156156256'),
+
+ # Test a date+hour like construct, where the hour of the last day
+ # is the same but the date had changed, so print the hour anyway.
+ 'ifchanged-param04': ('{% for d in days %}{% ifchanged %}{{ d.day }}{% endifchanged %}{% for h in d.hours %}{% ifchanged d h %}{{ h }}{% endifchanged %}{% endfor %}{% endfor %}', {'days':[{'day':1, 'hours':[1,2,3]},{'day':2, 'hours':[3]},] }, '112323'),
+
+ # Logically the same as above, just written with explicit
+ # ifchanged for the day.
+ 'ifchanged-param04': ('{% for d in days %}{% ifchanged d.day %}{{ d.day }}{% endifchanged %}{% for h in d.hours %}{% ifchanged d.day h %}{{ h }}{% endifchanged %}{% endfor %}{% endfor %}', {'days':[{'day':1, 'hours':[1,2,3]},{'day':2, 'hours':[3]},] }, '112323'),
### IFEQUAL TAG ###########################################################
'ifequal01': ("{% ifequal a b %}yes{% endifequal %}", {"a": 1, "b": 2}, ""),