diff options
author | Malcolm Tredinnick <malcolm.tredinnick@gmail.com> | 2008-03-20 19:56:44 +0000 |
---|---|---|
committer | Malcolm Tredinnick <malcolm.tredinnick@gmail.com> | 2008-03-20 19:56:44 +0000 |
commit | 334279f8dd3abd98aa527a1658cb3ff5c3973ec7 (patch) | |
tree | f440439fc9f79a39cfb90c16bf5c2df0106b14ee | |
parent | 04da22633fcda983cb9ee69e63b2ebe99301b717 (diff) | |
download | django-334279f8dd3abd98aa527a1658cb3ff5c3973ec7.tar.gz |
queryset-refactor: Merged from trunk up to [7338].
git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@7341 bcc190cf-cafb-0310-a4f2-bffc1f526a37
58 files changed, 2126 insertions, 1006 deletions
@@ -59,6 +59,7 @@ answer newbie questions, and generally made Django that much better: Arthur <avandorp@gmail.com> David Avsajanishvili <avsd05@gmail.com> axiak@mit.edu + Niran Babalola <niran@niran.org> Morten Bagai <m@bagai.com> Mikaël Barbero <mikael.barbero nospam at nospam free.fr> Jiri Barton @@ -145,6 +146,7 @@ answer newbie questions, and generally made Django that much better: Jorge Gajon <gajon@gajon.org> gandalf@owca.info Marc Garcia <marc.garcia@accopensys.com> + Alex Gaynor <alex.gaynor@gmail.com> Andy Gayton <andy-django@thecablelounge.com> Baishampayan Ghose Dimitris Glezos <dimitris@glezos.com> @@ -243,6 +245,7 @@ answer newbie questions, and generally made Django that much better: michael.mcewan@gmail.com michal@plovarna.cz Mikko Hellsing <mikko@sorl.net> + Daniel Lindsley <polarcowz@gmail.com> Orestis Markou <orestis@orestis.gr> Slawek Mikula <slawek dot mikula at gmail dot com> mitakummaa@gmail.com @@ -256,6 +259,7 @@ answer newbie questions, and generally made Django that much better: Robin Munn <http://www.geekforgod.com/> Robert Myers <myer0052@gmail.com> Nebojša Dorđević + Doug Napoleone <doug@dougma.com> Gopal Narayanan <gopastro@gmail.com> Fraser Nevett <mail@nevett.org> Sam Newman <http://www.magpiebrain.com/> @@ -269,6 +273,7 @@ answer newbie questions, and generally made Django that much better: Barry Pederson <bp@barryp.org> permonik@mesias.brnonet.cz petr.marhoun@gmail.com + peter@mymart.com pgross@thoughtworks.com phaedo <http://phaedo.cx/> phil@produxion.net @@ -307,6 +312,7 @@ answer newbie questions, and generally made Django that much better: serbaut@gmail.com John Shaffer <jshaffer2112@gmail.com> Pete Shinners <pete@shinners.org> + Leo Shklovskii jason.sidabras@gmail.com Jozko Skrablin <jozko.skrablin@gmail.com> Ben Slavin <benjamin.slavin@gmail.com> diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index 6be789a853..3917d40943 100644 --- a/django/conf/global_settings.py +++ b/django/conf/global_settings.py @@ -287,7 +287,7 @@ SESSION_COOKIE_PATH = '/' # The path of the sessio SESSION_SAVE_EVERY_REQUEST = False # Whether to save the session data on every request. SESSION_EXPIRE_AT_BROWSER_CLOSE = False # Whether sessions expire when a user closes his browser. SESSION_ENGINE = 'django.contrib.sessions.backends.db' # The module to store session data -SESSION_FILE_PATH = '/tmp/' # Directory to store session files if using the file session module +SESSION_FILE_PATH = None # Directory to store session files if using the file session module. If set to None the backend will use a sensible default. ######### # CACHE # diff --git a/django/conf/locale/de/LC_MESSAGES/django.mo b/django/conf/locale/de/LC_MESSAGES/django.mo Binary files differindex 683848a50d..226c64c560 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 1601d2c3b2..754c840de0 100644 --- a/django/conf/locale/de/LC_MESSAGES/django.po +++ b/django/conf/locale/de/LC_MESSAGES/django.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2008-03-03 15:46+0100\n" +"POT-Creation-Date: 2008-03-18 15:10-0500\n" "PO-Revision-Date: 2008-03-03 16:10+0100\n" -"Last-Translator: Michael Radziej <mir@noris.de>\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -18,7 +18,6 @@ msgstr "" "X-Poedit-Country: GERMANY\n" "X-Poedit-SourceCharset: utf-8\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"X-Generator: KBabel 1.11.4\n" #: conf/global_settings.py:39 msgid "Arabic" @@ -217,10 +216,8 @@ msgstr "" "<h3>Nach %s:</h3>\n" "<ul>\n" -#: contrib/admin/filterspecs.py:74 -#: contrib/admin/filterspecs.py:92 -#: contrib/admin/filterspecs.py:147 -#: contrib/admin/filterspecs.py:173 +#: contrib/admin/filterspecs.py:74 contrib/admin/filterspecs.py:92 +#: contrib/admin/filterspecs.py:147 contrib/admin/filterspecs.py:173 msgid "All" msgstr "Alle" @@ -244,20 +241,17 @@ msgstr "Diesen Monat" msgid "This year" msgstr "Dieses Jahr" -#: contrib/admin/filterspecs.py:147 -#: newforms/widgets.py:231 +#: contrib/admin/filterspecs.py:147 newforms/widgets.py:231 #: oldforms/__init__.py:592 msgid "Yes" msgstr "Ja" -#: contrib/admin/filterspecs.py:147 -#: newforms/widgets.py:231 +#: contrib/admin/filterspecs.py:147 newforms/widgets.py:231 #: oldforms/__init__.py:592 msgid "No" msgstr "Nein" -#: contrib/admin/filterspecs.py:154 -#: newforms/widgets.py:231 +#: contrib/admin/filterspecs.py:154 newforms/widgets.py:231 #: oldforms/__init__.py:592 msgid "Unknown" msgstr "Unbekannt" @@ -297,7 +291,8 @@ msgstr "Seite nicht gefunden" #: contrib/admin/templates/admin/404.html:10 msgid "We're sorry, but the requested page could not be found." -msgstr "Es tut uns leid, aber die angeforderte Seite konnte nicht gefunden werden." +msgstr "" +"Es tut uns leid, aber die angeforderte Seite konnte nicht gefunden werden." #: contrib/admin/templates/admin/500.html:4 #: contrib/admin/templates/admin/base.html:37 @@ -329,8 +324,13 @@ msgid "Server Error <em>(500)</em>" msgstr "Serverfehler <em>(500)</em>" #: contrib/admin/templates/admin/500.html:10 -msgid "There's been an error. It's been reported to the site administrators via e-mail and should be fixed shortly. Thanks for your patience." -msgstr "Ein Fehler ist aufgetreten. Dieser Fehler wurde an die Serververwalter per E-Mail weitergegeben und sollte bald behoben sein. Vielen Dank für Ihr Verständnis." +msgid "" +"There's been an error. It's been reported to the site administrators via e-" +"mail and should be fixed shortly. Thanks for your patience." +msgstr "" +"Ein Fehler ist aufgetreten. Dieser Fehler wurde an die Serververwalter per E-" +"Mail weitergegeben und sollte bald behoben sein. Vielen Dank für Ihr " +"Verständnis." #: contrib/admin/templates/admin/base.html:26 msgid "Welcome," @@ -401,13 +401,23 @@ msgstr "Löschen" #: contrib/admin/templates/admin/delete_confirmation.html:13 #, python-format -msgid "Deleting the %(object_name)s '%(escaped_object)s' would result in deleting related objects, but your account doesn't have permission to delete the following types of objects:" -msgstr "Die Löschung des %(object_name)s '%(escaped_object)s' hätte die Löschung von abhängigen Daten zur Folge, aber Sie haben nicht die nötigen Rechte um die folgenden abhängigen Daten zu löschen:" +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"Die Löschung des %(object_name)s '%(escaped_object)s' hätte die Löschung von " +"abhängigen Daten zur Folge, aber Sie haben nicht die nötigen Rechte um die " +"folgenden abhängigen Daten zu löschen:" #: contrib/admin/templates/admin/delete_confirmation.html:20 #, python-format -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, dass Sie %(object_name)s \"%(escaped_object)s\" löschen wollen? Es werden zusätzlich die folgenden abhängigen Daten mit gelöscht:" +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, 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:25 msgid "Yes, I'm sure" @@ -438,7 +448,7 @@ msgstr "Ändern" #: contrib/admin/templates/admin/index.html:44 msgid "You don't have permission to edit anything." -msgstr "Sie haben keine Berechtigung irgendwas zu ändern." +msgstr "Sie haben keine Berechtigung, irgendetwas zu ändern." #: contrib/admin/templates/admin/index.html:52 msgid "Recent Actions" @@ -453,8 +463,14 @@ msgid "None available" msgstr "Keine vorhanden" #: contrib/admin/templates/admin/invalid_setup.html:8 -msgid "Something's wrong with your database installation. Make sure the appropriate database tables have been created, and make sure the database is readable by the appropriate user." -msgstr "Etwas stimmt nicht mit der Datenbankkonfiguration. Bitte sicherstellen, dass die richtigen Datenbanktabellen angelegt wurden und die Datenbank vom verwendeten Datenbankbenutzer auch lesbar ist." +msgid "" +"Something's wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"Etwas stimmt nicht mit der Datenbankkonfiguration. Bitte sicherstellen, dass " +"die richtigen Datenbanktabellen angelegt wurden und die Datenbank vom " +"verwendeten Datenbankbenutzer auch lesbar ist." #: contrib/admin/templates/admin/login.html:17 #: contrib/comments/templates/comments/form.html:6 @@ -489,8 +505,12 @@ msgid "DATE_WITH_TIME_FULL" msgstr "j. N Y, H:i" #: contrib/admin/templates/admin/object_history.html:35 -msgid "This object doesn't have a change history. It probably wasn't added via this admin site." -msgstr "Dieses Objekt hat keine Änderungsgeschichte. Es wurde möglicherweise nicht über diese Verwaltungsseiten angelegt." +msgid "" +"This object doesn't have a change history. It probably wasn't added via this " +"admin site." +msgstr "" +"Dieses Objekt hat keine Änderungsgeschichte. Es wurde möglicherweise nicht " +"über diese Verwaltungsseiten angelegt." #: contrib/admin/templates/admin/pagination.html:10 msgid "Show all" @@ -529,8 +549,12 @@ msgid "Save" msgstr "Sichern" #: contrib/admin/templates/admin/auth/user/add_form.html:6 -msgid "First, enter a username and password. Then, you'll be able to edit more user options." -msgstr "Zuerst einen Benutzer und ein Passwort eingeben. Danach können weitere Optionen für den Benutzer geändert werden." +msgid "" +"First, enter a username and password. Then, you'll be able to edit more user " +"options." +msgstr "" +"Zuerst einen Benutzer und ein Passwort eingeben. Danach können weitere " +"Optionen für den Benutzer geändert werden." #: contrib/admin/templates/admin/auth/user/add_form.html:12 msgid "Username" @@ -554,7 +578,9 @@ msgstr "Bitte das gleiche Passwort zur Überprüfung nochmal eingeben." #: contrib/admin/templates/admin/auth/user/change_password.html:27 #, python-format msgid "Enter a new password for the user <strong>%(username)s</strong>." -msgstr "Bitte geben Sie ein neues Passwort für den Benutzer <strong>%(username)s</strong> ein." +msgstr "" +"Bitte geben Sie ein neues Passwort für den Benutzer <strong>%(username)s</" +"strong> ein." #: contrib/admin/templates/admin_doc/bookmarklets.html:3 msgid "Bookmarklets" @@ -575,9 +601,12 @@ msgid "" "your computer is \"internal\").</p>\n" msgstr "" "\n" -"<p class=\"help\">Um Bookmarklets zu installieren, müssen diese Links in die\n" -"Browser-Werkzeugleiste gezogen werden, oder mittels rechter Maustaste in die\n" -"Bookmarks gespeichert werden. Danach können die Bookmarklets von jeder Seite\n" +"<p class=\"help\">Um Bookmarklets zu installieren, müssen diese Links in " +"die\n" +"Browser-Werkzeugleiste gezogen werden, oder mittels rechter Maustaste in " +"die\n" +"Bookmarks gespeichert werden. Danach können die Bookmarklets von jeder " +"Seite\n" "aufgerufen werden. Einige Bookmarklets sind für den Zugriff von 'internen'\n" "Rechnern eingeschränkt. Falls nicht klar ist, ob ein Rechner als 'intern'\n" "bewertet wird, bitte den Administrator fragen.</p>\n" @@ -587,16 +616,24 @@ msgid "Documentation for this page" msgstr "Dokumentation für diese Seite" #: contrib/admin/templates/admin_doc/bookmarklets.html:19 -msgid "Jumps you from any page to the documentation for the view that generates that page." -msgstr "Springt von jeder Seite zu der Dokumentation für den View der diese Seite erzeugt." +msgid "" +"Jumps you from any page to the documentation for the view that generates " +"that page." +msgstr "" +"Springt von jeder Seite zu der Dokumentation für den View der diese Seite " +"erzeugt." #: contrib/admin/templates/admin_doc/bookmarklets.html:21 msgid "Show object ID" msgstr "Objekt-ID anzeigen" #: contrib/admin/templates/admin_doc/bookmarklets.html:22 -msgid "Shows the content-type and unique ID for pages that represent a single object." -msgstr "Zeigt den Content-Type und die eindeutige ID für Seiten die ein einzelnes Objekt repräsentieren." +msgid "" +"Shows the content-type and unique ID for pages that represent a single " +"object." +msgstr "" +"Zeigt den Content-Type und die eindeutige ID für Seiten die ein einzelnes " +"Objekt repräsentieren." #: contrib/admin/templates/admin_doc/bookmarklets.html:24 msgid "Edit this object (current window)" @@ -604,7 +641,9 @@ msgstr "Dieses Objekt im aktuellen Fenster ändern." #: contrib/admin/templates/admin_doc/bookmarklets.html:25 msgid "Jumps to the admin page for pages that represent a single object." -msgstr "Springt zu der Administrationsseite für dieses Objekt, wenn diese Seite ein Objekt repräsentiert." +msgstr "" +"Springt zu der Administrationsseite für dieses Objekt, wenn diese Seite ein " +"Objekt repräsentiert." #: contrib/admin/templates/admin_doc/bookmarklets.html:27 msgid "Edit this object (new window)" @@ -612,7 +651,8 @@ msgstr "Dieses Objekt in einem neuen Fenster ändern." #: contrib/admin/templates/admin_doc/bookmarklets.html:28 msgid "As above, but opens the admin page in a new window." -msgstr "Wie zuvor, aber öffnet die Administrationsseite in einem neuen Fenster." +msgstr "" +"Wie zuvor, aber öffnet die Administrationsseite in einem neuen Fenster." #: contrib/admin/templates/registration/logged_out.html:8 msgid "Thanks for spending some quality time with the Web site today." @@ -639,8 +679,13 @@ msgid "Your password was changed." msgstr "Ihr Passwort wurde geändert." #: contrib/admin/templates/registration/password_change_form.html:11 -msgid "Please enter your old password, for security's sake, and then enter your new password twice so we can verify you typed it in correctly." -msgstr "Bitte geben Sie aus Sicherheitsgründen erst Ihr altes Passwort und darunter dann zweimal (um sicherzustellen, dass Sie es korrekt eingegeben haben) das neue Kennwort ein." +msgid "" +"Please enter your old password, for security's sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"Bitte geben Sie aus Sicherheitsgründen erst Ihr altes Passwort und darunter " +"dann zweimal (um sicherzustellen, dass Sie es korrekt eingegeben haben) das " +"neue Kennwort ein." #: contrib/admin/templates/registration/password_change_form.html:16 msgid "Old password:" @@ -671,8 +716,12 @@ msgid "Password reset successful" msgstr "Passwort wurde erfolgreich zurückgesetzt" #: contrib/admin/templates/registration/password_reset_done.html:12 -msgid "We've e-mailed a new password to the e-mail address you submitted. You should be receiving it shortly." -msgstr "Wir haben ein neues Passwort an die von Ihnen angegebene E-Mail-Adresse geschickt. Sie sollten es in Kürze erhalten." +msgid "" +"We've e-mailed a new password to the e-mail address you submitted. You " +"should be receiving it shortly." +msgstr "" +"Wir haben ein neues Passwort an die von Ihnen angegebene E-Mail-Adresse " +"geschickt. Sie sollten es in Kürze erhalten." #: contrib/admin/templates/registration/password_reset_email.html:2 msgid "You're receiving this e-mail because you requested a password reset" @@ -706,8 +755,12 @@ msgid "The %(site_name)s team" msgstr "Das Team von %(site_name)s" #: contrib/admin/templates/registration/password_reset_form.html:12 -msgid "Forgotten your password? Enter your e-mail address below, and we'll reset your password and e-mail the new one to you." -msgstr "Passwort vergessen? Einfach die E-Mail-Adresse eingeben und wir setzen das Passwort zurück und lassen es Ihnen per E-Mail zukommen." +msgid "" +"Forgotten your password? Enter your e-mail address below, and we'll reset " +"your password and e-mail the new one to you." +msgstr "" +"Passwort vergessen? Einfach die E-Mail-Adresse eingeben und wir setzen das " +"Passwort zurück und lassen es Ihnen per E-Mail zukommen." #: contrib/admin/templates/registration/password_reset_form.html:16 msgid "E-mail address:" @@ -737,14 +790,12 @@ msgstr "Ändern:" msgid "All dates" msgstr "Alle Tage" -#: contrib/admin/views/auth.py:20 -#: contrib/admin/views/main.py:267 +#: contrib/admin/views/auth.py:20 contrib/admin/views/main.py:267 #, python-format msgid "The %(name)s \"%(obj)s\" was added successfully." msgstr "%(name)s \"%(obj)s\" wurde erfolgreich hinzugefügt." -#: contrib/admin/views/auth.py:25 -#: contrib/admin/views/main.py:271 +#: contrib/admin/views/auth.py:25 contrib/admin/views/main.py:271 #: contrib/admin/views/main.py:356 msgid "You may edit it again below." msgstr "Das Element kann jetzt weiter bearbeitet werden." @@ -762,18 +813,29 @@ msgstr "Passwort erfolgreich geändert." msgid "Change password: %s" msgstr "Passwort ändern: %s" -#: contrib/admin/views/decorators.py:17 -#: contrib/auth/forms.py:60 -msgid "Please enter a correct username and password. Note that both fields are case-sensitive." -msgstr "Bitte einen gültigen Benutzernamen und ein Passwort eingeben. Beide Felder berücksichtigen die Groß-/Kleinschreibung." +#: contrib/admin/views/decorators.py:17 contrib/auth/forms.py:60 +msgid "" +"Please enter a correct username and password. Note that both fields are case-" +"sensitive." +msgstr "" +"Bitte einen gültigen Benutzernamen und ein Passwort eingeben. Beide Felder " +"berücksichtigen die Groß-/Kleinschreibung." #: contrib/admin/views/decorators.py:69 -msgid "Please log in again, because your session has expired. Don't worry: Your submission has been saved." -msgstr "Bitte neu anmelden, da die Session ausgelaufen ist. Keine Angst, die Beiträge wurden gesichert." +msgid "" +"Please log in again, because your session has expired. Don't worry: Your " +"submission has been saved." +msgstr "" +"Bitte neu anmelden, da die Session ausgelaufen ist. Keine Angst, die " +"Beiträge wurden gesichert." #: contrib/admin/views/decorators.py:76 -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, dass der Browser keine Cookies akzeptiert. Bitte im Browser Cookies aktivieren und diese Seite neu laden." +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, dass der Browser keine Cookies akzeptiert. Bitte im " +"Browser Cookies aktivieren und diese Seite neu laden." #: contrib/admin/views/decorators.py:90 msgid "Usernames cannot contain the '@' character." @@ -782,22 +844,21 @@ msgstr "Benutzernamen dürfen das Zeichen '@' nicht enthalten." #: contrib/admin/views/decorators.py:92 #, python-format msgid "Your e-mail address is not your username. Try '%s' instead." -msgstr "Die E-Mail-Adresse entspricht nicht Ihrem Benutzernamen. Bitte stattdessen '%s' versuchen." +msgstr "" +"Die E-Mail-Adresse entspricht nicht Ihrem Benutzernamen. Bitte stattdessen " +"'%s' versuchen." -#: contrib/admin/views/doc.py:48 -#: contrib/admin/views/doc.py:50 +#: contrib/admin/views/doc.py:48 contrib/admin/views/doc.py:50 #: contrib/admin/views/doc.py:52 msgid "tag:" msgstr "Schlagwort:" -#: contrib/admin/views/doc.py:79 -#: contrib/admin/views/doc.py:81 +#: contrib/admin/views/doc.py:79 contrib/admin/views/doc.py:81 #: contrib/admin/views/doc.py:83 msgid "filter:" msgstr "Filter:" -#: contrib/admin/views/doc.py:137 -#: contrib/admin/views/doc.py:139 +#: contrib/admin/views/doc.py:137 contrib/admin/views/doc.py:139 #: contrib/admin/views/doc.py:141 msgid "view:" msgstr "Ansicht:" @@ -817,10 +878,8 @@ msgstr "Modell %(name)r wurde nicht in Anwendung %(label)r gefunden" msgid "the related `%(label)s.%(type)s` object" msgstr "Das verknüpfte `%(label)s.%(type)s` Objekt" -#: contrib/admin/views/doc.py:185 -#: contrib/admin/views/doc.py:207 -#: contrib/admin/views/doc.py:221 -#: contrib/admin/views/doc.py:226 +#: contrib/admin/views/doc.py:185 contrib/admin/views/doc.py:207 +#: contrib/admin/views/doc.py:221 contrib/admin/views/doc.py:226 msgid "model:" msgstr "Modell:" @@ -844,12 +903,9 @@ msgstr "Anzahl von %s" msgid "Fields on %s objects" msgstr "Felder am %s Objekt" -#: contrib/admin/views/doc.py:293 -#: contrib/admin/views/doc.py:304 -#: contrib/admin/views/doc.py:306 -#: contrib/admin/views/doc.py:312 -#: contrib/admin/views/doc.py:313 -#: contrib/admin/views/doc.py:315 +#: contrib/admin/views/doc.py:293 contrib/admin/views/doc.py:304 +#: contrib/admin/views/doc.py:306 contrib/admin/views/doc.py:312 +#: contrib/admin/views/doc.py:313 contrib/admin/views/doc.py:315 msgid "Integer" msgstr "Ganzzahl" @@ -857,8 +913,7 @@ msgstr "Ganzzahl" msgid "Boolean (Either True or False)" msgstr "Boolscher Wert (True oder False)" -#: contrib/admin/views/doc.py:295 -#: contrib/admin/views/doc.py:314 +#: contrib/admin/views/doc.py:295 contrib/admin/views/doc.py:314 #, python-format msgid "String (up to %(max_length)s)" msgstr "Zeichenkette (bis zu %(max_length)s Zeichen)" @@ -883,8 +938,7 @@ msgstr "Dezimalzahl" msgid "E-mail address" msgstr "E-Mail-Adresse" -#: contrib/admin/views/doc.py:301 -#: contrib/admin/views/doc.py:302 +#: contrib/admin/views/doc.py:301 contrib/admin/views/doc.py:302 #: contrib/admin/views/doc.py:305 msgid "File path" msgstr "Dateipfad" @@ -893,8 +947,7 @@ msgstr "Dateipfad" msgid "Floating point number" msgstr "Fließkommazahl" -#: contrib/admin/views/doc.py:307 -#: contrib/comments/models.py:89 +#: contrib/admin/views/doc.py:307 contrib/comments/models.py:89 msgid "IP address" msgstr "IP-Adresse" @@ -918,8 +971,7 @@ msgstr "Text" msgid "Time" msgstr "Zeit" -#: contrib/admin/views/doc.py:318 -#: contrib/flatpages/models.py:7 +#: contrib/admin/views/doc.py:318 contrib/flatpages/models.py:7 msgid "URL" msgstr "Adresse (URL)" @@ -940,8 +992,7 @@ msgstr "%s ist scheinbar kein urlpattern Objekt" msgid "Site administration" msgstr "Website-Verwaltung" -#: contrib/admin/views/main.py:280 -#: contrib/admin/views/main.py:365 +#: contrib/admin/views/main.py:280 contrib/admin/views/main.py:365 #, python-format msgid "You may add another %s below." msgstr "Jetzt kann ein weiteres Element vom Typ %s angelegt werden." @@ -956,10 +1007,8 @@ msgstr "%s hinzufügen" msgid "Added %s." msgstr "%s hinzugefügt." -#: contrib/admin/views/main.py:344 -#: contrib/admin/views/main.py:346 -#: contrib/admin/views/main.py:348 -#: core/validators.py:283 +#: contrib/admin/views/main.py:344 contrib/admin/views/main.py:346 +#: contrib/admin/views/main.py:348 core/validators.py:283 #: db/models/manipulators.py:309 msgid "and" msgstr "und" @@ -985,8 +1034,11 @@ msgstr "%(name)s \"%(obj)s\" wurde erfolgreich geändert." #: contrib/admin/views/main.py:362 #, python-format -msgid "The %(name)s \"%(obj)s\" was added successfully. You may edit it again below." -msgstr "%(name)s \"%(obj)s\" wurde erfolgreich hinzugefügt. Das Element kann jetzt geändert werden." +msgid "" +"The %(name)s \"%(obj)s\" was added successfully. You may edit it again below." +msgstr "" +"%(name)s \"%(obj)s\" wurde erfolgreich hinzugefügt. Das Element kann jetzt " +"geändert werden." #: contrib/admin/views/main.py:400 #, python-format @@ -1031,8 +1083,7 @@ msgstr "%s zur Änderung auswählen" msgid "Database error" msgstr "Datenbankfehler" -#: contrib/auth/forms.py:17 -#: contrib/auth/forms.py:138 +#: contrib/auth/forms.py:17 contrib/auth/forms.py:138 msgid "The two password fields didn't match." msgstr "Die beiden Passwörter sind nicht identisch." @@ -1041,16 +1092,24 @@ msgid "A user with that username already exists." msgstr "Ein Benutzer mit diesem Namen existiert bereits." #: contrib/auth/forms.py:53 -msgid "Your Web browser doesn't appear to have cookies enabled. Cookies are required for logging in." -msgstr "Der Webbrowser scheint keine Cookies aktiviert zu haben. Cookies sind für die Anmeldung zwingend erforderlich." +msgid "" +"Your Web browser doesn't appear to have cookies enabled. Cookies are " +"required for logging in." +msgstr "" +"Der Webbrowser scheint keine Cookies aktiviert zu haben. Cookies sind für " +"die Anmeldung zwingend erforderlich." #: contrib/auth/forms.py:62 msgid "This account is inactive." msgstr "Dieser Benutzer ist inaktiv." #: contrib/auth/forms.py:84 -msgid "That e-mail address doesn't have an associated user account. Are you sure you've registered?" -msgstr "Zu dieser E-Mail-Adresse existiert kein Benutzer. Sicher, dass Sie sich mit dieser Adresse angemeldet haben?" +msgid "" +"That e-mail address doesn't have an associated user account. Are you sure " +"you've registered?" +msgstr "" +"Zu dieser E-Mail-Adresse existiert kein Benutzer. Sicher, dass Sie sich mit " +"dieser Adresse angemeldet haben?" #: contrib/auth/forms.py:107 #, python-format @@ -1065,8 +1124,7 @@ msgstr "Die beiden neuen Passwörter sind nicht identisch." msgid "Your old password was entered incorrectly. Please enter it again." msgstr "Das alte Passwort war falsch. Bitte neu eingeben." -#: contrib/auth/models.py:73 -#: contrib/auth/models.py:93 +#: contrib/auth/models.py:73 contrib/auth/models.py:93 msgid "name" msgstr "Name" @@ -1078,8 +1136,7 @@ msgstr "Codename" msgid "permission" msgstr "Berechtigung" -#: contrib/auth/models.py:79 -#: contrib/auth/models.py:94 +#: contrib/auth/models.py:79 contrib/auth/models.py:94 msgid "permissions" msgstr "Berechtigungen" @@ -1087,8 +1144,7 @@ msgstr "Berechtigungen" msgid "group" msgstr "Gruppe" -#: contrib/auth/models.py:98 -#: contrib/auth/models.py:141 +#: contrib/auth/models.py:98 contrib/auth/models.py:141 msgid "groups" msgstr "Gruppen" @@ -1097,8 +1153,12 @@ msgid "username" msgstr "Benutzername" #: contrib/auth/models.py:131 -msgid "Required. 30 characters or fewer. Alphanumeric characters only (letters, digits and underscores)." -msgstr "Erforderlich. 30 Zeichen oder weniger. Alphanumerische Zeichen (Buchstaben, Ziffern und Unterstriche sind erlaubt)." +msgid "" +"Required. 30 characters or fewer. Alphanumeric characters only (letters, " +"digits and underscores)." +msgstr "" +"Erforderlich. 30 Zeichen oder weniger. Alphanumerische Zeichen (Buchstaben, " +"Ziffern und Unterstriche sind erlaubt)." #: contrib/auth/models.py:132 msgid "first name" @@ -1117,8 +1177,12 @@ msgid "password" msgstr "Passwort" #: contrib/auth/models.py:135 -msgid "Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change password form</a>." -msgstr "Die Form '[algo]$[salt]$[hexdigest]' verwenden, oder das <a href=\"password/\">Passwort ändern Formular</a> benutzen." +msgid "" +"Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change " +"password form</a>." +msgstr "" +"Die Form '[algo]$[salt]$[hexdigest]' verwenden, oder das <a href=\"password/" +"\">Passwort ändern Formular</a> benutzen." #: contrib/auth/models.py:136 msgid "staff status" @@ -1126,23 +1190,33 @@ msgstr "Administrator" #: contrib/auth/models.py:136 msgid "Designates whether the user can log into this admin site." -msgstr "Legt fest, ob sich der Benutzer an der Administrationsseite anmelden kann." +msgstr "" +"Legt fest, ob sich der Benutzer an der Administrationsseite anmelden kann." #: contrib/auth/models.py:137 msgid "active" msgstr "Aktiv" #: contrib/auth/models.py:137 -msgid "Designates whether this user can log into the Django admin. Unselect this instead of deleting accounts." -msgstr "Legt fest, ob sich der Benutzer an der Administrationsseite anmelden kann. Anstatt einen Benutzer zu löschen, kann er hier auch einfach deaktiviert werden." +msgid "" +"Designates whether this user can log into the Django admin. Unselect this " +"instead of deleting accounts." +msgstr "" +"Legt fest, ob sich der Benutzer an der Administrationsseite anmelden kann. " +"Anstatt einen Benutzer zu löschen, kann er hier auch einfach deaktiviert " +"werden." #: contrib/auth/models.py:138 msgid "superuser status" msgstr "Hauptadmin." #: contrib/auth/models.py:138 -msgid "Designates that this user has all permissions without explicitly assigning them." -msgstr "Legt fest, dass der Benutzer alle Berechtigungen hat, ohne diese einzeln zuweisen zu müssen." +msgid "" +"Designates that this user has all permissions without explicitly assigning " +"them." +msgstr "" +"Legt fest, dass der Benutzer alle Berechtigungen hat, ohne diese einzeln " +"zuweisen zu müssen." #: contrib/auth/models.py:139 msgid "last login" @@ -1153,8 +1227,12 @@ msgid "date joined" msgstr "Mitglied seit" #: contrib/auth/models.py:142 -msgid "In addition to the permissions manually assigned, this user will also get all permissions granted to each group he/she is in." -msgstr "Zusätzlich zu den manuell angelegten Rechten erhält dieser Benutzer auch alle Rechte, die seine zugewiesenen Gruppen haben." +msgid "" +"In addition to the permissions manually assigned, this user will also get " +"all permissions granted to each group he/she is in." +msgstr "" +"Zusätzlich zu den manuell angelegten Rechten erhält dieser Benutzer auch " +"alle Rechte, die seine zugewiesenen Gruppen haben." #: contrib/auth/models.py:143 msgid "user permissions" @@ -1192,8 +1270,7 @@ msgstr "Mitteilung" msgid "Logged out" msgstr "Abgemeldet" -#: contrib/comments/models.py:71 -#: contrib/comments/models.py:176 +#: contrib/comments/models.py:71 contrib/comments/models.py:176 msgid "object ID" msgstr "Objekt-ID" @@ -1201,8 +1278,7 @@ msgstr "Objekt-ID" msgid "headline" msgstr "Überschrift" -#: contrib/comments/models.py:73 -#: contrib/comments/models.py:95 +#: contrib/comments/models.py:73 contrib/comments/models.py:95 #: contrib/comments/models.py:177 msgid "comment" msgstr "Kommentar" @@ -1243,13 +1319,11 @@ msgstr "Bewertung #8" msgid "is valid rating" msgstr "ist eine Bewertung" -#: contrib/comments/models.py:87 -#: contrib/comments/models.py:179 +#: contrib/comments/models.py:87 contrib/comments/models.py:179 msgid "date/time submitted" msgstr "Datum/Zeit Erstellung" -#: contrib/comments/models.py:88 -#: contrib/comments/models.py:180 +#: contrib/comments/models.py:88 contrib/comments/models.py:180 msgid "is public" msgstr "ist öffentlich" @@ -1258,15 +1332,18 @@ msgid "is removed" msgstr "ist gelöscht" #: contrib/comments/models.py:90 -msgid "Check this box if the comment is inappropriate. A \"This comment has been removed\" message will be displayed instead." -msgstr "Hier einen Haken setzen, wenn der Kommentar unpassend ist. Stattdessen wird dann \"Dieser Kommentar wurde entfernt\" Meldung angezeigt." +msgid "" +"Check this box if the comment is inappropriate. A \"This comment has been " +"removed\" message will be displayed instead." +msgstr "" +"Hier einen Haken setzen, wenn der Kommentar unpassend ist. Stattdessen wird " +"dann \"Dieser Kommentar wurde entfernt\" Meldung angezeigt." #: contrib/comments/models.py:96 msgid "comments" msgstr "Kommentare" -#: contrib/comments/models.py:140 -#: contrib/comments/models.py:222 +#: contrib/comments/models.py:140 contrib/comments/models.py:222 msgid "Content object" msgstr "Inhaltsobjekt" @@ -1408,25 +1485,32 @@ msgid "Your name:" msgstr "Ihr Name:" #: contrib/comments/views/comments.py:28 -msgid "This rating is required because you've entered at least one other rating." -msgstr "Diese Abstimmung ist zwingend erforderlich, da Sie an mindestens einer weiteren Abstimmung teilnehmen." +msgid "" +"This rating is required because you've entered at least one other rating." +msgstr "" +"Diese Abstimmung ist zwingend erforderlich, da Sie an mindestens einer " +"weiteren Abstimmung teilnehmen." #: contrib/comments/views/comments.py:112 #, python-format msgid "" -"This comment was posted by a user who has posted fewer than %(count)s comment:\n" +"This comment was posted by a user who has posted fewer than %(count)s " +"comment:\n" "\n" "%(text)s" msgid_plural "" -"This comment was posted by a user who has posted fewer than %(count)s comments:\n" +"This comment was posted by a user who has posted fewer than %(count)s " +"comments:\n" "\n" "%(text)s" msgstr[0] "" -"Dieser Kommentar ist von einem Benutzer mit weniger als %(count)s Kommentar:\n" +"Dieser Kommentar ist von einem Benutzer mit weniger als %(count)s " +"Kommentar:\n" "\n" "%(text)s" msgstr[1] "" -"Dieser Kommentar ist von einem Benutzer mit weniger als %(count)s Kommentaren:\n" +"Dieser Kommentar ist von einem Benutzer mit weniger als %(count)s " +"Kommentaren:\n" "\n" "%(text)s" @@ -1454,17 +1538,23 @@ msgstr "Eines oder mehrere der erforderlichen Felder fehlen" #: contrib/comments/views/comments.py:198 #: contrib/comments/views/comments.py:289 msgid "Somebody tampered with the comment form (security violation)" -msgstr "Jemand hat mit dem Kommentarformular herumgespielt (Sicherheitsverletzung)" +msgstr "" +"Jemand hat mit dem Kommentarformular herumgespielt (Sicherheitsverletzung)" #: contrib/comments/views/comments.py:208 #: contrib/comments/views/comments.py:295 -msgid "The comment form had an invalid 'target' parameter -- the object ID was invalid" -msgstr "Das Kommentarformular hatte einen falschen 'target' Parameter -- die Objekt-ID ist ungültig." +msgid "" +"The comment form had an invalid 'target' parameter -- the object ID was " +"invalid" +msgstr "" +"Das Kommentarformular hatte einen falschen 'target' Parameter -- die Objekt-" +"ID ist ungültig." #: contrib/comments/views/comments.py:259 #: contrib/comments/views/comments.py:324 msgid "The comment form didn't provide either 'preview' or 'post'" -msgstr "Das Kommentarformular wurde nicht mit 'preview' oder 'post' abgeschickt" +msgstr "" +"Das Kommentarformular wurde nicht mit 'preview' oder 'post' abgeschickt" #: contrib/comments/views/karma.py:21 msgid "Anonymous users cannot vote" @@ -1478,21 +1568,23 @@ msgstr "Ungültige Kommentar-ID" msgid "No voting for yourself" msgstr "Keine Abstimmung für dich selbst" -#: contrib/contenttypes/models.py:37 +#: contrib/contenttypes/models.py:67 msgid "python model class name" msgstr "Python Model-Klassenname" -#: contrib/contenttypes/models.py:40 +#: contrib/contenttypes/models.py:71 msgid "content type" msgstr "Inhaltstyp" -#: contrib/contenttypes/models.py:41 +#: contrib/contenttypes/models.py:72 msgid "content types" msgstr "Inhaltstypen" #: contrib/flatpages/models.py:8 -msgid "Example: '/about/contact/'. Make sure to have leading and trailing slashes." -msgstr "Beispiel: '/about/contact/'. Wichtig: vorne und hinten muss ein / stehen." +msgid "" +"Example: '/about/contact/'. Make sure to have leading and trailing slashes." +msgstr "" +"Beispiel: '/about/contact/'. Wichtig: Am Anfang und Ende muss ein / stehen." #: contrib/flatpages/models.py:9 msgid "title" @@ -1511,8 +1603,12 @@ msgid "template name" msgstr "Name der Vorlage" #: contrib/flatpages/models.py:13 -msgid "Example: 'flatpages/contact_page.html'. If this isn't provided, the system will use 'flatpages/default.html'." -msgstr "Beispiel: 'flatpages/contact_page.html'. Wenn dieses Feld nicht gefüllt ist, wird 'flatpages/default.html' als Standard gewählt." +msgid "" +"Example: 'flatpages/contact_page.html'. If this isn't provided, the system " +"will use 'flatpages/default.html'." +msgstr "" +"Beispiel: 'flatpages/contact_page.html'. Wenn dieses Feld nicht gefüllt ist, " +"wird 'flatpages/default.html' als Standard gewählt." #: contrib/flatpages/models.py:14 msgid "registration required" @@ -1520,7 +1616,9 @@ msgstr "Registrierung erforderlich" #: contrib/flatpages/models.py:14 msgid "If this is checked, only logged-in users will be able to view the page." -msgstr "Wenn hier ein Haken gesetzt ist, können nur angemeldete Benutzer diese Seite sehen." +msgstr "" +"Wenn hier ein Haken gesetzt ist, können nur angemeldete Benutzer diese Seite " +"sehen." #: contrib/flatpages/models.py:18 msgid "flat page" @@ -1618,31 +1716,31 @@ msgstr "gestern" #: contrib/localflavor/ar/forms.py:27 #, fuzzy msgid "Enter a postal code in the format NNNN or ANNNNAAA." -msgstr "Bitte eine gültige Postleitzahl im Format XXXXXXX oder XXX-XXXX eingeben." +msgstr "" +"Bitte eine gültige Postleitzahl im Format NNNN oder ANNNNAAA eingeben." -#: contrib/localflavor/ar/forms.py:49 -#: contrib/localflavor/br/forms.py:96 -#: contrib/localflavor/br/forms.py:135 -#: contrib/localflavor/pe/forms.py:23 +#: contrib/localflavor/ar/forms.py:49 contrib/localflavor/br/forms.py:96 +#: contrib/localflavor/br/forms.py:135 contrib/localflavor/pe/forms.py:23 #: contrib/localflavor/pe/forms.py:51 #, fuzzy msgid "This field requires only numbers." -msgstr "Dieses Feld ist zwingend erforderlich." +msgstr "Dieses Feld darf nur Ziffern enthalten." #: contrib/localflavor/ar/forms.py:50 #, fuzzy msgid "This field requires 7 or 8 digits." -msgstr "Dieses Feld ist zwingend erforderlich." +msgstr "Dieses Feld benötigt 7 oder 8 Ziffern." #: contrib/localflavor/ar/forms.py:79 #, fuzzy msgid "Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format." -msgstr "Bitte eine gültige deutsche Personalausweisnummer im Format XXXXXXXXXXX-XXXXXXX-XXXXXXX-X eingeben." +msgstr "" +"Bitte eine gültige CUIT im Format XX-XXXXXXXX-X oder XXXXXXXXXXXX eingeben." #: contrib/localflavor/ar/forms.py:80 #, fuzzy msgid "Invalid CUIT." -msgstr "Ungültige URL: %s" +msgstr "Ungültige CUIT." #: contrib/localflavor/au/forms.py:16 msgid "Enter a 4 digit post code." @@ -1654,30 +1752,32 @@ msgstr "Bitte eine gültige Postleitzahl im Format XXXXX-XXX eingeben." #: contrib/localflavor/br/forms.py:30 msgid "Phone numbers must be in XX-XXXX-XXXX format." -msgstr "Telefonnummern müssen das Format XXX-XXX-XXXX haben." +msgstr "Telefonnummern müssen das Format XX-XXXX-XXXX haben." #: contrib/localflavor/br/forms.py:58 #, fuzzy -msgid "Select a valid brazilian state. That state is not one of the available states." -msgstr "Bitte eine gültige Auswahl treffen." +msgid "" +"Select a valid brazilian state. That state is not one of the available " +"states." +msgstr "Bitte einen gültigen brasilianischen Bundesstaat auswählen." #: contrib/localflavor/br/forms.py:94 msgid "Invalid CPF number." -msgstr "" +msgstr "Ungültige CPF-Nummer." #: contrib/localflavor/br/forms.py:95 #, fuzzy msgid "This field requires at most 11 digits or 14 characters." -msgstr "Bitte sicherstellen, dass der Text maximal %d Zeichen hat." +msgstr "Dieses Feld benötigt mindestens 11 Ziffern oder 14 Zeichen." #: contrib/localflavor/br/forms.py:134 msgid "Invalid CNPJ number." -msgstr "" +msgstr "Ungültige CNPJ-Nummer." #: contrib/localflavor/br/forms.py:136 #, fuzzy msgid "This field requires at least 14 digits" -msgstr "Dieses Feld ist zwingend erforderlich." +msgstr "Dieses Feld benötigt mindestens 14 Ziffern." #: contrib/localflavor/ca/forms.py:17 #, fuzzy @@ -1687,7 +1787,9 @@ msgstr "Bitte eine gültige Postleitzahl im Format XXXXX eingeben." #: contrib/localflavor/ca/forms.py:88 #, fuzzy msgid "Enter a valid Canadian Social Insurance number in XXX-XXX-XXX format." -msgstr "Bitte eine gültige US-amerikanische Sozialversicherungsnummer im Format XXX-XX-XXXX eingeben." +msgstr "" +"Bitte eine gültige kanadische Sozialversicherungsnummer im Format XXX-" +"XXX-XXX eingeben." #: contrib/localflavor/ch/ch_states.py:5 msgid "Aargau" @@ -1712,15 +1814,15 @@ msgstr "" #: contrib/localflavor/ch/ch_states.py:10 #, fuzzy msgid "Berne" -msgstr "Berlin" +msgstr "Bern" #: contrib/localflavor/ch/ch_states.py:11 msgid "Fribourg" -msgstr "" +msgstr "Freiburg" #: contrib/localflavor/ch/ch_states.py:12 msgid "Geneva" -msgstr "" +msgstr "Genf" #: contrib/localflavor/ch/ch_states.py:13 #, fuzzy @@ -1771,7 +1873,7 @@ msgstr "" #: contrib/localflavor/ch/ch_states.py:24 #, fuzzy msgid "Thurgau" -msgstr "Thüringen" +msgstr "" #: contrib/localflavor/ch/ch_states.py:25 msgid "Ticino" @@ -1780,7 +1882,7 @@ msgstr "" #: contrib/localflavor/ch/ch_states.py:26 #, fuzzy msgid "Uri" -msgstr "Fr" +msgstr "" #: contrib/localflavor/ch/ch_states.py:27 msgid "Valais" @@ -1793,31 +1895,34 @@ msgstr "" #: contrib/localflavor/ch/ch_states.py:29 #, fuzzy msgid "Zug" -msgstr "Aug" +msgstr "" #: contrib/localflavor/ch/ch_states.py:30 #, fuzzy msgid "Zurich" -msgstr "Türkisch" +msgstr "Zürich" -#: contrib/localflavor/ch/forms.py:16 -#: contrib/localflavor/no/forms.py:12 +#: contrib/localflavor/ch/forms.py:16 contrib/localflavor/no/forms.py:12 msgid "Enter a zip code in the format XXXX." msgstr "Bitte eine gültige Postleitzahl im Format XXXX eingeben." #: contrib/localflavor/ch/forms.py:64 -msgid "Enter a valid Swiss identity or passport card number in X1234567<0 or 1234567890 format." +msgid "" +"Enter a valid Swiss identity or passport card number in X1234567<0 or " +"1234567890 format." msgstr "" +"Bitte eine gültige Schweizer Identifikations- oder Reisepassnummer im Format" +"X1234567<0 oder 1234567890 eingeben." #: contrib/localflavor/cl/forms.py:29 #, fuzzy msgid "Enter a valid Chilean RUT." -msgstr "Bitte einen gültigen Dateinamen eingeben." +msgstr "Bitte einen gültige chilenische RUT eingeben." #: contrib/localflavor/cl/forms.py:30 #, fuzzy msgid "Enter a valid Chilean RUT. The format is XX.XXX.XXX-X." -msgstr "Bitte eine gültige Postleitzahl im Format XXXXX-XXX eingeben." +msgstr "Bitte eine chilenische RUT im Format XX.XXX.XXX-X eingeben." #: contrib/localflavor/cl/forms.py:31 #, fuzzy @@ -1888,15 +1993,18 @@ msgstr "Schleswig-Holstein" msgid "Thuringia" msgstr "Thüringen" -#: contrib/localflavor/de/forms.py:14 -#: contrib/localflavor/fi/forms.py:12 +#: contrib/localflavor/de/forms.py:14 contrib/localflavor/fi/forms.py:12 #: contrib/localflavor/fr/forms.py:15 msgid "Enter a zip code in the format XXXXX." msgstr "Bitte eine gültige Postleitzahl im Format XXXXX eingeben." #: contrib/localflavor/de/forms.py:41 -msgid "Enter a valid German identity card number in XXXXXXXXXXX-XXXXXXX-XXXXXXX-X format." -msgstr "Bitte eine gültige deutsche Personalausweisnummer im Format XXXXXXXXXXX-XXXXXXX-XXXXXXX-X eingeben." +msgid "" +"Enter a valid German identity card number in XXXXXXXXXXX-XXXXXXX-XXXXXXX-X " +"format." +msgstr "" +"Bitte eine gültige deutsche Personalausweisnummer im Format XXXXXXXXXXX-" +"XXXXXXX-XXXXXXX-X eingeben." #: contrib/localflavor/es/es_provinces.py:5 msgid "Arava" @@ -1917,7 +2025,7 @@ msgstr "" #: contrib/localflavor/es/es_provinces.py:9 #, fuzzy msgid "Avila" -msgstr "April" +msgstr "" #: contrib/localflavor/es/es_provinces.py:10 msgid "Badajoz" @@ -1930,7 +2038,7 @@ msgstr "" #: contrib/localflavor/es/es_provinces.py:12 #, fuzzy msgid "Barcelona" -msgstr "Mazedonisch" +msgstr "" #: contrib/localflavor/es/es_provinces.py:13 msgid "Burgos" @@ -1971,7 +2079,7 @@ msgstr "" #: contrib/localflavor/es/es_provinces.py:22 #, fuzzy msgid "Granada" -msgstr "Kannada" +msgstr "" #: contrib/localflavor/es/es_provinces.py:23 msgid "Guadalajara" @@ -1988,17 +2096,17 @@ msgstr "" #: contrib/localflavor/es/es_provinces.py:26 #, fuzzy msgid "Huesca" -msgstr "Dienstag" +msgstr "" #: contrib/localflavor/es/es_provinces.py:27 #, fuzzy msgid "Jaen" -msgstr "Jan." +msgstr "" #: contrib/localflavor/es/es_provinces.py:28 #, fuzzy msgid "Leon" -msgstr "Anmelden" +msgstr "" #: contrib/localflavor/es/es_provinces.py:29 msgid "Lleida" @@ -2041,7 +2149,7 @@ msgstr "" #: contrib/localflavor/es/es_provinces.py:38 #, fuzzy msgid "Palencia" -msgstr "Galicisch" +msgstr "" #: contrib/localflavor/es/es_provinces.py:39 msgid "Las Palmas" @@ -2063,12 +2171,12 @@ msgstr "" #: contrib/localflavor/es/es_regions.py:11 #, fuzzy msgid "Cantabria" -msgstr "Katalanisch" +msgstr "" #: contrib/localflavor/es/es_provinces.py:44 #, fuzzy msgid "Segovia" -msgstr "Slowenisch" +msgstr "" #: contrib/localflavor/es/es_provinces.py:45 msgid "Seville" @@ -2077,7 +2185,7 @@ msgstr "" #: contrib/localflavor/es/es_provinces.py:46 #, fuzzy msgid "Soria" -msgstr "Serbisch" +msgstr "" #: contrib/localflavor/es/es_provinces.py:47 msgid "Tarragona" @@ -2086,7 +2194,7 @@ msgstr "" #: contrib/localflavor/es/es_provinces.py:48 #, fuzzy msgid "Teruel" -msgstr "Di" +msgstr "" #: contrib/localflavor/es/es_provinces.py:49 msgid "Toledo" @@ -2095,7 +2203,7 @@ msgstr "" #: contrib/localflavor/es/es_provinces.py:50 #, fuzzy msgid "Valencia" -msgstr "Galicisch" +msgstr "" #: contrib/localflavor/es/es_provinces.py:51 msgid "Valladolid" @@ -2144,7 +2252,7 @@ msgstr "" #: contrib/localflavor/es/es_regions.py:10 #, fuzzy msgid "Canary Islands" -msgstr "Saarland" +msgstr "" #: contrib/localflavor/es/es_regions.py:12 msgid "Castile-La Mancha" @@ -2157,7 +2265,7 @@ msgstr "" #: contrib/localflavor/es/es_regions.py:14 #, fuzzy msgid "Catalonia" -msgstr "Katalanisch" +msgstr "" #: contrib/localflavor/es/es_regions.py:15 msgid "Extremadura" @@ -2166,7 +2274,7 @@ msgstr "" #: contrib/localflavor/es/es_regions.py:16 #, fuzzy msgid "Galicia" -msgstr "Galicisch" +msgstr "Galicien" #: contrib/localflavor/es/es_regions.py:19 msgid "Region of Murcia" @@ -2183,43 +2291,50 @@ msgstr "" #: contrib/localflavor/es/forms.py:19 #, fuzzy msgid "Enter a valid postal code in the range and format 01XXX - 52XXX." -msgstr "Bitte eine gültige Postleitzahl im Format XXXXXXX oder XXX-XXXX eingeben." +msgstr "" +"Bitte eine gültige Postleitzahl im Format 01XXX bis 52XXX eingeben." #: contrib/localflavor/es/forms.py:39 #, fuzzy -msgid "Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX." -msgstr "Bitte eine gültige Postleitzahl im Format XXXXXXX oder XXX-XXXX eingeben." +msgid "" +"Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or " +"9XXXXXXXX." +msgstr "" +"Bitte eine gültige Telefonnummer in einem der folgenden Formate eingeben " +"6XXXXXXXX, 8XXXXXXXX oder 9XXXXXXXX." #: contrib/localflavor/es/forms.py:66 #, fuzzy msgid "Please enter a valid NIF, NIE, or CIF." -msgstr "Bitte eine gültige IP-Adresse eingeben." +msgstr "Bitte eine gültige NIF, NIE oder CIF eingeben." #: contrib/localflavor/es/forms.py:67 #, fuzzy msgid "Please enter a valid NIF or NIE." -msgstr "Bitte ein gültiges '%s' eingeben." +msgstr "Bitte eine gültige NIF oder NIE eingeben." #: contrib/localflavor/es/forms.py:68 msgid "Invalid checksum for NIF." -msgstr "" +msgstr "Ungültige Prüfsumme für NIF." #: contrib/localflavor/es/forms.py:69 msgid "Invalid checksum for NIE." -msgstr "" +msgstr "Ungültige Prüfsumme für NIE." #: contrib/localflavor/es/forms.py:70 msgid "Invalid checksum for CIF." -msgstr "" +msgstr "Ungültige Prüfsumme für CIF." #: contrib/localflavor/es/forms.py:142 #, fuzzy -msgid "Please enter a valid bank account number in format XXXX-XXXX-XX-XXXXXXXXXX." -msgstr "Bitte eine gültige deutsche Personalausweisnummer im Format XXXXXXXXXXX-XXXXXXX-XXXXXXX-X eingeben." +msgid "" +"Please enter a valid bank account number in format XXXX-XXXX-XX-XXXXXXXXXX." +msgstr "" +"Bitte eine gültige Kontonummer im Format XXXX-XXXX-XX-XXXXXXXXXX eingeben." #: contrib/localflavor/es/forms.py:143 msgid "Invalid checksum for bank account number." -msgstr "" +msgstr "Ungültige Prüfsumme für Kontonummer." #: contrib/localflavor/fi/forms.py:28 msgid "Enter a valid Finnish social security number." @@ -2228,35 +2343,39 @@ msgstr "Bitte eine gültige finnische Sozialversicherungsnummer eingeben." #: contrib/localflavor/in_/forms.py:14 #, fuzzy msgid "Enter a zip code in the format XXXXXXX." -msgstr "Bitte eine gültige Postleitzahl im Format XXXXX-XXX eingeben." +msgstr "Bitte eine gültige Postleitzahl im Format XXXXXXX eingeben." #: contrib/localflavor/is_/forms.py:17 #, fuzzy -msgid "Enter a valid Icelandic identification number. The format is XXXXXX-XXXX." -msgstr "Bitte eine gültige Postleitzahl im Format XXXXX-XXX eingeben." +msgid "" +"Enter a valid Icelandic identification number. The format is XXXXXX-XXXX." +msgstr "" +"Bitte eine gültige isländische Identifikationsnummer im Format XXXXXX-XXXX " +"eingeben." #: contrib/localflavor/is_/forms.py:18 msgid "The Icelandic identification number is not valid." -msgstr "" +msgstr "Die isländische Identifikationsnummer ist nicht gültig." #: contrib/localflavor/it/forms.py:14 #, fuzzy msgid "Enter a valid zip code." -msgstr "Bitte ein gültiges Datum eingeben." +msgstr "Bitte eine Postleitzahl eingeben." #: contrib/localflavor/it/forms.py:43 #, fuzzy msgid "Enter a valid Social Security number." -msgstr "Bitte eine gültige finnische Sozialversicherungsnummer eingeben." +msgstr "Bitte eine gültige Sozialversicherungsnummer eingeben." #: contrib/localflavor/it/forms.py:68 #, fuzzy msgid "Enter a valid VAT number." -msgstr "Bitte eine gültige Uhrzeit eingeben." +msgstr "Bitte eine gültige Umsatzsteuernummer eingeben." #: contrib/localflavor/jp/forms.py:17 msgid "Enter a postal code in the format XXXXXXX or XXX-XXXX." -msgstr "Bitte eine gültige Postleitzahl im Format XXXXXXX oder XXX-XXXX eingeben." +msgstr "" +"Bitte eine gültige Postleitzahl im Format XXXXXXX oder XXX-XXXX eingeben." #: contrib/localflavor/jp/jp_prefectures.py:4 msgid "Hokkaido" @@ -2408,7 +2527,7 @@ msgstr "" #: contrib/localflavor/jp/jp_prefectures.py:41 msgid "Ehime" -msgstr "Zeit" +msgstr "" #: contrib/localflavor/jp/jp_prefectures.py:42 msgid "Kochi" @@ -2461,7 +2580,7 @@ msgstr "" #: contrib/localflavor/mx/mx_states.py:15 #, fuzzy msgid "Campeche" -msgstr "Tschechisch" +msgstr "" #: contrib/localflavor/mx/mx_states.py:16 msgid "Chihuahua" @@ -2490,7 +2609,7 @@ msgstr "" #: contrib/localflavor/mx/mx_states.py:22 #, fuzzy msgid "Guerrero" -msgstr "Serverfehler" +msgstr "" #: contrib/localflavor/mx/mx_states.py:23 msgid "Guanajuato" @@ -2543,7 +2662,7 @@ msgstr "" #: contrib/localflavor/mx/mx_states.py:35 #, fuzzy msgid "Sinaloa" -msgstr "Slowakisch" +msgstr "" #: contrib/localflavor/mx/mx_states.py:36 msgid "San Luis Potosí" @@ -2580,22 +2699,22 @@ msgstr "" #: contrib/localflavor/nl/forms.py:21 #, fuzzy msgid "Enter a valid postal code" -msgstr "Bitte einen gültigen Wert eingeben." +msgstr "Bitte eine gültige Postleitzahl eingeben." #: contrib/localflavor/nl/forms.py:52 #, fuzzy msgid "Enter a valid phone number" -msgstr "Bitte eine ganze Zahl eingeben." +msgstr "Bitte eine gültige Telefonnummer eingeben." #: contrib/localflavor/nl/forms.py:78 #, fuzzy msgid "Enter a valid SoFi number" -msgstr "Bitte eine gültige Uhrzeit eingeben." +msgstr "Bitte eine gültige SoFi-Nummer eingeben." #: contrib/localflavor/nl/nl_provinces.py:4 #, fuzzy msgid "Drente" -msgstr "Löschen" +msgstr "" #: contrib/localflavor/nl/nl_provinces.py:5 msgid "Flevoland" @@ -2608,17 +2727,17 @@ msgstr "" #: contrib/localflavor/nl/nl_provinces.py:7 #, fuzzy msgid "Gelderland" -msgstr "Deutsch" +msgstr "" #: contrib/localflavor/nl/nl_provinces.py:8 #, fuzzy msgid "Groningen" -msgstr "neun" +msgstr "" #: contrib/localflavor/nl/nl_provinces.py:9 #, fuzzy msgid "Limburg" -msgstr "Hamburg" +msgstr "" #: contrib/localflavor/nl/nl_provinces.py:10 msgid "Noord-Brabant" @@ -2635,12 +2754,12 @@ msgstr "" #: contrib/localflavor/nl/nl_provinces.py:13 #, fuzzy msgid "Utrecht" -msgstr "Französisch" +msgstr "" #: contrib/localflavor/nl/nl_provinces.py:14 #, fuzzy msgid "Zeeland" -msgstr "Isländisch" +msgstr "" #: contrib/localflavor/nl/nl_provinces.py:15 msgid "Zuid-Holland" @@ -2653,47 +2772,52 @@ msgstr "Bitte eine gültige norwegische Sozialversicherungsnummer eingeben." #: contrib/localflavor/pe/forms.py:24 #, fuzzy msgid "This field requires 8 digits." -msgstr "Dieses Feld ist zwingend erforderlich." +msgstr "Dieses Feld benötigt 8 Zeichen." #: contrib/localflavor/pe/forms.py:52 #, fuzzy msgid "This field requires 11 digits." -msgstr "Dieses Feld ist zwingend erforderlich." +msgstr "Dieses Feld benötigt 11 Zeichen." #: contrib/localflavor/pl/forms.py:39 msgid "National Identification Number consists of 11 digits." -msgstr "" +msgstr "Nationale Identifikationsnummer besteht aus 11 Ziffern." #: contrib/localflavor/pl/forms.py:40 msgid "Wrong checksum for the National Identification Number." -msgstr "" +msgstr "Falsche Prüfsumme für die nationale Identifikationsnummer." #: contrib/localflavor/pl/forms.py:72 #, fuzzy -msgid "Enter a tax number field (NIP) in the format XXX-XXX-XX-XX or XX-XX-XXX-XXX." -msgstr "Bitte eine gültige Postleitzahl im Format XXXXXXX oder XXX-XXXX eingeben." +msgid "" +"Enter a tax number field (NIP) in the format XXX-XXX-XX-XX or XX-XX-XXX-XXX." +msgstr "" +"Bitte eine Steuernummer (NIP) im Format XXX-XXX-XX-XX oder XX-XX-XXX-XXX " +"eingeben." #: contrib/localflavor/pl/forms.py:73 msgid "Wrong checksum for the Tax Number (NIP)." -msgstr "" +msgstr "Falsche Prüfsumme für die Steuernummer (NIP)." #: contrib/localflavor/pl/forms.py:112 msgid "National Business Register Number (REGON) consists of 7 or 9 digits." -msgstr "" +msgstr "Nationale Geschäftsregistrierungsnummer (REGON) besteht aus 7 oder " +"9 Zeichen." #: contrib/localflavor/pl/forms.py:113 msgid "Wrong checksum for the National Business Register Number (REGON)." -msgstr "" +msgstr "Falsche Prüfsumme für die nationale Geschäftsregistrierungsnummer " +"(REGON)." #: contrib/localflavor/pl/forms.py:156 #, fuzzy msgid "Enter a postal code in the format XX-XXX." -msgstr "Bitte eine gültige Postleitzahl im Format XXXXX eingeben." +msgstr "Bitte eine gültige Postleitzahl im Format XX-XXX eingeben." #: contrib/localflavor/pl/pl_voivodeships.py:8 #, fuzzy msgid "Lower Silesia" -msgstr "Niedersachsen" +msgstr "" #: contrib/localflavor/pl/pl_voivodeships.py:9 msgid "Kuyavia-Pomerania" @@ -2722,7 +2846,7 @@ msgstr "" #: contrib/localflavor/pl/pl_voivodeships.py:15 #, fuzzy msgid "Opole" -msgstr "Optional" +msgstr "" #: contrib/localflavor/pl/pl_voivodeships.py:16 msgid "Subcarpatia" @@ -2735,12 +2859,12 @@ msgstr "" #: contrib/localflavor/pl/pl_voivodeships.py:18 #, fuzzy msgid "Pomerania" -msgstr "Rumänisch" +msgstr "" #: contrib/localflavor/pl/pl_voivodeships.py:19 #, fuzzy msgid "Silesia" -msgstr "Slowenisch" +msgstr "" #: contrib/localflavor/pl/pl_voivodeships.py:20 msgid "Swietokrzyskie" @@ -2757,12 +2881,13 @@ msgstr "" #: contrib/localflavor/pl/pl_voivodeships.py:23 #, fuzzy msgid "West Pomerania" -msgstr "Mecklenburg-Vorpommern" +msgstr "" #: contrib/localflavor/sk/forms.py:30 #, fuzzy msgid "Enter a postal code in the format XXXXX or XXX XX." -msgstr "Bitte eine gültige Postleitzahl im Format XXXXXXX oder XXX-XXXX eingeben." +msgstr "" +"Bitte eine gültige Postleitzahl im Format XXXXX oder XXX XX eingeben." #: contrib/localflavor/sk/sk_districts.py:8 msgid "Banska Bystrica" @@ -2783,7 +2908,7 @@ msgstr "" #: contrib/localflavor/sk/sk_districts.py:12 #, fuzzy msgid "Brezno" -msgstr "Bremen" +msgstr "" #: contrib/localflavor/sk/sk_districts.py:13 msgid "Bratislava I" @@ -2828,12 +2953,12 @@ msgstr "" #: contrib/localflavor/sk/sk_districts.py:23 #, fuzzy msgid "Galanta" -msgstr "Galicisch" +msgstr "" #: contrib/localflavor/sk/sk_districts.py:24 #, fuzzy msgid "Gelnica" -msgstr "Galicisch" +msgstr "" #: contrib/localflavor/sk/sk_districts.py:25 msgid "Hlohovec" @@ -2854,7 +2979,7 @@ msgstr "" #: contrib/localflavor/sk/sk_districts.py:29 #, fuzzy msgid "Komarno" -msgstr "Koreanisch" +msgstr "" #: contrib/localflavor/sk/sk_districts.py:30 msgid "Kosice I" @@ -2903,12 +3028,12 @@ msgstr "" #: contrib/localflavor/sk/sk_districts.py:41 #, fuzzy msgid "Malacky" -msgstr "Mai" +msgstr "" #: contrib/localflavor/sk/sk_districts.py:42 #, fuzzy msgid "Martin" -msgstr "Lettisch" +msgstr "" #: contrib/localflavor/sk/sk_districts.py:43 msgid "Medzilaborce" @@ -2949,7 +3074,7 @@ msgstr "" #: contrib/localflavor/sk/sk_districts.py:52 #, fuzzy msgid "Piestany" -msgstr "Persisch" +msgstr "" #: contrib/localflavor/sk/sk_districts.py:53 msgid "Poltar" @@ -2994,7 +3119,7 @@ msgstr "" #: contrib/localflavor/sk/sk_districts.py:63 #, fuzzy msgid "Sabinov" -msgstr "Nov" +msgstr "" #: contrib/localflavor/sk/sk_districts.py:64 msgid "Senec" @@ -3003,17 +3128,17 @@ msgstr "" #: contrib/localflavor/sk/sk_districts.py:65 #, fuzzy msgid "Senica" -msgstr "Slowenisch" +msgstr "" #: contrib/localflavor/sk/sk_districts.py:66 #, fuzzy msgid "Skalica" -msgstr "Galicisch" +msgstr "" #: contrib/localflavor/sk/sk_districts.py:67 #, fuzzy msgid "Snina" -msgstr "neun" +msgstr "" #: contrib/localflavor/sk/sk_districts.py:68 msgid "Sobrance" @@ -3038,7 +3163,7 @@ msgstr "" #: contrib/localflavor/sk/sk_districts.py:73 #, fuzzy msgid "Sala" -msgstr "Saarland" +msgstr "" #: contrib/localflavor/sk/sk_districts.py:74 msgid "Topolcany" @@ -3051,7 +3176,7 @@ msgstr "" #: contrib/localflavor/sk/sk_districts.py:76 #, fuzzy msgid "Trencin" -msgstr "Französisch" +msgstr "" #: contrib/localflavor/sk/sk_districts.py:77 msgid "Trnava" @@ -3128,7 +3253,7 @@ msgstr "" #: contrib/localflavor/uk/forms.py:21 #, fuzzy msgid "Enter a valid postcode." -msgstr "Bitte ein gültiges Datum eingeben." +msgstr "Bitte eine gültige Postleitzahl eingeben." #: contrib/localflavor/uk/uk_regions.py:11 msgid "Bedfordshire" @@ -3157,7 +3282,7 @@ msgstr "" #: contrib/localflavor/uk/uk_regions.py:18 #, fuzzy msgid "Devon" -msgstr "sieben" +msgstr "" #: contrib/localflavor/uk/uk_regions.py:19 msgid "Dorset" @@ -3198,7 +3323,7 @@ msgstr "" #: contrib/localflavor/uk/uk_regions.py:28 #, fuzzy msgid "Kent" -msgstr "Koreanisch" +msgstr "" #: contrib/localflavor/uk/uk_regions.py:29 msgid "Lancashire" @@ -3352,7 +3477,7 @@ msgstr "" #: contrib/localflavor/uk/uk_regions.py:75 #, fuzzy msgid "Borders" -msgstr "Reihenfolge:" +msgstr "" #: contrib/localflavor/uk/uk_regions.py:76 msgid "Central Scotland" @@ -3365,12 +3490,12 @@ msgstr "" #: contrib/localflavor/uk/uk_regions.py:78 #, fuzzy msgid "Fife" -msgstr "Filter" +msgstr "" #: contrib/localflavor/uk/uk_regions.py:79 #, fuzzy msgid "Grampian" -msgstr "Deutsch" +msgstr "" #: contrib/localflavor/uk/uk_regions.py:80 msgid "Highland" @@ -3379,7 +3504,7 @@ msgstr "" #: contrib/localflavor/uk/uk_regions.py:81 #, fuzzy msgid "Lothian" -msgstr "Lettisch" +msgstr "" #: contrib/localflavor/uk/uk_regions.py:82 msgid "Orkney Islands" @@ -3404,54 +3529,57 @@ msgstr "" #: contrib/localflavor/uk/uk_regions.py:90 #, fuzzy msgid "England" -msgstr "und" +msgstr "England" #: contrib/localflavor/uk/uk_regions.py:91 msgid "Northern Ireland" -msgstr "" +msgstr "Nordirland" #: contrib/localflavor/uk/uk_regions.py:92 #, fuzzy msgid "Scotland" -msgstr "Saarland" +msgstr "Schottland" #: contrib/localflavor/uk/uk_regions.py:93 #, fuzzy msgid "Wales" -msgstr "Walisisch" +msgstr "Wales" #: contrib/localflavor/us/forms.py:16 msgid "Enter a zip code in the format XXXXX or XXXXX-XXXX." -msgstr "Bitte eine gültige Postleitzahl im Format XXXXX oder XXXXX-XXXX eingeben." +msgstr "" +"Bitte eine gültige Postleitzahl im Format XXXXX oder XXXXX-XXXX eingeben." #: contrib/localflavor/us/forms.py:54 msgid "Enter a valid U.S. Social Security number in XXX-XX-XXXX format." -msgstr "Bitte eine gültige US-amerikanische Sozialversicherungsnummer im Format XXX-XX-XXXX eingeben." +msgstr "" +"Bitte eine gültige US-amerikanische Sozialversicherungsnummer im Format XXX-" +"XX-XXXX eingeben." #: contrib/localflavor/za/forms.py:20 #, fuzzy msgid "Enter a valid South African ID number" -msgstr "Bitte eine gültige norwegische Sozialversicherungsnummer eingeben." +msgstr "Bitte eine gültige südafrikanische Sozialversicherungsnummer eingeben." #: contrib/localflavor/za/forms.py:54 #, fuzzy msgid "Enter a valid South African postal code" -msgstr "Bitte eine gültige vierstellige Postleitzahl eingeben." +msgstr "Bitte eine gültige südafrikanische Postleitzahl eingeben." #: contrib/localflavor/za/za_provinces.py:4 #, fuzzy msgid "Eastern Cape" -msgstr "Benutzername" +msgstr "" #: contrib/localflavor/za/za_provinces.py:5 #, fuzzy msgid "Free State" -msgstr "Bewertungsdatum" +msgstr "" #: contrib/localflavor/za/za_provinces.py:6 #, fuzzy msgid "Gauteng" -msgstr "Aug" +msgstr "" #: contrib/localflavor/za/za_provinces.py:7 msgid "KwaZulu-Natal" @@ -3472,28 +3600,36 @@ msgstr "" #: contrib/localflavor/za/za_provinces.py:11 #, fuzzy msgid "North West" -msgstr "Nordrhein-Westfalen" +msgstr "" #: contrib/localflavor/za/za_provinces.py:12 #, fuzzy msgid "Western Cape" -msgstr "Benutzername" +msgstr "" #: contrib/redirects/models.py:7 msgid "redirect from" msgstr "Umleitung von" #: contrib/redirects/models.py:8 -msgid "This should be an absolute path, excluding the domain name. Example: '/events/search/'." -msgstr "Hier sollte ein absoluter Pfad stehen, ohne den Domainnamen. Beispiel: '/events/search/'." +msgid "" +"This should be an absolute path, excluding the domain name. Example: '/" +"events/search/'." +msgstr "" +"Hier sollte ein absoluter Pfad stehen, ohne den Domainnamen. Beispiel: '/" +"events/search/'." #: contrib/redirects/models.py:9 msgid "redirect to" msgstr "Umleitung zu" #: contrib/redirects/models.py:10 -msgid "This can be either an absolute path (as above) or a full URL starting with 'http://'." -msgstr "Hier muss entweder ein absoluter Pfad oder eine komplette URL mit http:// am Anfang stehen." +msgid "" +"This can be either an absolute path (as above) or a full URL starting with " +"'http://'." +msgstr "" +"Hier muss entweder ein absoluter Pfad oder eine komplette URL mit http:// am " +"Anfang stehen." #: contrib/redirects/models.py:13 msgid "redirect" @@ -3544,12 +3680,18 @@ msgid "This value must contain only letters, numbers and underscores." msgstr "Dieser Wert darf nur Buchstaben, Ziffern und Unterstriche enthalten." #: core/validators.py:76 -msgid "This value must contain only letters, numbers, underscores, dashes or slashes." -msgstr "Dieser Wert darf nur Buchstaben, Ziffern, Unterstriche und Schrägstriche enthalten." +msgid "" +"This value must contain only letters, numbers, underscores, dashes or " +"slashes." +msgstr "" +"Dieser Wert darf nur Buchstaben, Ziffern, Unterstriche und Schrägstriche " +"enthalten." #: core/validators.py:80 msgid "This value must contain only letters, numbers, underscores or hyphens." -msgstr "Dieser Wert darf nur Buchstaben, Ziffern, Unterstriche und Bindestriche enthalten." +msgstr "" +"Dieser Wert darf nur Buchstaben, Ziffern, Unterstriche und Bindestriche " +"enthalten." #: core/validators.py:84 msgid "Uppercase letters are not allowed here." @@ -3583,8 +3725,7 @@ msgstr "Nichtnumerische Zeichen sind hier nicht erlaubt." msgid "This value can't be comprised solely of digits." msgstr "Dieser Wert darf nicht nur aus Ziffern bestehen." -#: core/validators.py:128 -#: newforms/fields.py:151 +#: core/validators.py:128 newforms/fields.py:151 msgid "Enter a whole number." msgstr "Bitte eine ganze Zahl eingeben." @@ -3601,8 +3742,7 @@ msgstr "Das Jahr muss 1900 oder später sein." msgid "Invalid date: %s" msgstr "Ungültiges Datum: %s" -#: core/validators.py:156 -#: db/models/fields/__init__.py:522 +#: core/validators.py:156 db/models/fields/__init__.py:527 msgid "Enter a valid date in YYYY-MM-DD format." msgstr "Bitte ein gültiges Datum im Format JJJJ-MM-TT eingeben." @@ -3610,27 +3750,29 @@ msgstr "Bitte ein gültiges Datum im Format JJJJ-MM-TT eingeben." msgid "Enter a valid time in HH:MM format." msgstr "Bitte eine gültige Zeit im Format SS:MM eingeben." -#: core/validators.py:165 -#: db/models/fields/__init__.py:599 +#: core/validators.py:165 db/models/fields/__init__.py:604 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." +msgstr "" +"Bitte eine gültige Datums- und Zeitangabe im Format JJJJ-MM-TT SS:MM " +"eingeben." -#: core/validators.py:170 -#: newforms/fields.py:402 +#: core/validators.py:170 newforms/fields.py:402 msgid "Enter a valid e-mail address." msgstr "Bitte eine gültige E-Mail-Adresse eingeben." -#: core/validators.py:182 -#: core/validators.py:474 -#: newforms/fields.py:432 +#: core/validators.py:182 core/validators.py:474 newforms/fields.py:432 #: oldforms/__init__.py:687 msgid "No file was submitted. Check the encoding type on the form." -msgstr "Es wurde keine Datei übermittelt. Eventuell ist das Formular-Encoding falsch." +msgstr "" +"Es wurde keine Datei übermittelt. Eventuell ist das Formular-Encoding falsch." -#: core/validators.py:193 -#: newforms/fields.py:458 -msgid "Upload a valid image. The file you uploaded was either not an image or a corrupted image." -msgstr "Bitte ein Bild hochladen. Die hochgeladene Datei ist kein Bild, oder ist defekt." +#: core/validators.py:193 newforms/fields.py:458 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" +"Bitte ein Bild hochladen. Die hochgeladene Datei ist kein Bild, oder ist " +"defekt." #: core/validators.py:200 #, python-format @@ -3640,7 +3782,8 @@ msgstr "Die URL %s zeigt nicht auf ein gültiges Bild." #: core/validators.py:204 #, python-format msgid "Phone numbers must be in XXX-XXX-XXXX format. \"%s\" is invalid." -msgstr "Telefonnummern müssen das Format XXX-XXX-XXXX haben. \"%s\" ist ungültig." +msgstr "" +"Telefonnummern müssen das Format XXX-XXX-XXXX haben. \"%s\" ist ungültig." #: core/validators.py:212 #, python-format @@ -3670,8 +3813,7 @@ msgstr "Ungültiges XML: %s" msgid "Invalid URL: %s" msgstr "Ungültige URL: %s" -#: core/validators.py:259 -#: core/validators.py:261 +#: core/validators.py:259 core/validators.py:261 #, python-format msgid "The URL %s is a broken link." msgstr "Die URL %s funktioniert nicht." @@ -3696,20 +3838,21 @@ msgstr "Dieses Feld muss zum Feld '%s' passen." msgid "Please enter something for at least one field." msgstr "Bitte mindestens eines der Felder ausfüllen." -#: core/validators.py:316 -#: core/validators.py:327 +#: core/validators.py:316 core/validators.py:327 msgid "Please enter both fields or leave them both empty." msgstr "Bitte entweder beide Felder ausfüllen, oder beide leer lassen." #: core/validators.py:335 #, 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." +msgstr "" +"Dieses Feld muss gefüllt sein, wenn Feld %(field)s den Wert %(value)s hat." #: core/validators.py:348 #, 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." +msgstr "" +"Dieses Feld muss gefüllt sein, wenn Feld %(field)s nicht %(value)s ist." #: core/validators.py:367 msgid "Duplicate values are not allowed." @@ -3742,23 +3885,33 @@ msgstr "Bitte eine gültige Dezimalzahl eingeben." #: core/validators.py:444 #, 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." +msgid_plural "" +"Please enter a valid decimal number with at most %s total digits." 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:447 #, python-format -msgid "Please enter a valid decimal number with a whole part of at most %s digit." -msgid_plural "Please enter a valid decimal number with a whole part of at most %s digits." -msgstr[0] "Bitte eine gültige Dezimalzahl mit einer Gesamtzahl von maximal %s Ziffer eingeben." -msgstr[1] "Bitte eine gültige Dezimalzahl mit einer Gesamtzahl von maximal %s Ziffern eingeben." +msgid "" +"Please enter a valid decimal number with a whole part of at most %s digit." +msgid_plural "" +"Please enter a valid decimal number with a whole part of at most %s digits." +msgstr[0] "" +"Bitte eine gültige Dezimalzahl mit einer Gesamtzahl von maximal %s Ziffer " +"eingeben." +msgstr[1] "" +"Bitte eine gültige Dezimalzahl mit einer Gesamtzahl von maximal %s Ziffern " +"eingeben." #: core/validators.py:450 #, python-format 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] "Bitte eine gültige Dezimalzahl mit maximal %s Dezimalstelle eingeben." -msgstr[1] "Bitte eine gültige Dezimalzahl mit maximal %s Dezimalstellen eingeben." +msgid_plural "" +"Please enter a valid decimal number with at most %s decimal places." +msgstr[0] "" +"Bitte eine gültige Dezimalzahl mit maximal %s Dezimalstelle eingeben." +msgstr[1] "" +"Bitte eine gültige Dezimalzahl mit maximal %s Dezimalstellen eingeben." #: core/validators.py:458 msgid "Please enter a valid floating point number." @@ -3767,12 +3920,15 @@ msgstr "Bitte eine gültige Dezimalzahl eingeben." #: core/validators.py:467 #, python-format msgid "Make sure your uploaded file is at least %s bytes big." -msgstr "Bitte sicherstellen, dass die hochgeladene Datei mindestens %s Bytes groß ist." +msgstr "" +"Bitte sicherstellen, dass die hochgeladene Datei mindestens %s Bytes groß " +"ist." #: core/validators.py:468 #, python-format msgid "Make sure your uploaded file is at most %s bytes big." -msgstr "Bitte sicherstellen, dass die hochgeladene Datei maximal %s Bytes groß ist." +msgstr "" +"Bitte sicherstellen, dass die hochgeladene Datei maximal %s Bytes groß ist." #: core/validators.py:485 msgid "The format for this field is wrong." @@ -3789,79 +3945,103 @@ msgstr "Konnte nichts von %s empfangen." #: core/validators.py:539 #, python-format -msgid "The URL %(url)s returned the invalid Content-Type header '%(contenttype)s'." +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:572 #, python-format -msgid "Please close the unclosed %(tag)s tag from line %(line)s. (Line starts with \"%(start)s\".)" -msgstr "Bitte das ungeschlossene %(tag)s Tag in Zeile %(line)s schließen. Die Zeile beginnt mit \"%(start)s\"." +msgid "" +"Please close the unclosed %(tag)s tag from line %(line)s. (Line starts with " +"\"%(start)s\".)" +msgstr "" +"Bitte das ungeschlossene %(tag)s Tag in Zeile %(line)s schließen. Die Zeile " +"beginnt mit \"%(start)s\"." #: core/validators.py:576 #, python-format -msgid "Some text starting on line %(line)s is not allowed in that context. (Line starts with \"%(start)s\".)" -msgstr "In Zeile %(line)s ist Text, der nicht in dem Kontext erlaubt ist. Die Zeile beginnt mit \"%(start)s\"." +msgid "" +"Some text starting on line %(line)s is not allowed in that context. (Line " +"starts with \"%(start)s\".)" +msgstr "" +"In Zeile %(line)s ist Text, der nicht in dem Kontext erlaubt ist. Die Zeile " +"beginnt mit \"%(start)s\"." #: core/validators.py:581 #, python-format -msgid "\"%(attr)s\" on line %(line)s is an invalid attribute. (Line starts with \"%(start)s\".)" -msgstr "Das Attribute %(attr)s in Zeile %(line)s ist ungültig. Die Zeile beginnt mit \"%(start)s\"." +msgid "" +"\"%(attr)s\" on line %(line)s is an invalid attribute. (Line starts with \"%" +"(start)s\".)" +msgstr "" +"Das Attribute %(attr)s in Zeile %(line)s ist ungültig. Die Zeile beginnt mit " +"\"%(start)s\"." #: core/validators.py:586 #, python-format -msgid "\"<%(tag)s>\" on line %(line)s is an invalid tag. (Line starts with \"%(start)s\".)" -msgstr "<%(tag)s> in Zeile %(line)s ist ungültig. Die Zeile beginnt mit \"%(start)s\"." +msgid "" +"\"<%(tag)s>\" on line %(line)s is an invalid tag. (Line starts with \"%" +"(start)s\".)" +msgstr "" +"<%(tag)s> in Zeile %(line)s ist ungültig. Die Zeile beginnt mit \"%(start)s" +"\"." #: core/validators.py:590 #, python-format -msgid "A tag on line %(line)s is missing one or more required attributes. (Line starts with \"%(start)s\".)" -msgstr "Ein Tag in Zeile %(line)s hat eines oder mehrere Pflichtattribute nicht. Die Zeile beginnt mit \"%(start)s\"." +msgid "" +"A tag on line %(line)s is missing one or more required attributes. (Line " +"starts with \"%(start)s\".)" +msgstr "" +"Ein Tag in Zeile %(line)s hat eines oder mehrere Pflichtattribute nicht. Die " +"Zeile beginnt mit \"%(start)s\"." #: core/validators.py:595 #, python-format -msgid "The \"%(attr)s\" attribute on line %(line)s has an invalid value. (Line starts with \"%(start)s\".)" -msgstr "Das Attribut %(attr)s in Zeile %(line)s hat einen ungültigen Wert. Die Zeile beginnt mit \"%(start)s\"." +msgid "" +"The \"%(attr)s\" attribute on line %(line)s has an invalid value. (Line " +"starts with \"%(start)s\".)" +msgstr "" +"Das Attribut %(attr)s in Zeile %(line)s hat einen ungültigen Wert. Die Zeile " +"beginnt mit \"%(start)s\"." #: db/models/manipulators.py:308 #, 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'." +msgstr "" +"Ein '%(object)s' in dieser '%(type)s' existiert bereits für dieses '%(field)" +"s'." #: db/models/fields/__init__.py:52 #, 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:161 -#: db/models/fields/__init__.py:322 -#: db/models/fields/__init__.py:754 -#: db/models/fields/__init__.py:765 -#: newforms/fields.py:45 -#: oldforms/__init__.py:374 +#: db/models/fields/__init__.py:161 db/models/fields/__init__.py:327 +#: db/models/fields/__init__.py:759 db/models/fields/__init__.py:770 +#: newforms/fields.py:45 oldforms/__init__.py:374 msgid "This field is required." msgstr "Dieses Feld ist zwingend erforderlich." -#: db/models/fields/__init__.py:422 +#: db/models/fields/__init__.py:427 msgid "This value must be an integer." msgstr "Dieser Wert muss eine Ganzzahl sein." -#: db/models/fields/__init__.py:461 +#: db/models/fields/__init__.py:466 msgid "This value must be either True or False." msgstr "Dieser Wert muss wahr oder falsch sein." -#: db/models/fields/__init__.py:485 +#: db/models/fields/__init__.py:490 msgid "This field cannot be null." msgstr "Dieses Feld darf nicht leer sein." -#: db/models/fields/__init__.py:663 +#: db/models/fields/__init__.py:668 msgid "This value must be a decimal number." msgstr "Dieser Wert muss eine Ganzzahl sein." -#: db/models/fields/__init__.py:774 +#: db/models/fields/__init__.py:779 msgid "Enter a valid filename." msgstr "Bitte einen gültigen Dateinamen eingeben." -#: db/models/fields/__init__.py:945 +#: db/models/fields/__init__.py:950 msgid "This value must be either None, True or False." msgstr "Dieser Wert muss None, True oder False sein." @@ -3875,15 +4055,21 @@ msgid "Separate multiple IDs with commas." msgstr "Mehrere IDs können mit Komma getrennt werden." #: db/models/fields/related.py:696 -msgid "Hold down \"Control\", or \"Command\" on a Mac, to select more than one." -msgstr "Um mehr als eine Selektion zu treffen, \"Strg\", oder auf dem Mac \"Command\", beim Klicken gedrückt halten." +msgid "" +"Hold down \"Control\", or \"Command\" on a Mac, to select more than one." +msgstr "" +"Um mehr als eine Selektion zu treffen, \"Strg\", oder auf dem Mac \"Command" +"\", beim Klicken gedrückt halten." #: db/models/fields/related.py:743 #, python-format msgid "Please enter valid %(self)s IDs. The value %(value)r is invalid." -msgid_plural "Please enter valid %(self)s IDs. The values %(value)r are invalid." -msgstr[0] "Bitte gültige IDs für %(self)s eingeben. Der Wert %(value)r ist ungültig." -msgstr[1] "Bitte gültige IDs für %(self)s eingeben. Die Werte %(value)r sind ungültig." +msgid_plural "" +"Please enter valid %(self)s IDs. The values %(value)r are invalid." +msgstr[0] "" +"Bitte gültige IDs für %(self)s eingeben. Der Wert %(value)r ist ungültig." +msgstr[1] "" +"Bitte gültige IDs für %(self)s eingeben. Die Werte %(value)r sind ungültig." #: newforms/fields.py:46 msgid "Enter a valid value." @@ -3892,29 +4078,28 @@ msgstr "Bitte einen gültigen Wert eingeben." #: newforms/fields.py:123 #, python-format msgid "Ensure this value has at most %(max)d characters (it has %(length)d)." -msgstr "Bitte sicherstellen, dass der Text maximal %(max)d Zeichen hat. (Er hat %(length)d)." +msgstr "" +"Bitte sicherstellen, dass der Text maximal %(max)d Zeichen hat. (Er hat %" +"(length)d)." #: newforms/fields.py:124 #, python-format msgid "Ensure this value has at least %(min)d characters (it has %(length)d)." -msgstr "Bitte sicherstellen, dass der Text wenigstens %(min)d Zeichen hat. (Er hat %(length)d.)" +msgstr "" +"Bitte sicherstellen, dass der Text wenigstens %(min)d Zeichen hat. (Er hat %" +"(length)d.)" -#: newforms/fields.py:152 -#: newforms/fields.py:181 -#: newforms/fields.py:210 +#: newforms/fields.py:152 newforms/fields.py:181 newforms/fields.py:210 #, python-format msgid "Ensure this value is less than or equal to %s." msgstr "Dieser Wert darf maximal %s sein." -#: newforms/fields.py:153 -#: newforms/fields.py:182 -#: newforms/fields.py:211 +#: newforms/fields.py:153 newforms/fields.py:182 newforms/fields.py:211 #, python-format msgid "Ensure this value is greater than or equal to %s." msgstr "Dieser Wert muss größer oder gleich %s sein." -#: newforms/fields.py:180 -#: newforms/fields.py:209 +#: newforms/fields.py:180 newforms/fields.py:209 msgid "Enter a number." msgstr "Bitte eine Zahl eingeben." @@ -3933,13 +4118,11 @@ msgstr "Bitte geben Sie nicht mehr als %s Dezimalstellen ein." msgid "Ensure that there are no more than %s digits before the decimal point." msgstr "Bitte geben Sie nicht mehr als %s Ziffern vor dem Komma ein." -#: newforms/fields.py:262 -#: newforms/fields.py:723 +#: newforms/fields.py:262 newforms/fields.py:723 msgid "Enter a valid date." msgstr "Bitte ein gültiges Datum eingeben." -#: newforms/fields.py:295 -#: newforms/fields.py:724 +#: newforms/fields.py:295 newforms/fields.py:724 msgid "Enter a valid time." msgstr "Bitte eine gültige Uhrzeit eingeben." @@ -3951,8 +4134,7 @@ msgstr "Bitte gültiges Datum und Uhrzeit eingeben." msgid "No file was submitted." msgstr "Es wurde keine Datei übertragen." -#: newforms/fields.py:434 -#: oldforms/__init__.py:689 +#: newforms/fields.py:434 oldforms/__init__.py:689 msgid "The submitted file is empty." msgstr "Die ausgewählte Datei ist leer." @@ -3964,19 +4146,17 @@ msgstr "Bitte eine gültige Adresse eingeben." msgid "This URL appears to be a broken link." msgstr "Diese Adresse scheint nicht gültig zu sein." -#: newforms/fields.py:559 -#: newforms/models.py:300 +#: newforms/fields.py:559 newforms/models.py:300 msgid "Select a valid choice. That choice is not one of the available choices." msgstr "Bitte eine gültige Auswahl treffen." #: newforms/fields.py:598 #, python-format msgid "Select a valid choice. %(value)s is not one of the available choices." -msgstr "Bitte eine gültige Auswahl treffen. %(value)s ist keine gültige Auswahl." +msgstr "" +"Bitte eine gültige Auswahl treffen. %(value)s ist keine gültige Auswahl." -#: newforms/fields.py:599 -#: newforms/fields.py:661 -#: newforms/models.py:360 +#: newforms/fields.py:599 newforms/fields.py:661 newforms/models.py:360 msgid "Enter a list of values." msgstr "Eine Liste mit Werten eingeben." @@ -4000,12 +4180,11 @@ msgstr[1] "Bitte sicherstellen, dass der Text weniger als %s Zeichen hat." msgid "Line breaks are not allowed here." msgstr "Zeilenumbrüche sind hier nicht erlaubt." -#: oldforms/__init__.py:512 -#: oldforms/__init__.py:586 -#: oldforms/__init__.py:625 +#: oldforms/__init__.py:512 oldforms/__init__.py:586 oldforms/__init__.py:625 #, python-format msgid "Select a valid choice; '%(data)s' is not in %(choices)s." -msgstr "Bitte eine gültige Auswahl treffen; '%(data)s' ist nicht in %(choices)s." +msgstr "" +"Bitte eine gültige Auswahl treffen; '%(data)s' ist nicht in %(choices)s." #: oldforms/__init__.py:745 msgid "Enter a whole number between -32,768 and 32,767." @@ -4133,28 +4312,23 @@ msgstr "Januar" msgid "February" msgstr "Februar" -#: utils/dates.py:18 -#: utils/dates.py:31 +#: utils/dates.py:18 utils/dates.py:31 msgid "March" msgstr "März" -#: utils/dates.py:18 -#: utils/dates.py:31 +#: utils/dates.py:18 utils/dates.py:31 msgid "April" msgstr "April" -#: utils/dates.py:18 -#: utils/dates.py:31 +#: utils/dates.py:18 utils/dates.py:31 msgid "May" msgstr "Mai" -#: utils/dates.py:18 -#: utils/dates.py:31 +#: utils/dates.py:18 utils/dates.py:31 msgid "June" msgstr "Juni" -#: utils/dates.py:19 -#: utils/dates.py:31 +#: utils/dates.py:19 utils/dates.py:31 msgid "July" msgstr "Juli" @@ -4308,23 +4482,23 @@ msgstr "%(number)d %(type)s" msgid ", %(number)d %(type)s" msgstr ", %(number)d %(type)s" -#: utils/translation/trans_real.py:404 +#: utils/translation/trans_real.py:403 msgid "DATE_FORMAT" msgstr "j. N Y" -#: utils/translation/trans_real.py:405 +#: utils/translation/trans_real.py:404 msgid "DATETIME_FORMAT" msgstr "j. N Y, H:i" -#: utils/translation/trans_real.py:406 +#: utils/translation/trans_real.py:405 msgid "TIME_FORMAT" msgstr "H:i" -#: utils/translation/trans_real.py:422 +#: utils/translation/trans_real.py:421 msgid "YEAR_MONTH_FORMAT" msgstr "F Y" -#: utils/translation/trans_real.py:423 +#: utils/translation/trans_real.py:422 msgid "MONTH_DAY_FORMAT" msgstr "j. F" @@ -4342,10 +4516,3 @@ msgstr "%(verbose_name)s wurde erfolgreich aktualisiert." #, python-format msgid "The %(verbose_name)s was deleted." msgstr "%(verbose_name)s wurde gelöscht" - -#~ msgid "" -#~ "Enter a postcode. A space is required between the two postcode parts." -#~ msgstr "" -#~ "Bitte eine gültige Postleitzahl eingeben. Ein Leerzeichen trennt die zwei " -#~ "Teile." - diff --git a/django/contrib/admin/templates/admin_doc/index.html b/django/contrib/admin/templates/admin_doc/index.html index 64766fb46f..750dd2f5ac 100644 --- a/django/contrib/admin/templates/admin_doc/index.html +++ b/django/contrib/admin/templates/admin_doc/index.html @@ -1,27 +1,27 @@ -{% extends "admin/base_site.html" %}
-{% load i18n %}
-{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">Home</a> › Documentation</div>{% endblock %}
-{% block title %}Documentation{% endblock %}
-
-{% block content %}
-
-<h1>Documentation</h1>
-
-<div id="content-main">
- <h3><a href="tags/">Tags</a></h3>
- <p>List of all the template tags and their functions.</p>
-
- <h3><a href="filters/">Filters</a></h3>
- <p>Filters are actions which can be applied to variables in a template to alter the output.</p>
-
- <h3><a href="models/">Models</a></h3>
- <p>Models are descriptions of all the objects in the system and their associated fields. Each model has a list of fields which can be accessed as template variables.</p>
-
- <h3><a href="views/">Views</a></h3>
- <p>Each page on the public site is generated by a view. The view defines which template is used to generate the page and which objects are available to that template.</p>
-
- <h3><a href="bookmarklets/">Bookmarklets</a></h3>
- <p>Tools for your browser to quickly access admin functionality.</p>
-</div>
-
-{% endblock %}
+{% extends "admin/base_site.html" %} +{% load i18n %} +{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">Home</a> › Documentation</div>{% endblock %} +{% block title %}Documentation{% endblock %} + +{% block content %} + +<h1>Documentation</h1> + +<div id="content-main"> + <h3><a href="tags/">Tags</a></h3> + <p>List of all the template tags and their functions.</p> + + <h3><a href="filters/">Filters</a></h3> + <p>Filters are actions which can be applied to variables in a template to alter the output.</p> + + <h3><a href="models/">Models</a></h3> + <p>Models are descriptions of all the objects in the system and their associated fields. Each model has a list of fields which can be accessed as template variables.</p> + + <h3><a href="views/">Views</a></h3> + <p>Each page on the public site is generated by a view. The view defines which template is used to generate the page and which objects are available to that template.</p> + + <h3><a href="bookmarklets/">Bookmarklets</a></h3> + <p>Tools for your browser to quickly access admin functionality.</p> +</div> + +{% endblock %} diff --git a/django/contrib/admin/templates/admin_doc/view_index.html b/django/contrib/admin/templates/admin_doc/view_index.html index 1ed5ba2ac1..716e2d1a91 100644 --- a/django/contrib/admin/templates/admin_doc/view_index.html +++ b/django/contrib/admin/templates/admin_doc/view_index.html @@ -1,42 +1,42 @@ -{% extends "admin/base_site.html" %}
-{% load i18n %}
-{% block coltype %}colSM{% endblock %}
-{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> › <a href="../">Documentation</a> › Views</div>{% endblock %}
-{% block title %}Views{% endblock %}
-
-{% block content %}
-
-<h1>View documentation</h1>
-
-{% regroup views|dictsort:"site_id" by site as views_by_site %}
-
-<div id="content-related" class="sidebar">
-<div class="module">
-<h2>Jump to site</h2>
-<ul>
- {% for site_views in views_by_site %}
- <li><a href="#site{{ site_views.grouper.id }}">{{ site_views.grouper.name }}</a></li>
- {% endfor %}
-</ul>
-</div>
-</div>
-
-<div id="content-main">
-
-{% for site_views in views_by_site %}
-<div class="module">
-<h2 id="site{{ site_views.grouper.id }}">Views by URL on {{ site_views.grouper.name }}</h2>
-
-{% for view in site_views.list|dictsort:"url" %}
-{% ifchanged %}
-<h3><a href="{{ view.module }}.{{ view.name }}/">{{ view.url|escape }}</a></h3>
-<p class="small quiet">View function: {{ view.module }}.{{ view.name }}</p>
-<p>{{ view.title }}</p>
-<hr />
-{% endifchanged %}
-{% endfor %}
-</div>
-{% endfor %}
-</div>
-{% endblock %}
-
+{% extends "admin/base_site.html" %} +{% load i18n %} +{% block coltype %}colSM{% endblock %} +{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> › <a href="../">Documentation</a> › Views</div>{% endblock %} +{% block title %}Views{% endblock %} + +{% block content %} + +<h1>View documentation</h1> + +{% regroup views|dictsort:"site_id" by site as views_by_site %} + +<div id="content-related" class="sidebar"> +<div class="module"> +<h2>Jump to site</h2> +<ul> + {% for site_views in views_by_site %} + <li><a href="#site{{ site_views.grouper.id }}">{{ site_views.grouper.name }}</a></li> + {% endfor %} +</ul> +</div> +</div> + +<div id="content-main"> + +{% for site_views in views_by_site %} +<div class="module"> +<h2 id="site{{ site_views.grouper.id }}">Views by URL on {{ site_views.grouper.name }}</h2> + +{% for view in site_views.list|dictsort:"url" %} +{% ifchanged %} +<h3><a href="{{ view.module }}.{{ view.name }}/">{{ view.url|escape }}</a></h3> +<p class="small quiet">View function: {{ view.module }}.{{ view.name }}</p> +<p>{{ view.title }}</p> +<hr /> +{% endifchanged %} +{% endfor %} +</div> +{% endfor %} +</div> +{% endblock %} + diff --git a/django/contrib/formtools/wizard.py b/django/contrib/formtools/wizard.py index da2f0d56de..a197c3e659 100644 --- a/django/contrib/formtools/wizard.py +++ b/django/contrib/formtools/wizard.py @@ -1,246 +1,246 @@ -"""
-FormWizard class -- implements a multi-page form, validating between each
-step and storing the form's state as HTML hidden fields so that no state is
-stored on the server side.
-"""
-
-from django import newforms as forms
-from django.conf import settings
-from django.http import Http404
-from django.shortcuts import render_to_response
-from django.template.context import RequestContext
-import cPickle as pickle
-import md5
-
-class FormWizard(object):
- # Dictionary of extra template context variables.
- extra_context = {}
-
- # The HTML (and POST data) field name for the "step" variable.
- step_field_name="wizard_step"
-
- # METHODS SUBCLASSES SHOULDN'T OVERRIDE ###################################
-
- def __init__(self, form_list, initial=None):
- "form_list should be a list of Form classes (not instances)."
- self.form_list = form_list[:]
- self.initial = initial or {}
- self.step = 0 # A zero-based counter keeping track of which step we're in.
-
- def __repr__(self):
- return "step: %d\nform_list: %s\ninitial_data: %s" % (self.step, self.form_list, self.initial)
-
- def get_form(self, step, data=None):
- "Helper method that returns the Form instance for the given step."
- return self.form_list[step](data, prefix=self.prefix_for_step(step), initial=self.initial.get(step, None))
-
- def num_steps(self):
- "Helper method that returns the number of steps."
- # You might think we should just set "self.form_list = len(form_list)"
- # in __init__(), but this calculation needs to be dynamic, because some
- # hook methods might alter self.form_list.
- return len(self.form_list)
-
- def __call__(self, request, *args, **kwargs):
- """
- Main method that does all the hard work, conforming to the Django view
- interface.
- """
- if 'extra_context' in kwargs:
- self.extra_context.update(kwargs['extra_context'])
- current_step = self.determine_step(request, *args, **kwargs)
- self.parse_params(request, *args, **kwargs)
-
- # Sanity check.
- if current_step >= self.num_steps():
- raise Http404('Step %s does not exist' % current_step)
-
- # For each previous step, verify the hash and process.
- # TODO: Move "hash_%d" to a method to make it configurable.
- for i in range(current_step):
- form = self.get_form(i, request.POST)
- if request.POST.get("hash_%d" % i, '') != self.security_hash(request, form):
- return self.render_hash_failure(request, i)
- self.process_step(request, form, i)
-
- # Process the current step. If it's valid, go to the next step or call
- # done(), depending on whether any steps remain.
- if request.method == 'POST':
- form = self.get_form(current_step, request.POST)
- else:
- form = self.get_form(current_step)
- if form.is_valid():
- self.process_step(request, form, current_step)
- next_step = current_step + 1
-
- # If this was the last step, validate all of the forms one more
- # time, as a sanity check, and call done().
- num = self.num_steps()
- if next_step == num:
- final_form_list = [self.get_form(i, request.POST) for i in range(num)]
-
- # Validate all the forms. If any of them fail validation, that
- # must mean the validator relied on some other input, such as
- # an external Web site.
- for i, f in enumerate(final_form_list):
- if not f.is_valid():
- return self.render_revalidation_failure(request, i, f)
- return self.done(request, final_form_list)
-
- # Otherwise, move along to the next step.
- else:
- form = self.get_form(next_step)
- current_step = next_step
-
- return self.render(form, request, current_step)
-
- def render(self, form, request, step, context=None):
- "Renders the given Form object, returning an HttpResponse."
- old_data = request.POST
- prev_fields = []
- if old_data:
- hidden = forms.HiddenInput()
- # Collect all data from previous steps and render it as HTML hidden fields.
- for i in range(step):
- old_form = self.get_form(i, old_data)
- hash_name = 'hash_%s' % i
- prev_fields.extend([bf.as_hidden() for bf in old_form])
- prev_fields.append(hidden.render(hash_name, old_data.get(hash_name, self.security_hash(request, old_form))))
- return self.render_template(request, form, ''.join(prev_fields), step, context)
-
- # METHODS SUBCLASSES MIGHT OVERRIDE IF APPROPRIATE ########################
-
- def prefix_for_step(self, step):
- "Given the step, returns a Form prefix to use."
- return str(step)
-
- def render_hash_failure(self, request, step):
- """
- Hook for rendering a template if a hash check failed.
-
- step is the step that failed. Any previous step is guaranteed to be
- valid.
-
- This default implementation simply renders the form for the given step,
- but subclasses may want to display an error message, etc.
- """
- return self.render(self.get_form(step), request, step, context={'wizard_error': 'We apologize, but your form has expired. Please continue filling out the form from this page.'})
-
- def render_revalidation_failure(self, request, step, form):
- """
- Hook for rendering a template if final revalidation failed.
-
- It is highly unlikely that this point would ever be reached, but See
- the comment in __call__() for an explanation.
- """
- return self.render(form, request, step)
-
- def security_hash(self, request, form):
- """
- Calculates the security hash for the given HttpRequest and Form instances.
-
- This creates a list of the form field names/values in a deterministic
- order, pickles the result with the SECRET_KEY setting and takes an md5
- hash of that.
-
- Subclasses may want to take into account request-specific information,
- such as the IP address.
- """
- data = [(bf.name, bf.data or '') for bf in form] + [settings.SECRET_KEY]
- # Use HIGHEST_PROTOCOL because it's the most efficient. It requires
- # Python 2.3, but Django requires 2.3 anyway, so that's OK.
- pickled = pickle.dumps(data, protocol=pickle.HIGHEST_PROTOCOL)
- return md5.new(pickled).hexdigest()
-
- def determine_step(self, request, *args, **kwargs):
- """
- Given the request object and whatever *args and **kwargs were passed to
- __call__(), returns the current step (which is zero-based).
-
- Note that the result should not be trusted. It may even be a completely
- invalid number. It's not the job of this method to validate it.
- """
- if not request.POST:
- return 0
- try:
- step = int(request.POST.get(self.step_field_name, 0))
- except ValueError:
- return 0
- return step
-
- def parse_params(self, request, *args, **kwargs):
- """
- Hook for setting some state, given the request object and whatever
- *args and **kwargs were passed to __call__(), sets some state.
-
- This is called at the beginning of __call__().
- """
- pass
-
- def get_template(self, step):
- """
- Hook for specifying the name of the template to use for a given step.
-
- Note that this can return a tuple of template names if you'd like to
- use the template system's select_template() hook.
- """
- return 'forms/wizard.html'
-
- def render_template(self, request, form, previous_fields, step, context=None):
- """
- Renders the template for the given step, returning an HttpResponse object.
-
- Override this method if you want to add a custom context, return a
- different MIME type, etc. If you only need to override the template
- name, use get_template() instead.
-
- The template will be rendered with the following context:
- step_field -- The name of the hidden field containing the step.
- step0 -- The current step (zero-based).
- step -- The current step (one-based).
- step_count -- The total number of steps.
- form -- The Form instance for the current step (either empty
- or with errors).
- previous_fields -- A string representing every previous data field,
- plus hashes for completed forms, all in the form of
- hidden fields. Note that you'll need to run this
- through the "safe" template filter, to prevent
- auto-escaping, because it's raw HTML.
- """
- context = context or {}
- context.update(self.extra_context)
- return render_to_response(self.get_template(self.step), dict(context,
- step_field=self.step_field_name,
- step0=step,
- step=step + 1,
- step_count=self.num_steps(),
- form=form,
- previous_fields=previous_fields
- ), context_instance=RequestContext(request))
-
- def process_step(self, request, form, step):
- """
- Hook for modifying the FormWizard's internal state, given a fully
- validated Form object. The Form is guaranteed to have clean, valid
- data.
-
- This method should *not* modify any of that data. Rather, it might want
- to set self.extra_context or dynamically alter self.form_list, based on
- previously submitted forms.
-
- Note that this method is called every time a page is rendered for *all*
- submitted steps.
- """
- pass
-
- # METHODS SUBCLASSES MUST OVERRIDE ########################################
-
- def done(self, request, form_list):
- """
- Hook for doing something with the validated data. This is responsible
- for the final processing.
-
- form_list is a list of Form instances, each containing clean, valid
- data.
- """
- raise NotImplementedError("Your %s class has not defined a done() method, which is required." % self.__class__.__name__)
+""" +FormWizard class -- implements a multi-page form, validating between each +step and storing the form's state as HTML hidden fields so that no state is +stored on the server side. +""" + +from django import newforms as forms +from django.conf import settings +from django.http import Http404 +from django.shortcuts import render_to_response +from django.template.context import RequestContext +import cPickle as pickle +import md5 + +class FormWizard(object): + # Dictionary of extra template context variables. + extra_context = {} + + # The HTML (and POST data) field name for the "step" variable. + step_field_name="wizard_step" + + # METHODS SUBCLASSES SHOULDN'T OVERRIDE ################################### + + def __init__(self, form_list, initial=None): + "form_list should be a list of Form classes (not instances)." + self.form_list = form_list[:] + self.initial = initial or {} + self.step = 0 # A zero-based counter keeping track of which step we're in. + + def __repr__(self): + return "step: %d\nform_list: %s\ninitial_data: %s" % (self.step, self.form_list, self.initial) + + def get_form(self, step, data=None): + "Helper method that returns the Form instance for the given step." + return self.form_list[step](data, prefix=self.prefix_for_step(step), initial=self.initial.get(step, None)) + + def num_steps(self): + "Helper method that returns the number of steps." + # You might think we should just set "self.form_list = len(form_list)" + # in __init__(), but this calculation needs to be dynamic, because some + # hook methods might alter self.form_list. + return len(self.form_list) + + def __call__(self, request, *args, **kwargs): + """ + Main method that does all the hard work, conforming to the Django view + interface. + """ + if 'extra_context' in kwargs: + self.extra_context.update(kwargs['extra_context']) + current_step = self.determine_step(request, *args, **kwargs) + self.parse_params(request, *args, **kwargs) + + # Sanity check. + if current_step >= self.num_steps(): + raise Http404('Step %s does not exist' % current_step) + + # For each previous step, verify the hash and process. + # TODO: Move "hash_%d" to a method to make it configurable. + for i in range(current_step): + form = self.get_form(i, request.POST) + if request.POST.get("hash_%d" % i, '') != self.security_hash(request, form): + return self.render_hash_failure(request, i) + self.process_step(request, form, i) + + # Process the current step. If it's valid, go to the next step or call + # done(), depending on whether any steps remain. + if request.method == 'POST': + form = self.get_form(current_step, request.POST) + else: + form = self.get_form(current_step) + if form.is_valid(): + self.process_step(request, form, current_step) + next_step = current_step + 1 + + # If this was the last step, validate all of the forms one more + # time, as a sanity check, and call done(). + num = self.num_steps() + if next_step == num: + final_form_list = [self.get_form(i, request.POST) for i in range(num)] + + # Validate all the forms. If any of them fail validation, that + # must mean the validator relied on some other input, such as + # an external Web site. + for i, f in enumerate(final_form_list): + if not f.is_valid(): + return self.render_revalidation_failure(request, i, f) + return self.done(request, final_form_list) + + # Otherwise, move along to the next step. + else: + form = self.get_form(next_step) + current_step = next_step + + return self.render(form, request, current_step) + + def render(self, form, request, step, context=None): + "Renders the given Form object, returning an HttpResponse." + old_data = request.POST + prev_fields = [] + if old_data: + hidden = forms.HiddenInput() + # Collect all data from previous steps and render it as HTML hidden fields. + for i in range(step): + old_form = self.get_form(i, old_data) + hash_name = 'hash_%s' % i + prev_fields.extend([bf.as_hidden() for bf in old_form]) + prev_fields.append(hidden.render(hash_name, old_data.get(hash_name, self.security_hash(request, old_form)))) + return self.render_template(request, form, ''.join(prev_fields), step, context) + + # METHODS SUBCLASSES MIGHT OVERRIDE IF APPROPRIATE ######################## + + def prefix_for_step(self, step): + "Given the step, returns a Form prefix to use." + return str(step) + + def render_hash_failure(self, request, step): + """ + Hook for rendering a template if a hash check failed. + + step is the step that failed. Any previous step is guaranteed to be + valid. + + This default implementation simply renders the form for the given step, + but subclasses may want to display an error message, etc. + """ + return self.render(self.get_form(step), request, step, context={'wizard_error': 'We apologize, but your form has expired. Please continue filling out the form from this page.'}) + + def render_revalidation_failure(self, request, step, form): + """ + Hook for rendering a template if final revalidation failed. + + It is highly unlikely that this point would ever be reached, but See + the comment in __call__() for an explanation. + """ + return self.render(form, request, step) + + def security_hash(self, request, form): + """ + Calculates the security hash for the given HttpRequest and Form instances. + + This creates a list of the form field names/values in a deterministic + order, pickles the result with the SECRET_KEY setting and takes an md5 + hash of that. + + Subclasses may want to take into account request-specific information, + such as the IP address. + """ + data = [(bf.name, bf.data or '') for bf in form] + [settings.SECRET_KEY] + # Use HIGHEST_PROTOCOL because it's the most efficient. It requires + # Python 2.3, but Django requires 2.3 anyway, so that's OK. + pickled = pickle.dumps(data, protocol=pickle.HIGHEST_PROTOCOL) + return md5.new(pickled).hexdigest() + + def determine_step(self, request, *args, **kwargs): + """ + Given the request object and whatever *args and **kwargs were passed to + __call__(), returns the current step (which is zero-based). + + Note that the result should not be trusted. It may even be a completely + invalid number. It's not the job of this method to validate it. + """ + if not request.POST: + return 0 + try: + step = int(request.POST.get(self.step_field_name, 0)) + except ValueError: + return 0 + return step + + def parse_params(self, request, *args, **kwargs): + """ + Hook for setting some state, given the request object and whatever + *args and **kwargs were passed to __call__(), sets some state. + + This is called at the beginning of __call__(). + """ + pass + + def get_template(self, step): + """ + Hook for specifying the name of the template to use for a given step. + + Note that this can return a tuple of template names if you'd like to + use the template system's select_template() hook. + """ + return 'forms/wizard.html' + + def render_template(self, request, form, previous_fields, step, context=None): + """ + Renders the template for the given step, returning an HttpResponse object. + + Override this method if you want to add a custom context, return a + different MIME type, etc. If you only need to override the template + name, use get_template() instead. + + The template will be rendered with the following context: + step_field -- The name of the hidden field containing the step. + step0 -- The current step (zero-based). + step -- The current step (one-based). + step_count -- The total number of steps. + form -- The Form instance for the current step (either empty + or with errors). + previous_fields -- A string representing every previous data field, + plus hashes for completed forms, all in the form of + hidden fields. Note that you'll need to run this + through the "safe" template filter, to prevent + auto-escaping, because it's raw HTML. + """ + context = context or {} + context.update(self.extra_context) + return render_to_response(self.get_template(self.step), dict(context, + step_field=self.step_field_name, + step0=step, + step=step + 1, + step_count=self.num_steps(), + form=form, + previous_fields=previous_fields + ), context_instance=RequestContext(request)) + + def process_step(self, request, form, step): + """ + Hook for modifying the FormWizard's internal state, given a fully + validated Form object. The Form is guaranteed to have clean, valid + data. + + This method should *not* modify any of that data. Rather, it might want + to set self.extra_context or dynamically alter self.form_list, based on + previously submitted forms. + + Note that this method is called every time a page is rendered for *all* + submitted steps. + """ + pass + + # METHODS SUBCLASSES MUST OVERRIDE ######################################## + + def done(self, request, form_list): + """ + Hook for doing something with the validated data. This is responsible + for the final processing. + + form_list is a list of Form instances, each containing clean, valid + data. + """ + raise NotImplementedError("Your %s class has not defined a done() method, which is required." % self.__class__.__name__) diff --git a/django/contrib/sessions/backends/file.py b/django/contrib/sessions/backends/file.py index cd3e3d9c75..d65c81c101 100644 --- a/django/contrib/sessions/backends/file.py +++ b/django/contrib/sessions/backends/file.py @@ -9,7 +9,9 @@ class SessionStore(SessionBase): Implements a file based session store. """ def __init__(self, session_key=None): - self.storage_path = getattr(settings, "SESSION_FILE_PATH", tempfile.gettempdir()) + self.storage_path = getattr(settings, "SESSION_FILE_PATH", None) + if not self.storage_path: + self.storage_path = tempfile.gettempdir() # Make sure the storage path is valid. if not os.path.isdir(self.storage_path): diff --git a/django/contrib/syndication/feeds.py b/django/contrib/syndication/feeds.py index 45b97d970a..85af79cc27 100644 --- a/django/contrib/syndication/feeds.py +++ b/django/contrib/syndication/feeds.py @@ -3,7 +3,8 @@ from django.template import Context, loader, Template, TemplateDoesNotExist from django.contrib.sites.models import Site, RequestSite from django.utils import feedgenerator from django.utils.encoding import smart_unicode, iri_to_uri -from django.conf import settings +from django.conf import settings +from django.template import RequestContext def add_domain(domain, url): if not (url.startswith('http://') or url.startswith('https://')): @@ -55,18 +56,23 @@ class Feed(object): return attr() return attr + def get_object(self, bits): + return None + def get_feed(self, url=None): """ Returns a feedgenerator.DefaultFeed object, fully populated, for this feed. Raises FeedDoesNotExist for invalid parameters. """ if url: - try: - obj = self.get_object(url.split('/')) - except (AttributeError, ObjectDoesNotExist): - raise FeedDoesNotExist + bits = url.split('/') else: - obj = None + bits = [] + + try: + obj = self.get_object(bits) + except ObjectDoesNotExist: + raise FeedDoesNotExist if Site._meta.installed: current_site = Site.objects.get_current() @@ -119,9 +125,9 @@ class Feed(object): else: author_email = author_link = None feed.add_item( - title = title_tmp.render(Context({'obj': item, 'site': current_site})), + title = title_tmp.render(RequestContext(self.request, {'obj': item, 'site': current_site})), link = link, - description = description_tmp.render(Context({'obj': item, 'site': current_site})), + description = description_tmp.render(RequestContext(self.request, {'obj': item, 'site': current_site})), unique_id = self.__get_dynamic_attr('item_guid', item, link), enclosure = enc, pubdate = self.__get_dynamic_attr('item_pubdate', item), diff --git a/django/core/management/__init__.py b/django/core/management/__init__.py index d78e2eda0b..819b19a366 100644 --- a/django/core/management/__init__.py +++ b/django/core/management/__init__.py @@ -243,7 +243,7 @@ def setup_environ(settings_mod): # way. For example, if this file (manage.py) lives in a directory # "myproject", this code would add "/path/to/myproject" to sys.path. project_directory, settings_filename = os.path.split(settings_mod.__file__) - if not project_directory: + if project_directory == os.curdir or not project_directory: project_directory = os.getcwd() project_name = os.path.basename(project_directory) settings_name = os.path.splitext(settings_filename)[0] diff --git a/django/core/management/commands/startproject.py b/django/core/management/commands/startproject.py index ab4f409f15..867d4fd3da 100644 --- a/django/core/management/commands/startproject.py +++ b/django/core/management/commands/startproject.py @@ -20,8 +20,13 @@ class Command(LabelCommand): # the parent directory. directory = os.getcwd() - if project_name in INVALID_PROJECT_NAMES: - raise CommandError("%r conflicts with the name of an existing Python module and cannot be used as a project name. Please try another name." % project_name) + try: + proj_name = __import__(project_name) + if proj_name: + raise CommandError("%r conflicts with the name of an existing Python module and cannot be used as a project name. Please try another name." % project_name) + except ImportError: + if project_name in INVALID_PROJECT_NAMES: + raise CommandError("%r contains an invalid project name. Please try another name." % project_name) copy_helper(self.style, 'project', project_name, directory) diff --git a/django/core/paginator.py b/django/core/paginator.py index 71a5479fd5..dabd20dfc0 100644 --- a/django/core/paginator.py +++ b/django/core/paginator.py @@ -1,46 +1,149 @@ class InvalidPage(Exception): pass -class ObjectPaginator(object): +class Paginator(object): + def __init__(self, object_list, per_page, orphans=0, allow_empty_first_page=True): + self.object_list = object_list + self.per_page = per_page + self.orphans = orphans + self.allow_empty_first_page = allow_empty_first_page + self._num_pages = self._count = None + + def validate_number(self, number): + "Validates the given 1-based page number." + try: + number = int(number) + except ValueError: + raise InvalidPage('That page number is not an integer') + if number < 1: + raise InvalidPage('That page number is less than 1') + if number > self.num_pages: + if number == 1 and self.allow_empty_first_page: + pass + else: + raise InvalidPage('That page contains no results') + return number + + def page(self, number): + "Returns a Page object for the given 1-based page number." + number = self.validate_number(number) + bottom = (number - 1) * self.per_page + top = bottom + self.per_page + if top + self.orphans >= self.count: + top = self.count + return Page(self.object_list[bottom:top], number, self) + + def _get_count(self): + "Returns the total number of objects, across all pages." + if self._count is None: + self._count = len(self.object_list) + return self._count + count = property(_get_count) + + def _get_num_pages(self): + "Returns the total number of pages." + if self._num_pages is None: + hits = self.count - 1 - self.orphans + if hits < 1: + hits = 0 + if hits == 0 and not self.allow_empty_first_page: + self._num_pages = 0 + else: + self._num_pages = hits // self.per_page + 1 + return self._num_pages + num_pages = property(_get_num_pages) + + def _get_page_range(self): + """ + Returns a 1-based range of pages for iterating through within + a template for loop. + """ + return range(1, self.num_pages + 1) + page_range = property(_get_page_range) + +class QuerySetPaginator(Paginator): """ - 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 + Like Paginator, but works on QuerySets. + """ + def _get_count(self): + if self._count is None: + self._count = self.object_list.count() + return self._count + count = property(_get_count) + +class Page(object): + def __init__(self, object_list, number, paginator): + self.object_list = object_list + self.number = number + self.paginator = paginator + + def __repr__(self): + return '<Page %s of %s>' % (self.number, self.paginator.num_pages) + + def has_next(self): + return self.number < self.paginator.num_pages + + def has_previous(self): + return self.number > 1 + + def has_other_pages(self): + return self.has_previous() or self.has_next() + + def next_page_number(self): + return self.number + 1 + + def previous_page_number(self): + return self.number - 1 + + def start_index(self): + """ + Returns the 1-based index of the first object on this page, + relative to total objects in the paginator. + """ + return (self.paginator.per_page * (self.number - 1)) + 1 + + def end_index(self): + """ + Returns the 1-based index of the last object on this page, + relative to total objects found (hits). + """ + if self.number == self.paginator.num_pages: + return self.paginator.count + return self.number * self.paginator.per_page + +class ObjectPaginator(Paginator): + """ + Legacy ObjectPaginator class, for backwards compatibility. + + Note that each method on this class that takes page_number expects a + zero-based page number, whereas the new API (Paginator/Page) uses one-based + page numbers. """ def __init__(self, query_set, num_per_page, orphans=0): + Paginator.__init__(self, query_set, num_per_page, orphans) + import warnings + warnings.warn("The ObjectPaginator is deprecated. Use django.core.paginator.Paginator instead.", DeprecationWarning) + + # Keep these attributes around for backwards compatibility. self.query_set = query_set self.num_per_page = num_per_page - self.orphans = orphans self._hits = self._pages = None - self._page_range = None def validate_page_number(self, page_number): try: - page_number = int(page_number) + page_number = int(page_number) + 1 except ValueError: raise InvalidPage - if page_number < 0 or page_number > self.pages - 1: - raise InvalidPage - return page_number + return self.validate_number(page_number) 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] + try: + page_number = int(page_number) + 1 + except ValueError: + raise InvalidPage + return self.page(page_number).object_list def has_next_page(self, page_number): - "Does page $page_number have a 'next' page?" return page_number < self.pages - 1 def has_previous_page(self, page_number): @@ -52,7 +155,7 @@ class ObjectPaginator(object): relative to total objects found (hits). """ page_number = self.validate_page_number(page_number) - return (self.num_per_page * page_number) + 1 + return (self.num_per_page * (page_number - 1)) + 1 def last_on_page(self, page_number): """ @@ -60,40 +163,23 @@ class ObjectPaginator(object): relative to total objects found (hits). """ page_number = self.validate_page_number(page_number) - page_number += 1 # 1-base - if page_number == self.pages: - return self.hits + if page_number == self.num_pages: + return self.count return page_number * self.num_per_page - def _get_hits(self): - if self._hits is None: - # Try .count() or fall back to len(). + def _get_count(self): + # The old API allowed for self.object_list to be either a QuerySet or a + # list. Here, we handle both. + if self._count is None: 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: - hits = (self.hits - 1 - self.orphans) - if hits < 1: - hits = 0 - self._pages = hits // self.num_per_page + 1 - return self._pages - - def _get_page_range(self): - """ - Returns a 1-based range of pages for iterating through within - a template for loop. - """ - if self._page_range is None: - self._page_range = range(1, self.pages + 1) - return self._page_range + self._count = self.object_list.count() + except AttributeError: + self._count = len(self.object_list) + return self._count + count = property(_get_count) - hits = property(_get_hits) - pages = property(_get_pages) - page_range = property(_get_page_range) + # The old API called it "hits" instead of "count". + hits = count + + # The old API called it "pages" instead of "num_pages". + pages = Paginator.num_pages diff --git a/django/core/serializers/base.py b/django/core/serializers/base.py index 50c87ff276..a79497ecec 100644 --- a/django/core/serializers/base.py +++ b/django/core/serializers/base.py @@ -22,10 +22,10 @@ class Serializer(object): Abstract serializer base class. """ - # Indicates if the implemented serializer is only available for + # Indicates if the implemented serializer is only available for # internal Django use. internal_use_only = False - + def serialize(self, queryset, **options): """ Serialize a queryset. @@ -60,8 +60,6 @@ class Serializer(object): """ if isinstance(field, models.DateTimeField): value = getattr(obj, field.name).strftime("%Y-%m-%d %H:%M:%S") - elif isinstance(field, models.FileField): - value = getattr(obj, "get_%s_url" % field.name, lambda: None)() else: value = field.flatten_data(follow=None, obj=obj).get(field.name, "") return smart_unicode(value) diff --git a/django/core/servers/fastcgi.py b/django/core/servers/fastcgi.py index de04a5af62..d7145e15ec 100644 --- a/django/core/servers/fastcgi.py +++ b/django/core/servers/fastcgi.py @@ -37,7 +37,9 @@ Optional Fcgi settings: (setting=value) 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 + workdir=DIRECTORY change to this directory when daemonizing. + outlog=FILE write stdout to this file. + errlog=FILE write stderr to this file. Examples: Run a "standard" fastcgi process on a file-descriptor @@ -69,6 +71,8 @@ FASTCGI_OPTIONS = { 'minspare': 2, 'maxchildren': 50, 'maxrequests': 0, + 'outlog': None, + 'errlog': None, } def fastcgi_help(message=None): @@ -150,9 +154,15 @@ def runfastcgi(argset=[], **kwargs): else: return fastcgi_help("ERROR: Invalid option for daemonize parameter.") + daemon_kwargs = {} + if options['outlog']: + daemon_kwargs['out_log'] = options['outlog'] + if options['errlog']: + daemon_kwargs['err_log'] = options['errlog'] + if daemonize: from django.utils.daemonize import become_daemon - become_daemon(our_home_dir=options["workdir"]) + become_daemon(our_home_dir=options["workdir"], **daemon_kwargs) if options["pidfile"]: fp = open(options["pidfile"], "w") diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index edf9259510..6e426318f5 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -856,6 +856,16 @@ class FilePathField(Field): self.path, self.match, self.recursive = path, match, recursive kwargs['max_length'] = kwargs.get('max_length', 100) Field.__init__(self, verbose_name, name, **kwargs) + + def formfield(self, **kwargs): + defaults = { + 'path': self.path, + 'match': self.match, + 'recursive': self.recursive, + 'form_class': forms.FilePathField, + } + defaults.update(kwargs) + return super(FilePathField, self).formfield(**defaults) def get_manipulator_field_objs(self): return [curry(oldforms.FilePathField, path=self.path, match=self.match, recursive=self.recursive)] diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index bb1783b87c..f2254d7373 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -622,6 +622,13 @@ class ForeignKey(RelatedField, Field): params['choices'] = self.get_choices_default() return field_objs, params + def get_default(self): + "Here we check if the default value is an object and return the to_field if so." + field_default = super(ForeignKey, self).get_default() + if isinstance(field_default, self.rel.to): + return getattr(field_default, self.rel.get_related_field().attname) + return field_default + def get_manipulator_field_objs(self): rel_field = self.rel.get_related_field() if self.rel.raw_id_admin and not isinstance(rel_field, AutoField): diff --git a/django/db/models/query.py b/django/db/models/query.py index 388a54dc85..f9b8baf3b1 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -1,6 +1,7 @@ import warnings -from django.db import connection, transaction +from django.conf import settings +from django.db import connection, transaction, IntegrityError from django.db.models.fields import DateField, FieldDoesNotExist from django.db.models.query_utils import Q, not_q from django.db.models import signals, sql @@ -204,11 +205,14 @@ class _QuerySet(object): try: return self.get(**kwargs), False except self.model.DoesNotExist: - params = dict([(k, v) for k, v in kwargs.items() if '__' not in k]) - params.update(defaults) - obj = self.model(**params) - obj.save() - return obj, True + try: + params = dict([(k, v) for k, v in kwargs.items() if '__' not in k]) + params.update(defaults) + obj = self.model(**params) + obj.save() + return obj, True + except IntegrityError, e: + return self.get(**kwargs), False def latest(self, field_name=None): """ diff --git a/django/http/__init__.py b/django/http/__init__.py index 5439aa6c63..7faa3c875e 100644 --- a/django/http/__init__.py +++ b/django/http/__init__.py @@ -82,6 +82,9 @@ class HttpRequest(object): def is_secure(self): return os.environ.get("HTTPS") == "on" + def is_ajax(self): + return self.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest' + def _set_encoding(self, val): """ Sets the encoding used for GET/POST accesses. If the GET or POST diff --git a/django/newforms/extras/widgets.py b/django/newforms/extras/widgets.py index 0097ba3f54..e3ef1d7f69 100644 --- a/django/newforms/extras/widgets.py +++ b/django/newforms/extras/widgets.py @@ -3,6 +3,7 @@ Extra HTML Widget classes """ import datetime +import re from django.newforms.widgets import Widget, Select from django.utils.dates import MONTHS @@ -10,6 +11,8 @@ from django.utils.safestring import mark_safe __all__ = ('SelectDateWidget',) +RE_DATE = re.compile(r'(\d{4})-(\d\d?)-(\d\d?)$') + class SelectDateWidget(Widget): """ A Widget that splits date input into three <select> boxes. @@ -32,28 +35,43 @@ class SelectDateWidget(Widget): def render(self, name, value, attrs=None): try: - value = datetime.date(*map(int, value.split('-'))) year_val, month_val, day_val = value.year, value.month, value.day - except (AttributeError, TypeError, ValueError): + except AttributeError: year_val = month_val = day_val = None + if isinstance(value, basestring): + match = RE_DATE.match(value) + if match: + year_val, month_val, day_val = [int(v) for v in match.groups()] output = [] + if 'id' in self.attrs: + id_ = self.attrs['id'] + else: + id_ = 'id_%s' % name + month_choices = MONTHS.items() month_choices.sort() - select_html = Select(choices=month_choices).render(self.month_field % name, month_val) + local_attrs = self.build_attrs(id=self.month_field % id_) + select_html = Select(choices=month_choices).render(self.month_field % name, month_val, local_attrs) output.append(select_html) day_choices = [(i, i) for i in range(1, 32)] - select_html = Select(choices=day_choices).render(self.day_field % name, day_val) + local_attrs['id'] = self.day_field % id_ + select_html = Select(choices=day_choices).render(self.day_field % name, day_val, local_attrs) output.append(select_html) year_choices = [(i, i) for i in self.years] - select_html = Select(choices=year_choices).render(self.year_field % name, year_val) + local_attrs['id'] = self.year_field % id_ + select_html = Select(choices=year_choices).render(self.year_field % name, year_val, local_attrs) output.append(select_html) return mark_safe(u'\n'.join(output)) + def id_for_label(self, id_): + return '%s_month' % id_ + id_for_label = classmethod(id_for_label) + def value_from_datadict(self, data, files, name): y, m, d = data.get(self.year_field % name), data.get(self.month_field % name), data.get(self.day_field % name) if y and m and d: diff --git a/django/newforms/fields.py b/django/newforms/fields.py index bc3e543037..08e8b842ec 100644 --- a/django/newforms/fields.py +++ b/django/newforms/fields.py @@ -4,6 +4,7 @@ Field classes. import copy import datetime +import os import re import time # Python 2.3 fallbacks @@ -31,7 +32,7 @@ __all__ = ( 'RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField', 'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField', 'ComboField', 'MultiValueField', 'FloatField', 'DecimalField', - 'SplitDateTimeField', 'IPAddressField', + 'SplitDateTimeField', 'IPAddressField', 'FilePathField', ) # These values, if given to to_python(), will trigger the self.required check. @@ -718,6 +719,33 @@ class MultiValueField(Field): """ raise NotImplementedError('Subclasses must implement this method.') +class FilePathField(ChoiceField): + def __init__(self, path, match=None, recursive=False, required=True, + widget=Select, label=None, initial=None, help_text=None, + *args, **kwargs): + self.path, self.match, self.recursive = path, match, recursive + super(FilePathField, self).__init__(choices=(), required=required, + widget=widget, label=label, initial=initial, help_text=help_text, + *args, **kwargs) + self.choices = [] + if self.match is not None: + self.match_re = re.compile(self.match) + if recursive: + for root, dirs, files in os.walk(self.path): + for f in files: + if self.match is None or self.match_re.search(f): + f = os.path.join(root, f) + self.choices.append((f, f.replace(path, "", 1))) + else: + try: + for f in os.listdir(self.path): + full_file = os.path.join(self.path, f) + if os.path.isfile(full_file) and (self.match is None or self.match_re.search(f)): + self.choices.append((full_file, f)) + except OSError: + pass + self.widget.choices = self.choices + class SplitDateTimeField(MultiValueField): default_error_messages = { 'invalid_date': _(u'Enter a valid date.'), diff --git a/django/newforms/models.py b/django/newforms/models.py index f3809dc370..0590839b25 100644 --- a/django/newforms/models.py +++ b/django/newforms/models.py @@ -277,19 +277,18 @@ class ModelForm(BaseModelForm): # Fields ##################################################################### -class QuerySetIterator(object): - def __init__(self, queryset, empty_label, cache_choices): - self.queryset = queryset - self.empty_label = empty_label - self.cache_choices = cache_choices +class ModelChoiceIterator(object): + def __init__(self, field): + self.field = field + self.queryset = field.queryset def __iter__(self): - if self.empty_label is not None: - yield (u"", self.empty_label) + if self.field.empty_label is not None: + yield (u"", self.field.empty_label) for obj in self.queryset: - yield (obj.pk, smart_unicode(obj)) + yield (obj.pk, self.field.label_from_instance(obj)) # Clear the QuerySet cache if required. - if not self.cache_choices: + if not self.field.cache_choices: self.queryset._result_cache = None class ModelChoiceField(ChoiceField): @@ -306,6 +305,7 @@ class ModelChoiceField(ChoiceField): help_text=None, *args, **kwargs): self.empty_label = empty_label self.cache_choices = cache_choices + # Call Field instead of ChoiceField __init__() because we don't need # ChoiceField.__init__(). Field.__init__(self, required, widget, label, initial, help_text, @@ -321,19 +321,30 @@ class ModelChoiceField(ChoiceField): queryset = property(_get_queryset, _set_queryset) + # this method will be used to create object labels by the QuerySetIterator. + # Override it to customize the label. + def label_from_instance(self, obj): + """ + This method is used to convert objects into strings; it's used to + generate the labels for the choices presented by this object. Subclasses + can override this method to customize the display of the choices. + """ + return smart_unicode(obj) + def _get_choices(self): # If self._choices is set, then somebody must have manually set # the property self.choices. In this case, just return self._choices. if hasattr(self, '_choices'): return self._choices + # Otherwise, execute the QuerySet in self.queryset to determine the - # choices dynamically. Return a fresh QuerySetIterator that has not - # been consumed. Note that we're instantiating a new QuerySetIterator - # *each* time _get_choices() is called (and, thus, each time - # self.choices is accessed) so that we can ensure the QuerySet has not - # been consumed. - return QuerySetIterator(self.queryset, self.empty_label, - self.cache_choices) + # choices dynamically. Return a fresh QuerySetIterator that has not been + # consumed. Note that we're instantiating a new QuerySetIterator *each* + # time _get_choices() is called (and, thus, each time self.choices is + # accessed) so that we can ensure the QuerySet has not been consumed. This + # construct might look complicated but it allows for lazy evaluation of + # the queryset. + return ModelChoiceIterator(self) def _set_choices(self, value): # This method is copied from ChoiceField._set_choices(). It's necessary diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py index 8e5bcf5bd8..cef314387a 100644 --- a/django/template/defaultfilters.py +++ b/django/template/defaultfilters.py @@ -124,7 +124,10 @@ def floatformat(text, arg=-1): d = int(arg) except ValueError: return force_unicode(f) - m = f - int(f) + try: + m = f - int(f) + except OverflowError: + return force_unicode(f) if not m and d < 0: return mark_safe(u'%d' % int(f)) else: diff --git a/django/test/client.py b/django/test/client.py index 67d3aa9030..bac28f797b 100644 --- a/django/test/client.py +++ b/django/test/client.py @@ -1,3 +1,4 @@ +import urllib import sys from cStringIO import StringIO from django.conf import settings @@ -208,7 +209,7 @@ class Client: r = { 'CONTENT_LENGTH': None, 'CONTENT_TYPE': 'text/html; charset=utf-8', - 'PATH_INFO': path, + 'PATH_INFO': urllib.unquote(path), 'QUERY_STRING': urlencode(data, doseq=True), 'REQUEST_METHOD': 'GET', } @@ -227,7 +228,7 @@ class Client: r = { 'CONTENT_LENGTH': len(post_data), 'CONTENT_TYPE': content_type, - 'PATH_INFO': path, + 'PATH_INFO': urllib.unquote(path), 'REQUEST_METHOD': 'POST', 'wsgi.input': StringIO(post_data), } diff --git a/django/utils/daemonize.py b/django/utils/daemonize.py index 9671b8d91f..f0c2f684bd 100644 --- a/django/utils/daemonize.py +++ b/django/utils/daemonize.py @@ -18,10 +18,10 @@ if os.name == 'posix': # Second fork try: if os.fork() > 0: - sys.exit(0) + os._exit(0) except OSError, e: sys.stderr.write("fork #2 failed: (%d) %s\n" % (e.errno, e.strerror)) - sys.exit(1) + os._exit(1) si = open('/dev/null', 'r') so = open(out_log, 'a+', 0) @@ -29,6 +29,8 @@ if os.name == 'posix': os.dup2(si.fileno(), sys.stdin.fileno()) os.dup2(so.fileno(), sys.stdout.fileno()) os.dup2(se.fileno(), sys.stderr.fileno()) + # Set custom file descriptors so that they get proper buffering. + sys.stdout, sys.stderr = so, se else: def become_daemon(our_home_dir='.', out_log=None, err_log=None): """ diff --git a/django/views/debug.py b/django/views/debug.py index b7ac12db38..a118ac3126 100644 --- a/django/views/debug.py +++ b/django/views/debug.py @@ -1,11 +1,12 @@ import os import re import sys +import datetime from django.conf import settings from django.template import Template, Context, TemplateDoesNotExist from django.utils.html import escape -from django.http import HttpResponseServerError, HttpResponseNotFound +from django.http import HttpResponse, HttpResponseServerError, HttpResponseNotFound from django.utils.encoding import smart_unicode HIDDEN_SETTINGS = re.compile('SECRET|PASSWORD|PROFANITIES_LIST') @@ -70,6 +71,11 @@ def technical_500_response(request, exc_type, exc_value, tb): Create a technical server error response. The last three arguments are the values returned from sys.exc_info() and friends. """ + html = get_traceback_html(request, exc_type, exc_value, tb) + return HttpResponseServerError(html, mimetype='text/html') + +def get_traceback_html(request, exc_type, exc_value, tb): + "Return HTML code for traceback." template_info = None template_does_not_exist = False loader_debug_info = None @@ -153,13 +159,14 @@ def technical_500_response(request, exc_type, exc_value, tb): 'settings': get_safe_settings(), 'sys_executable': sys.executable, 'sys_version_info': '%d.%d.%d' % sys.version_info[0:3], + 'server_time': datetime.datetime.now(), 'django_version_info': get_version(), 'sys_path' : sys.path, 'template_info': template_info, 'template_does_not_exist': template_does_not_exist, 'loader_debug_info': loader_debug_info, }) - return HttpResponseServerError(t.render(c), mimetype='text/html') + return t.render(c) def technical_404_response(request, exception): "Create a technical 404 error response. The exception should be the Http404." @@ -190,7 +197,7 @@ def empty_urlconf(request): c = Context({ 'project_name': settings.SETTINGS_MODULE.split('.')[0] }) - return HttpResponseNotFound(t.render(c), mimetype='text/html') + return HttpResponse(t.render(c), mimetype='text/html') def _get_lines_from_file(filename, lineno, context_lines, loader=None, module_name=None): """ @@ -384,6 +391,10 @@ TECHNICAL_500_TEMPLATE = """ <th>Python Path:</th> <td>{{ sys_path }}</td> </tr> + <tr> + <th>Server time:</th> + <td>{{server_time|date:"r"}}</td> + </tr> </table> </div> {% if unicode_hint %} diff --git a/django/views/generic/list_detail.py b/django/views/generic/list_detail.py index 617a6443a2..cb9b014eb5 100644 --- a/django/views/generic/list_detail.py +++ b/django/views/generic/list_detail.py @@ -1,7 +1,7 @@ from django.template import loader, RequestContext from django.http import Http404, HttpResponse from django.core.xheaders import populate_xheaders -from django.core.paginator import ObjectPaginator, InvalidPage +from django.core.paginator import QuerySetPaginator, InvalidPage from django.core.exceptions import ObjectDoesNotExist def object_list(request, queryset, paginate_by=None, page=None, @@ -45,43 +45,47 @@ def object_list(request, queryset, paginate_by=None, page=None, if extra_context is None: extra_context = {} queryset = queryset._clone() if paginate_by: - paginator = ObjectPaginator(queryset, paginate_by) + paginator = QuerySetPaginator(queryset, paginate_by, allow_empty_first_page=allow_empty) if not page: page = request.GET.get('page', 1) try: page_number = int(page) except ValueError: if page == 'last': - page_number = paginator.pages + page_number = paginator.num_pages else: - # Page is not 'last', nor can it be converted to an int + # Page is not 'last', nor can it be converted to an int. raise Http404 try: - object_list = paginator.get_page(page_number - 1) + page_obj = paginator.page(page_number) except InvalidPage: - if page_number == 1 and allow_empty: - object_list = [] - else: - raise Http404 + raise Http404 c = RequestContext(request, { - '%s_list' % template_object_name: object_list, - 'is_paginated': paginator.pages > 1, - 'results_per_page': paginate_by, - 'has_next': paginator.has_next_page(page_number - 1), - 'has_previous': paginator.has_previous_page(page_number - 1), - 'page': page_number, - 'next': page_number + 1, - 'previous': page_number - 1, - 'last_on_page': paginator.last_on_page(page_number - 1), - 'first_on_page': paginator.first_on_page(page_number - 1), - 'pages': paginator.pages, - 'hits' : paginator.hits, - 'page_range' : paginator.page_range + '%s_list' % template_object_name: page_obj.object_list, + 'paginator': paginator, + 'page_obj': page_obj, + + # Legacy template context stuff. New templates should use page_obj + # to access this instead. + 'is_paginated': page_obj.has_other_pages(), + 'results_per_page': paginator.per_page, + 'has_next': page_obj.has_next(), + 'has_previous': page_obj.has_previous(), + 'page': page_obj.number, + 'next': page_obj.next_page_number(), + 'previous': page_obj.previous_page_number(), + 'last_on_page': page_obj.start_index(), + 'first_on_page': page_obj.end_index(), + 'pages': paginator.num_pages, + 'hits': paginator.count, + 'page_range': paginator.page_range, }, context_processors) else: c = RequestContext(request, { '%s_list' % template_object_name: queryset, - 'is_paginated': False + 'paginator': None, + 'page_obj': None, + 'is_paginated': False, }, context_processors) if not allow_empty and len(queryset) == 0: raise Http404 diff --git a/docs/custom_model_fields.txt b/docs/custom_model_fields.txt index 923b331a95..2b344921ef 100644 --- a/docs/custom_model_fields.txt +++ b/docs/custom_model_fields.txt @@ -401,8 +401,8 @@ For example:: # ... def get_db_prep_save(self, value): - return ''.join([''.join(l) for l in (self.north, - self.east, self.south, self.west)]) + return ''.join([''.join(l) for l in (value.north, + value.east, value.south, value.west)]) ``pre_save(self, model_instance, add)`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/generic_views.txt b/docs/generic_views.txt index 17187894c0..b7beb0b4be 100644 --- a/docs/generic_views.txt +++ b/docs/generic_views.txt @@ -751,6 +751,19 @@ In addition to ``extra_context``, the template's context will be: If the results are paginated, the context will contain these extra variables: + * **New in Django development version:** ``paginator``: An instance of + ``django.core.paginator.Paginator``. + + * **New in Django development version:** ``page_obj``: An instance of + ``django.core.paginator.Page``. + +In older versions of Django, before ``paginator`` and ``page_obj`` were added +to this template's context, the template included several other variables +related to pagination. Note that you should *NOT* use these variables anymore; +use ``paginator`` and ``page_obj`` instead, because they let you do everything +these old variables let you do (and more!). But for legacy installations, +here's a list of those old template variables: + * ``results_per_page``: The number of objects per page. (Same as the ``paginate_by`` parameter.) @@ -777,8 +790,8 @@ If the results are paginated, the context will contain these extra variables: * ``hits``: The total number of objects across *all* pages, not just this page. - * **New in Django development version:** ``page_range``: A list of the - page numbers that are available. This is 1-based. + * ``page_range``: A list of the page numbers that are available. This is + 1-based. Notes on pagination ~~~~~~~~~~~~~~~~~~~ diff --git a/docs/install.txt b/docs/install.txt index 542036e2af..341c4280e8 100644 --- a/docs/install.txt +++ b/docs/install.txt @@ -167,6 +167,20 @@ These commands will install Django in your Python installation's Installing the development version ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. admonition:: Tracking Django development + + If you decide to use the latest development version of Django, + you'll want to pay close attention to `the development timeline`_, + and you'll want to keep an eye on `the list of + backwards-incompatible changes`_; this will help you stay on top + of any new features you might want to use, as well as any changes + you'll need to make to your code when updating your copy of Django + (for stable releases, any necessary changes are documented in the + release notes). + +.. _the development timeline: http://code.djangoproject.com/timeline +.. _the list of backwards-incompatible changes: http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges + If you'd like to be able to update your Django code occasionally with the latest bug fixes and improvements, follow these instructions: diff --git a/docs/model-api.txt b/docs/model-api.txt index e4d3805236..5502bd51eb 100644 --- a/docs/model-api.txt +++ b/docs/model-api.txt @@ -626,7 +626,8 @@ option is ignored. ``default`` ~~~~~~~~~~~ -The default value for the field. +The default value for the field. This can be a value or a callable object. If +callable it will be called every time a new object is created. ``editable`` ~~~~~~~~~~~~ @@ -1814,14 +1815,15 @@ For example:: This example allows you to request ``Person.men.all()``, ``Person.women.all()``, and ``Person.people.all()``, yielding predictable results. -If you use custom ``Manager`` objects, take note that the first ``Manager`` -Django encounters (in order by which they're defined in the model) has a -special status. Django interprets the first ``Manager`` defined in a class as -the "default" ``Manager``. Certain operations -- such as Django's admin site -- -use the default ``Manager`` to obtain lists of objects, so it's generally a -good idea for the first ``Manager`` to be relatively unfiltered. In the last -example, the ``people`` ``Manager`` is defined first -- so it's the default -``Manager``. +If you use custom ``Manager`` objects, take note that the first +``Manager`` Django encounters (in the order in which they're defined +in the model) has a special status. Django interprets this first +``Manager`` defined in a class as the "default" ``Manager``, and +several parts of Django (though not the admin application) will use +that ``Manager`` exclusively for that model. As a result, it's often a +good idea to be careful in your choice of default manager, in order to +avoid a situation where overriding of ``get_query_set()`` results in +an inability to retrieve objects you'd like to work with. Model methods ============= diff --git a/docs/modelforms.txt b/docs/modelforms.txt index 47eaa9a769..05f9b1b3d4 100644 --- a/docs/modelforms.txt +++ b/docs/modelforms.txt @@ -231,6 +231,16 @@ For example:: # Create and save the new author instance. There's no need to do anything else. >>> new_author = f.save() +Other than the ``save()`` and ``save_m2m()`` methods, a ``ModelForm`` +works exactly the same way as any other ``newforms`` form. For +example, the ``is_valid()`` method is used to check for validity, the +``is_multipart()`` method is used to determine whether a form requires +multipart file upload (and hence whether ``request.FILES`` must be +passed to the form), etc.; see `the standard newforms documentation`_ +for more information. + +.. _the standard newforms documentation: ../newforms/ + Using a subset of fields on the form ------------------------------------ diff --git a/docs/newforms.txt b/docs/newforms.txt index 0b5559ab88..533ff75185 100644 --- a/docs/newforms.txt +++ b/docs/newforms.txt @@ -1333,13 +1333,14 @@ given length. An ``UploadedFile`` object has two attributes: - ====================== ===================================================== - Argument Description - ====================== ===================================================== + ====================== ==================================================== + Attribute Description + ====================== ==================================================== ``filename`` The name of the file, provided by the uploading client. + ``content`` The array of bytes comprising the file content. - ====================== ===================================================== + ====================== ==================================================== The string representation of an ``UploadedFile`` is the same as the filename attribute. @@ -1349,6 +1350,38 @@ When you use a ``FileField`` on a form, you must also remember to .. _`bind the file data to the form`: `Binding uploaded files to a form`_ +``FilePathField`` +~~~~~~~~~~~~~~~~~ + +**New in Django development version** + + * Default widget: ``Select`` + * Empty value: ``None`` + * Normalizes to: A unicode object + * Validates that the selected choice exists in the list of choices. + * Error message keys: ``required``, ``invalid_choice`` + +The field allows choosing from files inside a certain directory. It takes three +extra arguments: + + ============== ========== =============================================== + Argument Required? Description + ============== ========== =============================================== + ``path`` Yes The absolute path to the directory whose + contents you want listed. This directory must + exist. + + ``recursive`` No If ``False`` (the default) only the direct + contents of ``path`` will be offered as choices. + If ``True``, the directory will be descended + into recursively and all descendants will be + listed as choices. + + ``match`` No A regular expression pattern; only files with + names matching this expression will be allowed + as choices. + ============== ========== =============================================== + ``ImageField`` ~~~~~~~~~~~~~~ @@ -1499,6 +1532,41 @@ the bottom of this document, for examples of their use. ``SplitDateTimeField`` ~~~~~~~~~~~~~~~~~~~~~~ +Fields which handle relationships +--------------------------------- + +For representing relationships between models, two fields are +provided which can derive their choices from a ``QuerySet``, and which +place one or more model objects into the ``cleaned_data`` dictionary +of forms in which they're used. Both of these fields have an +additional required argument: + +``queryset`` + A ``QuerySet`` of model objects from which the choices for the + field will be derived, and which will be used to validate the + user's selection. + +``ModelChoiceField`` +~~~~~~~~~~~~~~~~~~~~ + +Allows the selection of a single model object, suitable for representing a +foreign key. The method receives an object as an argument and must return a +string to represent it. + +The labels for the choice field call the ``__unicode__`` method of the model to +generate string representations. To provide custom labels, subclass ``ModelChoiceField`` and override ``label_for_model``:: + + class MyModelChoiceField(ModelChoiceField): + def label_from_instance(self, obj): + return "My Object #%i" % obj.id + +``ModelMultipleChoiceField`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Allows the selection of one or more model objects, suitable for representing a +many-to-many relation. As with ``ModelChoiceField``, you can use +``label_from_instance`` to customize the object labels. + Creating custom fields ---------------------- @@ -1569,9 +1637,9 @@ The three types of cleaning methods are: These methods are run in the order given above, one field at a time. That is, for each field in the form (in the order they are declared in the form -definition), the ``Field.clean()`` method (or it's override) is run, then +definition), the ``Field.clean()`` method (or its override) is run, then ``clean_<fieldname>()``. Finally, once those two methods are run for every -field, the ``Form.clean()`` method, or it's override, is executed. +field, the ``Form.clean()`` method, or its override, is executed. As mentioned above, any of these methods can raise a ``ValidationError``. For any field, if the ``Field.clean()`` method raises a ``ValidationError``, any @@ -1693,7 +1761,7 @@ For example, take the following simple form:: comment = forms.CharField() This form will include three default TextInput widgets, with default rendering - -no CSS class, no extra attributes. This means that the inputs boxes provided for +no CSS class, no extra attributes. This means that the input boxes provided for each widget will be rendered exactly the same:: >>> f = CommentForm(auto_id=False) diff --git a/docs/pagination.txt b/docs/pagination.txt new file mode 100644 index 0000000000..486c92264b --- /dev/null +++ b/docs/pagination.txt @@ -0,0 +1,133 @@ +========== +Pagination +========== + +**New in Django development version** + +Django provides a few classes that help you manage paginated data -- that is, +data that's split across several pages, with "Previous/Next" links. These +classes live in the module ``django/core/paginator.py``. + +Example +======= + +Give ``Paginator`` a list of objects, plus the number of items you'd like to +have on each page, and it gives you methods for accessing the items for each +page:: + + >>> from django.core.paginator import Paginator + >>> objects = ['john', 'paul', 'george', 'ringo'] + >>> p = Paginator(objects, 2) + + >>> p.count + 4 + >>> p.num_pages + 2 + >>> p.page_range + [1, 2] + + >>> page1 = p.page(1) + >>> page1 + <Page 1 of 2> + >>> page1.object_list + ['john', 'paul'] + + >>> page2 = p.page(2) + >>> page2.object_list + ['george', 'ringo'] + >>> page2.has_next() + False + >>> page2.has_previous() + True + >>> page2.has_other_pages() + True + >>> page2.next_page_number() + 3 + >>> page2.previous_page_number() + 1 + >>> page2.start_index() # The 1-based index of the first item on this page + 3 + >>> page2.end_index() # The 1-based index of the last item on this page + 4 + + >>> p.page(0) + Traceback (most recent call last): + ... + InvalidPage + >>> p.page(3) + Traceback (most recent call last): + ... + InvalidPage + +``Paginator`` objects +===================== + +Methods +------- + +``page(number)`` -- Returns a ``Page`` object with the given 1-based index. +Raises ``InvalidPage`` if the given page number doesn't exist. + +Attributes +---------- + +``count`` -- The total number of objects, across all pages. + +``num_pages`` -- The total number of pages. + +``page_range`` -- A 1-based range of page numbers, e.g., ``[1, 2, 3, 4]``. + +``Page`` objects +================ + +Methods +------- + +``has_next()`` -- Returns ``True`` if there's a next page. + +``has_previous()`` -- Returns ``True`` if there's a previous page. + +``has_other_pages()`` -- Returns ``True`` if there's a next *or* previous page. + +``next_page_number()`` -- Returns the next page number. Note that this is +"dumb" and will return the next page number regardless of whether a subsequent +page exists. + +``previous_page_number()`` -- Returns the previous page number. Note that this +is "dumb" and will return the previous page number regardless of whether a +previous page exists. + +``start_index()`` -- Returns the 1-based index of the first object on the page, +relative to all of the objects in the paginator's list. For example, when +paginating a list of 5 objects with 2 objects per page, the second page's +``start_index()`` would return ``3``. + +``end_index()`` -- Returns the 1-based index of the last object on the page, +relative to all of the objects in the paginator's list. For example, when +paginating a list of 5 objects with 2 objects per page, the second page's +``end_index()`` would return ``4``. + +Attributes +---------- + +``object_list`` -- The list of objects on this page. + +``number`` -- The 1-based page number for this page. + +``paginator`` -- The associated ``Paginator`` object. + +``QuerySetPaginator`` objects +============================= + +Use ``QuerySetPaginator`` instead of ``Paginator`` if you're paginating across +a ``QuerySet`` from Django's database API. This is slightly more efficient, and +there are no API differences between the two classes. + +The legacy ``ObjectPaginator`` class +==================================== + +The ``Paginator`` and ``Page`` classes are new in the Django development +version, as of revision 7306. In previous versions, Django provided an +``ObjectPaginator`` class that offered similar functionality but wasn't as +convenient. This class still exists, for backwards compatibility, but Django +now issues a ``DeprecationWarning`` if you try to use it. diff --git a/docs/request_response.txt b/docs/request_response.txt index e50cfc5ea3..0e0f046a2d 100644 --- a/docs/request_response.txt +++ b/docs/request_response.txt @@ -141,6 +141,16 @@ All attributes except ``session`` should be considered read-only. The raw HTTP POST data. This is only useful for advanced processing. Use ``POST`` instead. +``urlconf`` + Not defined by Django itself, but will be read if other code + (e.g., a custom middleware class) sets it; when present, this will + be used as the root URLConf for the current request, overriding + the ``ROOT_URLCONF`` setting. See `How Django processes a + request`_ for details. + +.. _How Django processes a request: ../url_dispatch/#how-django-processes-a-request + + Methods ------- @@ -189,6 +199,23 @@ Methods Returns ``True`` if the request is secure; that is, if it was made with HTTPS. +``is_ajax()`` + **New in Django development version** + + Returns ``True`` if the request was made via an XMLHttpRequest by checking + the ``HTTP_X_REQUESTED_WITH`` header for the string *'XMLHttpRequest'*. The + following major Javascript libraries all send this header: + + * jQuery + * Dojo + * MochiKit + * MooTools + * Prototype + * YUI + + If you write your own XMLHttpRequest call (on the browser side), you will + have to set this header manually to use this method. + QueryDict objects ----------------- diff --git a/docs/sessions.txt b/docs/sessions.txt index 6355524d2e..d8bac5b8d4 100644 --- a/docs/sessions.txt +++ b/docs/sessions.txt @@ -48,10 +48,10 @@ Using file-based sessions To use file-based sessions, set the ``SESSION_ENGINE`` setting to ``"django.contrib.sessions.backends.file"``. -You might also want to set the ``SESSION_FILE_PATH`` setting (which -defaults to ``/tmp``) to control where Django stores session files. Be -sure to check that your Web server has permissions to read and write to -this location. +You might also want to set the ``SESSION_FILE_PATH`` setting (which defaults +to output from ``tempfile.gettempdir()``, most likely ``/tmp``) to control +where Django stores session files. Be sure to check that your Web server has +permissions to read and write to this location. Using cache-based sessions -------------------------- diff --git a/docs/settings.txt b/docs/settings.txt index 77e3c6692f..fb2e04f1ea 100644 --- a/docs/settings.txt +++ b/docs/settings.txt @@ -185,8 +185,11 @@ ADMIN_MEDIA_PREFIX Default: ``'/media/'`` -The URL prefix for admin media -- CSS, JavaScript and images. Make sure to use -a trailing slash. +The URL prefix for admin media -- CSS, JavaScript and images used by +the Django administrative interface. Make sure to use a trailing +slash, and to have this be different from the ``MEDIA_URL`` setting +(since the same URL cannot be mapped onto two different sets of +files). ADMINS ------ @@ -754,7 +757,9 @@ ROOT_URLCONF Default: Not defined A string representing the full Python import path to your root URLconf. For example: -``"mydjangoapps.urls"``. See `How Django processes a request`_. +``"mydjangoapps.urls"``. Can be overridden on a per-request basis by +setting the attribute ``urlconf`` on the incoming ``HttpRequest`` +object. See `How Django processes a request`_ for details. .. _How Django processes a request: ../url_dispatch/#how-django-processes-a-request diff --git a/docs/syndication_feeds.txt b/docs/syndication_feeds.txt index ebd6af26f8..f86acfe54d 100644 --- a/docs/syndication_feeds.txt +++ b/docs/syndication_feeds.txt @@ -245,6 +245,13 @@ request to the URL ``/rss/beats/0613/``: subclass of ``ObjectDoesNotExist``. Raising ``ObjectDoesNotExist`` in ``get_object()`` tells Django to produce a 404 error for that request. + **New in Django development version:** The ``get_object()`` method also + has a chance to handle the ``/rss/beats/`` url. In this case, ``bits`` + will be an empty list. In our example, ``len(bits) != 1`` and an + ``ObjectDoesNotExist`` exception will be raised, so ``/rss/beats/`` will + generate a 404 page. But you can handle this case however you like. For + example you could generate a combined feed for all beats. + * To generate the feed's ``<title>``, ``<link>`` and ``<description>``, Django uses the ``title()``, ``link()`` and ``description()`` methods. In the previous example, they were simple string class attributes, but this diff --git a/docs/templates.txt b/docs/templates.txt index 3360b04178..ea9f3fb6b2 100644 --- a/docs/templates.txt +++ b/docs/templates.txt @@ -429,8 +429,9 @@ all block tags. For example:: # base.html {% autoescape off %} - <h1>{% block title %}</h1> + <h1>{% block title %}{% endblock %}</h1> {% block content %} + {% endblock %} {% endautoescape %} @@ -438,10 +439,11 @@ all block tags. For example:: {% extends "base.html" %} {% block title %}This & that{% endblock %} - {% block content %}<b>Hello!</b>{% endblock %} + {% block content %}{{ greeting }}{% endblock %} Because auto-escaping is turned off in the base template, it will also be -turned off in the child template, resulting in the following rendered HTML:: +turned off in the child template, resulting in the following rendered +HTML when the ``greeting`` variable contains the string ``<b>Hello!</b>``:: <h1>This & that</h1> <b>Hello!</b> diff --git a/docs/tutorial04.txt b/docs/tutorial04.txt index bd16fa2924..473fba1ef8 100644 --- a/docs/tutorial04.txt +++ b/docs/tutorial04.txt @@ -37,6 +37,12 @@ A quick rundown: form will alter data server-side. Whenever you create a form that alters data server-side, use ``method="post"``. This tip isn't specific to Django; it's just good Web development practice. + + * ``forloop.counter`` indicates how many times the ``for`` tag has + gone through its loop; for more information, see `the + documentation for the "for" tag`_. + +.. _the documentation for the "for" tag: ../templates/#for Now, let's create a Django view that handles the submitted data and does something with it. Remember, in `Tutorial 3`_, we created a URLconf for the diff --git a/docs/url_dispatch.txt b/docs/url_dispatch.txt index 789399de8d..053ee954a3 100644 --- a/docs/url_dispatch.txt +++ b/docs/url_dispatch.txt @@ -32,9 +32,11 @@ How Django processes a request When a user requests a page from your Django-powered site, this is the algorithm the system follows to determine which Python code to execute: - 1. Django looks at the ``ROOT_URLCONF`` setting in your `settings file`_. - This should be a string representing the full Python import path to your - URLconf. For example: ``"mydjangoapps.urls"``. + 1. Django determines the root URLConf module to use; ordinarily + this is the value of the ``ROOT_URLCONF`` setting in your + `settings file`_, but if the incoming ``HttpRequest`` object + has an attribute called ``urlconf``, its value will be used in + place of the ``ROOT_URLCONF`` setting. 2. Django loads that Python module and looks for the variable ``urlpatterns``. This should be a Python list, in the format returned by the function ``django.conf.urls.defaults.patterns()``. diff --git a/tests/modeltests/custom_pk/models.py b/tests/modeltests/custom_pk/models.py index 381d81b987..e3edf3187e 100644 --- a/tests/modeltests/custom_pk/models.py +++ b/tests/modeltests/custom_pk/models.py @@ -71,8 +71,9 @@ u'ABC123' >>> fran.save() >>> Employee.objects.filter(last_name__exact='Jones') [<Employee: Dan Jones>, <Employee: Fran Jones>] ->>> Employee.objects.in_bulk(['ABC123', 'XYZ456']) -{u'XYZ456': <Employee: Fran Jones>, u'ABC123': <Employee: Dan Jones>} +>>> emps = Employee.objects.in_bulk(['ABC123', 'XYZ456']) +>>> emps['ABC123'] +<Employee: Dan Jones> >>> b = Business(name='Sears') >>> b.save() diff --git a/tests/modeltests/lookup/models.py b/tests/modeltests/lookup/models.py index f3cce70e12..5cff48ac51 100644 --- a/tests/modeltests/lookup/models.py +++ b/tests/modeltests/lookup/models.py @@ -76,8 +76,11 @@ Article 4 # in_bulk() takes a list of IDs and returns a dictionary mapping IDs # to objects. ->>> Article.objects.in_bulk([1, 2]) -{1: <Article: Article 1>, 2: <Article: Article 2>} +>>> arts = Article.objects.in_bulk([1, 2]) +>>> arts[1] +<Article: Article 1> +>>> arts[2] +<Article: Article 2> >>> Article.objects.in_bulk([3]) {3: <Article: Article 3>} >>> Article.objects.in_bulk([1000]) diff --git a/tests/modeltests/manipulators/models.py b/tests/modeltests/manipulators/models.py index c9b9848235..3e52e33bbb 100644 --- a/tests/modeltests/manipulators/models.py +++ b/tests/modeltests/manipulators/models.py @@ -41,25 +41,33 @@ __test__ = {'API_TESTS':u""" True # Attempt to add a Musician without a first_name. ->>> man.get_validation_errors(MultiValueDict({'last_name': ['Blakey']})) -{'first_name': [u'This field is required.']} +>>> man.get_validation_errors(MultiValueDict({'last_name': ['Blakey']}))['first_name'] +[u'This field is required.'] # Attempt to add a Musician without a first_name and last_name. ->>> man.get_validation_errors(MultiValueDict({})) -{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.']} +>>> errors = man.get_validation_errors(MultiValueDict({})) +>>> errors['first_name'] +[u'This field is required.'] +>>> errors['last_name'] +[u'This field is required.'] # Attempt to create an Album without a name or musician. >>> man = Album.AddManipulator() ->>> man.get_validation_errors(MultiValueDict({})) -{'musician': [u'This field is required.'], 'name': [u'This field is required.']} +>>> errors = man.get_validation_errors(MultiValueDict({})) +>>> errors['musician'] +[u'This field is required.'] +>>> errors['name'] +[u'This field is required.'] # Attempt to create an Album with an invalid musician. ->>> man.get_validation_errors(MultiValueDict({'name': ['Sallies Fforth'], 'musician': ['foo']})) -{'musician': [u"Select a valid choice; 'foo' is not in [u'', u'1']."]} +>>> errors = man.get_validation_errors(MultiValueDict({'name': ['Sallies Fforth'], 'musician': ['foo']})) +>>> errors['musician'] +[u"Select a valid choice; 'foo' is not in [u'', u'1']."] # Attempt to create an Album with an invalid release_date. ->>> man.get_validation_errors(MultiValueDict({'name': ['Sallies Fforth'], 'musician': ['1'], 'release_date': 'today'})) -{'release_date': [u'Enter a valid date in YYYY-MM-DD format.']} +>>> errors = man.get_validation_errors(MultiValueDict({'name': ['Sallies Fforth'], 'musician': ['1'], 'release_date': 'today'})) +>>> errors['release_date'] +[u'Enter a valid date in YYYY-MM-DD format.'] # Create an Album without a release_date (because it's optional). >>> data = MultiValueDict({'name': ['Ella and Basie'], 'musician': ['1']}) diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py index c480899f84..470312f5ca 100644 --- a/tests/modeltests/model_forms/models.py +++ b/tests/modeltests/model_forms/models.py @@ -234,8 +234,12 @@ We can also subclass the Meta inner class to change the fields list. >>> f = CategoryForm({'name': 'Entertainment', 'slug': 'entertainment', 'url': 'entertainment'}) >>> f.is_valid() True ->>> f.cleaned_data -{'url': u'entertainment', 'name': u'Entertainment', 'slug': u'entertainment'} +>>> f.cleaned_data['url'] +u'entertainment' +>>> f.cleaned_data['name'] +u'Entertainment' +>>> f.cleaned_data['slug'] +u'entertainment' >>> obj = f.save() >>> obj <Category: Entertainment> @@ -245,8 +249,12 @@ True >>> f = CategoryForm({'name': "It's a test", 'slug': 'its-test', 'url': 'test'}) >>> f.is_valid() True ->>> f.cleaned_data -{'url': u'test', 'name': u"It's a test", 'slug': u'its-test'} +>>> f.cleaned_data['url'] +u'test' +>>> f.cleaned_data['name'] +u"It's a test" +>>> f.cleaned_data['slug'] +u'its-test' >>> obj = f.save() >>> obj <Category: It's a test> @@ -259,8 +267,12 @@ save() on the resulting model instance. >>> f = CategoryForm({'name': 'Third test', 'slug': 'third-test', 'url': 'third'}) >>> f.is_valid() True ->>> f.cleaned_data -{'url': u'third', 'name': u'Third test', 'slug': u'third-test'} +>>> f.cleaned_data['url'] +u'third' +>>> f.cleaned_data['name'] +u'Third test' +>>> f.cleaned_data['slug'] +u'third-test' >>> obj = f.save(commit=False) >>> obj <Category: Third test> @@ -272,8 +284,10 @@ True If you call save() with invalid data, you'll get a ValueError. >>> f = CategoryForm({'name': '', 'slug': '', 'url': 'foo'}) ->>> f.errors -{'name': [u'This field is required.'], 'slug': [u'This field is required.']} +>>> f.errors['name'] +[u'This field is required.'] +>>> f.errors['slug'] +[u'This field is required.'] >>> f.cleaned_data Traceback (most recent call last): ... @@ -645,6 +659,19 @@ Traceback (most recent call last): ... ValidationError: [u'Select a valid choice. That choice is not one of the available choices.'] +# check that we can safely iterate choices repeatedly +>>> gen_one = list(f.choices) +>>> gen_two = f.choices +>>> gen_one[2] +(2L, u"It's a test") +>>> list(gen_two) +[(u'', u'---------'), (1L, u'Entertainment'), (2L, u"It's a test"), (3L, u'Third')] + +# check that we can override the label_from_instance method to print custom labels (#4620) +>>> f.queryset = Category.objects.all() +>>> f.label_from_instance = lambda obj: "category " + str(obj) +>>> list(f.choices) +[(u'', u'---------'), (1L, 'category Entertainment'), (2L, "category It's a test"), (3L, 'category Third'), (4L, 'category Fourth')] # ModelMultipleChoiceField #################################################### @@ -730,6 +757,10 @@ Traceback (most recent call last): ... ValidationError: [u'Select a valid choice. 4 is not one of the available choices.'] +>>> f.queryset = Category.objects.all() +>>> f.label_from_instance = lambda obj: "multicategory " + str(obj) +>>> list(f.choices) +[(1L, 'multicategory Entertainment'), (2L, "multicategory It's a test"), (3L, 'multicategory Third'), (4L, 'multicategory Fourth')] # PhoneNumberField ############################################################ @@ -739,8 +770,10 @@ ValidationError: [u'Select a valid choice. 4 is not one of the available choices >>> f = PhoneNumberForm({'phone': '(312) 555-1212', 'description': 'Assistance'}) >>> f.is_valid() True ->>> f.cleaned_data -{'phone': u'312-555-1212', 'description': u'Assistance'} +>>> f.cleaned_data['phone'] +u'312-555-1212' +>>> f.cleaned_data['description'] +u'Assistance' # FileField ################################################################### @@ -766,7 +799,7 @@ True <class 'django.newforms.fields.UploadedFile'> >>> instance = f.save() >>> instance.file -u'.../test1.txt' +u'...test1.txt' # Edit an instance that already has the file defined in the model. This will not # save the file again, but leave it exactly as it is. @@ -775,10 +808,10 @@ u'.../test1.txt' >>> f.is_valid() True >>> f.cleaned_data['file'] -u'.../test1.txt' +u'...test1.txt' >>> instance = f.save() >>> instance.file -u'.../test1.txt' +u'...test1.txt' # Delete the current file since this is not done by Django. @@ -791,7 +824,7 @@ u'.../test1.txt' True >>> instance = f.save() >>> instance.file -u'.../test2.txt' +u'...test2.txt' >>> instance.delete() @@ -810,7 +843,7 @@ True True >>> instance = f.save() >>> instance.file -u'.../test3.txt' +u'...test3.txt' >>> instance.delete() # ImageField ################################################################### @@ -832,7 +865,7 @@ True <class 'django.newforms.fields.UploadedFile'> >>> instance = f.save() >>> instance.image -u'.../test.png' +u'...test.png' # Edit an instance that already has the image defined in the model. This will not # save the image again, but leave it exactly as it is. @@ -841,10 +874,10 @@ u'.../test.png' >>> f.is_valid() True >>> f.cleaned_data['image'] -u'.../test.png' +u'...test.png' >>> instance = f.save() >>> instance.image -u'.../test.png' +u'...test.png' # Delete the current image since this is not done by Django. @@ -857,7 +890,7 @@ u'.../test.png' True >>> instance = f.save() >>> instance.image -u'.../test2.png' +u'...test2.png' >>> instance.delete() @@ -876,7 +909,7 @@ True True >>> instance = f.save() >>> instance.image -u'.../test3.png' +u'...test3.png' >>> instance.delete() """} diff --git a/tests/modeltests/pagination/models.py b/tests/modeltests/pagination/models.py index f44c67a139..277c5961e3 100644 --- a/tests/modeltests/pagination/models.py +++ b/tests/modeltests/pagination/models.py @@ -4,6 +4,11 @@ Django provides a framework for paginating a list of objects in a few lines of code. This is often useful for dividing search results or long lists of objects into easily readable pages. + +In Django 0.96 and earlier, a single ObjectPaginator class implemented this +functionality. In the Django development version, the behavior is split across +two classes -- Paginator and Page -- that are more easier to use. The legacy +ObjectPaginator class is deprecated. """ from django.db import models @@ -16,70 +21,210 @@ class Article(models.Model): return self.headline __test__ = {'API_TESTS':""" -# prepare a list of objects for pagination +# Prepare a list of objects for pagination. >>> from datetime import datetime >>> for x in range(1, 10): ... a = Article(headline='Article %s' % x, pub_date=datetime(2005, 7, 29)) ... a.save() -# create a basic paginator, 5 articles per page +#################################### +# New/current API (Paginator/Page) # +#################################### + +>>> from django.core.paginator import Paginator, InvalidPage +>>> paginator = Paginator(Article.objects.all(), 5) +>>> paginator.count +9 +>>> paginator.num_pages +2 +>>> paginator.page_range +[1, 2] + +# Get the first page. +>>> p = paginator.page(1) +>>> p +<Page 1 of 2> +>>> p.object_list +[<Article: Article 1>, <Article: Article 2>, <Article: Article 3>, <Article: Article 4>, <Article: Article 5>] +>>> p.has_next() +True +>>> p.has_previous() +False +>>> p.has_other_pages() +True +>>> p.next_page_number() +2 +>>> p.previous_page_number() +0 +>>> p.start_index() +1 +>>> p.end_index() +5 + +# Get the second page. +>>> p = paginator.page(2) +>>> p +<Page 2 of 2> +>>> p.object_list +[<Article: Article 6>, <Article: Article 7>, <Article: Article 8>, <Article: Article 9>] +>>> p.has_next() +False +>>> p.has_previous() +True +>>> p.has_other_pages() +True +>>> p.next_page_number() +3 +>>> p.previous_page_number() +1 +>>> p.start_index() +6 +>>> p.end_index() +9 + +# Invalid pages raise InvalidPage. +>>> paginator.page(0) +Traceback (most recent call last): +... +InvalidPage: ... +>>> paginator.page(3) +Traceback (most recent call last): +... +InvalidPage: ... + +# Empty paginators with allow_empty_first_page=True. +>>> paginator = Paginator(Article.objects.filter(id=0), 5, allow_empty_first_page=True) +>>> paginator.count +0 +>>> paginator.num_pages +1 +>>> paginator.page_range +[1] + +# Empty paginators with allow_empty_first_page=False. +>>> paginator = Paginator(Article.objects.filter(id=0), 5, allow_empty_first_page=False) +>>> paginator.count +0 +>>> paginator.num_pages +0 +>>> paginator.page_range +[] + +# Paginators work with regular lists/tuples, too -- not just with QuerySets. +>>> paginator = Paginator([1, 2, 3, 4, 5, 6, 7, 8, 9], 5) +>>> paginator.count +9 +>>> paginator.num_pages +2 +>>> paginator.page_range +[1, 2] + +# Get the first page. +>>> p = paginator.page(1) +>>> p +<Page 1 of 2> +>>> p.object_list +[1, 2, 3, 4, 5] +>>> p.has_next() +True +>>> p.has_previous() +False +>>> p.has_other_pages() +True +>>> p.next_page_number() +2 +>>> p.previous_page_number() +0 +>>> p.start_index() +1 +>>> p.end_index() +5 + +################################ +# Legacy API (ObjectPaginator) # +################################ + +# Don't print out the deprecation warnings during testing. +>>> from warnings import filterwarnings +>>> filterwarnings("ignore") + >>> from django.core.paginator import ObjectPaginator, InvalidPage >>> paginator = ObjectPaginator(Article.objects.all(), 5) - -# the paginator knows how many hits and pages it contains >>> paginator.hits 9 - >>> paginator.pages 2 +>>> paginator.page_range +[1, 2] -# get the first page (zero-based) +# Get the first page. >>> paginator.get_page(0) [<Article: Article 1>, <Article: Article 2>, <Article: Article 3>, <Article: Article 4>, <Article: Article 5>] - -# get the second page ->>> paginator.get_page(1) -[<Article: Article 6>, <Article: Article 7>, <Article: Article 8>, <Article: Article 9>] - -# does the first page have a next or previous page? >>> paginator.has_next_page(0) True - >>> paginator.has_previous_page(0) False +>>> paginator.first_on_page(0) +1 +>>> paginator.last_on_page(0) +5 -# check the second page +# Get the second page. +>>> paginator.get_page(1) +[<Article: Article 6>, <Article: Article 7>, <Article: Article 8>, <Article: Article 9>] >>> paginator.has_next_page(1) False - >>> paginator.has_previous_page(1) True - ->>> paginator.first_on_page(0) -1 >>> paginator.first_on_page(1) 6 ->>> paginator.last_on_page(0) -5 >>> paginator.last_on_page(1) 9 +# Invalid pages raise InvalidPage. +>>> paginator.get_page(-1) +Traceback (most recent call last): +... +InvalidPage: ... +>>> paginator.get_page(2) +Traceback (most recent call last): +... +InvalidPage: ... + +# Empty paginators with allow_empty_first_page=True. +>>> paginator = ObjectPaginator(Article.objects.filter(id=0), 5) +>>> paginator.count +0 +>>> paginator.num_pages +1 +>>> paginator.page_range +[1] + +################## +# Orphan support # +################## + # 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: +# With orphans set to 3 and 10 items per page, we should get all 12 items on a single page. +>>> paginator = Paginator(Article.objects.all(), 10, orphans=3) +>>> paginator.num_pages +1 + +# With orphans only set to 1, we should get two pages. +>>> paginator = ObjectPaginator(Article.objects.all(), 10, orphans=1) +>>> paginator.num_pages +2 + +# LEGACY: 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: +# LEGACY: With orphans only set to 1, we should get two pages. >>> paginator = ObjectPaginator(Article.objects.all(), 10, orphans=1) >>> paginator.pages 2 - -# The paginator can provide a list of all available pages. ->>> paginator = ObjectPaginator(Article.objects.all(), 10) ->>> paginator.page_range -[1, 2] """} diff --git a/tests/modeltests/validation/models.py b/tests/modeltests/validation/models.py index aacd041678..63f9f7a361 100644 --- a/tests/modeltests/validation/models.py +++ b/tests/modeltests/validation/models.py @@ -41,8 +41,8 @@ __test__ = {'API_TESTS':""" 23 >>> p = Person(**dict(valid_params, id='foo')) ->>> p.validate() -{'id': [u'This value must be an integer.']} +>>> p.validate()['id'] +[u'This value must be an integer.'] >>> p = Person(**dict(valid_params, id=None)) >>> p.validate() @@ -75,8 +75,8 @@ True False >>> p = Person(**dict(valid_params, is_child='foo')) ->>> p.validate() -{'is_child': [u'This value must be either True or False.']} +>>> p.validate()['is_child'] +[u'This value must be either True or False.'] >>> p = Person(**dict(valid_params, name=u'Jose')) >>> p.validate() @@ -115,8 +115,8 @@ datetime.date(2000, 5, 3) datetime.date(2000, 5, 3) >>> p = Person(**dict(valid_params, birthdate='foo')) ->>> p.validate() -{'birthdate': [u'Enter a valid date in YYYY-MM-DD format.']} +>>> p.validate()['birthdate'] +[u'Enter a valid date in YYYY-MM-DD format.'] >>> p = Person(**dict(valid_params, favorite_moment=datetime.datetime(2002, 4, 3, 13, 23))) >>> p.validate() @@ -143,11 +143,15 @@ datetime.datetime(2002, 4, 3, 0, 0) u'john@example.com' >>> p = Person(**dict(valid_params, email=22)) ->>> p.validate() -{'email': [u'Enter a valid e-mail address.']} +>>> p.validate()['email'] +[u'Enter a valid e-mail address.'] # Make sure that Date and DateTime return validation errors and don't raise Python errors. ->>> Person(name='John Doe', is_child=True, email='abc@def.com').validate() -{'favorite_moment': [u'This field is required.'], 'birthdate': [u'This field is required.']} +>>> p = Person(name='John Doe', is_child=True, email='abc@def.com') +>>> errors = p.validate() +>>> errors['favorite_moment'] +[u'This field is required.'] +>>> errors['birthdate'] +[u'This field is required.'] """} diff --git a/tests/regressiontests/forms/extra.py b/tests/regressiontests/forms/extra.py index 9dff4071f1..a8b369715d 100644 --- a/tests/regressiontests/forms/extra.py +++ b/tests/regressiontests/forms/extra.py @@ -22,7 +22,7 @@ classes that demonstrate some of the library's abilities. >>> from django.newforms.extras import SelectDateWidget >>> w = SelectDateWidget(years=('2007','2008','2009','2010','2011','2012','2013','2014','2015','2016')) >>> print w.render('mydate', '') -<select name="mydate_month"> +<select name="mydate_month" id="id_mydate_month"> <option value="1">January</option> <option value="2">February</option> <option value="3">March</option> @@ -36,7 +36,7 @@ classes that demonstrate some of the library's abilities. <option value="11">November</option> <option value="12">December</option> </select> -<select name="mydate_day"> +<select name="mydate_day" id="id_mydate_day"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> @@ -69,7 +69,7 @@ classes that demonstrate some of the library's abilities. <option value="30">30</option> <option value="31">31</option> </select> -<select name="mydate_year"> +<select name="mydate_year" id="id_mydate_year"> <option value="2007">2007</option> <option value="2008">2008</option> <option value="2009">2009</option> @@ -84,7 +84,7 @@ classes that demonstrate some of the library's abilities. >>> w.render('mydate', None) == w.render('mydate', '') True >>> print w.render('mydate', '2010-04-15') -<select name="mydate_month"> +<select name="mydate_month" id="id_mydate_month"> <option value="1">January</option> <option value="2">February</option> <option value="3">March</option> @@ -98,7 +98,7 @@ True <option value="11">November</option> <option value="12">December</option> </select> -<select name="mydate_day"> +<select name="mydate_day" id="id_mydate_day"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> @@ -131,7 +131,74 @@ True <option value="30">30</option> <option value="31">31</option> </select> -<select name="mydate_year"> +<select name="mydate_year" id="id_mydate_year"> +<option value="2007">2007</option> +<option value="2008">2008</option> +<option value="2009">2009</option> +<option value="2010" selected="selected">2010</option> +<option value="2011">2011</option> +<option value="2012">2012</option> +<option value="2013">2013</option> +<option value="2014">2014</option> +<option value="2015">2015</option> +<option value="2016">2016</option> +</select> + +Accepts a datetime or a string: + +>>> w.render('mydate', datetime.date(2010, 4, 15)) == w.render('mydate', '2010-04-15') +True + +Invalid dates still render the failed date: +>>> print w.render('mydate', '2010-02-31') +<select name="mydate_month" id="id_mydate_month"> +<option value="1">January</option> +<option value="2" selected="selected">February</option> +<option value="3">March</option> +<option value="4">April</option> +<option value="5">May</option> +<option value="6">June</option> +<option value="7">July</option> +<option value="8">August</option> +<option value="9">September</option> +<option value="10">October</option> +<option value="11">November</option> +<option value="12">December</option> +</select> +<select name="mydate_day" id="id_mydate_day"> +<option value="1">1</option> +<option value="2">2</option> +<option value="3">3</option> +<option value="4">4</option> +<option value="5">5</option> +<option value="6">6</option> +<option value="7">7</option> +<option value="8">8</option> +<option value="9">9</option> +<option value="10">10</option> +<option value="11">11</option> +<option value="12">12</option> +<option value="13">13</option> +<option value="14">14</option> +<option value="15">15</option> +<option value="16">16</option> +<option value="17">17</option> +<option value="18">18</option> +<option value="19">19</option> +<option value="20">20</option> +<option value="21">21</option> +<option value="22">22</option> +<option value="23">23</option> +<option value="24">24</option> +<option value="25">25</option> +<option value="26">26</option> +<option value="27">27</option> +<option value="28">28</option> +<option value="29">29</option> +<option value="30">30</option> +<option value="31" selected="selected">31</option> +</select> +<select name="mydate_year" id="id_mydate_year"> <option value="2007">2007</option> <option value="2008">2008</option> <option value="2009">2009</option> @@ -252,8 +319,8 @@ ValidationError: [u'This field is required.'] </select> <input type="text" name="field1_2_0" value="2007-04-25" id="id_field1_2_0" /><input type="text" name="field1_2_1" value="06:24:00" id="id_field1_2_1" /></td></tr> ->>> f.cleaned_data -{'field1': u'some text,JP,2007-04-25 06:24:00'} +>>> f.cleaned_data['field1'] +u'some text,JP,2007-04-25 06:24:00' # IPAddressField ################################################################## diff --git a/tests/regressiontests/forms/fields.py b/tests/regressiontests/forms/fields.py index 9216210e09..9421d8c005 100644 --- a/tests/regressiontests/forms/fields.py +++ b/tests/regressiontests/forms/fields.py @@ -1133,6 +1133,33 @@ u'' >>> f.clean(None) u'' +# FilePathField ############################################################### + +>>> import os +>>> from django import newforms as forms +>>> path = forms.__file__ +>>> path = os.path.dirname(path) + '/' +>>> path +'.../django/newforms/' +>>> f = forms.FilePathField(path=path) +>>> f.choices.sort() +>>> f.choices +[('.../django/newforms/__init__.py', '__init__.py'), ('.../django/newforms/__init__.pyc', '__init__.pyc'), ('.../django/newforms/fields.py', 'fields.py'), ('.../django/newforms/fields.pyc', 'fields.pyc'), ('.../django/newforms/forms.py', 'forms.py'), ('.../django/newforms/forms.pyc', 'forms.pyc'), ('.../django/newforms/models.py', 'models.py'), ('.../django/newforms/models.pyc', 'models.pyc'), ('.../django/newforms/util.py', 'util.py'), ('.../django/newforms/util.pyc', 'util.pyc'), ('.../django/newforms/widgets.py', 'widgets.py'), ('.../django/newforms/widgets.pyc', 'widgets.pyc')] +>>> f.clean('fields.py') +Traceback (most recent call last): +... +ValidationError: [u'Select a valid choice. That choice is not one of the available choices.'] +>>> f.clean(path + 'fields.py') +u'.../django/newforms/fields.py' +>>> f = forms.FilePathField(path=path, match='^.*?\.py$') +>>> f.choices.sort() +>>> f.choices +[('.../django/newforms/__init__.py', '__init__.py'), ('.../django/newforms/fields.py', 'fields.py'), ('.../django/newforms/forms.py', 'forms.py'), ('.../django/newforms/models.py', 'models.py'), ('.../django/newforms/util.py', 'util.py'), ('.../django/newforms/widgets.py', 'widgets.py')] +>>> f = forms.FilePathField(path=path, recursive=True, match='^.*?\.py$') +>>> f.choices.sort() +>>> f.choices +[('.../django/newforms/__init__.py', '__init__.py'), ('.../django/newforms/extras/__init__.py', 'extras/__init__.py'), ('.../django/newforms/extras/widgets.py', 'extras/widgets.py'), ('.../django/newforms/fields.py', 'fields.py'), ('.../django/newforms/forms.py', 'forms.py'), ('.../django/newforms/models.py', 'models.py'), ('.../django/newforms/util.py', 'util.py'), ('.../django/newforms/widgets.py', 'widgets.py')] + # SplitDateTimeField ########################################################## >>> f = SplitDateTimeField() diff --git a/tests/regressiontests/forms/forms.py b/tests/regressiontests/forms/forms.py index 7c0cf8abf3..d7fa1780f5 100644 --- a/tests/regressiontests/forms/forms.py +++ b/tests/regressiontests/forms/forms.py @@ -36,8 +36,8 @@ True u'' >>> p.errors.as_text() u'' ->>> p.cleaned_data -{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)} +>>> p.cleaned_data["first_name"], p.cleaned_data["last_name"], p.cleaned_data["birthday"] +(u'John', u'Lennon', datetime.date(1940, 10, 9)) >>> print p['first_name'] <input type="text" name="first_name" value="John" id="id_first_name" /> >>> print p['last_name'] @@ -68,8 +68,12 @@ Empty dictionaries are valid, too. >>> p = Person({}) >>> p.is_bound True ->>> p.errors -{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']} +>>> p.errors['first_name'] +[u'This field is required.'] +>>> p.errors['last_name'] +[u'This field is required.'] +>>> p.errors['birthday'] +[u'This field is required.'] >>> p.is_valid() False >>> p.cleaned_data @@ -137,8 +141,10 @@ u'<li><label for="id_first_name">First name:</label> <input type="text" name="fi u'<p><label for="id_first_name">First name:</label> <input type="text" name="first_name" value="John" id="id_first_name" /></p>\n<p><label for="id_last_name">Last name:</label> <input type="text" name="last_name" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" id="id_last_name" /></p>\n<p><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" value="1940-10-9" id="id_birthday" /></p>' >>> p = Person({'last_name': u'Lennon'}) ->>> p.errors -{'first_name': [u'This field is required.'], 'birthday': [u'This field is required.']} +>>> p.errors['first_name'] +[u'This field is required.'] +>>> p.errors['birthday'] +[u'This field is required.'] >>> p.is_valid() False >>> p.errors.as_ul() @@ -175,8 +181,13 @@ but cleaned_data contains only the form's fields. >>> p = Person(data) >>> p.is_valid() True ->>> p.cleaned_data -{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)} +>>> p.cleaned_data['first_name'] +u'John' +>>> p.cleaned_data['last_name'] +u'Lennon' +>>> p.cleaned_data['birthday'] +datetime.date(1940, 10, 9) + cleaned_data will include a key and value for *all* fields defined in the Form, even if the Form's data didn't include a value for fields that are not @@ -191,8 +202,12 @@ empty string. >>> f = OptionalPersonForm(data) >>> f.is_valid() True ->>> f.cleaned_data -{'nick_name': u'', 'first_name': u'John', 'last_name': u'Lennon'} +>>> f.cleaned_data['nick_name'] +u'' +>>> f.cleaned_data['first_name'] +u'John' +>>> f.cleaned_data['last_name'] +u'Lennon' For DateFields, it's set to None. >>> class OptionalPersonForm(Form): @@ -203,8 +218,12 @@ For DateFields, it's set to None. >>> f = OptionalPersonForm(data) >>> f.is_valid() True ->>> f.cleaned_data -{'birth_date': None, 'first_name': u'John', 'last_name': u'Lennon'} +>>> print f.cleaned_data['birth_date'] +None +>>> f.cleaned_data['first_name'] +u'John' +>>> f.cleaned_data['last_name'] +u'Lennon' "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 @@ -549,18 +568,22 @@ The MultipleHiddenInput widget renders multiple values as hidden fields. When using CheckboxSelectMultiple, the framework expects a list of input and returns a list of input. >>> f = SongForm({'name': 'Yesterday'}, auto_id=False) ->>> f.errors -{'composers': [u'This field is required.']} +>>> f.errors['composers'] +[u'This field is required.'] >>> f = SongForm({'name': 'Yesterday', 'composers': ['J']}, auto_id=False) >>> f.errors {} ->>> f.cleaned_data -{'composers': [u'J'], 'name': u'Yesterday'} +>>> f.cleaned_data['composers'] +[u'J'] +>>> f.cleaned_data['name'] +u'Yesterday' >>> f = SongForm({'name': 'Yesterday', 'composers': ['J', 'P']}, auto_id=False) >>> f.errors {} ->>> f.cleaned_data -{'composers': [u'J', u'P'], 'name': u'Yesterday'} +>>> f.cleaned_data['composers'] +[u'J', u'P'] +>>> f.cleaned_data['name'] +u'Yesterday' Validation errors are HTML-escaped when output as HTML. >>> class EscapingForm(Form): @@ -598,16 +621,24 @@ including the current field (e.g., the field XXX if you're in clean_XXX()). >>> f.errors {} >>> f = UserRegistration({}, auto_id=False) ->>> f.errors -{'username': [u'This field is required.'], 'password1': [u'This field is required.'], 'password2': [u'This field is required.']} +>>> f.errors['username'] +[u'This field is required.'] +>>> f.errors['password1'] +[u'This field is required.'] +>>> f.errors['password2'] +[u'This field is required.'] >>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'bar'}, auto_id=False) ->>> f.errors -{'password2': [u'Please make sure your passwords match.']} +>>> f.errors['password2'] +[u'Please make sure your passwords match.'] >>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False) >>> f.errors {} ->>> f.cleaned_data -{'username': u'adrian', 'password1': u'foo', 'password2': u'foo'} +>>> f.cleaned_data['username'] +u'adrian' +>>> f.cleaned_data['password1'] +u'foo' +>>> f.cleaned_data['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 @@ -632,11 +663,15 @@ Form.clean() is required to return a dictionary of all clean data. <tr><th>Username:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="username" maxlength="10" /></td></tr> <tr><th>Password1:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="password" name="password1" /></td></tr> <tr><th>Password2:</th><td><ul class="errorlist"><li>This field is required.</li></ul><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.errors['username'] +[u'This field is required.'] +>>> f.errors['password1'] +[u'This field is required.'] +>>> f.errors['password2'] +[u'This field is required.'] >>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'bar'}, auto_id=False) ->>> f.errors -{'__all__': [u'Please make sure your passwords match.']} +>>> f.errors['__all__'] +[u'Please make sure your passwords match.'] >>> print f.as_table() <tr><td colspan="2"><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></td></tr> <tr><th>Username:</th><td><input type="text" name="username" value="adrian" maxlength="10" /></td></tr> @@ -650,8 +685,12 @@ Form.clean() is required to return a dictionary of all clean data. >>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False) >>> f.errors {} ->>> f.cleaned_data -{'username': u'adrian', 'password1': u'foo', 'password2': u'foo'} +>>> f.cleaned_data['username'] +u'adrian' +>>> f.cleaned_data['password1'] +u'foo' +>>> f.cleaned_data['password2'] +u'foo' # Dynamic construction ######################################################## @@ -1024,8 +1063,8 @@ An 'initial' value is *not* used as a fallback if data is not provided. In this example, we don't provide a value for 'username', and the form raises a validation error rather than using the initial value for 'username'. >>> p = UserRegistration({'password': 'secret'}) ->>> p.errors -{'username': [u'This field is required.']} +>>> p.errors['username'] +[u'This field is required.'] >>> p.is_valid() False @@ -1069,8 +1108,8 @@ A dynamic 'initial' value is *not* used as a fallback if data is not provided. In this example, we don't provide a value for 'username', and the form raises a validation error rather than using the initial value for 'username'. >>> p = UserRegistration({'password': 'secret'}, initial={'username': 'django'}) ->>> p.errors -{'username': [u'This field is required.']} +>>> p.errors['username'] +[u'This field is required.'] >>> p.is_valid() False @@ -1123,8 +1162,8 @@ A callable 'initial' value is *not* used as a fallback if data is not provided. In this example, we don't provide a value for 'username', and the form raises a validation error rather than using the initial value for 'username'. >>> p = UserRegistration({'password': 'secret'}, initial={'username': initial_django}) ->>> p.errors -{'username': [u'This field is required.']} +>>> p.errors['username'] +[u'This field is required.'] >>> p.is_valid() False @@ -1258,8 +1297,12 @@ actual field name. {} >>> p.is_valid() True ->>> p.cleaned_data -{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)} +>>> p.cleaned_data['first_name'] +u'John' +>>> p.cleaned_data['last_name'] +u'Lennon' +>>> p.cleaned_data['birthday'] +datetime.date(1940, 10, 9) Let's try submitting some bad data to make sure form.errors and field.errors work as expected. @@ -1269,8 +1312,12 @@ work as expected. ... 'person1-birthday': u'' ... } >>> p = Person(data, prefix='person1') ->>> p.errors -{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']} +>>> p.errors['first_name'] +[u'This field is required.'] +>>> p.errors['last_name'] +[u'This field is required.'] +>>> p.errors['birthday'] +[u'This field is required.'] >>> p['first_name'].errors [u'This field is required.'] >>> p['person1-first_name'].errors @@ -1286,8 +1333,12 @@ the form doesn't "see" the fields. ... 'birthday': u'1940-10-9' ... } >>> p = Person(data, prefix='person1') ->>> p.errors -{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']} +>>> p.errors['first_name'] +[u'This field is required.'] +>>> p.errors['last_name'] +[u'This field is required.'] +>>> p.errors['birthday'] +[u'This field is required.'] With prefixes, a single data dictionary can hold data for multiple instances of the same form. @@ -1302,13 +1353,21 @@ of the same form. >>> p1 = Person(data, prefix='person1') >>> p1.is_valid() True ->>> p1.cleaned_data -{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)} +>>> p1.cleaned_data['first_name'] +u'John' +>>> p1.cleaned_data['last_name'] +u'Lennon' +>>> p1.cleaned_data['birthday'] +datetime.date(1940, 10, 9) >>> p2 = Person(data, prefix='person2') >>> p2.is_valid() True ->>> p2.cleaned_data -{'first_name': u'Jim', 'last_name': u'Morrison', 'birthday': datetime.date(1943, 12, 8)} +>>> p2.cleaned_data['first_name'] +u'Jim' +>>> p2.cleaned_data['last_name'] +u'Morrison' +>>> p2.cleaned_data['birthday'] +datetime.date(1943, 12, 8) By default, forms append a hyphen between the prefix and the field name, but a form can alter that behavior by implementing the add_prefix() method. This @@ -1333,8 +1392,12 @@ self.prefix. >>> p = Person(data, prefix='foo') >>> p.is_valid() True ->>> p.cleaned_data -{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)} +>>> p.cleaned_data['first_name'] +u'John' +>>> p.cleaned_data['last_name'] +u'Lennon' +>>> p.cleaned_data['birthday'] +datetime.date(1940, 10, 9) # Forms with NullBooleanFields ################################################ diff --git a/tests/regressiontests/model_fields/models.py b/tests/regressiontests/model_fields/models.py index e69de29bb2..7e07227961 100644 --- a/tests/regressiontests/model_fields/models.py +++ b/tests/regressiontests/model_fields/models.py @@ -0,0 +1,24 @@ + +from django.db import models + +class Foo(models.Model): + a = models.CharField(max_length=10) + +def get_foo(): + return Foo.objects.get(id=1) + +class Bar(models.Model): + b = models.CharField(max_length=10) + a = models.ForeignKey(Foo, default=get_foo) + +__test__ = {'API_TESTS':""" +# Create a couple of Places. +>>> f = Foo.objects.create(a='abc') +>>> f.id +1 +>>> b = Bar(b = "bcd") +>>> b.a +<Foo: Foo object> +>>> b.save() + +"""} diff --git a/tests/regressiontests/syndication/__init__.py b/tests/regressiontests/syndication/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/regressiontests/syndication/__init__.py diff --git a/tests/regressiontests/syndication/models.py b/tests/regressiontests/syndication/models.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/regressiontests/syndication/models.py diff --git a/tests/regressiontests/syndication/tests.py b/tests/regressiontests/syndication/tests.py new file mode 100644 index 0000000000..6a9dd643d7 --- /dev/null +++ b/tests/regressiontests/syndication/tests.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- + +from django.test import TestCase +from django.test.client import Client + +class SyndicationFeedTest(TestCase): + def test_complex_base_url(self): + """ + Tests that that the base url for a complex feed doesn't raise a 500 + exception. + """ + c = Client() + response = c.get('/syndication/feeds/complex/') + self.assertEquals(response.status_code, 404) diff --git a/tests/regressiontests/syndication/urls.py b/tests/regressiontests/syndication/urls.py new file mode 100644 index 0000000000..24644ffd53 --- /dev/null +++ b/tests/regressiontests/syndication/urls.py @@ -0,0 +1,18 @@ +from django.conf.urls.defaults import patterns +from django.core.exceptions import ObjectDoesNotExist +from django.contrib.syndication import feeds + + +class ComplexFeed(feeds.Feed): + def get_object(self, bits): + if len(bits) != 1: + raise ObjectDoesNotExist + return None + + +urlpatterns = patterns('', + (r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed', { + 'feed_dict': dict( + complex = ComplexFeed, + )}), +) diff --git a/tests/regressiontests/test_client_regress/models.py b/tests/regressiontests/test_client_regress/models.py index b5d9ae63b9..305ccc9aa3 100644 --- a/tests/regressiontests/test_client_regress/models.py +++ b/tests/regressiontests/test_client_regress/models.py @@ -3,7 +3,7 @@ Regression tests for the Test Client, especially the customized assertions. """ from django.test import Client, TestCase -from django.core import mail +from django.core.urlresolvers import reverse import os class AssertContainsTests(TestCase): @@ -261,3 +261,31 @@ class LoginTests(TestCase): # Check that assertRedirects uses the original client, not the # default client. self.assertRedirects(response, "http://testserver/test_client_regress/get_view/") + + +class URLEscapingTests(TestCase): + def test_simple_argument_get(self): + "Get a view that has a simple string argument" + response = self.client.get(reverse('arg_view', args=['Slartibartfast'])) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.content, 'Howdy, Slartibartfast') + + def test_argument_with_space_get(self): + "Get a view that has a string argument that requires escaping" + response = self.client.get(reverse('arg_view', args=['Arthur Dent'])) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.content, 'Hi, Arthur') + + def test_simple_argument_post(self): + "Post for a view that has a simple string argument" + response = self.client.post(reverse('arg_view', args=['Slartibartfast'])) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.content, 'Howdy, Slartibartfast') + + def test_argument_with_space_post(self): + "Post for a view that has a string argument that requires escaping" + response = self.client.post(reverse('arg_view', args=['Arthur Dent'])) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.content, 'Hi, Arthur') + + diff --git a/tests/regressiontests/test_client_regress/urls.py b/tests/regressiontests/test_client_regress/urls.py index e771707f61..d3304caef0 100644 --- a/tests/regressiontests/test_client_regress/urls.py +++ b/tests/regressiontests/test_client_regress/urls.py @@ -5,5 +5,6 @@ urlpatterns = patterns('', (r'^no_template_view/$', views.no_template_view), (r'^file_upload/$', views.file_upload_view), (r'^get_view/$', views.get_view), + url(r'^arg_view/(?P<name>.+)/$', views.view_with_argument, name='arg_view'), (r'^login_protected_redirect_view/$', views.login_protected_redirect_view) ) diff --git a/tests/regressiontests/test_client_regress/views.py b/tests/regressiontests/test_client_regress/views.py index 75efd212e1..f44757dc10 100644 --- a/tests/regressiontests/test_client_regress/views.py +++ b/tests/regressiontests/test_client_regress/views.py @@ -1,7 +1,5 @@ from django.contrib.auth.decorators import login_required -from django.core.mail import EmailMessage, SMTPConnection from django.http import HttpResponse, HttpResponseRedirect, HttpResponseServerError -from django.shortcuts import render_to_response def no_template_view(request): "A simple view that expects a GET request, and returns a rendered template" @@ -20,10 +18,22 @@ def file_upload_view(request): return HttpResponseServerError() def get_view(request): - "A simple login protected view" + "A simple login protected view" return HttpResponse("Hello world") get_view = login_required(get_view) +def view_with_argument(request, name): + """A view that takes a string argument + + The purpose of this view is to check that if a space is provided in + the argument, the test framework unescapes the %20 before passing + the value to the view. + """ + if name == 'Arthur Dent': + return HttpResponse('Hi, Arthur') + else: + return HttpResponse('Howdy, %s' % name) + def login_protected_redirect_view(request): "A view that redirects all requests to the GET view" return HttpResponseRedirect('/test_client_regress/get_view/') diff --git a/tests/urls.py b/tests/urls.py index 41b4aaf6d3..dbdf9a8064 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -19,4 +19,7 @@ urlpatterns = patterns('', (r'^middleware/', include('regressiontests.middleware.urls')), (r'^utils/', include('regressiontests.utils.urls')), + + # test urlconf for syndication tests + (r'^syndication/', include('regressiontests.syndication.urls')), ) |