From 0c341d780ebcde0e81c81eda07e2db3aaa92549b Mon Sep 17 00:00:00 2001 From: Joseph Kocherhans Date: Fri, 2 Jun 2006 03:51:30 +0000 Subject: [PATCH] multi-auth: Merged to [3051] git-svn-id: http://code.djangoproject.com/svn/django/branches/multi-auth@3052 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- AUTHORS | 1 + django/conf/global_settings.py | 1 + django/conf/locale/el/LC_MESSAGES/django.mo | Bin 8192 -> 8187 bytes django/conf/locale/el/LC_MESSAGES/django.po | 2 +- django/conf/locale/fr/LC_MESSAGES/django.mo | Bin 32183 -> 32926 bytes django/conf/locale/fr/LC_MESSAGES/django.po | 394 +++++++++--------- django/conf/project_template/manage.py | 2 +- django/contrib/admin/media/css/login.css | 6 +- .../admin/media/img/admin/icon-unknown.gif | Bin 0 -> 130 bytes .../admin/templates/admin/change_form.html | 2 +- .../admin/templates/admin/field_line.html | 8 +- .../contrib/admin/templates/admin/filter.html | 2 +- .../admin/templates/admin/invalid_setup.html | 10 + .../admin/templates/admin/object_history.html | 4 +- .../admin/templatetags/adminapplist.py | 7 +- django/contrib/admin/urls.py | 2 +- django/contrib/admin/views/main.py | 19 +- django/contrib/auth/handlers/modpython.py | 7 +- django/contrib/auth/models.py | 2 +- django/contrib/sessions/middleware.py | 9 +- django/core/management.py | 4 +- django/core/paginator.py | 20 + django/core/validators.py | 19 +- .../db/backends/postgresql/introspection.py | 19 +- .../postgresql_psycopg2/introspection.py | 19 +- django/db/backends/util.py | 4 + django/db/models/base.py | 8 +- django/db/models/fields/__init__.py | 22 +- django/db/models/query.py | 10 +- django/db/transaction.py | 5 +- django/forms/__init__.py | 8 +- django/shortcuts/__init__.py | 8 +- django/utils/_threading_local.py | 5 +- django/utils/autoreload.py | 7 +- django/utils/feedgenerator.py | 2 +- django/views/generic/date_based.py | 35 +- django/views/generic/list_detail.py | 10 +- docs/apache_auth.txt | 9 + docs/generic_views.txt | 47 +++ docs/model-api.txt | 12 +- docs/sessions.txt | 24 ++ docs/settings.txt | 8 + docs/tutorial02.txt | 2 +- docs/tutorial03.txt | 6 +- tests/modeltests/custom_managers/models.py | 9 +- tests/modeltests/custom_methods/models.py | 2 +- tests/modeltests/field_defaults/models.py | 17 +- tests/modeltests/invalid_models/models.py | 24 +- tests/modeltests/m2m_and_m2o/models.py | 24 +- tests/modeltests/m2m_recursive/models.py | 30 +- tests/modeltests/manipulators/models.py | 4 +- tests/modeltests/model_inheritance/models.py | 1 + tests/modeltests/pagination/models.py | 34 +- tests/modeltests/properties/models.py | 2 + tests/modeltests/transactions/models.py | 2 +- tests/modeltests/validation/models.py | 4 +- 56 files changed, 582 insertions(+), 362 deletions(-) create mode 100644 django/contrib/admin/media/img/admin/icon-unknown.gif create mode 100644 django/contrib/admin/templates/admin/invalid_setup.html diff --git a/AUTHORS b/AUTHORS index b1b4e6b977..970b0d90a2 100644 --- a/AUTHORS +++ b/AUTHORS @@ -45,6 +45,7 @@ answer newbie questions, and generally made Django that much better: andy@jadedplanet.net Antonio Cavedoni C8E + Chris Chamberlin Amit Chakradeo ChaosKCW Ian Clelland diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index 0090f79b97..332af199f8 100644 --- a/django/conf/global_settings.py +++ b/django/conf/global_settings.py @@ -235,6 +235,7 @@ SESSION_COOKIE_NAME = 'sessionid' # Cookie name. This can be whatever yo SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2 # Age of cookie, in seconds (default: 2 weeks). SESSION_COOKIE_DOMAIN = None # A string like ".lawrence.com", or None for standard domain cookie. SESSION_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. ######### # CACHE # diff --git a/django/conf/locale/el/LC_MESSAGES/django.mo b/django/conf/locale/el/LC_MESSAGES/django.mo index 915c768de76f20ede9c2323488056f3a968f49e0..1c95d6b5d96d31469d7322c5f7a8fe0cca3c8b25 100644 GIT binary patch delta 690 zcmXZZODM!q6vy!&c|=|_jK_F4B6)4hu<atUWfeAU0~K z!AdAQ3-%;6Sty0oq9^tjkRTa`ShOixPkR$bl5zMArK2;P=qX!m29>Fm( zbzPt-aF5IQjMUZ$^EW?)(1&w4id%SvFSv)hT)Pni)%N$(=qC?jo!LiI?;6d-tp|c8 zf?qV9R@d00??jr`Fq)Bt(fqK1)KVNxV~@ydDCy)W$KqOhzr}IDamsPQ)~gr+&!j_a Z!33H{-s3}=2p_Qz%RJH)2C)IJkt6km%a}^He5wsJjUHJ9sRXCU z)D=Th;0_n?38}3S=5KxoVg=6P2ySBxpK%xWxpoz{7uny>pr1U1rDh*Zy(=^mx9$n5 z34YOZT3&3Az71(w!)QhpLi58qQcLG(8hb=uL+?(m5_>)0vB|ODaoTaw)~hW7o=L}8 ehZkrXd5aDtj-{l8=4O4Zt^PJ&bM!D{GV>oWi%*0A diff --git a/django/conf/locale/el/LC_MESSAGES/django.po b/django/conf/locale/el/LC_MESSAGES/django.po index fb65fa561d..173b300d04 100644 --- a/django/conf/locale/el/LC_MESSAGES/django.po +++ b/django/conf/locale/el/LC_MESSAGES/django.po @@ -13,7 +13,7 @@ msgstr "" "Last-Translator: panos laganakos \n" "Language-Team: Greek\n" "MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=iso-8859-1\n" +"Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" #: contrib/comments/models.py:67 contrib/comments/models.py:166 diff --git a/django/conf/locale/fr/LC_MESSAGES/django.mo b/django/conf/locale/fr/LC_MESSAGES/django.mo index b6baa932c2ed81ddeee2ac062c0fb0fd66068cd6..38d7fab7bb37167e626629dec5f7788043d106de 100644 GIT binary patch delta 11682 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 delta 11060 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 diff --git a/django/conf/locale/fr/LC_MESSAGES/django.po b/django/conf/locale/fr/LC_MESSAGES/django.po index f3843dfe8e..acb86bf6ff 100644 --- a/django/conf/locale/fr/LC_MESSAGES/django.po +++ b/django/conf/locale/fr/LC_MESSAGES/django.po @@ -9,8 +9,8 @@ msgstr "" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2006-05-16 10:11+0200\n" "PO-Revision-Date: 2006-05-08 15:12+0200\n" -"Last-Translator: Gaël Chardon \n" -"Language-Team: français \n" +"Last-Translator: Gaël Chardon \n" +"Language-Team: français \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ISO-8859-1\n" "Content-Transfer-Encoding: 8bit\n" @@ -30,35 +30,35 @@ msgstr "commentaire" #: contrib/comments/models.py:70 msgid "rating #1" -msgstr "vote n°1" +msgstr "vote n°1" #: contrib/comments/models.py:71 msgid "rating #2" -msgstr "vote n°2" +msgstr "vote n°2" #: contrib/comments/models.py:72 msgid "rating #3" -msgstr "vote n°3" +msgstr "vote n°3" #: contrib/comments/models.py:73 msgid "rating #4" -msgstr "vote n°4" +msgstr "vote n°4" #: contrib/comments/models.py:74 msgid "rating #5" -msgstr "vote n°5" +msgstr "vote n°5" #: contrib/comments/models.py:75 msgid "rating #6" -msgstr "vote n°6" +msgstr "vote n°6" #: contrib/comments/models.py:76 msgid "rating #7" -msgstr "vote n°7" +msgstr "vote n°7" #: contrib/comments/models.py:77 msgid "rating #8" -msgstr "vote n°8" +msgstr "vote n°8" #: contrib/comments/models.py:82 msgid "is valid rating" @@ -78,15 +78,15 @@ msgstr "adresse IP" #: contrib/comments/models.py:86 msgid "is removed" -msgstr "est supprimé" +msgstr "est supprimé" #: contrib/comments/models.py:86 msgid "" "Check this box if the comment is inappropriate. A \"This comment has been " "removed\" message will be displayed instead." msgstr "" -"Cochez cette case si le commentaire est inadéquat. Un message type \"Ce " -"commentaire a été supprimé\" sera affiché en lieu et place de celui-ci." +"Cochez cette case si le commentaire est inadéquat. Un message type \"Ce " +"commentaire a été supprimé\" sera affiché en lieu et place de celui-ci." #: contrib/comments/models.py:91 msgid "comments" @@ -105,7 +105,7 @@ msgid "" "\n" "http://%(domain)s%(url)s" msgstr "" -"Posté par %(user)s à %(date)s\n" +"Posté par %(user)s à %(date)s\n" "\n" "%(comment)s\n" "\n" @@ -121,7 +121,7 @@ msgstr "adresse IP" #: contrib/comments/models.py:173 msgid "approved by staff" -msgstr "approuvé par l'équipe" +msgstr "approuvé par l'équipe" #: contrib/comments/models.py:176 msgid "free comment" @@ -137,7 +137,7 @@ msgstr "evaluation" #: contrib/comments/models.py:234 msgid "score date" -msgstr "date d'évaluation" +msgstr "date d'évaluation" #: contrib/comments/models.py:237 msgid "karma score" @@ -150,7 +150,7 @@ msgstr "points de Karma" #: contrib/comments/models.py:242 #, python-format msgid "%(score)d rating by %(user)s" -msgstr "%(score)d évalué par %(user)s" +msgstr "%(score)d évalué par %(user)s" #: contrib/comments/models.py:258 #, python-format @@ -159,7 +159,7 @@ msgid "" "\n" "%(text)s" msgstr "" -"Ce commentaire a été marqué par %(user)s:\n" +"Ce commentaire a été marqué par %(user)s:\n" "\n" "%(text)s" @@ -186,16 +186,16 @@ msgstr "date de suppression" #: contrib/comments/models.py:280 msgid "moderator deletion" -msgstr "suppression de modérateur" +msgstr "suppression de modérateur" #: contrib/comments/models.py:281 msgid "moderator deletions" -msgstr "suppressions de modérateur" +msgstr "suppressions de modérateur" #: contrib/comments/models.py:285 #, python-format msgid "Moderator deletion by %r" -msgstr "Suppression de modérateur par %r" +msgstr "Suppression de modérateur par %r" #: contrib/comments/views/karma.py:19 msgid "Anonymous users cannot vote" @@ -207,13 +207,13 @@ msgstr "ID de commentaire invalide" #: contrib/comments/views/karma.py:25 msgid "No voting for yourself" -msgstr "Impossible de voter pour soi-même" +msgstr "Impossible de voter pour soi-même" #: contrib/comments/views/comments.py:28 msgid "" "This rating is required because you've entered at least one other rating." msgstr "" -"Ce votre est nécéssaire parceque vous avez saisi au moins un autre vote." +"Ce votre est nécéssaire parceque vous avez saisi au moins un autre vote." #: contrib/comments/views/comments.py:112 #, python-format @@ -228,12 +228,12 @@ msgid_plural "" "\n" "%(text)s" msgstr[0] "" -"Ce commentaire a été posté par un utilisateur qui a posté moins de %(count)s " +"Ce commentaire a été posté par un utilisateur qui a posté moins de %(count)s " "commentaire :\n" "\n" "%(text)s" msgstr[1] "" -"Ce commentaire a été posté par un utilisateur qui a posté moins de %(count)s " +"Ce commentaire a été posté par un utilisateur qui a posté moins de %(count)s " "commentaires :\n" "\n" "%(text)s" @@ -245,26 +245,26 @@ msgid "" "\n" "%(text)s" msgstr "" -"Ce commentaire a été posté par un utilisateur imprécis :\n" +"Ce commentaire a été posté par un utilisateur imprécis :\n" "\n" "%(text)s" #: contrib/comments/views/comments.py:189 #: contrib/comments/views/comments.py:280 msgid "Only POSTs are allowed" -msgstr "Seuls les POSTs sont autorisés" +msgstr "Seuls les POSTs sont autorisés" #: contrib/comments/views/comments.py:193 #: contrib/comments/views/comments.py:284 msgid "One or more of the required fields wasn't submitted" -msgstr "Un ou plusieurs champs requis n'ont pas été remplis" +msgstr "Un ou plusieurs champs requis n'ont pas été remplis" #: contrib/comments/views/comments.py:197 #: contrib/comments/views/comments.py:286 msgid "Somebody tampered with the comment form (security violation)" msgstr "" -"Quelqu'un a trafiqué le formulaire de commentaire (violation des règles de " -"sécurité)" +"Quelqu'un a trafiqué le formulaire de commentaire (violation des règles de " +"sécurité)" #: contrib/comments/views/comments.py:207 #: contrib/comments/views/comments.py:292 @@ -272,14 +272,14 @@ msgid "" "The comment form had an invalid 'target' parameter -- the object ID was " "invalid" msgstr "" -"Ce formulaire de commentaire avait un paramètre cible invalide ; l'ID de " -"l'objet était invalide" +"Ce formulaire de commentaire avait un paramètre cible invalide ; l'ID de " +"l'objet était invalide" #: contrib/comments/views/comments.py:257 #: contrib/comments/views/comments.py:321 msgid "The comment form didn't provide either 'preview' or 'post'" msgstr "" -"Le formulaire de commentaire ne proposait ni les options de prévisualisation " +"Le formulaire de commentaire ne proposait ni les options de prévisualisation " "ni d'envoi" #: contrib/comments/templates/comments/form.html:6 @@ -295,7 +295,7 @@ msgstr "Mot de passe" #: contrib/comments/templates/comments/form.html:6 msgid "Forgotten your password?" -msgstr "Mot de passe oublié?" +msgstr "Mot de passe oublié?" #: contrib/comments/templates/comments/form.html:8 #: contrib/admin/templates/admin/object_history.html:3 @@ -316,12 +316,11 @@ msgstr "Mot de passe oubli #: contrib/admin/templates/admin_doc/index.html:4 #: contrib/admin/templates/admin_doc/model_index.html:5 msgid "Log out" -msgstr "Déconnexion" +msgstr "Déconnexion" #: contrib/comments/templates/comments/form.html:12 -#, fuzzy msgid "Ratings" -msgstr "vote n°1" +msgstr "Votes" #: contrib/comments/templates/comments/form.html:12 #: contrib/comments/templates/comments/form.html:23 @@ -345,7 +344,7 @@ msgstr "Commentaire :" #: contrib/comments/templates/comments/form.html:32 #: contrib/comments/templates/comments/freeform.html:9 msgid "Preview comment" -msgstr "Prévisualisation du commentaire" +msgstr "Prévisualisation du commentaire" #: contrib/comments/templates/comments/freeform.html:4 msgid "Your name:" @@ -383,7 +382,7 @@ msgstr "Ce mois-ci" #: contrib/admin/filterspecs.py:117 msgid "This year" -msgstr "Cette année" +msgstr "Cette année" #: contrib/admin/filterspecs.py:143 msgid "Yes" @@ -407,7 +406,7 @@ msgstr "id de l'objet" #: contrib/admin/models.py:20 msgid "object repr" -msgstr "représentation de l'objet" +msgstr "représentation de l'objet" #: contrib/admin/models.py:21 msgid "action flag" @@ -419,11 +418,11 @@ msgstr "message de modification" #: contrib/admin/models.py:25 msgid "log entry" -msgstr "entrée d'historique" +msgstr "entrée d'historique" #: contrib/admin/models.py:26 msgid "log entries" -msgstr "entrées d'historique" +msgstr "entrées d'historique" #: contrib/admin/templatetags/admin_list.py:228 msgid "All dates" @@ -435,8 +434,8 @@ msgid "" "Please enter a correct username and password. Note that both fields are case-" "sensitive." msgstr "" -"Saisissez s'il vous plaît un nom d'utilisateur et un mot de passe valide. " -"Remarquez que chacun de ces champs est sensible à la casse (différenciation " +"Saisissez s'il vous plaît un nom d'utilisateur et un mot de passe valide. " +"Remarquez que chacun de ces champs est sensible à la casse (différenciation " "des majuscules/minuscules)." #: contrib/admin/views/decorators.py:23 @@ -449,8 +448,8 @@ msgid "" "Please log in again, because your session has expired. Don't worry: Your " "submission has been saved." msgstr "" -"Votre session a expiré, connectez-vous de nouveau s'il vous plaît. Ne vous " -"inquiétez pas, votre travail précédement éffectué a été sauvé." +"Votre session a expiré, connectez-vous de nouveau s'il vous plaît. Ne vous " +"inquiétez pas, votre travail précédement éffectué a été sauvé." #: contrib/admin/views/decorators.py:68 msgid "" @@ -458,17 +457,17 @@ msgid "" "cookies, reload this page, and try again." msgstr "" "Il semblerait que votre navigateur n'accepte pas les cookies. Activez-les, " -"rechargez cette page et rééssayez s'il vous plaît." +"rechargez cette page et rééssayez s'il vous plaît." #: contrib/admin/views/decorators.py:82 msgid "Usernames cannot contain the '@' character." -msgstr "Les noms d'utilisateur ne peuvent contenir le caractère '@'" +msgstr "Les noms d'utilisateur ne peuvent contenir le caractère '@'" #: contrib/admin/views/decorators.py:84 #, python-format msgid "Your e-mail address is not your username. Try '%s' instead." msgstr "" -"Votre courriel n'est pas votre nom d'utilisateur. Essayez '%s' à la place." +"Votre courriel n'est pas votre nom d'utilisateur. Essayez '%s' à la place." #: contrib/admin/views/main.py:226 msgid "Site administration" @@ -477,11 +476,11 @@ msgstr "Gestion du site" #: contrib/admin/views/main.py:260 #, python-format msgid "The %(name)s \"%(obj)s\" was added successfully." -msgstr "L'objet %(name)s \"%(obj)s\" a été ajouté avec succès." +msgstr "L'objet %(name)s \"%(obj)s\" a été ajouté avec succès." #: contrib/admin/views/main.py:264 contrib/admin/views/main.py:348 msgid "You may edit it again below." -msgstr "Vous pouvez continuez de l'éditez ci-dessous." +msgstr "Vous pouvez continuez de l'éditez ci-dessous." #: contrib/admin/views/main.py:272 contrib/admin/views/main.py:357 #, python-format @@ -496,7 +495,7 @@ msgstr "Ajouter %s" #: contrib/admin/views/main.py:336 #, python-format msgid "Added %s." -msgstr "Ajouté %s." +msgstr "Ajouté %s." #: contrib/admin/views/main.py:336 contrib/admin/views/main.py:338 #: contrib/admin/views/main.py:340 @@ -506,29 +505,29 @@ msgstr "et" #: contrib/admin/views/main.py:338 #, python-format msgid "Changed %s." -msgstr "Modifié %s." +msgstr "Modifié %s." #: contrib/admin/views/main.py:340 #, python-format msgid "Deleted %s." -msgstr "Supprimé %s." +msgstr "Supprimé %s." #: contrib/admin/views/main.py:343 msgid "No fields changed." -msgstr "Aucun champs modifié." +msgstr "Aucun champs modifié." #: contrib/admin/views/main.py:346 #, python-format msgid "The %(name)s \"%(obj)s\" was changed successfully." -msgstr "L'objet %(name)s \"%(obj)s\" a été modifié avec succès." +msgstr "L'objet %(name)s \"%(obj)s\" a été modifié avec succès." #: contrib/admin/views/main.py:354 #, python-format msgid "" "The %(name)s \"%(obj)s\" was added successfully. You may edit it again below." msgstr "" -"L'objet %(name)s \"%(obj)s\" a été ajouté avec succès.Vous pouvez continuez " -"de l'éditez ci-dessous." +"L'objet %(name)s \"%(obj)s\" a été ajouté avec succès.Vous pouvez continuez " +"de l'éditez ci-dessous." #: contrib/admin/views/main.py:392 #, python-format @@ -548,11 +547,11 @@ msgstr "Un ou plusieurs %(fieldname)s dans %(name)s:" #: contrib/admin/views/main.py:508 #, python-format msgid "The %(name)s \"%(obj)s\" was deleted successfully." -msgstr "L'objet %(name)s \"%(obj)s\" a été supprimé avec succès." +msgstr "L'objet %(name)s \"%(obj)s\" a été supprimé avec succès." #: contrib/admin/views/main.py:511 msgid "Are you sure?" -msgstr "Êtes-vous sûr ?" +msgstr "Êtes-vous sûr ?" #: contrib/admin/views/main.py:533 #, python-format @@ -562,12 +561,12 @@ msgstr "Historique des changements : %s" #: contrib/admin/views/main.py:565 #, python-format msgid "Select %s" -msgstr "Sélectionnez %s" +msgstr "Sélectionnez %s" #: contrib/admin/views/main.py:565 #, python-format msgid "Select %s to change" -msgstr "Sélectionnez %s pour changer" +msgstr "Sélectionnez %s pour changer" #: contrib/admin/views/doc.py:277 contrib/admin/views/doc.py:286 #: contrib/admin/views/doc.py:288 contrib/admin/views/doc.py:294 @@ -577,16 +576,16 @@ msgstr "Entier" #: contrib/admin/views/doc.py:278 msgid "Boolean (Either True or False)" -msgstr "Booléen (Vrai ou Faux)" +msgstr "Booléen (Vrai ou Faux)" #: contrib/admin/views/doc.py:279 contrib/admin/views/doc.py:296 #, python-format msgid "String (up to %(maxlength)s)" -msgstr "Chaîne de caractère (jusqu'à %(maxlength)s)" +msgstr "Chaîne de caractère (jusqu'à %(maxlength)s)" #: contrib/admin/views/doc.py:280 msgid "Comma-separated integers" -msgstr "Des entiers séparés par une virgule" +msgstr "Des entiers séparés par une virgule" #: contrib/admin/views/doc.py:281 msgid "Date (without time)" @@ -606,19 +605,19 @@ msgstr "Chemin vers le fichier" #: contrib/admin/views/doc.py:285 msgid "Decimal number" -msgstr "Nombre décimal" +msgstr "Nombre décimal" #: contrib/admin/views/doc.py:291 msgid "Boolean (Either True, False or None)" -msgstr "Booléen (Vrai, Faux ou None)" +msgstr "Booléen (Vrai, Faux ou None)" #: contrib/admin/views/doc.py:292 msgid "Relation to parent model" -msgstr "Relation au modèle parent" +msgstr "Relation au modèle parent" #: contrib/admin/views/doc.py:293 msgid "Phone number" -msgstr "Numéro de téléphone" +msgstr "Numéro de téléphone" #: contrib/admin/views/doc.py:298 msgid "Text" @@ -634,7 +633,7 @@ msgstr "URL" #: contrib/admin/views/doc.py:301 msgid "U.S. state (two uppercase letters)" -msgstr "État U.S. (deux lettres majuscules)" +msgstr "État U.S. (deux lettres majuscules)" #: contrib/admin/views/doc.py:302 msgid "XML text" @@ -712,8 +711,8 @@ msgid "" "This object doesn't have a change history. It probably wasn't added via this " "admin site." msgstr "" -"Cet objet n'a pas d'historique de modification. Il n'a probablement pas été " -"ajouté au moyen de ce site d'administration." +"Cet objet n'a pas d'historique de modification. Il n'a probablement pas été " +"ajouté au moyen de ce site d'administration." #: contrib/admin/templates/admin/base_site.html:4 msgid "Django site admin" @@ -740,23 +739,23 @@ 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 "" -"Une erreur est survenue. Elle a été transmise par courriel aux " -"administrateurs du site et sera corrigée dans les meilleurs délais. Merci " +"Une erreur est survenue. Elle a été transmise par courriel aux " +"administrateurs du site et sera corrigée dans les meilleurs délais. Merci " "pour votre patience." #: contrib/admin/templates/admin/404.html:4 #: contrib/admin/templates/admin/404.html:8 msgid "Page not found" -msgstr "Cette page n'a pas été trouvée" +msgstr "Cette page n'a pas été trouvée" #: contrib/admin/templates/admin/404.html:10 msgid "We're sorry, but the requested page could not be found." -msgstr "Nous sommes désolés, mais la page demandée est introuvable." +msgstr "Nous sommes désolés, mais la page demandée est introuvable." #: contrib/admin/templates/admin/index.html:17 #, python-format msgid "Models available in the %(name)s application." -msgstr "Modèles disponibles dans l'application %(name)s." +msgstr "Modèles disponibles dans l'application %(name)s." #: contrib/admin/templates/admin/index.html:28 #: contrib/admin/templates/admin/change_form.html:15 @@ -769,11 +768,11 @@ msgstr "Modifier" #: contrib/admin/templates/admin/index.html:44 msgid "You don't have permission to edit anything." -msgstr "Vous n'avez pas la permission d'éditer quoi que ce soit." +msgstr "Vous n'avez pas la permission d'éditer quoi que ce soit." #: contrib/admin/templates/admin/index.html:52 msgid "Recent Actions" -msgstr "Actions récentes" +msgstr "Actions récentes" #: contrib/admin/templates/admin/index.html:53 msgid "My Actions" @@ -809,7 +808,7 @@ msgid "" "types of objects:" msgstr "" "Supprimer l'objet %(object_name)s '%(object)s' provoquerait la suppression " -"des objets qui lui sont liés mais votre compte ne possède pas la permission " +"des objets qui lui sont liés mais votre compte ne possède pas la permission " "de supprimer les types d'objets suivants :" #: contrib/admin/templates/admin/delete_confirmation.html:21 @@ -818,8 +817,8 @@ msgid "" "Are you sure you want to delete the %(object_name)s \"%(object)s\"? All of " "the following related items will be deleted:" msgstr "" -"Êtes vous certain de vouloir supprimer l'objet %(object_name)s \"%(object)s" -"\" ? Les éléments suivant sont liés à celui-ci et seront aussi supprimés :" +"Êtes vous certain de vouloir supprimer l'objet %(object_name)s \"%(object)s" +"\" ? Les éléments suivant sont liés à celui-ci et seront aussi supprimés :" #: contrib/admin/templates/admin/delete_confirmation.html:26 msgid "Yes, I'm sure" @@ -878,18 +877,18 @@ msgstr "Modification de votre mot de passe" #: contrib/admin/templates/registration/password_change_done.html:6 #: contrib/admin/templates/registration/password_change_done.html:10 msgid "Password change successful" -msgstr "Mot de passe modifié avec succés" +msgstr "Mot de passe modifié avec succés" #: contrib/admin/templates/registration/password_change_done.html:12 msgid "Your password was changed." -msgstr "Votre mot de passe a été modifié." +msgstr "Votre mot de passe a été modifié." #: contrib/admin/templates/registration/password_reset_form.html:4 #: contrib/admin/templates/registration/password_reset_form.html:6 #: contrib/admin/templates/registration/password_reset_form.html:10 #: contrib/admin/templates/registration/password_reset_done.html:4 msgid "Password reset" -msgstr "Réinitialisation de votre mot de passe" +msgstr "Réinitialisation de votre mot de passe" #: contrib/admin/templates/registration/password_reset_form.html:12 msgid "" @@ -906,27 +905,27 @@ msgstr "Courriel :" #: contrib/admin/templates/registration/password_reset_form.html:16 msgid "Reset my password" -msgstr "Réinitialiser mon mot de passe" +msgstr "Réinitialiser mon mot de passe" #: contrib/admin/templates/registration/logged_out.html:8 msgid "Thanks for spending some quality time with the Web site today." -msgstr "Merci pour le temps que vous avez accordé à ce site aujourd'hui." +msgstr "Merci pour le temps que vous avez accordé à ce site aujourd'hui." #: contrib/admin/templates/registration/logged_out.html:10 msgid "Log in again" -msgstr "Connectez vous à nouveau" +msgstr "Connectez vous à nouveau" #: contrib/admin/templates/registration/password_reset_done.html:6 #: contrib/admin/templates/registration/password_reset_done.html:10 msgid "Password reset successful" -msgstr "Mot de passe réinitialisé avec succès" +msgstr "Mot de passe réinitialisé avec succès" #: 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 "" -"Nous vous avons envoyé par courriel un nouveau mot de passe. Vous devriez le " +"Nous vous avons envoyé par courriel un nouveau mot de passe. Vous devriez le " "recevoir rapidement." #: contrib/admin/templates/registration/password_change_form.html:12 @@ -934,9 +933,9 @@ 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 "" -"Pour des raisons de sécurité, veuillez entrer votre ancien mot de passe puis " +"Pour des raisons de sécurité, veuillez entrer votre ancien mot de passe puis " "saisissez deux fois votre nouveau mot de passe afin que nous puissions " -"vérifier que vous l'avez tapé correctement." +"vérifier que vous l'avez tapé correctement." #: contrib/admin/templates/registration/password_change_form.html:17 msgid "Old password:" @@ -957,7 +956,7 @@ msgstr "Modifier mon mot de passe" #: contrib/admin/templates/registration/password_reset_email.html:2 msgid "You're receiving this e-mail because you requested a password reset" msgstr "" -"Vous recevez ce courriel car vous avez demandé un changement de mot de passe" +"Vous recevez ce courriel car vous avez demandé un changement de mot de passe" #: contrib/admin/templates/registration/password_reset_email.html:3 #, python-format @@ -971,7 +970,7 @@ msgstr "Votre nouveau mot de passe est : %(new_password)s" #: contrib/admin/templates/registration/password_reset_email.html:7 msgid "Feel free to change this password by going to this page:" -msgstr "Vous pouvez modifier ce mot de passe à l'adresse suivante :" +msgstr "Vous pouvez modifier ce mot de passe à l'adresse suivante :" #: contrib/admin/templates/registration/password_reset_email.html:11 msgid "Your username, in case you've forgotten:" @@ -984,7 +983,7 @@ msgstr "Merci d'utiliser notre site !" #: contrib/admin/templates/registration/password_reset_email.html:15 #, python-format msgid "The %(site_name)s team" -msgstr "L'équipe %(site_name)s" +msgstr "L'équipe %(site_name)s" #: contrib/admin/templates/admin_doc/bookmarklets.html:3 msgid "Bookmarklets" @@ -1015,7 +1014,7 @@ msgid "" "that page." msgstr "" "Vous envoie de n'importe quelle page vers la documentation de la vue qui a " -"généré cette page." +"généré cette page." #: contrib/admin/templates/admin_doc/bookmarklets.html:22 msgid "Show object ID" @@ -1026,26 +1025,26 @@ msgid "" "Shows the content-type and unique ID for pages that represent a single " "object." msgstr "" -"Montre le content-type et l'ID unique pour les pages qui représente un objet " +"Montre le content-type et l'ID unique pour les pages qui représente un objet " "unique." #: contrib/admin/templates/admin_doc/bookmarklets.html:25 msgid "Edit this object (current window)" -msgstr "Editer cet objet (fenêtre courante)" +msgstr "Editer cet objet (fenêtre courante)" #: contrib/admin/templates/admin_doc/bookmarklets.html:26 msgid "Jumps to the admin page for pages that represent a single object." -msgstr "Renvoie à la page d'administration qui représente un objet seul." +msgstr "Renvoie à la page d'administration qui représente un objet seul." #: contrib/admin/templates/admin_doc/bookmarklets.html:28 msgid "Edit this object (new window)" -msgstr "Editer cet objet (nouvelle fenêtre)" +msgstr "Editer cet objet (nouvelle fenêtre)" #: contrib/admin/templates/admin_doc/bookmarklets.html:29 msgid "As above, but opens the admin page in a new window." msgstr "" "Comme ci-dessus, mais ouvre la page d'administration dans une nouvelle " -"fenêtre." +"fenêtre." #: contrib/admin/templates/widget/date_time.html:3 msgid "Date:" @@ -1065,27 +1064,27 @@ msgstr "Modification :" #: contrib/redirects/models.py:7 msgid "redirect from" -msgstr "redirigé depuis" +msgstr "redirigé depuis" #: contrib/redirects/models.py:8 msgid "" "This should be an absolute path, excluding the domain name. Example: '/" "events/search/'." msgstr "" -"Ceci doit être un chemin absolu, sans nom de domaine. Par exemple: '/events/" +"Ceci doit être un chemin absolu, sans nom de domaine. Par exemple: '/events/" "search/'." #: contrib/redirects/models.py:9 msgid "redirect to" -msgstr "redirigé vers" +msgstr "redirigé vers" #: contrib/redirects/models.py:10 msgid "" "This can be either an absolute path (as above) or a full URL starting with " "'http://'." msgstr "" -"Ceci peut être soit un chemin absolu (voir ci-dessus) soit une URL complète " -"débutant par 'http://'." +"Ceci peut être soit un chemin absolu (voir ci-dessus) soit une URL complète " +"débutant par 'http://'." #: contrib/redirects/models.py:12 msgid "redirect" @@ -1099,8 +1098,8 @@ msgstr "redirige" msgid "" "Example: '/about/contact/'. Make sure to have leading and trailing slashes." msgstr "" -"Par exemple : '/about/contact/'. Vérifiez la présence du caractère '/' en " -"début et en fin de chaine." +"Par exemple : '/about/contact/'. Vérifiez la présence du caractère '/' en " +"début et en fin de chaine." #: contrib/flatpages/models.py:9 msgid "title" @@ -1123,7 +1122,7 @@ msgid "" "Example: 'flatpages/contact_page'. If this isn't provided, the system will " "use 'flatpages/default'." msgstr "" -"Par exemple: 'flatfiles/contact_page'. Sans définition, le système utilisera " +"Par exemple: 'flatfiles/contact_page'. Sans définition, le système utilisera " "'flatfiles/default'." #: contrib/flatpages/models.py:14 @@ -1133,16 +1132,16 @@ msgstr "enregistrement requis" #: contrib/flatpages/models.py:14 msgid "If this is checked, only logged-in users will be able to view the page." msgstr "" -"Si coché, seuls les utilisateurs connectés auront la possibilité de voir " +"Si coché, seuls les utilisateurs connectés auront la possibilité de voir " "cette page." #: contrib/flatpages/models.py:18 msgid "flat page" -msgstr "page à plat" +msgstr "page à plat" #: contrib/flatpages/models.py:19 msgid "flat pages" -msgstr "pages à plat" +msgstr "pages à plat" #: contrib/auth/models.py:13 contrib/auth/models.py:26 msgid "name" @@ -1174,7 +1173,7 @@ msgstr "nom d'utilisateur" #: contrib/auth/models.py:56 msgid "first name" -msgstr "prénom" +msgstr "prénom" #: contrib/auth/models.py:57 msgid "last name" @@ -1194,11 +1193,11 @@ msgstr "Utilisez '[algo]$[salt]$[hexdigest]'" #: contrib/auth/models.py:60 msgid "staff status" -msgstr "statut équipe" +msgstr "statut équipe" #: contrib/auth/models.py:60 msgid "Designates whether the user can log into this admin site." -msgstr "Précise si l'utilisateur peut se connecter à ce site d'administration." +msgstr "Précise si l'utilisateur peut se connecter à ce site d'administration." #: contrib/auth/models.py:61 msgid "active" @@ -1210,7 +1209,7 @@ msgstr "statut super-utilisateur" #: contrib/auth/models.py:63 msgid "last login" -msgstr "dernière connexion" +msgstr "dernière connexion" #: contrib/auth/models.py:64 msgid "date joined" @@ -1221,7 +1220,7 @@ msgid "" "In addition to the permissions manually assigned, this user will also get " "all permissions granted to each group he/she is in." msgstr "" -"En plus des permissions qui lui sont manuellement assignées, cet utilisateur " +"En plus des permissions qui lui sont manuellement assignées, cet utilisateur " "recevra aussi toutes les permissions de tous les groupes auquels il " "appartient. " @@ -1262,8 +1261,8 @@ msgid "" "Your Web browser doesn't appear to have cookies enabled. Cookies are " "required for logging in." msgstr "" -"Votre navigateur ne semble pas avoir activé les cookies. Les cookies sont " -"nécessaire pour se connecter" +"Votre navigateur ne semble pas avoir activé les cookies. Les cookies sont " +"nécessaire pour se connecter" #: contrib/contenttypes/models.py:25 msgid "python model class name" @@ -1279,11 +1278,11 @@ msgstr "types de contenu" #: contrib/sessions/models.py:35 msgid "session key" -msgstr "clé de session" +msgstr "clé de session" #: contrib/sessions/models.py:36 msgid "session data" -msgstr "donnée de session" +msgstr "donnée de session" #: contrib/sessions/models.py:37 msgid "expire date" @@ -1303,7 +1302,7 @@ msgstr "nom de domaine" #: contrib/sites/models.py:11 msgid "display name" -msgstr "nom à afficher" +msgstr "nom à afficher" #: contrib/sites/models.py:15 msgid "site" @@ -1359,7 +1358,7 @@ msgstr "Janvier" #: utils/dates.py:14 msgid "February" -msgstr "Février" +msgstr "Février" #: utils/dates.py:14 utils/dates.py:27 msgid "March" @@ -1383,7 +1382,7 @@ msgstr "Juillet" #: utils/dates.py:15 msgid "August" -msgstr "Août" +msgstr "Août" #: utils/dates.py:15 msgid "September" @@ -1399,7 +1398,7 @@ msgstr "Novembre" #: utils/dates.py:16 msgid "December" -msgstr "Décembre" +msgstr "Décembre" #: utils/dates.py:19 msgid "jan" @@ -1407,7 +1406,7 @@ msgstr "jan" #: utils/dates.py:19 msgid "feb" -msgstr "fév" +msgstr "fév" #: utils/dates.py:19 msgid "mar" @@ -1447,7 +1446,7 @@ msgstr "nov" #: utils/dates.py:20 msgid "dec" -msgstr "déc" +msgstr "déc" #: utils/dates.py:27 msgid "Jan." @@ -1455,11 +1454,11 @@ msgstr "Jan." #: utils/dates.py:27 msgid "Feb." -msgstr "Fév." +msgstr "Fév." #: utils/dates.py:28 msgid "Aug." -msgstr "Aôut" +msgstr "Aôut" #: utils/dates.py:28 msgid "Sept." @@ -1475,16 +1474,15 @@ msgstr "Nov." #: utils/dates.py:28 msgid "Dec." -msgstr "Déc." +msgstr "Déc." #: utils/timesince.py:12 msgid "year" msgid_plural "years" -msgstr[0] "année" -msgstr[1] "années" +msgstr[0] "année" +msgstr[1] "années" #: utils/timesince.py:13 -#, fuzzy msgid "month" msgid_plural "months" msgstr[0] "mois" @@ -1499,7 +1497,7 @@ msgstr[1] "" #: utils/timesince.py:15 msgid "day" msgid_plural "days" -msgstr[0] "journée" +msgstr[0] "journée" msgstr[1] "jours" #: utils/timesince.py:16 @@ -1516,11 +1514,11 @@ msgstr[1] "minutes" #: conf/global_settings.py:37 msgid "Bengali" -msgstr "" +msgstr "Indien" #: conf/global_settings.py:38 msgid "Czech" -msgstr "Tchèque" +msgstr "Tchèque" #: conf/global_settings.py:39 msgid "Welsh" @@ -1536,7 +1534,7 @@ msgstr "Allemand" #: conf/global_settings.py:42 msgid "Greek" -msgstr "" +msgstr "Grec" #: conf/global_settings.py:43 msgid "English" @@ -1548,7 +1546,7 @@ msgstr "Espagnol" #: conf/global_settings.py:45 msgid "French" -msgstr "Français" +msgstr "Français" #: conf/global_settings.py:46 msgid "Galician" @@ -1556,11 +1554,11 @@ msgstr "Galicien" #: conf/global_settings.py:47 msgid "Hungarian" -msgstr "" +msgstr "Hongrois" #: conf/global_settings.py:48 msgid "Hebrew" -msgstr "" +msgstr "Israélien" #: conf/global_settings.py:49 msgid "Icelandic" @@ -1576,15 +1574,15 @@ msgstr "Japonais" #: conf/global_settings.py:52 msgid "Dutch" -msgstr "" +msgstr "Néerlandais" #: conf/global_settings.py:53 msgid "Norwegian" -msgstr "Norvégien" +msgstr "Norvégien" #: conf/global_settings.py:54 msgid "Brazilian" -msgstr "Brésilien" +msgstr "Brésilien" #: conf/global_settings.py:55 msgid "Romanian" @@ -1599,7 +1597,6 @@ msgid "Slovak" msgstr "Slovaque" #: conf/global_settings.py:58 -#, fuzzy msgid "Slovenian" msgstr "Slovaque" @@ -1609,7 +1606,7 @@ msgstr "Serbe" #: conf/global_settings.py:60 msgid "Swedish" -msgstr "Suédois" +msgstr "Suédois" #: conf/global_settings.py:61 msgid "Ukrainian" @@ -1617,7 +1614,7 @@ msgstr "Ukrainien" #: conf/global_settings.py:62 msgid "Simplified Chinese" -msgstr "Chinois simplifié" +msgstr "Chinois simplifié" #: conf/global_settings.py:63 msgid "Traditional Chinese" @@ -1639,19 +1636,19 @@ msgstr "" #: core/validators.py:72 msgid "Uppercase letters are not allowed here." -msgstr "Les lettres majuscules ne sont pas autorisées ici." +msgstr "Les lettres majuscules ne sont pas autorisées ici." #: core/validators.py:76 msgid "Lowercase letters are not allowed here." -msgstr "Les lettres minuscules ne sont pas autorisées ici." +msgstr "Les lettres minuscules ne sont pas autorisées ici." #: core/validators.py:83 msgid "Enter only digits separated by commas." -msgstr "Saisissez uniquement des chiffres séparés par des virgules." +msgstr "Saisissez uniquement des chiffres séparés par des virgules." #: core/validators.py:95 msgid "Enter valid e-mail addresses separated by commas." -msgstr "Entrez des adresses de courriel valides séparées par des virgules." +msgstr "Entrez des adresses de courriel valides séparées par des virgules." #: core/validators.py:99 msgid "Please enter a valid IP address." @@ -1663,11 +1660,11 @@ msgstr "Vous ne pouvez pas laisser ce champ vide." #: core/validators.py:107 msgid "Non-numeric characters aren't allowed here." -msgstr "Les caractères non numériques ne sont pas autorisés ici." +msgstr "Les caractères non numériques ne sont pas autorisés ici." #: core/validators.py:111 msgid "This value can't be comprised solely of digits." -msgstr "Cette valeur ne peut pas être composé uniquement de chiffres." +msgstr "Cette valeur ne peut pas être composé uniquement de chiffres." #: core/validators.py:116 msgid "Enter a whole number." @@ -1675,7 +1672,7 @@ msgstr "Entrez un nombre entier." #: core/validators.py:120 msgid "Only alphabetical characters are allowed here." -msgstr "Seules les lettres de l'alphabet sont autorisées ici." +msgstr "Seules les lettres de l'alphabet sont autorisées ici." #: core/validators.py:124 msgid "Enter a valid date in YYYY-MM-DD format." @@ -1698,7 +1695,7 @@ msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -"Envoyez une image valide. Le fichier que vous avez transferé n'est pas une " +"Envoyez une image valide. Le fichier que vous avez transferé n'est pas une " "image ou bien est une image corrompue." #: core/validators.py:155 @@ -1710,13 +1707,13 @@ msgstr "L'URL %s ne pointe pas vers une image valide." #, python-format msgid "Phone numbers must be in XXX-XXX-XXXX format. \"%s\" is invalid." msgstr "" -"Les numéros de téléphone doivent être au format XXX-XXX-XXXX. \"%s\" est " +"Les numéros de téléphone doivent être au format XXX-XXX-XXXX. \"%s\" est " "incorrect." #: core/validators.py:167 #, python-format msgid "The URL %s does not point to a valid QuickTime video." -msgstr "L'URL %s ne pointe pas vers une vidéo QuickTime valide." +msgstr "L'URL %s ne pointe pas vers une vidéo QuickTime valide." #: core/validators.py:171 msgid "A valid URL is required." @@ -1734,7 +1731,7 @@ msgstr "" #: core/validators.py:192 #, python-format msgid "Badly formed XML: %s" -msgstr "XML mal formé : %s" +msgstr "XML mal formé : %s" #: core/validators.py:202 #, python-format @@ -1744,18 +1741,18 @@ msgstr "URL invalide : %s" #: core/validators.py:206 core/validators.py:208 #, python-format msgid "The URL %s is a broken link." -msgstr "L'URL %s est un lien cassé." +msgstr "L'URL %s est un lien cassé." #: core/validators.py:214 msgid "Enter a valid U.S. state abbreviation." -msgstr "Entrez une abréviation d'état américain valide." +msgstr "Entrez une abréviation d'état américain valide." #: core/validators.py:229 #, python-format msgid "Watch your mouth! The word %s is not allowed here." msgid_plural "Watch your mouth! The words %s are not allowed here." -msgstr[0] "Attention à votre langage ! Le mot %s n'est pas autorisé ici." -msgstr[1] "Attention à votre langage ! Les mots %s ne sont pas autorisés ici." +msgstr[0] "Attention à votre langage ! Le mot %s n'est pas autorisé ici." +msgstr[1] "Attention à votre langage ! Les mots %s ne sont pas autorisés ici." #: core/validators.py:236 #, python-format @@ -1764,35 +1761,35 @@ msgstr "Ce champ doit correspondre au champ '%s'." #: core/validators.py:255 msgid "Please enter something for at least one field." -msgstr "Saisissez au moins une valeur dans un des champs s'il vous plaît." +msgstr "Saisissez au moins une valeur dans un des champs s'il vous plaît." #: core/validators.py:264 core/validators.py:275 msgid "Please enter both fields or leave them both empty." msgstr "" -"Renseignez chacun des champs ou laissez les deux vides s'il vous plaît." +"Renseignez chacun des champs ou laissez les deux vides s'il vous plaît." #: core/validators.py:282 #, python-format msgid "This field must be given if %(field)s is %(value)s" -msgstr "Ce champ doit être renseigné si %(field)s vaut %(value)s" +msgstr "Ce champ doit être renseigné si %(field)s vaut %(value)s" #: core/validators.py:294 #, python-format msgid "This field must be given if %(field)s is not %(value)s" -msgstr "Ce champ doit être renseigné si %(field)s ne vaut pas %(value)s" +msgstr "Ce champ doit être renseigné si %(field)s ne vaut pas %(value)s" #: core/validators.py:313 msgid "Duplicate values are not allowed." -msgstr "Des valeurs identiques ne sont pas autorisées." +msgstr "Des valeurs identiques ne sont pas autorisées." #: core/validators.py:336 #, python-format msgid "This value must be a power of %s." -msgstr "Cette valeur doit être une puissance de %s." +msgstr "Cette valeur doit être une puissance de %s." #: core/validators.py:347 msgid "Please enter a valid decimal number." -msgstr "Saisissez un nombre décimal valide s'il vous plaît." +msgstr "Saisissez un nombre décimal valide s'il vous plaît." #: core/validators.py:349 #, python-format @@ -1800,9 +1797,9 @@ msgid "Please enter a valid decimal number with at most %s total digit." msgid_plural "" "Please enter a valid decimal number with at most %s total digits." msgstr[0] "" -"Saisissez un nombre décimal valide avec au plus %s chiffre s'il vous plaît." +"Saisissez un nombre décimal valide avec au plus %s chiffre s'il vous plaît." msgstr[1] "" -"Saisissez un nombre décimal valide avec au plus %s chiffres s'il vous plaît." +"Saisissez un nombre décimal valide avec au plus %s chiffres s'il vous plaît." #: core/validators.py:352 #, python-format @@ -1810,21 +1807,21 @@ 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] "" -"Saisissez un nombre décimal valide avec au plus %s décimale s'il vous plaît" +"Saisissez un nombre décimal valide avec au plus %s décimale s'il vous plaît" msgstr[1] "" -"Saisissez un nombre décimal valide avec au plus %s décimales s'il vous plaît" +"Saisissez un nombre décimal valide avec au plus %s décimales s'il vous plaît" #: core/validators.py:362 #, python-format msgid "Make sure your uploaded file is at least %s bytes big." msgstr "" -"Vérifiez que le fichier transféré fait au moins une taille de %s octets." +"Vérifiez que le fichier transféré fait au moins une taille de %s octets." #: core/validators.py:363 #, python-format msgid "Make sure your uploaded file is at most %s bytes big." msgstr "" -"Vérifiez que le fichier transféré fait au plus une taille de %s octets." +"Vérifiez que le fichier transféré fait au plus une taille de %s octets." #: core/validators.py:376 msgid "The format for this field is wrong." @@ -1837,14 +1834,14 @@ msgstr "Ce champ est invalide." #: core/validators.py:426 #, python-format msgid "Could not retrieve anything from %s." -msgstr "Impossible de récupérer quoi que ce soit depuis %s." +msgstr "Impossible de récupérer quoi que ce soit depuis %s." #: core/validators.py:429 #, python-format msgid "" "The URL %(url)s returned the invalid Content-Type header '%(contenttype)s'." msgstr "" -"L'entête Content-Type '%(contenttype)s', renvoyée par l'url %(url)s n'est " +"L'entête Content-Type '%(contenttype)s', renvoyée par l'url %(url)s n'est " "pas valide." #: core/validators.py:462 @@ -1853,7 +1850,7 @@ msgid "" "Please close the unclosed %(tag)s tag from line %(line)s. (Line starts with " "\"%(start)s\".)" msgstr "" -"Veuillez fermer le tag %(tag)s de la ligne %(line)s. (La ligne débutant par " +"Veuillez fermer le tag %(tag)s de la ligne %(line)s. (La ligne débutant par " "\"%(start)s\".)" #: core/validators.py:466 @@ -1862,8 +1859,8 @@ msgid "" "Some text starting on line %(line)s is not allowed in that context. (Line " "starts with \"%(start)s\".)" msgstr "" -"Du texte commençant à la ligne %(line)s n'est pas autorisé dans ce contexte. " -"(Ligne débutant par \"%(start)s\".)" +"Du texte commençant à la ligne %(line)s n'est pas autorisé dans ce contexte. " +"(Ligne débutant par \"%(start)s\".)" #: core/validators.py:471 #, python-format @@ -1871,7 +1868,7 @@ msgid "" "\"%(attr)s\" on line %(line)s is an invalid attribute. (Line starts with \"%" "(start)s\".)" msgstr "" -"\"%(attr)s\" ligne %(line)s n'est pas un attribut valide. (Ligne débutant " +"\"%(attr)s\" ligne %(line)s n'est pas un attribut valide. (Ligne débutant " "par \"%(start)s\".)" #: core/validators.py:476 @@ -1880,7 +1877,7 @@ msgid "" "\"<%(tag)s>\" on line %(line)s is an invalid tag. (Line starts with \"%" "(start)s\".)" msgstr "" -"\"<%(tag)s>\" ligne %(line)s n'est pas un tag valide. (Ligne débutant par \"%" +"\"<%(tag)s>\" ligne %(line)s n'est pas un tag valide. (Ligne débutant par \"%" "(start)s\".)" #: core/validators.py:480 @@ -1890,7 +1887,7 @@ msgid "" "starts with \"%(start)s\".)" msgstr "" "Un tag, ou un ou plusieurs attributs, de la ligne %(line)s est manquant. " -"(Ligne débutant par \"%(start)s\".)" +"(Ligne débutant par \"%(start)s\".)" #: core/validators.py:485 #, python-format @@ -1899,7 +1896,7 @@ msgid "" "starts with \"%(start)s\".)" msgstr "" "La valeur de l'attribut \"%(attr)s\" de la ligne %(line)s n'est pas valide. " -"(Ligne débutant par \"%(start)s\".)" +"(Ligne débutant par \"%(start)s\".)" #: db/models/manipulators.py:302 #, python-format @@ -1909,7 +1906,7 @@ msgstr "" #: db/models/fields/__init__.py:40 #, python-format msgid "%(optname)s with this %(fieldname)s already exists." -msgstr "%(optname)s avec le champs %(fieldname)s existe déjà." +msgstr "%(optname)s avec le champs %(fieldname)s existe déjà." #: db/models/fields/__init__.py:114 db/models/fields/__init__.py:265 #: db/models/fields/__init__.py:542 db/models/fields/__init__.py:553 @@ -1919,15 +1916,15 @@ msgstr "Ce champ est obligatoire." #: db/models/fields/__init__.py:337 msgid "This value must be an integer." -msgstr "Cette valeur doit être un entier." +msgstr "Cette valeur doit être un entier." #: db/models/fields/__init__.py:369 msgid "This value must be either True or False." -msgstr "Cette valeur doit être soit Vraie soit Fausse." +msgstr "Cette valeur doit être soit Vraie soit Fausse." #: db/models/fields/__init__.py:385 msgid "This field cannot be null." -msgstr "Ce champ ne peut pas être vide." +msgstr "Ce champ ne peut pas être vide." #: db/models/fields/__init__.py:562 msgid "Enter a valid filename." @@ -1940,14 +1937,14 @@ msgstr "Entrez un %s valide." #: db/models/fields/related.py:579 msgid "Separate multiple IDs with commas." -msgstr "Séparez les ID par des virgules." +msgstr "Séparez les ID par des virgules." #: db/models/fields/related.py:581 msgid "" "Hold down \"Control\", or \"Command\" on a Mac, to select more than one." msgstr "" -"Maintenez \"Contrôle (ctrl)\", ou \"Commande (touche pomme)\" sur un Mac, " -"pour en sélectionner plusieurs." +"Maintenez \"Contrôle (ctrl)\", ou \"Commande (touche pomme)\" sur un Mac, " +"pour en sélectionner plusieurs." #: db/models/fields/related.py:625 #, python-format @@ -1962,18 +1959,17 @@ msgstr[1] "" #, python-format msgid "Ensure your text is less than %s character." msgid_plural "Ensure your text is less than %s characters." -msgstr[0] "Assurez-vous que votre texte fais moins de %s caractère." -msgstr[1] "Assurez-vous que votre texte fais moins de %s caractères." +msgstr[0] "Assurez-vous que votre texte fais moins de %s caractère." +msgstr[1] "Assurez-vous que votre texte fais moins de %s caractères." #: forms/__init__.py:385 -#, fuzzy msgid "Line breaks are not allowed here." -msgstr "Les retours à la ligne ne sont pas autorisés ici." +msgstr "Les retours à la ligne ne sont pas autorisés ici." #: forms/__init__.py:480 forms/__init__.py:551 forms/__init__.py:589 #, python-format msgid "Select a valid choice; '%(data)s' is not in %(choices)s." -msgstr "Sélectionnez un choix valide ; '%(data)s' n'est pas dans %(choices)s." +msgstr "Sélectionnez un choix valide ; '%(data)s' n'est pas dans %(choices)s." #: forms/__init__.py:645 msgid "The submitted file is empty." @@ -1993,7 +1989,7 @@ msgstr "Entrez un nombre entier entre 0 et 32 767." #: template/defaultfilters.py:379 msgid "yes,no,maybe" -msgstr "oui,non,peut-être" +msgstr "oui,non,peut-être" #~ msgid "Comment" #~ msgstr "Commentaire" @@ -2002,10 +1998,10 @@ msgstr "oui,non,peut- #~ msgstr "Commentaires" #~ msgid "String (up to 50)" -#~ msgstr "Chaîne de caractères (jusqu'à 50)" +#~ msgstr "Chaîne de caractères (jusqu'à 50)" #~ msgid "label" -#~ msgstr "intitulé" +#~ msgstr "intitulé" #~ msgid "package" #~ msgstr "paquetage" @@ -2014,4 +2010,4 @@ msgstr "oui,non,peut- #~ msgstr "paquetages" #~ msgid "Messages" -#~ msgstr "Messages" +#~ msgstr "Messages" \ No newline at end of file diff --git a/django/conf/project_template/manage.py b/django/conf/project_template/manage.py index 008aeeb72b..5e78ea979e 100755 --- a/django/conf/project_template/manage.py +++ b/django/conf/project_template/manage.py @@ -4,7 +4,7 @@ try: import settings # Assumed to be in the same directory. except ImportError: import sys - sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n" % __file__) + sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__) sys.exit(1) if __name__ == "__main__": diff --git a/django/contrib/admin/media/css/login.css b/django/contrib/admin/media/css/login.css index 041135f31e..f904957cc5 100644 --- a/django/contrib/admin/media/css/login.css +++ b/django/contrib/admin/media/css/login.css @@ -7,7 +7,7 @@ body.login { background:#eee; } .login #content-main { width:100%; } .login form { margin-top:1em; } .login .form-row { padding:4px 0; float:left; width:100%; } -.login .form-row label { float:left; width:7em; padding-right:0.5em; line-height:2em; text-align:right; font-size:1em; color:#333; } -.login .form-row #id_username, .login .form-row #id_password { width:16em; } +.login .form-row label { float:left; width:9em; padding-right:0.5em; line-height:2em; text-align:right; font-size:1em; color:#333; } +.login .form-row #id_username, .login .form-row #id_password { width:14em; } .login span.help { font-size:10px; display:block; } -.login .submit-row { clear:both; padding:1em 0 0 7.4em; } \ No newline at end of file +.login .submit-row { clear:both; padding:1em 0 0 9.4em; } \ No newline at end of file diff --git a/django/contrib/admin/media/img/admin/icon-unknown.gif b/django/contrib/admin/media/img/admin/icon-unknown.gif new file mode 100644 index 0000000000000000000000000000000000000000..cfd2b02ad91b3677dbe59111faaf4f437c362cb8 GIT binary patch literal 130 zcmV-|0Db>QNk%w1VF~~W0J9GO^z`(anwr7E!O_vts;a8Fxw+ur;K|9!=;-Lh#l`#k z`?0aH)z#IOmX?c)i~s-sA^8LW000jFEC2ui015yK000Cp@IAI#TTH&>x=&LlD2fp{ kltU;-pbSpsb&B9v9)J|xHP4tFtdrsVKoW`tBZ&Y2J8`5w82|tP literal 0 HcmV?d00001 diff --git a/django/contrib/admin/templates/admin/change_form.html b/django/contrib/admin/templates/admin/change_form.html index a667087f5f..fa04969f01 100644 --- a/django/contrib/admin/templates/admin/change_form.html +++ b/django/contrib/admin/templates/admin/change_form.html @@ -12,7 +12,7 @@ {% endif %}{% endblock %} {% block content %}
diff --git a/django/contrib/admin/templates/admin/field_line.html b/django/contrib/admin/templates/admin/field_line.html index b7e2fc2ae0..680830b506 100644 --- a/django/contrib/admin/templates/admin/field_line.html +++ b/django/contrib/admin/templates/admin/field_line.html @@ -2,13 +2,9 @@
{% for bound_field in bound_fields %}{{ bound_field.html_error_list }}{% endfor %} {% for bound_field in bound_fields %} - {% if bound_field.has_label_first %} - {% field_label bound_field %} - {% endif %} + {% if bound_field.has_label_first %}{% field_label bound_field %}{% endif %} {% field_widget bound_field %} - {% if not bound_field.has_label_first %} - {% field_label bound_field %} - {% endif %} + {% if not bound_field.has_label_first %}{% field_label bound_field %}{% endif %} {% if bound_field.field.help_text %}

{{ bound_field.field.help_text }}

{% endif %} {% endfor %}
diff --git a/django/contrib/admin/templates/admin/filter.html b/django/contrib/admin/templates/admin/filter.html index f6f5455c01..5b0e78b6fc 100644 --- a/django/contrib/admin/templates/admin/filter.html +++ b/django/contrib/admin/templates/admin/filter.html @@ -3,6 +3,6 @@ diff --git a/django/contrib/admin/templates/admin/invalid_setup.html b/django/contrib/admin/templates/admin/invalid_setup.html new file mode 100644 index 0000000000..1fa0d32358 --- /dev/null +++ b/django/contrib/admin/templates/admin/invalid_setup.html @@ -0,0 +1,10 @@ +{% extends "admin/base_site.html" %} +{% load i18n %} + +{% block breadcrumbs %}{% endblock %} + +{% block content %} + +

{% trans "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." %}

+ +{% endblock %} diff --git a/django/contrib/admin/templates/admin/object_history.html b/django/contrib/admin/templates/admin/object_history.html index 0dbe7af743..fc568305ca 100644 --- a/django/contrib/admin/templates/admin/object_history.html +++ b/django/contrib/admin/templates/admin/object_history.html @@ -24,8 +24,8 @@ {% for action in action_list %} {{ action.action_time|date:_("DATE_WITH_TIME_FULL") }} - {{ action.user.username }}{% if action.user.first_name %} ({{ action.user.first_name }} {{ action.user.last_name }}){% endif %} - {{ action.change_message}} + {{ action.user.username }}{% if action.user.first_name %} ({{ action.user.first_name|escape }} {{ action.user.last_name|escape }}){% endif %} + {{ action.change_message|escape }} {% endfor %} diff --git a/django/contrib/admin/templatetags/adminapplist.py b/django/contrib/admin/templatetags/adminapplist.py index 5e544fe19f..10e09ca0b6 100644 --- a/django/contrib/admin/templatetags/adminapplist.py +++ b/django/contrib/admin/templatetags/adminapplist.py @@ -42,7 +42,12 @@ class AdminApplistNode(template.Node): }) if model_list: - model_list.sort() + # Sort using verbose decorate-sort-undecorate pattern + # instead of key argument to sort() for python 2.3 compatibility + decorated = [(x['name'], x) for x in model_list] + decorated.sort() + model_list = [x for key, x in decorated] + app_list.append({ 'name': app_label.title(), 'has_module_perms': has_module_perms, diff --git a/django/contrib/admin/urls.py b/django/contrib/admin/urls.py index dde848d766..a2d3ccae48 100644 --- a/django/contrib/admin/urls.py +++ b/django/contrib/admin/urls.py @@ -2,7 +2,7 @@ from django.conf.urls.defaults import * urlpatterns = patterns('', ('^$', 'django.contrib.admin.views.main.index'), - ('^r/(\d+)/(\d+)/$', 'django.views.defaults.shortcut'), + ('^r/(\d+)/(.*)/$', 'django.views.defaults.shortcut'), ('^jsi18n/$', 'django.views.i18n.javascript_catalog', {'packages': 'django.conf'}), ('^logout/$', 'django.contrib.auth.views.logout'), ('^password_change/$', 'django.contrib.auth.views.password_change'), diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py index d9ac07c11b..8fe3c95178 100644 --- a/django/contrib/admin/views/main.py +++ b/django/contrib/admin/views/main.py @@ -35,6 +35,7 @@ ORDER_TYPE_VAR = 'ot' PAGE_VAR = 'p' SEARCH_VAR = 'q' IS_POPUP_VAR = 'pop' +ERROR_FLAG = 'e' # Text to display within change-list table cells if the value is blank. EMPTY_CHANGELIST_VALUE = '(None)' @@ -73,8 +74,7 @@ def unquote(s): for item in list: if item[1:2]: try: - myappend(mychr(myatoi(item[:2], 16)) - + item[2:]) + myappend(mychr(myatoi(item[:2], 16)) + item[2:]) except ValueError: myappend('_' + item) else: @@ -143,9 +143,9 @@ class AdminBoundField(object): return self._display except AttributeError: if isinstance(self.field.rel, models.ManyToOneRel): - self._display = getattr(self.original, self.field.attname) + self._display = getattr(self.original, self.field.name) elif isinstance(self.field.rel, models.ManyToManyRel): - self._display = ", ".join([str(obj) for obj in getattr(self.original, self.field.attname).all()]) + self._display = ", ".join([str(obj) for obj in getattr(self.original, self.field.name).all()]) return self._display def __repr__(self): @@ -557,6 +557,8 @@ class ChangeList(object): self.params = dict(request.GET.items()) if self.params.has_key(PAGE_VAR): del self.params[PAGE_VAR] + if self.params.has_key(ERROR_FLAG): + del self.params[ERROR_FLAG] self.order_field, self.order_type = self.get_ordering() self.query = request.GET.get(SEARCH_VAR, '') @@ -730,7 +732,14 @@ def change_list(request, app_label, model_name): try: cl = ChangeList(request, model) except IncorrectLookupParameters: - return HttpResponseRedirect(request.path) + # Wacky lookup parameters were given, so redirect to the main + # changelist page, without parameters, and pass an 'invalid=1' + # parameter via the query string. If wacky parameters were given and + # the 'invalid=1' parameter was already in the query string, something + # is screwed up with the database, so display an error page. + if ERROR_FLAG in request.GET.keys(): + return render_to_response('admin/invalid_setup.html', {'title': _('Database error')}) + return HttpResponseRedirect(request.path + '?' + ERROR_FLAG + '=1') c = template.RequestContext(request, { 'title': cl.title, 'is_popup': cl.is_popup, diff --git a/django/contrib/auth/handlers/modpython.py b/django/contrib/auth/handlers/modpython.py index b1d7680a33..e6719794a1 100644 --- a/django/contrib/auth/handlers/modpython.py +++ b/django/contrib/auth/handlers/modpython.py @@ -10,8 +10,6 @@ def authenhandler(req, **kwargs): # that so that the following import works os.environ.update(req.subprocess_env) - from django.contrib.auth.models import User - # check for PythonOptions _str_to_bool = lambda s: s.lower() in ('1', 'true', 'on', 'yes') @@ -19,6 +17,11 @@ def authenhandler(req, **kwargs): permission_name = options.get('DjangoPermissionName', None) staff_only = _str_to_bool(options.get('DjangoRequireStaffStatus', "on")) superuser_only = _str_to_bool(options.get('DjangoRequireSuperuserStatus', "off")) + settings_module = options.get('DJANGO_SETTINGS_MODULE', None) + if settings_module: + os.environ['DJANGO_SETTINGS_MODULE'] = settings_module + + from django.contrib.auth.models import User # check that the username is valid kwargs = {'username': req.user, 'is_active': True} diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py index 40037b96b4..80f3ea0d6b 100644 --- a/django/contrib/auth/models.py +++ b/django/contrib/auth/models.py @@ -32,7 +32,7 @@ class Permission(models.Model): ordering = ('content_type', 'codename') def __str__(self): - return "%r | %s" % (self.content_type, self.name) + return "%s | %s" % (self.content_type, self.name) class Group(models.Model): name = models.CharField(_('name'), maxlength=80, unique=True) diff --git a/django/contrib/sessions/middleware.py b/django/contrib/sessions/middleware.py index dae8a11554..da1130f560 100644 --- a/django/contrib/sessions/middleware.py +++ b/django/contrib/sessions/middleware.py @@ -79,9 +79,14 @@ class SessionMiddleware: else: if modified or settings.SESSION_SAVE_EVERY_REQUEST: session_key = request.session.session_key or Session.objects.get_new_session_key() + if settings.SESSION_EXPIRE_AT_BROWSER_CLOSE: + max_age = None + expires = None + else: + max_age = settings.SESSION_COOKIE_AGE + expires = datetime.datetime.strftime(datetime.datetime.utcnow() + datetime.timedelta(seconds=settings.SESSION_COOKIE_AGE), "%a, %d-%b-%Y %H:%M:%S GMT") new_session = Session.objects.save(session_key, request.session._session, datetime.datetime.now() + datetime.timedelta(seconds=settings.SESSION_COOKIE_AGE)) - expires = datetime.datetime.strftime(datetime.datetime.utcnow() + datetime.timedelta(seconds=settings.SESSION_COOKIE_AGE), "%a, %d-%b-%Y %H:%M:%S GMT") response.set_cookie(settings.SESSION_COOKIE_NAME, session_key, - max_age=settings.SESSION_COOKIE_AGE, expires=expires, domain=settings.SESSION_COOKIE_DOMAIN) + max_age=max_age, expires=expires, domain=settings.SESSION_COOKIE_DOMAIN) return response diff --git a/django/core/management.py b/django/core/management.py index 8e8133f443..f7bbf29227 100644 --- a/django/core/management.py +++ b/django/core/management.py @@ -334,8 +334,8 @@ def get_sql_initial_data_for_model(model): r"""( # each statement is... (?: # one or more chunks of ... (?:[^;'"]+) # not the end of a statement or start of a quote - | (?:'[^']+') # something in single quotes - | (?:"[^"]+") # something in double quotes + | (?:'[^']*') # something in single quotes + | (?:"[^"]*") # something in double quotes )+)""", re.VERBOSE) # Find custom SQL, if it's available. diff --git a/django/core/paginator.py b/django/core/paginator.py index 6e01c1ccec..f4941cb678 100644 --- a/django/core/paginator.py +++ b/django/core/paginator.py @@ -54,6 +54,26 @@ class ObjectPaginator: def has_previous_page(self, page_number): return page_number > 0 + def first_on_page(self, page_number): + """ + Returns the 1-based index of the first object on the given page, + relative to total objects found (hits). + """ + if page_number == 0: + return 1 + return (self.num_per_page * page_number) + 1 + + def last_on_page(self, page_number): + """ + Returns the 1-based index of the last object on the given page, + relative to total objects found (hits). + """ + if page_number == 0 and self.num_per_page >= self._hits: + return self._hits + elif page_number == (self._pages - 1) and (page_number + 1) * self.num_per_page > self._hits: + return self._hits + return (page_number + 1) * self.num_per_page + def _get_hits(self): if self._hits is None: self._hits = self.query_set.count() diff --git a/django/core/validators.py b/django/core/validators.py index 91d72033de..27505b7d5a 100644 --- a/django/core/validators.py +++ b/django/core/validators.py @@ -20,7 +20,10 @@ alnumurl_re = re.compile(r'^[-\w/]+$') ansi_date_re = re.compile('^%s$' % _datere) ansi_time_re = re.compile('^%s$' % _timere) ansi_datetime_re = re.compile('^%s %s$' % (_datere, _timere)) -email_re = re.compile(r'^[A-Z0-9._%-][+A-Z0-9._%-]*@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}$', re.IGNORECASE) +email_re = re.compile( + r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*" # dot-atom + r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-011\013\014\016-\177])*"' # quoted-string + r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}$', re.IGNORECASE) # domain integer_re = re.compile(r'^-?\d+$') ip4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$') phone_re = re.compile(r'^[A-PR-Y0-9]{3}-[A-PR-Y0-9]{3}-[A-PR-Y0-9]{4}$', re.IGNORECASE) @@ -143,7 +146,11 @@ def isValidImage(field_data, all_data): from PIL import Image from cStringIO import StringIO try: - Image.open(StringIO(field_data['content'])) + content = field_data['content'] + except TypeError: + raise ValidationError, gettext("No file was submitted. Check the encoding type on the form.") + try: + Image.open(StringIO(content)) except IOError: # Python Imaging Library doesn't recognize it as an image raise ValidationError, gettext("Upload a valid image. The file you uploaded was either not an image or a corrupted image.") @@ -363,9 +370,13 @@ class HasAllowableSize: self.max_error_message = max_error_message or lazy_inter(gettext_lazy("Make sure your uploaded file is at most %s bytes big."), max_size) def __call__(self, field_data, all_data): - if self.min_size is not None and len(field_data['content']) < self.min_size: + try: + content = field_data['content'] + except TypeError: + raise ValidationError, gettext_lazy("No file was submitted. Check the encoding type on the form.") + if self.min_size is not None and len(content) < self.min_size: raise ValidationError, self.min_error_message - if self.max_size is not None and len(field_data['content']) > self.max_size: + if self.max_size is not None and len(content) > self.max_size: raise ValidationError, self.max_error_message class MatchesRegularExpression: diff --git a/django/db/backends/postgresql/introspection.py b/django/db/backends/postgresql/introspection.py index c4f759da10..c3a16d61c3 100644 --- a/django/db/backends/postgresql/introspection.py +++ b/django/db/backends/postgresql/introspection.py @@ -45,27 +45,26 @@ def get_indexes(cursor, table_name): {'primary_key': boolean representing whether it's the primary key, 'unique': boolean representing whether it's a unique index} """ - # Get the table description because we only have the column indexes, and we - # need the column names. - desc = get_table_description(cursor, table_name) - # This query retrieves each index on the given table. + # This query retrieves each index on the given table, including the + # first associated field name cursor.execute(""" - SELECT idx.indkey, idx.indisunique, idx.indisprimary + SELECT attr.attname, idx.indkey, idx.indisunique, idx.indisprimary FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, - pg_catalog.pg_index idx + pg_catalog.pg_index idx, pg_catalog.pg_attribute attr WHERE c.oid = idx.indrelid AND idx.indexrelid = c2.oid + AND attr.attrelid = c.oid + AND attr.attnum = idx.indkey[0] AND c.relname = %s""", [table_name]) indexes = {} for row in cursor.fetchall(): - # row[0] (idx.indkey) is stored in the DB as an array. It comes out as + # row[1] (idx.indkey) is stored in the DB as an array. It comes out as # a string of space-separated integers. This designates the field # indexes (1-based) of the fields that have indexes on the table. # Here, we skip any indexes across multiple fields. - if ' ' in row[0]: + if ' ' in row[1]: continue - col_name = desc[int(row[0])-1][0] - indexes[col_name] = {'primary_key': row[2], 'unique': row[1]} + indexes[row[0]] = {'primary_key': row[3], 'unique': row[2]} return indexes # Maps type codes to Django Field types. diff --git a/django/db/backends/postgresql_psycopg2/introspection.py b/django/db/backends/postgresql_psycopg2/introspection.py index 88c44f98d7..b991493d39 100644 --- a/django/db/backends/postgresql_psycopg2/introspection.py +++ b/django/db/backends/postgresql_psycopg2/introspection.py @@ -45,27 +45,26 @@ def get_indexes(cursor, table_name): {'primary_key': boolean representing whether it's the primary key, 'unique': boolean representing whether it's a unique index} """ - # Get the table description because we only have the column indexes, and we - # need the column names. - desc = get_table_description(cursor, table_name) - # This query retrieves each index on the given table. + # This query retrieves each index on the given table, including the + # first associated field name cursor.execute(""" - SELECT idx.indkey, idx.indisunique, idx.indisprimary + SELECT attr.attname, idx.indkey, idx.indisunique, idx.indisprimary FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, - pg_catalog.pg_index idx + pg_catalog.pg_index idx, pg_catalog.pg_attribute attr WHERE c.oid = idx.indrelid AND idx.indexrelid = c2.oid + AND attr.attrelid = c.oid + AND attr.attnum = idx.indkey[0] AND c.relname = %s""", [table_name]) indexes = {} for row in cursor.fetchall(): - # row[0] (idx.indkey) is stored in the DB as an array. It comes out as + # row[1] (idx.indkey) is stored in the DB as an array. It comes out as # a string of space-separated integers. This designates the field # indexes (1-based) of the fields that have indexes on the table. # Here, we skip any indexes across multiple fields. - if ' ' in row[0]: + if ' ' in row[1]: continue - col_name = desc[int(row[0])-1][0] - indexes[col_name] = {'primary_key': row[2], 'unique': row[1]} + indexes[row[0]] = {'primary_key': row[3], 'unique': row[2]} return indexes # Maps type codes to Django Field types. diff --git a/django/db/backends/util.py b/django/db/backends/util.py index b9c6f573c9..3098a53556 100644 --- a/django/db/backends/util.py +++ b/django/db/backends/util.py @@ -12,6 +12,10 @@ class CursorDebugWrapper: return self.cursor.execute(sql, params) finally: stop = time() + # If params was a list, convert it to a tuple, because string + # formatting with '%' only works with tuples or dicts. + if not isinstance(params, (tuple, dict)): + params = tuple(params) self.db.queries.append({ 'sql': sql % tuple(params), 'time': "%.3f" % (stop - start), diff --git a/django/db/models/base.py b/django/db/models/base.py index bc722de505..3538826356 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -161,7 +161,7 @@ class Model(object): (backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column)), [pk_val]) # If it does already exist, do an UPDATE. if cursor.fetchone(): - db_values = [f.get_db_prep_save(f.pre_save(getattr(self, f.attname), False)) for f in non_pks] + db_values = [f.get_db_prep_save(f.pre_save(self, False)) for f in non_pks] cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % \ (backend.quote_name(self._meta.db_table), ','.join(['%s=%%s' % backend.quote_name(f.column) for f in non_pks]), @@ -171,11 +171,11 @@ class Model(object): record_exists = False if not pk_set or not record_exists: field_names = [backend.quote_name(f.column) for f in self._meta.fields if not isinstance(f, AutoField)] - db_values = [f.get_db_prep_save(f.pre_save(getattr(self, f.attname), True)) for f in self._meta.fields if not isinstance(f, AutoField)] + db_values = [f.get_db_prep_save(f.pre_save(self, True)) for f in self._meta.fields if not isinstance(f, AutoField)] # If the PK has been manually set, respect that. if pk_set: field_names += [f.column for f in self._meta.fields if isinstance(f, AutoField)] - db_values += [f.get_db_prep_save(f.pre_save(getattr(self, f.column), True)) for f in self._meta.fields if isinstance(f, AutoField)] + db_values += [f.get_db_prep_save(f.pre_save(self, True)) for f in self._meta.fields if isinstance(f, AutoField)] placeholders = ['%s'] * len(field_names) if self._meta.order_with_respect_to: field_names.append(backend.quote_name('_order')) @@ -279,7 +279,7 @@ class Model(object): order_field = self._meta.order_with_respect_to where = ['%s %s (SELECT %s FROM %s WHERE %s=%%s)' % \ (backend.quote_name('_order'), op, backend.quote_name('_order'), - backend.quote_name(opts.db_table), backend.quote_name(opts.pk.column)), + backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column)), '%s=%%s' % backend.quote_name(order_field.column)] params = [self._get_pk_val(), getattr(self, order_field.attname)] obj = self._default_manager.order_by('_order').extra(where=where, params=params)[:1].get() diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index 334aa01766..b5245d6624 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -152,9 +152,9 @@ class Field(object): def get_internal_type(self): return self.__class__.__name__ - def pre_save(self, value, add): + def pre_save(self, model_instance, add): "Returns field's value just before saving." - return value + return getattr(model_instance, self.attname) def get_db_prep_save(self, value): "Returns field's value prepared for saving into a database." @@ -417,10 +417,13 @@ class DateField(Field): value = str(value) return Field.get_db_prep_lookup(self, lookup_type, value) - def pre_save(self, value, add): + def pre_save(self, model_instance, add): if self.auto_now or (self.auto_now_add and add): - return datetime.datetime.now() - return value + value = datetime.datetime.now() + setattr(model_instance, self.attname, value) + return value + else: + return super(DateField, self).pre_save(model_instance, add) def contribute_to_class(self, cls, name): super(DateField,self).contribute_to_class(cls, name) @@ -723,10 +726,13 @@ class TimeField(Field): value = str(value) return Field.get_db_prep_lookup(self, lookup_type, value) - def pre_save(self, value, add): + def pre_save(self, model_instance, add): if self.auto_now or (self.auto_now_add and add): - return datetime.datetime.now().time() - return value + value = datetime.datetime.now().time() + setattr(model_instance, self.attname, value) + return value + else: + return super(TimeField, self).pre_save(model_instance, add) def get_db_prep_save(self, value): # Casts dates into string format for entry into database. diff --git a/django/db/models/query.py b/django/db/models/query.py index 91ae294a62..3517d6bed5 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -3,8 +3,8 @@ from django.db.models.fields import DateField, FieldDoesNotExist from django.db.models import signals from django.dispatch import dispatcher from django.utils.datastructures import SortedDict - import operator +import re # For Python 2.3 if not hasattr(__builtins__, 'set'): @@ -59,7 +59,7 @@ def orderlist2sql(order_list, opts, prefix=''): return ', '.join(output) def quote_only_if_word(word): - if ' ' in word: + if re.search('\W', word): # Don't quote if there are spaces or non-word chars. return word else: return backend.quote_name(word) @@ -315,7 +315,7 @@ class QuerySet(object): def complex_filter(self, filter_obj): """Returns a new QuerySet instance with filter_obj added to the filters. - filter_obj can be a Q object (has 'get_sql' method) or a dictionary of + filter_obj can be a Q object (has 'get_sql' method) or a dictionary of keyword lookup arguments.""" # This exists to support framework features such as 'limit_choices_to', # and usually it will be more natural to use other methods. @@ -380,6 +380,10 @@ class QuerySet(object): # (so that A.filter(args1) & A.filter(args2) does the same as # A.filter(args1).filter(args2) combined = other._clone() + if self._select: combined._select.update(self._select) + if self._where: combined._where.extend(self._where) + if self._params: combined._params.extend(self._params) + if self._tables: combined._tables.extend(self._tables) # If 'self' is ordered and 'other' isn't, propagate 'self's ordering if (self._order_by is not None and len(self._order_by) > 0) and \ (combined._order_by is None or len(combined._order_by) == 0): diff --git a/django/db/transaction.py b/django/db/transaction.py index 906995ca02..60a743c42a 100644 --- a/django/db/transaction.py +++ b/django/db/transaction.py @@ -12,7 +12,10 @@ Managed transactions don't do those commits, but will need some kind of manual or implicit commits or rollbacks. """ -import thread +try: + import thread +except ImportError: + import dummy_thread as thread from django.db import connection from django.conf import settings diff --git a/django/forms/__init__.py b/django/forms/__init__.py index 7ad26a4d71..b67e1d0f84 100644 --- a/django/forms/__init__.py +++ b/django/forms/__init__.py @@ -577,7 +577,7 @@ class SelectMultipleField(SelectField): selected_html = '' if str(value) in str_data_list: selected_html = ' selected="selected"' - output.append(' ' % (escape(value), selected_html, choice)) + output.append(' ' % (escape(value), selected_html, escape(choice))) output.append(' ') return '\n'.join(output) @@ -641,7 +641,11 @@ class FileUploadField(FormField): self.validator_list = [self.isNonEmptyFile] + validator_list def isNonEmptyFile(self, field_data, all_data): - if not field_data['content']: + try: + content = field_data['content'] + except TypeError: + raise validators.CriticalValidationError, gettext("No file was submitted. Check the encoding type on the form.") + if not content: raise validators.CriticalValidationError, gettext("The submitted file is empty.") def render(self, data): diff --git a/django/shortcuts/__init__.py b/django/shortcuts/__init__.py index b42ede0339..76d54917ad 100644 --- a/django/shortcuts/__init__.py +++ b/django/shortcuts/__init__.py @@ -10,14 +10,14 @@ def render_to_response(*args, **kwargs): return HttpResponse(loader.render_to_string(*args, **kwargs)) load_and_render = render_to_response # For backwards compatibility. -def get_object_or_404(klass, **kwargs): +def get_object_or_404(klass, *args, **kwargs): try: - return klass._default_manager.get(**kwargs) + return klass._default_manager.get(*args, **kwargs) except klass.DoesNotExist: raise Http404 -def get_list_or_404(klass, **kwargs): - obj_list = list(klass._default_manager.filter(**kwargs)) +def get_list_or_404(klass, *args, **kwargs): + obj_list = list(klass._default_manager.filter(*args, **kwargs)) if not obj_list: raise Http404 return obj_list diff --git a/django/utils/_threading_local.py b/django/utils/_threading_local.py index 90717a8d84..bf9a25753a 100644 --- a/django/utils/_threading_local.py +++ b/django/utils/_threading_local.py @@ -234,4 +234,7 @@ class local(_localbase): return __del__ __del__ = __del__() -from threading import currentThread, enumerate, RLock +try: + from threading import currentThread, enumerate, RLock +except ImportError: + from dummy_threading import currentThread, enumerate, RLock diff --git a/django/utils/autoreload.py b/django/utils/autoreload.py index 1edec190d8..6363af7835 100644 --- a/django/utils/autoreload.py +++ b/django/utils/autoreload.py @@ -28,7 +28,12 @@ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import os, sys, thread, time +import os, sys, time + +try: + import thread +except ImportError: + import dummy_thread as thread RUN_RELOADER = True diff --git a/django/utils/feedgenerator.py b/django/utils/feedgenerator.py index f7c25f2933..4c5d515f41 100644 --- a/django/utils/feedgenerator.py +++ b/django/utils/feedgenerator.py @@ -217,7 +217,7 @@ class Atom1Feed(SyndicationFeed): for item in self.items: handler.startElement(u"entry", {}) handler.addQuickElement(u"title", item['title']) - handler.addQuickElement(u"link", u"", {u"href": item['link']}) + handler.addQuickElement(u"link", u"", {u"href": item['link'], u"rel": u"alternate"}) if item['pubdate'] is not None: handler.addQuickElement(u"updated", rfc3339_date(item['pubdate']).decode('ascii')) diff --git a/django/views/generic/date_based.py b/django/views/generic/date_based.py index 1a6cbc8369..0fc657d2c1 100644 --- a/django/views/generic/date_based.py +++ b/django/views/generic/date_based.py @@ -6,7 +6,8 @@ import datetime, time def archive_index(request, queryset, date_field, num_latest=15, template_name=None, template_loader=loader, - extra_context={}, allow_empty=False, context_processors=None): + extra_context={}, allow_empty=False, context_processors=None, + mimetype=None): """ Generic top-level archive of date-based objects. @@ -40,11 +41,12 @@ def archive_index(request, queryset, date_field, num_latest=15, c[key] = value() else: c[key] = value - return HttpResponse(t.render(c)) + return HttpResponse(t.render(c), mimetype=mimetype) def archive_year(request, year, queryset, date_field, template_name=None, template_loader=loader, extra_context={}, allow_empty=False, - context_processors=None): + context_processors=None, template_object_name='object', mimetype=None, + make_object_list=False): """ Generic yearly archive view. @@ -54,6 +56,9 @@ def archive_year(request, year, queryset, date_field, template_name=None, List of months in this year with objects year This year + object_list + List of objects published in the given month + (Only available if make_object_list argument is True) """ model = queryset.model now = datetime.datetime.now() @@ -66,24 +71,29 @@ def archive_year(request, year, queryset, date_field, template_name=None, date_list = queryset.filter(**lookup_kwargs).dates(date_field, 'month') if not date_list and not allow_empty: raise Http404 + if make_object_list: + object_list = queryset.filter(**lookup_kwargs).order_by(date_field) + else: + object_list = [] if not template_name: template_name = "%s/%s_archive_year.html" % (model._meta.app_label, model._meta.object_name.lower()) t = template_loader.get_template(template_name) c = RequestContext(request, { 'date_list': date_list, 'year': year, + '%s_list' % template_object_name: object_list, }, context_processors) for key, value in extra_context.items(): if callable(value): c[key] = value() else: c[key] = value - return HttpResponse(t.render(c)) + return HttpResponse(t.render(c), mimetype=mimetype) def archive_month(request, year, month, queryset, date_field, month_format='%b', template_name=None, template_loader=loader, extra_context={}, allow_empty=False, context_processors=None, - template_object_name='object'): + template_object_name='object', mimetype=None): """ Generic monthly archive view. @@ -134,12 +144,12 @@ def archive_month(request, year, month, queryset, date_field, c[key] = value() else: c[key] = value - return HttpResponse(t.render(c)) + return HttpResponse(t.render(c), mimetype=mimetype) def archive_week(request, year, week, queryset, date_field, template_name=None, template_loader=loader, extra_context={}, allow_empty=True, context_processors=None, - template_object_name='object'): + template_object_name='object', mimetype=None): """ Generic weekly archive view. @@ -181,12 +191,13 @@ def archive_week(request, year, week, queryset, date_field, c[key] = value() else: c[key] = value - return HttpResponse(t.render(c)) + return HttpResponse(t.render(c), mimetype=mimetype) def archive_day(request, year, month, day, queryset, date_field, month_format='%b', day_format='%d', template_name=None, template_loader=loader, extra_context={}, allow_empty=False, - context_processors=None, template_object_name='object'): + context_processors=None, template_object_name='object', + mimetype=None): """ Generic daily archive view. @@ -233,7 +244,7 @@ def archive_day(request, year, month, day, queryset, date_field, c[key] = value() else: c[key] = value - return HttpResponse(t.render(c)) + return HttpResponse(t.render(c), mimetype=mimetype) def archive_today(request, **kwargs): """ @@ -251,7 +262,7 @@ def object_detail(request, year, month, day, queryset, date_field, month_format='%b', day_format='%d', object_id=None, slug=None, slug_field=None, template_name=None, template_name_field=None, template_loader=loader, extra_context={}, context_processors=None, - template_object_name='object'): + template_object_name='object', mimetype=None): """ Generic detail view from year/month/day/slug or year/month/day/id structure. @@ -300,6 +311,6 @@ def object_detail(request, year, month, day, queryset, date_field, c[key] = value() else: c[key] = value - response = HttpResponse(t.render(c)) + response = HttpResponse(t.render(c), mimetype=mimetype) populate_xheaders(request, response, model, getattr(obj, obj._meta.pk.name)) return response diff --git a/django/views/generic/list_detail.py b/django/views/generic/list_detail.py index 7d254c08da..77b97ca711 100644 --- a/django/views/generic/list_detail.py +++ b/django/views/generic/list_detail.py @@ -6,7 +6,8 @@ from django.core.exceptions import ObjectDoesNotExist def object_list(request, queryset, paginate_by=None, allow_empty=False, template_name=None, template_loader=loader, - extra_context={}, context_processors=None, template_object_name='object'): + extra_context={}, context_processors=None, template_object_name='object', + mimetype=None): """ Generic list of objects. @@ -73,12 +74,13 @@ def object_list(request, queryset, paginate_by=None, allow_empty=False, model = queryset.model template_name = "%s/%s_list.html" % (model._meta.app_label, model._meta.object_name.lower()) t = template_loader.get_template(template_name) - return HttpResponse(t.render(c)) + return HttpResponse(t.render(c), mimetype=mimetype) def object_detail(request, queryset, object_id=None, slug=None, slug_field=None, template_name=None, template_name_field=None, template_loader=loader, extra_context={}, - context_processors=None, template_object_name='object'): + context_processors=None, template_object_name='object', + mimetype=None): """ Generic list of objects. @@ -113,6 +115,6 @@ def object_detail(request, queryset, object_id=None, slug=None, c[key] = value() else: c[key] = value - response = HttpResponse(t.render(c)) + response = HttpResponse(t.render(c), mimetype=mimetype) populate_xheaders(request, response, model, getattr(obj, obj._meta.pk.name)) return response diff --git a/docs/apache_auth.txt b/docs/apache_auth.txt index 0dd5d00cbe..72e0841305 100644 --- a/docs/apache_auth.txt +++ b/docs/apache_auth.txt @@ -56,6 +56,15 @@ location to users marked as staff members. You can use a set of required. ================================ ========================================= +Note that sometimes ``SetEnv`` doesn't play well in this mod_python +configuration, for reasons unknown. If you're having problems getting +mod_python to recognize your ``DJANGO_SETTINGS_MODULE``, you can set it using +``PythonOption`` instead of ``SetEnv``. Therefore, these two Apache directives +are equivalent:: + + SetEnv DJANGO_SETTINGS_MODULE mysite.settings + PythonOption DJANGO_SETTINGS_MODULE mysite.settings + .. _authentication system: http://www.djangoproject.com/documentation/authentication/ .. _Subversion: http://subversion.tigris.org/ .. _mod_dav: http://httpd.apache.org/docs/2.0/mod/mod_dav.html diff --git a/docs/generic_views.txt b/docs/generic_views.txt index 5b978af1de..317828a2b2 100644 --- a/docs/generic_views.txt +++ b/docs/generic_views.txt @@ -182,6 +182,9 @@ a date in the *future* are not included. * ``context_processors``: A list of template-context processors to apply to the view's template. See the `RequestContext docs`_. + * ``mimetype``: The MIME type to use for the resulting document. Defaults + to the value of the ``DEFAULT_MIME_TYPE`` setting. + **Template name:** If ``template_name`` isn't specified, this view will use the template @@ -247,6 +250,21 @@ with a date in the *future* are not displayed. * ``context_processors``: A list of template-context processors to apply to the view's template. See the `RequestContext docs`_. + * ``template_object_name``: Designates the name of the template variable + to use in the template context. By default, this is ``'object'``. The + view will append ``'_list'`` to the value of this parameter in + determining the variable's name. + + * ``make_object_list``: A boolean specifying whether to retrieve the full + list of objects for this year and pass those to the template. If ``True``, + this list of objects will be made available to the template as + ``object_list``. (The name ``object_list`` may be different; see the docs + for ``object_list`` in the "Template context" section below.) By default, + this is ``False``. + + * ``mimetype``: The MIME type to use for the resulting document. Defaults + to the value of the ``DEFAULT_MIME_TYPE`` setting. + **Template name:** If ``template_name`` isn't specified, this view will use the template @@ -259,8 +277,19 @@ In addition to ``extra_context``, the template's context will be: * ``date_list``: A list of ``datetime.date`` objects representing all months that have objects available in the given year, according to ``queryset``, in ascending order. + * ``year``: The given year, as a four-character string. + * ``object_list``: If the ``make_object_list`` parameter is ``True``, this + will be set to a list of objects available for the given year, ordered by + the date field. This variable's name depends on the + ``template_object_name`` parameter, which is ``'object'`` by default. If + ``template_object_name`` is ``'foo'``, this variable's name will be + ``foo_list``. + + If ``make_object_list`` is ``False``, ``object_list`` will be passed to + the template as an empty list. + ``django.views.generic.date_based.archive_month`` ------------------------------------------------- @@ -314,6 +343,9 @@ date in the *future* are not displayed. view will append ``'_list'`` to the value of this parameter in determining the variable's name. + * ``mimetype``: The MIME type to use for the resulting document. Defaults + to the value of the ``DEFAULT_MIME_TYPE`` setting. + **Template name:** If ``template_name`` isn't specified, this view will use the template @@ -387,6 +419,9 @@ in the *future* are not displayed. view will append ``'_list'`` to the value of this parameter in determining the variable's name. + * ``mimetype``: The MIME type to use for the resulting document. Defaults + to the value of the ``DEFAULT_MIME_TYPE`` setting. + **Template name:** If ``template_name`` isn't specified, this view will use the template @@ -463,6 +498,9 @@ a 404 error, regardless of whether any objects exist for future days. view will append ``'_list'`` to the value of this parameter in determining the variable's name. + * ``mimetype``: The MIME type to use for the resulting document. Defaults + to the value of the ``DEFAULT_MIME_TYPE`` setting. + **Template name:** If ``template_name`` isn't specified, this view will use the template @@ -563,6 +601,9 @@ A page representing an individual object. * ``template_object_name``: Designates the name of the template variable to use in the template context. By default, this is ``'object'``. + * ``mimetype``: The MIME type to use for the resulting document. Defaults + to the value of the ``DEFAULT_MIME_TYPE`` setting. + **Template name:** If ``template_name`` isn't specified, this view will use the template @@ -627,6 +668,9 @@ A page representing a list of objects. view will append ``'_list'`` to the value of this parameter in determining the variable's name. + * ``mimetype``: The MIME type to use for the resulting document. Defaults + to the value of the ``DEFAULT_MIME_TYPE`` setting. + **Template name:** If ``template_name`` isn't specified, this view will use the template @@ -717,6 +761,9 @@ A page representing an individual object. * ``template_object_name``: Designates the name of the template variable to use in the template context. By default, this is ``'object'``. + * ``mimetype``: The MIME type to use for the resulting document. Defaults + to the value of the ``DEFAULT_MIME_TYPE`` setting. + **Template name:** If ``template_name`` isn't specified, this view will use the template diff --git a/docs/model-api.txt b/docs/model-api.txt index 3e40a6f63f..9cc5b8f203 100644 --- a/docs/model-api.txt +++ b/docs/model-api.txt @@ -162,11 +162,15 @@ A date field. Has a few extra optional arguments: ====================== =================================================== ``auto_now`` Automatically set the field to now every time the object is saved. Useful for "last-modified" - timestamps. + timestamps. Note that the current date is *always* + used; it's not just a default value that you can + override. ``auto_now_add`` Automatically set the field to now when the object is first created. Useful for creation of - timestamps. + timestamps. Note that the current date is *always* + used; it's not just a default value that you can + override. ====================== =================================================== The admin represents this as an ```` with a JavaScript @@ -488,6 +492,10 @@ cleared, the object will be deleted. It is an error to have an inline-editable relation without at least one ``core=True`` field. +Please note that each field marked "core" is treated as a required field by the +Django admin site. Essentially, this means you should put ``core=True`` on all +required fields in your related object that is being edited inline. + ``db_column`` ~~~~~~~~~~~~~ diff --git a/docs/sessions.txt b/docs/sessions.txt index b7f16b67b9..2dba491159 100644 --- a/docs/sessions.txt +++ b/docs/sessions.txt @@ -197,6 +197,22 @@ will be sent on every request. Similarly, the ``expires`` part of a session cookie is updated each time the session cookie is sent. +Browser-length sessions vs. persistent sessions +=============================================== + +You can control whether the session framework uses browser-length sessions vs. +persistent sessions with the ``SESSION_EXPIRE_AT_BROWSER_CLOSE`` setting. + +By default, ``SESSION_EXPIRE_AT_BROWSER_CLOSE`` is set to ``False``, which +means session cookies will be stored in users' browsers for as long as +``SESSION_COOKIE_AGE``. Use this if you don't want people to have to log in +every time they open a browser. + +If ``SESSION_EXPIRE_AT_BROWSER_CLOSE`` is set to ``True``, Django will use +browser-length cookies -- cookies that expire as soon as the user closes his or +her browser. Use this if you want people to have to log in every time they open +a browser. + Settings ======== @@ -225,6 +241,14 @@ Default: ``'sessionid'`` The name of the cookie to use for sessions. This can be whatever you want. +SESSION_EXPIRE_AT_BROWSER_CLOSE +------------------------------- + +Default: ``False`` + +Whether to expire the session when the user closes his or her browser. See +"Browser-length sessions vs. persistent sessions" above. + SESSION_SAVE_EVERY_REQUEST -------------------------- diff --git a/docs/settings.txt b/docs/settings.txt index 80000fad5b..26d5930f21 100644 --- a/docs/settings.txt +++ b/docs/settings.txt @@ -603,6 +603,14 @@ Default: ``'sessionid'`` The name of the cookie to use for sessions. This can be whatever you want. See the `session docs`_. +SESSION_EXPIRE_AT_BROWSER_CLOSE +------------------------------- + +Default: ``False`` + +Whether to expire the session when the user closes his or her browser. +See the `session docs`_. + SESSION_SAVE_EVERY_REQUEST -------------------------- diff --git a/docs/tutorial02.txt b/docs/tutorial02.txt index 4cc263dddc..84eae3eb83 100644 --- a/docs/tutorial02.txt +++ b/docs/tutorial02.txt @@ -85,7 +85,7 @@ creating an empty class means "give this object an admin interface using all the default options." Now reload the Django admin page to see your changes. Note that you don't have -to restart the development server -- the server will auto-reloads your project, +to restart the development server -- the server will auto-reload your project, so any modifications code will be seen immediately in your browser. Explore the free admin functionality diff --git a/docs/tutorial03.txt b/docs/tutorial03.txt index f368d54c53..3a830eb76f 100644 --- a/docs/tutorial03.txt +++ b/docs/tutorial03.txt @@ -209,13 +209,13 @@ So let's use Django's template system to separate the design from Python:: }) return HttpResponse(t.render(c)) -That code loads the template called "polls/index" and passes it a context. The +That code loads the template called "polls/index.html" and passes it a context. The context is a dictionary mapping template variable names to Python objects. Reload the page. Now you'll see an error:: - TemplateDoesNotExist: Your TEMPLATE_DIRS settings is empty. - Change it to point to at least one template directory. + TemplateDoesNotExist at /polls/ + polls/index.html Ah. There's no template yet. First, create a directory, somewhere on your filesystem, whose contents Django can access. (Django runs as whatever user diff --git a/tests/modeltests/custom_managers/models.py b/tests/modeltests/custom_managers/models.py index a6ae80029a..ceecea2fc6 100644 --- a/tests/modeltests/custom_managers/models.py +++ b/tests/modeltests/custom_managers/models.py @@ -1,5 +1,12 @@ """ 23. Giving models a custom manager + +You can use a custom ``Manager`` in a particular model by extending the base +``Manager`` class and instantiating your custom ``Manager`` in your model. + +There are two reasons you might want to customize a ``Manager``: to add extra +``Manager`` methods, and/or to modify the initial ``QuerySet`` the ``Manager`` +returns. """ from django.db import models @@ -19,7 +26,7 @@ class Person(models.Model): def __repr__(self): return "%s %s" % (self.first_name, self.last_name) -# An example of a custom manager that sets a core_filter on its lookups. +# An example of a custom manager that sets get_query_set(). class PublishedBookManager(models.Manager): def get_query_set(self): diff --git a/tests/modeltests/custom_methods/models.py b/tests/modeltests/custom_methods/models.py index 3fdefca6bf..6cc3fe8548 100644 --- a/tests/modeltests/custom_methods/models.py +++ b/tests/modeltests/custom_methods/models.py @@ -1,5 +1,5 @@ """ -3. Giving models custom methods and custom managers +3. Giving models custom methods Any method you add to a model will be available to instances. """ diff --git a/tests/modeltests/field_defaults/models.py b/tests/modeltests/field_defaults/models.py index e5b7fd8e6d..0d69ffd8be 100644 --- a/tests/modeltests/field_defaults/models.py +++ b/tests/modeltests/field_defaults/models.py @@ -1,7 +1,12 @@ """ -XXX. Callable defaults +31. Callable defaults -??? +You can pass callable objects as the ``default`` parameter to a field. When +the object is created without an explicit value passed in, Django will call +the method to determine the default value. + +This example uses ``datetime.datetime.now`` as the default for the ``pub_date`` +field. """ from django.db import models @@ -9,9 +14,9 @@ from datetime import datetime class Article(models.Model): headline = models.CharField(maxlength=100, default='Default headline') - pub_date = models.DateTimeField(default = datetime.now) - - def __repr__(self): + pub_date = models.DateTimeField(default=datetime.now) + + def __str__(self): return self.headline API_TESTS = """ @@ -43,6 +48,4 @@ API_TESTS = """ >>> d = now - a.pub_date >>> d.seconds < 5 True - - """ diff --git a/tests/modeltests/invalid_models/models.py b/tests/modeltests/invalid_models/models.py index 0b3ffcf073..127cc9e0d2 100644 --- a/tests/modeltests/invalid_models/models.py +++ b/tests/modeltests/invalid_models/models.py @@ -1,5 +1,7 @@ """ -26. A test to check that the model validator works can correctly identify errors in a model. +26. Invalid models + +This example exists purely to point out errors in models. """ from django.db import models @@ -11,16 +13,16 @@ class FieldErrors(models.Model): prepopulate = models.CharField(maxlength=10, prepopulate_from='bad') choices = models.CharField(maxlength=10, choices='bad') choices2 = models.CharField(maxlength=10, choices=[(1,2,3),(1,2,3)]) - index = models.CharField(maxlength=10, db_index='bad') + index = models.CharField(maxlength=10, db_index='bad') class Target(models.Model): tgt_safe = models.CharField(maxlength=10) - + clash1_set = models.CharField(maxlength=10) - + class Clash1(models.Model): src_safe = models.CharField(maxlength=10) - + foreign = models.ForeignKey(Target) m2m = models.ManyToManyField(Target) @@ -36,27 +38,27 @@ class Clash2(models.Model): class Target2(models.Model): foreign_tgt = models.ForeignKey(Target) clashforeign_set = models.ForeignKey(Target) - + m2m_tgt = models.ManyToManyField(Target) clashm2m_set = models.ManyToManyField(Target) class Clash3(models.Model): foreign_1 = models.ForeignKey(Target2, related_name='foreign_tgt') foreign_2 = models.ForeignKey(Target2, related_name='m2m_tgt') - + m2m_1 = models.ManyToManyField(Target2, related_name='foreign_tgt') m2m_2 = models.ManyToManyField(Target2, related_name='m2m_tgt') - + class ClashForeign(models.Model): foreign = models.ForeignKey(Target2) class ClashM2M(models.Model): m2m = models.ManyToManyField(Target2) - + class SelfClashForeign(models.Model): src_safe = models.CharField(maxlength=10) - - selfclashforeign_set = models.ForeignKey("SelfClashForeign") + + selfclashforeign_set = models.ForeignKey("SelfClashForeign") foreign_1 = models.ForeignKey("SelfClashForeign", related_name='id') foreign_2 = models.ForeignKey("SelfClashForeign", related_name='src_safe') diff --git a/tests/modeltests/m2m_and_m2o/models.py b/tests/modeltests/m2m_and_m2o/models.py index 29631e5779..7a685ecbd2 100644 --- a/tests/modeltests/m2m_and_m2o/models.py +++ b/tests/modeltests/m2m_and_m2o/models.py @@ -1,8 +1,7 @@ """ -27. Many-to-many and many-to-one relationships to the same table. - -This is a response to bug #1535 +28. Many-to-many and many-to-one relationships to the same table +Make sure to set ``related_name`` if you use relationships to the same table. """ from django.db import models @@ -14,9 +13,10 @@ class Issue(models.Model): num = models.IntegerField() cc = models.ManyToManyField(User, blank=True, related_name='test_issue_cc') client = models.ForeignKey(User, related_name='test_issue_client') - def __repr__(self): - return "" % (self.num,) - + + def __str__(self): + return str(self.num) + class Meta: ordering = ('num',) @@ -47,20 +47,20 @@ API_TESTS = """ >>> i3.cc.add(r) >>> from django.db.models.query import Q >>> Issue.objects.filter(client=r.id) -[, ] +[, ] >>> Issue.objects.filter(client=g.id) -[] +[] >>> Issue.objects.filter(cc__id__exact=g.id) [] >>> Issue.objects.filter(cc__id__exact=r.id) -[, ] +[, ] # Queries that combine results from the m2m and the m2o relationship. # 3 ways of saying the same thing: >>> Issue.objects.filter(Q(cc__id__exact=r.id) | Q(client=r.id)) -[, , ] +[, , ] >>> Issue.objects.filter(cc__id__exact=r.id) | Issue.objects.filter(client=r.id) -[, , ] +[, , ] >>> Issue.objects.filter(Q(client=r.id) | Q(cc__id__exact=r.id)) -[, , ] +[, , ] """ diff --git a/tests/modeltests/m2m_recursive/models.py b/tests/modeltests/m2m_recursive/models.py index ff8a5a8f47..877a41fd14 100644 --- a/tests/modeltests/m2m_recursive/models.py +++ b/tests/modeltests/m2m_recursive/models.py @@ -1,15 +1,15 @@ """ -26. Many-to-many relationships between the same two tables +27. Many-to-many relationships between the same two tables -In this example, A Person can have many friends, who are also people. Friendship is a +In this example, A Person can have many friends, who are also people. Friendship is a symmetrical relationshiup - if I am your friend, you are my friend. A person can also have many idols - but while I may idolize you, you may not think -the same of me. 'Idols' is an example of a non-symmetrical m2m field. Only recursive +the same of me. 'Idols' is an example of a non-symmetrical m2m field. Only recursive m2m fields may be non-symmetrical, and they are symmetrical by default. This test validates that the m2m table will create a mangled name for the m2m table if -there will be a clash, and tests that symmetry is preserved where appropriate. +there will be a clash, and tests that symmetry is preserved where appropriate. """ from django.db import models @@ -40,7 +40,7 @@ API_TESTS = """ >>> d.friends.add(a,c) # Who is friends with Anne? ->>> a.friends.all() +>>> a.friends.all() [Bill, Chuck, David] # Who is friends with Bill? @@ -52,14 +52,14 @@ API_TESTS = """ [Anne, David] # Who is friends with David? ->>> d.friends.all() +>>> d.friends.all() [Anne, Chuck] # Bill is already friends with Anne - add Anne again, but in the reverse direction >>> b.friends.add(a) # Who is friends with Anne? ->>> a.friends.all() +>>> a.friends.all() [Bill, Chuck, David] # Who is friends with Bill? @@ -70,7 +70,7 @@ API_TESTS = """ >>> b.friends.remove(a) # Who is friends with Anne? ->>> a.friends.all() +>>> a.friends.all() [Chuck, David] # Who is friends with Bill? @@ -81,7 +81,7 @@ API_TESTS = """ >>> a.friends.clear() # Who is friends with Anne? ->>> a.friends.all() +>>> a.friends.all() [] # Reverse relationships should also be gone @@ -90,7 +90,7 @@ API_TESTS = """ [David] # Who is friends with David? ->>> d.friends.all() +>>> d.friends.all() [Chuck] @@ -105,7 +105,7 @@ API_TESTS = """ >>> d.stalkers.add(a,c) # Who are Anne's idols? ->>> a.idols.all() +>>> a.idols.all() [Bill, Chuck, David] # Who is stalking Anne? @@ -140,7 +140,7 @@ API_TESTS = """ >>> b.stalkers.add(a) # Who are Anne's idols? ->>> a.idols.all() +>>> a.idols.all() [Bill, Chuck, David] # Who is stalking Anne? @@ -158,7 +158,7 @@ API_TESTS = """ >>> b.stalkers.remove(a) # Who are Anne's idols? ->>> a.idols.all() +>>> a.idols.all() [Chuck, David] # Who is stalking Anne? @@ -177,7 +177,7 @@ API_TESTS = """ >>> a.idols.clear() # Who are Anne's idols ->>> a.idols.all() +>>> a.idols.all() [] # Reverse relationships should also be gone @@ -186,7 +186,7 @@ API_TESTS = """ [] # Who is friends with David? ->>> d.stalkers.all() +>>> d.stalkers.all() [Chuck] """ diff --git a/tests/modeltests/manipulators/models.py b/tests/modeltests/manipulators/models.py index da47c0afd5..cf833cc468 100644 --- a/tests/modeltests/manipulators/models.py +++ b/tests/modeltests/manipulators/models.py @@ -1,5 +1,7 @@ """ -25. Default manipulators +26. Default manipulators + +Each model gets an AddManipulator and ChangeManipulator by default. """ from django.db import models diff --git a/tests/modeltests/model_inheritance/models.py b/tests/modeltests/model_inheritance/models.py index cdc4b4e2ac..f2d184017c 100644 --- a/tests/modeltests/model_inheritance/models.py +++ b/tests/modeltests/model_inheritance/models.py @@ -1,6 +1,7 @@ """ XX. Model inheritance +Model inheritance isn't yet supported. """ from django.db import models diff --git a/tests/modeltests/pagination/models.py b/tests/modeltests/pagination/models.py index 6525168b97..3667c84d69 100644 --- a/tests/modeltests/pagination/models.py +++ b/tests/modeltests/pagination/models.py @@ -1,21 +1,20 @@ """ -20. Object Pagination - -Django provides a framework for paginating a list of objects in a few. -This is often useful for dividing search results or long lists of objects -in to easily readable pages. - +29. Object pagination +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. """ + from django.db import models class Article(models.Model): headline = models.CharField(maxlength=100, default='Default headline') pub_date = models.DateTimeField() - + def __repr__(self): - return self.headline - + return self.headline + API_TESTS = """ # prepare a list of objects for pagination >>> from datetime import datetime @@ -34,8 +33,8 @@ API_TESTS = """ >>> paginator.pages 2 -# get the first page (zero-based) ->>> paginator.get_page(0) +# get the first page (zero-based) +>>> paginator.get_page(0) [Article 1, Article 2, Article 3, Article 4, Article 5] # get the second page @@ -45,7 +44,7 @@ API_TESTS = """ # does the first page have a next or previous page? >>> paginator.has_next_page(0) True - + >>> paginator.has_previous_page(0) False @@ -55,5 +54,14 @@ 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 + """ diff --git a/tests/modeltests/properties/models.py b/tests/modeltests/properties/models.py index 2c2190e989..e9d8da9594 100644 --- a/tests/modeltests/properties/models.py +++ b/tests/modeltests/properties/models.py @@ -1,5 +1,7 @@ """ 22. Using properties on models + +Use properties on models just like on any other Python object. """ from django.db import models diff --git a/tests/modeltests/transactions/models.py b/tests/modeltests/transactions/models.py index 22f38f7a0c..ef7791c3f3 100644 --- a/tests/modeltests/transactions/models.py +++ b/tests/modeltests/transactions/models.py @@ -1,5 +1,5 @@ """ -XXX. Transactions +15. Transactions Django handles transactions in three different ways. The default is to commit each transaction upon a write, but you can decorate a function to get diff --git a/tests/modeltests/validation/models.py b/tests/modeltests/validation/models.py index d03fffea25..8904c42727 100644 --- a/tests/modeltests/validation/models.py +++ b/tests/modeltests/validation/models.py @@ -1,5 +1,7 @@ """ -XX. Validation +30. Validation + +This is an experimental feature! Each model instance has a validate() method that returns a dictionary of validation errors in the instance's fields. This method has a side effect