mirror of
https://github.com/django/django.git
synced 2025-07-04 17:59:13 +00:00
newforms-admin: Merged from trunk up to [6670]. This is just before auto-escaping was checked in.
git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@6761 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
8b3d65e517
commit
4d52c80dd1
2
AUTHORS
2
AUTHORS
@ -121,6 +121,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Marc Fargas <telenieko@telenieko.com>
|
Marc Fargas <telenieko@telenieko.com>
|
||||||
Szilveszter Farkas <szilveszter.farkas@gmail.com>
|
Szilveszter Farkas <szilveszter.farkas@gmail.com>
|
||||||
favo@exoweb.net
|
favo@exoweb.net
|
||||||
|
Dmitri Fedortchenko <zeraien@gmail.com>
|
||||||
Bill Fenner <fenner@gmail.com>
|
Bill Fenner <fenner@gmail.com>
|
||||||
Stefane Fermgier <sf@fermigier.com>
|
Stefane Fermgier <sf@fermigier.com>
|
||||||
Afonso Fernández Nogueira <fonzzo.django@gmail.com>
|
Afonso Fernández Nogueira <fonzzo.django@gmail.com>
|
||||||
@ -304,6 +305,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Tyler Tarabula <tyler.tarabula@gmail.com>
|
Tyler Tarabula <tyler.tarabula@gmail.com>
|
||||||
Tyson Tate <tyson@fallingbullets.com>
|
Tyson Tate <tyson@fallingbullets.com>
|
||||||
Frank Tegtmeyer <fte@fte.to>
|
Frank Tegtmeyer <fte@fte.to>
|
||||||
|
terryh.tp@gmail.com
|
||||||
thebjorn <bp@datakortet.no>
|
thebjorn <bp@datakortet.no>
|
||||||
Zach Thompson <zthompson47@gmail.com>
|
Zach Thompson <zthompson47@gmail.com>
|
||||||
Michael Thornhill
|
Michael Thornhill
|
||||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -7,8 +7,8 @@ msgstr ""
|
|||||||
"Project-Id-Version: django\n"
|
"Project-Id-Version: django\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2007-07-18 16:49+0200\n"
|
"POT-Creation-Date: 2007-07-18 16:49+0200\n"
|
||||||
"PO-Revision-Date: 2007-03-06 10:30+0100\n"
|
"PO-Revision-Date: 2007-11-03 00:30+0100\n"
|
||||||
"Last-Translator: Philip Lindborg <philip.lindborg@gmail.com>\n"
|
"Last-Translator: Dmitri Fedortchenko <zeraien@gmail.com>\n"
|
||||||
"Language-Team: Django I18N <Django-I18N@googlegroups.com>\n"
|
"Language-Team: Django I18N <Django-I18N@googlegroups.com>\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
@ -146,7 +146,7 @@ msgid_plural ""
|
|||||||
msgstr[0] ""
|
msgstr[0] ""
|
||||||
"Var god och fyll giltiga %(self)s ID-nummer. Värdet %(value)r är ogiltigt."
|
"Var god och fyll giltiga %(self)s ID-nummer. Värdet %(value)r är ogiltigt."
|
||||||
msgstr[1] ""
|
msgstr[1] ""
|
||||||
"Var god och fyll giltiga %(self)s ID-nummer. Värdena %(value)r är ogiltiga."
|
"Var god och fyll giltiga %(self)s ID-nummer. Värden %(value)r är ogiltiga."
|
||||||
|
|
||||||
#: conf/global_settings.py:38
|
#: conf/global_settings.py:38
|
||||||
msgid "Arabic"
|
msgid "Arabic"
|
||||||
@ -318,20 +318,20 @@ msgstr "Traditionell Kinesiska"
|
|||||||
|
|
||||||
#: core/validators.py:71
|
#: core/validators.py:71
|
||||||
msgid "This value must contain only letters, numbers and underscores."
|
msgid "This value must contain only letters, numbers and underscores."
|
||||||
msgstr "Det här värdet får bara innehålla bokstäver, siffror och understreck."
|
msgstr "Det här värdet får endast innehålla bokstäver, siffror och understreck."
|
||||||
|
|
||||||
#: core/validators.py:75
|
#: core/validators.py:75
|
||||||
msgid ""
|
msgid ""
|
||||||
"This value must contain only letters, numbers, underscores, dashes or "
|
"This value must contain only letters, numbers, underscores, dashes or "
|
||||||
"slashes."
|
"slashes."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Det här värdet får bara innehålla bokstäver, siffror, understreck, "
|
"Det här värdet får endast innehålla bokstäver, siffror, understreck, "
|
||||||
"bindestreck eller snedstreck."
|
"bindestreck eller snedstreck."
|
||||||
|
|
||||||
#: core/validators.py:79
|
#: core/validators.py:79
|
||||||
msgid "This value must contain only letters, numbers, underscores or hyphens."
|
msgid "This value must contain only letters, numbers, underscores or hyphens."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Det här värdet får bara innehålla bokstäver, siffror, understreck eller "
|
"Det här värdet får endast innehålla bokstäver, siffror, understreck eller "
|
||||||
"avstavningstecken."
|
"avstavningstecken."
|
||||||
|
|
||||||
#: core/validators.py:83
|
#: core/validators.py:83
|
||||||
@ -352,7 +352,7 @@ msgstr "Fyll i giltiga e-postadresser avskilda med kommatecken."
|
|||||||
|
|
||||||
#: core/validators.py:110
|
#: core/validators.py:110
|
||||||
msgid "Please enter a valid IP address."
|
msgid "Please enter a valid IP address."
|
||||||
msgstr "Var god fyll i en giltigt IP-adress."
|
msgstr "Var god fyll i en giltig IP-adress."
|
||||||
|
|
||||||
#: core/validators.py:114
|
#: core/validators.py:114
|
||||||
msgid "Empty values are not allowed here."
|
msgid "Empty values are not allowed here."
|
||||||
@ -414,7 +414,7 @@ msgstr ""
|
|||||||
#: core/validators.py:204
|
#: core/validators.py:204
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "The URL %s does not point to a valid QuickTime video."
|
msgid "The URL %s does not point to a valid QuickTime video."
|
||||||
msgstr "URL:en %s pekar inte på en giltig QuickTime-video."
|
msgstr "URL:en %s pekar inte mot en giltig QuickTime-video."
|
||||||
|
|
||||||
#: core/validators.py:208
|
#: core/validators.py:208
|
||||||
msgid "A valid URL is required."
|
msgid "A valid URL is required."
|
||||||
@ -432,7 +432,7 @@ msgstr ""
|
|||||||
#: core/validators.py:229
|
#: core/validators.py:229
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Badly formed XML: %s"
|
msgid "Badly formed XML: %s"
|
||||||
msgstr "Missformad XML: %s"
|
msgstr "Felaktigt XML: %s"
|
||||||
|
|
||||||
#: core/validators.py:246
|
#: core/validators.py:246
|
||||||
#, python-format
|
#, python-format
|
||||||
@ -480,7 +480,7 @@ msgstr "Det här fältet måste anges om %(field)s inte är %(value)s"
|
|||||||
|
|
||||||
#: core/validators.py:359
|
#: core/validators.py:359
|
||||||
msgid "Duplicate values are not allowed."
|
msgid "Duplicate values are not allowed."
|
||||||
msgstr "Dubbelvärden är inte tillåtna."
|
msgstr "Dubletter är inte tillåtna."
|
||||||
|
|
||||||
#: core/validators.py:374
|
#: core/validators.py:374
|
||||||
#, python-format
|
#, python-format
|
||||||
@ -544,12 +544,12 @@ msgstr "Fyll i ett giltigt decimaltal."
|
|||||||
#: core/validators.py:454
|
#: core/validators.py:454
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Make sure your uploaded file is at least %s bytes big."
|
msgid "Make sure your uploaded file is at least %s bytes big."
|
||||||
msgstr "Se till att filen du laddade upp är minst %s bytes stor."
|
msgstr "Se till att filen du laddade upp är minst %s byte stor."
|
||||||
|
|
||||||
#: core/validators.py:455
|
#: core/validators.py:455
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Make sure your uploaded file is at most %s bytes big."
|
msgid "Make sure your uploaded file is at most %s bytes big."
|
||||||
msgstr "Se till att filen du laddade upp är som mest %s bytes stor."
|
msgstr "Se till att filen du laddade upp är som mest %s byte stor."
|
||||||
|
|
||||||
#: core/validators.py:472
|
#: core/validators.py:472
|
||||||
msgid "The format for this field is wrong."
|
msgid "The format for this field is wrong."
|
||||||
@ -876,7 +876,7 @@ msgstr "är ett giltigt betyg"
|
|||||||
|
|
||||||
#: contrib/comments/models.py:83 contrib/comments/models.py:169
|
#: contrib/comments/models.py:83 contrib/comments/models.py:169
|
||||||
msgid "date/time submitted"
|
msgid "date/time submitted"
|
||||||
msgstr "datum/tid skickat"
|
msgstr "skickat datum/tid"
|
||||||
|
|
||||||
#: contrib/comments/models.py:84 contrib/comments/models.py:170
|
#: contrib/comments/models.py:84 contrib/comments/models.py:170
|
||||||
msgid "is public"
|
msgid "is public"
|
||||||
@ -915,7 +915,7 @@ msgid ""
|
|||||||
"\n"
|
"\n"
|
||||||
"http://%(domain)s%(url)s"
|
"http://%(domain)s%(url)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Postat av %(user)s %(date)s\n"
|
"Inlagt av %(user)s %(date)s\n"
|
||||||
"\n"
|
"\n"
|
||||||
"%(comment)s\n"
|
"%(comment)s\n"
|
||||||
"\n"
|
"\n"
|
||||||
@ -947,7 +947,7 @@ msgstr "poäng"
|
|||||||
|
|
||||||
#: contrib/comments/models.py:234
|
#: contrib/comments/models.py:234
|
||||||
msgid "score date"
|
msgid "score date"
|
||||||
msgstr "poängdatum"
|
msgstr "poängen tillsatt den"
|
||||||
|
|
||||||
#: contrib/comments/models.py:237
|
#: contrib/comments/models.py:237
|
||||||
msgid "karma score"
|
msgid "karma score"
|
||||||
@ -960,7 +960,7 @@ msgstr "karmapoäng"
|
|||||||
#: contrib/comments/models.py:242
|
#: contrib/comments/models.py:242
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "%(score)d rating by %(user)s"
|
msgid "%(score)d rating by %(user)s"
|
||||||
msgstr "Betyget %(score)d av %(user)s"
|
msgstr "Poäng %(score)d av %(user)s"
|
||||||
|
|
||||||
#: contrib/comments/models.py:258
|
#: contrib/comments/models.py:258
|
||||||
#, python-format
|
#, python-format
|
||||||
@ -996,20 +996,20 @@ msgstr "borttagningsdatum"
|
|||||||
|
|
||||||
#: contrib/comments/models.py:280
|
#: contrib/comments/models.py:280
|
||||||
msgid "moderator deletion"
|
msgid "moderator deletion"
|
||||||
msgstr "moderatorborttagning"
|
msgstr "borttaget av moderator"
|
||||||
|
|
||||||
#: contrib/comments/models.py:281
|
#: contrib/comments/models.py:281
|
||||||
msgid "moderator deletions"
|
msgid "moderator deletions"
|
||||||
msgstr "moderatorborttagningar"
|
msgstr "borttagna av moderator"
|
||||||
|
|
||||||
#: contrib/comments/models.py:285
|
#: contrib/comments/models.py:285
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Moderator deletion by %r"
|
msgid "Moderator deletion by %r"
|
||||||
msgstr "Moderatorborttagning av %r"
|
msgstr "Borttaget av moderator %r"
|
||||||
|
|
||||||
#: contrib/comments/views/karma.py:20
|
#: contrib/comments/views/karma.py:20
|
||||||
msgid "Anonymous users cannot vote"
|
msgid "Anonymous users cannot vote"
|
||||||
msgstr "Anonyma användare kan inte rösta"
|
msgstr "Anonyma användare får inte rösta"
|
||||||
|
|
||||||
#: contrib/comments/views/karma.py:24
|
#: contrib/comments/views/karma.py:24
|
||||||
msgid "Invalid comment ID"
|
msgid "Invalid comment ID"
|
||||||
@ -1162,7 +1162,7 @@ msgstr "domännamn"
|
|||||||
|
|
||||||
#: contrib/sites/models.py:16
|
#: contrib/sites/models.py:16
|
||||||
msgid "display name"
|
msgid "display name"
|
||||||
msgstr "visat namn"
|
msgstr "visningsnamn"
|
||||||
|
|
||||||
#: contrib/sites/models.py:20
|
#: contrib/sites/models.py:20
|
||||||
msgid "site"
|
msgid "site"
|
||||||
@ -1243,8 +1243,8 @@ msgid ""
|
|||||||
"Please enter a correct username and password. Note that both fields are case-"
|
"Please enter a correct username and password. Note that both fields are case-"
|
||||||
"sensitive."
|
"sensitive."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Var god ange ett korrekt användarnamn och lösenord. Observera att båda "
|
"Var god ange ett korrekt användarnamn och lösenord. Tänk på att "
|
||||||
"fälten är skiftlägeskänsliga."
|
"skilja mellan gemener och versaler."
|
||||||
|
|
||||||
#: contrib/admin/views/decorators.py:24
|
#: contrib/admin/views/decorators.py:24
|
||||||
#: contrib/admin/templates/admin/login.html:25
|
#: contrib/admin/templates/admin/login.html:25
|
||||||
@ -1474,12 +1474,12 @@ msgstr "Decimaltal"
|
|||||||
|
|
||||||
#: contrib/admin/views/doc.py:299
|
#: contrib/admin/views/doc.py:299
|
||||||
msgid "E-mail address"
|
msgid "E-mail address"
|
||||||
msgstr "E-postadress:"
|
msgstr "E-postadress"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:300 contrib/admin/views/doc.py:301
|
#: contrib/admin/views/doc.py:300 contrib/admin/views/doc.py:301
|
||||||
#: contrib/admin/views/doc.py:304
|
#: contrib/admin/views/doc.py:304
|
||||||
msgid "File path"
|
msgid "File path"
|
||||||
msgstr "Filsökväg"
|
msgstr "Sökväg till fil"
|
||||||
|
|
||||||
#: contrib/admin/views/doc.py:302
|
#: contrib/admin/views/doc.py:302
|
||||||
msgid "Floating point number"
|
msgid "Floating point number"
|
||||||
@ -1697,7 +1697,7 @@ msgstr "Sidan kunde inte hittas"
|
|||||||
|
|
||||||
#: contrib/admin/templates/admin/404.html:10
|
#: contrib/admin/templates/admin/404.html:10
|
||||||
msgid "We're sorry, but the requested page could not be found."
|
msgid "We're sorry, but the requested page could not be found."
|
||||||
msgstr "Vi är ledsna, men den efterfrågade sidan kunde inte hittas."
|
msgstr "Vi beklagar, men den efterfrågade sidan kunde tyvärr inte hittas."
|
||||||
|
|
||||||
#: contrib/admin/templates/admin/index.html:17
|
#: contrib/admin/templates/admin/index.html:17
|
||||||
#, python-format
|
#, python-format
|
||||||
@ -2026,11 +2026,11 @@ msgstr "Python-modellklassnamn"
|
|||||||
|
|
||||||
#: contrib/contenttypes/models.py:40
|
#: contrib/contenttypes/models.py:40
|
||||||
msgid "content type"
|
msgid "content type"
|
||||||
msgstr "innehållstyp"
|
msgstr "typ av innehåll"
|
||||||
|
|
||||||
#: contrib/contenttypes/models.py:41
|
#: contrib/contenttypes/models.py:41
|
||||||
msgid "content types"
|
msgid "content types"
|
||||||
msgstr "innehållstyper"
|
msgstr "typer av innehåll"
|
||||||
|
|
||||||
#: contrib/auth/views.py:41
|
#: contrib/auth/views.py:41
|
||||||
msgid "Logged out"
|
msgid "Logged out"
|
||||||
@ -2178,7 +2178,7 @@ msgstr "meddelande"
|
|||||||
|
|
||||||
#: 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."
|
msgid "The two password fields didn't match."
|
||||||
msgstr "De båda lösenorden stämde inte överens."
|
msgstr "Lösenorden stämde inte överens."
|
||||||
|
|
||||||
#: contrib/auth/forms.py:25
|
#: contrib/auth/forms.py:25
|
||||||
msgid "A user with that username already exists."
|
msgid "A user with that username already exists."
|
||||||
@ -2218,7 +2218,7 @@ msgstr "Fyll i ett postnummer. Du måste ha mellanslag mellan nummerdelarna."
|
|||||||
|
|
||||||
#: contrib/localflavor/br/forms.py:18
|
#: contrib/localflavor/br/forms.py:18
|
||||||
msgid "Enter a zip code in the format XXXXX-XXX."
|
msgid "Enter a zip code in the format XXXXX-XXX."
|
||||||
msgstr "Fyll i zipkod på formatet XXXXX-XXX."
|
msgstr "Fyll i ett postnummer på formatet XXXXX-XXX."
|
||||||
|
|
||||||
#: contrib/localflavor/br/forms.py:30
|
#: contrib/localflavor/br/forms.py:30
|
||||||
msgid "Phone numbers must be in XX-XXXX-XXXX format."
|
msgid "Phone numbers must be in XX-XXXX-XXXX format."
|
||||||
@ -2246,16 +2246,16 @@ msgstr "Ogiltigt CNPJ-nummer."
|
|||||||
|
|
||||||
#: contrib/localflavor/au/forms.py:18
|
#: contrib/localflavor/au/forms.py:18
|
||||||
msgid "Enter a 4 digit post code."
|
msgid "Enter a 4 digit post code."
|
||||||
msgstr "Fyll i en fyrsiffrig postkod."
|
msgstr "Fyll i ett fyrsiffrigt postnummer."
|
||||||
|
|
||||||
#: contrib/localflavor/fr/forms.py:17 contrib/localflavor/de/forms.py:16
|
#: contrib/localflavor/fr/forms.py:17 contrib/localflavor/de/forms.py:16
|
||||||
#: contrib/localflavor/fi/forms.py:14
|
#: contrib/localflavor/fi/forms.py:14
|
||||||
msgid "Enter a zip code in the format XXXXX."
|
msgid "Enter a zip code in the format XXXXX."
|
||||||
msgstr "Fyll i en zipkod på formatet XXXXX."
|
msgstr "Fyll i ett postnummer på formatet XXXXX."
|
||||||
|
|
||||||
#: contrib/localflavor/us/forms.py:18
|
#: contrib/localflavor/us/forms.py:18
|
||||||
msgid "Enter a zip code in the format XXXXX or XXXXX-XXXX."
|
msgid "Enter a zip code in the format XXXXX or XXXXX-XXXX."
|
||||||
msgstr "Fyll i zipkod på formatet XXXXX eller XXXXX-XXXX."
|
msgstr "Fyll i ett postnummer på formatet XXXXX eller XXXXX-XXXX."
|
||||||
|
|
||||||
#: contrib/localflavor/us/forms.py:51
|
#: contrib/localflavor/us/forms.py:51
|
||||||
msgid "Enter a valid U.S. Social Security number in XXX-XX-XXXX format."
|
msgid "Enter a valid U.S. Social Security number in XXX-XX-XXXX format."
|
||||||
@ -2335,7 +2335,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: contrib/localflavor/jp/forms.py:21
|
#: contrib/localflavor/jp/forms.py:21
|
||||||
msgid "Enter a postal code in the format XXXXXXX or XXX-XXXX."
|
msgid "Enter a postal code in the format XXXXXXX or XXX-XXXX."
|
||||||
msgstr "Fyll i postkod på formatet XXXXXXX eller XXX-XXXX."
|
msgstr "Fyll i ett postnummer på formatet XXXXXXX eller XXX-XXXX."
|
||||||
|
|
||||||
#: contrib/localflavor/jp/jp_prefectures.py:4
|
#: contrib/localflavor/jp/jp_prefectures.py:4
|
||||||
msgid "Hokkaido"
|
msgid "Hokkaido"
|
||||||
@ -2631,7 +2631,7 @@ msgstr "Zürich"
|
|||||||
|
|
||||||
#: contrib/localflavor/ch/forms.py:18 contrib/localflavor/no/forms.py:14
|
#: contrib/localflavor/ch/forms.py:18 contrib/localflavor/no/forms.py:14
|
||||||
msgid "Enter a zip code in the format XXXX."
|
msgid "Enter a zip code in the format XXXX."
|
||||||
msgstr "Fyll i zipkod på formatet XXXX."
|
msgstr "Fyll i postnummer på formatet XXXX."
|
||||||
|
|
||||||
#: contrib/localflavor/ch/forms.py:90
|
#: contrib/localflavor/ch/forms.py:90
|
||||||
msgid ""
|
msgid ""
|
||||||
@ -2652,7 +2652,7 @@ msgstr "Det isländska personnumret är inte giltigt."
|
|||||||
|
|
||||||
#: contrib/localflavor/it/forms.py:16
|
#: contrib/localflavor/it/forms.py:16
|
||||||
msgid "Enter a valid zip code."
|
msgid "Enter a valid zip code."
|
||||||
msgstr "Fyll i en giltigt zipkod."
|
msgstr "Fyll i ett giltigt postnummer."
|
||||||
|
|
||||||
#: contrib/localflavor/it/forms.py:41
|
#: contrib/localflavor/it/forms.py:41
|
||||||
msgid "Enter a valid Social Security number."
|
msgid "Enter a valid Social Security number."
|
||||||
@ -2739,11 +2739,11 @@ msgstr ""
|
|||||||
|
|
||||||
#: contrib/flatpages/models.py:18
|
#: contrib/flatpages/models.py:18
|
||||||
msgid "flat page"
|
msgid "flat page"
|
||||||
msgstr "flat sida"
|
msgstr "statisk sida"
|
||||||
|
|
||||||
#: contrib/flatpages/models.py:19
|
#: contrib/flatpages/models.py:19
|
||||||
msgid "flat pages"
|
msgid "flat pages"
|
||||||
msgstr "flata sidor"
|
msgstr "statiska sidor"
|
||||||
|
|
||||||
#: utils/dates.py:6
|
#: utils/dates.py:6
|
||||||
msgid "Monday"
|
msgid "Monday"
|
||||||
@ -3024,7 +3024,7 @@ msgstr "ja,nej,kanske"
|
|||||||
msgid "%(size)d byte"
|
msgid "%(size)d byte"
|
||||||
msgid_plural "%(size)d bytes"
|
msgid_plural "%(size)d bytes"
|
||||||
msgstr[0] "%(size)d byte"
|
msgstr[0] "%(size)d byte"
|
||||||
msgstr[1] "%(size)d bytes"
|
msgstr[1] "%(size)d byte"
|
||||||
|
|
||||||
#: template/defaultfilters.py:516
|
#: template/defaultfilters.py:516
|
||||||
#, python-format
|
#, python-format
|
||||||
|
Binary file not shown.
@ -58,7 +58,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: contrib/admin/media/js/dateparse.js:33
|
#: contrib/admin/media/js/dateparse.js:33
|
||||||
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday"
|
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday"
|
||||||
msgstr "Söndag Mondag Tisdag Onsdag Torsdag Fredag Lördag"
|
msgstr "Söndag Måndag Tisdag Onsdag Torsdag Fredag Lördag"
|
||||||
|
|
||||||
#: contrib/admin/media/js/calendar.js:25
|
#: contrib/admin/media/js/calendar.js:25
|
||||||
msgid "S M T W T F S"
|
msgid "S M T W T F S"
|
||||||
@ -96,7 +96,7 @@ msgstr "06.00"
|
|||||||
|
|
||||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:84
|
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:84
|
||||||
msgid "Noon"
|
msgid "Noon"
|
||||||
msgstr "Mitt på dagen"
|
msgstr "Middag"
|
||||||
|
|
||||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:88
|
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:88
|
||||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:183
|
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:183
|
||||||
@ -118,5 +118,5 @@ msgstr "Igår"
|
|||||||
|
|
||||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:179
|
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:179
|
||||||
msgid "Tomorrow"
|
msgid "Tomorrow"
|
||||||
msgstr "Imorgon"
|
msgstr "I morgon"
|
||||||
|
|
||||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -8,19 +8,9 @@ def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIE
|
|||||||
redirecting to the log-in page if necessary. The test should be a callable
|
redirecting to the log-in page if necessary. The test should be a callable
|
||||||
that takes the user object and returns True if the user passes.
|
that takes the user object and returns True if the user passes.
|
||||||
"""
|
"""
|
||||||
if not login_url:
|
def decorate(view_func):
|
||||||
from django.conf import settings
|
return _CheckLogin(view_func, test_func, login_url, redirect_field_name)
|
||||||
login_url = settings.LOGIN_URL
|
return decorate
|
||||||
def _dec(view_func):
|
|
||||||
def _checklogin(request, *args, **kwargs):
|
|
||||||
if test_func(request.user):
|
|
||||||
return view_func(request, *args, **kwargs)
|
|
||||||
return HttpResponseRedirect('%s?%s=%s' % (login_url, redirect_field_name, urlquote(request.get_full_path())))
|
|
||||||
_checklogin.__doc__ = view_func.__doc__
|
|
||||||
_checklogin.__dict__ = view_func.__dict__
|
|
||||||
|
|
||||||
return _checklogin
|
|
||||||
return _dec
|
|
||||||
|
|
||||||
def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME):
|
def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME):
|
||||||
"""
|
"""
|
||||||
@ -42,3 +32,34 @@ def permission_required(perm, login_url=None):
|
|||||||
"""
|
"""
|
||||||
return user_passes_test(lambda u: u.has_perm(perm), login_url=login_url)
|
return user_passes_test(lambda u: u.has_perm(perm), login_url=login_url)
|
||||||
|
|
||||||
|
class _CheckLogin(object):
|
||||||
|
"""
|
||||||
|
Class that checks that the user passes the given test, redirecting to
|
||||||
|
the log-in page if necessary. If the test is passed, the view function
|
||||||
|
is invoked. The test should be a callable that takes the user object
|
||||||
|
and returns True if the user passes.
|
||||||
|
|
||||||
|
We use a class here so that we can define __get__. This way, when a
|
||||||
|
_CheckLogin object is used as a method decorator, the view function
|
||||||
|
is properly bound to its instance.
|
||||||
|
"""
|
||||||
|
def __init__(self, view_func, test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
|
||||||
|
if not login_url:
|
||||||
|
from django.conf import settings
|
||||||
|
login_url = settings.LOGIN_URL
|
||||||
|
self.view_func = view_func
|
||||||
|
self.test_func = test_func
|
||||||
|
self.login_url = login_url
|
||||||
|
self.redirect_field_name = redirect_field_name
|
||||||
|
self.__name__ = view_func.__name__
|
||||||
|
|
||||||
|
def __get__(self, obj, cls=None):
|
||||||
|
view_func = self.view_func.__get__(obj, cls)
|
||||||
|
return _CheckLogin(view_func, self.test_func, self.login_url, self.redirect_field_name)
|
||||||
|
|
||||||
|
def __call__(self, request, *args, **kwargs):
|
||||||
|
if self.test_func(request.user):
|
||||||
|
return self.view_func(request, *args, **kwargs)
|
||||||
|
path = urlquote(request.get_full_path())
|
||||||
|
tup = self.login_url, self.redirect_field_name, path
|
||||||
|
return HttpResponseRedirect('%s?%s=%s' % tup)
|
||||||
|
@ -4,6 +4,10 @@ from django import http
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
class BaseHandler(object):
|
class BaseHandler(object):
|
||||||
|
# Changes that are always applied to a response (in this order).
|
||||||
|
response_fixes = [http.fix_location_header,
|
||||||
|
http.conditional_content_removal]
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._request_middleware = self._view_middleware = self._response_middleware = self._exception_middleware = None
|
self._request_middleware = self._view_middleware = self._response_middleware = self._exception_middleware = None
|
||||||
|
|
||||||
@ -50,10 +54,6 @@ class BaseHandler(object):
|
|||||||
|
|
||||||
def get_response(self, request):
|
def get_response(self, request):
|
||||||
"Returns an HttpResponse object for the given HttpRequest"
|
"Returns an HttpResponse object for the given HttpRequest"
|
||||||
response = self._real_get_response(request)
|
|
||||||
return fix_location_header(request, response)
|
|
||||||
|
|
||||||
def _real_get_response(self, request):
|
|
||||||
from django.core import exceptions, urlresolvers
|
from django.core import exceptions, urlresolvers
|
||||||
from django.core.mail import mail_admins
|
from django.core.mail import mail_admins
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@ -134,15 +134,13 @@ class BaseHandler(object):
|
|||||||
import traceback
|
import traceback
|
||||||
return '\n'.join(traceback.format_exception(*(exc_info or sys.exc_info())))
|
return '\n'.join(traceback.format_exception(*(exc_info or sys.exc_info())))
|
||||||
|
|
||||||
def fix_location_header(request, response):
|
def apply_response_fixes(self, request, response):
|
||||||
"""
|
"""
|
||||||
Ensure that we always use an absolute URI in any location header in the
|
Applies each of the functions in self.response_fixes to the request and
|
||||||
response. This is required by RFC 2616, section 14.30.
|
response, modifying the response in the process. Returns the new
|
||||||
|
response.
|
||||||
Code constructing response objects is free to insert relative paths and
|
"""
|
||||||
this function converts them to absolute paths.
|
for func in self.response_fixes:
|
||||||
"""
|
response = func(request, response)
|
||||||
if 'Location' in response and request.get_host():
|
return response
|
||||||
response['Location'] = request.build_absolute_uri(response['Location'])
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
@ -162,6 +162,7 @@ class ModPythonHandler(BaseHandler):
|
|||||||
# Apply response middleware
|
# Apply response middleware
|
||||||
for middleware_method in self._response_middleware:
|
for middleware_method in self._response_middleware:
|
||||||
response = middleware_method(request, response)
|
response = middleware_method(request, response)
|
||||||
|
response = self.apply_response_fixes(request, response)
|
||||||
finally:
|
finally:
|
||||||
dispatcher.send(signal=signals.request_finished)
|
dispatcher.send(signal=signals.request_finished)
|
||||||
|
|
||||||
|
@ -207,6 +207,7 @@ class WSGIHandler(BaseHandler):
|
|||||||
# Apply response middleware
|
# Apply response middleware
|
||||||
for middleware_method in self._response_middleware:
|
for middleware_method in self._response_middleware:
|
||||||
response = middleware_method(request, response)
|
response = middleware_method(request, response)
|
||||||
|
response = self.apply_response_fixes(request, response)
|
||||||
finally:
|
finally:
|
||||||
dispatcher.send(signal=signals.request_finished)
|
dispatcher.send(signal=signals.request_finished)
|
||||||
|
|
||||||
@ -220,3 +221,4 @@ class WSGIHandler(BaseHandler):
|
|||||||
response_headers.append(('Set-Cookie', str(c.output(header=''))))
|
response_headers.append(('Set-Cookie', str(c.output(header=''))))
|
||||||
start_response(status, response_headers)
|
start_response(status, response_headers)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ from urllib import urlencode
|
|||||||
from urlparse import urljoin
|
from urlparse import urljoin
|
||||||
from django.utils.datastructures import MultiValueDict, FileDict
|
from django.utils.datastructures import MultiValueDict, FileDict
|
||||||
from django.utils.encoding import smart_str, iri_to_uri, force_unicode
|
from django.utils.encoding import smart_str, iri_to_uri, force_unicode
|
||||||
|
from utils import *
|
||||||
|
|
||||||
RESERVED_CHARS="!*'();:@&=+$,/?%#[]"
|
RESERVED_CHARS="!*'();:@&=+$,/?%#[]"
|
||||||
|
|
||||||
|
34
django/http/utils.py
Normal file
34
django/http/utils.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
"""
|
||||||
|
Functions that modify an HTTP request or response in some way.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# This group of functions are run as part of the response handling, after
|
||||||
|
# everything else, including all response middleware. Think of them as
|
||||||
|
# "compulsory response middleware". Be careful about what goes here, because
|
||||||
|
# it's a little fiddly to override this behaviour, so they should be truly
|
||||||
|
# universally applicable.
|
||||||
|
|
||||||
|
def fix_location_header(request, response):
|
||||||
|
"""
|
||||||
|
Ensures that we always use an absolute URI in any location header in the
|
||||||
|
response. This is required by RFC 2616, section 14.30.
|
||||||
|
|
||||||
|
Code constructing response objects is free to insert relative paths and
|
||||||
|
this function converts them to absolute paths.
|
||||||
|
"""
|
||||||
|
if 'Location' in response and request.get_host():
|
||||||
|
response['Location'] = request.build_absolute_uri(response['Location'])
|
||||||
|
return response
|
||||||
|
|
||||||
|
def conditional_content_removal(request, response):
|
||||||
|
"""
|
||||||
|
Removes the content of responses for HEAD requests, 1xx, 204 and 304
|
||||||
|
responses. Ensures compliance with RFC 2616, section 4.3.
|
||||||
|
"""
|
||||||
|
if 100 <= response.status_code < 200 or response.status_code in (204, 304):
|
||||||
|
response.content = ''
|
||||||
|
response['Content-Length'] = 0
|
||||||
|
if request.method == 'HEAD':
|
||||||
|
response.content = ''
|
||||||
|
return response
|
||||||
|
|
@ -6,8 +6,6 @@ class ConditionalGetMiddleware(object):
|
|||||||
Last-Modified header, and the request has If-None-Match or
|
Last-Modified header, and the request has If-None-Match or
|
||||||
If-Modified-Since, the response is replaced by an HttpNotModified.
|
If-Modified-Since, the response is replaced by an HttpNotModified.
|
||||||
|
|
||||||
Removes the content from any response to a HEAD request.
|
|
||||||
|
|
||||||
Also sets the Date and Content-Length response-headers.
|
Also sets the Date and Content-Length response-headers.
|
||||||
"""
|
"""
|
||||||
def process_response(self, request, response):
|
def process_response(self, request, response):
|
||||||
@ -18,19 +16,17 @@ class ConditionalGetMiddleware(object):
|
|||||||
if response.has_header('ETag'):
|
if response.has_header('ETag'):
|
||||||
if_none_match = request.META.get('HTTP_IF_NONE_MATCH', None)
|
if_none_match = request.META.get('HTTP_IF_NONE_MATCH', None)
|
||||||
if if_none_match == response['ETag']:
|
if if_none_match == response['ETag']:
|
||||||
response.status_code = 304
|
# Setting the status is enough here. The response handling path
|
||||||
response.content = ''
|
# automatically removes content for this status code (in
|
||||||
response['Content-Length'] = '0'
|
# http.conditional_content_removal()).
|
||||||
|
response.status = 304
|
||||||
|
|
||||||
if response.has_header('Last-Modified'):
|
if response.has_header('Last-Modified'):
|
||||||
if_modified_since = request.META.get('HTTP_IF_MODIFIED_SINCE', None)
|
if_modified_since = request.META.get('HTTP_IF_MODIFIED_SINCE', None)
|
||||||
if if_modified_since == response['Last-Modified']:
|
if if_modified_since == response['Last-Modified']:
|
||||||
response.status_code = 304
|
# Setting the status code is enough here (same reasons as
|
||||||
response.content = ''
|
# above).
|
||||||
response['Content-Length'] = '0'
|
response.status = 304
|
||||||
|
|
||||||
if request.method == 'HEAD':
|
|
||||||
response.content = ''
|
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
Form classes
|
Form classes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import copy
|
from copy import deepcopy
|
||||||
|
|
||||||
from django.utils.datastructures import SortedDict
|
from django.utils.datastructures import SortedDict
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
@ -21,18 +21,6 @@ def pretty_name(name):
|
|||||||
name = name[0].upper() + name[1:]
|
name = name[0].upper() + name[1:]
|
||||||
return name.replace('_', ' ')
|
return name.replace('_', ' ')
|
||||||
|
|
||||||
class SortedDictFromList(SortedDict):
|
|
||||||
"A dictionary that keeps its keys in the order in which they're inserted."
|
|
||||||
# This is different than django.utils.datastructures.SortedDict, because
|
|
||||||
# this takes a list/tuple as the argument to __init__().
|
|
||||||
def __init__(self, data=None):
|
|
||||||
if data is None: data = []
|
|
||||||
self.keyOrder = [d[0] for d in data]
|
|
||||||
dict.__init__(self, dict(data))
|
|
||||||
|
|
||||||
def copy(self):
|
|
||||||
return SortedDictFromList([(k, copy.deepcopy(v)) for k, v in self.items()])
|
|
||||||
|
|
||||||
class DeclarativeFieldsMetaclass(type):
|
class DeclarativeFieldsMetaclass(type):
|
||||||
"""
|
"""
|
||||||
Metaclass that converts Field attributes to a dictionary called
|
Metaclass that converts Field attributes to a dictionary called
|
||||||
@ -50,7 +38,7 @@ class DeclarativeFieldsMetaclass(type):
|
|||||||
if hasattr(base, 'base_fields'):
|
if hasattr(base, 'base_fields'):
|
||||||
fields = base.base_fields.items() + fields
|
fields = base.base_fields.items() + fields
|
||||||
|
|
||||||
attrs['base_fields'] = SortedDictFromList(fields)
|
attrs['base_fields'] = SortedDict(fields)
|
||||||
|
|
||||||
new_class = type.__new__(cls, name, bases, attrs)
|
new_class = type.__new__(cls, name, bases, attrs)
|
||||||
if 'media' not in attrs:
|
if 'media' not in attrs:
|
||||||
@ -79,7 +67,7 @@ class BaseForm(StrAndUnicode):
|
|||||||
# alter self.fields, we create self.fields here by copying base_fields.
|
# alter self.fields, we create self.fields here by copying base_fields.
|
||||||
# Instances should always modify self.fields; they should not modify
|
# Instances should always modify self.fields; they should not modify
|
||||||
# self.base_fields.
|
# self.base_fields.
|
||||||
self.fields = self.base_fields.copy()
|
self.fields = deepcopy(self.base_fields)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.as_table()
|
return self.as_table()
|
||||||
|
@ -5,9 +5,10 @@ and database field objects.
|
|||||||
|
|
||||||
from django.utils.translation import ugettext
|
from django.utils.translation import ugettext
|
||||||
from django.utils.encoding import smart_unicode
|
from django.utils.encoding import smart_unicode
|
||||||
|
from django.utils.datastructures import SortedDict
|
||||||
|
|
||||||
from util import ValidationError
|
from util import ValidationError
|
||||||
from forms import BaseForm, SortedDictFromList
|
from forms import BaseForm
|
||||||
from fields import Field, ChoiceField, IntegerField
|
from fields import Field, ChoiceField, IntegerField
|
||||||
from formsets import BaseFormSet, formset_for_form, DELETION_FIELD_NAME
|
from formsets import BaseFormSet, formset_for_form, DELETION_FIELD_NAME
|
||||||
from widgets import Select, SelectMultiple, HiddenInput, MultipleHiddenInput
|
from widgets import Select, SelectMultiple, HiddenInput, MultipleHiddenInput
|
||||||
@ -91,7 +92,7 @@ def form_for_model(model, form=BaseForm, fields=None,
|
|||||||
formfield = formfield_callback(f)
|
formfield = formfield_callback(f)
|
||||||
if formfield:
|
if formfield:
|
||||||
field_list.append((f.name, formfield))
|
field_list.append((f.name, formfield))
|
||||||
base_fields = SortedDictFromList(field_list)
|
base_fields = SortedDict(field_list)
|
||||||
return type(opts.object_name + 'Form', (form,),
|
return type(opts.object_name + 'Form', (form,),
|
||||||
{'base_fields': base_fields, '_model': model,
|
{'base_fields': base_fields, '_model': model,
|
||||||
'save': make_model_save(model, fields, 'created')})
|
'save': make_model_save(model, fields, 'created')})
|
||||||
@ -120,7 +121,7 @@ def form_for_instance(instance, form=BaseForm, fields=None,
|
|||||||
formfield = formfield_callback(f, initial=current_value)
|
formfield = formfield_callback(f, initial=current_value)
|
||||||
if formfield:
|
if formfield:
|
||||||
field_list.append((f.name, formfield))
|
field_list.append((f.name, formfield))
|
||||||
base_fields = SortedDictFromList(field_list)
|
base_fields = SortedDict(field_list)
|
||||||
return type(opts.object_name + 'InstanceForm', (form,),
|
return type(opts.object_name + 'InstanceForm', (form,),
|
||||||
{'base_fields': base_fields, '_model': model,
|
{'base_fields': base_fields, '_model': model,
|
||||||
'save': make_instance_save(instance, fields, 'changed')})
|
'save': make_instance_save(instance, fields, 'changed')})
|
||||||
@ -129,8 +130,8 @@ def form_for_fields(field_list):
|
|||||||
"""
|
"""
|
||||||
Returns a Form class for the given list of Django database field instances.
|
Returns a Form class for the given list of Django database field instances.
|
||||||
"""
|
"""
|
||||||
fields = SortedDictFromList([(f.name, f.formfield())
|
fields = SortedDict([(f.name, f.formfield())
|
||||||
for f in field_list if f.editable])
|
for f in field_list if f.editable])
|
||||||
return type('FormForFields', (BaseForm,), {'base_fields': fields})
|
return type('FormForFields', (BaseForm,), {'base_fields': fields})
|
||||||
|
|
||||||
class QuerySetIterator(object):
|
class QuerySetIterator(object):
|
||||||
@ -156,14 +157,22 @@ class ModelChoiceField(ChoiceField):
|
|||||||
def __init__(self, queryset, empty_label=u"---------", cache_choices=False,
|
def __init__(self, queryset, empty_label=u"---------", cache_choices=False,
|
||||||
required=True, widget=Select, label=None, initial=None,
|
required=True, widget=Select, label=None, initial=None,
|
||||||
help_text=None):
|
help_text=None):
|
||||||
self.queryset = queryset
|
|
||||||
self.empty_label = empty_label
|
self.empty_label = empty_label
|
||||||
self.cache_choices = cache_choices
|
self.cache_choices = cache_choices
|
||||||
# Call Field instead of ChoiceField __init__() because we don't need
|
# Call Field instead of ChoiceField __init__() because we don't need
|
||||||
# ChoiceField.__init__().
|
# ChoiceField.__init__().
|
||||||
Field.__init__(self, required, widget, label, initial, help_text)
|
Field.__init__(self, required, widget, label, initial, help_text)
|
||||||
|
self.queryset = queryset
|
||||||
|
|
||||||
|
def _get_queryset(self):
|
||||||
|
return self._queryset
|
||||||
|
|
||||||
|
def _set_queryset(self, queryset):
|
||||||
|
self._queryset = queryset
|
||||||
self.widget.choices = self.choices
|
self.widget.choices = self.choices
|
||||||
|
|
||||||
|
queryset = property(_get_queryset, _set_queryset)
|
||||||
|
|
||||||
def _get_choices(self):
|
def _get_choices(self):
|
||||||
# If self._choices is set, then somebody must have manually set
|
# If self._choices is set, then somebody must have manually set
|
||||||
# the property self.choices. In this case, just return self._choices.
|
# the property self.choices. In this case, just return self._choices.
|
||||||
@ -191,7 +200,7 @@ class ModelChoiceField(ChoiceField):
|
|||||||
if value in ('', None):
|
if value in ('', None):
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
value = self.queryset.model._default_manager.get(pk=value)
|
value = self.queryset.get(pk=value)
|
||||||
except self.queryset.model.DoesNotExist:
|
except self.queryset.model.DoesNotExist:
|
||||||
raise ValidationError(ugettext(u'Select a valid choice. That'
|
raise ValidationError(ugettext(u'Select a valid choice. That'
|
||||||
u' choice is not one of the'
|
u' choice is not one of the'
|
||||||
@ -218,7 +227,7 @@ class ModelMultipleChoiceField(ModelChoiceField):
|
|||||||
final_values = []
|
final_values = []
|
||||||
for val in value:
|
for val in value:
|
||||||
try:
|
try:
|
||||||
obj = self.queryset.model._default_manager.get(pk=val)
|
obj = self.queryset.get(pk=val)
|
||||||
except self.queryset.model.DoesNotExist:
|
except self.queryset.model.DoesNotExist:
|
||||||
raise ValidationError(ugettext(u'Select a valid choice. %s is'
|
raise ValidationError(ugettext(u'Select a valid choice. %s is'
|
||||||
u' not one of the available'
|
u' not one of the available'
|
||||||
|
@ -42,7 +42,7 @@ class ClientHandler(BaseHandler):
|
|||||||
# Apply response middleware
|
# Apply response middleware
|
||||||
for middleware_method in self._response_middleware:
|
for middleware_method in self._response_middleware:
|
||||||
response = middleware_method(request, response)
|
response = middleware_method(request, response)
|
||||||
|
response = self.apply_response_fixes(request, response)
|
||||||
finally:
|
finally:
|
||||||
dispatcher.send(signal=signals.request_finished)
|
dispatcher.send(signal=signals.request_finished)
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import re
|
import re
|
||||||
import unittest
|
import unittest
|
||||||
from urlparse import urlsplit
|
from urlparse import urlsplit, urlunsplit
|
||||||
|
|
||||||
from django.http import QueryDict
|
from django.http import QueryDict
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
@ -74,7 +74,7 @@ class TestCase(unittest.TestCase):
|
|||||||
super(TestCase, self).__call__(result)
|
super(TestCase, self).__call__(result)
|
||||||
|
|
||||||
def assertRedirects(self, response, expected_url, status_code=302,
|
def assertRedirects(self, response, expected_url, status_code=302,
|
||||||
target_status_code=200):
|
target_status_code=200, host=None):
|
||||||
"""Asserts that a response redirected to a specific URL, and that the
|
"""Asserts that a response redirected to a specific URL, and that the
|
||||||
redirect URL can be loaded.
|
redirect URL can be loaded.
|
||||||
|
|
||||||
@ -86,6 +86,10 @@ class TestCase(unittest.TestCase):
|
|||||||
" (expected %d)" % (response.status_code, status_code)))
|
" (expected %d)" % (response.status_code, status_code)))
|
||||||
url = response['Location']
|
url = response['Location']
|
||||||
scheme, netloc, path, query, fragment = urlsplit(url)
|
scheme, netloc, path, query, fragment = urlsplit(url)
|
||||||
|
e_scheme, e_netloc, e_path, e_query, e_fragment = urlsplit(expected_url)
|
||||||
|
if not (e_scheme or e_netloc):
|
||||||
|
expected_url = urlunsplit(('http', host or 'testserver', e_path,
|
||||||
|
e_query, e_fragment))
|
||||||
self.assertEqual(url, expected_url,
|
self.assertEqual(url, expected_url,
|
||||||
"Response redirected to '%s', expected '%s'" % (url, expected_url))
|
"Response redirected to '%s', expected '%s'" % (url, expected_url))
|
||||||
|
|
||||||
|
@ -62,12 +62,10 @@ class SortedDict(dict):
|
|||||||
else:
|
else:
|
||||||
self.keyOrder = [key for key, value in data]
|
self.keyOrder = [key for key, value in data]
|
||||||
|
|
||||||
def __deepcopy__(self,memo):
|
def __deepcopy__(self, memo):
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
obj = self.__class__()
|
return self.__class__([(key, deepcopy(value, memo))
|
||||||
for k, v in self.items():
|
for key, value in self.iteritems()])
|
||||||
obj[k] = deepcopy(v, memo)
|
|
||||||
return obj
|
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key, value):
|
||||||
dict.__setitem__(self, key, value)
|
dict.__setitem__(self, key, value)
|
||||||
|
@ -47,7 +47,7 @@ Initialization
|
|||||||
==============
|
==============
|
||||||
|
|
||||||
To activate sitemap generation on your Django site, add this line to your
|
To activate sitemap generation on your Django site, add this line to your
|
||||||
URLconf_:
|
URLconf_::
|
||||||
|
|
||||||
(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})
|
(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})
|
||||||
|
|
||||||
|
@ -440,6 +440,8 @@ the data in the database when the form is instantiated.
|
|||||||
>>> from django.newforms import ModelChoiceField, ModelMultipleChoiceField
|
>>> from django.newforms import ModelChoiceField, ModelMultipleChoiceField
|
||||||
|
|
||||||
>>> f = ModelChoiceField(Category.objects.all())
|
>>> f = ModelChoiceField(Category.objects.all())
|
||||||
|
>>> list(f.choices)
|
||||||
|
[(u'', u'---------'), (1, u'Entertainment'), (2, u"It's a test"), (3, u'Third'), (4, u'Fourth')]
|
||||||
>>> f.clean('')
|
>>> f.clean('')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
@ -485,9 +487,23 @@ Traceback (most recent call last):
|
|||||||
...
|
...
|
||||||
ValidationError: [u'Select a valid choice. That choice is not one of the available choices.']
|
ValidationError: [u'Select a valid choice. That choice is not one of the available choices.']
|
||||||
|
|
||||||
|
# queryset can be changed after the field is created.
|
||||||
|
>>> f.queryset = Category.objects.exclude(name='Fourth')
|
||||||
|
>>> list(f.choices)
|
||||||
|
[(u'', u'---------'), (1, u'Entertainment'), (2, u"It's a test"), (3, u'Third')]
|
||||||
|
>>> f.clean(3)
|
||||||
|
<Category: Third>
|
||||||
|
>>> f.clean(4)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Select a valid choice. That choice is not one of the available choices.']
|
||||||
|
|
||||||
|
|
||||||
# ModelMultipleChoiceField ####################################################
|
# ModelMultipleChoiceField ####################################################
|
||||||
|
|
||||||
>>> f = ModelMultipleChoiceField(Category.objects.all())
|
>>> f = ModelMultipleChoiceField(Category.objects.all())
|
||||||
|
>>> list(f.choices)
|
||||||
|
[(1, u'Entertainment'), (2, u"It's a test"), (3, u'Third'), (4, u'Fourth')]
|
||||||
>>> f.clean(None)
|
>>> f.clean(None)
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
@ -517,7 +533,7 @@ Traceback (most recent call last):
|
|||||||
...
|
...
|
||||||
ValidationError: [u'Enter a list of values.']
|
ValidationError: [u'Enter a list of values.']
|
||||||
|
|
||||||
# Add a Category object *after* the ModelChoiceField has already been
|
# Add a Category object *after* the ModelMultipleChoiceField has already been
|
||||||
# instantiated. This proves clean() checks the database during clean() rather
|
# instantiated. This proves clean() checks the database during clean() rather
|
||||||
# than caching it at time of instantiation.
|
# than caching it at time of instantiation.
|
||||||
>>> Category.objects.create(id=6, name='Sixth', url='6th')
|
>>> Category.objects.create(id=6, name='Sixth', url='6th')
|
||||||
@ -525,7 +541,7 @@ ValidationError: [u'Enter a list of values.']
|
|||||||
>>> f.clean([6])
|
>>> f.clean([6])
|
||||||
[<Category: Sixth>]
|
[<Category: Sixth>]
|
||||||
|
|
||||||
# Delete a Category object *after* the ModelChoiceField has already been
|
# Delete a Category object *after* the ModelMultipleChoiceField has already been
|
||||||
# instantiated. This proves clean() checks the database during clean() rather
|
# instantiated. This proves clean() checks the database during clean() rather
|
||||||
# than caching it at time of instantiation.
|
# than caching it at time of instantiation.
|
||||||
>>> Category.objects.get(url='6th').delete()
|
>>> Category.objects.get(url='6th').delete()
|
||||||
@ -552,6 +568,22 @@ Traceback (most recent call last):
|
|||||||
...
|
...
|
||||||
ValidationError: [u'Select a valid choice. 10 is not one of the available choices.']
|
ValidationError: [u'Select a valid choice. 10 is not one of the available choices.']
|
||||||
|
|
||||||
|
# queryset can be changed after the field is created.
|
||||||
|
>>> f.queryset = Category.objects.exclude(name='Fourth')
|
||||||
|
>>> list(f.choices)
|
||||||
|
[(1, u'Entertainment'), (2, u"It's a test"), (3, u'Third')]
|
||||||
|
>>> f.clean([3])
|
||||||
|
[<Category: Third>]
|
||||||
|
>>> f.clean([4])
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Select a valid choice. 4 is not one of the available choices.']
|
||||||
|
>>> f.clean(['3', '4'])
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Select a valid choice. 4 is not one of the available choices.']
|
||||||
|
|
||||||
|
|
||||||
# PhoneNumberField ############################################################
|
# PhoneNumberField ############################################################
|
||||||
|
|
||||||
>>> PhoneNumberForm = form_for_model(PhoneNumber)
|
>>> PhoneNumberForm = form_for_model(PhoneNumber)
|
||||||
|
@ -83,13 +83,16 @@ class ClientTest(TestCase):
|
|||||||
def test_redirect(self):
|
def test_redirect(self):
|
||||||
"GET a URL that redirects elsewhere"
|
"GET a URL that redirects elsewhere"
|
||||||
response = self.client.get('/test_client/redirect_view/')
|
response = self.client.get('/test_client/redirect_view/')
|
||||||
# Check that the response was a 302 (redirect)
|
# Check that the response was a 302 (redirect) and that
|
||||||
self.assertRedirects(response, 'http://testserver/test_client/get_view/')
|
# assertRedirect() understands to put an implicit http://testserver/ in
|
||||||
|
# front of non-absolute URLs.
|
||||||
|
self.assertRedirects(response, '/test_client/get_view/')
|
||||||
|
|
||||||
client_providing_host = Client(HTTP_HOST='django.testserver')
|
host = 'django.testserver'
|
||||||
|
client_providing_host = Client(HTTP_HOST=host)
|
||||||
response = client_providing_host.get('/test_client/redirect_view/')
|
response = client_providing_host.get('/test_client/redirect_view/')
|
||||||
# Check that the response was a 302 (redirect) with absolute URI
|
# Check that the response was a 302 (redirect) with absolute URI
|
||||||
self.assertRedirects(response, 'http://django.testserver/test_client/get_view/')
|
self.assertRedirects(response, '/test_client/get_view/', host=host)
|
||||||
|
|
||||||
def test_redirect_with_query(self):
|
def test_redirect_with_query(self):
|
||||||
"GET a URL that redirects with given GET parameters"
|
"GET a URL that redirects with given GET parameters"
|
||||||
@ -250,6 +253,22 @@ class ClientTest(TestCase):
|
|||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEqual(response.context['user'].username, 'testclient')
|
self.assertEqual(response.context['user'].username, 'testclient')
|
||||||
|
|
||||||
|
def test_view_with_method_login(self):
|
||||||
|
"Request a page that is protected with a @login_required method"
|
||||||
|
|
||||||
|
# Get the page without logging in. Should result in 302.
|
||||||
|
response = self.client.get('/test_client/login_protected_method_view/')
|
||||||
|
self.assertRedirects(response, 'http://testserver/accounts/login/?next=/test_client/login_protected_method_view/')
|
||||||
|
|
||||||
|
# Log in
|
||||||
|
login = self.client.login(username='testclient', password='password')
|
||||||
|
self.failUnless(login, 'Could not log in')
|
||||||
|
|
||||||
|
# Request a page that requires a login
|
||||||
|
response = self.client.get('/test_client/login_protected_method_view/')
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(response.context['user'].username, 'testclient')
|
||||||
|
|
||||||
def test_view_with_login_and_custom_redirect(self):
|
def test_view_with_login_and_custom_redirect(self):
|
||||||
"Request a page that is protected with @login_required(redirect_field_name='redirect_to')"
|
"Request a page that is protected with @login_required(redirect_field_name='redirect_to')"
|
||||||
|
|
||||||
@ -295,6 +314,40 @@ class ClientTest(TestCase):
|
|||||||
response = self.client.get('/test_client/login_protected_view/')
|
response = self.client.get('/test_client/login_protected_view/')
|
||||||
self.assertRedirects(response, 'http://testserver/accounts/login/?next=/test_client/login_protected_view/')
|
self.assertRedirects(response, 'http://testserver/accounts/login/?next=/test_client/login_protected_view/')
|
||||||
|
|
||||||
|
def test_view_with_permissions(self):
|
||||||
|
"Request a page that is protected with @permission_required"
|
||||||
|
|
||||||
|
# Get the page without logging in. Should result in 302.
|
||||||
|
response = self.client.get('/test_client/permission_protected_view/')
|
||||||
|
self.assertRedirects(response, 'http://testserver/accounts/login/?next=/test_client/permission_protected_view/')
|
||||||
|
|
||||||
|
# Log in
|
||||||
|
login = self.client.login(username='testclient', password='password')
|
||||||
|
self.failUnless(login, 'Could not log in')
|
||||||
|
|
||||||
|
# Log in with wrong permissions. Should result in 302.
|
||||||
|
response = self.client.get('/test_client/permission_protected_view/')
|
||||||
|
self.assertRedirects(response, 'http://testserver/accounts/login/?next=/test_client/permission_protected_view/')
|
||||||
|
|
||||||
|
# TODO: Log in with right permissions and request the page again
|
||||||
|
|
||||||
|
def test_view_with_method_permissions(self):
|
||||||
|
"Request a page that is protected with a @permission_required method"
|
||||||
|
|
||||||
|
# Get the page without logging in. Should result in 302.
|
||||||
|
response = self.client.get('/test_client/permission_protected_method_view/')
|
||||||
|
self.assertRedirects(response, 'http://testserver/accounts/login/?next=/test_client/permission_protected_method_view/')
|
||||||
|
|
||||||
|
# Log in
|
||||||
|
login = self.client.login(username='testclient', password='password')
|
||||||
|
self.failUnless(login, 'Could not log in')
|
||||||
|
|
||||||
|
# Log in with wrong permissions. Should result in 302.
|
||||||
|
response = self.client.get('/test_client/permission_protected_method_view/')
|
||||||
|
self.assertRedirects(response, 'http://testserver/accounts/login/?next=/test_client/permission_protected_method_view/')
|
||||||
|
|
||||||
|
# TODO: Log in with right permissions and request the page again
|
||||||
|
|
||||||
def test_session_modifying_view(self):
|
def test_session_modifying_view(self):
|
||||||
"Request a page that modifies the session"
|
"Request a page that modifies the session"
|
||||||
# Session value isn't set initially
|
# Session value isn't set initially
|
||||||
|
@ -13,7 +13,10 @@ urlpatterns = patterns('',
|
|||||||
(r'^form_view/$', views.form_view),
|
(r'^form_view/$', views.form_view),
|
||||||
(r'^form_view_with_template/$', views.form_view_with_template),
|
(r'^form_view_with_template/$', views.form_view_with_template),
|
||||||
(r'^login_protected_view/$', views.login_protected_view),
|
(r'^login_protected_view/$', views.login_protected_view),
|
||||||
|
(r'^login_protected_method_view/$', views.login_protected_method_view),
|
||||||
(r'^login_protected_view_custom_redirect/$', views.login_protected_view_changed_redirect),
|
(r'^login_protected_view_custom_redirect/$', views.login_protected_view_changed_redirect),
|
||||||
|
(r'^permission_protected_view/$', views.permission_protected_view),
|
||||||
|
(r'^permission_protected_method_view/$', views.permission_protected_method_view),
|
||||||
(r'^session_view/$', views.session_view),
|
(r'^session_view/$', views.session_view),
|
||||||
(r'^broken_view/$', views.broken_view),
|
(r'^broken_view/$', views.broken_view),
|
||||||
(r'^mail_sending_view/$', views.mail_sending_view),
|
(r'^mail_sending_view/$', views.mail_sending_view),
|
||||||
|
@ -3,7 +3,7 @@ from xml.dom.minidom import parseString
|
|||||||
from django.core.mail import EmailMessage, SMTPConnection
|
from django.core.mail import EmailMessage, SMTPConnection
|
||||||
from django.template import Context, Template
|
from django.template import Context, Template
|
||||||
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseNotFound
|
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseNotFound
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required, permission_required
|
||||||
from django.newforms.forms import Form
|
from django.newforms.forms import Form
|
||||||
from django.newforms import fields
|
from django.newforms import fields
|
||||||
from django.shortcuts import render_to_response
|
from django.shortcuts import render_to_response
|
||||||
@ -130,6 +130,38 @@ def login_protected_view_changed_redirect(request):
|
|||||||
return HttpResponse(t.render(c))
|
return HttpResponse(t.render(c))
|
||||||
login_protected_view_changed_redirect = login_required(redirect_field_name="redirect_to")(login_protected_view_changed_redirect)
|
login_protected_view_changed_redirect = login_required(redirect_field_name="redirect_to")(login_protected_view_changed_redirect)
|
||||||
|
|
||||||
|
def permission_protected_view(request):
|
||||||
|
"A simple view that is permission protected."
|
||||||
|
t = Template('This is a permission protected test. '
|
||||||
|
'Username is {{ user.username }}. '
|
||||||
|
'Permissions are {{ user.get_all_permissions }}.' ,
|
||||||
|
name='Permissions Template')
|
||||||
|
c = Context({'user': request.user})
|
||||||
|
return HttpResponse(t.render(c))
|
||||||
|
permission_protected_view = permission_required('modeltests.test_perm')(permission_protected_view)
|
||||||
|
|
||||||
|
class _ViewManager(object):
|
||||||
|
def login_protected_view(self, request):
|
||||||
|
t = Template('This is a login protected test using a method. '
|
||||||
|
'Username is {{ user.username }}.',
|
||||||
|
name='Login Method Template')
|
||||||
|
c = Context({'user': request.user})
|
||||||
|
return HttpResponse(t.render(c))
|
||||||
|
login_protected_view = login_required(login_protected_view)
|
||||||
|
|
||||||
|
def permission_protected_view(self, request):
|
||||||
|
t = Template('This is a permission protected test using a method. '
|
||||||
|
'Username is {{ user.username }}. '
|
||||||
|
'Permissions are {{ user.get_all_permissions }}.' ,
|
||||||
|
name='Permissions Template')
|
||||||
|
c = Context({'user': request.user})
|
||||||
|
return HttpResponse(t.render(c))
|
||||||
|
permission_protected_view = permission_required('modeltests.test_perm')(permission_protected_view)
|
||||||
|
|
||||||
|
_view_manager = _ViewManager()
|
||||||
|
login_protected_method_view = _view_manager.login_protected_view
|
||||||
|
permission_protected_method_view = _view_manager.permission_protected_view
|
||||||
|
|
||||||
def session_view(request):
|
def session_view(request):
|
||||||
"A view that modifies the session"
|
"A view that modifies the session"
|
||||||
request.session['tobacconist'] = 'hovercraft'
|
request.session['tobacconist'] = 'hovercraft'
|
||||||
|
@ -119,7 +119,7 @@ class AssertRedirectsTests(TestCase):
|
|||||||
try:
|
try:
|
||||||
self.assertRedirects(response, '/test_client/get_view/')
|
self.assertRedirects(response, '/test_client/get_view/')
|
||||||
except AssertionError, e:
|
except AssertionError, e:
|
||||||
self.assertEquals(str(e), "Response redirected to 'http://testserver/test_client/get_view/?var=value', expected '/test_client/get_view/'")
|
self.assertEquals(str(e), "Response redirected to 'http://testserver/test_client/get_view/?var=value', expected 'http://testserver/test_client/get_view/'")
|
||||||
|
|
||||||
def test_incorrect_target(self):
|
def test_incorrect_target(self):
|
||||||
"An assertion is raised if the response redirects to another target"
|
"An assertion is raised if the response redirects to another target"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user