diff options
author | Adrian Holovaty <adrian@holovaty.com> | 2006-11-24 04:17:58 +0000 |
---|---|---|
committer | Adrian Holovaty <adrian@holovaty.com> | 2006-11-24 04:17:58 +0000 |
commit | 1b54fc3abace9b5a9752c2d950222c487b960899 (patch) | |
tree | a601c2962f93b2ee860362c796f68dda8ebca452 | |
parent | 47fca880e3d7bd19744b6c77ebb4667225308a7b (diff) | |
download | django-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
87 files changed, 3022 insertions, 637 deletions
@@ -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 Binary files differindex aef68099d3..0dd2b26dff 100644 --- a/django/conf/locale/de/LC_MESSAGES/django.mo +++ b/django/conf/locale/de/LC_MESSAGES/django.mo 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 Binary files differindex 1c95d6b5d9..4a7d8e41f0 100644 --- a/django/conf/locale/el/LC_MESSAGES/django.mo +++ b/django/conf/locale/el/LC_MESSAGES/django.mo 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 " ÎÏα:" #: 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 Binary files differnew file mode 100644 index 0000000000..7d43b315fe --- /dev/null +++ b/django/conf/locale/el/LC_MESSAGES/djangojs.mo 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 Binary files differindex dd96bf99ce..a247bb2385 100644 --- a/django/conf/locale/es_AR/LC_MESSAGES/django.mo +++ b/django/conf/locale/es_AR/LC_MESSAGES/django.mo 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 Binary files differindex f24c9a12d0..3f89e3e33f 100644 --- a/django/conf/locale/es_AR/LC_MESSAGES/djangojs.mo +++ b/django/conf/locale/es_AR/LC_MESSAGES/djangojs.mo 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 Binary files differindex 3df145a0d3..7f506f972f 100644 --- a/django/conf/locale/pt_BR/LC_MESSAGES/django.mo +++ b/django/conf/locale/pt_BR/LC_MESSAGES/django.mo 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 Binary files differindex f8c53203c9..31a2b1b3c0 100644 --- a/django/conf/locale/pt_BR/LC_MESSAGES/djangojs.mo +++ b/django/conf/locale/pt_BR/LC_MESSAGES/djangojs.mo 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 "quoted" & 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 "quoted" & 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 "quoted" & 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 "quoted" & 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 "quoted" & 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}, ""), |