diff options
author | Arthur Koziel <arthur@arthurkoziel.com> | 2010-09-13 00:04:27 +0000 |
---|---|---|
committer | Arthur Koziel <arthur@arthurkoziel.com> | 2010-09-13 00:04:27 +0000 |
commit | dd49269c7db008b2567f50cb03c4d3d9b321daa1 (patch) | |
tree | 326dd25bb045ac016cda7966b43cbdfe1f67d699 | |
parent | c9b188c4ec939abbe48dae5a371276742e64b6b8 (diff) | |
download | django-dd49269c7db008b2567f50cb03c4d3d9b321daa1.tar.gz |
[soc2010/app-loading] merged trunkarchive/soc2010/app-loadingsoc2010/app-loading
git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2010/app-loading@13818 bcc190cf-cafb-0310-a4f2-bffc1f526a37
473 files changed, 19779 insertions, 9394 deletions
@@ -29,7 +29,6 @@ answer newbie questions, and generally made Django that much better: Gisle Aas <gisle@aas.no> ajs <adi@sieker.info> alang@bright-green.com - Alcides Fonseca Andi Albrecht <albrecht.andi@gmail.com> Marty Alchin <gulopine@gamemusic.org> Ahmad Alhashemi <trans@ahmadh.com> @@ -39,7 +38,6 @@ answer newbie questions, and generally made Django that much better: AgarFu <heaven@croasanaso.sytes.net> Dagur Páll Ammendrup <dagurp@gmail.com> Collin Anderson <cmawebsite@gmail.com> - Nicolas Lara <nicolaslara@gmail.com> Jeff Anderson <jefferya@programmerq.net> Marian Andre <django@andre.sk> Andreas @@ -85,19 +83,19 @@ answer newbie questions, and generally made Django that much better: Sean Brant Andrew Brehaut <http://brehaut.net/blog> brut.alll@gmail.com + bthomas btoll@bestweb.net Jonathan Buchanan <jonathan.buchanan@gmail.com> Keith Bussell <kbussell@gmail.com> + C8E Chris Cahoon <chris.cahoon@gmail.com> Juan Manuel Caicedo <juan.manuel.caicedo@gmail.com> Trevor Caira <trevor@caira.com> Brett Cannon <brett@python.org> Ricardo Javier Cárdenes Medina <ricardo.cardenes@gmail.com> Jeremy Carbaugh <jcarbaugh@gmail.com> - Carl Meyer <carl@dirtcircle.com> Graham Carlyle <graham.carlyle@maplecroft.net> Antonio Cavedoni <http://cavedoni.com/> - C8E cedric@terramater.net Chris Chamberlin <dja@cdc.msbx.net> Amit Chakradeo <http://amit.chakradeo.net/> @@ -110,6 +108,7 @@ answer newbie questions, and generally made Django that much better: Michal Chruszcz <troll@pld-linux.org> Can Burak Çilingir <canburak@cs.bilgi.edu.tr> Ian Clelland <clelland@gmail.com> + Travis Cline <travis.cline@gmail.com> Russell Cloran <russell@rucus.net> colin@owlfish.com crankycoder@gmail.com @@ -137,6 +136,7 @@ answer newbie questions, and generally made Django that much better: Rajesh Dhawan <rajesh.dhawan@gmail.com> Sander Dijkhuis <sander.dijkhuis@gmail.com> Jordan Dimov <s3x3y1@gmail.com> + NebojÅ¡a DorÄ‘ević dne@mayonnaise.net dready <wil@mojipage.com> Maximillian Dornseif <md@hudora.de> @@ -167,7 +167,6 @@ answer newbie questions, and generally made Django that much better: Liang Feng <hutuworm@gmail.com> Bill Fenner <fenner@gmail.com> Stefane Fermgier <sf@fermigier.com> - Afonso Fernández Nogueira <fonzzo.django@gmail.com> J. Pablo Fernandez <pupeno@pupeno.com> Maciej Fijalkowski Ben Firshman <ben@firshman.co.uk> @@ -175,6 +174,7 @@ answer newbie questions, and generally made Django that much better: Eric Floehr <eric@intellovations.com> Eric Florenzano <floguy@gmail.com> Vincent Foley <vfoleybourgon@yahoo.ca> + Alcides Fonseca Rudolph Froger <rfroger@estrate.nl> Jorge Gajon <gajon@gajon.org> gandalf@owca.info @@ -184,11 +184,13 @@ answer newbie questions, and generally made Django that much better: Idan Gazit geber@datacollect.com Baishampayan Ghose + Joshua Ginsberg <jag@flowtheory.net> Dimitris Glezos <dimitris@glezos.com> glin@seznam.cz martin.glueck@gmail.com Artyom Gnilov <boobsd@gmail.com> Ben Godfrey <http://aftnn.org> + Andrew Godwin <andrew@aeracode.org> GomoX <gomo@datafull.com> Guilherme Mesquita Gondim <semente@taurinus.org> Mario Gonzalez <gonzalemario@gmail.com> @@ -220,6 +222,7 @@ answer newbie questions, and generally made Django that much better: Kieran Holland <http://www.kieranholland.com> Sung-Jin Hong <serialx.net@gmail.com> Leo "hylje" Honkanen <sealage@gmail.com> + Matt Hoskins <skaffenuk@googlemail.com> Tareque Hossain <http://www.codexn.com> Richard House <Richard.House@i-logue.com> Robert Rock Howard <http://djangomojo.com/> @@ -235,6 +238,7 @@ answer newbie questions, and generally made Django that much better: jcrasta@gmail.com jdetaeye jhenry <jhenry@theonion.com> + john@calixto.net Zak Johnson <zakj@nox.cx> Nis Jørgensen <nis@superlativ.dk> Michael Josephson <http://www.sdjournal.com/> @@ -275,11 +279,11 @@ answer newbie questions, and generally made Django that much better: kurtiss@meetro.com Denis Kuzmichyov <kuzmichyov@gmail.com> Panos Laganakos <panos.laganakos@gmail.com> - Lakin Wecker <lakin@structuredabstraction.com> Nick Lane <nick.lane.au@gmail.com> Stuart Langridge <http://www.kryogenix.org/> Paul Lanier <planier@google.com> David Larlet <http://david.larlet.fr> + Nicolas Lara <nicolaslara@gmail.com> Nicola Larosa <nico@teknico.net> Finn Gruwier Larsen <finn@gruwier.dk> Lau Bech Lauritzen @@ -300,7 +304,6 @@ answer newbie questions, and generally made Django that much better: Simon Litchfield <simon@quo.com.au> Daniel Lindsley <polarcowz@gmail.com> Trey Long <trey@ktrl.com> - msaelices <msaelices@gmail.com> Martin Mahner <http://www.mahner.org/> Matt McClanahan <http://mmcc.cx/> Stanislaus Madueke @@ -313,20 +316,21 @@ answer newbie questions, and generally made Django that much better: Petr Marhoun <petr.marhoun@gmail.com> Petar Marić <http://www.petarmaric.com/> Nuno Mariz <nmariz@gmail.com> - Marijn Vriens <marijn@metronomo.cl> mark@junklight.com Orestis Markou <orestis@orestis.gr> Takashi Matsuo <matsuo.takashi@gmail.com> Yasushi Masuda <whosaysni@gmail.com> mattycakes@gmail.com + Glenn Maynard <glenn@zewt.org> Jason McBrayer <http://www.carcosa.net/jason/> Kevin McConnell <kevin.mcconnell@gmail.com> mccutchen@gmail.com + michael.mcewan@gmail.com Paul McLanahan <paul@mclanahan.net> Tobias McNulty <http://www.caktusgroup.com/blog> Zain Memon Christian Metts - michael.mcewan@gmail.com + Carl Meyer <carl@dirtcircle.com> michal@plovarna.cz Slawek Mikula <slawek dot mikula at gmail dot com> mitakummaa@gmail.com @@ -336,12 +340,14 @@ answer newbie questions, and generally made Django that much better: Aljosa Mohorovic <aljosa.mohorovic@gmail.com> Ramiro Morales <rm0@gmx.net> Eric Moritz <http://eric.themoritzfamily.com/> + msaelices <msaelices@gmail.com> + Gregor Müllegger <gregor@muellegger.de> Robin Munn <http://www.geekforgod.com/> James Murty msundstr Robert Myers <myer0052@gmail.com> + Aaron T. Myers <atmyers@gmail.com> Alexander Myodov <alex@myodov.com> - NebojÅ¡a DorÄ‘ević Doug Napoleone <doug@dougma.com> Gopal Narayanan <gopastro@gmail.com> Fraser Nevett <mail@nevett.org> @@ -369,7 +375,6 @@ answer newbie questions, and generally made Django that much better: phil.h.smith@gmail.com Gustavo Picon Michael Placentra II <someone@michaelplacentra2.net> - Luke Plant <http://lukeplant.me.uk/> plisk Daniel Poelzleithner <http://poelzi.org/> polpak@yahoo.com @@ -400,7 +405,6 @@ answer newbie questions, and generally made Django that much better: Henrique Romano <onaiort@gmail.com> Armin Ronacher Daniel Roseman <http://roseman.org.uk/> - Brian Rosner <brosner@gmail.com> Rozza <ross.lawley@gmail.com> Oliver Rutherfurd <http://rutherfurd.net/> ryankanno @@ -479,6 +483,7 @@ answer newbie questions, and generally made Django that much better: George Vilches <gav@thataddress.com> Vlado <vlado@labath.org> Zachary Voase <zacharyvoase@gmail.com> + Marijn Vriens <marijn@metronomo.cl> Milton Waddams Chris Wagner <cw264701@ohio.edu> Rick Wagner <rwagner@physics.ucsd.edu> @@ -487,6 +492,7 @@ answer newbie questions, and generally made Django that much better: Filip Wasilewski <filip.wasilewski@gmail.com> Dan Watson <http://theidioteque.net/> Joel Watts <joel@joelwatts.com> + Lakin Wecker <lakin@structuredabstraction.com> Chris Wesseling <Chris.Wesseling@cwi.nl> James Wheare <django@sparemint.com> Mike Wiacek <mjwiacek@google.com> @@ -508,8 +514,6 @@ answer newbie questions, and generally made Django that much better: Gasper Zejn <zejn@kiberpipa.org> Jarek Zgoda <jarek.zgoda@gmail.com> Cheng Zhang - Glenn Maynard <glenn@zewt.org> - bthomas A big THANK YOU goes to: diff --git a/MANIFEST.in b/MANIFEST.in index 3fbad67b1d..d12b2523cd 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -3,12 +3,12 @@ include AUTHORS include INSTALL include LICENSE include MANIFEST.in -include django/utils/simplejson/LICENSE.txt include django/contrib/gis/gdal/LICENSE include django/contrib/gis/geos/LICENSE +include django/dispatch/license.txt +include django/utils/simplejson/LICENSE.txt recursive-include docs * recursive-include scripts * -recursive-include examples * recursive-include extras * recursive-include tests * recursive-include django/conf/locale * @@ -20,6 +20,8 @@ recursive-include django/contrib/auth/tests/templates * recursive-include django/contrib/comments/templates * recursive-include django/contrib/databrowse/templates * recursive-include django/contrib/formtools/templates * +recursive-include django/contrib/flatpages/fixtures * +recursive-include django/contrib/flatpages/tests/templates * recursive-include django/contrib/gis/templates * recursive-include django/contrib/gis/tests/data * recursive-include django/contrib/gis/tests/geoapp/fixtures * diff --git a/django/__init__.py b/django/__init__.py index cdaf1ec40e..6293d15c47 100644 --- a/django/__init__.py +++ b/django/__init__.py @@ -1,4 +1,4 @@ -VERSION = (1, 2, 1, 'final', 0) +VERSION = (1, 3, 0, 'alpha', 0) def get_version(): version = '%s.%s' % (VERSION[0], VERSION[1]) diff --git a/django/conf/__init__.py b/django/conf/__init__.py index d94f6e9867..bda0ef3691 100644 --- a/django/conf/__init__.py +++ b/django/conf/__init__.py @@ -103,6 +103,12 @@ class Settings(object): self.INSTALLED_APPS = new_installed_apps if hasattr(time, 'tzset') and getattr(self, 'TIME_ZONE'): + # When we can, attempt to validate the timezone. If we can't find + # this file, no check happens and it's harmless. + zoneinfo_root = '/usr/share/zoneinfo' + if (os.path.exists(zoneinfo_root) and not + os.path.exists(os.path.join(zoneinfo_root, *(self.TIME_ZONE.split('/'))))): + raise ValueError("Incorrect timezone setting: %s" % self.TIME_ZONE) # Move the time zone info into os.environ. See ticket #2315 for why # we don't do this unconditionally (breaks Windows). os.environ['TZ'] = self.TIME_ZONE diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index 49dc3da8f9..84ca140a45 100644 --- a/django/conf/global_settings.py +++ b/django/conf/global_settings.py @@ -54,7 +54,7 @@ LANGUAGES = ( ('en', gettext_noop('English')), ('en-gb', gettext_noop('British English')), ('es', gettext_noop('Spanish')), - ('es-ar', gettext_noop('Argentinean Spanish')), + ('es-ar', gettext_noop('Argentinian Spanish')), ('et', gettext_noop('Estonian')), ('eu', gettext_noop('Basque')), ('fa', gettext_noop('Persian')), @@ -78,6 +78,7 @@ LANGUAGES = ( ('lt', gettext_noop('Lithuanian')), ('lv', gettext_noop('Latvian')), ('mk', gettext_noop('Macedonian')), + ('ml', gettext_noop('Malayalam')), ('mn', gettext_noop('Mongolian')), ('nl', gettext_noop('Dutch')), ('no', gettext_noop('Norwegian')), @@ -372,8 +373,8 @@ DECIMAL_SEPARATOR = '.' # Boolean that sets whether to add thousand separator when formatting numbers USE_THOUSAND_SEPARATOR = False -# Number of digits that will be togheter, when spliting them by THOUSAND_SEPARATOR -# 0 means no grouping, 3 means splitting by thousands... +# Number of digits that will be together, when spliting them by +# THOUSAND_SEPARATOR. 0 means no grouping, 3 means splitting by thousands... NUMBER_GROUPING = 0 # Thousand separator symbol diff --git a/django/conf/locale/ca/LC_MESSAGES/django.mo b/django/conf/locale/ca/LC_MESSAGES/django.mo Binary files differindex 457a828047..2002d5e601 100644 --- a/django/conf/locale/ca/LC_MESSAGES/django.mo +++ b/django/conf/locale/ca/LC_MESSAGES/django.mo diff --git a/django/conf/locale/ca/LC_MESSAGES/django.po b/django/conf/locale/ca/LC_MESSAGES/django.po index f8cd8aa609..2ebfc654cd 100644 --- a/django/conf/locale/ca/LC_MESSAGES/django.po +++ b/django/conf/locale/ca/LC_MESSAGES/django.po @@ -1,11 +1,11 @@ -# translation of django.po to +# translation of django.po to Catalan # This file is distributed under the same license as the Django package. # msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2009-11-30 11:19+0100\n" +"POT-Creation-Date: 2010-06-03 20:18+0200\n" "PO-Revision-Date: 2009-03-24 13:28+0100\n" "Last-Translator: Django Catalan Group <django-cat@googlegroups.com>\n" "Language-Team: Catalan <ca@li.org>\n" @@ -14,225 +14,265 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: conf/global_settings.py:44 +#: .\conf\global_settings.py:44 msgid "Arabic" -msgstr "arà bic" +msgstr "à rab" -#: conf/global_settings.py:45 +#: .\conf\global_settings.py:45 +msgid "Bulgarian" +msgstr "búlgar" + +#: .\conf\global_settings.py:46 msgid "Bengali" msgstr "bengalÃ" -#: conf/global_settings.py:46 -msgid "Bulgarian" -msgstr "búlgar" +#: .\conf\global_settings.py:47 +msgid "Bosnian" +msgstr "bosnià " -#: conf/global_settings.py:47 +#: .\conf\global_settings.py:48 msgid "Catalan" msgstr "català " -#: conf/global_settings.py:48 +#: .\conf\global_settings.py:49 msgid "Czech" msgstr "txec" -#: conf/global_settings.py:49 +#: .\conf\global_settings.py:50 msgid "Welsh" -msgstr "galès" +msgstr "gal·lès" -#: conf/global_settings.py:50 +#: .\conf\global_settings.py:51 msgid "Danish" msgstr "danès" -#: conf/global_settings.py:51 +#: .\conf\global_settings.py:52 msgid "German" msgstr "alemany" -#: conf/global_settings.py:52 +#: .\conf\global_settings.py:53 msgid "Greek" msgstr "grec" -#: conf/global_settings.py:53 +#: .\conf\global_settings.py:54 msgid "English" msgstr "anglès" -#: conf/global_settings.py:54 +#: .\conf\global_settings.py:55 +msgid "British English" +msgstr "anglès brità nic" + +#: .\conf\global_settings.py:56 msgid "Spanish" msgstr "espanyol" -#: conf/global_settings.py:55 -msgid "Estonian" -msgstr "estonià " - -#: conf/global_settings.py:56 +#: .\conf\global_settings.py:57 msgid "Argentinean Spanish" msgstr "castellà argentÃ" -#: conf/global_settings.py:57 +#: .\conf\global_settings.py:58 +msgid "Estonian" +msgstr "estonià " + +#: .\conf\global_settings.py:59 msgid "Basque" msgstr "euskera" -#: conf/global_settings.py:58 +#: .\conf\global_settings.py:60 msgid "Persian" msgstr "persa" -#: conf/global_settings.py:59 +#: .\conf\global_settings.py:61 msgid "Finnish" msgstr "finlandès" -#: conf/global_settings.py:60 +#: .\conf\global_settings.py:62 msgid "French" msgstr "francès" -#: conf/global_settings.py:61 +#: .\conf\global_settings.py:63 +msgid "Frisian" +msgstr "frisi" + +#: .\conf\global_settings.py:64 msgid "Irish" msgstr "irlandès" -#: conf/global_settings.py:62 +#: .\conf\global_settings.py:65 msgid "Galician" msgstr "gallec" -#: conf/global_settings.py:63 -msgid "Hungarian" -msgstr "húngar" - -#: conf/global_settings.py:64 +#: .\conf\global_settings.py:66 msgid "Hebrew" msgstr "hebreu" -#: conf/global_settings.py:65 +#: .\conf\global_settings.py:67 msgid "Hindi" msgstr "hindi" -#: conf/global_settings.py:66 +#: .\conf\global_settings.py:68 msgid "Croatian" msgstr "croat" -#: conf/global_settings.py:67 +#: .\conf\global_settings.py:69 +msgid "Hungarian" +msgstr "hongarès" + +#: .\conf\global_settings.py:70 +msgid "Indonesian" +msgstr "indonesi" + +#: .\conf\global_settings.py:71 msgid "Icelandic" msgstr "islandès" -#: conf/global_settings.py:68 +#: .\conf\global_settings.py:72 msgid "Italian" msgstr "italià " -#: conf/global_settings.py:69 +#: .\conf\global_settings.py:73 msgid "Japanese" msgstr "japonès" -#: conf/global_settings.py:70 +#: .\conf\global_settings.py:74 msgid "Georgian" msgstr "georgià " -#: conf/global_settings.py:71 -msgid "Korean" -msgstr "coreà " - -#: conf/global_settings.py:72 +#: .\conf\global_settings.py:75 msgid "Khmer" msgstr "khmer" -#: conf/global_settings.py:73 +#: .\conf\global_settings.py:76 msgid "Kannada" -msgstr "canès" +msgstr "kannarès" -#: conf/global_settings.py:74 -msgid "Latvian" -msgstr "letó" +#: .\conf\global_settings.py:77 +msgid "Korean" +msgstr "coreà " -#: conf/global_settings.py:75 +#: .\conf\global_settings.py:78 msgid "Lithuanian" msgstr "lituà " -#: conf/global_settings.py:76 +#: .\conf\global_settings.py:79 +msgid "Latvian" +msgstr "letó" + +#: .\conf\global_settings.py:80 msgid "Macedonian" msgstr "macedoni" -#: conf/global_settings.py:77 +#: .\conf\global_settings.py:81 +msgid "Mongolian" +msgstr "mongol" + +#: .\conf\global_settings.py:82 msgid "Dutch" msgstr "holandès" -#: conf/global_settings.py:78 +#: .\conf\global_settings.py:83 msgid "Norwegian" -msgstr "norueg" +msgstr "noruec" + +#: .\conf\global_settings.py:84 +msgid "Norwegian Bokmal" +msgstr "noruec bokmal" + +#: .\conf\global_settings.py:85 +msgid "Norwegian Nynorsk" +msgstr "noruec nynorsk" -#: conf/global_settings.py:79 +#: .\conf\global_settings.py:86 msgid "Polish" -msgstr "polac" +msgstr "polonès" -#: conf/global_settings.py:80 +#: .\conf\global_settings.py:87 msgid "Portuguese" msgstr "portuguès" -#: conf/global_settings.py:81 +#: .\conf\global_settings.py:88 msgid "Brazilian Portuguese" msgstr "portuguès de brasil" -#: conf/global_settings.py:82 +#: .\conf\global_settings.py:89 msgid "Romanian" -msgstr "rumanès" +msgstr "romanès" -#: conf/global_settings.py:83 +#: .\conf\global_settings.py:90 msgid "Russian" -msgstr "rús" +msgstr "rus" -#: conf/global_settings.py:84 +#: .\conf\global_settings.py:91 msgid "Slovak" msgstr "eslovac" -#: conf/global_settings.py:85 +#: .\conf\global_settings.py:92 msgid "Slovenian" -msgstr "esloveni" +msgstr "eslovè" -#: conf/global_settings.py:86 +#: .\conf\global_settings.py:93 +msgid "Albanian" +msgstr "albanès" + +#: .\conf\global_settings.py:94 msgid "Serbian" msgstr "serbi" -#: conf/global_settings.py:87 +#: .\conf\global_settings.py:95 +msgid "Serbian Latin" +msgstr "serbi llatÃ" + +#: .\conf\global_settings.py:96 msgid "Swedish" msgstr "suec" -#: conf/global_settings.py:88 +#: .\conf\global_settings.py:97 msgid "Tamil" msgstr "tà mil" -#: conf/global_settings.py:89 +#: .\conf\global_settings.py:98 msgid "Telugu" msgstr "telugu" -#: conf/global_settings.py:90 +#: .\conf\global_settings.py:99 msgid "Thai" msgstr "tailandès" -#: conf/global_settings.py:91 +#: .\conf\global_settings.py:100 msgid "Turkish" msgstr "turc" -#: conf/global_settings.py:92 +#: .\conf\global_settings.py:101 msgid "Ukrainian" -msgstr "ucranià " +msgstr "ucraïnès" + +#: .\conf\global_settings.py:102 +msgid "Vietnamese" +msgstr "vietnamita" -#: conf/global_settings.py:93 +#: .\conf\global_settings.py:103 msgid "Simplified Chinese" msgstr "xinès simplificat" -#: conf/global_settings.py:94 +#: .\conf\global_settings.py:104 msgid "Traditional Chinese" msgstr "xinès tradicional" -#: contrib/admin/actions.py:60 +#: .\contrib\admin\actions.py:48 #, python-format msgid "Successfully deleted %(count)d %(items)s." msgstr "Eliminat/s %(count)d %(items)s satisfactòriament." -#: contrib/admin/actions.py:67 contrib/admin/options.py:1033 +#: .\contrib\admin\actions.py:55 .\contrib\admin\options.py:1125 msgid "Are you sure?" -msgstr "Esteu segurs?" +msgstr "N'esteu segur?" -#: contrib/admin/actions.py:85 +#: .\contrib\admin\actions.py:73 #, python-format msgid "Delete selected %(verbose_name_plural)s" msgstr "Eliminar els %(verbose_name_plural)s seleccionats" -#: contrib/admin/filterspecs.py:44 +#: .\contrib\admin\filterspecs.py:44 #, python-format msgid "" "<h3>By %s:</h3>\n" @@ -241,175 +281,197 @@ msgstr "" "<h3>Per %s:</h3>\n" "<ul>\n" -#: contrib/admin/filterspecs.py:75 contrib/admin/filterspecs.py:92 -#: contrib/admin/filterspecs.py:147 contrib/admin/filterspecs.py:173 +#: .\contrib\admin\filterspecs.py:75 .\contrib\admin\filterspecs.py:92 +#: .\contrib\admin\filterspecs.py:147 .\contrib\admin\filterspecs.py:173 msgid "All" msgstr "Tots" -#: contrib/admin/filterspecs.py:113 +#: .\contrib\admin\filterspecs.py:113 msgid "Any date" msgstr "Qualsevol data" -#: contrib/admin/filterspecs.py:114 +#: .\contrib\admin\filterspecs.py:114 msgid "Today" msgstr "Avui" -#: contrib/admin/filterspecs.py:117 +#: .\contrib\admin\filterspecs.py:117 msgid "Past 7 days" msgstr "Últims 7 dies" -#: contrib/admin/filterspecs.py:119 +#: .\contrib\admin\filterspecs.py:119 msgid "This month" msgstr "Aquest mes" -#: contrib/admin/filterspecs.py:121 +#: .\contrib\admin\filterspecs.py:121 msgid "This year" msgstr "Aquest any" -#: contrib/admin/filterspecs.py:147 forms/widgets.py:435 +#: .\contrib\admin\filterspecs.py:147 .\forms\widgets.py:466 msgid "Yes" -msgstr "Si" +msgstr "SÃ" -#: contrib/admin/filterspecs.py:147 forms/widgets.py:435 +#: .\contrib\admin\filterspecs.py:147 .\forms\widgets.py:466 msgid "No" msgstr "No" -#: contrib/admin/filterspecs.py:154 forms/widgets.py:435 +#: .\contrib\admin\filterspecs.py:154 .\forms\widgets.py:466 msgid "Unknown" msgstr "Desconegut" -#: contrib/admin/helpers.py:14 +#: .\contrib\admin\helpers.py:20 msgid "Action:" msgstr "Acció:" -#: contrib/admin/models.py:19 +#: .\contrib\admin\models.py:19 msgid "action time" msgstr "moment de l'acció" -#: contrib/admin/models.py:22 +#: .\contrib\admin\models.py:22 msgid "object id" -msgstr "id del objecte" +msgstr "id de l'objecte" -#: contrib/admin/models.py:23 +#: .\contrib\admin\models.py:23 msgid "object repr" msgstr "'repr' de l'objecte" -#: contrib/admin/models.py:24 +#: .\contrib\admin\models.py:24 msgid "action flag" -msgstr "marca de l'acció" +msgstr "indicador de l'acció" -#: contrib/admin/models.py:25 +#: .\contrib\admin\models.py:25 msgid "change message" msgstr "missatge del canvi" -#: contrib/admin/models.py:28 +#: .\contrib\admin\models.py:28 msgid "log entry" msgstr "entrada del registre" -#: contrib/admin/models.py:29 +#: .\contrib\admin\models.py:29 msgid "log entries" msgstr "entrades del registre" -#: contrib/admin/options.py:134 contrib/admin/options.py:148 +#: .\contrib\admin\options.py:138 .\contrib\admin\options.py:153 msgid "None" msgstr "cap" -#: contrib/admin/options.py:521 +#: .\contrib\admin\options.py:559 #, python-format msgid "Changed %s." msgstr "Modificat %s." -#: contrib/admin/options.py:521 contrib/admin/options.py:531 -#: contrib/comments/templates/comments/preview.html:16 forms/models.py:384 -#: forms/models.py:596 +#: .\contrib\admin\options.py:559 .\contrib\admin\options.py:569 +#: .\contrib\comments\templates\comments\preview.html.py:16 +#: .\db\models\base.py:845 .\forms\models.py:568 msgid "and" msgstr "i" -#: contrib/admin/options.py:526 +#: .\contrib\admin\options.py:564 #, python-format msgid "Added %(name)s \"%(object)s\"." msgstr "Afegit %(name)s \"%(object)s\"" -#: contrib/admin/options.py:530 +#: .\contrib\admin\options.py:568 #, python-format msgid "Changed %(list)s for %(name)s \"%(object)s\"." msgstr "Modificat %(list)s per a %(name)s \"%(object)s\"." -#: contrib/admin/options.py:535 +#: .\contrib\admin\options.py:573 #, python-format msgid "Deleted %(name)s \"%(object)s\"." msgstr "Eliminat %(name)s \"%(object)s\"." -#: contrib/admin/options.py:539 +#: .\contrib\admin\options.py:577 msgid "No fields changed." -msgstr "Cap camp canviat." +msgstr "Cap camp modificat." -#: contrib/admin/options.py:601 contrib/auth/admin.py:67 +#: .\contrib\admin\options.py:643 #, python-format msgid "The %(name)s \"%(obj)s\" was added successfully." -msgstr "El/la %(name)s \"%(obj)s\".ha estat afegit/da amb èxit." +msgstr "El/la %(name)s \"%(obj)s\" ha estat afegit/da amb èxit." -#: contrib/admin/options.py:605 contrib/admin/options.py:638 -#: contrib/auth/admin.py:75 +#: .\contrib\admin\options.py:647 .\contrib\admin\options.py:680 msgid "You may edit it again below." -msgstr "Podeu editar-lo de nou a baix." +msgstr "Podeu editar-lo de nou a sota." -#: contrib/admin/options.py:615 contrib/admin/options.py:648 +#: .\contrib\admin\options.py:657 .\contrib\admin\options.py:690 #, python-format msgid "You may add another %s below." -msgstr "Podeu afegir un altre %s a baix." +msgstr "Podeu afegir un altre %s a sota." -#: contrib/admin/options.py:636 +#: .\contrib\admin\options.py:678 #, python-format msgid "The %(name)s \"%(obj)s\" was changed successfully." msgstr "S'ha modificat amb èxit el/la %(name)s \"%(obj)s." -#: contrib/admin/options.py:644 +#: .\contrib\admin\options.py:686 #, python-format msgid "" "The %(name)s \"%(obj)s\" was added successfully. You may edit it again below." msgstr "" -"S'ha afegit exitosament el/la %(name)s \"%(obj)s\". Pot editar-lo de nou " -"abaix." +"S'ha afegit amb èxit el/la %(name)s \"%(obj)s\". Pot editar-lo de nou a sota." -#: contrib/admin/options.py:777 +#: .\contrib\admin\options.py:740 .\contrib\admin\options.py:997 +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "" +"Heu de seleccionar els elements per poder realitzar-hi accions. " +"No heu seleccionat cap element." + +#: .\contrib\admin\options.py:759 +msgid "No action selected." +msgstr "no heu seleccionat cap acció" + +#: .\contrib\admin\options.py:840 #, python-format msgid "Add %s" msgstr "Afegir %s" -#: contrib/admin/options.py:809 contrib/admin/options.py:1011 +#: .\contrib\admin\options.py:866 .\contrib\admin\options.py:1105 #, python-format msgid "%(name)s object with primary key %(key)r does not exist." -msgstr "No existèix cap objecte %(name)s amb la clau primà ria %(key)r." +msgstr "No existeix cap objecte %(name)s amb la clau primà ria %(key)r." -#: contrib/admin/options.py:866 +#: .\contrib\admin\options.py:931 #, python-format msgid "Change %s" msgstr "Modificar %s" -#: contrib/admin/options.py:910 +#: .\contrib\admin\options.py:977 msgid "Database error" msgstr "Error de base de dades" -#: contrib/admin/options.py:946 +#: .\contrib\admin\options.py:1039 #, python-format msgid "%(count)s %(name)s was changed successfully." msgid_plural "%(count)s %(name)s were changed successfully." msgstr[0] "%(count)s %(name)s s'ha modificat amb èxit." msgstr[1] "%(count)s %(name)s s'han modificat amb èxit." -#: contrib/admin/options.py:1026 +#: .\contrib\admin\options.py:1066 +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s seleccionat(s)" +msgstr[1] "Tots %(total_count)s seleccionat(s)" + +#: .\contrib\admin\options.py:1071 +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "0 de %(cnt)s seleccionats" + +#: .\contrib\admin\options.py:1118 #, python-format msgid "The %(name)s \"%(obj)s\" was deleted successfully." -msgstr "El/la %(name)s \"%(obj)s\" ha estat eliminat amb èxit." +msgstr "El/la %(name)s \"%(obj)s\" s'ha eliminat amb èxit." -#: contrib/admin/options.py:1063 +#: .\contrib\admin\options.py:1155 #, python-format msgid "Change history: %s" msgstr "Modificar històric: %s" -#: contrib/admin/sites.py:22 contrib/admin/views/decorators.py:14 -#: contrib/auth/forms.py:80 +#: .\contrib\admin\sites.py:18 .\contrib\admin\views\decorators.py:14 +#: .\contrib\auth\forms.py:81 msgid "" "Please enter a correct username and password. Note that both fields are case-" "sensitive." @@ -417,216 +479,223 @@ msgstr "" "Si us plau, introduïu un nom d'usuari i contrasenya và lids. Tingueu en " "compte que tots dos camps son sensibles a majúscules i minúscules." -#: contrib/admin/sites.py:292 contrib/admin/views/decorators.py:40 +#: .\contrib\admin\sites.py:307 .\contrib\admin\views\decorators.py:40 msgid "Please log in again, because your session has expired." -msgstr "Si us plau, identifiqueu-vos de nou doncs la vostra sessió ha expirat." +msgstr "Si us plau, entreu de nou perquè la vostra sessió ha caducat." -#: contrib/admin/sites.py:299 contrib/admin/views/decorators.py:47 +#: .\contrib\admin\sites.py:314 .\contrib\admin\views\decorators.py:47 msgid "" "Looks like your browser isn't configured to accept cookies. Please enable " "cookies, reload this page, and try again." msgstr "" -"Sembla ser que el vostre navegador no està configurat per acceptar " -"'cookies' (galetes). Si us plau, habiliteu les 'cookies', recarregueu " -"aquesta pà gina i proveu-ho de nou. " +"Sembla ser que el vostre navegador no està configurat per acceptar galetes " +"('cookies'). Si us plau, habiliteu les galetes, recarregueu aquesta pà gina i " +"proveu-ho de nou. " -#: contrib/admin/sites.py:315 contrib/admin/sites.py:321 -#: contrib/admin/views/decorators.py:66 +#: .\contrib\admin\sites.py:330 .\contrib\admin\sites.py:336 +#: .\contrib\admin\views\decorators.py:66 msgid "Usernames cannot contain the '@' character." -msgstr "Els noms d'usuari no poden contenir el caracter '@'." +msgstr "Els noms d'usuari no poden contenir el carà cter '@'." -#: contrib/admin/sites.py:318 contrib/admin/views/decorators.py:62 +#: .\contrib\admin\sites.py:333 .\contrib\admin\views\decorators.py:62 #, python-format msgid "Your e-mail address is not your username. Try '%s' instead." msgstr "" -"La vostra adreça de correu no és el vostre nom d'usuari. Provi '%s' en tot " -"cas." +"La vostra adreça de correu no és el vostre nom d'usuari. Provi '%s' en el " +"seu lloc." -#: contrib/admin/sites.py:374 +#: .\contrib\admin\sites.py:389 msgid "Site administration" -msgstr "Lloc administratiu" +msgstr "Administració del lloc" -#: contrib/admin/sites.py:388 contrib/admin/templates/admin/login.html:26 -#: contrib/admin/templates/registration/password_reset_complete.html:14 -#: contrib/admin/views/decorators.py:20 +#: .\contrib\admin\sites.py:403 +#: .\contrib\admin\templates\admin\login.html.py:26 +#: .\contrib\admin\templates\registration\password_reset_complete.html.py:14 +#: .\contrib\admin\views\decorators.py:20 msgid "Log in" msgstr "Iniciar sessió" -#: contrib/admin/sites.py:433 +#: .\contrib\admin\sites.py:448 #, python-format msgid "%s administration" msgstr "Administració de %s" -#: contrib/admin/util.py:168 -#, python-format -msgid "One or more %(fieldname)s in %(name)s: %(obj)s" -msgstr "Un o més %(fieldname)s en %(name)s: %(obj)s" - -#: contrib/admin/util.py:173 -#, python-format -msgid "One or more %(fieldname)s in %(name)s:" -msgstr "Un o més %(fieldname)s en %(name)s:" - -#: contrib/admin/widgets.py:72 +#: .\contrib\admin\widgets.py:75 msgid "Date:" msgstr "Data:" -#: contrib/admin/widgets.py:72 +#: .\contrib\admin\widgets.py:75 msgid "Time:" msgstr "Hora:" -#: contrib/admin/widgets.py:96 +#: .\contrib\admin\widgets.py:99 msgid "Currently:" msgstr "Actualment:" -#: contrib/admin/widgets.py:96 +#: .\contrib\admin\widgets.py:99 msgid "Change:" msgstr "Modificar:" -#: contrib/admin/widgets.py:125 +#: .\contrib\admin\widgets.py:129 msgid "Lookup" msgstr "Cercar" -#: contrib/admin/widgets.py:237 +#: .\contrib\admin\widgets.py:244 msgid "Add Another" -msgstr "Afegir un altre" +msgstr "Afegir-ne un altre" -#: contrib/admin/templates/admin/404.html:4 -#: contrib/admin/templates/admin/404.html:8 +#: .\contrib\admin\templates\admin\404.html.py:4 +#: .\contrib\admin\templates\admin\404.html.py:8 msgid "Page not found" msgstr "No s'ha pogut trobar la pà gina" -#: contrib/admin/templates/admin/404.html:10 +#: .\contrib\admin\templates\admin\404.html.py:10 msgid "We're sorry, but the requested page could not be found." msgstr "Ho sentim, però no s'ha pogut trobar la pà gina sol·licitada" -#: contrib/admin/templates/admin/500.html:4 -#: contrib/admin/templates/admin/app_index.html:8 -#: contrib/admin/templates/admin/base.html:54 -#: contrib/admin/templates/admin/change_form.html:17 -#: contrib/admin/templates/admin/change_list.html:25 -#: contrib/admin/templates/admin/delete_confirmation.html:6 -#: contrib/admin/templates/admin/delete_selected_confirmation.html:6 -#: contrib/admin/templates/admin/invalid_setup.html:4 -#: contrib/admin/templates/admin/object_history.html:6 -#: contrib/admin/templates/admin/auth/user/change_password.html:10 -#: contrib/admin/templates/registration/logged_out.html:4 -#: contrib/admin/templates/registration/password_change_done.html:4 -#: contrib/admin/templates/registration/password_change_form.html:4 -#: contrib/admin/templates/registration/password_reset_complete.html:4 -#: contrib/admin/templates/registration/password_reset_confirm.html:4 -#: contrib/admin/templates/registration/password_reset_done.html:4 -#: contrib/admin/templates/registration/password_reset_form.html:4 -#: contrib/admindocs/templates/admin_doc/bookmarklets.html:3 +#: .\contrib\admin\templates\admin\500.html.py:4 +#: .\contrib\admin\templates\admin\app_index.html.py:8 +#: .\contrib\admin\templates\admin\base.html.py:55 +#: .\contrib\admin\templates\admin\change_form.html.py:18 +#: .\contrib\admin\templates\admin\change_list.html.py:42 +#: .\contrib\admin\templates\admin\delete_confirmation.html.py:6 +#: .\contrib\admin\templates\admin\delete_selected_confirmation.html.py:6 +#: .\contrib\admin\templates\admin\invalid_setup.html.py:4 +#: .\contrib\admin\templates\admin\object_history.html.py:6 +#: .\contrib\admin\templates\admin\auth\user\change_password.html.py:11 +#: .\contrib\admin\templates\registration\logged_out.html.py:4 +#: .\contrib\admin\templates\registration\password_change_done.html.py:4 +#: .\contrib\admin\templates\registration\password_change_form.html.py:5 +#: .\contrib\admin\templates\registration\password_reset_complete.html.py:4 +#: .\contrib\admin\templates\registration\password_reset_confirm.html.py:4 +#: .\contrib\admin\templates\registration\password_reset_done.html.py:4 +#: .\contrib\admin\templates\registration\password_reset_form.html.py:4 +#: .\contrib\admindocs\templates\admin_doc\bookmarklets.html.py:3 msgid "Home" msgstr "Inici" -#: contrib/admin/templates/admin/500.html:4 +#: .\contrib\admin\templates\admin\500.html.py:4 msgid "Server error" msgstr "Error del servidor" -#: contrib/admin/templates/admin/500.html:6 +#: .\contrib\admin\templates\admin\500.html.py:6 msgid "Server error (500)" msgstr "Error del servidor (500)" -#: contrib/admin/templates/admin/500.html:9 +#: .\contrib\admin\templates\admin\500.html.py:9 msgid "Server Error <em>(500)</em>" msgstr "Error del servidor <em>(500)</em>" -#: contrib/admin/templates/admin/500.html:10 +#: .\contrib\admin\templates\admin\500.html.py:10 msgid "" "There's been an error. It's been reported to the site administrators via e-" "mail and should be fixed shortly. Thanks for your patience." msgstr "" -"Ha ocorregut un error. S'ha informat als administradors del lloc per correu " -"electrònic y hauria d'arreglar-se en breu. Grà cies per la vostra paciència." +"S'ha produït un error. Se n'ha informat els administradors del lloc per " +"correu electrònic, i hauria d'arreglar-se en breu. Grà cies per la vostra " +"paciència." -#: contrib/admin/templates/admin/actions.html:4 +#: .\contrib\admin\templates\admin\actions.html.py:4 msgid "Run the selected action" -msgstr "Executar la acció seleccionada" +msgstr "Executar l'acció seleccionada" -#: contrib/admin/templates/admin/actions.html:4 +#: .\contrib\admin\templates\admin\actions.html.py:4 msgid "Go" msgstr "Anar" -#: contrib/admin/templates/admin/app_index.html:10 -#: contrib/admin/templates/admin/index.html:19 +#: .\contrib\admin\templates\admin\actions.html.py:11 +msgid "Click here to select the objects across all pages" +msgstr "Feu clic aquà per seleccionar els objectes a totes les pà gines" + +#: .\contrib\admin\templates\admin\actions.html.py:11 +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "Seleccioneu tots %(total_count)s %(module_name)s" + +#: .\contrib\admin\templates\admin\actions.html.py:13 +msgid "Clear selection" +msgstr "Netejar la selecció" + +#: .\contrib\admin\templates\admin\app_index.html.py:10 +#: .\contrib\admin\templates\admin\index.html.py:19 #, python-format msgid "%(name)s" msgstr "%(name)s" -#: contrib/admin/templates/admin/base.html:27 +#: .\contrib\admin\templates\admin\base.html.py:28 msgid "Welcome," msgstr "Benvingut/da," -#: contrib/admin/templates/admin/base.html:32 -#: contrib/admin/templates/registration/password_change_done.html:3 -#: contrib/admin/templates/registration/password_change_form.html:3 -#: contrib/admindocs/templates/admin_doc/bookmarklets.html:3 +#: .\contrib\admin\templates\admin\base.html.py:33 +#: .\contrib\admin\templates\registration\password_change_done.html.py:3 +#: .\contrib\admin\templates\registration\password_change_form.html.py:4 +#: .\contrib\admindocs\templates\admin_doc\bookmarklets.html.py:3 msgid "Documentation" msgstr "Documentació" -#: contrib/admin/templates/admin/base.html:40 -#: contrib/admin/templates/admin/auth/user/change_password.html:14 -#: contrib/admin/templates/admin/auth/user/change_password.html:47 -#: contrib/admin/templates/registration/password_change_done.html:3 -#: contrib/admin/templates/registration/password_change_form.html:3 +#: .\contrib\admin\templates\admin\base.html.py:41 +#: .\contrib\admin\templates\admin\auth\user\change_password.html.py:15 +#: .\contrib\admin\templates\admin\auth\user\change_password.html.py:48 +#: .\contrib\admin\templates\registration\password_change_done.html.py:3 +#: .\contrib\admin\templates\registration\password_change_form.html.py:4 msgid "Change password" msgstr "Canviar contrasenya" -#: contrib/admin/templates/admin/base.html:47 -#: contrib/admin/templates/registration/password_change_done.html:3 -#: contrib/admin/templates/registration/password_change_form.html:3 +#: .\contrib\admin\templates\admin\base.html.py:48 +#: .\contrib\admin\templates\registration\password_change_done.html.py:3 +#: .\contrib\admin\templates\registration\password_change_form.html.py:4 msgid "Log out" msgstr "Finalitzar sessió" -#: contrib/admin/templates/admin/base_site.html:4 +#: .\contrib\admin\templates\admin\base_site.html.py:4 msgid "Django site admin" msgstr "Lloc administratiu de Django" -#: contrib/admin/templates/admin/base_site.html:7 +#: .\contrib\admin\templates\admin\base_site.html.py:7 msgid "Django administration" msgstr "Administració de Django" -#: contrib/admin/templates/admin/change_form.html:20 -#: contrib/admin/templates/admin/index.html:29 +#: .\contrib\admin\templates\admin\change_form.html.py:21 +#: .\contrib\admin\templates\admin\index.html.py:29 msgid "Add" msgstr "Afegir" -#: contrib/admin/templates/admin/change_form.html:27 -#: contrib/admin/templates/admin/object_history.html:10 +#: .\contrib\admin\templates\admin\change_form.html.py:28 +#: .\contrib\admin\templates\admin\object_history.html.py:10 msgid "History" msgstr "Històric" -#: contrib/admin/templates/admin/change_form.html:28 -#: contrib/admin/templates/admin/edit_inline/stacked.html:13 -#: contrib/admin/templates/admin/edit_inline/tabular.html:28 +#: .\contrib\admin\templates\admin\change_form.html.py:29 +#: .\contrib\admin\templates\admin\edit_inline\stacked.html.py:9 +#: .\contrib\admin\templates\admin\edit_inline\tabular.html.py:28 msgid "View on site" msgstr "Veure al lloc" -#: contrib/admin/templates/admin/change_form.html:38 -#: contrib/admin/templates/admin/change_list.html:54 -#: contrib/admin/templates/admin/auth/user/change_password.html:23 +#: .\contrib\admin\templates\admin\change_form.html.py:39 +#: .\contrib\admin\templates\admin\change_list.html.py:71 +#: .\contrib\admin\templates\admin\auth\user\change_password.html.py:24 +#: .\contrib\admin\templates\registration\password_change_form.html.py:15 msgid "Please correct the error below." msgid_plural "Please correct the errors below." -msgstr[0] "Si us plau, corregiu l'error mostrat a baix." -msgstr[1] "Si us plau, corregiu els errors mostrats a baix." +msgstr[0] "Si us plau, corregiu l'error mostrat a sota." +msgstr[1] "Si us plau, corregiu els errors mostrats a sota." -#: contrib/admin/templates/admin/change_list.html:46 +#: .\contrib\admin\templates\admin\change_list.html.py:63 #, python-format msgid "Add %(name)s" msgstr "Afegir %(name)s" -#: contrib/admin/templates/admin/change_list.html:65 +#: .\contrib\admin\templates\admin\change_list.html.py:82 msgid "Filter" msgstr "Filtre" -#: contrib/admin/templates/admin/delete_confirmation.html:10 -#: contrib/admin/templates/admin/submit_line.html:4 forms/formsets.py:275 +#: .\contrib\admin\templates\admin\delete_confirmation.html.py:10 +#: .\contrib\admin\templates\admin\submit_line.html.py:4 +#: .\forms\formsets.py:302 msgid "Delete" msgstr "Eliminar" -#: contrib/admin/templates/admin/delete_confirmation.html:16 +#: .\contrib\admin\templates\admin\delete_confirmation.html.py:16 #, python-format msgid "" "Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " @@ -634,10 +703,10 @@ msgid "" "following types of objects:" msgstr "" "Eliminar el/la %(object_name)s '%(escaped_object)s' provocaria l'eliminació " -"d'objectes relacionats, però el vostre compte no te permissos per a esborrar " +"d'objectes relacionats, però el vostre compte no te permisos per esborrar " "els tipus d'objecte següents:" -#: contrib/admin/templates/admin/delete_confirmation.html:23 +#: .\contrib\admin\templates\admin\delete_confirmation.html.py:23 #, python-format msgid "" "Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " @@ -646,16 +715,16 @@ msgstr "" "Esteu segurs de voler esborrar els/les %(object_name)s \"%(escaped_object)s" "\"? S'esborraran els següents elements relacionats:" -#: contrib/admin/templates/admin/delete_confirmation.html:28 -#: contrib/admin/templates/admin/delete_selected_confirmation.html:33 +#: .\contrib\admin\templates\admin\delete_confirmation.html.py:28 +#: .\contrib\admin\templates\admin\delete_selected_confirmation.html.py:33 msgid "Yes, I'm sure" -msgstr "Si, estic segur" +msgstr "SÃ, n'estic segur" -#: contrib/admin/templates/admin/delete_selected_confirmation.html:9 +#: .\contrib\admin\templates\admin\delete_selected_confirmation.html.py:9 msgid "Delete multiple objects" -msgstr "Eliminar multiples objectes" +msgstr "Eliminar múltiples objectes" -#: contrib/admin/templates/admin/delete_selected_confirmation.html:15 +#: .\contrib\admin\templates\admin\delete_selected_confirmation.html.py:15 #, python-format msgid "" "Deleting the %(object_name)s would result in deleting related objects, but " @@ -663,500 +732,426 @@ msgid "" "objects:" msgstr "" "Eliminar el/la %(object_name)s provocaria l'eliminació d'objectes " -"relacionats, però el vostre compte no te permissos per a esborrar els tipus " +"relacionats, però el vostre compte no te permisos per esborrar els tipus " "d'objecte següents:" -#: contrib/admin/templates/admin/delete_selected_confirmation.html:22 +#: .\contrib\admin\templates\admin\delete_selected_confirmation.html.py:22 #, python-format msgid "" "Are you sure you want to delete the selected %(object_name)s objects? All of " "the following objects and their related items will be deleted:" msgstr "" -"Esteu segurs de voler esborrar els/les %(object_name)s seleccionats? Tots " +"Esteu segur de voler esborrar els/les %(object_name)s seleccionats? Tots " "aquests objectes i els seus elements relacionats s'esborraran:" -#: contrib/admin/templates/admin/filter.html:2 +#: .\contrib\admin\templates\admin\filter.html.py:2 #, python-format msgid " By %(filter_title)s " msgstr "Per %(filter_title)s " -#: contrib/admin/templates/admin/index.html:18 +#: .\contrib\admin\templates\admin\index.html.py:18 #, python-format msgid "Models available in the %(name)s application." msgstr "Models disponibles a l'aplicació %(name)s." -#: contrib/admin/templates/admin/index.html:35 +#: .\contrib\admin\templates\admin\index.html.py:35 msgid "Change" msgstr "Modificar" -#: contrib/admin/templates/admin/index.html:45 +#: .\contrib\admin\templates\admin\index.html.py:45 msgid "You don't have permission to edit anything." msgstr "No teniu permÃs per editar res." -#: contrib/admin/templates/admin/index.html:53 +#: .\contrib\admin\templates\admin\index.html.py:53 msgid "Recent Actions" msgstr "Accions recents" -#: contrib/admin/templates/admin/index.html:54 +#: .\contrib\admin\templates\admin\index.html.py:54 msgid "My Actions" msgstr "Les meves accions" -#: contrib/admin/templates/admin/index.html:58 +#: .\contrib\admin\templates\admin\index.html.py:58 msgid "None available" msgstr "Cap disponible" -#: contrib/admin/templates/admin/index.html:72 +#: .\contrib\admin\templates\admin\index.html.py:72 msgid "Unknown content" msgstr "Contingut desconegut" -#: contrib/admin/templates/admin/invalid_setup.html:7 +#: .\contrib\admin\templates\admin\invalid_setup.html.py:7 msgid "" "Something's wrong with your database installation. Make sure the appropriate " "database tables have been created, and make sure the database is readable by " "the appropriate user." msgstr "" -"Alguna cosa està malament en la instal·lació de la vostra base de dades. " -"Assegureu-vos de que s'han creat les taules, i de que la base de dades és " -"llegible per l'usuari apropiat." +"Hi ha algun problema a la instal·lació de la vostra base de dades. Assegureu-" +"vos que s'han creat les taules adients, i que la base de dades és llegible " +"per l'usuari apropiat." -#: contrib/admin/templates/admin/login.html:19 +#: .\contrib\admin\templates\admin\login.html.py:19 msgid "Username:" -msgstr "Usuari:" +msgstr "Nom d'usuari:" -#: contrib/admin/templates/admin/login.html:22 +#: .\contrib\admin\templates\admin\login.html.py:22 msgid "Password:" msgstr "Contrasenya:" -#: contrib/admin/templates/admin/object_history.html:22 +#: .\contrib\admin\templates\admin\object_history.html.py:22 msgid "Date/time" msgstr "Data/hora" -#: contrib/admin/templates/admin/object_history.html:23 +#: .\contrib\admin\templates\admin\object_history.html.py:23 msgid "User" msgstr "Usuari" -#: contrib/admin/templates/admin/object_history.html:24 +#: .\contrib\admin\templates\admin\object_history.html.py:24 msgid "Action" msgstr "Acció" -#: contrib/admin/templates/admin/object_history.html:30 -#: utils/translation/trans_real.py:400 -msgid "DATETIME_FORMAT" -msgstr "j \\de F \\de Y, H:i" - -#: contrib/admin/templates/admin/object_history.html:38 +#: .\contrib\admin\templates\admin\object_history.html.py:38 msgid "" "This object doesn't have a change history. It probably wasn't added via this " "admin site." msgstr "" -"Aquest objecte no té historial de canvis. Probablement no va ser afegit " +"Aquest objecte no té historial de canvis. Probablement no es va afegir " "utilitzant aquest lloc administratiu." -#: contrib/admin/templates/admin/pagination.html:10 +#: .\contrib\admin\templates\admin\pagination.html.py:10 msgid "Show all" msgstr "Mostrar tots" -#: contrib/admin/templates/admin/pagination.html:11 -#: contrib/admin/templates/admin/submit_line.html:3 +#: .\contrib\admin\templates\admin\pagination.html.py:11 +#: .\contrib\admin\templates\admin\submit_line.html.py:3 msgid "Save" msgstr "Desar" -#: contrib/admin/templates/admin/search_form.html:8 +#: .\contrib\admin\templates\admin\search_form.html.py:8 msgid "Search" msgstr "Cerca" -#: contrib/admin/templates/admin/search_form.html:10 +#: .\contrib\admin\templates\admin\search_form.html.py:10 #, python-format msgid "1 result" msgid_plural "%(counter)s results" msgstr[0] "1 resultat" msgstr[1] "%(counter)s resultats" -#: contrib/admin/templates/admin/search_form.html:10 +#: .\contrib\admin\templates\admin\search_form.html.py:10 #, python-format msgid "%(full_result_count)s total" msgstr "%(full_result_count)s en total" -#: contrib/admin/templates/admin/submit_line.html:5 +#: .\contrib\admin\templates\admin\submit_line.html.py:5 msgid "Save as new" msgstr "Desar com a nou" -#: contrib/admin/templates/admin/submit_line.html:6 +#: .\contrib\admin\templates\admin\submit_line.html.py:6 msgid "Save and add another" msgstr "Desar i afegir-ne un de nou" -#: contrib/admin/templates/admin/submit_line.html:7 +#: .\contrib\admin\templates\admin\submit_line.html.py:7 msgid "Save and continue editing" msgstr "Desar i continuar editant" -#: contrib/admin/templates/admin/auth/user/add_form.html:6 +#: .\contrib\admin\templates\admin\auth\user\add_form.html.py:5 msgid "" "First, enter a username and password. Then, you'll be able to edit more user " "options." msgstr "" -"Primer, entreu un usuari i una contrasenya. Després podreu editar més " +"Primer, entreu un nom d'usuari i una contrasenya. Després podreu editar més " "opcions de l'usuari." -#: contrib/admin/templates/admin/auth/user/add_form.html:13 -#: contrib/auth/forms.py:14 contrib/auth/forms.py:47 contrib/auth/forms.py:59 -msgid "Username" -msgstr "Usuari" +#: .\contrib\admin\templates\admin\auth\user\change_password.html.py:28 +#, python-format +msgid "Enter a new password for the user <strong>%(username)s</strong>." +msgstr "Introduïu una contrasenya per l'usuari <strong>%(username)s</strong>" -#: contrib/admin/templates/admin/auth/user/add_form.html:20 -#: contrib/admin/templates/admin/auth/user/change_password.html:34 -#: contrib/auth/forms.py:17 contrib/auth/forms.py:60 contrib/auth/forms.py:185 +#: .\contrib\admin\templates\admin\auth\user\change_password.html.py:35 +#: .\contrib\auth\forms.py:17 .\contrib\auth\forms.py:61 +#: .\contrib\auth\forms.py:186 msgid "Password" msgstr "Contrasenya" -#: contrib/admin/templates/admin/auth/user/add_form.html:26 -#: contrib/admin/templates/admin/auth/user/change_password.html:40 -#: contrib/auth/forms.py:186 +#: .\contrib\admin\templates\admin\auth\user\change_password.html.py:41 +#: .\contrib\admin\templates\registration\password_change_form.html.py:37 +#: .\contrib\auth\forms.py:187 msgid "Password (again)" msgstr "Contrasenya (de nou)" -#: contrib/admin/templates/admin/auth/user/add_form.html:27 -#: contrib/admin/templates/admin/auth/user/change_password.html:41 +#: .\contrib\admin\templates\admin\auth\user\change_password.html.py:42 +#: .\contrib\auth\forms.py:19 msgid "Enter the same password as above, for verification." msgstr "Introduïu la mateixa contrasenya de dalt, per fer-ne la verificació." -#: contrib/admin/templates/admin/auth/user/change_password.html:27 +#: .\contrib\admin\templates\admin\edit_inline\stacked.html.py:64 +#: .\contrib\admin\templates\admin\edit_inline\tabular.html.py:110 #, python-format -msgid "Enter a new password for the user <strong>%(username)s</strong>." -msgstr "Introduïu una contrasenya per l'usuari <strong>%(username)s</strong>" +msgid "Add another %(verbose_name)s" +msgstr "Afegir un/a altre/a %(verbose_name)s." -#: contrib/admin/templates/admin/edit_inline/tabular.html:15 +#: .\contrib\admin\templates\admin\edit_inline\stacked.html.py:67 +#: .\contrib\admin\templates\admin\edit_inline\tabular.html.py:113 +#: .\contrib\comments\templates\comments\delete.html.py:12 +msgid "Remove" +msgstr "Eliminar" + +#: .\contrib\admin\templates\admin\edit_inline\tabular.html.py:15 msgid "Delete?" msgstr "Eliminar?" -#: contrib/admin/templates/registration/logged_out.html:8 +#: .\contrib\admin\templates\registration\logged_out.html.py:8 msgid "Thanks for spending some quality time with the Web site today." -msgstr "Grà cies per destinar el vostre temps en el web durant el dia d'avui." +msgstr "Grà cies per passar una estona de qualitat al web durant el dia d'avui." -#: contrib/admin/templates/registration/logged_out.html:10 +#: .\contrib\admin\templates\registration\logged_out.html.py:10 msgid "Log in again" msgstr "Iniciar sessió de nou" -#: contrib/admin/templates/registration/password_change_done.html:4 -#: contrib/admin/templates/registration/password_change_form.html:4 -#: contrib/admin/templates/registration/password_change_form.html:6 -#: contrib/admin/templates/registration/password_change_form.html:10 +#: .\contrib\admin\templates\registration\password_change_done.html.py:4 +#: .\contrib\admin\templates\registration\password_change_form.html.py:5 +#: .\contrib\admin\templates\registration\password_change_form.html.py:7 +#: .\contrib\admin\templates\registration\password_change_form.html.py:19 msgid "Password change" msgstr "Canvi de contrasenya" -#: contrib/admin/templates/registration/password_change_done.html:6 -#: contrib/admin/templates/registration/password_change_done.html:10 +#: .\contrib\admin\templates\registration\password_change_done.html.py:6 +#: .\contrib\admin\templates\registration\password_change_done.html.py:10 msgid "Password change successful" msgstr "Contrasenya canviada amb èxit" -#: contrib/admin/templates/registration/password_change_done.html:12 +#: .\contrib\admin\templates\registration\password_change_done.html.py:12 msgid "Your password was changed." msgstr "La seva contrasenya ha estat canviada." -#: contrib/admin/templates/registration/password_change_form.html:12 +#: .\contrib\admin\templates\registration\password_change_form.html.py:21 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 "" "Si us plau, introduïu la vostra contrasenya antiga, per seguretat, i tot " "seguit introduïu la vostra contrasenya nova dues vegades per verificar que " -"l'heu escrit correctament." +"l'heu escrita correctament." -#: contrib/admin/templates/registration/password_change_form.html:17 -msgid "Old password:" -msgstr "Contrasenya antiga:" - -#: contrib/admin/templates/registration/password_change_form.html:19 -#: contrib/admin/templates/registration/password_reset_confirm.html:18 -msgid "New password:" -msgstr "Contrasenya nova:" +#: .\contrib\admin\templates\registration\password_change_form.html.py:27 +#: .\contrib\auth\forms.py:170 +msgid "Old password" +msgstr "Contrasenya antiga" -#: contrib/admin/templates/registration/password_change_form.html:21 -#: contrib/admin/templates/registration/password_reset_confirm.html:20 -msgid "Confirm password:" -msgstr "Confirmar contrasenya:" +#: .\contrib\admin\templates\registration\password_change_form.html.py:32 +#: .\contrib\auth\forms.py:144 +msgid "New password" +msgstr "Contrasenya nova" -#: contrib/admin/templates/registration/password_change_form.html:23 -#: contrib/admin/templates/registration/password_reset_confirm.html:21 +#: .\contrib\admin\templates\registration\password_change_form.html.py:43 +#: .\contrib\admin\templates\registration\password_reset_confirm.html.py:21 msgid "Change my password" -msgstr "Canviar la meva clau:" - -#: contrib/admin/templates/registration/password_reset_complete.html:4 -#: contrib/admin/templates/registration/password_reset_confirm.html:6 -#: contrib/admin/templates/registration/password_reset_done.html:4 -#: 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 +msgstr "Canviar la meva contrasenya:" + +#: .\contrib\admin\templates\registration\password_reset_complete.html.py:4 +#: .\contrib\admin\templates\registration\password_reset_confirm.html.py:6 +#: .\contrib\admin\templates\registration\password_reset_done.html.py:4 +#: .\contrib\admin\templates\registration\password_reset_form.html.py:4 +#: .\contrib\admin\templates\registration\password_reset_form.html.py:6 +#: .\contrib\admin\templates\registration\password_reset_form.html.py:10 msgid "Password reset" msgstr "Restablir contrasenya" -#: contrib/admin/templates/registration/password_reset_complete.html:6 -#: contrib/admin/templates/registration/password_reset_complete.html:10 +#: .\contrib\admin\templates\registration\password_reset_complete.html.py:6 +#: .\contrib\admin\templates\registration\password_reset_complete.html.py:10 msgid "Password reset complete" msgstr "Contrasenya restablerta" -#: contrib/admin/templates/registration/password_reset_complete.html:12 +#: .\contrib\admin\templates\registration\password_reset_complete.html.py:12 msgid "Your password has been set. You may go ahead and log in now." msgstr "" -"La seva contrasenya ha estat canviada. Ara pot continuar i iniciar sessió." +"S'ha canviat la vostra contrasenya. Ara podeu continuar i iniciar sessió." -#: contrib/admin/templates/registration/password_reset_confirm.html:4 +#: .\contrib\admin\templates\registration\password_reset_confirm.html.py:4 msgid "Password reset confirmation" -msgstr "Confirmació de reestabliment de contrasenya" +msgstr "Confirmació de restabliment de contrasenya" -#: contrib/admin/templates/registration/password_reset_confirm.html:12 +#: .\contrib\admin\templates\registration\password_reset_confirm.html.py:12 msgid "Enter new password" -msgstr "Introdueixi la nova contrasenya" +msgstr "Introduïu la nova contrasenya" -#: contrib/admin/templates/registration/password_reset_confirm.html:14 +#: .\contrib\admin\templates\registration\password_reset_confirm.html.py:14 msgid "" "Please enter your new password twice so we can verify you typed it in " "correctly." msgstr "" "Si us plau, introduïu la vostra nova contrasenya dues vegades, per verificar " -"que l'heu escrit correctament." +"que l'heu escrita correctament." -#: contrib/admin/templates/registration/password_reset_confirm.html:26 +#: .\contrib\admin\templates\registration\password_reset_confirm.html.py:18 +msgid "New password:" +msgstr "Contrasenya nova:" + +#: .\contrib\admin\templates\registration\password_reset_confirm.html.py:20 +msgid "Confirm password:" +msgstr "Confirmar contrasenya:" + +#: .\contrib\admin\templates\registration\password_reset_confirm.html.py:26 msgid "Password reset unsuccessful" -msgstr "Restabliment de contrasenya fallit" +msgstr "Restabliment de contrasenya fallat" -#: contrib/admin/templates/registration/password_reset_confirm.html:28 +#: .\contrib\admin\templates\registration\password_reset_confirm.html.py:28 msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" -"L'enllaç de restabliment de contrasenya era invà lid, segurament ja deu haver " -"estat utilitzat. Per favor, soliciti un nou reestabliment de contrasenya." +"L'enllaç de restabliment de contrasenya era invà lid, potser perquè ja s'ha " +"utilitzat. Si us plau, sol·liciteu un nou reestabliment de contrasenya." -#: contrib/admin/templates/registration/password_reset_done.html:6 -#: contrib/admin/templates/registration/password_reset_done.html:10 +#: .\contrib\admin\templates\registration\password_reset_done.html.py:6 +#: .\contrib\admin\templates\registration\password_reset_done.html.py:10 msgid "Password reset successful" -msgstr "Restabliment de contrasenya exitós" +msgstr "Restabliment de contrasenya amb èxit" -#: contrib/admin/templates/registration/password_reset_done.html:12 +#: .\contrib\admin\templates\registration\password_reset_done.html.py:12 msgid "" "We've e-mailed you instructions for setting your password to the e-mail " "address you submitted. You should be receiving it shortly." msgstr "" "Us hem enviat les instruccions per canviar la vostra contrasenya a l'adreça " -"de correu electrònic que ens heu indicat. L'haurieu de rebre en breu." +"de correu electrònic que ens heu indicat. L'haurÃeu de rebre en breu." -#: contrib/admin/templates/registration/password_reset_email.html:2 +#: .\contrib\admin\templates\registration\password_reset_email.html.py:2 msgid "You're receiving this e-mail because you requested a password reset" msgstr "" -"Esteu rebent aquest missatge degut a que veu solicitar un restabliment de " +"Esteu rebent aquest missatge perquè vau sol·licitar un restabliment de " "contrasenya." -#: contrib/admin/templates/registration/password_reset_email.html:3 +#: .\contrib\admin\templates\registration\password_reset_email.html.py:3 #, python-format msgid "for your user account at %(site_name)s" msgstr "del vostre compte d'usuari a %(site_name)s." -#: contrib/admin/templates/registration/password_reset_email.html:5 +#: .\contrib\admin\templates\registration\password_reset_email.html.py:5 msgid "Please go to the following page and choose a new password:" -msgstr "Si us plau, adrecis a la pà gina següen i esculli una nova contrasenya:" +msgstr "" +"Si us plau, aneu a la pà gina següent i escolliu una nova contrasenya:" -#: contrib/admin/templates/registration/password_reset_email.html:9 +#: .\contrib\admin\templates\registration\password_reset_email.html.py:9 msgid "Your username, in case you've forgotten:" -msgstr "El vostre nom d'usuari, en cas d'haver-lo oblidat:" +msgstr "El vostre nom d'usuari, en cas que l'hagueu oblidat:" -#: contrib/admin/templates/registration/password_reset_email.html:11 +#: .\contrib\admin\templates\registration\password_reset_email.html.py:11 msgid "Thanks for using our site!" -msgstr "Grà cies per fer us del nostre lloc!" +msgstr "Grà cies per fer ús del nostre lloc!" -#: contrib/admin/templates/registration/password_reset_email.html:13 +#: .\contrib\admin\templates\registration\password_reset_email.html.py:13 #, python-format msgid "The %(site_name)s team" msgstr "L'equip de %(site_name)s" -#: contrib/admin/templates/registration/password_reset_form.html:12 +#: .\contrib\admin\templates\registration\password_reset_form.html.py:12 msgid "" "Forgotten your password? Enter your e-mail address below, and we'll e-mail " "instructions for setting a new one." msgstr "" "Heu oblidat la vostra contrasenya? Introduïu la vostra adreça de correu " -"electrònic i li enviarem instruccions per canviar-la." +"electrònic a sota, i us enviarem instruccions per canviar-la." -#: contrib/admin/templates/registration/password_reset_form.html:16 +#: .\contrib\admin\templates\registration\password_reset_form.html.py:16 msgid "E-mail address:" msgstr "Adreça de correu electrònic:" -#: contrib/admin/templates/registration/password_reset_form.html:16 +#: .\contrib\admin\templates\registration\password_reset_form.html.py:16 msgid "Reset my password" msgstr "Restablir la meva contrasenya" -#: contrib/admin/templatetags/admin_list.py:304 +#: .\contrib\admin\templatetags\admin_list.py:257 msgid "All dates" msgstr "Totes les dates" -#: contrib/admin/views/main.py:70 +#: .\contrib\admin\views\main.py:65 #, python-format msgid "Select %s" msgstr "Seleccioneu %s" -#: contrib/admin/views/main.py:70 +#: .\contrib\admin\views\main.py:65 #, python-format msgid "Select %s to change" msgstr "Seleccioneu %s per modificar" -#: contrib/admin/views/template.py:37 contrib/sites/models.py:38 +#: .\contrib\admin\views\template.py:38 .\contrib\sites\models.py:38 msgid "site" msgstr "lloc" -#: contrib/admin/views/template.py:39 +#: .\contrib\admin\views\template.py:40 msgid "template" msgstr "plantilla" -#: contrib/admindocs/views.py:61 contrib/admindocs/views.py:63 -#: contrib/admindocs/views.py:65 +#: .\contrib\admindocs\views.py:61 .\contrib\admindocs\views.py:63 +#: .\contrib\admindocs\views.py:65 msgid "tag:" msgstr "etiqueta:" -#: contrib/admindocs/views.py:94 contrib/admindocs/views.py:96 -#: contrib/admindocs/views.py:98 +#: .\contrib\admindocs\views.py:94 .\contrib\admindocs\views.py:96 +#: .\contrib\admindocs\views.py:98 msgid "filter:" msgstr "filtre:" -#: contrib/admindocs/views.py:158 contrib/admindocs/views.py:160 -#: contrib/admindocs/views.py:162 +#: .\contrib\admindocs\views.py:158 .\contrib\admindocs\views.py:160 +#: .\contrib\admindocs\views.py:162 msgid "view:" msgstr "vista:" -#: contrib/admindocs/views.py:190 +#: .\contrib\admindocs\views.py:190 #, python-format msgid "App %r not found" msgstr "No s'ha pogut trobar l'aplicació %r" -#: contrib/admindocs/views.py:197 +#: .\contrib\admindocs\views.py:197 #, python-format msgid "Model %(model_name)r not found in app %(app_label)r" -msgstr "El model %(model_name)r no s'ha trobat en l'aplicació %(app_label)r" +msgstr "El model %(model_name)r no s'ha trobat a l'aplicació %(app_label)r" -#: contrib/admindocs/views.py:209 +#: .\contrib\admindocs\views.py:209 #, python-format msgid "the related `%(app_label)s.%(data_type)s` object" msgstr "l'objecte relacionat `%(app_label)s.%(data_type)s`" -#: contrib/admindocs/views.py:209 contrib/admindocs/views.py:228 -#: contrib/admindocs/views.py:233 contrib/admindocs/views.py:247 -#: contrib/admindocs/views.py:261 contrib/admindocs/views.py:266 +#: .\contrib\admindocs\views.py:209 .\contrib\admindocs\views.py:228 +#: .\contrib\admindocs\views.py:233 .\contrib\admindocs\views.py:247 +#: .\contrib\admindocs\views.py:261 .\contrib\admindocs\views.py:266 msgid "model:" msgstr "model:" -#: contrib/admindocs/views.py:224 contrib/admindocs/views.py:256 +#: .\contrib\admindocs\views.py:224 .\contrib\admindocs\views.py:256 #, python-format msgid "related `%(app_label)s.%(object_name)s` objects" msgstr "objectes relacionats `%(app_label)s.%(object_name)s`" -#: contrib/admindocs/views.py:228 contrib/admindocs/views.py:261 +#: .\contrib\admindocs\views.py:228 .\contrib\admindocs\views.py:261 #, python-format msgid "all %s" msgstr "tots %s" -#: contrib/admindocs/views.py:233 contrib/admindocs/views.py:266 +#: .\contrib\admindocs\views.py:233 .\contrib\admindocs\views.py:266 #, python-format msgid "number of %s" msgstr "nombre de %s" -#: contrib/admindocs/views.py:271 +#: .\contrib\admindocs\views.py:271 #, python-format msgid "Fields on %s objects" msgstr "Camps en objectes %s" -#: contrib/admindocs/views.py:334 contrib/admindocs/views.py:345 -#: contrib/admindocs/views.py:347 contrib/admindocs/views.py:353 -#: contrib/admindocs/views.py:354 contrib/admindocs/views.py:356 -msgid "Integer" -msgstr "Enter" - -#: contrib/admindocs/views.py:335 -msgid "Boolean (Either True or False)" -msgstr "Booleà (Verdader o Fals)" - -#: contrib/admindocs/views.py:336 contrib/admindocs/views.py:355 -#, python-format -msgid "String (up to %(max_length)s)" -msgstr "Cadena (de fins a %(max_length)s)" - -#: contrib/admindocs/views.py:337 -msgid "Comma-separated integers" -msgstr "Enters separats per comes" - -#: contrib/admindocs/views.py:338 -msgid "Date (without time)" -msgstr "Data (sense hora)" - -#: contrib/admindocs/views.py:339 -msgid "Date (with time)" -msgstr "Data (amb hora)" - -#: contrib/admindocs/views.py:340 -msgid "Decimal number" -msgstr "Número decimal" - -#: contrib/admindocs/views.py:341 -msgid "E-mail address" -msgstr "Adreça de correu electrònic" - -#: contrib/admindocs/views.py:342 contrib/admindocs/views.py:343 -#: contrib/admindocs/views.py:346 -msgid "File path" -msgstr "Ruta del fitxer" - -#: contrib/admindocs/views.py:344 -msgid "Floating point number" -msgstr "Número amb punt de coma flotant" - -#: contrib/admindocs/views.py:348 contrib/comments/models.py:60 -msgid "IP address" -msgstr "Adreça IP" - -#: contrib/admindocs/views.py:350 -msgid "Boolean (Either True, False or None)" -msgstr "Booleà (Verdader, Fals o 'None' (cap))" - -#: contrib/admindocs/views.py:351 -msgid "Relation to parent model" -msgstr "Relació amb el model pare" - -#: contrib/admindocs/views.py:352 -msgid "Phone number" -msgstr "Número de telèfon" - -#: contrib/admindocs/views.py:357 -msgid "Text" -msgstr "Text" - -#: contrib/admindocs/views.py:358 -msgid "Time" -msgstr "Hora" - -#: contrib/admindocs/views.py:359 contrib/comments/forms.py:95 -#: contrib/flatpages/admin.py:8 contrib/flatpages/models.py:7 -msgid "URL" -msgstr "URL" - -#: contrib/admindocs/views.py:360 -msgid "U.S. state (two uppercase letters)" -msgstr "Estat dels E.U.A. (dues lletres majúscules)" - -#: contrib/admindocs/views.py:361 -msgid "XML text" -msgstr "Text XML" - -#: contrib/admindocs/views.py:387 +#: .\contrib\admindocs\views.py:361 #, python-format msgid "%s does not appear to be a urlpattern object" msgstr "%s no sembla ser un objecte 'urlpattern'" -#: contrib/admindocs/templates/admin_doc/bookmarklets.html:3 +#: .\contrib\admindocs\templates\admin_doc\bookmarklets.html.py:3 msgid "Bookmarklets" msgstr "'Bookmarklets'" -#: contrib/admindocs/templates/admin_doc/bookmarklets.html:4 +#: .\contrib\admindocs\templates\admin_doc\bookmarklets.html.py:4 msgid "Documentation bookmarklets" msgstr "'Bookmarklets' de documentació" -#: contrib/admindocs/templates/admin_doc/bookmarklets.html:8 +#: .\contrib\admindocs\templates\admin_doc\bookmarklets.html.py:8 msgid "" "\n" "<p class=\"help\">To install bookmarklets, drag the link to your bookmarks\n" @@ -1167,203 +1162,197 @@ msgid "" "your computer is \"internal\").</p>\n" msgstr "" "\n" -"<p class=\"help\">Per a instal·lar 'bookmarklets', arrosegueu l'enllaç a la " -"seva barra de\n" -"marcadors, o feu click amb el botó dret en l'enllaç i afegeixi'l als " -"marcadors.\n" +"<p class=\"help\">Per tal d'instal·lar 'bookmarklets', arrosegueu l'enllaç\n" +"a la vostra barra de marcadors, o feu click amb el botó dret a l'enllaç i\n" +"afegiu-lo als marcadors.\n" "Ara podeu escollir el 'bookmarklet' des de qualsevol pà gina del lloc.\n" -"Observeu que alguns d'aquests 'bookmarklets' precisen que estigui veient\n" -"el lloc des de un ordinador senyalat com a \"intern\" (parleu\n" -"amb el vostre administrador de sistemes si no esteu segurs de la condició " -"del vostre).</p>\n" +"Observeu que per a alguns d'aquests 'bookmarklets' cal que estigueu veient\n" +"el lloc des d'un ordinador designat com a \"intern\" (parleu\n" +"amb el vostre administrador de sistemes si no esteu segurs si el vostre " +"ordinador és \"intern\").</p>\n" -#: contrib/admindocs/templates/admin_doc/bookmarklets.html:18 +#: .\contrib\admindocs\templates\admin_doc\bookmarklets.html.py:18 msgid "Documentation for this page" msgstr "Documentació d'aquesta pà gina" -#: contrib/admindocs/templates/admin_doc/bookmarklets.html:19 +#: .\contrib\admindocs\templates\admin_doc\bookmarklets.html.py:19 msgid "" "Jumps you from any page to the documentation for the view that generates " "that page." msgstr "" -"Us porta des de qualsevol pà gina de la documentació a la vista que la genera." +"Us porta des de qualsevol pà gina a la documentació de la vista que la genera." -#: contrib/admindocs/templates/admin_doc/bookmarklets.html:21 +#: .\contrib\admindocs\templates\admin_doc\bookmarklets.html.py:21 msgid "Show object ID" -msgstr "Mostra el ID de l'objecte" +msgstr "Mostra l'ID de l'objecte" -#: contrib/admindocs/templates/admin_doc/bookmarklets.html:22 +#: .\contrib\admindocs\templates\admin_doc\bookmarklets.html.py:22 msgid "" "Shows the content-type and unique ID for pages that represent a single " "object." msgstr "" -"Mostra el 'content-type' (tipus de contingut) i el ID inequÃvoc de les " +"Mostra el 'content-type' (tipus de contingut) i l'ID inequÃvoc de les " "pà gines que representen un únic objecte." -#: contrib/admindocs/templates/admin_doc/bookmarklets.html:24 +#: .\contrib\admindocs\templates\admin_doc\bookmarklets.html.py:24 msgid "Edit this object (current window)" msgstr "Editar aquest objecte (finestra actual)" -#: contrib/admindocs/templates/admin_doc/bookmarklets.html:25 +#: .\contrib\admindocs\templates\admin_doc\bookmarklets.html.py:25 msgid "Jumps to the admin page for pages that represent a single object." msgstr "" "El porta a la pà gina d'administració de pà gines que representen un únic " "objecte." -#: contrib/admindocs/templates/admin_doc/bookmarklets.html:27 +#: .\contrib\admindocs\templates\admin_doc\bookmarklets.html.py:27 msgid "Edit this object (new window)" msgstr "Editar aquest objecte (nova finestra)" -#: contrib/admindocs/templates/admin_doc/bookmarklets.html:28 +#: .\contrib\admindocs\templates\admin_doc\bookmarklets.html.py:28 msgid "As above, but opens the admin page in a new window." msgstr "Com abans, però obre la pà gina d'administració en una nova finestra." -#: contrib/auth/admin.py:21 +#: .\contrib\auth\admin.py:29 msgid "Personal info" msgstr "Informació personal" -#: contrib/auth/admin.py:22 +#: .\contrib\auth\admin.py:30 msgid "Permissions" -msgstr "permisos" +msgstr "Permisos" -#: contrib/auth/admin.py:23 +#: .\contrib\auth\admin.py:31 msgid "Important dates" msgstr "Dates importants" -#: contrib/auth/admin.py:24 +#: .\contrib\auth\admin.py:32 msgid "Groups" msgstr "Grups" -#: contrib/auth/admin.py:80 -msgid "Add user" -msgstr "Afegir usuari" - -#: contrib/auth/admin.py:106 +#: .\contrib\auth\admin.py:114 msgid "Password changed successfully." -msgstr "Contrasenya cambiada amb èxit" +msgstr "Contrasenya canviada amb èxit" -#: contrib/auth/admin.py:112 +#: .\contrib\auth\admin.py:124 #, python-format msgid "Change password: %s" msgstr "Canviar contrasenya: %s" -#: contrib/auth/forms.py:15 contrib/auth/forms.py:48 -#: contrib/auth/models.py:128 -msgid "" -"Required. 30 characters or fewer. Alphanumeric characters only (letters, " -"digits and underscores)." +#: .\contrib\auth\forms.py:14 .\contrib\auth\forms.py:48 +#: .\contrib\auth\forms.py:60 +msgid "Username" +msgstr "Nom d'usuari" + +#: .\contrib\auth\forms.py:15 .\contrib\auth\forms.py:49 +msgid "Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only." msgstr "" -"Obligatori. 30 o menys carà cters. Només carà cters alfanumèrics (lletres, " -"dÃgits i el guió baix)." +"Obligatori. 30 o menys carà cters. Només lletres, dÃgits i @/./+/-/_." -#: contrib/auth/forms.py:16 contrib/auth/forms.py:49 -msgid "This value must contain only letters, numbers and underscores." -msgstr "Aquest valor ha de contenir només lletres, números i guions baixos." +#: .\contrib\auth\forms.py:16 .\contrib\auth\forms.py:50 +msgid "This value may contain only letters, numbers and @/./+/-/_ characters." +msgstr "Aquest valor ha de contenir només lletres, números i @/./+/-/_." -#: contrib/auth/forms.py:18 +#: .\contrib\auth\forms.py:18 msgid "Password confirmation" msgstr "Confirmació de contrasenya" -#: contrib/auth/forms.py:30 +#: .\contrib\auth\forms.py:31 msgid "A user with that username already exists." msgstr "Ja existeix un usuari amb aquest nom." -#: contrib/auth/forms.py:36 contrib/auth/forms.py:155 -#: contrib/auth/forms.py:197 +#: .\contrib\auth\forms.py:37 .\contrib\auth\forms.py:156 +#: .\contrib\auth\forms.py:198 msgid "The two password fields didn't match." msgstr "Els dos camps de contrasenya no coincideixen." -#: contrib/auth/forms.py:82 +#: .\contrib\auth\forms.py:83 msgid "This account is inactive." msgstr "Aquest compte està inactiu" -#: contrib/auth/forms.py:87 +#: .\contrib\auth\forms.py:88 msgid "" "Your Web browser doesn't appear to have cookies enabled. Cookies are " "required for logging in." msgstr "" -"El seu navegador no sembla tenir les 'cookies' (galetes) activades. Aquestes " -"són necessà ries per iniciar la sessió." +"El vostre navegador no sembla tenir les galetes ('cookies') activades. " +"Aquestes són necessà ries per iniciar la sessió." -#: contrib/auth/forms.py:100 +#: .\contrib\auth\forms.py:101 msgid "E-mail" msgstr "Correu electrònic" -#: contrib/auth/forms.py:109 +#: .\contrib\auth\forms.py:110 msgid "" "That e-mail address doesn't have an associated user account. Are you sure " "you've registered?" msgstr "" -"Aquesta adreça de correu no té associada cap compte d'usuari. Esteu segurs " -"de que s'ha registrat?" +"Aquesta adreça de correu no té associat cap compte d'usuari. Esteu segurs " +"que s'ha registrat?" -#: contrib/auth/forms.py:135 +#: .\contrib\auth\forms.py:136 #, python-format msgid "Password reset on %s" msgstr "Restablir contrasenya en %s" -#: contrib/auth/forms.py:143 -msgid "New password" -msgstr "Contrasenya nova" - -#: contrib/auth/forms.py:144 +#: .\contrib\auth\forms.py:145 msgid "New password confirmation" msgstr "Contrasenya nova confirmada" -#: contrib/auth/forms.py:169 -msgid "Old password" -msgstr "Contrasenya antiga" - -#: contrib/auth/forms.py:177 +#: .\contrib\auth\forms.py:178 msgid "Your old password was entered incorrectly. Please enter it again." msgstr "" -"La seva antiga contrasenya no és correcte. Si el plau, introduïu-la de nou." +"La vostra antiga contrasenya no és correcta. Si us plau, introduïu-la de nou." -#: contrib/auth/models.py:63 contrib/auth/models.py:86 +#: .\contrib\auth\models.py:66 .\contrib\auth\models.py:94 msgid "name" msgstr "nom" -#: contrib/auth/models.py:65 +#: .\contrib\auth\models.py:68 msgid "codename" msgstr "nom en clau" -#: contrib/auth/models.py:68 +#: .\contrib\auth\models.py:72 msgid "permission" msgstr "permÃs" -#: contrib/auth/models.py:69 contrib/auth/models.py:87 +#: .\contrib\auth\models.py:73 .\contrib\auth\models.py:95 msgid "permissions" msgstr "permisos" -#: contrib/auth/models.py:90 +#: .\contrib\auth\models.py:98 msgid "group" msgstr "grup" -#: contrib/auth/models.py:91 contrib/auth/models.py:138 +#: .\contrib\auth\models.py:99 .\contrib\auth\models.py:206 msgid "groups" msgstr "grups" -#: contrib/auth/models.py:128 +#: .\contrib\auth\models.py:196 msgid "username" msgstr "nom d'usuari" -#: contrib/auth/models.py:129 +#: .\contrib\auth\models.py:196 +msgid "" +"Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters" +msgstr "" +"Obligatori. 30 o menys carà cters. Només lletres, números i @/./+/-/_" + +#: .\contrib\auth\models.py:197 msgid "first name" msgstr "nom propi" -#: contrib/auth/models.py:130 +#: .\contrib\auth\models.py:198 msgid "last name" msgstr "cognoms" -#: contrib/auth/models.py:131 +#: .\contrib\auth\models.py:199 msgid "e-mail address" msgstr "adreça de correu electrònic" -#: contrib/auth/models.py:132 +#: .\contrib\auth\models.py:200 msgid "password" msgstr "contrasenya" -#: contrib/auth/models.py:132 +#: .\contrib\auth\models.py:200 msgid "" "Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change " "password form</a>." @@ -1371,19 +1360,19 @@ msgstr "" "Utilitzeu '[algo]$[salt]$[hexdigest]' o el <a href=\"password/\">formulari " "de canvi de contrasenya</a>." -#: contrib/auth/models.py:133 +#: .\contrib\auth\models.py:201 msgid "staff status" msgstr "membre del personal" -#: contrib/auth/models.py:133 +#: .\contrib\auth\models.py:201 msgid "Designates whether the user can log into this admin site." -msgstr "Indica si l'usuari pot entrar en el lloc administratiu." +msgstr "Designa si l'usuari pot entrar al lloc administratiu." -#: contrib/auth/models.py:134 +#: .\contrib\auth\models.py:202 msgid "active" msgstr "actiu" -#: contrib/auth/models.py:134 +#: .\contrib\auth\models.py:202 msgid "" "Designates whether this user should be treated as active. Unselect this " "instead of deleting accounts." @@ -1391,122 +1380,134 @@ msgstr "" "Designa si aquest usuari ha de ser tractat com a actiu. Deseleccioneu-ho " "enlloc d'esborrar comptes d'usuari." -#: contrib/auth/models.py:135 +#: .\contrib\auth\models.py:203 msgid "superuser status" msgstr "estat de superusuari" -#: contrib/auth/models.py:135 +#: .\contrib\auth\models.py:203 msgid "" "Designates that this user has all permissions without explicitly assigning " "them." msgstr "" -"Designa que aquest usuari té tots els permissos sense assignar-los " +"Designa que aquest usuari té tots els permisos sense assignar-los " "explÃcitament." -#: contrib/auth/models.py:136 +#: .\contrib\auth\models.py:204 msgid "last login" msgstr "últim inici de sessió" -#: contrib/auth/models.py:137 +#: .\contrib\auth\models.py:205 msgid "date joined" -msgstr "data de creació" +msgstr "data d'incorporació" -#: contrib/auth/models.py:139 +#: .\contrib\auth\models.py:207 msgid "" "In addition to the permissions manually assigned, this user will also get " "all permissions granted to each group he/she is in." msgstr "" -"Junt amb els permissos asignats manualment, aquest usuari tindrà , també, els " -"permissos dels grups dels que sigui membre." +"A més dels permisos assignats manualment, aquest usuari tindrà també els " +"permisos dels grups dels que sigui membre." -#: contrib/auth/models.py:140 +#: .\contrib\auth\models.py:208 msgid "user permissions" -msgstr "permissos de l'usuari" +msgstr "permisos de l'usuari" -#: contrib/auth/models.py:144 contrib/comments/models.py:50 -#: contrib/comments/models.py:168 +#: .\contrib\auth\models.py:212 .\contrib\comments\models.py:50 +#: .\contrib\comments\models.py:168 msgid "user" msgstr "usuari" -#: contrib/auth/models.py:145 +#: .\contrib\auth\models.py:213 msgid "users" msgstr "usuaris" -#: contrib/auth/models.py:301 +#: .\contrib\auth\models.py:394 msgid "message" msgstr "missatge" -#: contrib/auth/views.py:60 +#: .\contrib\auth\views.py:79 msgid "Logged out" msgstr "Sessió finalitzada" -#: contrib/auth/management/commands/createsuperuser.py:23 forms/fields.py:428 +#: .\contrib\auth\management\commands\createsuperuser.py:24 +#: .\core\validators.py:120 .\forms\fields.py:427 msgid "Enter a valid e-mail address." msgstr "Introduïu una adreça de correu và lida." -#: contrib/comments/admin.py:12 +#: .\contrib\comments\admin.py:12 msgid "Content" -msgstr "contingut" +msgstr "Contingut" -#: contrib/comments/admin.py:15 +#: .\contrib\comments\admin.py:15 msgid "Metadata" -msgstr "metadades" +msgstr "Metadades" # Context problem... waitting for comments from django-i18n -#: contrib/comments/admin.py:39 +#: .\contrib\comments\admin.py:40 msgid "flagged" -msgstr "marcat" +msgid_plural "flagged" +msgstr[0] "marcat" +msgstr[1] "marcats" -#: contrib/comments/admin.py:40 +#: .\contrib\comments\admin.py:41 msgid "Flag selected comments" msgstr "Marcar els comentaris seleccionats" -#: contrib/comments/admin.py:43 +#: .\contrib\comments\admin.py:45 msgid "approved" -msgstr "aprovat" +msgid_plural "approved" +msgstr[0] "aprovat" +msgstr[1] "aprovats" -#: contrib/comments/admin.py:44 +#: .\contrib\comments\admin.py:46 msgid "Approve selected comments" msgstr "Aprovar els comentaris seleccionats" -#: contrib/comments/admin.py:47 +#: .\contrib\comments\admin.py:50 msgid "removed" -msgstr "eliminat" +msgid_plural "removed" +msgstr[0] "eliminat" +msgstr[1] "eliminats" -#: contrib/comments/admin.py:48 +#: .\contrib\comments\admin.py:51 msgid "Remove selected comments" msgstr "Eliminar els comentaris seleccionats" -#: contrib/comments/admin.py:60 +#: .\contrib\comments\admin.py:63 #, python-format msgid "1 comment was successfully %(action)s." msgid_plural "%(count)s comments were successfully %(action)s." msgstr[0] "1 comentari ha estat %(action)s satisfactòriament." msgstr[1] "%(count)s comentaris han estat %(action)s satisfactòriament." -#: contrib/comments/feeds.py:13 +#: .\contrib\comments\feeds.py:13 #, python-format msgid "%(site_name)s comments" msgstr "comentaris de %(site_name)s" -#: contrib/comments/feeds.py:23 +#: .\contrib\comments\feeds.py:23 #, python-format msgid "Latest comments on %(site_name)s" msgstr "Últims comentaris a %(site_name)s." -#: contrib/comments/forms.py:93 +#: .\contrib\comments\forms.py:93 msgid "Name" -msgstr "nom" +msgstr "Nom" -#: contrib/comments/forms.py:94 +#: .\contrib\comments\forms.py:94 msgid "Email address" msgstr "Adreça de correu electrònic" -#: contrib/comments/forms.py:96 +#: .\contrib\comments\forms.py:95 .\contrib\flatpages\admin.py:8 +#: .\contrib\flatpages\models.py:7 .\db\models\fields\__init__.py:1101 +msgid "URL" +msgstr "URL" + +#: .\contrib\comments\forms.py:96 msgid "Comment" msgstr "Comentari" -#: contrib/comments/forms.py:173 +#: .\contrib\comments\forms.py:175 #, 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." @@ -1514,78 +1515,82 @@ msgstr[0] "Vigileu amb el vostre llenguatge! Aquà no s'admet la paraula: %s." msgstr[1] "" "Vigileu amb el vostre llenguatge! Aquà no s'admeten les paraules: %s." -#: contrib/comments/forms.py:180 +#: .\contrib\comments\forms.py:182 msgid "" "If you enter anything in this field your comment will be treated as spam" msgstr "" -"Si entreu cualsevol cosa en aquest camp el vostre comentari es tractarà com " +"Si entreu qualsevol cosa en aquest camp el vostre comentari es tractarà com " "a spam" -#: contrib/comments/models.py:22 contrib/contenttypes/models.py:74 +#: .\contrib\comments\models.py:22 .\contrib\contenttypes\models.py:81 msgid "content type" msgstr "tipus de contingut" -#: contrib/comments/models.py:24 +#: .\contrib\comments\models.py:24 msgid "object ID" msgstr "ID de l'objecte" -#: contrib/comments/models.py:52 +#: .\contrib\comments\models.py:52 msgid "user's name" -msgstr "nom d'usuari" +msgstr "nom de l'usuari" -#: contrib/comments/models.py:53 +#: .\contrib\comments\models.py:53 msgid "user's email address" -msgstr "adreça de correu electrònic del usuari" +msgstr "adreça de correu electrònic de l'usuari" -#: contrib/comments/models.py:54 +#: .\contrib\comments\models.py:54 msgid "user's URL" -msgstr "URL del usuari" +msgstr "URL de l'usuari" -#: contrib/comments/models.py:56 contrib/comments/models.py:76 -#: contrib/comments/models.py:169 +#: .\contrib\comments\models.py:56 .\contrib\comments\models.py:76 +#: .\contrib\comments\models.py:169 msgid "comment" msgstr "comentari" -#: contrib/comments/models.py:59 +#: .\contrib\comments\models.py:59 msgid "date/time submitted" msgstr "data/hora d'enviament" -#: contrib/comments/models.py:61 +#: .\contrib\comments\models.py:60 .\db\models\fields\__init__.py:896 +msgid "IP address" +msgstr "Adreça IP" + +#: .\contrib\comments\models.py:61 msgid "is public" msgstr "és públic" -#: contrib/comments/models.py:62 +#: .\contrib\comments\models.py:62 msgid "" "Uncheck this box to make the comment effectively disappear from the site." msgstr "" -"Desmarqui aquesta casella per fer desaparèixar aquest comentari del lloc web " -"de forma efectiva." +"Desmarqueu aquesta casella per fer desaparèixer aquest comentari del lloc " +"web de forma efectiva." -#: contrib/comments/models.py:64 +#: .\contrib\comments\models.py:64 msgid "is removed" msgstr "està eliminat" -#: contrib/comments/models.py:65 +#: .\contrib\comments\models.py:65 msgid "" "Check this box if the comment is inappropriate. A \"This comment has been " "removed\" message will be displayed instead." msgstr "" -"Marqueu aquesta caixa si el comentari no és apropiat. En lloc seu es " +"Marqueu aquesta casella si el comentari no és apropiat. En lloc seu es " "mostrarà \"Aquest comentari ha estat eliminat\" " -#: contrib/comments/models.py:77 +#: .\contrib\comments\models.py:77 msgid "comments" msgstr "comentaris" -#: contrib/comments/models.py:119 +#: .\contrib\comments\models.py:119 msgid "" "This comment was posted by an authenticated user and thus the name is read-" "only." msgstr "" "Aquest comentari va ser publicat per un usuari autentificat, per això el seu " -"nom no es modificable." +"nom no es pot modificar." -#: contrib/comments/models.py:128 +#: .\contrib\comments\models.py:128 msgid "" "This comment was posted by an authenticated user and thus the email is read-" "only." @@ -1593,7 +1598,7 @@ msgstr "" "Aquest comentari va ser publicat per un usuari autentificat, per això la " "seva adreça de correu electrònic no es pot modificar." -#: contrib/comments/models.py:153 +#: .\contrib\comments\models.py:153 #, python-format msgid "" "Posted by %(user)s at %(date)s\n" @@ -1609,379 +1614,410 @@ msgstr "" "http://%(domain)s%(url)s" # Context problem... waitting for comments from django-i18n -#: contrib/comments/models.py:170 +#: .\contrib\comments\models.py:170 msgid "flag" msgstr "marcar" -#: contrib/comments/models.py:171 +#: .\contrib\comments\models.py:171 msgid "date" msgstr "data" -#: contrib/comments/models.py:181 +#: .\contrib\comments\models.py:181 msgid "comment flag" msgstr "marca del comentari" -#: contrib/comments/models.py:182 +#: .\contrib\comments\models.py:182 msgid "comment flags" -msgstr "marques de comentari" +msgstr "marques del comentari" -#: contrib/comments/templates/comments/approve.html:4 +#: .\contrib\comments\templates\comments\approve.html.py:4 msgid "Approve a comment" msgstr "Aprovar un comentari" -#: contrib/comments/templates/comments/approve.html:7 +#: .\contrib\comments\templates\comments\approve.html.py:7 msgid "Really make this comment public?" -msgstr "Realment vol fer aquest comentari públic?" +msgstr "Voleu realment fer públic aquest comentari?" -#: contrib/comments/templates/comments/approve.html:12 +#: .\contrib\comments\templates\comments\approve.html.py:12 msgid "Approve" msgstr "Aprovar" -#: contrib/comments/templates/comments/approved.html:4 +#: .\contrib\comments\templates\comments\approved.html.py:4 msgid "Thanks for approving" msgstr "Grà cies per aprovar" -#: contrib/comments/templates/comments/approved.html:7 -#: contrib/comments/templates/comments/deleted.html:7 -#: contrib/comments/templates/comments/flagged.html:7 +#: .\contrib\comments\templates\comments\approved.html.py:7 +#: .\contrib\comments\templates\comments\deleted.html.py:7 +#: .\contrib\comments\templates\comments\flagged.html.py:7 msgid "" "Thanks for taking the time to improve the quality of discussion on our site" msgstr "" "Grà cies per dedicar el temps a millorar la qualitat del debat al nostre lloc" -#: contrib/comments/templates/comments/delete.html:4 +#: .\contrib\comments\templates\comments\delete.html.py:4 msgid "Remove a comment" -msgstr "Eliminar un comentaris" +msgstr "Eliminar un comentari" -#: contrib/comments/templates/comments/delete.html:7 +#: .\contrib\comments\templates\comments\delete.html.py:7 msgid "Really remove this comment?" -msgstr "Realment vol eliminar aquest comentari?" - -#: contrib/comments/templates/comments/delete.html:12 -msgid "Remove" -msgstr "Eliminar" +msgstr "Realment voleu eliminar aquest comentari?" -#: contrib/comments/templates/comments/deleted.html:4 +#: .\contrib\comments\templates\comments\deleted.html.py:4 msgid "Thanks for removing" msgstr "Grà cies per eliminar" -#: contrib/comments/templates/comments/flag.html:4 +#: .\contrib\comments\templates\comments\flag.html.py:4 msgid "Flag this comment" msgstr "Marcar aquest comentari" -#: contrib/comments/templates/comments/flag.html:7 +#: .\contrib\comments\templates\comments\flag.html.py:7 msgid "Really flag this comment?" -msgstr "Realment vol marcar aquest comentari?" +msgstr "Realment voleu marcar aquest comentari?" # Context problem... waitting for comments from django-i18n -#: contrib/comments/templates/comments/flag.html:12 +#: .\contrib\comments\templates\comments\flag.html.py:12 msgid "Flag" msgstr "Marcar" -#: contrib/comments/templates/comments/flagged.html:4 +#: .\contrib\comments\templates\comments\flagged.html.py:4 msgid "Thanks for flagging" msgstr "Grà cies per marcar" -#: contrib/comments/templates/comments/form.html:17 -#: contrib/comments/templates/comments/preview.html:32 +#: .\contrib\comments\templates\comments\form.html.py:17 +#: .\contrib\comments\templates\comments\preview.html.py:32 msgid "Post" msgstr "Publicar" -#: contrib/comments/templates/comments/form.html:18 -#: contrib/comments/templates/comments/preview.html:33 +#: .\contrib\comments\templates\comments\form.html.py:18 +#: .\contrib\comments\templates\comments\preview.html.py:33 msgid "Preview" msgstr "Vista prèvia" -#: contrib/comments/templates/comments/posted.html:4 +#: .\contrib\comments\templates\comments\posted.html.py:4 msgid "Thanks for commenting" msgstr "Grà cies per comentar" -#: contrib/comments/templates/comments/posted.html:7 +#: .\contrib\comments\templates\comments\posted.html.py:7 msgid "Thank you for your comment" -msgstr "Grà cies pel seu comentari" +msgstr "Grà cies pel vostre comentari" -#: contrib/comments/templates/comments/preview.html:4 -#: contrib/comments/templates/comments/preview.html:13 +#: .\contrib\comments\templates\comments\preview.html.py:4 +#: .\contrib\comments\templates\comments\preview.html.py:13 msgid "Preview your comment" -msgstr "Previsualitzar el seu comentari" +msgstr "Previsualitzar el vostre comentari" -#: contrib/comments/templates/comments/preview.html:11 +#: .\contrib\comments\templates\comments\preview.html.py:11 msgid "Please correct the error below" msgid_plural "Please correct the errors below" -msgstr[0] "Si us plau, corregiu l'error mostrat a baix." -msgstr[1] "Si us plau, corregiu els errors mostrats a baix." +msgstr[0] "Si us plau, corregiu l'error mostrat a sota." +msgstr[1] "Si us plau, corregiu els errors mostrats a sota." -#: contrib/comments/templates/comments/preview.html:16 +#: .\contrib\comments\templates\comments\preview.html.py:16 msgid "Post your comment" msgstr "Enviar el seu comentari" -#: contrib/comments/templates/comments/preview.html:16 +#: .\contrib\comments\templates\comments\preview.html.py:16 msgid "or make changes" -msgstr "o faci canvis." +msgstr "o feu canvis." -#: contrib/contenttypes/models.py:70 +#: .\contrib\contenttypes\models.py:77 msgid "python model class name" msgstr "nom de la classe del model en python" -#: contrib/contenttypes/models.py:75 +#: .\contrib\contenttypes\models.py:82 msgid "content types" msgstr "tipus de continguts" -#: contrib/flatpages/admin.py:9 +#: .\contrib\flatpages\admin.py:9 msgid "" "Example: '/about/contact/'. Make sure to have leading and trailing slashes." msgstr "" "Exemple: '/about/contact/'. Assegureu-vos de posar les barres al principi i " "al final." -#: contrib/flatpages/admin.py:11 +#: .\contrib\flatpages\admin.py:11 msgid "" "This value must contain only letters, numbers, underscores, dashes or " "slashes." msgstr "" -"Aquest valor ha de contenir només lletres, números, guions baixos, guions i " +"Aquest valor ha de contenir només lletres, números, guions baixos, guions o " "barres (/)." -#: contrib/flatpages/admin.py:22 +#: .\contrib\flatpages\admin.py:22 msgid "Advanced options" msgstr "Opcions avançades" -#: contrib/flatpages/models.py:8 +#: .\contrib\flatpages\models.py:8 msgid "title" msgstr "tÃtol" -#: contrib/flatpages/models.py:9 +#: .\contrib\flatpages\models.py:9 msgid "content" msgstr "contingut" -#: contrib/flatpages/models.py:10 +#: .\contrib\flatpages\models.py:10 msgid "enable comments" msgstr "habilitar comentaris" -#: contrib/flatpages/models.py:11 +#: .\contrib\flatpages\models.py:11 msgid "template name" msgstr "nom de la plantilla" -#: contrib/flatpages/models.py:12 +#: .\contrib\flatpages\models.py:12 msgid "" "Example: 'flatpages/contact_page.html'. If this isn't provided, the system " "will use 'flatpages/default.html'." msgstr "" "Exemple: 'flatpages/contact_page.html'. Si no es proporciona, el sistema " -"utilitzarà 'flatpages/default.htmlt'." +"utilitzarà 'flatpages/default.html'." -#: contrib/flatpages/models.py:13 +#: .\contrib\flatpages\models.py:13 msgid "registration required" msgstr "cal estar registrat" -#: contrib/flatpages/models.py:13 +#: .\contrib\flatpages\models.py:13 msgid "If this is checked, only logged-in users will be able to view the page." msgstr "Si està marcat, només els usuaris registrats podran veure la pà gina." -#: contrib/flatpages/models.py:18 +#: .\contrib\flatpages\models.py:18 msgid "flat page" msgstr "pà gina està tica" -#: contrib/flatpages/models.py:19 +#: .\contrib\flatpages\models.py:19 msgid "flat pages" msgstr "pà gines està tiques" -#: contrib/formtools/wizard.py:132 +#: .\contrib\formtools\wizard.py:140 msgid "" "We apologize, but your form has expired. Please continue filling out the " "form from this page." msgstr "" -"Ho sentim, pero el seu formulari ha expirat. Per favor, continui omplint el " -"formulari en aquesta pà gina." +"Ho sentim, pero el vostre formulari ha caducat. Si us plau, continueu " +"omplint el formulari en aquesta pà gina." + +#: .\contrib\gis\db\models\fields.py:50 +msgid "The base GIS field -- maps to the OpenGIS Specification Geometry type." +msgstr "El camp GIS base -- correspon al tipus 'Geometry' de l'especificació " +"OpenGIS." + +#: .\contrib\gis\db\models\fields.py:270 +msgid "Point" +msgstr "Punt" + +#: .\contrib\gis\db\models\fields.py:274 +msgid "Line string" +msgstr "Cadena de lÃnies" + +#: .\contrib\gis\db\models\fields.py:278 +msgid "Polygon" +msgstr "PolÃgon" + +#: .\contrib\gis\db\models\fields.py:282 +msgid "Multi-point" +msgstr "Multi-punt" -#: contrib/gis/forms/fields.py:17 +#: .\contrib\gis\db\models\fields.py:286 +msgid "Multi-line string" +msgstr "Cadena de multi-lÃnies" + +#: .\contrib\gis\db\models\fields.py:290 +msgid "Multi polygon" +msgstr "Multi polÃgon" + +#: .\contrib\gis\db\models\fields.py:294 +msgid "Geometry collection" +msgstr "Col·leció de geometria" + +#: .\contrib\gis\forms\fields.py:17 msgid "No geometry value provided." msgstr "No s'ha indicat cap valor de geometria." -#: contrib/gis/forms/fields.py:18 +#: .\contrib\gis\forms\fields.py:18 msgid "Invalid geometry value." msgstr "Valor de geometria invà lid." -#: contrib/gis/forms/fields.py:19 +#: .\contrib\gis\forms\fields.py:19 msgid "Invalid geometry type." msgstr "Tipus de geometria invà lid." -#: contrib/gis/forms/fields.py:20 +#: .\contrib\gis\forms\fields.py:20 msgid "" "An error occurred when transforming the geometry to the SRID of the geometry " "form field." msgstr "" -"Ha ocurregut un error al transformar la geometria al SRID de la geometria " -"del camp de formulari." +"S'ha produït un error en transformar la geometria al SRID del camp de " +"geometria del formulari." -#: contrib/humanize/templatetags/humanize.py:19 +#: .\contrib\humanize\templatetags\humanize.py:19 msgid "th" -msgstr "rt" +msgstr "è" -#: contrib/humanize/templatetags/humanize.py:19 +# problema: 4t, 5è, 6è... +# o posem t o posem è, i t només aplica a 4t. +#: .\contrib\humanize\templatetags\humanize.py:19 msgid "st" msgstr "r" -#: contrib/humanize/templatetags/humanize.py:19 +#: .\contrib\humanize\templatetags\humanize.py:19 msgid "nd" msgstr "n" -#: contrib/humanize/templatetags/humanize.py:19 +#: .\contrib\humanize\templatetags\humanize.py:19 msgid "rd" msgstr "r" -#: contrib/humanize/templatetags/humanize.py:51 +#: .\contrib\humanize\templatetags\humanize.py:51 #, python-format msgid "%(value).1f million" msgid_plural "%(value).1f million" msgstr[0] "%(value).1f milió" msgstr[1] "%(value).1f milions" -#: contrib/humanize/templatetags/humanize.py:54 +#: .\contrib\humanize\templatetags\humanize.py:54 #, python-format msgid "%(value).1f billion" msgid_plural "%(value).1f billion" msgstr[0] "%(value).1f bilió" msgstr[1] "%(value).1f bilions" -#: contrib/humanize/templatetags/humanize.py:57 +#: .\contrib\humanize\templatetags\humanize.py:57 #, python-format msgid "%(value).1f trillion" msgid_plural "%(value).1f trillion" msgstr[0] "%(value).1f trilió" msgstr[1] "%(value).1f trilions" -#: contrib/humanize/templatetags/humanize.py:73 +#: .\contrib\humanize\templatetags\humanize.py:73 msgid "one" msgstr "un" -#: contrib/humanize/templatetags/humanize.py:73 +#: .\contrib\humanize\templatetags\humanize.py:73 msgid "two" msgstr "dos" -#: contrib/humanize/templatetags/humanize.py:73 +#: .\contrib\humanize\templatetags\humanize.py:73 msgid "three" msgstr "tres" -#: contrib/humanize/templatetags/humanize.py:73 +#: .\contrib\humanize\templatetags\humanize.py:73 msgid "four" msgstr "quatre" -#: contrib/humanize/templatetags/humanize.py:73 +#: .\contrib\humanize\templatetags\humanize.py:73 msgid "five" msgstr "cinc" -#: contrib/humanize/templatetags/humanize.py:73 +#: .\contrib\humanize\templatetags\humanize.py:73 msgid "six" msgstr "sis" -#: contrib/humanize/templatetags/humanize.py:73 +#: .\contrib\humanize\templatetags\humanize.py:73 msgid "seven" msgstr "set" -#: contrib/humanize/templatetags/humanize.py:73 +#: .\contrib\humanize\templatetags\humanize.py:73 msgid "eight" msgstr "vuit" -#: contrib/humanize/templatetags/humanize.py:73 +#: .\contrib\humanize\templatetags\humanize.py:73 msgid "nine" msgstr "nou" -#: contrib/humanize/templatetags/humanize.py:93 +#: .\contrib\humanize\templatetags\humanize.py:93 msgid "today" msgstr "avui" -#: contrib/humanize/templatetags/humanize.py:95 +#: .\contrib\humanize\templatetags\humanize.py:95 msgid "tomorrow" msgstr "demà " -#: contrib/humanize/templatetags/humanize.py:97 +#: .\contrib\humanize\templatetags\humanize.py:97 msgid "yesterday" msgstr "ahir" -#: contrib/localflavor/ar/forms.py:27 +#: .\contrib\localflavor\ar\forms.py:28 msgid "Enter a postal code in the format NNNN or ANNNNAAA." msgstr "Introduïu un codi postal en el format NNNN o ANNNNAAA." -#: contrib/localflavor/ar/forms.py:49 contrib/localflavor/br/forms.py:96 -#: contrib/localflavor/br/forms.py:135 contrib/localflavor/pe/forms.py:23 -#: contrib/localflavor/pe/forms.py:51 +#: .\contrib\localflavor\ar\forms.py:50 .\contrib\localflavor\br\forms.py:92 +#: .\contrib\localflavor\br\forms.py:131 .\contrib\localflavor\pe\forms.py:24 +#: .\contrib\localflavor\pe\forms.py:52 msgid "This field requires only numbers." msgstr "Aquest camp precisa només números." -#: contrib/localflavor/ar/forms.py:50 +#: .\contrib\localflavor\ar\forms.py:51 msgid "This field requires 7 or 8 digits." msgstr "Aquest camp precisa 7 o 8 dÃgits." -#: contrib/localflavor/ar/forms.py:79 +#: .\contrib\localflavor\ar\forms.py:80 msgid "Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format." msgstr "" "Introduïu un número CUIT và lid en el format XX-XXXXXXXX-X o XXXXXXXXXXXX." -#: contrib/localflavor/ar/forms.py:80 +#: .\contrib\localflavor\ar\forms.py:81 msgid "Invalid CUIT." -msgstr "Invà lid CUIT." +msgstr "CUIT invà lid." -#: contrib/localflavor/at/at_states.py:5 +#: .\contrib\localflavor\at\at_states.py:5 msgid "Burgenland" msgstr "Burgenland" -#: contrib/localflavor/at/at_states.py:6 +#: .\contrib\localflavor\at\at_states.py:6 msgid "Carinthia" msgstr "Carinthia" -#: contrib/localflavor/at/at_states.py:7 +#: .\contrib\localflavor\at\at_states.py:7 msgid "Lower Austria" -msgstr "Lower Austria" +msgstr "Àustria Inferior" -#: contrib/localflavor/at/at_states.py:8 +#: .\contrib\localflavor\at\at_states.py:8 msgid "Upper Austria" -msgstr "Upper Austria" +msgstr "Àustria Superior" -#: contrib/localflavor/at/at_states.py:9 +#: .\contrib\localflavor\at\at_states.py:9 msgid "Salzburg" msgstr "Salzburg" -#: contrib/localflavor/at/at_states.py:10 +#: .\contrib\localflavor\at\at_states.py:10 msgid "Styria" msgstr "Styria" -#: contrib/localflavor/at/at_states.py:11 +#: .\contrib\localflavor\at\at_states.py:11 msgid "Tyrol" -msgstr "Tyrol" +msgstr "Tirol" -#: contrib/localflavor/at/at_states.py:12 +#: .\contrib\localflavor\at\at_states.py:12 msgid "Vorarlberg" msgstr "Vorarlberg" -#: contrib/localflavor/at/at_states.py:13 +#: .\contrib\localflavor\at\at_states.py:13 msgid "Vienna" msgstr "Viena" -#: contrib/localflavor/at/forms.py:20 contrib/localflavor/ch/forms.py:16 -#: contrib/localflavor/no/forms.py:12 +#: .\contrib\localflavor\at\forms.py:20 .\contrib\localflavor\ch\forms.py:17 +#: .\contrib\localflavor\no\forms.py:13 msgid "Enter a zip code in the format XXXX." msgstr "Introduïu un codi zip en el format XXXX." -#: contrib/localflavor/at/forms.py:48 +#: .\contrib\localflavor\at\forms.py:48 msgid "Enter a valid Austrian Social Security Number in XXXX XXXXXX format." msgstr "" -"Introduïu un número và lid de la Seguretat Social Austriaca en el format XXXX " +"Introduïu un número và lid de la Seguretat Social AustrÃaca en el format XXXX " "XXXXXX." -#: contrib/localflavor/au/forms.py:16 +#: .\contrib\localflavor\au\forms.py:17 msgid "Enter a 4 digit post code." msgstr "Introduïu un codi postal de 4 dÃgits." -#: contrib/localflavor/br/forms.py:21 +#: .\contrib\localflavor\br\forms.py:17 msgid "Enter a zip code in the format XXXXX-XXX." msgstr "Introduïu un codi zip en el format XXXXX-XXX." -#: contrib/localflavor/br/forms.py:30 +#: .\contrib\localflavor\br\forms.py:26 msgid "Phone numbers must be in XX-XXXX-XXXX format." -msgstr "El número de telèfon ha de estar en el format XX-XXXX-XXXX." +msgstr "El número de telèfon ha d'estar en el format XX-XXXX-XXXX." -#: contrib/localflavor/br/forms.py:58 +#: .\contrib\localflavor\br\forms.py:54 msgid "" "Select a valid brazilian state. That state is not one of the available " "states." @@ -1989,137 +2025,137 @@ msgstr "" "Seleccioneu un estat brasiler và lid. Aquest estat no és un dels estats " "disponibles." -#: contrib/localflavor/br/forms.py:94 +#: .\contrib\localflavor\br\forms.py:90 msgid "Invalid CPF number." msgstr "Número CPF invà lid." -#: contrib/localflavor/br/forms.py:95 +#: .\contrib\localflavor\br\forms.py:91 msgid "This field requires at most 11 digits or 14 characters." -msgstr "Aquest camp precisa com a mà xim 11 dÃgits o 14 caracters." +msgstr "Aquest camp precisa com a mà xim 11 dÃgits o 14 carà cters." -#: contrib/localflavor/br/forms.py:134 +#: .\contrib\localflavor\br\forms.py:130 msgid "Invalid CNPJ number." msgstr "Número CNPJ invà lid." -#: contrib/localflavor/br/forms.py:136 +#: .\contrib\localflavor\br\forms.py:132 msgid "This field requires at least 14 digits" msgstr "Aquest camp precisa almenys 14 dÃgits." -#: contrib/localflavor/ca/forms.py:17 +#: .\contrib\localflavor\ca\forms.py:25 msgid "Enter a postal code in the format XXX XXX." msgstr "Introduïu un codi postal en el format XXX XXX." -#: contrib/localflavor/ca/forms.py:88 +#: .\contrib\localflavor\ca\forms.py:96 msgid "Enter a valid Canadian Social Insurance number in XXX-XXX-XXX format." msgstr "" -"Introduïu un número và lid de la Assegurança Social de Canadà en el format " -"XXX-XXX-XXX." +"Introduïu un número và lid de la Seguretat Social de Canadà en el format XXX-" +"XXX-XXX." -#: contrib/localflavor/ch/ch_states.py:5 +#: .\contrib\localflavor\ch\ch_states.py:5 msgid "Aargau" msgstr "Argau" -#: contrib/localflavor/ch/ch_states.py:6 +#: .\contrib\localflavor\ch\ch_states.py:6 msgid "Appenzell Innerrhoden" msgstr "Appenzell Inner-Rhoden" -#: contrib/localflavor/ch/ch_states.py:7 +#: .\contrib\localflavor\ch\ch_states.py:7 msgid "Appenzell Ausserrhoden" msgstr "Appenzell Ausser-Rhoden" -#: contrib/localflavor/ch/ch_states.py:8 +#: .\contrib\localflavor\ch\ch_states.py:8 msgid "Basel-Stadt" -msgstr "Basel-Ciutat" +msgstr "Basilea-Ciutat" -#: contrib/localflavor/ch/ch_states.py:9 +#: .\contrib\localflavor\ch\ch_states.py:9 msgid "Basel-Land" -msgstr "Basel-Camp" +msgstr "Basilea-Land" -#: contrib/localflavor/ch/ch_states.py:10 +#: .\contrib\localflavor\ch\ch_states.py:10 msgid "Berne" msgstr "Berna" -#: contrib/localflavor/ch/ch_states.py:11 +#: .\contrib\localflavor\ch\ch_states.py:11 msgid "Fribourg" msgstr "Friburg" -#: contrib/localflavor/ch/ch_states.py:12 +#: .\contrib\localflavor\ch\ch_states.py:12 msgid "Geneva" msgstr "Ginebra" -#: contrib/localflavor/ch/ch_states.py:13 +#: .\contrib\localflavor\ch\ch_states.py:13 msgid "Glarus" msgstr "Glarus" -#: contrib/localflavor/ch/ch_states.py:14 +#: .\contrib\localflavor\ch\ch_states.py:14 msgid "Graubuenden" msgstr "Graubuenden" -#: contrib/localflavor/ch/ch_states.py:15 +#: .\contrib\localflavor\ch\ch_states.py:15 msgid "Jura" msgstr "Jura" -#: contrib/localflavor/ch/ch_states.py:16 +#: .\contrib\localflavor\ch\ch_states.py:16 msgid "Lucerne" msgstr "Lucerna" -#: contrib/localflavor/ch/ch_states.py:17 +#: .\contrib\localflavor\ch\ch_states.py:17 msgid "Neuchatel" msgstr "Neuchatel" -#: contrib/localflavor/ch/ch_states.py:18 +#: .\contrib\localflavor\ch\ch_states.py:18 msgid "Nidwalden" msgstr "Nidwalden" -#: contrib/localflavor/ch/ch_states.py:19 +#: .\contrib\localflavor\ch\ch_states.py:19 msgid "Obwalden" msgstr "Obwalden" -#: contrib/localflavor/ch/ch_states.py:20 +#: .\contrib\localflavor\ch\ch_states.py:20 msgid "Schaffhausen" msgstr "Schaffhausen" -#: contrib/localflavor/ch/ch_states.py:21 +#: .\contrib\localflavor\ch\ch_states.py:21 msgid "Schwyz" msgstr "Schwyz" -#: contrib/localflavor/ch/ch_states.py:22 +#: .\contrib\localflavor\ch\ch_states.py:22 msgid "Solothurn" msgstr "Solothurn" -#: contrib/localflavor/ch/ch_states.py:23 +#: .\contrib\localflavor\ch\ch_states.py:23 msgid "St. Gallen" msgstr "St. Gallen" -#: contrib/localflavor/ch/ch_states.py:24 +#: .\contrib\localflavor\ch\ch_states.py:24 msgid "Thurgau" msgstr "Thurgau" -#: contrib/localflavor/ch/ch_states.py:25 +#: .\contrib\localflavor\ch\ch_states.py:25 msgid "Ticino" msgstr "Ticino" -#: contrib/localflavor/ch/ch_states.py:26 +#: .\contrib\localflavor\ch\ch_states.py:26 msgid "Uri" msgstr "Uri" -#: contrib/localflavor/ch/ch_states.py:27 +#: .\contrib\localflavor\ch\ch_states.py:27 msgid "Valais" msgstr "Valais" -#: contrib/localflavor/ch/ch_states.py:28 +#: .\contrib\localflavor\ch\ch_states.py:28 msgid "Vaud" msgstr "Vaud" -#: contrib/localflavor/ch/ch_states.py:29 +#: .\contrib\localflavor\ch\ch_states.py:29 msgid "Zug" msgstr "Zug" -#: contrib/localflavor/ch/ch_states.py:30 +#: .\contrib\localflavor\ch\ch_states.py:30 msgid "Zurich" msgstr "Zuric" -#: contrib/localflavor/ch/forms.py:64 +#: .\contrib\localflavor\ch\forms.py:65 msgid "" "Enter a valid Swiss identity or passport card number in X1234567<0 or " "1234567890 format." @@ -2127,445 +2163,445 @@ msgstr "" "Introduïu un número d'identificació o de passaport Suïssos en els formats " "1234567890 o X1234567<0." -#: contrib/localflavor/cl/forms.py:29 +#: .\contrib\localflavor\cl\forms.py:30 msgid "Enter a valid Chilean RUT." msgstr "Introduïu un RUT Xilè và lid." -#: contrib/localflavor/cl/forms.py:30 +#: .\contrib\localflavor\cl\forms.py:31 msgid "Enter a valid Chilean RUT. The format is XX.XXX.XXX-X." msgstr "Introduïu un RUT Xilè và lid. El format és XX.XXX.XXX-X" -#: contrib/localflavor/cl/forms.py:31 +#: .\contrib\localflavor\cl\forms.py:32 msgid "The Chilean RUT is not valid." msgstr "El RUT Xilè no és và lid." -#: contrib/localflavor/cz/cz_regions.py:8 +#: .\contrib\localflavor\cz\cz_regions.py:8 msgid "Prague" msgstr "Praga" -#: contrib/localflavor/cz/cz_regions.py:9 +#: .\contrib\localflavor\cz\cz_regions.py:9 msgid "Central Bohemian Region" msgstr "Regió Bohèmia Central" -#: contrib/localflavor/cz/cz_regions.py:10 +#: .\contrib\localflavor\cz\cz_regions.py:10 msgid "South Bohemian Region" msgstr "Regió Bohèmia Sur" -#: contrib/localflavor/cz/cz_regions.py:11 +#: .\contrib\localflavor\cz\cz_regions.py:11 msgid "Pilsen Region" msgstr "Regió Pilsen" -#: contrib/localflavor/cz/cz_regions.py:12 +#: .\contrib\localflavor\cz\cz_regions.py:12 msgid "Carlsbad Region" msgstr "Regió Carlsbad" -#: contrib/localflavor/cz/cz_regions.py:13 +#: .\contrib\localflavor\cz\cz_regions.py:13 msgid "Usti Region" msgstr "Regió Usti" -#: contrib/localflavor/cz/cz_regions.py:14 +#: .\contrib\localflavor\cz\cz_regions.py:14 msgid "Liberec Region" msgstr "Regió Liberec" -#: contrib/localflavor/cz/cz_regions.py:15 +#: .\contrib\localflavor\cz\cz_regions.py:15 msgid "Hradec Region" msgstr "Regió Hradec" -#: contrib/localflavor/cz/cz_regions.py:16 +#: .\contrib\localflavor\cz\cz_regions.py:16 msgid "Pardubice Region" msgstr "Regió Pardubice" -#: contrib/localflavor/cz/cz_regions.py:17 +#: .\contrib\localflavor\cz\cz_regions.py:17 msgid "Vysocina Region" msgstr "Regió Vysocina" -#: contrib/localflavor/cz/cz_regions.py:18 +#: .\contrib\localflavor\cz\cz_regions.py:18 msgid "South Moravian Region" msgstr "Regió Morà via Sur" -#: contrib/localflavor/cz/cz_regions.py:19 +#: .\contrib\localflavor\cz\cz_regions.py:19 msgid "Olomouc Region" msgstr "Regió Olomouc" -#: contrib/localflavor/cz/cz_regions.py:20 +#: .\contrib\localflavor\cz\cz_regions.py:20 msgid "Zlin Region" msgstr "Regió Zlin" -#: contrib/localflavor/cz/cz_regions.py:21 +#: .\contrib\localflavor\cz\cz_regions.py:21 msgid "Moravian-Silesian Region" msgstr "Regió Morà via-Silesiana" -#: contrib/localflavor/cz/forms.py:27 contrib/localflavor/sk/forms.py:30 +#: .\contrib\localflavor\cz\forms.py:28 .\contrib\localflavor\sk\forms.py:30 msgid "Enter a postal code in the format XXXXX or XXX XX." -msgstr "Introduïu un codi postal en el format XXXXX or XXX XX." +msgstr "Introduïu un codi postal en el format XXXXX o XXX XX." -#: contrib/localflavor/cz/forms.py:47 +#: .\contrib\localflavor\cz\forms.py:48 msgid "Enter a birth number in the format XXXXXX/XXXX or XXXXXXXXXX." msgstr "" "Introduïu un número de naixement en el format XXXXXX/XXXX o XXXXXXXXXX." -#: contrib/localflavor/cz/forms.py:48 +#: .\contrib\localflavor\cz\forms.py:49 msgid "Invalid optional parameter Gender, valid values are 'f' and 'm'" msgstr "" "El parà metre opcional 'Gènere' és invà lid, els valors và lids son 'f' i 'm'." -#: contrib/localflavor/cz/forms.py:49 +#: .\contrib\localflavor\cz\forms.py:50 msgid "Enter a valid birth number." msgstr "Introduïu un número de naixement và lid." -#: contrib/localflavor/cz/forms.py:106 +#: .\contrib\localflavor\cz\forms.py:107 msgid "Enter a valid IC number." -msgstr "Intrduïu un número de IC và lid." +msgstr "Introduïu un número de IC và lid." -#: contrib/localflavor/de/de_states.py:5 +#: .\contrib\localflavor\de\de_states.py:5 msgid "Baden-Wuerttemberg" msgstr "Baden-Württemberg" -#: contrib/localflavor/de/de_states.py:6 +#: .\contrib\localflavor\de\de_states.py:6 msgid "Bavaria" msgstr "Baviera" -#: contrib/localflavor/de/de_states.py:7 +#: .\contrib\localflavor\de\de_states.py:7 msgid "Berlin" msgstr "BerlÃn" -#: contrib/localflavor/de/de_states.py:8 +#: .\contrib\localflavor\de\de_states.py:8 msgid "Brandenburg" msgstr "Brandenburg" -#: contrib/localflavor/de/de_states.py:9 +#: .\contrib\localflavor\de\de_states.py:9 msgid "Bremen" msgstr "Bremen" -#: contrib/localflavor/de/de_states.py:10 +#: .\contrib\localflavor\de\de_states.py:10 msgid "Hamburg" msgstr "Hamburg" -#: contrib/localflavor/de/de_states.py:11 +#: .\contrib\localflavor\de\de_states.py:11 msgid "Hessen" msgstr "Hessen" -#: contrib/localflavor/de/de_states.py:12 +#: .\contrib\localflavor\de\de_states.py:12 msgid "Mecklenburg-Western Pomerania" msgstr "Mecklenburg-Pomerà nia Occidental" -#: contrib/localflavor/de/de_states.py:13 +#: .\contrib\localflavor\de\de_states.py:13 msgid "Lower Saxony" msgstr "Baixa Saxònia" -#: contrib/localflavor/de/de_states.py:14 +#: .\contrib\localflavor\de\de_states.py:14 msgid "North Rhine-Westphalia" msgstr "Renà nia del Nord-Westfà lia" -#: contrib/localflavor/de/de_states.py:15 +#: .\contrib\localflavor\de\de_states.py:15 msgid "Rhineland-Palatinate" msgstr "Renà nia-Palatinat" -#: contrib/localflavor/de/de_states.py:16 +#: .\contrib\localflavor\de\de_states.py:16 msgid "Saarland" msgstr "Saarland" -#: contrib/localflavor/de/de_states.py:17 +#: .\contrib\localflavor\de\de_states.py:17 msgid "Saxony" msgstr "Saxònia" -#: contrib/localflavor/de/de_states.py:18 +#: .\contrib\localflavor\de\de_states.py:18 msgid "Saxony-Anhalt" msgstr "Saxònia-Anhalt" -#: contrib/localflavor/de/de_states.py:19 +#: .\contrib\localflavor\de\de_states.py:19 msgid "Schleswig-Holstein" msgstr "Slesvig-Holstein" -#: contrib/localflavor/de/de_states.py:20 +#: .\contrib\localflavor\de\de_states.py:20 msgid "Thuringia" msgstr "TurÃngia" -#: contrib/localflavor/de/forms.py:14 contrib/localflavor/fi/forms.py:12 -#: contrib/localflavor/fr/forms.py:15 +#: .\contrib\localflavor\de\forms.py:15 .\contrib\localflavor\fi\forms.py:13 +#: .\contrib\localflavor\fr\forms.py:16 msgid "Enter a zip code in the format XXXXX." msgstr "Introduïu un codi zip en el format XXXXX." -#: contrib/localflavor/de/forms.py:41 +#: .\contrib\localflavor\de\forms.py:42 msgid "" "Enter a valid German identity card number in XXXXXXXXXXX-XXXXXXX-XXXXXXX-X " "format." msgstr "" -"Introduïu un número de tarjeta d'identificació alemany và lid en el format " +"Introduïu un número và lid de tarjeta d'identificació alemanya en el format " "XXXXXXXXXXX-XXXXXXX-XXXXXXX-X." -#: contrib/localflavor/es/es_provinces.py:5 +#: .\contrib\localflavor\es\es_provinces.py:5 msgid "Arava" msgstr "Àlaba" -#: contrib/localflavor/es/es_provinces.py:6 +#: .\contrib\localflavor\es\es_provinces.py:6 msgid "Albacete" msgstr "Albacete" -#: contrib/localflavor/es/es_provinces.py:7 +#: .\contrib\localflavor\es\es_provinces.py:7 msgid "Alacant" msgstr "Alacant" -#: contrib/localflavor/es/es_provinces.py:8 +#: .\contrib\localflavor\es\es_provinces.py:8 msgid "Almeria" msgstr "Almeria" -#: contrib/localflavor/es/es_provinces.py:9 +#: .\contrib\localflavor\es\es_provinces.py:9 msgid "Avila" msgstr "Àvila" -#: contrib/localflavor/es/es_provinces.py:10 +#: .\contrib\localflavor\es\es_provinces.py:10 msgid "Badajoz" msgstr "Badajoz" -#: contrib/localflavor/es/es_provinces.py:11 +#: .\contrib\localflavor\es\es_provinces.py:11 msgid "Illes Balears" msgstr "Illes Balears" -#: contrib/localflavor/es/es_provinces.py:12 +#: .\contrib\localflavor\es\es_provinces.py:12 msgid "Barcelona" msgstr "Barcelona" -#: contrib/localflavor/es/es_provinces.py:13 +#: .\contrib\localflavor\es\es_provinces.py:13 msgid "Burgos" msgstr "Burgos" -#: contrib/localflavor/es/es_provinces.py:14 +#: .\contrib\localflavor\es\es_provinces.py:14 msgid "Caceres" msgstr "Cà ceres" -#: contrib/localflavor/es/es_provinces.py:15 +#: .\contrib\localflavor\es\es_provinces.py:15 msgid "Cadiz" msgstr "Cadis" -#: contrib/localflavor/es/es_provinces.py:16 +#: .\contrib\localflavor\es\es_provinces.py:16 msgid "Castello" msgstr "Castelló" -#: contrib/localflavor/es/es_provinces.py:17 +#: .\contrib\localflavor\es\es_provinces.py:17 msgid "Ciudad Real" msgstr "Ciudad Real" -#: contrib/localflavor/es/es_provinces.py:18 +#: .\contrib\localflavor\es\es_provinces.py:18 msgid "Cordoba" msgstr "Còrdova" -#: contrib/localflavor/es/es_provinces.py:19 +#: .\contrib\localflavor\es\es_provinces.py:19 msgid "A Coruna" msgstr "La Corunya" -#: contrib/localflavor/es/es_provinces.py:20 +#: .\contrib\localflavor\es\es_provinces.py:20 msgid "Cuenca" msgstr "Conca" -#: contrib/localflavor/es/es_provinces.py:21 +#: .\contrib\localflavor\es\es_provinces.py:21 msgid "Girona" msgstr "Girona" -#: contrib/localflavor/es/es_provinces.py:22 +#: .\contrib\localflavor\es\es_provinces.py:22 msgid "Granada" msgstr "Granada" -#: contrib/localflavor/es/es_provinces.py:23 +#: .\contrib\localflavor\es\es_provinces.py:23 msgid "Guadalajara" msgstr "Guadalajara" -#: contrib/localflavor/es/es_provinces.py:24 +#: .\contrib\localflavor\es\es_provinces.py:24 msgid "Guipuzkoa" msgstr "Guipúscoa" -#: contrib/localflavor/es/es_provinces.py:25 +#: .\contrib\localflavor\es\es_provinces.py:25 msgid "Huelva" msgstr "Huelva" -#: contrib/localflavor/es/es_provinces.py:26 +#: .\contrib\localflavor\es\es_provinces.py:26 msgid "Huesca" msgstr "Osca" -#: contrib/localflavor/es/es_provinces.py:27 +#: .\contrib\localflavor\es\es_provinces.py:27 msgid "Jaen" msgstr "Jaén" -#: contrib/localflavor/es/es_provinces.py:28 +#: .\contrib\localflavor\es\es_provinces.py:28 msgid "Leon" msgstr "Lleó" -#: contrib/localflavor/es/es_provinces.py:29 +#: .\contrib\localflavor\es\es_provinces.py:29 msgid "Lleida" msgstr "Lleida" -#: contrib/localflavor/es/es_provinces.py:30 -#: contrib/localflavor/es/es_regions.py:17 +#: .\contrib\localflavor\es\es_provinces.py:30 +#: .\contrib\localflavor\es\es_regions.py:17 msgid "La Rioja" msgstr "La Rioja" -#: contrib/localflavor/es/es_provinces.py:31 +#: .\contrib\localflavor\es\es_provinces.py:31 msgid "Lugo" msgstr "Lugo" -#: contrib/localflavor/es/es_provinces.py:32 -#: contrib/localflavor/es/es_regions.py:18 +#: .\contrib\localflavor\es\es_provinces.py:32 +#: .\contrib\localflavor\es\es_regions.py:18 msgid "Madrid" msgstr "Madrid" -#: contrib/localflavor/es/es_provinces.py:33 +#: .\contrib\localflavor\es\es_provinces.py:33 msgid "Malaga" msgstr "Mà laga" -#: contrib/localflavor/es/es_provinces.py:34 +#: .\contrib\localflavor\es\es_provinces.py:34 msgid "Murcia" msgstr "Múrcia" -#: contrib/localflavor/es/es_provinces.py:35 +#: .\contrib\localflavor\es\es_provinces.py:35 msgid "Navarre" msgstr "Navarra" -#: contrib/localflavor/es/es_provinces.py:36 +#: .\contrib\localflavor\es\es_provinces.py:36 msgid "Ourense" msgstr "Ourense" -#: contrib/localflavor/es/es_provinces.py:37 +#: .\contrib\localflavor\es\es_provinces.py:37 msgid "Asturias" msgstr "Astúries" -#: contrib/localflavor/es/es_provinces.py:38 +#: .\contrib\localflavor\es\es_provinces.py:38 msgid "Palencia" msgstr "Palència" -#: contrib/localflavor/es/es_provinces.py:39 +#: .\contrib\localflavor\es\es_provinces.py:39 msgid "Las Palmas" msgstr "Las Palmas" -#: contrib/localflavor/es/es_provinces.py:40 +#: .\contrib\localflavor\es\es_provinces.py:40 msgid "Pontevedra" msgstr "Pontevedra" -#: contrib/localflavor/es/es_provinces.py:41 +#: .\contrib\localflavor\es\es_provinces.py:41 msgid "Salamanca" msgstr "Salamanca" -#: contrib/localflavor/es/es_provinces.py:42 +#: .\contrib\localflavor\es\es_provinces.py:42 msgid "Santa Cruz de Tenerife" msgstr "Santa Cruz de Tenerife" -#: contrib/localflavor/es/es_provinces.py:43 -#: contrib/localflavor/es/es_regions.py:11 +#: .\contrib\localflavor\es\es_provinces.py:43 +#: .\contrib\localflavor\es\es_regions.py:11 msgid "Cantabria" msgstr "Cantà bria" -#: contrib/localflavor/es/es_provinces.py:44 +#: .\contrib\localflavor\es\es_provinces.py:44 msgid "Segovia" msgstr "Segòvia" -#: contrib/localflavor/es/es_provinces.py:45 +#: .\contrib\localflavor\es\es_provinces.py:45 msgid "Seville" msgstr "Sevilla" -#: contrib/localflavor/es/es_provinces.py:46 +#: .\contrib\localflavor\es\es_provinces.py:46 msgid "Soria" msgstr "Sòria" -#: contrib/localflavor/es/es_provinces.py:47 +#: .\contrib\localflavor\es\es_provinces.py:47 msgid "Tarragona" msgstr "Tarragona" -#: contrib/localflavor/es/es_provinces.py:48 +#: .\contrib\localflavor\es\es_provinces.py:48 msgid "Teruel" msgstr "Terol" -#: contrib/localflavor/es/es_provinces.py:49 +#: .\contrib\localflavor\es\es_provinces.py:49 msgid "Toledo" msgstr "Toledo" -#: contrib/localflavor/es/es_provinces.py:50 +#: .\contrib\localflavor\es\es_provinces.py:50 msgid "Valencia" msgstr "València" -#: contrib/localflavor/es/es_provinces.py:51 +#: .\contrib\localflavor\es\es_provinces.py:51 msgid "Valladolid" msgstr "Valladolid" -#: contrib/localflavor/es/es_provinces.py:52 +#: .\contrib\localflavor\es\es_provinces.py:52 msgid "Bizkaia" msgstr "Biscaia" -#: contrib/localflavor/es/es_provinces.py:53 +#: .\contrib\localflavor\es\es_provinces.py:53 msgid "Zamora" msgstr "Zamora" -#: contrib/localflavor/es/es_provinces.py:54 +#: .\contrib\localflavor\es\es_provinces.py:54 msgid "Zaragoza" msgstr "Saragossa" -#: contrib/localflavor/es/es_provinces.py:55 +#: .\contrib\localflavor\es\es_provinces.py:55 msgid "Ceuta" msgstr "Ceuta" -#: contrib/localflavor/es/es_provinces.py:56 +#: .\contrib\localflavor\es\es_provinces.py:56 msgid "Melilla" msgstr "Melilla" -#: contrib/localflavor/es/es_regions.py:5 +#: .\contrib\localflavor\es\es_regions.py:5 msgid "Andalusia" msgstr "Andalusia" -#: contrib/localflavor/es/es_regions.py:6 +#: .\contrib\localflavor\es\es_regions.py:6 msgid "Aragon" msgstr "Aragó" -#: contrib/localflavor/es/es_regions.py:7 +#: .\contrib\localflavor\es\es_regions.py:7 msgid "Principality of Asturias" msgstr "Principat d'Astúries" -#: contrib/localflavor/es/es_regions.py:8 +#: .\contrib\localflavor\es\es_regions.py:8 msgid "Balearic Islands" msgstr "Illes Balears" -#: contrib/localflavor/es/es_regions.py:9 +#: .\contrib\localflavor\es\es_regions.py:9 msgid "Basque Country" msgstr "Euskadi" -#: contrib/localflavor/es/es_regions.py:10 +#: .\contrib\localflavor\es\es_regions.py:10 msgid "Canary Islands" msgstr "Canà ries" -#: contrib/localflavor/es/es_regions.py:12 +#: .\contrib\localflavor\es\es_regions.py:12 msgid "Castile-La Mancha" msgstr "Castella-La Mancha" -#: contrib/localflavor/es/es_regions.py:13 +#: .\contrib\localflavor\es\es_regions.py:13 msgid "Castile and Leon" msgstr "Castella i Lleó" -#: contrib/localflavor/es/es_regions.py:14 +#: .\contrib\localflavor\es\es_regions.py:14 msgid "Catalonia" msgstr "Catalunya" -#: contrib/localflavor/es/es_regions.py:15 +#: .\contrib\localflavor\es\es_regions.py:15 msgid "Extremadura" msgstr "Extremadura" -#: contrib/localflavor/es/es_regions.py:16 +#: .\contrib\localflavor\es\es_regions.py:16 msgid "Galicia" msgstr "GalÃcia" -#: contrib/localflavor/es/es_regions.py:19 +#: .\contrib\localflavor\es\es_regions.py:19 msgid "Region of Murcia" msgstr "Regió de Múrcia" -#: contrib/localflavor/es/es_regions.py:20 +#: .\contrib\localflavor\es\es_regions.py:20 msgid "Foral Community of Navarre" msgstr "Comunitat Foral de Navarra" -#: contrib/localflavor/es/es_regions.py:21 +#: .\contrib\localflavor\es\es_regions.py:21 msgid "Valencian Community" msgstr "Comunitat Valenciana" -#: contrib/localflavor/es/forms.py:19 +#: .\contrib\localflavor\es\forms.py:20 msgid "Enter a valid postal code in the range and format 01XXX - 52XXX." msgstr "Introduïu un codi postal en rang i format 01XXX - 52XXX." -#: contrib/localflavor/es/forms.py:39 +#: .\contrib\localflavor\es\forms.py:40 msgid "" "Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or " "9XXXXXXXX." @@ -2573,1382 +2609,2112 @@ msgstr "" "Introduïu un número de telèfon và lid en un dels formats 6XXXXXXXX, 8XXXXXXXX " "o 9XXXXXXXX." -#: contrib/localflavor/es/forms.py:66 +#: .\contrib\localflavor\es\forms.py:67 msgid "Please enter a valid NIF, NIE, or CIF." msgstr "Si us plau, introduïu un NIF, NIE o CIF và lid." -#: contrib/localflavor/es/forms.py:67 +#: .\contrib\localflavor\es\forms.py:68 msgid "Please enter a valid NIF or NIE." msgstr "Si us plau, introduïu un NIF o NIE và lid." -#: contrib/localflavor/es/forms.py:68 +#: .\contrib\localflavor\es\forms.py:69 msgid "Invalid checksum for NIF." -msgstr "Validació invà lida del NIF." +msgstr "Verificació del NIF invà lida." -#: contrib/localflavor/es/forms.py:69 +#: .\contrib\localflavor\es\forms.py:70 msgid "Invalid checksum for NIE." -msgstr "Validació invà lida del NIE." +msgstr "Verificació del NIE invà lida." -#: contrib/localflavor/es/forms.py:70 +#: .\contrib\localflavor\es\forms.py:71 msgid "Invalid checksum for CIF." -msgstr "Validació invà lida del CIF." +msgstr "Verificació del CIF invà lida." -#: contrib/localflavor/es/forms.py:142 +#: .\contrib\localflavor\es\forms.py:143 msgid "" "Please enter a valid bank account number in format XXXX-XXXX-XX-XXXXXXXXXX." msgstr "" "Introduïu un número de compte bancari và lid en el format XXXX-XXXX-XX-" "XXXXXXXXXX." -#: contrib/localflavor/es/forms.py:143 +#: .\contrib\localflavor\es\forms.py:144 msgid "Invalid checksum for bank account number." -msgstr "Validació invà lida del número de compte bancari." +msgstr "Verificació del número de compte bancari invà lida." -#: contrib/localflavor/fi/forms.py:28 +#: .\contrib\localflavor\fi\forms.py:29 msgid "Enter a valid Finnish social security number." msgstr "Introduïu un número và lid de la seguretat social finlandesa." -#: contrib/localflavor/fr/forms.py:30 +#: .\contrib\localflavor\fr\forms.py:31 msgid "Phone numbers must be in 0X XX XX XX XX format." msgstr "Els números de telèfon han de estar en el format 0X XX XX XX XX." -#: contrib/localflavor/in_/forms.py:14 +#: .\contrib\localflavor\id\forms.py:28 +msgid "Enter a valid post code" +msgstr "Introduïu un codi postal và lid." + +#: .\contrib\localflavor\id\forms.py:68 .\contrib\localflavor\nl\forms.py:53 +msgid "Enter a valid phone number" +msgstr "Introduïu un número de telèfon và lid." + +#: .\contrib\localflavor\id\forms.py:107 +msgid "Enter a valid vehicle license plate number" +msgstr "Introduïu un número de matrÃcula và lid." + +#: .\contrib\localflavor\id\forms.py:170 +msgid "Enter a valid NIK/KTP number" +msgstr "Introduïu un número NIK/KTP và lid." + +#: .\contrib\localflavor\id\id_choices.py:9 +#: .\contrib\localflavor\id\id_choices.py:73 +msgid "Bali" +msgstr "Bali" + +#: .\contrib\localflavor\id\id_choices.py:10 +#: .\contrib\localflavor\id\id_choices.py:45 +msgid "Banten" +msgstr "Banten" + +#: .\contrib\localflavor\id\id_choices.py:11 +#: .\contrib\localflavor\id\id_choices.py:54 +msgid "Bengkulu" +msgstr "Bengkulu" + +#: .\contrib\localflavor\id\id_choices.py:12 +#: .\contrib\localflavor\id\id_choices.py:47 +msgid "Yogyakarta" +msgstr "Yogyakarta" + +#: .\contrib\localflavor\id\id_choices.py:13 +#: .\contrib\localflavor\id\id_choices.py:51 +msgid "Jakarta" +msgstr "Jakarta" + +#: .\contrib\localflavor\id\id_choices.py:14 +#: .\contrib\localflavor\id\id_choices.py:75 +msgid "Gorontalo" +msgstr "Gorontalo" + +#: .\contrib\localflavor\id\id_choices.py:15 +#: .\contrib\localflavor\id\id_choices.py:57 +msgid "Jambi" +msgstr "Jambi" + +#: .\contrib\localflavor\id\id_choices.py:16 +msgid "Jawa Barat" +msgstr "Jawa Barat" + +#: .\contrib\localflavor\id\id_choices.py:17 +msgid "Jawa Tengah" +msgstr "Jawa Tengah" + +#: .\contrib\localflavor\id\id_choices.py:18 +msgid "Jawa Timur" +msgstr "Jawa Timur" + +#: .\contrib\localflavor\id\id_choices.py:19 +#: .\contrib\localflavor\id\id_choices.py:88 +msgid "Kalimantan Barat" +msgstr "Kalimantan Barat" + +#: .\contrib\localflavor\id\id_choices.py:20 +#: .\contrib\localflavor\id\id_choices.py:66 +msgid "Kalimantan Selatan" +msgstr "Kalimantan Selatan" + +#: .\contrib\localflavor\id\id_choices.py:21 +#: .\contrib\localflavor\id\id_choices.py:89 +msgid "Kalimantan Tengah" +msgstr "Kalimantan Tengah" + +#: .\contrib\localflavor\id\id_choices.py:22 +#: .\contrib\localflavor\id\id_choices.py:90 +msgid "Kalimantan Timur" +msgstr "Kalimantan Timur" + +#: .\contrib\localflavor\id\id_choices.py:23 +msgid "Kepulauan Bangka-Belitung" +msgstr "Kepulauan Bangka-Belitung" + +#: .\contrib\localflavor\id\id_choices.py:24 +#: .\contrib\localflavor\id\id_choices.py:62 +msgid "Kepulauan Riau" +msgstr "Kepulauan Riau" + +#: .\contrib\localflavor\id\id_choices.py:25 +#: .\contrib\localflavor\id\id_choices.py:55 +msgid "Lampung" +msgstr "Lampung" + +#: .\contrib\localflavor\id\id_choices.py:26 +#: .\contrib\localflavor\id\id_choices.py:70 +msgid "Maluku" +msgstr "Maluku" + +#: .\contrib\localflavor\id\id_choices.py:27 +#: .\contrib\localflavor\id\id_choices.py:71 +msgid "Maluku Utara" +msgstr "Maluku Utara" + +#: .\contrib\localflavor\id\id_choices.py:28 +#: .\contrib\localflavor\id\id_choices.py:59 +msgid "Nanggroe Aceh Darussalam" +msgstr "Nanggroe Aceh Darussalam" + +#: .\contrib\localflavor\id\id_choices.py:29 +msgid "Nusa Tenggara Barat" +msgstr "Nusa Tenggara Barat" + +#: .\contrib\localflavor\id\id_choices.py:30 +msgid "Nusa Tenggara Timur" +msgstr "Nusa Tenggara Timur" + +#: .\contrib\localflavor\id\id_choices.py:31 +msgid "Papua" +msgstr "Papua" + +#: .\contrib\localflavor\id\id_choices.py:32 +msgid "Papua Barat" +msgstr "Papua Barat" + +#: .\contrib\localflavor\id\id_choices.py:33 +#: .\contrib\localflavor\id\id_choices.py:60 +msgid "Riau" +msgstr "Riau" + +#: .\contrib\localflavor\id\id_choices.py:34 +#: .\contrib\localflavor\id\id_choices.py:68 +msgid "Sulawesi Barat" +msgstr "Sulawesi Barat" + +#: .\contrib\localflavor\id\id_choices.py:35 +#: .\contrib\localflavor\id\id_choices.py:69 +msgid "Sulawesi Selatan" +msgstr "Sulawesi Selatan" + +#: .\contrib\localflavor\id\id_choices.py:36 +#: .\contrib\localflavor\id\id_choices.py:76 +msgid "Sulawesi Tengah" +msgstr "Sulawesi Tengah" + +#: .\contrib\localflavor\id\id_choices.py:37 +#: .\contrib\localflavor\id\id_choices.py:79 +msgid "Sulawesi Tenggara" +msgstr "Sulawesi Tenggara" + +#: .\contrib\localflavor\id\id_choices.py:38 +msgid "Sulawesi Utara" +msgstr "Sulawesi Utara" + +#: .\contrib\localflavor\id\id_choices.py:39 +#: .\contrib\localflavor\id\id_choices.py:52 +msgid "Sumatera Barat" +msgstr "Sumatera Barat" + +#: .\contrib\localflavor\id\id_choices.py:40 +#: .\contrib\localflavor\id\id_choices.py:56 +msgid "Sumatera Selatan" +msgstr "Sumatera Selatan" + +#: .\contrib\localflavor\id\id_choices.py:41 +#: .\contrib\localflavor\id\id_choices.py:58 +msgid "Sumatera Utara" +msgstr "Sumatera Utara" + +#: .\contrib\localflavor\id\id_choices.py:46 +msgid "Magelang" +msgstr "Magelang" + +#: .\contrib\localflavor\id\id_choices.py:48 +msgid "Surakarta - Solo" +msgstr "Surakarta - Solo" + +#: .\contrib\localflavor\id\id_choices.py:49 +msgid "Madiun" +msgstr "Madiun" + +#: .\contrib\localflavor\id\id_choices.py:50 +msgid "Kediri" +msgstr "Kediri" + +#: .\contrib\localflavor\id\id_choices.py:53 +msgid "Tapanuli" +msgstr "Tapanuli" + +#: .\contrib\localflavor\id\id_choices.py:61 +msgid "Kepulauan Bangka Belitung" +msgstr "Kepulauan Bangka Belitung" + +#: .\contrib\localflavor\id\id_choices.py:63 +msgid "Corps Consulate" +msgstr "Corps Consulate" + +#: .\contrib\localflavor\id\id_choices.py:64 +msgid "Corps Diplomatic" +msgstr "Corps Diplomatic" + +#: .\contrib\localflavor\id\id_choices.py:65 +msgid "Bandung" +msgstr "Bandung" + +#: .\contrib\localflavor\id\id_choices.py:67 +msgid "Sulawesi Utara Daratan" +msgstr "Sulawesi Utara Daratan" + +#: .\contrib\localflavor\id\id_choices.py:72 +msgid "NTT - Timor" +msgstr "NTT - Timor" + +#: .\contrib\localflavor\id\id_choices.py:74 +msgid "Sulawesi Utara Kepulauan" +msgstr "Sulawesi Utara Kepulauan" + +#: .\contrib\localflavor\id\id_choices.py:77 +msgid "NTB - Lombok" +msgstr "NTB - Lombok" + +#: .\contrib\localflavor\id\id_choices.py:78 +msgid "Papua dan Papua Barat" +msgstr "Papua dan Papua Barat" + +#: .\contrib\localflavor\id\id_choices.py:80 +msgid "Cirebon" +msgstr "Cirebon" + +#: .\contrib\localflavor\id\id_choices.py:81 +msgid "NTB - Sumbawa" +msgstr "NTB - Sumbawa" + +#: .\contrib\localflavor\id\id_choices.py:82 +msgid "NTT - Flores" +msgstr "NTT - Flores" + +#: .\contrib\localflavor\id\id_choices.py:83 +msgid "NTT - Sumba" +msgstr "NTT - Sumba" + +#: .\contrib\localflavor\id\id_choices.py:84 +msgid "Bogor" +msgstr "Bogor" + +#: .\contrib\localflavor\id\id_choices.py:85 +msgid "Pekalongan" +msgstr "Pekalongan" + +#: .\contrib\localflavor\id\id_choices.py:86 +msgid "Semarang" +msgstr "Semarang" + +#: .\contrib\localflavor\id\id_choices.py:87 +msgid "Pati" +msgstr "Pati" + +#: .\contrib\localflavor\id\id_choices.py:91 +msgid "Surabaya" +msgstr "Surabaya" + +#: .\contrib\localflavor\id\id_choices.py:92 +msgid "Madura" +msgstr "Madura" + +#: .\contrib\localflavor\id\id_choices.py:93 +msgid "Malang" +msgstr "Malang" + +#: .\contrib\localflavor\id\id_choices.py:94 +msgid "Jember" +msgstr "Jember" + +#: .\contrib\localflavor\id\id_choices.py:95 +msgid "Banyumas" +msgstr "Banyumas" + +#: .\contrib\localflavor\id\id_choices.py:96 +msgid "Federal Government" +msgstr "Govern Federal" + +#: .\contrib\localflavor\id\id_choices.py:97 +msgid "Bojonegoro" +msgstr "Bojonegoro" + +#: .\contrib\localflavor\id\id_choices.py:98 +msgid "Purwakarta" +msgstr "Purwakarta" + +#: .\contrib\localflavor\id\id_choices.py:99 +msgid "Sidoarjo" +msgstr "Sidoarjo" + +#: .\contrib\localflavor\id\id_choices.py:100 +msgid "Garut" +msgstr "Garut" + +#: .\contrib\localflavor\ie\ie_counties.py:8 +msgid "Antrim" +msgstr "Antrim" + +#: .\contrib\localflavor\ie\ie_counties.py:9 +msgid "Armagh" +msgstr "Armagh" + +#: .\contrib\localflavor\ie\ie_counties.py:10 +msgid "Carlow" +msgstr "Carlow" + +#: .\contrib\localflavor\ie\ie_counties.py:11 +msgid "Cavan" +msgstr "Cavan" + +#: .\contrib\localflavor\ie\ie_counties.py:12 +msgid "Clare" +msgstr "Clare" + +#: .\contrib\localflavor\ie\ie_counties.py:13 +msgid "Cork" +msgstr "Cork" + +#: .\contrib\localflavor\ie\ie_counties.py:14 +msgid "Derry" +msgstr "Derry" + +#: .\contrib\localflavor\ie\ie_counties.py:15 +msgid "Donegal" +msgstr "Donegal" + +#: .\contrib\localflavor\ie\ie_counties.py:16 +msgid "Down" +msgstr "Down" + +#: .\contrib\localflavor\ie\ie_counties.py:17 +msgid "Dublin" +msgstr "Dublin" + +#: .\contrib\localflavor\ie\ie_counties.py:18 +msgid "Fermanagh" +msgstr "Fermanagh" + +#: .\contrib\localflavor\ie\ie_counties.py:19 +msgid "Galway" +msgstr "Galway" + +#: .\contrib\localflavor\ie\ie_counties.py:20 +msgid "Kerry" +msgstr "Kerry" + +#: .\contrib\localflavor\ie\ie_counties.py:21 +msgid "Kildare" +msgstr "Kildare" + +#: .\contrib\localflavor\ie\ie_counties.py:22 +msgid "Kilkenny" +msgstr "Kilkenny" + +#: .\contrib\localflavor\ie\ie_counties.py:23 +msgid "Laois" +msgstr "Laois" + +#: .\contrib\localflavor\ie\ie_counties.py:24 +msgid "Leitrim" +msgstr "Leitrim" + +#: .\contrib\localflavor\ie\ie_counties.py:25 +msgid "Limerick" +msgstr "Limerick" + +#: .\contrib\localflavor\ie\ie_counties.py:26 +msgid "Longford" +msgstr "Longford" + +#: .\contrib\localflavor\ie\ie_counties.py:27 +msgid "Louth" +msgstr "Louth" + +#: .\contrib\localflavor\ie\ie_counties.py:28 +msgid "Mayo" +msgstr "Mayo" + +#: .\contrib\localflavor\ie\ie_counties.py:29 +msgid "Meath" +msgstr "Meath" + +#: .\contrib\localflavor\ie\ie_counties.py:30 +msgid "Monaghan" +msgstr "Monaghan" + +#: .\contrib\localflavor\ie\ie_counties.py:31 +msgid "Offaly" +msgstr "Offaly" + +#: .\contrib\localflavor\ie\ie_counties.py:32 +msgid "Roscommon" +msgstr "Roscommon" + +#: .\contrib\localflavor\ie\ie_counties.py:33 +msgid "Sligo" +msgstr "Sligo" + +#: .\contrib\localflavor\ie\ie_counties.py:34 +msgid "Tipperary" +msgstr "Tipperary" + +#: .\contrib\localflavor\ie\ie_counties.py:35 +msgid "Tyrone" +msgstr "Tyrone" + +#: .\contrib\localflavor\ie\ie_counties.py:36 +msgid "Waterford" +msgstr "Waterford" + +#: .\contrib\localflavor\ie\ie_counties.py:37 +msgid "Westmeath" +msgstr "Westmeath" + +#: .\contrib\localflavor\ie\ie_counties.py:38 +msgid "Wexford" +msgstr "Wexford" + +#: .\contrib\localflavor\ie\ie_counties.py:39 +msgid "Wicklow" +msgstr "Wicklow" + +#: .\contrib\localflavor\in_\forms.py:15 msgid "Enter a zip code in the format XXXXXXX." -msgstr "Introduïu un codi zip en el format XXXXXXX." +msgstr "Introduïu un codi postal en el format XXXXXXX." -#: contrib/localflavor/is_/forms.py:17 +#: .\contrib\localflavor\is_\forms.py:18 msgid "" "Enter a valid Icelandic identification number. The format is XXXXXX-XXXX." msgstr "" -"Introduïu un número de identificació d'Islà ndia. El format és XXXXXX-XXXX." +"Introduïu un número và lid d'identificació d'Islà ndia. El format és XXXXXX-" +"XXXX." -#: contrib/localflavor/is_/forms.py:18 +#: .\contrib\localflavor\is_\forms.py:19 msgid "The Icelandic identification number is not valid." -msgstr "El número de identificació d'Islà ndia no és và lid." +msgstr "El número d'identificació d'Islà ndia no és và lid." -#: contrib/localflavor/it/forms.py:14 +#: .\contrib\localflavor\it\forms.py:15 msgid "Enter a valid zip code." msgstr "Introduïu un codi zip và lid." -#: contrib/localflavor/it/forms.py:43 +#: .\contrib\localflavor\it\forms.py:44 msgid "Enter a valid Social Security number." msgstr "Introduïu un número valid de la Seguretat Social." -#: contrib/localflavor/it/forms.py:68 +#: .\contrib\localflavor\it\forms.py:69 msgid "Enter a valid VAT number." -msgstr "Introduïu un número de IVA (VAT) và lid." +msgstr "Introduïu un número d'IVA (VAT) và lid." -#: contrib/localflavor/jp/forms.py:16 +#: .\contrib\localflavor\jp\forms.py:16 msgid "Enter a postal code in the format XXXXXXX or XXX-XXXX." msgstr "Introduïu un codi postal en el format XXXXXXX o XX-XXXX." -#: contrib/localflavor/jp/jp_prefectures.py:4 +#: .\contrib\localflavor\jp\jp_prefectures.py:4 msgid "Hokkaido" msgstr "Hokkaido" -#: contrib/localflavor/jp/jp_prefectures.py:5 +#: .\contrib\localflavor\jp\jp_prefectures.py:5 msgid "Aomori" msgstr "Aomori" -#: contrib/localflavor/jp/jp_prefectures.py:6 +#: .\contrib\localflavor\jp\jp_prefectures.py:6 msgid "Iwate" msgstr "Iwate" -#: contrib/localflavor/jp/jp_prefectures.py:7 +#: .\contrib\localflavor\jp\jp_prefectures.py:7 msgid "Miyagi" msgstr "Miyagi" -#: contrib/localflavor/jp/jp_prefectures.py:8 +#: .\contrib\localflavor\jp\jp_prefectures.py:8 msgid "Akita" msgstr "Akita" -#: contrib/localflavor/jp/jp_prefectures.py:9 +#: .\contrib\localflavor\jp\jp_prefectures.py:9 msgid "Yamagata" msgstr "Yamagata" -#: contrib/localflavor/jp/jp_prefectures.py:10 +#: .\contrib\localflavor\jp\jp_prefectures.py:10 msgid "Fukushima" msgstr "Fukushima" -#: contrib/localflavor/jp/jp_prefectures.py:11 +#: .\contrib\localflavor\jp\jp_prefectures.py:11 msgid "Ibaraki" msgstr "Ibaraki" -#: contrib/localflavor/jp/jp_prefectures.py:12 +#: .\contrib\localflavor\jp\jp_prefectures.py:12 msgid "Tochigi" msgstr "Tochigi" -#: contrib/localflavor/jp/jp_prefectures.py:13 +#: .\contrib\localflavor\jp\jp_prefectures.py:13 msgid "Gunma" msgstr "Gunma" -#: contrib/localflavor/jp/jp_prefectures.py:14 +#: .\contrib\localflavor\jp\jp_prefectures.py:14 msgid "Saitama" msgstr "Saitama" -#: contrib/localflavor/jp/jp_prefectures.py:15 +#: .\contrib\localflavor\jp\jp_prefectures.py:15 msgid "Chiba" msgstr "Chiba" -#: contrib/localflavor/jp/jp_prefectures.py:16 +#: .\contrib\localflavor\jp\jp_prefectures.py:16 msgid "Tokyo" msgstr "Tokyo" -#: contrib/localflavor/jp/jp_prefectures.py:17 +#: .\contrib\localflavor\jp\jp_prefectures.py:17 msgid "Kanagawa" msgstr "Kanagawa" -#: contrib/localflavor/jp/jp_prefectures.py:18 +#: .\contrib\localflavor\jp\jp_prefectures.py:18 msgid "Yamanashi" msgstr "Yamanashi" -#: contrib/localflavor/jp/jp_prefectures.py:19 +#: .\contrib\localflavor\jp\jp_prefectures.py:19 msgid "Nagano" msgstr "Nagano" -#: contrib/localflavor/jp/jp_prefectures.py:20 +#: .\contrib\localflavor\jp\jp_prefectures.py:20 msgid "Niigata" msgstr "Niigata" -#: contrib/localflavor/jp/jp_prefectures.py:21 +#: .\contrib\localflavor\jp\jp_prefectures.py:21 msgid "Toyama" msgstr "Toyama" -#: contrib/localflavor/jp/jp_prefectures.py:22 +#: .\contrib\localflavor\jp\jp_prefectures.py:22 msgid "Ishikawa" msgstr "Ishikawa" -#: contrib/localflavor/jp/jp_prefectures.py:23 +#: .\contrib\localflavor\jp\jp_prefectures.py:23 msgid "Fukui" msgstr "Fukui" -#: contrib/localflavor/jp/jp_prefectures.py:24 +#: .\contrib\localflavor\jp\jp_prefectures.py:24 msgid "Gifu" msgstr "Gifu" -#: contrib/localflavor/jp/jp_prefectures.py:25 +#: .\contrib\localflavor\jp\jp_prefectures.py:25 msgid "Shizuoka" msgstr "Shizuoka" -#: contrib/localflavor/jp/jp_prefectures.py:26 +#: .\contrib\localflavor\jp\jp_prefectures.py:26 msgid "Aichi" msgstr "Aichi" -#: contrib/localflavor/jp/jp_prefectures.py:27 +#: .\contrib\localflavor\jp\jp_prefectures.py:27 msgid "Mie" msgstr "Mie" -#: contrib/localflavor/jp/jp_prefectures.py:28 +#: .\contrib\localflavor\jp\jp_prefectures.py:28 msgid "Shiga" msgstr "Shiga" -#: contrib/localflavor/jp/jp_prefectures.py:29 +#: .\contrib\localflavor\jp\jp_prefectures.py:29 msgid "Kyoto" msgstr "Kyoto" -#: contrib/localflavor/jp/jp_prefectures.py:30 +#: .\contrib\localflavor\jp\jp_prefectures.py:30 msgid "Osaka" msgstr "Osaka" -#: contrib/localflavor/jp/jp_prefectures.py:31 +#: .\contrib\localflavor\jp\jp_prefectures.py:31 msgid "Hyogo" msgstr "Hyogo" -#: contrib/localflavor/jp/jp_prefectures.py:32 +#: .\contrib\localflavor\jp\jp_prefectures.py:32 msgid "Nara" msgstr "Nara" -#: contrib/localflavor/jp/jp_prefectures.py:33 +#: .\contrib\localflavor\jp\jp_prefectures.py:33 msgid "Wakayama" msgstr "Wakayama" -#: contrib/localflavor/jp/jp_prefectures.py:34 +#: .\contrib\localflavor\jp\jp_prefectures.py:34 msgid "Tottori" msgstr "Tottori" -#: contrib/localflavor/jp/jp_prefectures.py:35 +#: .\contrib\localflavor\jp\jp_prefectures.py:35 msgid "Shimane" msgstr "Shimane" -#: contrib/localflavor/jp/jp_prefectures.py:36 +#: .\contrib\localflavor\jp\jp_prefectures.py:36 msgid "Okayama" msgstr "Okayama" -#: contrib/localflavor/jp/jp_prefectures.py:37 +#: .\contrib\localflavor\jp\jp_prefectures.py:37 msgid "Hiroshima" msgstr "Hiroshima" -#: contrib/localflavor/jp/jp_prefectures.py:38 +#: .\contrib\localflavor\jp\jp_prefectures.py:38 msgid "Yamaguchi" msgstr "Yamaguchi" -#: contrib/localflavor/jp/jp_prefectures.py:39 +#: .\contrib\localflavor\jp\jp_prefectures.py:39 msgid "Tokushima" msgstr "Tokushima" -#: contrib/localflavor/jp/jp_prefectures.py:40 +#: .\contrib\localflavor\jp\jp_prefectures.py:40 msgid "Kagawa" msgstr "Kagawa" -#: contrib/localflavor/jp/jp_prefectures.py:41 +#: .\contrib\localflavor\jp\jp_prefectures.py:41 msgid "Ehime" msgstr "Ehime" -#: contrib/localflavor/jp/jp_prefectures.py:42 +#: .\contrib\localflavor\jp\jp_prefectures.py:42 msgid "Kochi" msgstr "Kochi" -#: contrib/localflavor/jp/jp_prefectures.py:43 +#: .\contrib\localflavor\jp\jp_prefectures.py:43 msgid "Fukuoka" msgstr "Fukuoka" -#: contrib/localflavor/jp/jp_prefectures.py:44 +#: .\contrib\localflavor\jp\jp_prefectures.py:44 msgid "Saga" msgstr "Saga" -#: contrib/localflavor/jp/jp_prefectures.py:45 +#: .\contrib\localflavor\jp\jp_prefectures.py:45 msgid "Nagasaki" msgstr "Nagasaki" -#: contrib/localflavor/jp/jp_prefectures.py:46 +#: .\contrib\localflavor\jp\jp_prefectures.py:46 msgid "Kumamoto" msgstr "Kumamoto" -#: contrib/localflavor/jp/jp_prefectures.py:47 +#: .\contrib\localflavor\jp\jp_prefectures.py:47 msgid "Oita" msgstr "Oita" -#: contrib/localflavor/jp/jp_prefectures.py:48 +#: .\contrib\localflavor\jp\jp_prefectures.py:48 msgid "Miyazaki" msgstr "Miyazaki" -#: contrib/localflavor/jp/jp_prefectures.py:49 +#: .\contrib\localflavor\jp\jp_prefectures.py:49 msgid "Kagoshima" msgstr "Kagoshima" -#: contrib/localflavor/jp/jp_prefectures.py:50 +#: .\contrib\localflavor\jp\jp_prefectures.py:50 msgid "Okinawa" msgstr "Okinawa" -#: contrib/localflavor/mx/mx_states.py:12 +#: .\contrib\localflavor\kw\forms.py:25 +msgid "Enter a valid Kuwaiti Civil ID number" +msgstr "Introduïu un número d'Identitat Kuwaitià và lid" + +#: .\contrib\localflavor\mx\mx_states.py:12 msgid "Aguascalientes" msgstr "Aguascalientes" -#: contrib/localflavor/mx/mx_states.py:13 +#: .\contrib\localflavor\mx\mx_states.py:13 msgid "Baja California" -msgstr "Baixa California" +msgstr "Baixa Califòrnia" -#: contrib/localflavor/mx/mx_states.py:14 +#: .\contrib\localflavor\mx\mx_states.py:14 msgid "Baja California Sur" -msgstr "Baixa California Sud" +msgstr "Baixa Califòrnia Sud" -#: contrib/localflavor/mx/mx_states.py:15 +#: .\contrib\localflavor\mx\mx_states.py:15 msgid "Campeche" msgstr "Campeche" -#: contrib/localflavor/mx/mx_states.py:16 +#: .\contrib\localflavor\mx\mx_states.py:16 msgid "Chihuahua" msgstr "Chihuahua" -#: contrib/localflavor/mx/mx_states.py:17 +#: .\contrib\localflavor\mx\mx_states.py:17 msgid "Chiapas" msgstr "Chiapas" -#: contrib/localflavor/mx/mx_states.py:18 +#: .\contrib\localflavor\mx\mx_states.py:18 msgid "Coahuila" msgstr "Coahuila" -#: contrib/localflavor/mx/mx_states.py:19 +#: .\contrib\localflavor\mx\mx_states.py:19 msgid "Colima" msgstr "Colima" -#: contrib/localflavor/mx/mx_states.py:20 +#: .\contrib\localflavor\mx\mx_states.py:20 msgid "Distrito Federal" msgstr "Districte Federal" -#: contrib/localflavor/mx/mx_states.py:21 +#: .\contrib\localflavor\mx\mx_states.py:21 msgid "Durango" msgstr "Durango" -#: contrib/localflavor/mx/mx_states.py:22 +#: .\contrib\localflavor\mx\mx_states.py:22 msgid "Guerrero" msgstr "Guerrero" -#: contrib/localflavor/mx/mx_states.py:23 +#: .\contrib\localflavor\mx\mx_states.py:23 msgid "Guanajuato" msgstr "Guanajuato" -#: contrib/localflavor/mx/mx_states.py:24 +#: .\contrib\localflavor\mx\mx_states.py:24 msgid "Hidalgo" msgstr "Hidalgo" -#: contrib/localflavor/mx/mx_states.py:25 +#: .\contrib\localflavor\mx\mx_states.py:25 msgid "Jalisco" msgstr "Jalisco" -#: contrib/localflavor/mx/mx_states.py:26 +#: .\contrib\localflavor\mx\mx_states.py:26 msgid "Estado de México" msgstr "Estat de Mèxic" -#: contrib/localflavor/mx/mx_states.py:27 +#: .\contrib\localflavor\mx\mx_states.py:27 msgid "Michoacán" msgstr "Michoacán" -#: contrib/localflavor/mx/mx_states.py:28 +#: .\contrib\localflavor\mx\mx_states.py:28 msgid "Morelos" msgstr "Morelos" -#: contrib/localflavor/mx/mx_states.py:29 +#: .\contrib\localflavor\mx\mx_states.py:29 msgid "Nayarit" msgstr "Nayarit" -#: contrib/localflavor/mx/mx_states.py:30 +#: .\contrib\localflavor\mx\mx_states.py:30 msgid "Nuevo León" msgstr "Nuevo León" -#: contrib/localflavor/mx/mx_states.py:31 +#: .\contrib\localflavor\mx\mx_states.py:31 msgid "Oaxaca" msgstr "Oaxaca" -#: contrib/localflavor/mx/mx_states.py:32 +#: .\contrib\localflavor\mx\mx_states.py:32 msgid "Puebla" msgstr "Puebla" -#: contrib/localflavor/mx/mx_states.py:33 +#: .\contrib\localflavor\mx\mx_states.py:33 msgid "Querétaro" msgstr "Querétaro" -#: contrib/localflavor/mx/mx_states.py:34 +#: .\contrib\localflavor\mx\mx_states.py:34 msgid "Quintana Roo" msgstr "Quintana Roo" -#: contrib/localflavor/mx/mx_states.py:35 +#: .\contrib\localflavor\mx\mx_states.py:35 msgid "Sinaloa" msgstr "Sinaloa" -#: contrib/localflavor/mx/mx_states.py:36 +#: .\contrib\localflavor\mx\mx_states.py:36 msgid "San Luis PotosÃ" msgstr "San Luis PotosÃ" -#: contrib/localflavor/mx/mx_states.py:37 +#: .\contrib\localflavor\mx\mx_states.py:37 msgid "Sonora" msgstr "Sonora" -#: contrib/localflavor/mx/mx_states.py:38 +#: .\contrib\localflavor\mx\mx_states.py:38 msgid "Tabasco" msgstr "Tabasco" -#: contrib/localflavor/mx/mx_states.py:39 +#: .\contrib\localflavor\mx\mx_states.py:39 msgid "Tamaulipas" msgstr "Tamaulipas" -#: contrib/localflavor/mx/mx_states.py:40 +#: .\contrib\localflavor\mx\mx_states.py:40 msgid "Tlaxcala" msgstr "Tlaxcala" -#: contrib/localflavor/mx/mx_states.py:41 +#: .\contrib\localflavor\mx\mx_states.py:41 msgid "Veracruz" msgstr "Veracruz" -#: contrib/localflavor/mx/mx_states.py:42 +#: .\contrib\localflavor\mx\mx_states.py:42 msgid "Yucatán" msgstr "Yucatán" -#: contrib/localflavor/mx/mx_states.py:43 +#: .\contrib\localflavor\mx\mx_states.py:43 msgid "Zacatecas" msgstr "Zacatecas" -#: contrib/localflavor/nl/forms.py:21 +#: .\contrib\localflavor\nl\forms.py:22 msgid "Enter a valid postal code" msgstr "Introduïu un codi postal và lid." -#: contrib/localflavor/nl/forms.py:52 -msgid "Enter a valid phone number" -msgstr "Introduïu un número de telèfon và lid." - -#: contrib/localflavor/nl/forms.py:78 +#: .\contrib\localflavor\nl\forms.py:79 msgid "Enter a valid SoFi number" msgstr "Introduïu un número SoFi và lid." -#: contrib/localflavor/nl/nl_provinces.py:4 +#: .\contrib\localflavor\nl\nl_provinces.py:4 msgid "Drenthe" msgstr "Drenthe" -#: contrib/localflavor/nl/nl_provinces.py:5 +#: .\contrib\localflavor\nl\nl_provinces.py:5 msgid "Flevoland" msgstr "Flevoland" -#: contrib/localflavor/nl/nl_provinces.py:6 +#: .\contrib\localflavor\nl\nl_provinces.py:6 msgid "Friesland" msgstr "Friesland" -#: contrib/localflavor/nl/nl_provinces.py:7 +#: .\contrib\localflavor\nl\nl_provinces.py:7 msgid "Gelderland" msgstr "Gelderland" -#: contrib/localflavor/nl/nl_provinces.py:8 +#: .\contrib\localflavor\nl\nl_provinces.py:8 msgid "Groningen" msgstr "Groningen" -#: contrib/localflavor/nl/nl_provinces.py:9 +#: .\contrib\localflavor\nl\nl_provinces.py:9 msgid "Limburg" msgstr "Limburg" -#: contrib/localflavor/nl/nl_provinces.py:10 +#: .\contrib\localflavor\nl\nl_provinces.py:10 msgid "Noord-Brabant" msgstr "Noord-Brabant" -#: contrib/localflavor/nl/nl_provinces.py:11 +#: .\contrib\localflavor\nl\nl_provinces.py:11 msgid "Noord-Holland" msgstr "Noord-Holland" -#: contrib/localflavor/nl/nl_provinces.py:12 +#: .\contrib\localflavor\nl\nl_provinces.py:12 msgid "Overijssel" msgstr "Overijssel" -#: contrib/localflavor/nl/nl_provinces.py:13 +#: .\contrib\localflavor\nl\nl_provinces.py:13 msgid "Utrecht" msgstr "Utrecht" -#: contrib/localflavor/nl/nl_provinces.py:14 +#: .\contrib\localflavor\nl\nl_provinces.py:14 msgid "Zeeland" msgstr "Zeeland" -#: contrib/localflavor/nl/nl_provinces.py:15 +#: .\contrib\localflavor\nl\nl_provinces.py:15 msgid "Zuid-Holland" msgstr "Zuid-Holland" -#: contrib/localflavor/no/forms.py:33 +#: .\contrib\localflavor\no\forms.py:34 msgid "Enter a valid Norwegian social security number." msgstr "Introduïu un número de la seguretat social Noruega và lid." -#: contrib/localflavor/pe/forms.py:24 +#: .\contrib\localflavor\pe\forms.py:25 msgid "This field requires 8 digits." msgstr "Aquest camp precisa 8 dÃgits." -#: contrib/localflavor/pe/forms.py:52 +#: .\contrib\localflavor\pe\forms.py:53 msgid "This field requires 11 digits." msgstr "Aquest camp precisa 11 dÃgits." -#: contrib/localflavor/pl/forms.py:38 +#: .\contrib\localflavor\pl\forms.py:38 msgid "National Identification Number consists of 11 digits." msgstr "El número d'identidicació nacional està compost de 11 digits." -#: contrib/localflavor/pl/forms.py:39 +#: .\contrib\localflavor\pl\forms.py:39 msgid "Wrong checksum for the National Identification Number." -msgstr "Validació invà lida del número d'identificació nacional." +msgstr "Verificació del número d'identificació nacional invà lida." -#: contrib/localflavor/pl/forms.py:71 +#: .\contrib\localflavor\pl\forms.py:71 msgid "" "Enter a tax number field (NIP) in the format XXX-XXX-XX-XX or XX-XX-XXX-XXX." msgstr "Introduïu un número NIP en el format XXX-XXX-XX-XX o XX-XX-XXX-XXX." -#: contrib/localflavor/pl/forms.py:72 +#: .\contrib\localflavor\pl\forms.py:72 msgid "Wrong checksum for the Tax Number (NIP)." -msgstr "Validació invà lida del número tributari (NIP)." +msgstr "Verificació del número tributari (NIP) invà lida." -#: contrib/localflavor/pl/forms.py:109 +#: .\contrib\localflavor\pl\forms.py:109 msgid "National Business Register Number (REGON) consists of 9 or 14 digits." msgstr "" -"El número nacional de registre de negocis (REGON) consisteix en 9 o 14 " +"El número nacional de registre de negocis (REGON) està compost de 9 o 14 " "dÃgits." -#: contrib/localflavor/pl/forms.py:110 +#: .\contrib\localflavor\pl\forms.py:110 msgid "Wrong checksum for the National Business Register Number (REGON)." -msgstr "Validació invà lida del número nacional de registre de negocis." +msgstr "Verificació del número nacional de registre de negocis invà lida." -#: contrib/localflavor/pl/forms.py:148 +#: .\contrib\localflavor\pl\forms.py:148 msgid "Enter a postal code in the format XX-XXX." msgstr "Introduïu un codi postal en el format XX-XXX." -#: contrib/localflavor/pl/pl_voivodeships.py:8 +#: .\contrib\localflavor\pl\pl_voivodeships.py:8 msgid "Lower Silesia" -msgstr "Lower Silesia" +msgstr "Baixa Silèsia" -#: contrib/localflavor/pl/pl_voivodeships.py:9 +#: .\contrib\localflavor\pl\pl_voivodeships.py:9 msgid "Kuyavia-Pomerania" msgstr "Kuyavia-Pomerania" -#: contrib/localflavor/pl/pl_voivodeships.py:10 +#: .\contrib\localflavor\pl\pl_voivodeships.py:10 msgid "Lublin" msgstr "Lublin" -#: contrib/localflavor/pl/pl_voivodeships.py:11 +#: .\contrib\localflavor\pl\pl_voivodeships.py:11 msgid "Lubusz" msgstr "Lubusz" -#: contrib/localflavor/pl/pl_voivodeships.py:12 +#: .\contrib\localflavor\pl\pl_voivodeships.py:12 msgid "Lodz" msgstr "Lodz" -#: contrib/localflavor/pl/pl_voivodeships.py:13 +#: .\contrib\localflavor\pl\pl_voivodeships.py:13 msgid "Lesser Poland" -msgstr "Lesser Poland" +msgstr "Polònia Menor" -#: contrib/localflavor/pl/pl_voivodeships.py:14 +#: .\contrib\localflavor\pl\pl_voivodeships.py:14 msgid "Masovia" msgstr "Masovia" -#: contrib/localflavor/pl/pl_voivodeships.py:15 +#: .\contrib\localflavor\pl\pl_voivodeships.py:15 msgid "Opole" msgstr "Opole" -#: contrib/localflavor/pl/pl_voivodeships.py:16 +#: .\contrib\localflavor\pl\pl_voivodeships.py:16 msgid "Subcarpatia" msgstr "Subcarpatia" -#: contrib/localflavor/pl/pl_voivodeships.py:17 +#: .\contrib\localflavor\pl\pl_voivodeships.py:17 msgid "Podlasie" msgstr "Podlasie" -#: contrib/localflavor/pl/pl_voivodeships.py:18 +#: .\contrib\localflavor\pl\pl_voivodeships.py:18 msgid "Pomerania" -msgstr "Pomerania" +msgstr "Pomerà nia" -#: contrib/localflavor/pl/pl_voivodeships.py:19 +#: .\contrib\localflavor\pl\pl_voivodeships.py:19 msgid "Silesia" -msgstr "Silesia" +msgstr "Silèsia" -#: contrib/localflavor/pl/pl_voivodeships.py:20 +#: .\contrib\localflavor\pl\pl_voivodeships.py:20 msgid "Swietokrzyskie" msgstr "Swietokrzyskie" -#: contrib/localflavor/pl/pl_voivodeships.py:21 +#: .\contrib\localflavor\pl\pl_voivodeships.py:21 msgid "Warmia-Masuria" msgstr "Warmia-Masuria" -#: contrib/localflavor/pl/pl_voivodeships.py:22 +#: .\contrib\localflavor\pl\pl_voivodeships.py:22 msgid "Greater Poland" -msgstr "Greater Poland" +msgstr "Polònia Major" -#: contrib/localflavor/pl/pl_voivodeships.py:23 +#: .\contrib\localflavor\pl\pl_voivodeships.py:23 msgid "West Pomerania" -msgstr "West Pomerania" +msgstr "Pomerà nia Oest" -#: contrib/localflavor/ro/forms.py:19 +#: .\contrib\localflavor\pt\forms.py:17 +msgid "Enter a zip code in the format XXXX-XXX." +msgstr "Introduïu un codi postal en el format XXXX-XXX." + +#: .\contrib\localflavor\pt\forms.py:37 +msgid "Phone numbers must have 9 digits, or start by + or 00." +msgstr "Els números de telèfon han de tenir 9 dÃgits, o començar per " +"+ o 00." + +#: .\contrib\localflavor\ro\forms.py:19 msgid "Enter a valid CIF." msgstr "Introduïu un CIF và lid." -#: contrib/localflavor/ro/forms.py:56 +#: .\contrib\localflavor\ro\forms.py:56 msgid "Enter a valid CNP." msgstr "Introduïu un CNP và lid." -#: contrib/localflavor/ro/forms.py:141 +#: .\contrib\localflavor\ro\forms.py:141 msgid "Enter a valid IBAN in ROXX-XXXX-XXXX-XXXX-XXXX-XXXX format" msgstr "Introduïu un IBAN và lid en el format ROXX-XXXX-XXXX-XXXX-XXXX-XXXX." -#: contrib/localflavor/ro/forms.py:171 +#: .\contrib\localflavor\ro\forms.py:171 msgid "Phone numbers must be in XXXX-XXXXXX format." -msgstr "El número de telèfon ha de estar en el format XXXX-XXXXXX." +msgstr "El número de telèfon ha d'estar en el format XXXX-XXXXXX." -#: contrib/localflavor/ro/forms.py:194 +#: .\contrib\localflavor\ro\forms.py:194 msgid "Enter a valid postal code in the format XXXXXX" msgstr "Introduïu un codi postal và lid en el format XXXXXX." -#: contrib/localflavor/sk/sk_districts.py:8 +#: .\contrib\localflavor\se\forms.py:50 +msgid "Enter a valid Swedish organisation number." +msgstr "Introduïu un número d'organització Sueca và lid." + +#: .\contrib\localflavor\se\forms.py:107 +msgid "Enter a valid Swedish personal identity number." +msgstr "Introduïu un número d'identitat personal suec và lid." + +#: .\contrib\localflavor\se\forms.py:108 +msgid "Co-ordination numbers are not allowed." +msgstr "No es permeten números de coordinació." + +#: .\contrib\localflavor\se\forms.py:150 +msgid "Enter a Swedish postal code in the format XXXXX." +msgstr "Introduïu un codi postal suec en el format XXXXX." + +#: .\contrib\localflavor\se\se_counties.py:15 +msgid "Stockholm" +msgstr "Estocolm" + +#: .\contrib\localflavor\se\se_counties.py:16 +msgid "Västerbotten" +msgstr "Västerbotten" + +#: .\contrib\localflavor\se\se_counties.py:17 +msgid "Norrbotten" +msgstr "Norrbotten" + +#: .\contrib\localflavor\se\se_counties.py:18 +msgid "Uppsala" +msgstr "Uppsala" + +#: .\contrib\localflavor\se\se_counties.py:19 +msgid "Södermanland" +msgstr "Södermanland" + +#: .\contrib\localflavor\se\se_counties.py:20 +msgid "Östergötland" +msgstr "Östergötland" + +#: .\contrib\localflavor\se\se_counties.py:21 +msgid "Jönköping" +msgstr "Jönköping" + +#: .\contrib\localflavor\se\se_counties.py:22 +msgid "Kronoberg" +msgstr "Kronoberg" + +#: .\contrib\localflavor\se\se_counties.py:23 +msgid "Kalmar" +msgstr "Kalmar" + +#: .\contrib\localflavor\se\se_counties.py:24 +msgid "Gotland" +msgstr "Gotland" + +#: .\contrib\localflavor\se\se_counties.py:25 +msgid "Blekinge" +msgstr "Blekinge" + +#: .\contrib\localflavor\se\se_counties.py:26 +msgid "SkÃ¥ne" +msgstr "SkÃ¥ne" + +#: .\contrib\localflavor\se\se_counties.py:27 +msgid "Halland" +msgstr "Halland" + +#: .\contrib\localflavor\se\se_counties.py:28 +msgid "Västra Götaland" +msgstr "Västra Götaland" + +#: .\contrib\localflavor\se\se_counties.py:29 +msgid "Värmland" +msgstr "Värmland" + +#: .\contrib\localflavor\se\se_counties.py:30 +msgid "Örebro" +msgstr "Örebro" + +#: .\contrib\localflavor\se\se_counties.py:31 +msgid "Västmanland" +msgstr "Västmanland" + +#: .\contrib\localflavor\se\se_counties.py:32 +msgid "Dalarna" +msgstr "Dalarna" + +#: .\contrib\localflavor\se\se_counties.py:33 +msgid "Gävleborg" +msgstr "Gävleborg" + +#: .\contrib\localflavor\se\se_counties.py:34 +msgid "Västernorrland" +msgstr "Västernorrland" + +#: .\contrib\localflavor\se\se_counties.py:35 +msgid "Jämtland" +msgstr "Jämtland" + +#: .\contrib\localflavor\sk\sk_districts.py:8 msgid "Banska Bystrica" msgstr "Banska Bystrica" -#: contrib/localflavor/sk/sk_districts.py:9 +#: .\contrib\localflavor\sk\sk_districts.py:9 msgid "Banska Stiavnica" msgstr "Banska Stiavnica" -#: contrib/localflavor/sk/sk_districts.py:10 +#: .\contrib\localflavor\sk\sk_districts.py:10 msgid "Bardejov" msgstr "Bardejov" -#: contrib/localflavor/sk/sk_districts.py:11 +#: .\contrib\localflavor\sk\sk_districts.py:11 msgid "Banovce nad Bebravou" msgstr "Banovce nad Bebravou" -#: contrib/localflavor/sk/sk_districts.py:12 +#: .\contrib\localflavor\sk\sk_districts.py:12 msgid "Brezno" msgstr "Brezno" -#: contrib/localflavor/sk/sk_districts.py:13 +#: .\contrib\localflavor\sk\sk_districts.py:13 msgid "Bratislava I" msgstr "Bratislava I" -#: contrib/localflavor/sk/sk_districts.py:14 +#: .\contrib\localflavor\sk\sk_districts.py:14 msgid "Bratislava II" msgstr "Bratislava II" -#: contrib/localflavor/sk/sk_districts.py:15 +#: .\contrib\localflavor\sk\sk_districts.py:15 msgid "Bratislava III" msgstr "Bratislava III" -#: contrib/localflavor/sk/sk_districts.py:16 +#: .\contrib\localflavor\sk\sk_districts.py:16 msgid "Bratislava IV" msgstr "Bratislava IV" -#: contrib/localflavor/sk/sk_districts.py:17 +#: .\contrib\localflavor\sk\sk_districts.py:17 msgid "Bratislava V" msgstr "Bratislava V" -#: contrib/localflavor/sk/sk_districts.py:18 +#: .\contrib\localflavor\sk\sk_districts.py:18 msgid "Bytca" msgstr "Bytca" -#: contrib/localflavor/sk/sk_districts.py:19 +#: .\contrib\localflavor\sk\sk_districts.py:19 msgid "Cadca" msgstr "Cadca" -#: contrib/localflavor/sk/sk_districts.py:20 +#: .\contrib\localflavor\sk\sk_districts.py:20 msgid "Detva" msgstr "Detva" -#: contrib/localflavor/sk/sk_districts.py:21 +#: .\contrib\localflavor\sk\sk_districts.py:21 msgid "Dolny Kubin" msgstr "Dolny Kubin" -#: contrib/localflavor/sk/sk_districts.py:22 +#: .\contrib\localflavor\sk\sk_districts.py:22 msgid "Dunajska Streda" msgstr "Dunajska Streda" -#: contrib/localflavor/sk/sk_districts.py:23 +#: .\contrib\localflavor\sk\sk_districts.py:23 msgid "Galanta" msgstr "Galanta" -#: contrib/localflavor/sk/sk_districts.py:24 +#: .\contrib\localflavor\sk\sk_districts.py:24 msgid "Gelnica" msgstr "Gelnica" -#: contrib/localflavor/sk/sk_districts.py:25 +#: .\contrib\localflavor\sk\sk_districts.py:25 msgid "Hlohovec" msgstr "Hlohovec" -#: contrib/localflavor/sk/sk_districts.py:26 +#: .\contrib\localflavor\sk\sk_districts.py:26 msgid "Humenne" msgstr "Humenne" -#: contrib/localflavor/sk/sk_districts.py:27 +#: .\contrib\localflavor\sk\sk_districts.py:27 msgid "Ilava" msgstr "Ilava" -#: contrib/localflavor/sk/sk_districts.py:28 +#: .\contrib\localflavor\sk\sk_districts.py:28 msgid "Kezmarok" msgstr "Kezmarok" -#: contrib/localflavor/sk/sk_districts.py:29 +#: .\contrib\localflavor\sk\sk_districts.py:29 msgid "Komarno" msgstr "Komarno" -#: contrib/localflavor/sk/sk_districts.py:30 +#: .\contrib\localflavor\sk\sk_districts.py:30 msgid "Kosice I" msgstr "Kosice I" -#: contrib/localflavor/sk/sk_districts.py:31 +#: .\contrib\localflavor\sk\sk_districts.py:31 msgid "Kosice II" msgstr "Kosice II" -#: contrib/localflavor/sk/sk_districts.py:32 +#: .\contrib\localflavor\sk\sk_districts.py:32 msgid "Kosice III" msgstr "Kosice III" -#: contrib/localflavor/sk/sk_districts.py:33 +#: .\contrib\localflavor\sk\sk_districts.py:33 msgid "Kosice IV" msgstr "Kosice IV" -#: contrib/localflavor/sk/sk_districts.py:34 +#: .\contrib\localflavor\sk\sk_districts.py:34 msgid "Kosice - okolie" msgstr "Kosice - okolie" -#: contrib/localflavor/sk/sk_districts.py:35 +#: .\contrib\localflavor\sk\sk_districts.py:35 msgid "Krupina" msgstr "Krupina" -#: contrib/localflavor/sk/sk_districts.py:36 +#: .\contrib\localflavor\sk\sk_districts.py:36 msgid "Kysucke Nove Mesto" msgstr "Kysucke Nove Mesto" -#: contrib/localflavor/sk/sk_districts.py:37 +#: .\contrib\localflavor\sk\sk_districts.py:37 msgid "Levice" msgstr "Levice" -#: contrib/localflavor/sk/sk_districts.py:38 +#: .\contrib\localflavor\sk\sk_districts.py:38 msgid "Levoca" msgstr "Levoca" -#: contrib/localflavor/sk/sk_districts.py:39 +#: .\contrib\localflavor\sk\sk_districts.py:39 msgid "Liptovsky Mikulas" msgstr "Liptovsky Mikulas" -#: contrib/localflavor/sk/sk_districts.py:40 +#: .\contrib\localflavor\sk\sk_districts.py:40 msgid "Lucenec" msgstr "Lucenec" -#: contrib/localflavor/sk/sk_districts.py:41 +#: .\contrib\localflavor\sk\sk_districts.py:41 msgid "Malacky" msgstr "Malacky" -#: contrib/localflavor/sk/sk_districts.py:42 +#: .\contrib\localflavor\sk\sk_districts.py:42 msgid "Martin" msgstr "Martin" -#: contrib/localflavor/sk/sk_districts.py:43 +#: .\contrib\localflavor\sk\sk_districts.py:43 msgid "Medzilaborce" msgstr "Medzilaborce" -#: contrib/localflavor/sk/sk_districts.py:44 +#: .\contrib\localflavor\sk\sk_districts.py:44 msgid "Michalovce" msgstr "Michalovce" -#: contrib/localflavor/sk/sk_districts.py:45 +#: .\contrib\localflavor\sk\sk_districts.py:45 msgid "Myjava" msgstr "Myjava" -#: contrib/localflavor/sk/sk_districts.py:46 +#: .\contrib\localflavor\sk\sk_districts.py:46 msgid "Namestovo" msgstr "Namestovo" -#: contrib/localflavor/sk/sk_districts.py:47 +#: .\contrib\localflavor\sk\sk_districts.py:47 msgid "Nitra" msgstr "Nitra" -#: contrib/localflavor/sk/sk_districts.py:48 +#: .\contrib\localflavor\sk\sk_districts.py:48 msgid "Nove Mesto nad Vahom" msgstr "Nove Mesto nad Vahom" -#: contrib/localflavor/sk/sk_districts.py:49 +#: .\contrib\localflavor\sk\sk_districts.py:49 msgid "Nove Zamky" msgstr "Nove Zamky" -#: contrib/localflavor/sk/sk_districts.py:50 +#: .\contrib\localflavor\sk\sk_districts.py:50 msgid "Partizanske" msgstr "Partizanske" -#: contrib/localflavor/sk/sk_districts.py:51 +#: .\contrib\localflavor\sk\sk_districts.py:51 msgid "Pezinok" msgstr "Pezinok" -#: contrib/localflavor/sk/sk_districts.py:52 +#: .\contrib\localflavor\sk\sk_districts.py:52 msgid "Piestany" msgstr "Piestany" -#: contrib/localflavor/sk/sk_districts.py:53 +#: .\contrib\localflavor\sk\sk_districts.py:53 msgid "Poltar" msgstr "Poltar" -#: contrib/localflavor/sk/sk_districts.py:54 +#: .\contrib\localflavor\sk\sk_districts.py:54 msgid "Poprad" msgstr "Poprad" -#: contrib/localflavor/sk/sk_districts.py:55 +#: .\contrib\localflavor\sk\sk_districts.py:55 msgid "Povazska Bystrica" msgstr "Povazska Bystrica" -#: contrib/localflavor/sk/sk_districts.py:56 +#: .\contrib\localflavor\sk\sk_districts.py:56 msgid "Presov" msgstr "Presov" -#: contrib/localflavor/sk/sk_districts.py:57 +#: .\contrib\localflavor\sk\sk_districts.py:57 msgid "Prievidza" msgstr "Prievidza" -#: contrib/localflavor/sk/sk_districts.py:58 +#: .\contrib\localflavor\sk\sk_districts.py:58 msgid "Puchov" msgstr "Puchov" -#: contrib/localflavor/sk/sk_districts.py:59 +#: .\contrib\localflavor\sk\sk_districts.py:59 msgid "Revuca" msgstr "Revuca" -#: contrib/localflavor/sk/sk_districts.py:60 +#: .\contrib\localflavor\sk\sk_districts.py:60 msgid "Rimavska Sobota" msgstr "Rimavska Sobota" -#: contrib/localflavor/sk/sk_districts.py:61 +#: .\contrib\localflavor\sk\sk_districts.py:61 msgid "Roznava" msgstr "Roznava" -#: contrib/localflavor/sk/sk_districts.py:62 +#: .\contrib\localflavor\sk\sk_districts.py:62 msgid "Ruzomberok" msgstr "Ruzomberok" -#: contrib/localflavor/sk/sk_districts.py:63 +#: .\contrib\localflavor\sk\sk_districts.py:63 msgid "Sabinov" msgstr "Sabinov" -#: contrib/localflavor/sk/sk_districts.py:64 +#: .\contrib\localflavor\sk\sk_districts.py:64 msgid "Senec" msgstr "Senec" -#: contrib/localflavor/sk/sk_districts.py:65 +#: .\contrib\localflavor\sk\sk_districts.py:65 msgid "Senica" msgstr "Senica" -#: contrib/localflavor/sk/sk_districts.py:66 +#: .\contrib\localflavor\sk\sk_districts.py:66 msgid "Skalica" msgstr "Skalica" -#: contrib/localflavor/sk/sk_districts.py:67 +#: .\contrib\localflavor\sk\sk_districts.py:67 msgid "Snina" msgstr "Snina" -#: contrib/localflavor/sk/sk_districts.py:68 +#: .\contrib\localflavor\sk\sk_districts.py:68 msgid "Sobrance" msgstr "Sobrance" -#: contrib/localflavor/sk/sk_districts.py:69 +#: .\contrib\localflavor\sk\sk_districts.py:69 msgid "Spisska Nova Ves" msgstr "Spisska Nova Ves" -#: contrib/localflavor/sk/sk_districts.py:70 +#: .\contrib\localflavor\sk\sk_districts.py:70 msgid "Stara Lubovna" msgstr "Stara Lubovna" -#: contrib/localflavor/sk/sk_districts.py:71 +#: .\contrib\localflavor\sk\sk_districts.py:71 msgid "Stropkov" msgstr "Stropkov" -#: contrib/localflavor/sk/sk_districts.py:72 +#: .\contrib\localflavor\sk\sk_districts.py:72 msgid "Svidnik" msgstr "Svidnik" -#: contrib/localflavor/sk/sk_districts.py:73 +#: .\contrib\localflavor\sk\sk_districts.py:73 msgid "Sala" msgstr "Sala" -#: contrib/localflavor/sk/sk_districts.py:74 +#: .\contrib\localflavor\sk\sk_districts.py:74 msgid "Topolcany" msgstr "Topolcany" -#: contrib/localflavor/sk/sk_districts.py:75 +#: .\contrib\localflavor\sk\sk_districts.py:75 msgid "Trebisov" msgstr "Trebisov" -#: contrib/localflavor/sk/sk_districts.py:76 +#: .\contrib\localflavor\sk\sk_districts.py:76 msgid "Trencin" msgstr "Trencin" -#: contrib/localflavor/sk/sk_districts.py:77 +#: .\contrib\localflavor\sk\sk_districts.py:77 msgid "Trnava" msgstr "Trnava" -#: contrib/localflavor/sk/sk_districts.py:78 +#: .\contrib\localflavor\sk\sk_districts.py:78 msgid "Turcianske Teplice" msgstr "Turcianske Teplice" -#: contrib/localflavor/sk/sk_districts.py:79 +#: .\contrib\localflavor\sk\sk_districts.py:79 msgid "Tvrdosin" msgstr "Tvrdosin" -#: contrib/localflavor/sk/sk_districts.py:80 +#: .\contrib\localflavor\sk\sk_districts.py:80 msgid "Velky Krtis" msgstr "Velky Krtis" -#: contrib/localflavor/sk/sk_districts.py:81 +#: .\contrib\localflavor\sk\sk_districts.py:81 msgid "Vranov nad Toplou" msgstr "Vranov nad Toplou" -#: contrib/localflavor/sk/sk_districts.py:82 +#: .\contrib\localflavor\sk\sk_districts.py:82 msgid "Zlate Moravce" msgstr "Zlate Moravce" -#: contrib/localflavor/sk/sk_districts.py:83 +#: .\contrib\localflavor\sk\sk_districts.py:83 msgid "Zvolen" msgstr "Zvolen" -#: contrib/localflavor/sk/sk_districts.py:84 +#: .\contrib\localflavor\sk\sk_districts.py:84 msgid "Zarnovica" msgstr "Zarnovica" -#: contrib/localflavor/sk/sk_districts.py:85 +#: .\contrib\localflavor\sk\sk_districts.py:85 msgid "Ziar nad Hronom" msgstr "Ziar nad Hronom" -#: contrib/localflavor/sk/sk_districts.py:86 +#: .\contrib\localflavor\sk\sk_districts.py:86 msgid "Zilina" msgstr "Zilina" -#: contrib/localflavor/sk/sk_regions.py:8 +#: .\contrib\localflavor\sk\sk_regions.py:8 msgid "Banska Bystrica region" msgstr "Regió de Banska Bystrica" -#: contrib/localflavor/sk/sk_regions.py:9 +#: .\contrib\localflavor\sk\sk_regions.py:9 msgid "Bratislava region" msgstr "Regió de Bratislava" -#: contrib/localflavor/sk/sk_regions.py:10 +#: .\contrib\localflavor\sk\sk_regions.py:10 msgid "Kosice region" msgstr "Regió de Kosice" -#: contrib/localflavor/sk/sk_regions.py:11 +#: .\contrib\localflavor\sk\sk_regions.py:11 msgid "Nitra region" msgstr "Regió de Nitra" -#: contrib/localflavor/sk/sk_regions.py:12 +#: .\contrib\localflavor\sk\sk_regions.py:12 msgid "Presov region" msgstr "Regió de Presov" -#: contrib/localflavor/sk/sk_regions.py:13 +#: .\contrib\localflavor\sk\sk_regions.py:13 msgid "Trencin region" msgstr "Regió de Trencin" -#: contrib/localflavor/sk/sk_regions.py:14 +#: .\contrib\localflavor\sk\sk_regions.py:14 msgid "Trnava region" msgstr "Regió de Trnava" -#: contrib/localflavor/sk/sk_regions.py:15 +#: .\contrib\localflavor\sk\sk_regions.py:15 msgid "Zilina region" msgstr "Regió de Zilina" -#: contrib/localflavor/uk/forms.py:21 +#: .\contrib\localflavor\uk\forms.py:21 msgid "Enter a valid postcode." -msgstr "Introdueix un codi postal và lid." +msgstr "Introduïu un codi postal và lid." -#: contrib/localflavor/uk/uk_regions.py:11 +#: .\contrib\localflavor\uk\uk_regions.py:11 msgid "Bedfordshire" msgstr "Bedfordshire" -#: contrib/localflavor/uk/uk_regions.py:12 +#: .\contrib\localflavor\uk\uk_regions.py:12 msgid "Buckinghamshire" msgstr "Buckinghamshire" -#: contrib/localflavor/uk/uk_regions.py:14 +#: .\contrib\localflavor\uk\uk_regions.py:14 msgid "Cheshire" msgstr "Cheshire" -#: contrib/localflavor/uk/uk_regions.py:15 +#: .\contrib\localflavor\uk\uk_regions.py:15 msgid "Cornwall and Isles of Scilly" msgstr "Cornwall and Isles of Scilly" -#: contrib/localflavor/uk/uk_regions.py:16 +#: .\contrib\localflavor\uk\uk_regions.py:16 msgid "Cumbria" msgstr "Cumbria" -#: contrib/localflavor/uk/uk_regions.py:17 +#: .\contrib\localflavor\uk\uk_regions.py:17 msgid "Derbyshire" msgstr "Derbyshire" -#: contrib/localflavor/uk/uk_regions.py:18 +#: .\contrib\localflavor\uk\uk_regions.py:18 msgid "Devon" msgstr "Devon" -#: contrib/localflavor/uk/uk_regions.py:19 +#: .\contrib\localflavor\uk\uk_regions.py:19 msgid "Dorset" msgstr "Dorset" -#: contrib/localflavor/uk/uk_regions.py:20 +#: .\contrib\localflavor\uk\uk_regions.py:20 msgid "Durham" msgstr "Durham" -#: contrib/localflavor/uk/uk_regions.py:21 +#: .\contrib\localflavor\uk\uk_regions.py:21 msgid "East Sussex" msgstr "East Sussex" -#: contrib/localflavor/uk/uk_regions.py:22 +#: .\contrib\localflavor\uk\uk_regions.py:22 msgid "Essex" msgstr "Essex" -#: contrib/localflavor/uk/uk_regions.py:23 +#: .\contrib\localflavor\uk\uk_regions.py:23 msgid "Gloucestershire" msgstr "Gloucestershire" -#: contrib/localflavor/uk/uk_regions.py:24 +#: .\contrib\localflavor\uk\uk_regions.py:24 msgid "Greater London" msgstr "Greater London" -#: contrib/localflavor/uk/uk_regions.py:25 +#: .\contrib\localflavor\uk\uk_regions.py:25 msgid "Greater Manchester" msgstr "Greater Manchester" -#: contrib/localflavor/uk/uk_regions.py:26 +#: .\contrib\localflavor\uk\uk_regions.py:26 msgid "Hampshire" msgstr "Hampshire" -#: contrib/localflavor/uk/uk_regions.py:27 +#: .\contrib\localflavor\uk\uk_regions.py:27 msgid "Hertfordshire" msgstr "Hertfordshire" -#: contrib/localflavor/uk/uk_regions.py:28 +#: .\contrib\localflavor\uk\uk_regions.py:28 msgid "Kent" msgstr "Kent" -#: contrib/localflavor/uk/uk_regions.py:29 +#: .\contrib\localflavor\uk\uk_regions.py:29 msgid "Lancashire" msgstr "Lancashire" -#: contrib/localflavor/uk/uk_regions.py:30 +#: .\contrib\localflavor\uk\uk_regions.py:30 msgid "Leicestershire" msgstr "Leicestershire" -#: contrib/localflavor/uk/uk_regions.py:31 +#: .\contrib\localflavor\uk\uk_regions.py:31 msgid "Lincolnshire" msgstr "Lincolnshire" -#: contrib/localflavor/uk/uk_regions.py:32 +#: .\contrib\localflavor\uk\uk_regions.py:32 msgid "Merseyside" msgstr "Merseyside" -#: contrib/localflavor/uk/uk_regions.py:33 +#: .\contrib\localflavor\uk\uk_regions.py:33 msgid "Norfolk" msgstr "Norfolk" -#: contrib/localflavor/uk/uk_regions.py:34 +#: .\contrib\localflavor\uk\uk_regions.py:34 msgid "North Yorkshire" msgstr "North Yorkshire" -#: contrib/localflavor/uk/uk_regions.py:35 +#: .\contrib\localflavor\uk\uk_regions.py:35 msgid "Northamptonshire" msgstr "Northamptonshire" -#: contrib/localflavor/uk/uk_regions.py:36 +#: .\contrib\localflavor\uk\uk_regions.py:36 msgid "Northumberland" msgstr "Northumberland" -#: contrib/localflavor/uk/uk_regions.py:37 +#: .\contrib\localflavor\uk\uk_regions.py:37 msgid "Nottinghamshire" msgstr "Nottinghamshire" -#: contrib/localflavor/uk/uk_regions.py:38 +#: .\contrib\localflavor\uk\uk_regions.py:38 msgid "Oxfordshire" msgstr "Oxfordshire" -#: contrib/localflavor/uk/uk_regions.py:39 +#: .\contrib\localflavor\uk\uk_regions.py:39 msgid "Shropshire" msgstr "Shropshire" -#: contrib/localflavor/uk/uk_regions.py:40 +#: .\contrib\localflavor\uk\uk_regions.py:40 msgid "Somerset" msgstr "Somerset" -#: contrib/localflavor/uk/uk_regions.py:41 +#: .\contrib\localflavor\uk\uk_regions.py:41 msgid "South Yorkshire" msgstr "South Yorkshire" -#: contrib/localflavor/uk/uk_regions.py:42 +#: .\contrib\localflavor\uk\uk_regions.py:42 msgid "Staffordshire" msgstr "Staffordshire" -#: contrib/localflavor/uk/uk_regions.py:43 +#: .\contrib\localflavor\uk\uk_regions.py:43 msgid "Suffolk" msgstr "Suffolk" -#: contrib/localflavor/uk/uk_regions.py:44 +#: .\contrib\localflavor\uk\uk_regions.py:44 msgid "Surrey" msgstr "Surrey" -#: contrib/localflavor/uk/uk_regions.py:45 +#: .\contrib\localflavor\uk\uk_regions.py:45 msgid "Tyne and Wear" msgstr "Tyne and Wear" -#: contrib/localflavor/uk/uk_regions.py:46 +#: .\contrib\localflavor\uk\uk_regions.py:46 msgid "Warwickshire" msgstr "Warwickshire" -#: contrib/localflavor/uk/uk_regions.py:47 +#: .\contrib\localflavor\uk\uk_regions.py:47 msgid "West Midlands" msgstr "West Midlands" -#: contrib/localflavor/uk/uk_regions.py:48 +#: .\contrib\localflavor\uk\uk_regions.py:48 msgid "West Sussex" msgstr "West Sussex" -#: contrib/localflavor/uk/uk_regions.py:49 +#: .\contrib\localflavor\uk\uk_regions.py:49 msgid "West Yorkshire" msgstr "West Yorkshire" -#: contrib/localflavor/uk/uk_regions.py:50 +#: .\contrib\localflavor\uk\uk_regions.py:50 msgid "Wiltshire" msgstr "Wiltshire" -#: contrib/localflavor/uk/uk_regions.py:51 +#: .\contrib\localflavor\uk\uk_regions.py:51 msgid "Worcestershire" msgstr "Worcestershire" -#: contrib/localflavor/uk/uk_regions.py:55 +#: .\contrib\localflavor\uk\uk_regions.py:55 msgid "County Antrim" msgstr "County Antrim" -#: contrib/localflavor/uk/uk_regions.py:56 +#: .\contrib\localflavor\uk\uk_regions.py:56 msgid "County Armagh" msgstr "County Armagh" -#: contrib/localflavor/uk/uk_regions.py:57 +#: .\contrib\localflavor\uk\uk_regions.py:57 msgid "County Down" msgstr "County Down" -#: contrib/localflavor/uk/uk_regions.py:58 +#: .\contrib\localflavor\uk\uk_regions.py:58 msgid "County Fermanagh" -msgstr "Comptat de Fermanagh" +msgstr "County Fermanagh" -#: contrib/localflavor/uk/uk_regions.py:59 +#: .\contrib\localflavor\uk\uk_regions.py:59 msgid "County Londonderry" msgstr "County Londonderry" -#: contrib/localflavor/uk/uk_regions.py:60 +#: .\contrib\localflavor\uk\uk_regions.py:60 msgid "County Tyrone" msgstr "County Tyrone" -#: contrib/localflavor/uk/uk_regions.py:64 +#: .\contrib\localflavor\uk\uk_regions.py:64 msgid "Clwyd" msgstr "Clwyd" -#: contrib/localflavor/uk/uk_regions.py:65 +#: .\contrib\localflavor\uk\uk_regions.py:65 msgid "Dyfed" msgstr "Dyfed" -#: contrib/localflavor/uk/uk_regions.py:66 +#: .\contrib\localflavor\uk\uk_regions.py:66 msgid "Gwent" msgstr "Gwent" -#: contrib/localflavor/uk/uk_regions.py:67 +#: .\contrib\localflavor\uk\uk_regions.py:67 msgid "Gwynedd" msgstr "Gwynedd" -#: contrib/localflavor/uk/uk_regions.py:68 +#: .\contrib\localflavor\uk\uk_regions.py:68 msgid "Mid Glamorgan" msgstr "Mid Glamorgan" -#: contrib/localflavor/uk/uk_regions.py:69 +#: .\contrib\localflavor\uk\uk_regions.py:69 msgid "Powys" msgstr "Powys" -#: contrib/localflavor/uk/uk_regions.py:70 +#: .\contrib\localflavor\uk\uk_regions.py:70 msgid "South Glamorgan" msgstr "Glamorgan Sud" -#: contrib/localflavor/uk/uk_regions.py:71 +#: .\contrib\localflavor\uk\uk_regions.py:71 msgid "West Glamorgan" msgstr "Glamorgan Oest" -#: contrib/localflavor/uk/uk_regions.py:75 +#: .\contrib\localflavor\uk\uk_regions.py:75 msgid "Borders" msgstr "Borders" -#: contrib/localflavor/uk/uk_regions.py:76 +#: .\contrib\localflavor\uk\uk_regions.py:76 msgid "Central Scotland" msgstr "Escòcia central" -#: contrib/localflavor/uk/uk_regions.py:77 +#: .\contrib\localflavor\uk\uk_regions.py:77 msgid "Dumfries and Galloway" msgstr "Dumfries and Galloway" -#: contrib/localflavor/uk/uk_regions.py:78 +#: .\contrib\localflavor\uk\uk_regions.py:78 msgid "Fife" msgstr "Fife" -#: contrib/localflavor/uk/uk_regions.py:79 +#: .\contrib\localflavor\uk\uk_regions.py:79 msgid "Grampian" msgstr "Grampian" -#: contrib/localflavor/uk/uk_regions.py:80 +#: .\contrib\localflavor\uk\uk_regions.py:80 msgid "Highland" msgstr "Highland" -#: contrib/localflavor/uk/uk_regions.py:81 +#: .\contrib\localflavor\uk\uk_regions.py:81 msgid "Lothian" msgstr "Lothian" -#: contrib/localflavor/uk/uk_regions.py:82 +#: .\contrib\localflavor\uk\uk_regions.py:82 msgid "Orkney Islands" -msgstr "Orkney Islands" +msgstr "Illes Orkney" -#: contrib/localflavor/uk/uk_regions.py:83 +#: .\contrib\localflavor\uk\uk_regions.py:83 msgid "Shetland Islands" -msgstr "Shetland Islands" +msgstr "Illes Shetland" -#: contrib/localflavor/uk/uk_regions.py:84 +#: .\contrib\localflavor\uk\uk_regions.py:84 msgid "Strathclyde" msgstr "Strathclyde" -#: contrib/localflavor/uk/uk_regions.py:85 +#: .\contrib\localflavor\uk\uk_regions.py:85 msgid "Tayside" msgstr "Tayside" -#: contrib/localflavor/uk/uk_regions.py:86 +#: .\contrib\localflavor\uk\uk_regions.py:86 msgid "Western Isles" -msgstr "Western Isles" +msgstr "Illes Occidentals" -#: contrib/localflavor/uk/uk_regions.py:90 +#: .\contrib\localflavor\uk\uk_regions.py:90 msgid "England" msgstr "Anglaterra" -#: contrib/localflavor/uk/uk_regions.py:91 +#: .\contrib\localflavor\uk\uk_regions.py:91 msgid "Northern Ireland" msgstr "Irlanda del Nord" -#: contrib/localflavor/uk/uk_regions.py:92 +#: .\contrib\localflavor\uk\uk_regions.py:92 msgid "Scotland" msgstr "Escòcia" -#: contrib/localflavor/uk/uk_regions.py:93 +#: .\contrib\localflavor\uk\uk_regions.py:93 msgid "Wales" msgstr "Gal·les" -#: contrib/localflavor/us/forms.py:16 +#: .\contrib\localflavor\us\forms.py:17 msgid "Enter a zip code in the format XXXXX or XXXXX-XXXX." -msgstr "Introduïu un codi zip en el format XXXXX o XXXXX-XXXX." +msgstr "Introduïu un codi postal en el format XXXXX o XXXXX-XXXX." -#: contrib/localflavor/us/forms.py:54 +#: .\contrib\localflavor\us\forms.py:26 +msgid "Phone numbers must be in XXX-XXX-XXXX format." +msgstr "Els números de telèfon han d'estar en el format XXX-XXX-XXXX." + +#: .\contrib\localflavor\us\forms.py:55 msgid "Enter a valid U.S. Social Security number in XXX-XX-XXXX format." msgstr "" "Introduïu un número và lid de la Seguretat Social dels E.U.A. en el format " "XXX-XX-XXXX." -#: contrib/localflavor/za/forms.py:20 +#: .\contrib\localflavor\us\forms.py:88 +msgid "Enter a U.S. state or territory." +msgstr "Introduïu un estat o territori dels E.U.A." + +#: .\contrib\localflavor\us\models.py:8 +msgid "U.S. state (two uppercase letters)" +msgstr "Estat dels E.U.A. (dues lletres majúscules)" + +#: .\contrib\localflavor\us\models.py:17 +msgid "Phone number" +msgstr "Número de telèfon" + +#: .\contrib\localflavor\uy\forms.py:28 +msgid "Enter a valid CI number in X.XXX.XXX-X,XXXXXXX-X or XXXXXXXX format." +msgstr "" +"Introduïu un número CI và lid en el format X.XXX.XXX-X,XXXXXXX-X o XXXXXXXX." + +#: .\contrib\localflavor\uy\forms.py:30 +msgid "Enter a valid CI number." +msgstr "Introduïu un número CI và lid." + +#: .\contrib\localflavor\za\forms.py:21 msgid "Enter a valid South African ID number" -msgstr "Introduïu un número d'Identitat Sud Africà valid" +msgstr "Introduïu un número d'Identitat Sud Africà và lid" -#: contrib/localflavor/za/forms.py:54 +#: .\contrib\localflavor\za\forms.py:55 msgid "Enter a valid South African postal code" msgstr "Introduïu un codi postal Sud Africà và lid." -#: contrib/localflavor/za/za_provinces.py:4 +#: .\contrib\localflavor\za\za_provinces.py:4 msgid "Eastern Cape" msgstr "Cap Oriental" -#: contrib/localflavor/za/za_provinces.py:5 +#: .\contrib\localflavor\za\za_provinces.py:5 msgid "Free State" msgstr "Estat lliure" -#: contrib/localflavor/za/za_provinces.py:6 +#: .\contrib\localflavor\za\za_provinces.py:6 msgid "Gauteng" msgstr "Gauteng" -#: contrib/localflavor/za/za_provinces.py:7 +#: .\contrib\localflavor\za\za_provinces.py:7 msgid "KwaZulu-Natal" msgstr "KwaZulu-Natal" -#: contrib/localflavor/za/za_provinces.py:8 +#: .\contrib\localflavor\za\za_provinces.py:8 msgid "Limpopo" msgstr "Limpopo" -#: contrib/localflavor/za/za_provinces.py:9 +#: .\contrib\localflavor\za\za_provinces.py:9 msgid "Mpumalanga" msgstr "Mpumalanga" -#: contrib/localflavor/za/za_provinces.py:10 +#: .\contrib\localflavor\za\za_provinces.py:10 msgid "Northern Cape" msgstr "Cap Nord" -#: contrib/localflavor/za/za_provinces.py:11 +#: .\contrib\localflavor\za\za_provinces.py:11 msgid "North West" msgstr "Cap Oest" -#: contrib/localflavor/za/za_provinces.py:12 +#: .\contrib\localflavor\za\za_provinces.py:12 msgid "Western Cape" msgstr "Cap Occidental" -#: contrib/redirects/models.py:7 +#: .\contrib\messages\tests\base.py:101 +msgid "lazy message" +msgstr "missatge gandul" + +#: .\contrib\redirects\models.py:7 msgid "redirect from" -msgstr "redreçar des de" +msgstr "redirecció des de" -#: contrib/redirects/models.py:8 +#: .\contrib\redirects\models.py:8 msgid "" "This should be an absolute path, excluding the domain name. Example: '/" "events/search/'." msgstr "" -"Aquesta ruta hauria de ser el camà absolut, excloent-ne el nom del domini. " +"Aquesta ruta hauria de ser un camà absolut, excloent-ne el nom del domini. " "Exemple '/events/search/'." -#: contrib/redirects/models.py:9 +#: .\contrib\redirects\models.py:9 msgid "redirect to" -msgstr "redreçar a" +msgstr "redirigir a" -#: contrib/redirects/models.py:10 +#: .\contrib\redirects\models.py:10 msgid "" "This can be either an absolute path (as above) or a full URL starting with " "'http://'." msgstr "" -"Això pot ser bé una ruta absoluta (com a dalt) o una URL completa que " -"comenci per http:// ." +"Això pot ser bé una ruta absoluta (com a sobre) o una URL completa que " +"comenci per 'http://'." -#: contrib/redirects/models.py:13 +#: .\contrib\redirects\models.py:13 msgid "redirect" -msgstr "redreçament" +msgstr "redirecció" -#: contrib/redirects/models.py:14 +#: .\contrib\redirects\models.py:14 msgid "redirects" -msgstr "redreçaments" +msgstr "redireccions" -#: contrib/sessions/models.py:45 +#: .\contrib\sessions\models.py:45 msgid "session key" msgstr "clau de la sessió" -#: contrib/sessions/models.py:47 +#: .\contrib\sessions\models.py:47 msgid "session data" msgstr "dades de la sessió" -#: contrib/sessions/models.py:48 +#: .\contrib\sessions\models.py:48 msgid "expire date" msgstr "data de caducitat" -#: contrib/sessions/models.py:53 +#: .\contrib\sessions\models.py:53 msgid "session" msgstr "sessió" -#: contrib/sessions/models.py:54 +#: .\contrib\sessions\models.py:54 msgid "sessions" msgstr "sessions" -#: contrib/sites/models.py:32 +#: .\contrib\sites\models.py:32 msgid "domain name" msgstr "nom del domini" -#: contrib/sites/models.py:33 +#: .\contrib\sites\models.py:33 msgid "display name" msgstr "nom per mostrar" -#: contrib/sites/models.py:39 +#: .\contrib\sites\models.py:39 msgid "sites" msgstr "llocs" -#: db/models/fields/__init__.py:356 db/models/fields/__init__.py:710 +#: .\core\validators.py:20 .\forms\fields.py:66 +msgid "Enter a valid value." +msgstr "Introduïu un valor và lid." + +#: .\core\validators.py:87 .\forms\fields.py:528 +msgid "Enter a valid URL." +msgstr "Introduïu una URL và lida." + +#: .\core\validators.py:89 .\forms\fields.py:529 +msgid "This URL appears to be a broken link." +msgstr "Aquesta URL sembla ser un enllaç trencat." + +#: .\core\validators.py:123 .\forms\fields.py:877 +msgid "" +"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens." +msgstr "" +"Introduïu un 'slug' và lid, consistent en lletres, números, guions o guions " +"baixos." + +#: .\core\validators.py:126 .\forms\fields.py:870 +msgid "Enter a valid IPv4 address." +msgstr "Introduïu una adreça IPv4 và lida." + +#: .\core\validators.py:129 .\db\models\fields\__init__.py:572 +msgid "Enter only digits separated by commas." +msgstr "Introduïu només dÃgits separats per comes." + +#: .\core\validators.py:135 +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "" +"Assegureu-vos que el valor sigui %(limit_value)s (és %(show_value)s)." + +#: .\core\validators.py:153 .\forms\fields.py:204 .\forms\fields.py:256 +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "Assegureu-vos que aquest valor sigui menor o igual que %(limit_value)s." + +#: .\core\validators.py:158 .\forms\fields.py:205 .\forms\fields.py:257 +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "Assegureu-vos que aquest valor sigui més gran o igual que " +"%(limit_value)s." + +#: .\core\validators.py:164 +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d characters (it has %" +"(show_value)d)." +msgstr "" +"Assegureu-vos que el valor tingui com a mÃnim %(limit_value)d carà cters " +"(en té %(show_value)d)." + +#: .\core\validators.py:170 +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d characters (it has %" +"(show_value)d)." +msgstr "" +"Assegureu-vos que el valor tingui com a mà xim %(limit_value)d carà cters " +"(en té %(show_value)d)." + +#: .\db\models\base.py:823 +#, python-format +msgid "%(field_name)s must be unique for %(date_field)s %(lookup)s." +msgstr "El camp %(field_name)s ha de ser únic per a %(lookup)s %(date_field)s." + +#: .\db\models\base.py:838 .\db\models\base.py:846 +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "Ja existeix %(model_name)s amb aquest %(field_label)s." + +#: .\db\models\fields\__init__.py:63 +#, python-format +msgid "Value %r is not a valid choice." +msgstr "El valor %r no és una opció và lida." + +#: .\db\models\fields\__init__.py:64 +msgid "This field cannot be null." +msgstr "Aquest camp no pot ser nul." + +#: .\db\models\fields\__init__.py:65 +msgid "This field cannot be blank." +msgstr "Aquest camp no pot estar en blanc." + +#: .\db\models\fields\__init__.py:70 +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "Camp del tipus: %(field_type)s" + +#: .\db\models\fields\__init__.py:451 .\db\models\fields\__init__.py:852 +#: .\db\models\fields\__init__.py:961 .\db\models\fields\__init__.py:972 +#: .\db\models\fields\__init__.py:999 +msgid "Integer" +msgstr "Enter" + +#: .\db\models\fields\__init__.py:455 .\db\models\fields\__init__.py:850 msgid "This value must be an integer." msgstr "Aquest valor ha de ser un enter." -#: db/models/fields/__init__.py:388 +#: .\db\models\fields\__init__.py:490 msgid "This value must be either True or False." -msgstr "Aquest valor ha de ser True (Veritat) o False (Fals)" +msgstr "Aquest valor ha de ser True (Cert) o False (Fals)" -#: db/models/fields/__init__.py:427 -msgid "This field cannot be null." -msgstr "Aquest camp no pot ser nul." +#: .\db\models\fields\__init__.py:492 +msgid "Boolean (Either True or False)" +msgstr "Booleà (Cert o Fals)" -#: db/models/fields/__init__.py:443 -msgid "Enter only digits separated by commas." -msgstr "Introduïu només dÃgits separats per comes." +#: .\db\models\fields\__init__.py:539 .\db\models\fields\__init__.py:982 +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "Cadena (de fins a %(max_length)s)" + +#: .\db\models\fields\__init__.py:567 +msgid "Comma-separated integers" +msgstr "Enters separats per comes" -#: db/models/fields/__init__.py:474 +#: .\db\models\fields\__init__.py:581 +msgid "Date (without time)" +msgstr "Data (sense hora)" + +#: .\db\models\fields\__init__.py:585 msgid "Enter a valid date in YYYY-MM-DD format." msgstr "Introduïu una data và lida en el forma AAAA-MM-DD." -#: db/models/fields/__init__.py:483 +#: .\db\models\fields\__init__.py:586 #, python-format msgid "Invalid date: %s" msgstr "Data invà lida: %s" -#: db/models/fields/__init__.py:547 db/models/fields/__init__.py:565 +#: .\db\models\fields\__init__.py:667 msgid "Enter a valid date/time in YYYY-MM-DD HH:MM[:ss[.uuuuuu]] format." msgstr "" "Introduïu un data/hora và lida en format YYYY-MM-DD HH:MM[:ss[.uuuuuu]]." -#: db/models/fields/__init__.py:601 +#: .\db\models\fields\__init__.py:669 +msgid "Date (with time)" +msgstr "Data (amb hora)" + +#: .\db\models\fields\__init__.py:735 msgid "This value must be a decimal number." msgstr "Aquest valor ha de ser un número decimal." -#: db/models/fields/__init__.py:686 +#: .\db\models\fields\__init__.py:737 +msgid "Decimal number" +msgstr "Número decimal" + +#: .\db\models\fields\__init__.py:792 +msgid "E-mail address" +msgstr "Adreça de correu electrònic" + +#: .\db\models\fields\__init__.py:799 .\db\models\fields\files.py:220 +#: .\db\models\fields\files.py:331 +msgid "File path" +msgstr "Ruta del fitxer" + +#: .\db\models\fields\__init__.py:822 msgid "This value must be a float." msgstr "Aquest valor ha de ser un número amb punt de coma flotant." -#: db/models/fields/__init__.py:746 +#: .\db\models\fields\__init__.py:824 +msgid "Floating point number" +msgstr "Número de coma flotant" + +#: .\db\models\fields\__init__.py:883 +msgid "Big (8 byte) integer" +msgstr "Enter gran (8 bytes)" + +#: .\db\models\fields\__init__.py:912 msgid "This value must be either None, True or False." -msgstr "Aquest valor ha de ser None (Cap), True (Veritat) o False (Fals)" +msgstr "Aquest valor ha de ser None (Cap), True (Cert) o False (Fals)" + +#: .\db\models\fields\__init__.py:914 +msgid "Boolean (Either True, False or None)" +msgstr "Booleà (Cert, Fals o Cap ('None'))" + +#: .\db\models\fields\__init__.py:1005 +msgid "Text" +msgstr "Text" + +#: .\db\models\fields\__init__.py:1021 +msgid "Time" +msgstr "Hora" -#: db/models/fields/__init__.py:849 db/models/fields/__init__.py:863 +#: .\db\models\fields\__init__.py:1025 msgid "Enter a valid time in HH:MM[:ss[.uuuuuu]] format." msgstr "Introduïu una hora và lida en el format HH:MM[:ss[.uuuuuu]]." -#: db/models/fields/related.py:869 +#: .\db\models\fields\__init__.py:1109 +msgid "XML text" +msgstr "Text XML" + +#: .\db\models\fields\related.py:799 +#, python-format +msgid "Model %(model)s with pk %(pk)r does not exist." +msgstr "No existeix el model %(model)s amb la clau primà ria %(pk)r." + +#: .\db\models\fields\related.py:801 +msgid "Foreign Key (type determined by related field)" +msgstr "Clau forana (tipus determinat pel camp relacionat)" + +#: .\db\models\fields\related.py:918 +msgid "One-to-one relationship" +msgstr "Inter-relació un-a-un" + +#: .\db\models\fields\related.py:980 +msgid "Many-to-many relationship" +msgstr "Inter-relació molts-a-molts" + +#: .\db\models\fields\related.py:1000 msgid "" "Hold down \"Control\", or \"Command\" on a Mac, to select more than one." msgstr "" -"Premeu la tecla \"Control\" -o \"Command\" en un Mac- per seleccionar més " +"Premeu la tecla \"Control\", o \"Command\" en un Mac, per seleccionar més " "d'un valor." -#: db/models/fields/related.py:930 +#: .\db\models\fields\related.py:1061 #, python-format msgid "Please enter valid %(self)s IDs. The value %(value)r is invalid." msgid_plural "" @@ -3960,516 +4726,478 @@ msgstr[1] "" "Si us plau, introduïu IDs de %(self)s và lids. Els valors %(value)r són " "invà lids." -#: forms/fields.py:53 +#: .\forms\fields.py:65 msgid "This field is required." msgstr "Aquest camp és obligatori." -#: forms/fields.py:54 -msgid "Enter a valid value." -msgstr "Introduïu un valor và lid." - -#: forms/fields.py:137 -#, python-format -msgid "Ensure this value has at most %(max)d characters (it has %(length)d)." -msgstr "" -"Assegureu-vos de que el valor té com a mà xim %(max)d carà cters (en té %" -"(length)d)." - -#: forms/fields.py:138 -#, python-format -msgid "Ensure this value has at least %(min)d characters (it has %(length)d)." -msgstr "" -"Assegureu-vos de que el valor té com a mÃnim %(min)d carà cters (en té %" -"(length)d)." - -#: forms/fields.py:165 +#: .\forms\fields.py:203 msgid "Enter a whole number." msgstr "Introduïu un número sencer." -#: forms/fields.py:166 forms/fields.py:195 forms/fields.py:224 -#, python-format -msgid "Ensure this value is less than or equal to %s." -msgstr "Aquest valor ha de ser menor o igual a %s." - -#: forms/fields.py:167 forms/fields.py:196 forms/fields.py:225 -#, python-format -msgid "Ensure this value is greater than or equal to %s." -msgstr "Assegureu-vos de que aquest valor sigui superior o igual a %s." - -#: forms/fields.py:194 forms/fields.py:223 +#: .\forms\fields.py:234 .\forms\fields.py:255 msgid "Enter a number." msgstr "Introduïu un número." -#: forms/fields.py:226 +#: .\forms\fields.py:258 #, python-format msgid "Ensure that there are no more than %s digits in total." -msgstr "Assegureu-vos de que no hi ha més de %s dÃgits en total." +msgstr "Assegureu-vos que no hi ha més de %s dÃgits en total." -#: forms/fields.py:227 +#: .\forms\fields.py:259 #, python-format msgid "Ensure that there are no more than %s decimal places." -msgstr "Assegureu-vos de que no hi ha més de %s decimals." +msgstr "Assegureu-vos que no hi ha més de %s decimals." -#: forms/fields.py:228 +#: .\forms\fields.py:260 #, python-format msgid "Ensure that there are no more than %s digits before the decimal point." -msgstr "Assegureu-vos de que no hi ha més de %s dÃgits decimals." +msgstr "Assegureu-vos que no hi ha més de %s dÃgits decimals." -#: forms/fields.py:287 forms/fields.py:862 +#: .\forms\fields.py:322 .\forms\fields.py:837 msgid "Enter a valid date." msgstr "Introduïu una data và lida." -#: forms/fields.py:321 forms/fields.py:863 +#: .\forms\fields.py:350 .\forms\fields.py:838 msgid "Enter a valid time." msgstr "Introduïu una hora và lida." -#: forms/fields.py:360 +#: .\forms\fields.py:376 msgid "Enter a valid date/time." msgstr "Introduïu una data/hora và lides." -#: forms/fields.py:446 +#: .\forms\fields.py:434 msgid "No file was submitted. Check the encoding type on the form." msgstr "" -"No s'ha enviat cap fitxer. Comprovi el tipus de codificació del formulari." +"No s'ha enviat cap fitxer. Comproveu el tipus de codificació del formulari." -#: forms/fields.py:447 +#: .\forms\fields.py:435 msgid "No file was submitted." msgstr "No s'ha enviat cap fitxer." -#: forms/fields.py:448 +#: .\forms\fields.py:436 msgid "The submitted file is empty." msgstr "El fitxer enviat està buit." -#: forms/fields.py:449 +#: .\forms\fields.py:437 #, python-format msgid "" "Ensure this filename has at most %(max)d characters (it has %(length)d)." msgstr "" -"Assegureu-vos de que el valor té com a mà xim %(max)d carà cters (en té %" -"(length)d)." +"Assegureu-vos que el valor té com a mà xim %(max)d carà cters (en té %(length)" +"d)." -#: forms/fields.py:482 +#: .\forms\fields.py:472 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -"Envieu una imatge và lida. El fitxer que heu enviat no era una imatge o " +"Carregueu una imatge và lida. El fitxer que heu carregat no era una imatge o " "estava corrupte." -#: forms/fields.py:543 -msgid "Enter a valid URL." -msgstr "Introduïu una URL và lida." - -#: forms/fields.py:544 -msgid "This URL appears to be a broken link." -msgstr "Aquesta URL sembla ser un enllaç trencat." - -#: forms/fields.py:624 forms/fields.py:702 +#: .\forms\fields.py:595 .\forms\fields.py:670 #, python-format msgid "Select a valid choice. %(value)s is not one of the available choices." msgstr "Esculliu una opció và lida. %(value)s no és una de les opcions và lides." -#: forms/fields.py:703 forms/fields.py:764 forms/models.py:999 +#: .\forms\fields.py:671 .\forms\fields.py:733 .\forms\models.py:1002 msgid "Enter a list of values." msgstr "Introduïu una llista de valors." -#: forms/fields.py:891 -msgid "Enter a valid IPv4 address." -msgstr "Introduïu una adreça IPv4 và lida." - -#: forms/fields.py:901 -msgid "" -"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens." -msgstr "" -"Introduïu un 'slug' và lid, consistent en lletres, números, guions o guions " -"baixos." - -#: forms/formsets.py:271 forms/formsets.py:273 +#: .\forms\formsets.py:298 .\forms\formsets.py:300 msgid "Order" msgstr "Ordre" -#: forms/models.py:363 -#, python-format -msgid "%(field_name)s must be unique for %(date_field)s %(lookup)s." -msgstr "El camp %(field_name)s ha de ser únic per a %(lookup)s %(date_field)s." - -#: forms/models.py:377 forms/models.py:385 -#, python-format -msgid "%(model_name)s with this %(field_label)s already exists." -msgstr "Ja existeix %(model_name)s amb aquest %(field_label)s." - -#: forms/models.py:590 +#: .\forms\models.py:562 #, python-format msgid "Please correct the duplicate data for %(field)s." msgstr "Si us plau, corregiu la dada duplicada per a %(field)s." -#: forms/models.py:594 +#: .\forms\models.py:566 #, python-format msgid "Please correct the duplicate data for %(field)s, which must be unique." msgstr "" "Si us plau, corregiu la dada duplicada per a %(field)s, la qual ha de ser " "única." -#: forms/models.py:600 +#: .\forms\models.py:572 #, python-format msgid "" "Please correct the duplicate data for %(field_name)s which must be unique " "for the %(lookup)s in %(date_field)s." msgstr "" "Si us plau, corregiu la dada duplicada per a %(field_name)s, la qual ha de " -"ser única per a la cerca %(lookup)s en %(date_field)s." +"ser única per a %(lookup)s en %(date_field)s." -#: forms/models.py:608 +#: .\forms\models.py:580 msgid "Please correct the duplicate values below." -msgstr "Si us plau, corregiu els valors duplicats a baix." +msgstr "Si us plau, corregiu els valors duplicats a sota." -#: forms/models.py:863 +#: .\forms\models.py:855 msgid "The inline foreign key did not match the parent instance primary key." msgstr "" -"La clau forà nea en lÃnea no coincideix amb la clau primà ria de la instà ncia " -"del pare" +"La clau forana en lÃnia no coincideix amb la clau primà ria de la instà ncia " +"mare." -#: forms/models.py:926 +#: .\forms\models.py:921 msgid "Select a valid choice. That choice is not one of the available choices." msgstr "" -"Escolli una opció và lida; Aquesta opció no és una de les opcions disponibles." +"Esculli una opció và lida. Aquesta opció no és una de les opcions disponibles." -#: forms/models.py:1000 +#: .\forms\models.py:1003 #, python-format msgid "Select a valid choice. %s is not one of the available choices." -msgstr "Escolliu una opció và lida; %s' no és una de les opcions và lides." +msgstr "Escolliu una opció và lida; %s no és una de les opcions và lides." -#: forms/models.py:1002 +#: .\forms\models.py:1005 #, python-format msgid "\"%s\" is not a valid value for a primary key." msgstr "\"%s\" no és un valor và lid per a una clau primà ria." -#: template/defaultfilters.py:767 +#: .\template\defaultfilters.py:776 msgid "yes,no,maybe" -msgstr "si,no,potser" +msgstr "sÃ,no,potser" -#: template/defaultfilters.py:798 +#: .\template\defaultfilters.py:807 #, python-format msgid "%(size)d byte" msgid_plural "%(size)d bytes" msgstr[0] "%(size)d byte" msgstr[1] "%(size)d bytes" -#: template/defaultfilters.py:800 +#: .\template\defaultfilters.py:809 #, python-format msgid "%.1f KB" msgstr "%.1f KB" -#: template/defaultfilters.py:802 +#: .\template\defaultfilters.py:811 #, python-format msgid "%.1f MB" msgstr "%.1f MB" -#: template/defaultfilters.py:803 +#: .\template\defaultfilters.py:812 #, python-format msgid "%.1f GB" msgstr "%.1f GB" -#: utils/dateformat.py:42 +#: .\utils\dateformat.py:42 msgid "p.m." msgstr "p.m." -#: utils/dateformat.py:43 +#: .\utils\dateformat.py:43 msgid "a.m." msgstr "a.m." -#: utils/dateformat.py:48 +#: .\utils\dateformat.py:48 msgid "PM" msgstr "PM" -#: utils/dateformat.py:49 +#: .\utils\dateformat.py:49 msgid "AM" msgstr "AM" -#: utils/dateformat.py:98 +#: .\utils\dateformat.py:98 msgid "midnight" -msgstr "mitja nit" +msgstr "mitjanit" -#: utils/dateformat.py:100 +#: .\utils\dateformat.py:100 msgid "noon" -msgstr "mig dia" +msgstr "migdia" -#: utils/dates.py:6 +#: .\utils\dates.py:6 msgid "Monday" msgstr "Dilluns" -#: utils/dates.py:6 +#: .\utils\dates.py:6 msgid "Tuesday" msgstr "Dimarts" -#: utils/dates.py:6 +#: .\utils\dates.py:6 msgid "Wednesday" msgstr "Dimecres" -#: utils/dates.py:6 +#: .\utils\dates.py:6 msgid "Thursday" msgstr "Dijous" -#: utils/dates.py:6 +#: .\utils\dates.py:6 msgid "Friday" msgstr "Divendres" -#: utils/dates.py:7 +#: .\utils\dates.py:7 msgid "Saturday" msgstr "Dissabte" -#: utils/dates.py:7 +#: .\utils\dates.py:7 msgid "Sunday" msgstr "Diumenge" -#: utils/dates.py:10 +#: .\utils\dates.py:10 msgid "Mon" -msgstr "Dl" +msgstr "dl." -#: utils/dates.py:10 +#: .\utils\dates.py:10 msgid "Tue" -msgstr "Dm" +msgstr "dt." -#: utils/dates.py:10 +#: .\utils\dates.py:10 msgid "Wed" -msgstr "Dmx" +msgstr "dc." -#: utils/dates.py:10 +#: .\utils\dates.py:10 msgid "Thu" -msgstr "Dj" +msgstr "dj." -#: utils/dates.py:10 +#: .\utils\dates.py:10 msgid "Fri" -msgstr "Dv" +msgstr "dv." -#: utils/dates.py:11 +#: .\utils\dates.py:11 msgid "Sat" -msgstr "Ds" +msgstr "ds." -#: utils/dates.py:11 +#: .\utils\dates.py:11 msgid "Sun" -msgstr "Dg" +msgstr "dg." -#: utils/dates.py:18 +#: .\utils\dates.py:18 msgid "January" msgstr "gener" -#: utils/dates.py:18 +#: .\utils\dates.py:18 msgid "February" msgstr "febrer" -#: utils/dates.py:18 utils/dates.py:31 +#: .\utils\dates.py:18 .\utils\dates.py:31 msgid "March" msgstr "març" -#: utils/dates.py:18 utils/dates.py:31 +#: .\utils\dates.py:18 .\utils\dates.py:31 msgid "April" msgstr "abril" -#: utils/dates.py:18 utils/dates.py:31 +#: .\utils\dates.py:18 .\utils\dates.py:31 msgid "May" msgstr "maig" -#: utils/dates.py:18 utils/dates.py:31 +#: .\utils\dates.py:18 .\utils\dates.py:31 msgid "June" msgstr "juny" -#: utils/dates.py:19 utils/dates.py:31 +#: .\utils\dates.py:19 .\utils\dates.py:31 msgid "July" msgstr "juliol" -#: utils/dates.py:19 +#: .\utils\dates.py:19 msgid "August" msgstr "agost" -#: utils/dates.py:19 +#: .\utils\dates.py:19 msgid "September" msgstr "setembre" -#: utils/dates.py:19 +#: .\utils\dates.py:19 msgid "October" msgstr "octubre" -#: utils/dates.py:19 +#: .\utils\dates.py:19 msgid "November" msgstr "novembre" -#: utils/dates.py:20 +#: .\utils\dates.py:20 msgid "December" msgstr "desembre" -#: utils/dates.py:23 +#: .\utils\dates.py:23 msgid "jan" -msgstr "gen" +msgstr "gen." -#: utils/dates.py:23 +#: .\utils\dates.py:23 msgid "feb" -msgstr "feb" +msgstr "feb." -#: utils/dates.py:23 +#: .\utils\dates.py:23 msgid "mar" -msgstr "mar" +msgstr "març" -#: utils/dates.py:23 +#: .\utils\dates.py:23 msgid "apr" -msgstr "abr" +msgstr "abr." -#: utils/dates.py:23 +#: .\utils\dates.py:23 msgid "may" -msgstr "mai" +msgstr "maig" -#: utils/dates.py:23 +#: .\utils\dates.py:23 msgid "jun" -msgstr "jun" +msgstr "juny" -#: utils/dates.py:24 +#: .\utils\dates.py:24 msgid "jul" -msgstr "jul" +msgstr "jul." -#: utils/dates.py:24 +#: .\utils\dates.py:24 msgid "aug" -msgstr "ago" +msgstr "ago." -#: utils/dates.py:24 +#: .\utils\dates.py:24 msgid "sep" -msgstr "set" +msgstr "set." -#: utils/dates.py:24 +#: .\utils\dates.py:24 msgid "oct" -msgstr "oct" +msgstr "oct." -#: utils/dates.py:24 +#: .\utils\dates.py:24 msgid "nov" -msgstr "nov" +msgstr "nov." -#: utils/dates.py:24 +#: .\utils\dates.py:24 msgid "dec" -msgstr "des" +msgstr "des." -#: utils/dates.py:31 +#: .\utils\dates.py:31 msgid "Jan." msgstr "gen." -#: utils/dates.py:31 +#: .\utils\dates.py:31 msgid "Feb." msgstr "feb." -#: utils/dates.py:32 +#: .\utils\dates.py:32 msgid "Aug." msgstr "ago." -#: utils/dates.py:32 +#: .\utils\dates.py:32 msgid "Sept." msgstr "set." -#: utils/dates.py:32 +#: .\utils\dates.py:32 msgid "Oct." msgstr "oct." -#: utils/dates.py:32 +#: .\utils\dates.py:32 msgid "Nov." msgstr "nov." -#: utils/dates.py:32 +#: .\utils\dates.py:32 msgid "Dec." msgstr "des." -#: utils/text.py:128 +#: .\utils\text.py:130 msgid "or" msgstr "o" -#: utils/timesince.py:21 +#: .\utils\timesince.py:21 msgid "year" msgid_plural "years" msgstr[0] "any" msgstr[1] "anys" -#: utils/timesince.py:22 +#: .\utils\timesince.py:22 msgid "month" msgid_plural "months" msgstr[0] "mes" msgstr[1] "mesos" -#: utils/timesince.py:23 +#: .\utils\timesince.py:23 msgid "week" msgid_plural "weeks" msgstr[0] "setmana" msgstr[1] "setmanes" -#: utils/timesince.py:24 +#: .\utils\timesince.py:24 msgid "day" msgid_plural "days" msgstr[0] "dia" msgstr[1] "dies" -#: utils/timesince.py:25 +#: .\utils\timesince.py:25 msgid "hour" msgid_plural "hours" msgstr[0] "hora" msgstr[1] "hores" -#: utils/timesince.py:26 +#: .\utils\timesince.py:26 msgid "minute" msgid_plural "minutes" msgstr[0] "minut" msgstr[1] "minuts" -#: utils/timesince.py:45 +#: .\utils\timesince.py:45 msgid "minutes" msgstr "minuts" -#: utils/timesince.py:50 +#: .\utils\timesince.py:50 #, python-format msgid "%(number)d %(type)s" msgstr "%(number)d %(type)s" -#: utils/timesince.py:56 +#: .\utils\timesince.py:56 #, python-format msgid ", %(number)d %(type)s" msgstr ", %(number)d %(type)s" -#: utils/translation/trans_real.py:399 +#: .\utils\translation\trans_real.py:519 msgid "DATE_FORMAT" msgstr "j \\de F \\de Y" -#: utils/translation/trans_real.py:401 +#: .\utils\translation\trans_real.py:520 +msgid "DATETIME_FORMAT" +msgstr "j \\de F \\de Y, H:i" + +#: .\utils\translation\trans_real.py:521 msgid "TIME_FORMAT" msgstr "H:i" -#: utils/translation/trans_real.py:417 +#: .\utils\translation\trans_real.py:542 msgid "YEAR_MONTH_FORMAT" msgstr "F \\de Y" -#: utils/translation/trans_real.py:418 +#: .\utils\translation\trans_real.py:543 msgid "MONTH_DAY_FORMAT" msgstr "j \\de F" -#: views/generic/create_update.py:114 +#: .\views\generic\create_update.py:115 #, python-format msgid "The %(verbose_name)s was created successfully." msgstr "El/la %(verbose_name)s s'ha creat amb èxit." -#: views/generic/create_update.py:156 +#: .\views\generic\create_update.py:158 #, python-format msgid "The %(verbose_name)s was updated successfully." msgstr "El/la %(verbose_name)s s'ha actualtzat amb èxit." -#: views/generic/create_update.py:198 +#: .\views\generic\create_update.py:201 #, python-format msgid "The %(verbose_name)s was deleted." -msgstr "El %(verbose_name)s s'ha eliminat." +msgstr "El/la %(verbose_name)s s'ha eliminat." + +#~ msgid "One or more %(fieldname)s in %(name)s: %(obj)s" +#~ msgstr "Un o més %(fieldname)s en %(name)s: %(obj)s" + +#~ msgid "One or more %(fieldname)s in %(name)s:" +#~ msgstr "Un o més %(fieldname)s en %(name)s:" + +#~ msgid "Old password:" +#~ msgstr "Contrasenya antiga:" + +#~ msgid "Relation to parent model" +#~ msgstr "Relació amb el model pare" + +#~ msgid "Add user" +#~ msgstr "Afegir usuari" #~ msgid "Comment moderation queue" #~ msgstr "Cua de moderació de comentaris" #~ msgid "No comments to moderate" -#~ msgstr "No hi ha comentaris per a moderar" +#~ msgstr "No hi ha comentaris per moderar" #~ msgid "Email" #~ msgstr "Correu electrònic" #~ msgid "Authenticated?" -#~ msgstr "Autentificat?" +#~ msgstr "Autenticat?" #~ msgid "IP Address" #~ msgstr "Adreça IP" @@ -4478,12 +5206,11 @@ msgstr "El %(verbose_name)s s'ha eliminat." #~ msgstr "Data d'enviament" #~ msgid "yes" -#~ msgstr "si" +#~ msgstr "sÃ" #~ msgid "no" #~ msgstr "no" -#, fuzzy #~ msgid "verbose_name" #~ msgid_plural "verbose_name_plural" #~ msgstr[0] "verbose_name" diff --git a/django/conf/locale/ca/LC_MESSAGES/djangojs.mo b/django/conf/locale/ca/LC_MESSAGES/djangojs.mo Binary files differindex 0df69aeb94..eefdf4be80 100644 --- a/django/conf/locale/ca/LC_MESSAGES/djangojs.mo +++ b/django/conf/locale/ca/LC_MESSAGES/djangojs.mo diff --git a/django/conf/locale/ca/LC_MESSAGES/djangojs.po b/django/conf/locale/ca/LC_MESSAGES/djangojs.po index 67c50c6532..788655b6b5 100644 --- a/django/conf/locale/ca/LC_MESSAGES/djangojs.po +++ b/django/conf/locale/ca/LC_MESSAGES/djangojs.po @@ -4,7 +4,7 @@ msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2007-02-15 01:00+0200\n" +"POT-Creation-Date: 2010-06-04 21:59+0200\n" "PO-Revision-Date: 2008-03-25 18:54+0100\n" "Last-Translator: Django Catalan Group <django-cat@googlegroups.com>\n" "Language-Team: Catalan <ca@li.org>\n" @@ -14,102 +14,144 @@ msgstr "" "X-Generator: VIM 7.1\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: contrib/admin/media/js/SelectFilter2.js:33 +#: .\contrib\admin\media\js\SelectFilter2.js.py:37 #, perl-format msgid "Available %s" msgstr "%s Disponibles" -#: contrib/admin/media/js/SelectFilter2.js:41 +#: .\contrib\admin\media\js\SelectFilter2.js.py:45 msgid "Choose all" msgstr "Seleccionar tots" -#: contrib/admin/media/js/SelectFilter2.js:46 +#: .\contrib\admin\media\js\SelectFilter2.js.py:50 msgid "Add" msgstr "Afegir" -#: contrib/admin/media/js/SelectFilter2.js:48 +#: .\contrib\admin\media\js\SelectFilter2.js.py:52 msgid "Remove" msgstr "Eliminar" -#: contrib/admin/media/js/SelectFilter2.js:53 +#: .\contrib\admin\media\js\SelectFilter2.js.py:57 #, perl-format msgid "Chosen %s" msgstr "%s Escollits" -#: contrib/admin/media/js/SelectFilter2.js:54 +#: .\contrib\admin\media\js\SelectFilter2.js.py:58 msgid "Select your choice(s) and click " msgstr "Faci les seves seleccions i faci click a" -#: contrib/admin/media/js/SelectFilter2.js:59 +#: .\contrib\admin\media\js\SelectFilter2.js.py:63 msgid "Clear all" msgstr "Deseleccionar tots" -#: contrib/admin/media/js/dateparse.js:32 -#: contrib/admin/media/js/calendar.js:24 -msgid "January February March April May June July August September October November December" -msgstr "Gener Febrer Març Abril Maig Juny Juliol Agost Setembre Octubre Novembre Desembre" +#: .\contrib\admin\media\js\actions.js.py:18 +#: .\contrib\admin\media\js\actions.min.js.py:1 +msgid "%(sel)s of %(cnt)s selected" +msgid_plural "%(sel)s of %(cnt)s selected" +msgstr[0] "%(sel)s de %(cnt)s seleccionat" +msgstr[1] "%(sel)s of %(cnt)s seleccionats" -#: contrib/admin/media/js/dateparse.js:33 -msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday" -msgstr "Diumenge Dilluns Dimarts Dimecres Dijous Divendres Dissabte" +#: .\contrib\admin\media\js\actions.js.py:109 +#: .\contrib\admin\media\js\actions.min.js.py:5 +msgid "" +"You have unsaved changes on individual editable fields. If you run an " +"action, your unsaved changes will be lost." +msgstr "" +"Teniu canvis sense desar a camps editables individuals. Si executeu " +"una acció, es perdran aquests canvis no desats." -#: contrib/admin/media/js/calendar.js:25 +#: .\contrib\admin\media\js\actions.js.py:121 +#: .\contrib\admin\media\js\actions.min.js.py:6 +msgid "" +"You have selected an action, but you haven't saved your changes to " +"individual fields yet. Please click OK to save. You'll need to re-run the " +"action." +msgstr "" +"Heu seleccionat una acció, però encara no heu desat els vostres canvis a " +"camps individuals. Si us plau premeu OK per desar. Haureu de tornar a " +"executar l'acció." + +#: .\contrib\admin\media\js\actions.js.py:123 +#: .\contrib\admin\media\js\actions.min.js.py:6 +msgid "" +"You have selected an action, and you haven't made any changes on individual " +"fields. You're probably looking for the Go button rather than the Save " +"button." +msgstr "" +"Heu seleccionat una acció i no heu fet cap canvi a camps individuals. " +"Probablement esteu cercant el botó 'Anar' enlloc de 'Desar'." + +#: .\contrib\admin\media\js\calendar.js.py:24 +#: .\contrib\admin\media\js\dateparse.js.py:32 +msgid "" +"January February March April May June July August September October November " +"December" +msgstr "" +"Gener Febrer Març Abril Maig Juny Juliol Agost Setembre Octubre Novembre " +"Desembre" + +#: .\contrib\admin\media\js\calendar.js.py:25 msgid "S M T W T F S" -msgstr "Dg Dl Dt Dc Dj Dv Ds" +msgstr "dg dl dt dc dj dv ds" -#: contrib/admin/media/js/admin/DateTimeShortcuts.js:47 -#: contrib/admin/media/js/admin/DateTimeShortcuts.js:81 +#: .\contrib\admin\media\js\collapse.js.py:9 +#: .\contrib\admin\media\js\collapse.js.py:21 +#: .\contrib\admin\media\js\collapse.min.js.py:1 +msgid "Show" +msgstr "Mostrar" + +#: .\contrib\admin\media\js\collapse.js.py:16 +#: .\contrib\admin\media\js\collapse.min.js.py:1 +msgid "Hide" +msgstr "Ocultar" + +#: .\contrib\admin\media\js\dateparse.js.py:33 +msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday" +msgstr "Diumenge Dilluns Dimarts Dimecres Dijous Divendres Dissabte" + +#: .\contrib\admin\media\js\admin\DateTimeShortcuts.js.py:48 +#: .\contrib\admin\media\js\admin\DateTimeShortcuts.js.py:83 msgid "Now" msgstr "Ara" -#: contrib/admin/media/js/admin/DateTimeShortcuts.js:51 +#: .\contrib\admin\media\js\admin\DateTimeShortcuts.js.py:52 msgid "Clock" -msgstr "Rellotje" +msgstr "Rellotge" -#: contrib/admin/media/js/admin/DateTimeShortcuts.js:78 +#: .\contrib\admin\media\js\admin\DateTimeShortcuts.js.py:79 msgid "Choose a time" msgstr "Esculli una hora" -#: contrib/admin/media/js/admin/DateTimeShortcuts.js:82 +#: .\contrib\admin\media\js\admin\DateTimeShortcuts.js.py:84 msgid "Midnight" -msgstr "Mitja nit" +msgstr "Mitjanit" -#: contrib/admin/media/js/admin/DateTimeShortcuts.js:83 +#: .\contrib\admin\media\js\admin\DateTimeShortcuts.js.py:85 msgid "6 a.m." msgstr "6 a.m." -#: contrib/admin/media/js/admin/DateTimeShortcuts.js:84 +#: .\contrib\admin\media\js\admin\DateTimeShortcuts.js.py:86 msgid "Noon" msgstr "Migdia" -#: contrib/admin/media/js/admin/DateTimeShortcuts.js:88 -#: contrib/admin/media/js/admin/DateTimeShortcuts.js:183 +#: .\contrib\admin\media\js\admin\DateTimeShortcuts.js.py:90 +#: .\contrib\admin\media\js\admin\DateTimeShortcuts.js.py:187 msgid "Cancel" msgstr "Cancel·lar" -#: contrib/admin/media/js/admin/DateTimeShortcuts.js:128 -#: contrib/admin/media/js/admin/DateTimeShortcuts.js:177 +#: .\contrib\admin\media\js\admin\DateTimeShortcuts.js.py:132 +#: .\contrib\admin\media\js\admin\DateTimeShortcuts.js.py:181 msgid "Today" msgstr "Avui" -#: contrib/admin/media/js/admin/DateTimeShortcuts.js:132 +#: .\contrib\admin\media\js\admin\DateTimeShortcuts.js.py:136 msgid "Calendar" msgstr "Calendari" -#: contrib/admin/media/js/admin/DateTimeShortcuts.js:175 +#: .\contrib\admin\media\js\admin\DateTimeShortcuts.js.py:179 msgid "Yesterday" msgstr "Ahir" -#: contrib/admin/media/js/admin/DateTimeShortcuts.js:179 +#: .\contrib\admin\media\js\admin\DateTimeShortcuts.js.py:183 msgid "Tomorrow" msgstr "Demà " - -#: contrib/admin/media/js/admin/CollapsedFieldsets.js:34 -#: contrib/admin/media/js/admin/CollapsedFieldsets.js:72 -msgid "Show" -msgstr "Mostrar" - -#: contrib/admin/media/js/admin/CollapsedFieldsets.js:63 -msgid "Hide" -msgstr "Ocultar" - diff --git a/django/conf/locale/cs/LC_MESSAGES/django.mo b/django/conf/locale/cs/LC_MESSAGES/django.mo Binary files differindex 9251a58fbe..82e12e8e09 100644 --- a/django/conf/locale/cs/LC_MESSAGES/django.mo +++ b/django/conf/locale/cs/LC_MESSAGES/django.mo diff --git a/django/conf/locale/cs/LC_MESSAGES/django.po b/django/conf/locale/cs/LC_MESSAGES/django.po index 4984d42517..b1a2e697d4 100644 --- a/django/conf/locale/cs/LC_MESSAGES/django.po +++ b/django/conf/locale/cs/LC_MESSAGES/django.po @@ -8,8 +8,8 @@ msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-05-09 14:26+0200\n" -"PO-Revision-Date: 2010-05-09 14:09+0100\n" +"POT-Creation-Date: 2010-08-06 18:35+0200\n" +"PO-Revision-Date: 2010-08-06 18:33+0100\n" "Last-Translator: Vlada Macek <macek@sandbox.cz>\n" "Language-Team: Czech\n" "MIME-Version: 1.0\n" @@ -70,7 +70,7 @@ msgid "Spanish" msgstr "Å¡panÄ›lsky" #: conf/global_settings.py:57 -msgid "Argentinean Spanish" +msgid "Argentinian Spanish" msgstr "Å¡panÄ›lsky (Argentina)" #: conf/global_settings.py:58 @@ -122,138 +122,146 @@ msgid "Hungarian" msgstr "maÄarsky" #: conf/global_settings.py:70 +msgid "Indonesian" +msgstr "indonésky" + +#: conf/global_settings.py:71 msgid "Icelandic" msgstr "islandsky" -#: conf/global_settings.py:71 +#: conf/global_settings.py:72 msgid "Italian" msgstr "italsky" -#: conf/global_settings.py:72 +#: conf/global_settings.py:73 msgid "Japanese" msgstr "japonsky" -#: conf/global_settings.py:73 +#: conf/global_settings.py:74 msgid "Georgian" msgstr "gruzÃnsky" -#: conf/global_settings.py:74 +#: conf/global_settings.py:75 msgid "Khmer" msgstr "khmersky" -#: conf/global_settings.py:75 +#: conf/global_settings.py:76 msgid "Kannada" msgstr "kannadsky" -#: conf/global_settings.py:76 +#: conf/global_settings.py:77 msgid "Korean" msgstr "korejsky" -#: conf/global_settings.py:77 +#: conf/global_settings.py:78 msgid "Lithuanian" msgstr "litevsky" -#: conf/global_settings.py:78 +#: conf/global_settings.py:79 msgid "Latvian" msgstr "lotyÅ¡sky" -#: conf/global_settings.py:79 +#: conf/global_settings.py:80 msgid "Macedonian" msgstr "makedonsky" -#: conf/global_settings.py:80 +#: conf/global_settings.py:81 +msgid "Malayalam" +msgstr "malajálamsky" + +#: conf/global_settings.py:82 msgid "Mongolian" msgstr "mongolsky" -#: conf/global_settings.py:81 +#: conf/global_settings.py:83 msgid "Dutch" msgstr "holandsky" -#: conf/global_settings.py:82 +#: conf/global_settings.py:84 msgid "Norwegian" msgstr "norsky" -#: conf/global_settings.py:83 +#: conf/global_settings.py:85 msgid "Norwegian Bokmal" msgstr "norsky (BokmÃ¥l)" -#: conf/global_settings.py:84 +#: conf/global_settings.py:86 msgid "Norwegian Nynorsk" msgstr "norsky (Nynorsk)" -#: conf/global_settings.py:85 +#: conf/global_settings.py:87 msgid "Polish" msgstr "polsky" -#: conf/global_settings.py:86 +#: conf/global_settings.py:88 msgid "Portuguese" msgstr "portugalsky" -#: conf/global_settings.py:87 +#: conf/global_settings.py:89 msgid "Brazilian Portuguese" msgstr "portugalsky (BrazÃlie)" -#: conf/global_settings.py:88 +#: conf/global_settings.py:90 msgid "Romanian" msgstr "rumunsky" -#: conf/global_settings.py:89 +#: conf/global_settings.py:91 msgid "Russian" msgstr "rusky" -#: conf/global_settings.py:90 +#: conf/global_settings.py:92 msgid "Slovak" msgstr "slovensky" -#: conf/global_settings.py:91 +#: conf/global_settings.py:93 msgid "Slovenian" msgstr "slovinsky" -#: conf/global_settings.py:92 +#: conf/global_settings.py:94 msgid "Albanian" msgstr "albánsky" -#: conf/global_settings.py:93 +#: conf/global_settings.py:95 msgid "Serbian" msgstr "srbsky" -#: conf/global_settings.py:94 +#: conf/global_settings.py:96 msgid "Serbian Latin" msgstr "srbsky (latinkou)" -#: conf/global_settings.py:95 +#: conf/global_settings.py:97 msgid "Swedish" msgstr "Å¡védsky" -#: conf/global_settings.py:96 +#: conf/global_settings.py:98 msgid "Tamil" msgstr "tamilsky" -#: conf/global_settings.py:97 +#: conf/global_settings.py:99 msgid "Telugu" msgstr "telužsky" -#: conf/global_settings.py:98 +#: conf/global_settings.py:100 msgid "Thai" msgstr "thajsky" -#: conf/global_settings.py:99 +#: conf/global_settings.py:101 msgid "Turkish" msgstr "turecky" -#: conf/global_settings.py:100 +#: conf/global_settings.py:102 msgid "Ukrainian" msgstr "ukrajinsky" -#: conf/global_settings.py:101 +#: conf/global_settings.py:103 msgid "Vietnamese" msgstr "vietnamsky" -#: conf/global_settings.py:102 +#: conf/global_settings.py:104 msgid "Simplified Chinese" msgstr "ÄÃnsky (zjednoduÅ¡enÄ›)" -#: conf/global_settings.py:103 +#: conf/global_settings.py:105 msgid "Traditional Chinese" msgstr "ÄÃnsky (tradiÄnÄ›)" @@ -305,15 +313,15 @@ msgstr "Tento mÄ›sÃc" msgid "This year" msgstr "Tento rok" -#: contrib/admin/filterspecs.py:147 forms/widgets.py:469 +#: contrib/admin/filterspecs.py:147 forms/widgets.py:478 msgid "Yes" msgstr "Ano" -#: contrib/admin/filterspecs.py:147 forms/widgets.py:469 +#: contrib/admin/filterspecs.py:147 forms/widgets.py:478 msgid "No" msgstr "Ne" -#: contrib/admin/filterspecs.py:154 forms/widgets.py:469 +#: contrib/admin/filterspecs.py:154 forms/widgets.py:478 msgid "Unknown" msgstr "Neznámé" @@ -359,7 +367,7 @@ msgid "Changed %s." msgstr "ZmÄ›nÄ›no: %s" #: contrib/admin/options.py:559 contrib/admin/options.py:569 -#: contrib/comments/templates/comments/preview.html:16 db/models/base.py:844 +#: contrib/comments/templates/comments/preview.html:16 db/models/base.py:845 #: forms/models.py:568 msgid "and" msgstr "a" @@ -853,7 +861,7 @@ msgstr "Uložit a pÅ™idat dalÅ¡Ã položku" msgid "Save and continue editing" msgstr "Uložit a pokraÄovat v úpravách" -#: contrib/admin/templates/admin/auth/user/add_form.html:5 +#: contrib/admin/templates/admin/auth/user/add_form.html:6 msgid "" "First, enter a username and password. Then, you'll be able to edit more user " "options." @@ -861,6 +869,10 @@ msgstr "" "NejdÅ™Ãve vložte uživatelské jméno a heslo. Poté budete moci upravovat vÃce " "uživatelských nastavenÃ." +#: contrib/admin/templates/admin/auth/user/add_form.html:8 +msgid "Enter a username and password." +msgstr "Vložte uživatelské jméno a heslo." + #: contrib/admin/templates/admin/auth/user/change_password.html:28 #, python-format msgid "Enter a new password for the user <strong>%(username)s</strong>." @@ -1418,8 +1430,8 @@ msgstr "zpráva" msgid "Logged out" msgstr "Odhlášeno" -#: contrib/auth/management/commands/createsuperuser.py:23 -#: core/validators.py:120 forms/fields.py:428 +#: contrib/auth/management/commands/createsuperuser.py:24 +#: core/validators.py:120 forms/fields.py:427 msgid "Enter a valid e-mail address." msgstr "Vložte platnou e-mailovou adresu." @@ -1491,7 +1503,7 @@ msgid "Email address" msgstr "E-mailová adresa" #: contrib/comments/forms.py:95 contrib/flatpages/admin.py:8 -#: contrib/flatpages/models.py:7 db/models/fields/__init__.py:1101 +#: contrib/flatpages/models.py:7 db/models/fields/__init__.py:1109 msgid "URL" msgstr "URL" @@ -1541,7 +1553,7 @@ msgstr "komentář" msgid "date/time submitted" msgstr "datum a Äas byly zaslané" -#: contrib/comments/models.py:60 db/models/fields/__init__.py:896 +#: contrib/comments/models.py:60 db/models/fields/__init__.py:904 msgid "IP address" msgstr "Adresa IP" @@ -4473,22 +4485,22 @@ msgstr "weby" msgid "Enter a valid value." msgstr "Vložte platnou hodnotu." -#: core/validators.py:87 forms/fields.py:529 +#: core/validators.py:87 forms/fields.py:528 msgid "Enter a valid URL." msgstr "Vložte platnou adresu URL." -#: core/validators.py:89 forms/fields.py:530 +#: core/validators.py:89 forms/fields.py:529 msgid "This URL appears to be a broken link." msgstr "Tato adresa URL je zÅ™ejmÄ› neplatný odkaz." -#: core/validators.py:123 forms/fields.py:873 +#: core/validators.py:123 forms/fields.py:877 msgid "" "Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens." msgstr "" "Vložte platný identifikátor složený pouze z pÃsmen, ÄÃsel, podtržÃtek a " "pomlÄek." -#: core/validators.py:126 forms/fields.py:866 +#: core/validators.py:126 forms/fields.py:870 msgid "Enter a valid IPv4 address." msgstr "Vložte platnou adresu typu IPv4." @@ -4501,12 +4513,12 @@ msgstr "Vložte pouze ÄÃslice oddÄ›lené Äárkami." msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." msgstr "Hodnota musà být %(limit_value)s (nynà je %(show_value)s)." -#: core/validators.py:153 forms/fields.py:205 forms/fields.py:257 +#: core/validators.py:153 forms/fields.py:204 forms/fields.py:256 #, python-format msgid "Ensure this value is less than or equal to %(limit_value)s." msgstr "Hodnota musà být menÅ¡Ã nebo rovna %(limit_value)s." -#: core/validators.py:158 forms/fields.py:206 forms/fields.py:258 +#: core/validators.py:158 forms/fields.py:205 forms/fields.py:257 #, python-format msgid "Ensure this value is greater than or equal to %(limit_value)s." msgstr "Hodnota musà být vÄ›tÅ¡Ã nebo rovna %(limit_value)s." @@ -4529,13 +4541,13 @@ msgstr "" "Hodnota smà mÃt nejvýše %(limit_value)d znaků, ale nynà jich má %(show_value)" "d." -#: db/models/base.py:822 +#: db/models/base.py:823 #, python-format msgid "%(field_name)s must be unique for %(date_field)s %(lookup)s." msgstr "" "Pole %(field_name)s musà být unikátnà testem %(lookup)s pole %(date_field)s." -#: db/models/base.py:837 db/models/base.py:845 +#: db/models/base.py:838 db/models/base.py:846 #, python-format msgid "%(model_name)s with this %(field_label)s already exists." msgstr "" @@ -4559,13 +4571,13 @@ msgstr "Pole nemůže být prázdné." msgid "Field of type: %(field_type)s" msgstr "Pole typu: %(field_type)s" -#: db/models/fields/__init__.py:451 db/models/fields/__init__.py:852 -#: db/models/fields/__init__.py:961 db/models/fields/__init__.py:972 -#: db/models/fields/__init__.py:999 +#: db/models/fields/__init__.py:451 db/models/fields/__init__.py:860 +#: db/models/fields/__init__.py:969 db/models/fields/__init__.py:980 +#: db/models/fields/__init__.py:1007 msgid "Integer" msgstr "Celé ÄÃslo" -#: db/models/fields/__init__.py:455 db/models/fields/__init__.py:850 +#: db/models/fields/__init__.py:455 db/models/fields/__init__.py:858 msgid "This value must be an integer." msgstr "Hodnota musà být celé ÄÃslo." @@ -4577,7 +4589,7 @@ msgstr "Hodnota musà být buÄ Ano (True) nebo Ne (False)." msgid "Boolean (Either True or False)" msgstr "Pravdivost (buÄ Ano (True), nebo Ne (False))" -#: db/models/fields/__init__.py:539 db/models/fields/__init__.py:982 +#: db/models/fields/__init__.py:539 db/models/fields/__init__.py:990 #, python-format msgid "String (up to %(max_length)s)" msgstr "ŘetÄ›zec (max. %(max_length)s znaků)" @@ -4619,44 +4631,44 @@ msgstr "Desetinné ÄÃslo" msgid "E-mail address" msgstr "E-mailová adresa" -#: db/models/fields/__init__.py:799 db/models/fields/files.py:220 +#: db/models/fields/__init__.py:807 db/models/fields/files.py:220 #: db/models/fields/files.py:331 msgid "File path" msgstr "Cesta k souboru" -#: db/models/fields/__init__.py:822 +#: db/models/fields/__init__.py:830 msgid "This value must be a float." msgstr "Hodnota musà být desetinné ÄÃslo." -#: db/models/fields/__init__.py:824 +#: db/models/fields/__init__.py:832 msgid "Floating point number" msgstr "ÄŒÃslo s pohyblivou řádovou Äárkou" -#: db/models/fields/__init__.py:883 +#: db/models/fields/__init__.py:891 msgid "Big (8 byte) integer" msgstr "Velké ÄÃslo (8 bajtů)" -#: db/models/fields/__init__.py:912 +#: db/models/fields/__init__.py:920 msgid "This value must be either None, True or False." msgstr "Hodnota musà být buÄ Nic (None), Ano (True) nebo Ne (False)." -#: db/models/fields/__init__.py:914 +#: db/models/fields/__init__.py:922 msgid "Boolean (Either True, False or None)" msgstr "Pravdivost (buÄ Ano (True), Ne (False) nebo Nic (None))" -#: db/models/fields/__init__.py:1005 +#: db/models/fields/__init__.py:1013 msgid "Text" msgstr "Text" -#: db/models/fields/__init__.py:1021 +#: db/models/fields/__init__.py:1029 msgid "Time" msgstr "ÄŒas" -#: db/models/fields/__init__.py:1025 +#: db/models/fields/__init__.py:1033 msgid "Enter a valid time in HH:MM[:ss[.uuuuuu]] format." msgstr "Vložte platný Äas ve tvaru HH:MM[:ss[.uuuuuu]]" -#: db/models/fields/__init__.py:1109 +#: db/models/fields/__init__.py:1125 msgid "XML text" msgstr "XML text" @@ -4669,22 +4681,22 @@ msgstr "Položka typu %(model)s s primárnÃm klÃÄem %(pk)r neexistuje." msgid "Foreign Key (type determined by related field)" msgstr "Cizà klÃÄ (typ urÄen pomocà souvisejÃcÃho pole)" -#: db/models/fields/related.py:918 +#: db/models/fields/related.py:919 msgid "One-to-one relationship" msgstr "Vazba jedna-jedna" -#: db/models/fields/related.py:980 +#: db/models/fields/related.py:981 msgid "Many-to-many relationship" msgstr "Vazba mnoho-mnoho" -#: db/models/fields/related.py:1000 +#: db/models/fields/related.py:1001 msgid "" "Hold down \"Control\", or \"Command\" on a Mac, to select more than one." msgstr "" "VýbÄ›r vÃce než jedné položky je možný pÅ™idrženÃm klávesy \"Control\" (nebo " "\"Command\" na Macu)." -#: db/models/fields/related.py:1061 +#: db/models/fields/related.py:1062 #, python-format msgid "Please enter valid %(self)s IDs. The value %(value)r is invalid." msgid_plural "" @@ -4697,74 +4709,74 @@ msgstr[2] "Vložte platné ID položky %(self)s. Hodnoty %(value)r jsou neplatnà msgid "This field is required." msgstr "Pole je povinné." -#: forms/fields.py:204 +#: forms/fields.py:203 msgid "Enter a whole number." msgstr "Vložte celé ÄÃslo." -#: forms/fields.py:235 forms/fields.py:256 +#: forms/fields.py:234 forms/fields.py:255 msgid "Enter a number." msgstr "Vložte ÄÃslo." -#: forms/fields.py:259 +#: forms/fields.py:258 #, python-format msgid "Ensure that there are no more than %s digits in total." msgstr "Hodnota nesmà celkem mÃt vÃce než %s cifer." -#: forms/fields.py:260 +#: forms/fields.py:259 #, python-format msgid "Ensure that there are no more than %s decimal places." msgstr "Hodnota nesmà mÃt za desetinnou Äárkou vÃce než %s cifer." -#: forms/fields.py:261 +#: forms/fields.py:260 #, python-format msgid "Ensure that there are no more than %s digits before the decimal point." msgstr "Hodnota nesmà mÃt pÅ™ed desetinnou Äárkou vÃce než %s cifer." -#: forms/fields.py:323 forms/fields.py:838 +#: forms/fields.py:322 forms/fields.py:837 msgid "Enter a valid date." msgstr "Vložte platné datum." -#: forms/fields.py:351 forms/fields.py:839 +#: forms/fields.py:350 forms/fields.py:838 msgid "Enter a valid time." msgstr "Vložte platný Äas." -#: forms/fields.py:377 +#: forms/fields.py:376 msgid "Enter a valid date/time." msgstr "Vložte platné datum a Äas." -#: forms/fields.py:435 +#: forms/fields.py:434 msgid "No file was submitted. Check the encoding type on the form." msgstr "" "Soubor nebyl odeslán. Zkontrolujte parametr \"encoding type\" formuláře." -#: forms/fields.py:436 +#: forms/fields.py:435 msgid "No file was submitted." msgstr "Žádný soubor nebyl odeslán." -#: forms/fields.py:437 +#: forms/fields.py:436 msgid "The submitted file is empty." msgstr "Odeslaný soubor je prázdný." -#: forms/fields.py:438 +#: forms/fields.py:437 #, python-format msgid "" "Ensure this filename has at most %(max)d characters (it has %(length)d)." msgstr "" "Délka názvu souboru má být nejvýše %(max)d znaků, ale nynà je %(length)d." -#: forms/fields.py:473 +#: forms/fields.py:472 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" "Nahrajte platný obrázek. Odeslaný soubor buÄ nebyl obrázek nebo byl poÅ¡kozen." -#: forms/fields.py:596 forms/fields.py:671 +#: forms/fields.py:595 forms/fields.py:670 #, python-format msgid "Select a valid choice. %(value)s is not one of the available choices." msgstr "Vyberte platnou možnost, \"%(value)s\" nenà k dispozici." -#: forms/fields.py:672 forms/fields.py:734 forms/models.py:1002 +#: forms/fields.py:671 forms/fields.py:733 forms/models.py:1002 msgid "Enter a list of values." msgstr "Vložte seznam hodnot." diff --git a/django/conf/locale/cs/LC_MESSAGES/djangojs.mo b/django/conf/locale/cs/LC_MESSAGES/djangojs.mo Binary files differindex 1cf448fe72..8efa6f6e4c 100644 --- a/django/conf/locale/cs/LC_MESSAGES/djangojs.mo +++ b/django/conf/locale/cs/LC_MESSAGES/djangojs.mo diff --git a/django/conf/locale/cs/LC_MESSAGES/djangojs.po b/django/conf/locale/cs/LC_MESSAGES/djangojs.po index 0b958e290c..e4f06a96ed 100644 --- a/django/conf/locale/cs/LC_MESSAGES/djangojs.po +++ b/django/conf/locale/cs/LC_MESSAGES/djangojs.po @@ -8,8 +8,8 @@ msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-05-09 14:30+0200\n" -"PO-Revision-Date: 2010-05-09 14:04+0100\n" +"POT-Creation-Date: 2010-08-06 18:35+0200\n" +"PO-Revision-Date: 2010-08-06 18:34+0100\n" "Last-Translator: Vlada Macek <macek@sandbox.cz>\n" "Language-Team: Czech\n" "MIME-Version: 1.0\n" diff --git a/django/conf/locale/da/LC_MESSAGES/djangojs.mo b/django/conf/locale/da/LC_MESSAGES/djangojs.mo Binary files differindex 697f1a9f6a..47490d8620 100644 --- a/django/conf/locale/da/LC_MESSAGES/djangojs.mo +++ b/django/conf/locale/da/LC_MESSAGES/djangojs.mo diff --git a/django/conf/locale/da/LC_MESSAGES/djangojs.po b/django/conf/locale/da/LC_MESSAGES/djangojs.po index 4db7601294..f220938ae1 100644 --- a/django/conf/locale/da/LC_MESSAGES/djangojs.po +++ b/django/conf/locale/da/LC_MESSAGES/djangojs.po @@ -6,9 +6,9 @@ msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-04-26 15:49+0200\n" -"PO-Revision-Date: 2008-08-13 22:00+0200\n" -"Last-Translator: Finn Gruwier Larsen<finn@gruwier.dk>\n" +"POT-Creation-Date: 2010-08-07 11:57+0200\n" +"PO-Revision-Date: 2010-08-07 22:00+0200\n" +"Last-Translator: Finn Gruwier Larsen<finngruwierlarsen@gmail.com>\n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -44,13 +44,42 @@ msgstr "Foretag dit/dine valg og klik " msgid "Clear all" msgstr "Fravælg alle" -#: contrib/admin/media/js/actions.js:17 +#: contrib/admin/media/js/actions.js:18 #: contrib/admin/media/js/actions.min.js:1 msgid "%(sel)s of %(cnt)s selected" msgid_plural "%(sel)s of %(cnt)s selected" msgstr[0] "%(sel)s af %(cnt)s valgt" msgstr[1] "%(sel)s af %(cnt)s valgt" +#: contrib/admin/media/js/actions.js:109 +#: contrib/admin/media/js/actions.min.js:5 +msgid "" +"You have unsaved changes on individual editable fields. If you run an " +"action, your unsaved changes will be lost." +msgstr "" +"Du har ugemte ændringer af et eller flere redigerbare felter. Hvis du " +"udfører en handling fra drop-down-menuen, vil du miste disse ændringer." + +#: contrib/admin/media/js/actions.js:121 +#: contrib/admin/media/js/actions.min.js:6 +msgid "" +"You have selected an action, but you haven't saved your changes to " +"individual fields yet. Please click OK to save. You'll need to re-run the " +"action." +msgstr "" +"Du har valgt en handling, men du har ikke gemt dine ændringer til et eller " +"flere felter. Klik venligst OK for at gemme og vælg dernæst handlingen igen." + +#: contrib/admin/media/js/actions.js:123 +#: contrib/admin/media/js/actions.min.js:6 +msgid "" +"You have selected an action, and you haven't made any changes on individual " +"fields. You're probably looking for the Go button rather than the Save " +"button." +msgstr "" +"Du har valgt en handling, og du har ikke udført nogen ændringer pÃ¥ felter. " +"Det, du søger er formentlig Udfør-knappen i stedet for Gem-knappen." + #: contrib/admin/media/js/calendar.js:24 #: contrib/admin/media/js/dateparse.js:32 msgid "" diff --git a/django/conf/locale/de/LC_MESSAGES/django.mo b/django/conf/locale/de/LC_MESSAGES/django.mo Binary files differindex 5f88e60a68..79151f8fe1 100644 --- a/django/conf/locale/de/LC_MESSAGES/django.mo +++ b/django/conf/locale/de/LC_MESSAGES/django.mo diff --git a/django/conf/locale/de/LC_MESSAGES/django.po b/django/conf/locale/de/LC_MESSAGES/django.po index 521bd3c89c..b59b339b2e 100644 --- a/django/conf/locale/de/LC_MESSAGES/django.po +++ b/django/conf/locale/de/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-05-13 15:30+0200\n" +"POT-Creation-Date: 2010-08-06 18:53+0200\n" "PO-Revision-Date: 2010-04-26 13:53+0100\n" "Last-Translator: Jannis Leidel <jannis@leidel.info>\n" "Language-Team: \n" @@ -72,7 +72,7 @@ msgid "Spanish" msgstr "Spanisch" #: conf/global_settings.py:57 -msgid "Argentinean Spanish" +msgid "Argentinian Spanish" msgstr "Argentinisches Spanisch" #: conf/global_settings.py:58 @@ -168,98 +168,102 @@ msgid "Macedonian" msgstr "Mazedonisch" #: conf/global_settings.py:81 +msgid "Malayalam" +msgstr "Malayalam" + +#: conf/global_settings.py:82 msgid "Mongolian" msgstr "Mongolisch" -#: conf/global_settings.py:82 +#: conf/global_settings.py:83 msgid "Dutch" msgstr "Holländisch" -#: conf/global_settings.py:83 +#: conf/global_settings.py:84 msgid "Norwegian" msgstr "Norwegisch" -#: conf/global_settings.py:84 +#: conf/global_settings.py:85 msgid "Norwegian Bokmal" msgstr "Norwegisch (BokmÃ¥l)" -#: conf/global_settings.py:85 +#: conf/global_settings.py:86 msgid "Norwegian Nynorsk" msgstr "Norwegisch (Nynorsk)" -#: conf/global_settings.py:86 +#: conf/global_settings.py:87 msgid "Polish" msgstr "Polnisch" -#: conf/global_settings.py:87 +#: conf/global_settings.py:88 msgid "Portuguese" msgstr "Portugiesisch" -#: conf/global_settings.py:88 +#: conf/global_settings.py:89 msgid "Brazilian Portuguese" msgstr "Brasilianisches Portugiesisch" -#: conf/global_settings.py:89 +#: conf/global_settings.py:90 msgid "Romanian" msgstr "Rumänisch" -#: conf/global_settings.py:90 +#: conf/global_settings.py:91 msgid "Russian" msgstr "Russisch" -#: conf/global_settings.py:91 +#: conf/global_settings.py:92 msgid "Slovak" msgstr "Slowakisch" -#: conf/global_settings.py:92 +#: conf/global_settings.py:93 msgid "Slovenian" msgstr "Slowenisch" -#: conf/global_settings.py:93 +#: conf/global_settings.py:94 msgid "Albanian" msgstr "Albanisch" -#: conf/global_settings.py:94 +#: conf/global_settings.py:95 msgid "Serbian" msgstr "Serbisch" -#: conf/global_settings.py:95 +#: conf/global_settings.py:96 msgid "Serbian Latin" msgstr "Serbisch (Latein)" -#: conf/global_settings.py:96 +#: conf/global_settings.py:97 msgid "Swedish" msgstr "Schwedisch" -#: conf/global_settings.py:97 +#: conf/global_settings.py:98 msgid "Tamil" msgstr "Tamilisch" -#: conf/global_settings.py:98 +#: conf/global_settings.py:99 msgid "Telugu" msgstr "Telugisch" -#: conf/global_settings.py:99 +#: conf/global_settings.py:100 msgid "Thai" msgstr "Thailändisch" -#: conf/global_settings.py:100 +#: conf/global_settings.py:101 msgid "Turkish" msgstr "Türkisch" -#: conf/global_settings.py:101 +#: conf/global_settings.py:102 msgid "Ukrainian" msgstr "Ukrainisch" -#: conf/global_settings.py:102 +#: conf/global_settings.py:103 msgid "Vietnamese" msgstr "Vietnamesisch" -#: conf/global_settings.py:103 +#: conf/global_settings.py:104 msgid "Simplified Chinese" msgstr "Vereinfachtes Chinesisch" -#: conf/global_settings.py:104 +#: conf/global_settings.py:105 msgid "Traditional Chinese" msgstr "Traditionelles Chinesisch" @@ -311,15 +315,15 @@ msgstr "Diesen Monat" msgid "This year" msgstr "Dieses Jahr" -#: contrib/admin/filterspecs.py:147 forms/widgets.py:469 +#: contrib/admin/filterspecs.py:147 forms/widgets.py:478 msgid "Yes" msgstr "Ja" -#: contrib/admin/filterspecs.py:147 forms/widgets.py:469 +#: contrib/admin/filterspecs.py:147 forms/widgets.py:478 msgid "No" msgstr "Nein" -#: contrib/admin/filterspecs.py:154 forms/widgets.py:469 +#: contrib/admin/filterspecs.py:154 forms/widgets.py:478 msgid "Unknown" msgstr "Unbekannt" @@ -696,7 +700,7 @@ msgid "Filter" msgstr "Filter" #: contrib/admin/templates/admin/delete_confirmation.html:10 -#: contrib/admin/templates/admin/submit_line.html:4 forms/formsets.py:302 +#: contrib/admin/templates/admin/submit_line.html:4 forms/formsets.py:300 msgid "Delete" msgstr "Löschen" @@ -859,7 +863,7 @@ msgstr "Sichern und neu hinzufügen" msgid "Save and continue editing" msgstr "Sichern und weiter bearbeiten" -#: contrib/admin/templates/admin/auth/user/add_form.html:5 +#: contrib/admin/templates/admin/auth/user/add_form.html:6 msgid "" "First, enter a username and password. Then, you'll be able to edit more user " "options." @@ -867,6 +871,10 @@ msgstr "" "Zuerst einen Benutzer und ein Passwort eingeben. Danach können weitere " "Optionen für den Benutzer geändert werden." +#: contrib/admin/templates/admin/auth/user/add_form.html:8 +msgid "Enter a username and password." +msgstr "Bitte einen Benutzernamen und ein Passwort eingeben." + #: contrib/admin/templates/admin/auth/user/change_password.html:28 #, python-format msgid "Enter a new password for the user <strong>%(username)s</strong>." @@ -1437,8 +1445,8 @@ msgstr "Mitteilung" msgid "Logged out" msgstr "Abgemeldet" -#: contrib/auth/management/commands/createsuperuser.py:23 -#: core/validators.py:120 forms/fields.py:428 +#: contrib/auth/management/commands/createsuperuser.py:24 +#: core/validators.py:120 forms/fields.py:427 msgid "Enter a valid e-mail address." msgstr "Bitte eine gültige E-Mail-Adresse eingeben." @@ -1506,7 +1514,7 @@ msgid "Email address" msgstr "E-Mail-Adresse" #: contrib/comments/forms.py:95 contrib/flatpages/admin.py:8 -#: contrib/flatpages/models.py:7 db/models/fields/__init__.py:1101 +#: contrib/flatpages/models.py:7 db/models/fields/__init__.py:1109 msgid "URL" msgstr "Adresse (URL)" @@ -1557,7 +1565,7 @@ msgstr "Kommentar" msgid "date/time submitted" msgstr "Datum/Zeit Erstellung" -#: contrib/comments/models.py:60 db/models/fields/__init__.py:896 +#: contrib/comments/models.py:60 db/models/fields/__init__.py:904 msgid "IP address" msgstr "IP-Adresse" @@ -4509,22 +4517,22 @@ msgstr "Sites" msgid "Enter a valid value." msgstr "Bitte einen gültigen Wert eingeben." -#: core/validators.py:87 forms/fields.py:529 +#: core/validators.py:87 forms/fields.py:528 msgid "Enter a valid URL." msgstr "Bitte eine gültige Adresse eingeben." -#: core/validators.py:89 forms/fields.py:530 +#: core/validators.py:89 forms/fields.py:529 msgid "This URL appears to be a broken link." msgstr "Diese Adresse scheint nicht gültig zu sein." -#: core/validators.py:123 forms/fields.py:873 +#: core/validators.py:123 forms/fields.py:877 msgid "" "Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens." msgstr "" "Bitte ein gültiges Kürzel, bestehend aus Buchstaben, Ziffern, Unterstrichen " "und Bindestrichen, eingeben." -#: core/validators.py:126 forms/fields.py:866 +#: core/validators.py:126 forms/fields.py:870 msgid "Enter a valid IPv4 address." msgstr "Bitte eine gültige IPv4-Adresse eingeben." @@ -4539,12 +4547,12 @@ msgstr "" "Bitte sicherstellen, dass der Wert %(limit_value)s ist. (Er ist %(show_value)" "s)" -#: core/validators.py:153 forms/fields.py:205 forms/fields.py:257 +#: core/validators.py:153 forms/fields.py:204 forms/fields.py:256 #, python-format msgid "Ensure this value is less than or equal to %(limit_value)s." msgstr "Dieser Wert muss kleiner oder gleich %(limit_value)s sein." -#: core/validators.py:158 forms/fields.py:206 forms/fields.py:258 +#: core/validators.py:158 forms/fields.py:205 forms/fields.py:257 #, python-format msgid "Ensure this value is greater than or equal to %(limit_value)s." msgstr "Dieser Wert muss größer oder gleich %(limit_value)s sein." @@ -4595,13 +4603,13 @@ msgstr "Dieses Feld darf nicht leer sein." msgid "Field of type: %(field_type)s" msgstr "Feldtyp: %(field_type)s" -#: db/models/fields/__init__.py:451 db/models/fields/__init__.py:852 -#: db/models/fields/__init__.py:961 db/models/fields/__init__.py:972 -#: db/models/fields/__init__.py:999 +#: db/models/fields/__init__.py:451 db/models/fields/__init__.py:860 +#: db/models/fields/__init__.py:969 db/models/fields/__init__.py:980 +#: db/models/fields/__init__.py:1007 msgid "Integer" msgstr "Ganzzahl" -#: db/models/fields/__init__.py:455 db/models/fields/__init__.py:850 +#: db/models/fields/__init__.py:455 db/models/fields/__init__.py:858 msgid "This value must be an integer." msgstr "Dieser Wert muss eine Ganzzahl sein." @@ -4613,7 +4621,7 @@ msgstr "Dieser Wert muss True oder False sein." msgid "Boolean (Either True or False)" msgstr "Boolescher Wert (True oder False)" -#: db/models/fields/__init__.py:539 db/models/fields/__init__.py:982 +#: db/models/fields/__init__.py:539 db/models/fields/__init__.py:990 #, python-format msgid "String (up to %(max_length)s)" msgstr "Zeichenkette (bis zu %(max_length)s Zeichen)" @@ -4657,44 +4665,44 @@ msgstr "Dezimalzahl" msgid "E-mail address" msgstr "E-Mail-Adresse" -#: db/models/fields/__init__.py:799 db/models/fields/files.py:220 +#: db/models/fields/__init__.py:807 db/models/fields/files.py:220 #: db/models/fields/files.py:331 msgid "File path" msgstr "Dateipfad" -#: db/models/fields/__init__.py:822 +#: db/models/fields/__init__.py:830 msgid "This value must be a float." msgstr "Dieser Wert muss eine Gleitkommazahl sein." -#: db/models/fields/__init__.py:824 +#: db/models/fields/__init__.py:832 msgid "Floating point number" msgstr "Gleitkommazahl" -#: db/models/fields/__init__.py:883 +#: db/models/fields/__init__.py:891 msgid "Big (8 byte) integer" msgstr "Große Ganzzahl (8 Byte)" -#: db/models/fields/__init__.py:912 +#: db/models/fields/__init__.py:920 msgid "This value must be either None, True or False." msgstr "Dieser Wert muss None, True oder False sein." -#: db/models/fields/__init__.py:914 +#: db/models/fields/__init__.py:922 msgid "Boolean (Either True, False or None)" msgstr "Boolescher Wert (True, False oder None)" -#: db/models/fields/__init__.py:1005 +#: db/models/fields/__init__.py:1013 msgid "Text" msgstr "Text" -#: db/models/fields/__init__.py:1021 +#: db/models/fields/__init__.py:1029 msgid "Time" msgstr "Zeit" -#: db/models/fields/__init__.py:1025 +#: db/models/fields/__init__.py:1033 msgid "Enter a valid time in HH:MM[:ss[.uuuuuu]] format." msgstr "Bitte eine gültige Zeit im Format HH:MM[:ss[.uuuuuu]] eingeben." -#: db/models/fields/__init__.py:1109 +#: db/models/fields/__init__.py:1125 msgid "XML text" msgstr "XML-Text" @@ -4707,22 +4715,22 @@ msgstr "Modell %(model)s mit dem Primärschlüssel %(pk)r ist nicht vorhanden." msgid "Foreign Key (type determined by related field)" msgstr "Fremdschlüssel (Typ definiert durch verknüpftes Feld)" -#: db/models/fields/related.py:918 +#: db/models/fields/related.py:919 msgid "One-to-one relationship" msgstr "One-to-one-Beziehung" -#: db/models/fields/related.py:980 +#: db/models/fields/related.py:981 msgid "Many-to-many relationship" msgstr "Many-to-many-Beziehung" -#: db/models/fields/related.py:1000 +#: db/models/fields/related.py:1001 msgid "" "Hold down \"Control\", or \"Command\" on a Mac, to select more than one." msgstr "" "Halten Sie die Strg-Taste (⌘ für Mac) während des Klickens gedrückt, um " "mehrere Einträge auszuwählen." -#: db/models/fields/related.py:1061 +#: db/models/fields/related.py:1062 #, python-format msgid "Please enter valid %(self)s IDs. The value %(value)r is invalid." msgid_plural "" @@ -4736,55 +4744,55 @@ msgstr[1] "" msgid "This field is required." msgstr "Dieses Feld ist zwingend erforderlich." -#: forms/fields.py:204 +#: forms/fields.py:203 msgid "Enter a whole number." msgstr "Bitte eine ganze Zahl eingeben." -#: forms/fields.py:235 forms/fields.py:256 +#: forms/fields.py:234 forms/fields.py:255 msgid "Enter a number." msgstr "Bitte eine Zahl eingeben." -#: forms/fields.py:259 +#: forms/fields.py:258 #, python-format msgid "Ensure that there are no more than %s digits in total." msgstr "Bitte geben Sie nicht mehr als insgesamt %s Ziffern ein." -#: forms/fields.py:260 +#: forms/fields.py:259 #, python-format msgid "Ensure that there are no more than %s decimal places." msgstr "Bitte geben Sie nicht mehr als %s Dezimalstellen ein." -#: forms/fields.py:261 +#: forms/fields.py:260 #, python-format msgid "Ensure that there are no more than %s digits before the decimal point." msgstr "Bitte geben Sie nicht mehr als %s Ziffern vor dem Komma ein." -#: forms/fields.py:323 forms/fields.py:838 +#: forms/fields.py:322 forms/fields.py:837 msgid "Enter a valid date." msgstr "Bitte ein gültiges Datum eingeben." -#: forms/fields.py:351 forms/fields.py:839 +#: forms/fields.py:350 forms/fields.py:838 msgid "Enter a valid time." msgstr "Bitte eine gültige Uhrzeit eingeben." -#: forms/fields.py:377 +#: forms/fields.py:376 msgid "Enter a valid date/time." msgstr "Bitte ein gültiges Datum und Uhrzeit eingeben." -#: forms/fields.py:435 +#: forms/fields.py:434 msgid "No file was submitted. Check the encoding type on the form." msgstr "" "Es wurde keine Datei übermittelt. Ãœberprüfen Sie das Encoding des Formulars." -#: forms/fields.py:436 +#: forms/fields.py:435 msgid "No file was submitted." msgstr "Es wurde keine Datei übertragen." -#: forms/fields.py:437 +#: forms/fields.py:436 msgid "The submitted file is empty." msgstr "Die ausgewählte Datei ist leer." -#: forms/fields.py:438 +#: forms/fields.py:437 #, python-format msgid "" "Ensure this filename has at most %(max)d characters (it has %(length)d)." @@ -4792,7 +4800,7 @@ msgstr "" "Bitte sicherstellen, dass der Dateiname maximal %(max)d Zeichen hat. (Er hat " "%(length)d)." -#: forms/fields.py:473 +#: forms/fields.py:472 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." @@ -4800,17 +4808,17 @@ msgstr "" "Bitte ein Bild hochladen. Die hochgeladene Datei ist kein Bild oder ist " "defekt." -#: forms/fields.py:596 forms/fields.py:671 +#: forms/fields.py:595 forms/fields.py:670 #, python-format msgid "Select a valid choice. %(value)s is not one of the available choices." msgstr "" "Bitte eine gültige Auswahl treffen. %(value)s ist keine gültige Auswahl." -#: forms/fields.py:672 forms/fields.py:734 forms/models.py:1002 +#: forms/fields.py:671 forms/fields.py:733 forms/models.py:1002 msgid "Enter a list of values." msgstr "Bitte eine Liste mit Werten eingeben." -#: forms/formsets.py:298 forms/formsets.py:300 +#: forms/formsets.py:296 forms/formsets.py:298 msgid "Order" msgstr "Reihenfolge" diff --git a/django/conf/locale/es_AR/LC_MESSAGES/django.mo b/django/conf/locale/es_AR/LC_MESSAGES/django.mo Binary files differindex 1aee7ccc22..6a30b72b4a 100644 --- a/django/conf/locale/es_AR/LC_MESSAGES/django.mo +++ b/django/conf/locale/es_AR/LC_MESSAGES/django.mo diff --git a/django/conf/locale/es_AR/LC_MESSAGES/django.po b/django/conf/locale/es_AR/LC_MESSAGES/django.po index 061cd7d1ba..35c8573e48 100644 --- a/django/conf/locale/es_AR/LC_MESSAGES/django.po +++ b/django/conf/locale/es_AR/LC_MESSAGES/django.po @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-05-11 08:35-0300\n" -"PO-Revision-Date: 2010-05-17 10:52-0300\n" +"POT-Creation-Date: 2010-08-06 15:38-0300\n" +"PO-Revision-Date: 2010-08-11 09:00-0300\n" "Last-Translator: Ramiro <rm0@gmx.net>\n" "Language-Team: Django-I18N <django-i18n@googlegroups.com>\n" "Language: es_AR\n" @@ -70,7 +70,7 @@ msgid "Spanish" msgstr "español" #: conf/global_settings.py:57 -msgid "Argentinean Spanish" +msgid "Argentinian Spanish" msgstr "español de Argentina" #: conf/global_settings.py:58 @@ -166,98 +166,102 @@ msgid "Macedonian" msgstr "macedonio" #: conf/global_settings.py:81 +msgid "Malayalam" +msgstr "Malayalam" + +#: conf/global_settings.py:82 msgid "Mongolian" msgstr "mongol" -#: conf/global_settings.py:82 +#: conf/global_settings.py:83 msgid "Dutch" msgstr "holandés" -#: conf/global_settings.py:83 +#: conf/global_settings.py:84 msgid "Norwegian" msgstr "noruego" -#: conf/global_settings.py:84 +#: conf/global_settings.py:85 msgid "Norwegian Bokmal" msgstr "bokmÃ¥l" -#: conf/global_settings.py:85 +#: conf/global_settings.py:86 msgid "Norwegian Nynorsk" msgstr "nynorsk" -#: conf/global_settings.py:86 +#: conf/global_settings.py:87 msgid "Polish" msgstr "polaco" -#: conf/global_settings.py:87 +#: conf/global_settings.py:88 msgid "Portuguese" msgstr "portugués" -#: conf/global_settings.py:88 +#: conf/global_settings.py:89 msgid "Brazilian Portuguese" msgstr "portugués de Brasil" -#: conf/global_settings.py:89 +#: conf/global_settings.py:90 msgid "Romanian" msgstr "rumano" -#: conf/global_settings.py:90 +#: conf/global_settings.py:91 msgid "Russian" msgstr "ruso" -#: conf/global_settings.py:91 +#: conf/global_settings.py:92 msgid "Slovak" msgstr "eslovaco" -#: conf/global_settings.py:92 +#: conf/global_settings.py:93 msgid "Slovenian" msgstr "esloveno" -#: conf/global_settings.py:93 +#: conf/global_settings.py:94 msgid "Albanian" msgstr "albanés" -#: conf/global_settings.py:94 +#: conf/global_settings.py:95 msgid "Serbian" msgstr "serbio" -#: conf/global_settings.py:95 +#: conf/global_settings.py:96 msgid "Serbian Latin" msgstr "LatÃn de Serbia" -#: conf/global_settings.py:96 +#: conf/global_settings.py:97 msgid "Swedish" msgstr "sueco" -#: conf/global_settings.py:97 +#: conf/global_settings.py:98 msgid "Tamil" msgstr "tamil" -#: conf/global_settings.py:98 +#: conf/global_settings.py:99 msgid "Telugu" msgstr "telugu" -#: conf/global_settings.py:99 +#: conf/global_settings.py:100 msgid "Thai" msgstr "tailandés" -#: conf/global_settings.py:100 +#: conf/global_settings.py:101 msgid "Turkish" msgstr "turco" -#: conf/global_settings.py:101 +#: conf/global_settings.py:102 msgid "Ukrainian" msgstr "ucraniano" -#: conf/global_settings.py:102 +#: conf/global_settings.py:103 msgid "Vietnamese" msgstr "vietnamita" -#: conf/global_settings.py:103 +#: conf/global_settings.py:104 msgid "Simplified Chinese" msgstr "chino simplificado" -#: conf/global_settings.py:104 +#: conf/global_settings.py:105 msgid "Traditional Chinese" msgstr "chino tradicional" @@ -309,15 +313,15 @@ msgstr "Este mes" msgid "This year" msgstr "Este año" -#: contrib/admin/filterspecs.py:147 forms/widgets.py:469 +#: contrib/admin/filterspecs.py:147 forms/widgets.py:478 msgid "Yes" msgstr "SÃ" -#: contrib/admin/filterspecs.py:147 forms/widgets.py:469 +#: contrib/admin/filterspecs.py:147 forms/widgets.py:478 msgid "No" msgstr "No" -#: contrib/admin/filterspecs.py:154 forms/widgets.py:469 +#: contrib/admin/filterspecs.py:154 forms/widgets.py:478 msgid "Unknown" msgstr "Desconocido" @@ -363,7 +367,7 @@ msgid "Changed %s." msgstr "Modifica %s." #: contrib/admin/options.py:559 contrib/admin/options.py:569 -#: contrib/comments/templates/comments/preview.html:16 db/models/base.py:844 +#: contrib/comments/templates/comments/preview.html:16 db/models/base.py:845 #: forms/models.py:568 msgid "and" msgstr "y" @@ -692,7 +696,7 @@ msgid "Filter" msgstr "Filtrar" #: contrib/admin/templates/admin/delete_confirmation.html:10 -#: contrib/admin/templates/admin/submit_line.html:4 forms/formsets.py:302 +#: contrib/admin/templates/admin/submit_line.html:4 forms/formsets.py:300 msgid "Delete" msgstr "Eliminar" @@ -855,13 +859,17 @@ msgstr "Guardar y agregar otro" msgid "Save and continue editing" msgstr "Guardar y continuar editando" -#: contrib/admin/templates/admin/auth/user/add_form.html:5 +#: contrib/admin/templates/admin/auth/user/add_form.html:6 msgid "" "First, enter a username and password. Then, you'll be able to edit more user " "options." msgstr "" -"Primero, introduzca un nombre de usuario y una contraseña. Luego podrá " -"configurar opciones adicionales." +"Primero introduzca un nombre de usuario y una contraseña. Luego podrá " +"configurar opciones adicionales acerca del usuario." + +#: contrib/admin/templates/admin/auth/user/add_form.html:8 +msgid "Enter a username and password." +msgstr "Introduzca un nombre de usuario y una contraseña." #: contrib/admin/templates/admin/auth/user/change_password.html:28 #, python-format @@ -873,7 +881,7 @@ msgstr "" #: contrib/admin/templates/admin/auth/user/change_password.html:35 #: contrib/auth/forms.py:17 contrib/auth/forms.py:61 contrib/auth/forms.py:186 msgid "Password" -msgstr "Contraseña:" +msgstr "Contraseña" #: contrib/admin/templates/admin/auth/user/change_password.html:41 #: contrib/admin/templates/registration/password_change_form.html:37 @@ -1243,7 +1251,7 @@ msgstr "Cambiar contraseña: %s" #: contrib/auth/forms.py:14 contrib/auth/forms.py:48 contrib/auth/forms.py:60 msgid "Username" -msgstr "Nombre de usuario:" +msgstr "Nombre de usuario" #: contrib/auth/forms.py:15 contrib/auth/forms.py:49 msgid "Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only." @@ -1435,8 +1443,8 @@ msgstr "mensaje" msgid "Logged out" msgstr "Sesión cerrada" -#: contrib/auth/management/commands/createsuperuser.py:23 -#: core/validators.py:120 forms/fields.py:428 +#: contrib/auth/management/commands/createsuperuser.py:24 +#: core/validators.py:120 forms/fields.py:427 msgid "Enter a valid e-mail address." msgstr "Introduzca una dirección de correo electrónico válida" @@ -1504,7 +1512,7 @@ msgid "Email address" msgstr "Dirección de correo electrónico" #: contrib/comments/forms.py:95 contrib/flatpages/admin.py:8 -#: contrib/flatpages/models.py:7 db/models/fields/__init__.py:1101 +#: contrib/flatpages/models.py:7 db/models/fields/__init__.py:1109 msgid "URL" msgstr "URL" @@ -1553,7 +1561,7 @@ msgstr "comentario" msgid "date/time submitted" msgstr "fecha/hora de envÃo" -#: contrib/comments/models.py:60 db/models/fields/__init__.py:896 +#: contrib/comments/models.py:60 db/models/fields/__init__.py:904 msgid "IP address" msgstr "Dirección IP" @@ -4503,20 +4511,20 @@ msgstr "sitios" msgid "Enter a valid value." msgstr "Introduzca un valor válido." -#: core/validators.py:87 forms/fields.py:529 +#: core/validators.py:87 forms/fields.py:528 msgid "Enter a valid URL." msgstr "Introduzca una URL válida." -#: core/validators.py:89 forms/fields.py:530 +#: core/validators.py:89 forms/fields.py:529 msgid "This URL appears to be a broken link." msgstr "La URL parece ser un enlace roto." -#: core/validators.py:123 forms/fields.py:873 +#: core/validators.py:123 forms/fields.py:877 msgid "" "Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens." msgstr "Introduzca un 'slug' válido consistente de letras, números o guiones." -#: core/validators.py:126 forms/fields.py:866 +#: core/validators.py:126 forms/fields.py:870 msgid "Enter a valid IPv4 address." msgstr "Introduzca una dirección IPv4 válida" @@ -4528,15 +4536,15 @@ msgstr "Introduzca sólo dÃgitos separados por comas." #, python-format msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." msgstr "" -"Asegúrese de que este valor sea %(limit_value)s (actualmente es %(show_value)" -"s)." +"Asegúrese de que este valor sea %(limit_value)s (actualmente es " +"%(show_value)s)." -#: core/validators.py:153 forms/fields.py:205 forms/fields.py:257 +#: core/validators.py:153 forms/fields.py:204 forms/fields.py:256 #, python-format msgid "Ensure this value is less than or equal to %(limit_value)s." msgstr "Asegúrese de que este valor sea menor o igual a %(limit_value)s." -#: core/validators.py:158 forms/fields.py:206 forms/fields.py:258 +#: core/validators.py:158 forms/fields.py:205 forms/fields.py:257 #, python-format msgid "Ensure this value is greater than or equal to %(limit_value)s." msgstr "Asegúrese de que este valor sea mayor o igual a %(limit_value)s." @@ -4544,8 +4552,8 @@ msgstr "Asegúrese de que este valor sea mayor o igual a %(limit_value)s." #: core/validators.py:164 #, python-format msgid "" -"Ensure this value has at least %(limit_value)d characters (it has %" -"(show_value)d)." +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." msgstr "" "Asegúrese de que este valor tenga al menos %(limit_value)d caracteres (tiene " "%(show_value)d)." @@ -4553,20 +4561,20 @@ msgstr "" #: core/validators.py:170 #, python-format msgid "" -"Ensure this value has at most %(limit_value)d characters (it has %" -"(show_value)d)." +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." msgstr "" "Asegúrese de que este valor tenga como máximo %(limit_value)d caracteres " "(tiene %(show_value)d)." -#: db/models/base.py:822 +#: db/models/base.py:823 #, python-format msgid "%(field_name)s must be unique for %(date_field)s %(lookup)s." msgstr "" "%(field_name)s debe ser único/a para un %(lookup)s %(date_field)s " "determinado." -#: db/models/base.py:837 db/models/base.py:845 +#: db/models/base.py:838 db/models/base.py:846 #, python-format msgid "%(model_name)s with this %(field_label)s already exists." msgstr "Ya existe un/a %(model_name)s con este/a %(field_label)s." @@ -4589,13 +4597,13 @@ msgstr "Este campo no puede estar en blanco." msgid "Field of type: %(field_type)s" msgstr "Campo tipo: %(field_type)s" -#: db/models/fields/__init__.py:451 db/models/fields/__init__.py:852 -#: db/models/fields/__init__.py:961 db/models/fields/__init__.py:972 -#: db/models/fields/__init__.py:999 +#: db/models/fields/__init__.py:451 db/models/fields/__init__.py:860 +#: db/models/fields/__init__.py:969 db/models/fields/__init__.py:980 +#: db/models/fields/__init__.py:1007 msgid "Integer" msgstr "Entero" -#: db/models/fields/__init__.py:455 db/models/fields/__init__.py:850 +#: db/models/fields/__init__.py:455 db/models/fields/__init__.py:858 msgid "This value must be an integer." msgstr "Este valor debe ser un número entero." @@ -4607,7 +4615,7 @@ msgstr "Este valor debe ser True o False." msgid "Boolean (Either True or False)" msgstr "Booleano (Verdadero o Falso)" -#: db/models/fields/__init__.py:539 db/models/fields/__init__.py:982 +#: db/models/fields/__init__.py:539 db/models/fields/__init__.py:990 #, python-format msgid "String (up to %(max_length)s)" msgstr "Cadena (máximo %(max_length)s)" @@ -4651,44 +4659,44 @@ msgstr "Número decimal" msgid "E-mail address" msgstr "Dirección de correo electrónico" -#: db/models/fields/__init__.py:799 db/models/fields/files.py:220 +#: db/models/fields/__init__.py:807 db/models/fields/files.py:220 #: db/models/fields/files.py:331 msgid "File path" msgstr "Ruta de archivo" -#: db/models/fields/__init__.py:822 +#: db/models/fields/__init__.py:830 msgid "This value must be a float." msgstr "Este valor debe ser un valor en representación de punto flotante." -#: db/models/fields/__init__.py:824 +#: db/models/fields/__init__.py:832 msgid "Floating point number" msgstr "Número de punto flotante" -#: db/models/fields/__init__.py:883 +#: db/models/fields/__init__.py:891 msgid "Big (8 byte) integer" msgstr "Entero grande (8 bytes)" -#: db/models/fields/__init__.py:912 +#: db/models/fields/__init__.py:920 msgid "This value must be either None, True or False." msgstr "Este valor debe ser None, True o False." -#: db/models/fields/__init__.py:914 +#: db/models/fields/__init__.py:922 msgid "Boolean (Either True, False or None)" msgstr "Booleano (Verdadero, Falso o Nulo)" -#: db/models/fields/__init__.py:1005 +#: db/models/fields/__init__.py:1013 msgid "Text" msgstr "Texto" -#: db/models/fields/__init__.py:1021 +#: db/models/fields/__init__.py:1029 msgid "Time" msgstr "Hora" -#: db/models/fields/__init__.py:1025 +#: db/models/fields/__init__.py:1033 msgid "Enter a valid time in HH:MM[:ss[.uuuuuu]] format." msgstr "Introduzca un valor de hora válido en formato HH:MM[:ss[.uuuuuu]]." -#: db/models/fields/__init__.py:1109 +#: db/models/fields/__init__.py:1125 msgid "XML text" msgstr "Texto XML" @@ -4701,22 +4709,22 @@ msgstr "No existe un modelo %(model)s con una clave primaria %(pk)r." msgid "Foreign Key (type determined by related field)" msgstr "Clave foránea (el tipo está determinado por el campo relacionado)" -#: db/models/fields/related.py:918 +#: db/models/fields/related.py:919 msgid "One-to-one relationship" msgstr "Relación uno-a-uno" -#: db/models/fields/related.py:980 +#: db/models/fields/related.py:981 msgid "Many-to-many relationship" msgstr "Relación muchos-a-muchos" -#: db/models/fields/related.py:1000 +#: db/models/fields/related.py:1001 msgid "" "Hold down \"Control\", or \"Command\" on a Mac, to select more than one." msgstr "" "Mantenga presionada \"Control\" (\"Command\" en una Mac) para seleccionar " "más de uno." -#: db/models/fields/related.py:1061 +#: db/models/fields/related.py:1062 #, python-format msgid "Please enter valid %(self)s IDs. The value %(value)r is invalid." msgid_plural "" @@ -4732,55 +4740,55 @@ msgstr[1] "" msgid "This field is required." msgstr "Este campo es obligatorio." -#: forms/fields.py:204 +#: forms/fields.py:203 msgid "Enter a whole number." msgstr "Introduzca un número entero." -#: forms/fields.py:235 forms/fields.py:256 +#: forms/fields.py:234 forms/fields.py:255 msgid "Enter a number." msgstr "Introduzca un número." -#: forms/fields.py:259 +#: forms/fields.py:258 #, python-format msgid "Ensure that there are no more than %s digits in total." msgstr "Asegúrese de que no existan en total mas de %s dÃgitos." -#: forms/fields.py:260 +#: forms/fields.py:259 #, python-format msgid "Ensure that there are no more than %s decimal places." msgstr "Asegúrese de que no existan mas de %s lugares decimales." -#: forms/fields.py:261 +#: forms/fields.py:260 #, python-format msgid "Ensure that there are no more than %s digits before the decimal point." msgstr "Asegúrese de que no existan mas de %s dÃgitos antes del punto decimal." -#: forms/fields.py:323 forms/fields.py:838 +#: forms/fields.py:322 forms/fields.py:837 msgid "Enter a valid date." msgstr "Introduzca una fecha válida." -#: forms/fields.py:351 forms/fields.py:839 +#: forms/fields.py:350 forms/fields.py:838 msgid "Enter a valid time." msgstr "Introduzca un valor de hora válido." -#: forms/fields.py:377 +#: forms/fields.py:376 msgid "Enter a valid date/time." msgstr "Introduzca un valor de fecha/hora válido." -#: forms/fields.py:435 +#: forms/fields.py:434 msgid "No file was submitted. Check the encoding type on the form." msgstr "" "No se envió un archivo. Verifique el tipo de codificación en el formulario." -#: forms/fields.py:436 +#: forms/fields.py:435 msgid "No file was submitted." msgstr "No se envió ningún archivo." -#: forms/fields.py:437 +#: forms/fields.py:436 msgid "The submitted file is empty." msgstr "El archivo enviado está vacÃo." -#: forms/fields.py:438 +#: forms/fields.py:437 #, python-format msgid "" "Ensure this filename has at most %(max)d characters (it has %(length)d)." @@ -4788,7 +4796,7 @@ msgstr "" "Asegúrese de que este nombre de archivo tenga como máximo %(max)d caracteres " "(tiene %(length)d)." -#: forms/fields.py:473 +#: forms/fields.py:472 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." @@ -4796,18 +4804,18 @@ msgstr "" "Seleccione una imagen válida. El archivo que ha seleccionado no es una " "imagen o es un un archivo de imagen corrupto." -#: forms/fields.py:596 forms/fields.py:671 +#: forms/fields.py:595 forms/fields.py:670 #, python-format msgid "Select a valid choice. %(value)s is not one of the available choices." msgstr "" "Seleccione una opción válida. %(value)s no es una de las opciones " "disponibles." -#: forms/fields.py:672 forms/fields.py:734 forms/models.py:1002 +#: forms/fields.py:671 forms/fields.py:733 forms/models.py:1002 msgid "Enter a list of values." msgstr "Introduzca una lista de valores." -#: forms/formsets.py:298 forms/formsets.py:300 +#: forms/formsets.py:296 forms/formsets.py:298 msgid "Order" msgstr "Ordenar" diff --git a/django/conf/locale/es_AR/LC_MESSAGES/djangojs.mo b/django/conf/locale/es_AR/LC_MESSAGES/djangojs.mo Binary files differindex f894397efb..e69f6fe95d 100644 --- a/django/conf/locale/es_AR/LC_MESSAGES/djangojs.mo +++ b/django/conf/locale/es_AR/LC_MESSAGES/djangojs.mo diff --git a/django/conf/locale/es_AR/LC_MESSAGES/djangojs.po b/django/conf/locale/es_AR/LC_MESSAGES/djangojs.po index 5f6278533a..387449ecc2 100644 --- a/django/conf/locale/es_AR/LC_MESSAGES/djangojs.po +++ b/django/conf/locale/es_AR/LC_MESSAGES/djangojs.po @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-05-04 22:01-0300\n" -"PO-Revision-Date: 2010-05-04 22:10-0300\n" +"POT-Creation-Date: 2010-08-06 15:47-0300\n" +"PO-Revision-Date: 2010-08-06 15:52-0300\n" "Last-Translator: Ramiro Morales <rm0@gmx.net>\n" "Language-Team: Django-I18N <django-i18n@googlegroups.com>\n" "Language: es_AR\n" @@ -17,45 +17,17 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Pootle 2.0.1\n" -#: contrib/admin/media/js/SelectFilter2.js:37 -#, perl-format -msgid "Available %s" -msgstr "%s disponibles" - -#: contrib/admin/media/js/SelectFilter2.js:45 -msgid "Choose all" -msgstr "Seleccionar todos" - -#: contrib/admin/media/js/SelectFilter2.js:50 -msgid "Add" -msgstr "Agregar" - -#: contrib/admin/media/js/SelectFilter2.js:52 -msgid "Remove" -msgstr "Eliminar" - -#: contrib/admin/media/js/SelectFilter2.js:57 -#, perl-format -msgid "Chosen %s" -msgstr "%s elegidos" - -#: contrib/admin/media/js/SelectFilter2.js:58 -msgid "Select your choice(s) and click " -msgstr "Seleccione los items a agregar y haga click en " - #: contrib/admin/media/js/SelectFilter2.js:63 msgid "Clear all" msgstr "Eliminar todos" #: contrib/admin/media/js/actions.js:18 -#: contrib/admin/media/js/actions.min.js:1 msgid "%(sel)s of %(cnt)s selected" msgid_plural "%(sel)s of %(cnt)s selected" msgstr[0] "%(sel)s de %(cnt)s seleccionado/a" msgstr[1] "%(sel)s de %(cnt)s seleccionados/as" #: contrib/admin/media/js/actions.js:109 -#: contrib/admin/media/js/actions.min.js:5 msgid "" "You have unsaved changes on individual editable fields. If you run an " "action, your unsaved changes will be lost." diff --git a/django/conf/locale/eu/LC_MESSAGES/django.mo b/django/conf/locale/eu/LC_MESSAGES/django.mo Binary files differindex c19c32116f..ab3f8fc84f 100644 --- a/django/conf/locale/eu/LC_MESSAGES/django.mo +++ b/django/conf/locale/eu/LC_MESSAGES/django.mo diff --git a/django/conf/locale/eu/LC_MESSAGES/django.po b/django/conf/locale/eu/LC_MESSAGES/django.po index 37a829d1ca..b96f7e4bfc 100644 --- a/django/conf/locale/eu/LC_MESSAGES/django.po +++ b/django/conf/locale/eu/LC_MESSAGES/django.po @@ -7,13 +7,13 @@ msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2008-02-08 19:37+0100\n" -"PO-Revision-Date: 2008-02-14 22:12+0100\n" -"Last-Translator: Ibon\n" +"PO-Revision-Date: 2010-07-08 12:55+0200\n" +"Last-Translator: Aitzol Naberan <anaberan@codesyntax.com>\n" "Language-Team: <es@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: KBabel 1.11.4\n" +"X-Generator: emacs\n" #: conf/global_settings.py:39 msgid "Arabic" @@ -53,7 +53,7 @@ msgstr "Greziera" #: conf/global_settings.py:48 msgid "English" -msgstr "Ingeles" +msgstr "Ingelesa" #: conf/global_settings.py:49 msgid "Spanish" @@ -69,7 +69,7 @@ msgstr "Persiera" #: conf/global_settings.py:52 msgid "Finnish" -msgstr "Finlandesa" +msgstr "Finlandiera" #: conf/global_settings.py:53 msgid "French" @@ -81,11 +81,11 @@ msgstr "Gaelikoa" #: conf/global_settings.py:55 msgid "Galician" -msgstr "Galiziarra" +msgstr "Galiziera" #: conf/global_settings.py:56 msgid "Hungarian" -msgstr "Hungarierra" +msgstr "Hungariera" #: conf/global_settings.py:57 msgid "Hebrew" @@ -129,7 +129,7 @@ msgstr "Mazedoniera" #: conf/global_settings.py:67 msgid "Dutch" -msgstr "Holandesa" +msgstr "Holandera" #: conf/global_settings.py:68 msgid "Norwegian" @@ -246,15 +246,15 @@ msgstr "Ezezaguna" #: contrib/admin/models.py:18 msgid "action time" -msgstr "Ekintz hordua" +msgstr "Ekintza hordua" #: contrib/admin/models.py:21 msgid "object id" -msgstr "Objetuaren id" +msgstr "Objetuaren id-a" #: contrib/admin/models.py:22 msgid "object repr" -msgstr "Objeturaren repr" +msgstr "Objeturaren aurkezpena" #: contrib/admin/models.py:23 msgid "action flag" @@ -275,11 +275,11 @@ msgstr "log sarrerak" #: contrib/admin/templates/admin/404.html:4 #: contrib/admin/templates/admin/404.html:8 msgid "Page not found" -msgstr "Ez da lekua aurkitu" +msgstr "Ez da orririk aurkitu" #: contrib/admin/templates/admin/404.html:10 msgid "We're sorry, but the requested page could not be found." -msgstr "Barkatu, eskatutako lekua ezin daiteke aurkitu" +msgstr "Barkatu, eskatutako orria ezin daiteke aurkitu" #: contrib/admin/templates/admin/500.html:4 #: contrib/admin/templates/admin/base.html:37 @@ -300,21 +300,21 @@ msgstr "Hasiera" #: contrib/admin/templates/admin/500.html:4 msgid "Server error" -msgstr "Serbidore errorea" +msgstr "Zerbitzari errorea" #: contrib/admin/templates/admin/500.html:6 msgid "Server error (500)" -msgstr "Serbidor errorea (500)" +msgstr "Zerbitzari errorea (500)" #: contrib/admin/templates/admin/500.html:9 msgid "Server Error <em>(500)</em>" -msgstr "Serbidore Errorea <em>(500)</em>" +msgstr "Zerbitzari Errorea <em>(500)</em>" #: contrib/admin/templates/admin/500.html:10 msgid "" "There's been an error. It's been reported to the site administrators via e-" "mail and should be fixed shortly. Thanks for your patience." -msgstr "Arazo bat izan da. Web guneraren administradorea e-mail bidez ohartuko da eta laister konpondua egon beharko luke. Barkatu arazoak." +msgstr "Arazo bat izan da. Web guneraren administradoreri abisu email bat bidali zaio eta laister konpondua egon beharko luke. Barkatu eragozpenak." #: contrib/admin/templates/admin/base.html:26 msgid "Welcome," @@ -329,7 +329,7 @@ msgstr "Dokumentazioa" #: contrib/admin/templates/admin/auth/user/change_password.html:14 #: contrib/admin/templates/admin/auth/user/change_password.html:45 msgid "Change password" -msgstr "Hitz ezkutua aldatu" +msgstr "Pasahitza aldatu" #: contrib/admin/templates/admin/base.html:30 #: contrib/comments/templates/comments/form.html:6 @@ -338,11 +338,11 @@ msgstr "Atera" #: contrib/admin/templates/admin/base_site.html:4 msgid "Django site admin" -msgstr "Django admin. gunea" +msgstr "Django kudeaketa gunea" #: contrib/admin/templates/admin/base_site.html:7 msgid "Django administration" -msgstr "Django administradorea" +msgstr "Django kudeaketa" #: contrib/admin/templates/admin/change_form.html:14 #: contrib/admin/templates/admin/index.html:28 @@ -356,18 +356,18 @@ msgstr "Aurrekoak" #: contrib/admin/templates/admin/change_form.html:21 msgid "View on site" -msgstr "Lekuaren bista" +msgstr "Webgunean ikusi" #: contrib/admin/templates/admin/change_form.html:31 #: contrib/admin/templates/admin/auth/user/change_password.html:23 msgid "Please correct the error below." msgid_plural "Please correct the errors below." -msgstr[0] "Arren zuzendu ondoko akatsa" -msgstr[1] "Arren zuzendu ondoko akatsak" +msgstr[0] "Arren zuzendu akatsa" +msgstr[1] "Arren zuzendu akatsak" #: contrib/admin/templates/admin/change_form.html:49 msgid "Ordering" -msgstr "Ordenaketa" +msgstr "Ordenazioa" #: contrib/admin/templates/admin/change_form.html:52 msgid "Order:" @@ -389,20 +389,14 @@ msgid "" "Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " "related objects, but your account doesn't have permission to delete the " "following types of objects:" -msgstr "" -"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " -"related objects, but your account doesn't have permission to delete the " -"following types of objects: " -"%(object_name)s '%(escaped_object)s' ezabatuz, erlazionatutako objetuak ere ezabatuko ditu, baina zure kontuak ez du baimenik, hurrengo objetuak ezabatzeko:" +msgstr "%(object_name)s ezabatzean bere '%(escaped_object)s' ere ezabatzen dira, baina zure kontuak ez dauka baimenik objetu mota hauek ezabatzeko:" #: contrib/admin/templates/admin/delete_confirmation.html:20 #, python-format msgid "" "Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " "All of the following related items will be deleted:" -msgstr "" -"Ziur zaude %(object_name)s \"%(escaped_object)s\" ezabatu nai duzula ? " -"Erlazionaturik dauden hurrengo elementuak ere ezabatuko dira:" +msgstr "Ziur zaude %(object_name)s \"%(escaped_object)s\" ezabatu nahi dituzula? Erlazionaturik dauden hurrengo elementuak ere ezabatuko dira:" #: contrib/admin/templates/admin/delete_confirmation.html:25 msgid "Yes, I'm sure" @@ -418,9 +412,9 @@ msgid "Filter" msgstr "Filtroa" #: contrib/admin/templates/admin/index.html:17 -#, python-format +#, python-format, fuzzy msgid "Models available in the %(name)s application." -msgstr "%(name)s aplikazioan Modeloak." +msgstr "%(name)s aplikazioan eskuragarri dauden modeloak." #: contrib/admin/templates/admin/index.html:18 #, python-format @@ -433,7 +427,7 @@ msgstr "Aldatu" #: contrib/admin/templates/admin/index.html:44 msgid "You don't have permission to edit anything." -msgstr "Ezer aldatzeko baimenik ez dezu." +msgstr "Ezer aldatzeko baimenik ez duzu." #: contrib/admin/templates/admin/index.html:52 msgid "Recent Actions" @@ -452,18 +446,18 @@ msgid "" "Something's wrong with your database installation. Make sure the appropriate " "database tables have been created, and make sure the database is readable by " "the appropriate user." -msgstr "Zerbait gaizki dago zure data-basearekin. Ziurtatu ezazu data-baseko taulak sortuak izan direla eta usuario egokiarengatik irakurriak izan daitekela" +msgstr "Zerbait gaizki dago zure data-basearekin. Ziurtatu ezazu datu-baseko taulak sortu direla eta erabiltzaile egokiak duela irakurketa baimena" #: contrib/admin/templates/admin/login.html:17 #: contrib/comments/templates/comments/form.html:6 #: contrib/comments/templates/comments/form.html:8 msgid "Username:" -msgstr "Usuario Izena:" +msgstr "Erabiltzaile izena:" #: contrib/admin/templates/admin/login.html:20 #: contrib/comments/templates/comments/form.html:8 msgid "Password:" -msgstr "Hitz ezkutua:" +msgstr "Pasahitza:" #: contrib/admin/templates/admin/login.html:25 #: contrib/admin/views/decorators.py:25 @@ -476,7 +470,7 @@ msgstr "Data/ordua" #: contrib/admin/templates/admin/object_history.html:18 msgid "User" -msgstr "Usuarioa" +msgstr "Erabiltzailea" #: contrib/admin/templates/admin/object_history.html:19 msgid "Action" @@ -490,7 +484,7 @@ msgstr "" msgid "" "This object doesn't have a change history. It probably wasn't added via this " "admin site." -msgstr "Objetu honek ez du alketa zerrenda bat. Ziur asko, administrazio leku hau erabili gabe gehitua izan da." +msgstr "Objetu honek ez dauka aldaketa zerrendarik. Ziur asko, kudeaketa gunetik kanpo gehituko zen." #: contrib/admin/templates/admin/pagination.html:10 msgid "Show all" @@ -532,31 +526,31 @@ msgstr "Gorde" msgid "" "First, enter a username and password. Then, you'll be able to edit more user " "options." -msgstr "Lehenengo usuario izena eta hitz ezkutua idatzi. Gero usuarioaren aukera gehiago aldatu ditzakezu" +msgstr "Lehenengo erabiltzaile izena eta pasahitza idatzi. Gero erabiltzaile aukera gehiago aldatzeko aukera gehiago izango duzu" #: contrib/admin/templates/admin/auth/user/add_form.html:12 msgid "Username" -msgstr "Usuario izena" +msgstr "Erabiltzaile izena" #: contrib/admin/templates/admin/auth/user/add_form.html:18 #: contrib/admin/templates/admin/auth/user/change_password.html:33 msgid "Password" -msgstr "Hitz ezkutua" +msgstr "Pasahitza" #: contrib/admin/templates/admin/auth/user/add_form.html:23 #: contrib/admin/templates/admin/auth/user/change_password.html:38 msgid "Password (again)" -msgstr "Hitz ezkutua (berriro)" +msgstr "Pasahitza (berriro)" #: contrib/admin/templates/admin/auth/user/add_form.html:24 #: contrib/admin/templates/admin/auth/user/change_password.html:39 msgid "Enter the same password as above, for verification." -msgstr "Idatzi berriro hitz ezkutua." +msgstr "Idatzi berriro pasahitza." #: contrib/admin/templates/admin/auth/user/change_password.html:27 #, python-format msgid "Enter a new password for the user <strong>%(username)s</strong>." -msgstr "Hitz ezkutu berria idatzi <strong>%(username)s</strong> usuarioarentzat." +msgstr "Pasahitz berria idatzi <strong>%(username)s</strong> erabiltzailearentzat." #: contrib/admin/templates/admin_doc/bookmarklets.html:3 msgid "Bookmarklets" @@ -577,20 +571,18 @@ msgid "" "your computer is \"internal\").</p>\n" msgstr "" "\n" -"<p class=\"help\">Markadoreak instalatzeko eraman linka zure erreminta panelera (toolbar).\n" -"Orain markadorea lekuko edozein horritik aukeratu dezakezu. Hauetako \n" -"markadore batzu, zure ordenadorea 'barnekoa' bezala erregistratua egotea\n" -"behar dute. Hitzegin web lekuaren administradorearekin azalpen gehiagorako.</p>\n" +"<p class=\"help\">Markadoreak instalatzeko eraman linka zure erreminta panelera (toolbar), edo eskuineko botoiarekin klik egin eta gehitu erreminta panelera. Kontuan izan, markadore hauetako batzuk, exekutatzen ari diren ordenagailua 'barneko' gisa markatua egotea behar dutela (hitzagin zure sistema kudeatzailearekin zure ordenagailua 'barnekoa' den edo ez argitzeko)\n" +".</p>\n" #: contrib/admin/templates/admin_doc/bookmarklets.html:18 msgid "Documentation for this page" -msgstr "Web horri honentzat dokumentazioa" +msgstr "Web orri honen dokumentazioa" #: contrib/admin/templates/admin_doc/bookmarklets.html:19 msgid "" "Jumps you from any page to the documentation for the view that generates " "that page." -msgstr "Edozein lekutik, horriaren bista sortzaileara (view) salto egiten du." +msgstr "Edozein orritik, orria sortzen duen bistaren dokumentaziora eramango zaitu." #: contrib/admin/templates/admin_doc/bookmarklets.html:21 msgid "Show object ID" @@ -600,23 +592,23 @@ msgstr "Objetuaren ID erakutsi" msgid "" "Shows the content-type and unique ID for pages that represent a single " "object." -msgstr "Objetu bakarra erakusten duten horrietan, eduki mota (content-type) eta horriaren ID bakarra erakutsi, ." +msgstr "Objetu bakarra erakusten duten orrietan, eduki mota (content-type) eta orriaren ID bakarra erakutsi, ." #: contrib/admin/templates/admin_doc/bookmarklets.html:24 msgid "Edit this object (current window)" -msgstr "Objetu hau aldatu ( leiho hau)" +msgstr "Objetu hau aldatu ( leiho honetan)" #: contrib/admin/templates/admin_doc/bookmarklets.html:25 msgid "Jumps to the admin page for pages that represent a single object." -msgstr "Objetu bakarra erakusten horrietan, adminiztrazio gunera joan." +msgstr "Objetu bakarra erakusten orrietan, adminiztrazio gunera joan." #: contrib/admin/templates/admin_doc/bookmarklets.html:27 msgid "Edit this object (new window)" -msgstr "Objetu hay aldatu (leiho berria)" +msgstr "Objetu hau aldatu (leiho berrian)" #: contrib/admin/templates/admin_doc/bookmarklets.html:28 msgid "As above, but opens the admin page in a new window." -msgstr "Goian bezala baina administrzazio gune leio berrian irekitzen." +msgstr "Goian bezala, baina kudeaketa guena leiho berri batean irekiko da." #: contrib/admin/templates/registration/logged_out.html:8 msgid "Thanks for spending some quality time with the Web site today." @@ -624,85 +616,85 @@ msgstr "Mila esker gaur zure denbora web gunean erabiltzegatik." #: contrib/admin/templates/registration/logged_out.html:10 msgid "Log in again" -msgstr "Berrio sartu (log in)" +msgstr "Sartu berriro" #: contrib/admin/templates/registration/password_change_done.html:3 #: contrib/admin/templates/registration/password_change_form.html:3 #: contrib/admin/templates/registration/password_change_form.html:5 #: contrib/admin/templates/registration/password_change_form.html:9 msgid "Password change" -msgstr "Hitz ezkutua aldatu" +msgstr "Pasahitza aldatu" #: contrib/admin/templates/registration/password_change_done.html:5 #: contrib/admin/templates/registration/password_change_done.html:9 msgid "Password change successful" -msgstr "Hitz ezkutuaren aldaketa arrakastatsua" +msgstr "Pasahitza ondo aldatu da" #: contrib/admin/templates/registration/password_change_done.html:11 msgid "Your password was changed." -msgstr "Hitz ezkutua aldatua izan da" +msgstr "Pasahitza aldatu da" #: contrib/admin/templates/registration/password_change_form.html:11 msgid "" "Please enter your old password, for security's sake, and then enter your new " "password twice so we can verify you typed it in correctly." -msgstr "Idatzi hitz ezkutu zaharra segurtasun arrazoiengatik eta gero hitz ezkutu berria bi aldiz, akatsik egiten ez duzula ziurtatu dezagun." +msgstr "Idatzi pasahitz zaharra segurtasun arrazoiengatik eta gero pasahitz berria bi aldiz, akatsik egiten ez duzula ziurtatu dezagun." #: contrib/admin/templates/registration/password_change_form.html:16 msgid "Old password:" -msgstr "Hitz ezkutu zaharra" +msgstr "Pasahitz zaharra" #: contrib/admin/templates/registration/password_change_form.html:18 msgid "New password:" -msgstr "Hitz ezkutu berria:" +msgstr "Pasahitz berria:" #: contrib/admin/templates/registration/password_change_form.html:20 msgid "Confirm password:" -msgstr "Hitz ezkutua baieztatu:" +msgstr "Pasahitza baieztatu:" #: contrib/admin/templates/registration/password_change_form.html:22 msgid "Change my password" -msgstr "Nire hitz ezkutua aldatu" +msgstr "Nire pasahitza aldatu" #: contrib/admin/templates/registration/password_reset_done.html:4 #: 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 msgid "Password reset" -msgstr "Hitz ezkutua ezabatu" +msgstr "Pasahitza berrezarri" #: contrib/admin/templates/registration/password_reset_done.html:6 #: contrib/admin/templates/registration/password_reset_done.html:10 msgid "Password reset successful" -msgstr "Hitz ezkutuaren ezabaketa zuzena" +msgstr "Pasahitza ondo berrezarri da" #: 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 "Hitz ezkutu berria e-mail bidez bidali dizugu. Epe laburrean jasokor duzu." +msgstr "Pasahitz berria e-mail bidez bidali dizugu. Epe laburrean jasoko duzu." #: contrib/admin/templates/registration/password_reset_email.html:2 msgid "You're receiving this e-mail because you requested a password reset" -msgstr "Mezu elektroniko hau jaso dezu, hitz ezkutuaren ezabaketa eskatu duzulako" +msgstr "Mezu elektroniko hau jaso duzu pasahitzaren berrezarketa eskatu duzulako" #: contrib/admin/templates/registration/password_reset_email.html:3 #, python-format msgid "for your user account at %(site_name)s" -msgstr "%(site_name)s usuario kontuarentzat" +msgstr "%(site_name)s erabiltzaile kontuarentzat" #: contrib/admin/templates/registration/password_reset_email.html:5 #, python-format msgid "Your new password is: %(new_password)s" -msgstr "Zure hitz ezkutu berria %(new_password)s da" +msgstr "Zure pasahitz berria %(new_password)s da" #: contrib/admin/templates/registration/password_reset_email.html:7 msgid "Feel free to change this password by going to this page:" -msgstr "Hitz ezkutua aldatu nai baduzu, zoaz web horri honetara joan:" +msgstr "Pasahitza aldatu dezakezu orri honetan:" #: contrib/admin/templates/registration/password_reset_email.html:11 msgid "Your username, in case you've forgotten:" -msgstr "Zure usuario izena (ahaztu badezu):" +msgstr "Zure erabiltzaile izena (ahaztu badezu):" #: contrib/admin/templates/registration/password_reset_email.html:13 msgid "Thanks for using our site!" @@ -711,13 +703,13 @@ msgstr "Mila esker gure web gunea erabiltzeagatik!" #: contrib/admin/templates/registration/password_reset_email.html:15 #, python-format msgid "The %(site_name)s team" -msgstr "%(site_name)s web gunearen taldea" +msgstr "%(site_name)s web guneko taldea" #: contrib/admin/templates/registration/password_reset_form.html:12 msgid "" "Forgotten your password? Enter your e-mail address below, and we'll reset " "your password and e-mail the new one to you." -msgstr "Hitz ezkutua ahaztu dezu ? Hidatzi zure e-mail helbidea, eta hitz ezkutu berri bat bidaliko dizugu." +msgstr "Pasahitza ahaztu duzu? Idatzi zure e-mail helbidea, eta pasahitz berri bat bidaliko dizugu." #: contrib/admin/templates/registration/password_reset_form.html:16 msgid "E-mail address:" @@ -725,7 +717,7 @@ msgstr "E-mail helbidea:" #: contrib/admin/templates/registration/password_reset_form.html:16 msgid "Reset my password" -msgstr "Hitz ezkutua ezabatu" +msgstr "Pasahitza berrezarri" #: contrib/admin/templates/widget/date_time.html:3 msgid "Date:" @@ -750,7 +742,7 @@ msgstr "Data guztiak" #: contrib/admin/views/auth.py:20 contrib/admin/views/main.py:267 #, python-format msgid "The %(name)s \"%(obj)s\" was added successfully." -msgstr "%(name)s \"%(obj)s arrakastaz gehitua izan da." +msgstr "%(name)s \"%(obj)s\" ondo gehitu da." #: contrib/admin/views/auth.py:25 contrib/admin/views/main.py:271 #: contrib/admin/views/main.py:356 @@ -759,48 +751,48 @@ msgstr "Berriro alkatu zenezake beherago." #: contrib/admin/views/auth.py:31 msgid "Add user" -msgstr "Usuario gehitu" +msgstr "Erabiltzailea gehitu" #: contrib/admin/views/auth.py:58 msgid "Password changed successfully." -msgstr "Hitz ezkutua aldaketa zuzena." +msgstr "Ondo aldatu da pasahitza." #: contrib/admin/views/auth.py:65 #, python-format msgid "Change password: %s" -msgstr "Hitz ezkutua aldatu: %s" +msgstr "Pasahitza aldatu: %s" #: contrib/admin/views/decorators.py:11 contrib/auth/forms.py:60 msgid "" "Please enter a correct username and password. Note that both fields are case-" "sensitive." -msgstr "Mesedez idatzi usuario izena eta hitz ezkutu egokiak. Maiskula eta minuskula ondo bereiztu." +msgstr "Mesedez idatzi erabiltzaile izen eta pasahitz egokiak. Maiskula eta minuskulak ondo bereiztu." #: contrib/admin/views/decorators.py:63 msgid "" "Please log in again, because your session has expired. Don't worry: Your " "submission has been saved." -msgstr "Mesedez berriro sar saitez, sesioa zahartu bai da. Bidalitakoa gorde egin da." +msgstr "Sesioa iraungitu da. Mesedez sar zaitez berriro. Ez arduratu, bidalitakoa gorde da." #: contrib/admin/views/decorators.py:70 msgid "" "Looks like your browser isn't configured to accept cookies. Please enable " "cookies, reload this page, and try again." -msgstr "Zure nabegatzaileak ez ditu cookie-ak onartzen. Aktiba ezazu hauen erabilera eta ekarri ezazu horria hau berriro." +msgstr "Zure nabigatzaileak ez ditu cookie-ak onartzen. Aktiba ezazu hauen erabilera eta eskatu ezazu orri hau berriro." #: contrib/admin/views/decorators.py:84 msgid "Usernames cannot contain the '@' character." -msgstr "Usuario izenek ezin dezkete @ karakterea eduki." +msgstr "Erabiltzaile izenek ezin dezkete '@' karakterea eduki." #: contrib/admin/views/decorators.py:86 #, python-format msgid "Your e-mail address is not your username. Try '%s' instead." -msgstr "Zure e-mail helbidea ez da zure usuario izena. Ahalegindo '%s' rekin." +msgstr "Zure e-mail helbidea ez da zure erabiltzaile izena. Saiatu '%s' rekin." #: contrib/admin/views/doc.py:48 contrib/admin/views/doc.py:50 #: contrib/admin/views/doc.py:52 msgid "tag:" -msgstr "tag:" +msgstr "etiketa:" #: contrib/admin/views/doc.py:79 contrib/admin/views/doc.py:81 #: contrib/admin/views/doc.py:83 @@ -825,7 +817,7 @@ msgstr "%(name)r modeloa ez da %(label)r aplikazioan aurkitu" #: contrib/admin/views/doc.py:185 #, python-format msgid "the related `%(label)s.%(type)s` object" -msgstr "erlazionaturik `%(label)s.%(type)s` objetua" +msgstr "erlazionatutako `%(label)s.%(type)s` objetua" #: contrib/admin/views/doc.py:185 contrib/admin/views/doc.py:207 #: contrib/admin/views/doc.py:221 contrib/admin/views/doc.py:226 @@ -835,7 +827,7 @@ msgstr "modeloa:" #: contrib/admin/views/doc.py:216 #, python-format msgid "related `%(label)s.%(name)s` objects" -msgstr "erlazionaturik `%(label)s.%(name)s` objetua" +msgstr "erlazionatutako `%(label)s.%(name)s` objetua" #: contrib/admin/views/doc.py:221 #, python-format @@ -850,7 +842,7 @@ msgstr "%s zenbakia" #: contrib/admin/views/doc.py:231 #, python-format msgid "Fields on %s objects" -msgstr "%s objetuan zutabeak" +msgstr "%s objetuaren eremuak" #: contrib/admin/views/doc.py:293 contrib/admin/views/doc.py:304 #: contrib/admin/views/doc.py:306 contrib/admin/views/doc.py:312 @@ -869,7 +861,7 @@ msgstr "Katea (%(max_length)s gehienez)" #: contrib/admin/views/doc.py:296 msgid "Comma-separated integers" -msgstr "Komaz bereiztutako zenbako osoak" +msgstr "Komaz bereiztutako zenbaki osoak" #: contrib/admin/views/doc.py:297 msgid "Date (without time)" @@ -881,7 +873,7 @@ msgstr "Data (orduarekin)" #: contrib/admin/views/doc.py:299 msgid "Decimal number" -msgstr "Zenbaki dezimala" +msgstr "Zenbaki hamartarra" #: contrib/admin/views/doc.py:300 msgid "E-mail address" @@ -890,7 +882,7 @@ msgstr "E-mail helbidea" #: contrib/admin/views/doc.py:301 contrib/admin/views/doc.py:302 #: contrib/admin/views/doc.py:305 msgid "File path" -msgstr "Fitxegi bidea (path)" +msgstr "Fitxegi bidea" #: contrib/admin/views/doc.py:303 msgid "Floating point number" @@ -906,7 +898,7 @@ msgstr "Boolearra (egia, gezurra edo hutsa[None])" #: contrib/admin/views/doc.py:310 msgid "Relation to parent model" -msgstr "Gurazo modeloarekin erlazioa" +msgstr "Guraso modeloarekin erlazioa" #: contrib/admin/views/doc.py:311 msgid "Phone number" @@ -926,11 +918,11 @@ msgstr "URL" #: contrib/admin/views/doc.py:319 msgid "U.S. state (two uppercase letters)" -msgstr "U.S statua (bi letra maiuskula)" +msgstr "AEB estatua (bi letra maiuskula)" #: contrib/admin/views/doc.py:320 msgid "XML text" -msgstr "XML textua" +msgstr "XML testua" #: contrib/admin/views/doc.py:346 #, python-format @@ -939,7 +931,7 @@ msgstr "%s ez dirudi url heredu objetua" #: contrib/admin/views/main.py:233 msgid "Site administration" -msgstr "Web gunearen administrazioa" +msgstr "Web gunearen kudeaketa" #: contrib/admin/views/main.py:280 contrib/admin/views/main.py:365 #, python-format @@ -974,17 +966,17 @@ msgstr "%s ezabatuta." #: contrib/admin/views/main.py:351 msgid "No fields changed." -msgstr "Ez da zutaberik aldatu." +msgstr "Ez da eremurik aldatu." #: contrib/admin/views/main.py:354 #, python-format msgid "The %(name)s \"%(obj)s\" was changed successfully." -msgstr "%(name)s \"%(obj)s\" aldatuta izan da." +msgstr "%(name)s \"%(obj)s\" ondo aldatu da." #: contrib/admin/views/main.py:362 #, python-format msgid "The %(name)s \"%(obj)s\" was added successfully. You may edit it again below." -msgstr "%(name)s \"%(obj)s\" arrakastarekin gehituta izan da. Jarraian aldatu dezakezu berriro." +msgstr "%(name)s \"%(obj)s\" ondo gehitu da. Jarraian aldatu dezakezu berriro." #: contrib/admin/views/main.py:400 #, python-format @@ -1004,7 +996,7 @@ msgstr "%(fieldname)s bat edo gehiago %(name)s n" #: contrib/admin/views/main.py:524 #, python-format msgid "The %(name)s \"%(obj)s\" was deleted successfully." -msgstr "%(name)s \"%(obj)s\" arrakastaz ezabatua izan da." +msgstr "%(name)s \"%(obj)s\" ondo ezabatu da." #: contrib/admin/views/main.py:527 msgid "Are you sure?" @@ -1023,7 +1015,7 @@ msgstr "%s aukeratu" #: contrib/admin/views/main.py:583 #, python-format msgid "Select %s to change" -msgstr "aldaketarako %s aukeratu" +msgstr "Aldaketarako %s aukeratu" #: contrib/admin/views/main.py:784 msgid "Database error" @@ -1031,40 +1023,41 @@ msgstr "Data base errorea" #: contrib/auth/forms.py:17 contrib/auth/forms.py:138 msgid "The two password fields didn't match." -msgstr "Hitz ezkutuak ez dira berdinak." +msgstr "Pasahitzak ez datoz bat." #: contrib/auth/forms.py:25 msgid "A user with that username already exists." -msgstr "Usuario izen hori erabiltzen ari da." +msgstr "Erabiltzaile izen hori ez dago eskuragarri." #: contrib/auth/forms.py:53 msgid "" "Your Web browser doesn't appear to have cookies enabled. Cookies are " "required for logging in." -msgstr "Zure nabegatzaileak ez ditu cookiak onartzen. Cookia-k beharrezkoak dira sistemn sartzeko." +msgstr "Zure nabigatzaileak ez ditu cookiak onartzen. Cookia-k beharrezkoak dira sisteman sartzeko." #: contrib/auth/forms.py:62 msgid "This account is inactive." -msgstr "Kontu hau az dago aktibatuta." +msgstr "Kontu hau az dago aktibo." #: contrib/auth/forms.py:84 msgid "" "That e-mail address doesn't have an associated user account. Are you sure " "you've registered?" -msgstr "E-mail helbide horrek ez du usariorik. Ziur al zaude erregistraturik zaudela?" +msgstr "E-mail helbide horrek ez du lotutako erabiltzailerik. Ziur al zaude erregistratu duzula?" #: contrib/auth/forms.py:107 #, python-format msgid "Password reset on %s" -msgstr "Hitz ezkutu ezabatua %s n" +msgstr "Pasahitza berrezarri %s n" #: contrib/auth/forms.py:117 msgid "The two 'new password' fields didn't match." -msgstr "Bi hitz ezkutu berriak ez dira berdinak." +msgstr "Bi pasahitz berriak ez datoz bat." +# c #: contrib/auth/forms.py:124 msgid "Your old password was entered incorrectly. Please enter it again." -msgstr "Zure hitz ezkutu zaharra ez zuzena. Idatzi ezazu berriro." +msgstr "Zure pasahitz zaharra ez da zuzena. Idatzi ezazu berriro." #: contrib/auth/models.py:73 contrib/auth/models.py:93 msgid "name" @@ -1072,7 +1065,7 @@ msgstr "izena" #: contrib/auth/models.py:75 msgid "codename" -msgstr "code izena (codename)" +msgstr "kode izena" #: contrib/auth/models.py:78 msgid "permission" @@ -1092,7 +1085,7 @@ msgstr "taldeak" #: contrib/auth/models.py:131 msgid "username" -msgstr "usuario izena" +msgstr "erabiltzaile izena" #: contrib/auth/models.py:131 msgid "" @@ -1116,21 +1109,21 @@ msgstr "e-mail helbidea" #: contrib/auth/models.py:135 msgid "password" -msgstr "hitz ezkutua" +msgstr "pasahitza" #: contrib/auth/models.py:135 msgid "" "Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change " "password form</a>." -msgstr "Erabili '[algo]$[salt]$[hexdigest]' edo erabili <a href=\"password/\">hitz ezkuta aldatu </a>." +msgstr "Erabili '[algo]$[salt]$[hexdigest]' edo erabili <a href=\"password/\">pasahitza aldatzeko formularioa </a>." #: contrib/auth/models.py:136 msgid "staff status" -msgstr "Arduraduen egoera" +msgstr "Arduradun egoera" #: contrib/auth/models.py:136 msgid "Designates whether the user can log into this admin site." -msgstr "Usuarioak administrazio gune honetan sartu dezkeen ala ez izendatzen du." +msgstr "Erabiltzaileak kudeaketa gune honetan sartzeko baimena duen edo ez adierazten du." #: contrib/auth/models.py:137 msgid "active" @@ -1140,17 +1133,17 @@ msgstr "Aktiboa" msgid "" "Designates whether this user can log into the Django admin. Unselect this " "instead of deleting accounts." -msgstr "Usuarioak Django adminiztrazio gunean sartu daitekeen ala ez. Desaktiba ezazu aukera hau,kontua ezabatu ordez." +msgstr "Erabiltzailea Django adminiztrazio gunean sartu daitekeen edo ez adierazten du. Desaktiba ezazu aukera hau kontua ezabatu ordez." #: contrib/auth/models.py:138 msgid "superuser status" -msgstr "Usuario nagusia (superuser)" +msgstr "Erabiltzaile nagusia" #: contrib/auth/models.py:138 msgid "" "Designates that this user has all permissions without explicitly assigning " "them." -msgstr "Usuario honek baimen guztiak ditu, banan banan denak zehaztu gabe." +msgstr "Erabiltzaileari baimen guztiak esleitzeko banan-banan aukeratu behar izan gabe." #: contrib/auth/models.py:139 msgid "last login" @@ -1164,19 +1157,19 @@ msgstr "erregistro eguna" msgid "" "In addition to the permissions manually assigned, this user will also get " "all permissions granted to each group he/she is in." -msgstr "Usuario honek, eskuz emandako baimen guztietaz aparte, berari egokitutako talde bakoitzari emandako baimenak ere izango ditu." +msgstr "Erabiltzaile honek, eskuz emandako baimen guztiez gain, berari egokitutako talde bakoitzari emandako baimenak ere izango ditu." #: contrib/auth/models.py:143 msgid "user permissions" -msgstr "Usuarioaren baimenak" +msgstr "Erabiltzailearen baimenak" #: contrib/auth/models.py:147 msgid "user" -msgstr "Usuarioa" +msgstr "Erabiltzailea" #: contrib/auth/models.py:148 msgid "users" -msgstr "Usuarioak" +msgstr "Erabiltzaileak" #: contrib/auth/models.py:154 msgid "Personal info" @@ -1267,7 +1260,7 @@ msgstr "ezabatua" msgid "" "Check this box if the comment is inappropriate. A \"This comment has been " "removed\" message will be displayed instead." -msgstr "Markatu kutxa hau komentario ezegokia bada. A \"Komentario hau ezabatua izan da\" mezua erakutsiko da bere ordez." +msgstr "Markatu kutxa hau komentario ezegokia bada. \"Komentario hau ezabatua izan da\" mezua erakutsiko da bere ordez." #: contrib/comments/models.py:96 msgid "comments" @@ -1298,7 +1291,7 @@ msgstr "pertsonaren izena" #: contrib/comments/models.py:181 msgid "ip address" -msgstr "ip zenbakia" +msgstr "ip helbidea" #: contrib/comments/models.py:183 msgid "approved by staff" @@ -1340,7 +1333,7 @@ msgid "" "\n" "%(text)s" msgstr "" -"%(user)s -k komentario hay markatu du:\n" +"%(user)s -k komentario hau markatu du:\n" "\n" "%(text)s" @@ -1350,11 +1343,11 @@ msgstr "markatutako eguna" #: contrib/comments/models.py:289 msgid "user flag" -msgstr "usuarioaren markaketa" +msgstr "erabiltzailearen markaketa" #: contrib/comments/models.py:290 msgid "user flags" -msgstr "usuariaren markaketak" +msgstr "erabiltzailearen markaketak" #: contrib/comments/models.py:294 #, python-format @@ -1380,7 +1373,7 @@ msgstr "%r moderatzaileak ezabatua" #: contrib/comments/templates/comments/form.html:8 msgid "Forgotten your password?" -msgstr "Hitz ezkutua ahaztu duzu?" +msgstr "Pasahitza ahaztu duzu?" #: contrib/comments/templates/comments/form.html:12 msgid "Ratings" @@ -1430,7 +1423,7 @@ msgid_plural "" "comments:\n" "\n" "%(text)s" -msgstr[0] "Komentario hau, %(count)s komentario baino gutxiago egindako usuario batek bidalia da:<br><br>%(text)s" +msgstr[0] "Komentario hau, %(count)s komentario baino gutxiago egindako erabiltzaile batek bidali du:<br><br>%(text)s" msgstr[1] "" #: contrib/comments/views/comments.py:117 @@ -1440,19 +1433,19 @@ msgid "" "\n" "%(text)s" msgstr "" -"Komentario hau usuario 'arin' batek bidalia da:\n" +"Komentario hau behin-behineko erabiltzaile batek bidali du:\n" "\n" "%(text)s" #: contrib/comments/views/comments.py:190 #: contrib/comments/views/comments.py:283 msgid "Only POSTs are allowed" -msgstr "POST bakarrik onartzen dira" +msgstr "POSTak bakarrik onartzen dira" #: contrib/comments/views/comments.py:194 #: contrib/comments/views/comments.py:287 msgid "One or more of the required fields wasn't submitted" -msgstr "Beharrezko fitxategi bat edo gehiago ez dira bidali" +msgstr "Beharrezko eremu bat edo gehiago ez dira bidali" #: contrib/comments/views/comments.py:198 #: contrib/comments/views/comments.py:289 @@ -1473,7 +1466,7 @@ msgstr "Komentario formularioa ez zuen ez 'berikusi' edo 'bidali'" #: contrib/comments/views/karma.py:21 msgid "Anonymous users cannot vote" -msgstr "Usuario ezezagunak ezin dezakete botoa eman" +msgstr "Erabiltzaile anonimoek ezin dezakete botorik eman" #: contrib/comments/views/karma.py:25 msgid "Invalid comment ID" @@ -1489,11 +1482,11 @@ msgstr "python model class izena" #: contrib/contenttypes/models.py:40 msgid "content type" -msgstr "edukiera moeta" +msgstr "eduki mota" #: contrib/contenttypes/models.py:41 msgid "content types" -msgstr "edukiera moetak" +msgstr "eduki motak" #: contrib/flatpages/models.py:8 msgid "Example: '/about/contact/'. Make sure to have leading and trailing slashes." @@ -1505,7 +1498,7 @@ msgstr "izenburua" #: contrib/flatpages/models.py:10 msgid "content" -msgstr "edukiera" +msgstr "edukia" #: contrib/flatpages/models.py:11 msgid "enable comments" @@ -1519,7 +1512,7 @@ msgstr "plantila izena" msgid "" "Example: 'flatpages/contact_page.html'. If this isn't provided, the system " "will use 'flatpages/default.html'." -msgstr "Adibidez: 'flatpages/contact_page.html'. Hau ematen ez bada, sistema 'flatpages/default.html' erabiliko du." +msgstr "Adibidez: 'flatpages/contact_page.html'. Hau ematen ez bada, sistemak 'flatpages/default.html' erabiliko du." #: contrib/flatpages/models.py:14 msgid "registration required" @@ -1527,7 +1520,7 @@ msgstr "erregistratzea beharrezkoa da" #: contrib/flatpages/models.py:14 msgid "If this is checked, only logged-in users will be able to view the page." -msgstr "Hau markatuta badago, erregistratutako usuarioak bakarrik ikusiko dute horria." +msgstr "Hau markatuta badago, erregistratutako erabiltzaileek bakarrik ikusiko dute orria." #: contrib/flatpages/models.py:18 msgid "flat page" @@ -1630,11 +1623,11 @@ msgstr "NNNN edo ANNNNAAA formatoan idatzi posta kode bat." #: contrib/localflavor/br/forms.py:135 contrib/localflavor/pe/forms.py:23 #: contrib/localflavor/pe/forms.py:51 msgid "This field requires only numbers." -msgstr "Data honek zenbakiak bakarrik behar ditu." +msgstr "Eremu honek zenbakiak bakarrik behar ditu." #: contrib/localflavor/ar/forms.py:51 msgid "This field requires 7 or 8 digits." -msgstr "Data honek 7 edo 8 digito behar ditu." +msgstr "Eremu honek 7 edo 8 digito behar ditu." #: contrib/localflavor/ar/forms.py:80 msgid "Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format." @@ -1646,7 +1639,7 @@ msgstr "CUIT okerra." #: contrib/localflavor/au/forms.py:16 msgid "Enter a 4 digit post code." -msgstr "4 zenbaki posta kodean idatzi." +msgstr "4 zenbakiko posta kodea idatzi." #: contrib/localflavor/br/forms.py:21 msgid "Enter a zip code in the format XXXXX-XXX." @@ -1668,7 +1661,7 @@ msgstr "CPF zenbaki okerra." #: contrib/localflavor/br/forms.py:95 msgid "This field requires at most 11 digits or 14 characters." -msgstr "Data honek gehienez 11 digito edo 14 karaktere behar ditu." +msgstr "Eremu honek gehienez 11 digito edo 14 karaktere behar ditu." #: contrib/localflavor/br/forms.py:134 msgid "Invalid CNPJ number." @@ -1676,7 +1669,7 @@ msgstr "CNPJ zenbaki okerra." #: contrib/localflavor/br/forms.py:136 msgid "This field requires at least 14 digits" -msgstr "Data honek 14 digito behar ditu gutxienez" +msgstr "Eremu honek 14 digito behar ditu gutxienez" #: contrib/localflavor/ca/forms.py:17 msgid "Enter a postal code in the format XXX XXX." @@ -1684,7 +1677,7 @@ msgstr "Posta kodea idatzi XXX XXX formatoan." #: contrib/localflavor/ca/forms.py:88 msgid "Enter a valid Canadian Social Insurance number in XXX-XXX-XXX format." -msgstr "" +msgstr "Sartu Kanadako segurtasun sozialeko zenbaki zuzen bat XXX-XXX-XXX formatoan." #: contrib/localflavor/ch/ch_states.py:5 msgid "Aargau" @@ -1802,15 +1795,15 @@ msgstr "" #: contrib/localflavor/cl/forms.py:29 msgid "Enter a valid Chilean RUT." -msgstr "" +msgstr "Txileko RUT zuzen bat sartu" #: contrib/localflavor/cl/forms.py:30 msgid "Enter a valid Chilean RUT. The format is XX.XXX.XXX-X." -msgstr "" +msgstr "Txileko RUT zuzen bat sartu. Formatoa: XX.XXX.XXX.X." #: contrib/localflavor/cl/forms.py:31 msgid "The Chilean RUT is not valid." -msgstr "" +msgstr "Txileko RUTa ez da baliozkoa" #: contrib/localflavor/de/de_states.py:5 msgid "Baden-Wuerttemberg" @@ -1879,7 +1872,7 @@ msgstr "" #: contrib/localflavor/de/forms.py:14 contrib/localflavor/fi/forms.py:12 #: contrib/localflavor/fr/forms.py:15 msgid "Enter a zip code in the format XXXXX." -msgstr "" +msgstr "Sartu zip kodea XXXXX formatoan" #: contrib/localflavor/de/forms.py:41 msgid "" @@ -2152,7 +2145,7 @@ msgstr "Nafarroako komunitate forala" #: contrib/localflavor/es/es_regions.py:21 msgid "Valencian Community" -msgstr "Valeciako komunitatea" +msgstr "Valenciako komunitatea" #: contrib/localflavor/es/forms.py:19 msgid "Enter a valid postal code in the range and format 01XXX - 52XXX." @@ -2182,7 +2175,7 @@ msgstr "NIE kontrol kode okerra." #: contrib/localflavor/es/forms.py:70 msgid "Invalid checksum for CIF." -msgstr "CIF kontrl kode okerra." +msgstr "CIF kontrol kode okerra." #: contrib/localflavor/es/forms.py:142 msgid "Please enter a valid bank account number in format XXXX-XXXX-XX-XXXXXXXXXX." @@ -3400,7 +3393,7 @@ msgstr "nondik berbidalia" msgid "" "This should be an absolute path, excluding the domain name. Example: '/" "events/search/'." -msgstr "Hau 'bide' absolutua izan beharko luke, dominio izena kenduta. 'Adibidez: events/search/'." +msgstr "Honek 'bide' absolutua izan beharko luke, dominio izena kenduta. 'Adibidez: events/search/'." #: contrib/redirects/models.py:9 msgid "redirect to" @@ -3484,7 +3477,7 @@ msgstr "Komaz bereiztutako digitoak bakarrik idatzi." #: core/validators.py:107 msgid "Enter valid e-mail addresses separated by commas." -msgstr "E-mail norabide zuzenak idatzi, komaz bereizturik." +msgstr "E-mail helbide zuzenak idatzi, komaz bereizturik." #: core/validators.py:111 msgid "Please enter a valid IP address." @@ -3500,7 +3493,7 @@ msgstr "Zenbaki karaktereak bakarrik onartzen dira hemen." #: core/validators.py:123 msgid "This value can't be comprised solely of digits." -msgstr "Balore honek ezin daiteke digitoz bakarrik osatua egon." +msgstr "Balore hau ezin daiteke digitoz bakarrik osatua egon." #: core/validators.py:128 newforms/fields.py:151 msgid "Enter a whole number." @@ -3512,7 +3505,7 @@ msgstr "Karaktere alfabetikoak bakarrik onartzen dira hemen." #: core/validators.py:147 msgid "Year must be 1900 or later." -msgstr "Urtea 1900 edo haundiagoa izan behar du." +msgstr "Urtea 1900 edo handiagoa izan behar du." #: core/validators.py:151 #, python-format @@ -3525,11 +3518,11 @@ msgstr "Data zuzena idatzi, YYY-MM-DD formatoan." #: core/validators.py:161 msgid "Enter a valid time in HH:MM format." -msgstr "Hordu zuzena idatzi HH:MM formatoan." +msgstr "Ordu zuzena idatzi HH:MM formatoan." #: core/validators.py:165 db/models/fields/__init__.py:583 msgid "Enter a valid date/time in YYYY-MM-DD HH:MM format." -msgstr "Idatzi data/hordua zuzena YYYY-MM-DD HH:MM formatoan." +msgstr "Idatzi data/ordua zuzena YYYY-MM-DD HH:MM formatoan." #: core/validators.py:170 newforms/fields.py:402 msgid "Enter a valid e-mail address." @@ -3538,13 +3531,13 @@ msgstr "e-mail helbide zuzena idatzi." #: core/validators.py:182 core/validators.py:474 newforms/fields.py:432 #: oldforms/__init__.py:687 msgid "No file was submitted. Check the encoding type on the form." -msgstr "Ez da fitxategirik bidali. Baiztatu ezazu formularioren kode formatoa." +msgstr "Ez da fitxategirik bidali. Baiztatu ezazu formularioren kodeketa." #: core/validators.py:193 newforms/fields.py:458 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." -msgstr "Bidali irudi zuzena. Zuk bidalitako fitxategia ez da irudi motako edo akatsa du." +msgstr "Bidali irudi zuzena. Zuk bidalitako fitxategia ez da irudi motako edo akatsa dauka." #: core/validators.py:200 #, python-format @@ -3554,7 +3547,7 @@ msgstr "%s URLa ez da irudi zuzena." #: core/validators.py:204 #, python-format msgid "Phone numbers must be in XXX-XXX-XXXX format. \"%s\" is invalid." -msgstr "Telefono zenbakiak XXX-XXX-XXXX formatoa eduki behar dute. \"%s\" okerra." +msgstr "Telefono zenbakiek XXX-XXX-XXXX formatoa eduki behar dute. \"%s\" okerra." #: core/validators.py:212 #, python-format @@ -3587,11 +3580,11 @@ msgstr "URL okerra: %s" #: core/validators.py:259 core/validators.py:261 #, python-format msgid "The URL %s is a broken link." -msgstr "%s URLa ez dabil ondo." +msgstr "%s URLa ez dago ondo." #: core/validators.py:267 msgid "Enter a valid U.S. state abbreviation." -msgstr "U.S estatu laburpen zuzen idatzi." +msgstr "AEB estatu laburpen zuzen bat idatzi." #: core/validators.py:281 #, python-format @@ -3603,73 +3596,73 @@ msgstr[1] "" #: core/validators.py:288 #, python-format msgid "This field must match the '%s' field." -msgstr "Data hau '%s' data berdindu behar du." +msgstr "Eremu hau eta '%s' eremua berdinak izan behar dira." #: core/validators.py:307 msgid "Please enter something for at least one field." -msgstr "Idatzi gutxienez data bat." +msgstr "Bete eremuren bat." #: core/validators.py:316 core/validators.py:327 msgid "Please enter both fields or leave them both empty." -msgstr "Hidatzi bi datuak edo utzi biak hutsik." +msgstr "Bete bi eremuak edo ez bete bat bera ere." #: core/validators.py:335 #, python-format msgid "This field must be given if %(field)s is %(value)s" -msgstr "Data hau idatzi behar da, %(field)s %(value)s balorea badu." +msgstr "%(field)s eremuak %(value)s balioa baldin badauka, eremu hau bete behar da." #: core/validators.py:348 #, python-format msgid "This field must be given if %(field)s is not %(value)s" -msgstr "Data hau idatzi behar da %(field)s %(value)s balorea ez bada." +msgstr "%(field)s eremuak %(value)s balioa ez baldin badauka, eremu hau bete behar da." #: core/validators.py:367 msgid "Duplicate values are not allowed." -msgstr "Errepikatutako datak ez dira onartzen." +msgstr "Errepikatutako balioak ez dira onartzen." #: core/validators.py:382 #, python-format msgid "This value must be between %(lower)s and %(upper)s." -msgstr "Balore hau %(lower)s eta %(upper)s artean egon behar du." +msgstr "Balio hau %(lower)s eta %(upper)s artean egon behar da." #: core/validators.py:384 #, python-format msgid "This value must be at least %s." -msgstr "Gutxienez %s izan behar du." +msgstr "Gutxienez %s izan behar da." #: core/validators.py:386 #, python-format msgid "This value must be no more than %s." -msgstr "Balore hau %s baino txikiagoa izan behar du." +msgstr "Balio hau %s baino txikiagoa izan behar da." #: core/validators.py:427 #, python-format msgid "This value must be a power of %s." -msgstr "Balore hau %s-ren multiploa izan behar du." +msgstr "Balio hau %s-ren multiploa izan behar da." #: core/validators.py:437 msgid "Please enter a valid decimal number." -msgstr "Idatzi zenbaki dezimal zuzena mesedez." +msgstr "Idatzi zenbaki hamartar zuzena mesedez." #: core/validators.py:444 #, python-format msgid "Please enter a valid decimal number with at most %s total digit." msgid_plural "Please enter a valid decimal number with at most %s total digits." -msgstr[0] "Idatzi zenbaki dezimal zuzena gehienez %s digitorekin." +msgstr[0] "Idatzi zenbaki hamartar zuzena gehienez %s digitorekin." msgstr[1] "" #: core/validators.py:447 #, python-format msgid "Please enter a valid decimal number with a whole part of at most %s digit." msgid_plural "Please enter a valid decimal number with a whole part of at most %s digits." -msgstr[0] "Idatzi zenbaki digital zuzena, %s digitorekin gutxienez alde osoan." +msgstr[0] "Idatzi zenbaki hamartar zuzena, %s digitorekin gutxienez alde osoan." msgstr[1] "" #: core/validators.py:450 #, python-format msgid "Please enter a valid decimal number with at most %s decimal place." msgid_plural "Please enter a valid decimal number with at most %s decimal places." -msgstr[0] "Idatzi zenbaki dezimal zuzena, %s digitorekin dezimalean." +msgstr[0] "Idatzi zenbaki hamartar zuzena, %s digitorekin dezimalean." msgstr[1] "" #: core/validators.py:458 @@ -3679,20 +3672,20 @@ msgstr "Idatzi zenbaki erreal zuzena." #: core/validators.py:467 #, python-format msgid "Make sure your uploaded file is at least %s bytes big." -msgstr "Ziurta zaitez bidalitako fitxategia gutxienex %s byte tamaina duela." +msgstr "Ziurta zaitez bidalitako fitxategia gutxienez %s byte tamaina duela." #: core/validators.py:468 #, python-format msgid "Make sure your uploaded file is at most %s bytes big." -msgstr "Ziurta zaitez bidalitako fitxategia gehienez %s byte dituela." +msgstr "Ziurta zaitez bidalitako fitxategiak gehienez %s byte dituela." #: core/validators.py:485 msgid "The format for this field is wrong." -msgstr "Data honen formatoa okerra da." +msgstr "Eremu honen formatoa okerra da." #: core/validators.py:500 msgid "This field is invalid." -msgstr "Data okerra." +msgstr "Eremu okerra." #: core/validators.py:536 #, python-format @@ -3709,47 +3702,47 @@ msgstr "%(url)s URLak content-type okerra itzuli du: '%(contenttype)s'." msgid "" "Please close the unclosed %(tag)s tag from line %(line)s. (Line starts with " "\"%(start)s\".)" -msgstr "Mesedez itxi itxigabeko %(tag)s elementoa %(line)s lerroan. Lerroa hasiera: \"%(start)s\"." +msgstr "Mesedez itxi itxigabeko %(tag)s elementoa %(line)s lerroan. Lerro hasiera: \"%(start)s\"." #: core/validators.py:576 #, python-format msgid "" "Some text starting on line %(line)s is not allowed in that context. (Line " "starts with \"%(start)s\".)" -msgstr "Texturen bat %(line)s lerroan ez da onartzen contextu horretan. Lerro hasiera: \"%(start)s\"." +msgstr "%(line)s lerroan hasitako testuren bat ez da onartzen testuinguru horretan. Lerro hasiera: \"%(start)s\"." #: core/validators.py:581 #, python-format msgid "" "\"%(attr)s\" on line %(line)s is an invalid attribute. (Line starts with \"%" "(start)s\".)" -msgstr "\"%(attr)s\" %(line)s lerroan atribitu okerra da. Lerro hasiera: \"%(start)s\"." +msgstr "%(line)s lerroko \"%(attr)s\" atributua okerra da. Lerro hasiera: \"%(start)s\"." #: core/validators.py:586 #, python-format msgid "" "\"<%(tag)s>\" on line %(line)s is an invalid tag. (Line starts with \"%" "(start)s\".)" -msgstr "\"%(tag)s\" %(line)s lerroan elementu okerra da. Lerro hasiera: \"%(start)s\"." +msgstr "%(line)s lerroko \"<%(tag)s>\" atributua okerra da. Lerro hasiera: \"%(start)s\"." #: core/validators.py:590 #, python-format msgid "" "A tag on line %(line)s is missing one or more required attributes. (Line " "starts with \"%(start)s\".)" -msgstr "%(line)s lerroan elementu atributu bat edo gehiago faltan ditu. Lerro hasiera:\"%(start)s\"." +msgstr "%(line)s lerroan elementuren bat falta de edo atributuren bat falta du. Lerro hasiera:\"%(start)s\"." #: core/validators.py:595 #, python-format msgid "" "The \"%(attr)s\" attribute on line %(line)s has an invalid value. (Line " "starts with \"%(start)s\".)" -msgstr "\"%(attr)s\" atributoa %(line)s lerroan balore okerra du. (Lerro hasiera: \"%(start)s\".)" +msgstr " %(line)s lerroko \"%(attr)s\" atributoak balio okerra du. (Lerro hasiera: \"%(start)s\".)" #: db/models/manipulators.py:308 #, python-format msgid "%(object)s with this %(type)s already exists for the given %(field)s." -msgstr "%(object)s with this %(type)s already exists for the given %(field)s." +msgstr "%(type)s motako %(object)s objetua existitzen da %(field)s eremurako." #: db/models/fields/__init__.py:52 #, python-format @@ -3760,40 +3753,40 @@ msgstr "Badago %(optname)s izenarekin %(fieldname)s-n." #: db/models/fields/__init__.py:735 db/models/fields/__init__.py:746 #: newforms/fields.py:45 oldforms/__init__.py:374 msgid "This field is required." -msgstr "Kanpo hau beharrezkoa da." +msgstr "Eremu hau beharrezkoa da." #: db/models/fields/__init__.py:418 msgid "This value must be an integer." -msgstr "Balore hau zenbaki osoa izan behar du." +msgstr "Balio honek zenbaki osoa izan behar du." #: db/models/fields/__init__.py:454 msgid "This value must be either True or False." -msgstr "Balore hau egia ala gezurra izan behar du (True/False)." +msgstr "Balio honek True edo False izan behar du." #: db/models/fields/__init__.py:475 msgid "This field cannot be null." -msgstr "Datu hau ezin daiteke hutsa izan (null)." +msgstr "Eremu hau ezin daiteke hutsa izan (null)." #: db/models/fields/__init__.py:644 msgid "This value must be a decimal number." -msgstr "Balore hau zenbaki dezimala izan begar du." +msgstr "Balio honek zenbaki hamartarra izan behar du." #: db/models/fields/__init__.py:755 msgid "Enter a valid filename." -msgstr "Idatzi fitxategi izen zuzena" +msgstr "Idatzi fitxategi izen zuzen bat." #: db/models/fields/__init__.py:908 msgid "This value must be either None, True or False." -msgstr "Balore hau hutsa, egia edo gezurra izan behar du (None, True,False)" +msgstr "Balio honek hutsa, egia edo gezurra izan behar du (None, True,False)" #: db/models/fields/related.py:55 #, python-format msgid "Please enter a valid %s." -msgstr "Mesades idatzi %s zuzena." +msgstr "Mesedez idatzi %s zuzena." #: db/models/fields/related.py:658 msgid "Separate multiple IDs with commas." -msgstr "Bereiztu ID zerrenda koma bidez." +msgstr "Bereiztu ID anitzak komaz." #: db/models/fields/related.py:660 msgid "Hold down \"Control\", or \"Command\" on a Mac, to select more than one." @@ -3813,22 +3806,22 @@ msgstr "Idatzi balio zuzena." #: newforms/fields.py:123 #, python-format msgid "Ensure this value has at most %(max)d characters (it has %(length)d)." -msgstr "Ziurta zaitez balore honek gehienez %(max)d karactere dituela, %(length)d ditu orain." +msgstr "Ziurta zaitez balio honek gehienez %(max)d karaktere dituela, %(length)d ditu orain." #: newforms/fields.py:124 #, python-format msgid "Ensure this value has at least %(min)d characters (it has %(length)d)." -msgstr "Ziurta zaitez balore honek gutxienez %(min)d karaktere dituela ,%(length)d ditu orain." +msgstr "Ziurta zaitez balio honek gutxienez %(min)d karaktere dituela ,%(length)d ditu orain." #: newforms/fields.py:152 newforms/fields.py:181 newforms/fields.py:210 #, python-format msgid "Ensure this value is less than or equal to %s." -msgstr "Ziurta zaitez balore hau %s baino txikiagoa edo berdina dela." +msgstr "Ziurta zaitez balio hau %s baino txikiagoa edo berdina dela." #: newforms/fields.py:153 newforms/fields.py:182 newforms/fields.py:211 #, python-format msgid "Ensure this value is greater than or equal to %s." -msgstr "Ziurta zaitez balore hau %s baino haundiagoa edo berdina dela." +msgstr "Ziurta zaitez balio hau %s baino handiagoa edo berdina dela." #: newforms/fields.py:180 newforms/fields.py:209 msgid "Enter a number." @@ -3842,12 +3835,12 @@ msgstr "Ziurta zaitez %s baino digito gehiago ez dagoela." #: newforms/fields.py:213 #, python-format msgid "Ensure that there are no more than %s decimal places." -msgstr "Ziurta zaitez %s baino dezimale gehiago ez dagoela." +msgstr "Ziurta zaitez %s baino hamartar gehiago ez dagoela." #: newforms/fields.py:214 #, python-format msgid "Ensure that there are no more than %s digits before the decimal point." -msgstr "Ziurta zaitez %s digitu baino gehiago ez dagoela puntu dezimalaren aurretik." +msgstr "Ziurta zaitez %s digitu baino gehiago ez dagoela puntu hamartarraren aurretik." #: newforms/fields.py:262 newforms/fields.py:723 msgid "Enter a valid date." @@ -3859,7 +3852,7 @@ msgstr "Ordu zuzen bat idatzi." #: newforms/fields.py:334 msgid "Enter a valid date/time." -msgstr "Data/Ordua zuzen bat idatzi." +msgstr "Data/Ordu zuzen bat idatzi." #: newforms/fields.py:433 msgid "No file was submitted." @@ -3875,7 +3868,7 @@ msgstr "URL zuzen bat idatzi." #: newforms/fields.py:497 msgid "This URL appears to be a broken link." -msgstr "URL hau ez dabil ondo." +msgstr "URL hau ez dago ondo." #: newforms/fields.py:559 newforms/models.py:317 msgid "Select a valid choice. That choice is not one of the available choices." @@ -3897,7 +3890,7 @@ msgstr "IPv4 zuzen bat idatzi." #: newforms/models.py:378 #, python-format msgid "Select a valid choice. %s is not one of the available choices." -msgstr "Aukera zuzena aukeratu. %s ez da zuzena." +msgstr "Aukera zuzena aukeratu. %s ez da aukeretako bat." #: oldforms/__init__.py:409 #, python-format @@ -3908,7 +3901,7 @@ msgstr[1] "" #: oldforms/__init__.py:414 msgid "Line breaks are not allowed here." -msgstr "Lerro berriak (line breaks) ez dire onartzen hemen." +msgstr "Lerro jauziak ez dira onartzen hemen." #: oldforms/__init__.py:512 oldforms/__init__.py:586 oldforms/__init__.py:625 #, python-format @@ -4007,31 +4000,31 @@ msgstr "Igandea" #: utils/dates.py:10 msgid "Mon" -msgstr "Astelehe" +msgstr "Al" #: utils/dates.py:10 msgid "Tue" -msgstr "Astear" +msgstr "Ar" #: utils/dates.py:10 msgid "Wed" -msgstr "Asteaz" +msgstr "Az" #: utils/dates.py:10 msgid "Thu" -msgstr "Oste" +msgstr "Og" #: utils/dates.py:10 msgid "Fri" -msgstr "Osti" +msgstr "Ol" #: utils/dates.py:11 msgid "Sat" -msgstr "Lar" +msgstr "Lr" #: utils/dates.py:11 msgid "Sun" -msgstr "Iga" +msgstr "Ig" #: utils/dates.py:18 msgid "January" @@ -4165,37 +4158,37 @@ msgstr "edo" msgid "year" msgid_plural "years" msgstr[0] "urtea" -msgstr[1] "" +msgstr[1] "urteak" #: utils/timesince.py:22 msgid "month" msgid_plural "months" msgstr[0] "hilabetea" -msgstr[1] "" +msgstr[1] "hilabeteak" #: utils/timesince.py:23 msgid "week" msgid_plural "weeks" msgstr[0] "astea" -msgstr[1] "" +msgstr[1] "asteak" #: utils/timesince.py:24 msgid "day" msgid_plural "days" msgstr[0] "eguna" -msgstr[1] "" +msgstr[1] "egunak" #: utils/timesince.py:25 msgid "hour" msgid_plural "hours" -msgstr[0] "hordua" -msgstr[1] "" +msgstr[0] "ordua" +msgstr[1] "orduak" #: utils/timesince.py:26 msgid "minute" msgid_plural "minutes" msgstr[0] "minutu" -msgstr[1] "" +msgstr[1] "minutuak" #: utils/timesince.py:46 msgid "minutes" @@ -4239,10 +4232,10 @@ msgstr "%(verbose_name)s arazorik gabe sortu da" #: views/generic/create_update.py:117 #, python-format msgid "The %(verbose_name)s was updated successfully." -msgstr "%(verbose_name)s arazorik gabe aldatua izan da." +msgstr "%(verbose_name)s arazorik gabe eguneratu da." #: views/generic/create_update.py:184 #, python-format msgid "The %(verbose_name)s was deleted." -msgstr "%(verbose_name)s ezabatua izan da." +msgstr "%(verbose_name)s ezabatu da." diff --git a/django/conf/locale/fr/LC_MESSAGES/django.mo b/django/conf/locale/fr/LC_MESSAGES/django.mo Binary files differindex eae7521b80..e0c7259b82 100644 --- a/django/conf/locale/fr/LC_MESSAGES/django.mo +++ b/django/conf/locale/fr/LC_MESSAGES/django.mo diff --git a/django/conf/locale/fr/LC_MESSAGES/django.po b/django/conf/locale/fr/LC_MESSAGES/django.po index 412c2cb535..e9ec95ea9f 100644 --- a/django/conf/locale/fr/LC_MESSAGES/django.po +++ b/django/conf/locale/fr/LC_MESSAGES/django.po @@ -10,9 +10,9 @@ msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-05-14 11:01+0200\n" -"PO-Revision-Date: 2010-04-17 00:18+0200\n" -"Last-Translator: David Larlet <http://larlet.fr>\n" +"POT-Creation-Date: 2010-08-09 12:11+0200\n" +"PO-Revision-Date: 2010-08-09 14:38+0200\n" +"Last-Translator: Stéphane Raimbault <stephane.raimbault@gmail.com>\n" "Language-Team: French <http://django-fr.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -72,7 +72,7 @@ msgid "Spanish" msgstr "Espagnol" #: conf/global_settings.py:57 -msgid "Argentinean Spanish" +msgid "Argentinian Spanish" msgstr "Espagnol argentin" #: conf/global_settings.py:58 @@ -168,98 +168,102 @@ msgid "Macedonian" msgstr "Macédonien" #: conf/global_settings.py:81 +msgid "Malayalam" +msgstr "Malayâlam" + +#: conf/global_settings.py:82 msgid "Mongolian" msgstr "Mongole" -#: conf/global_settings.py:82 +#: conf/global_settings.py:83 msgid "Dutch" msgstr "Hollandais" -#: conf/global_settings.py:83 +#: conf/global_settings.py:84 msgid "Norwegian" msgstr "Norvégien" -#: conf/global_settings.py:84 +#: conf/global_settings.py:85 msgid "Norwegian Bokmal" msgstr "Norvégien Bokmal" -#: conf/global_settings.py:85 +#: conf/global_settings.py:86 msgid "Norwegian Nynorsk" msgstr "Norvégien Nynorsk" -#: conf/global_settings.py:86 +#: conf/global_settings.py:87 msgid "Polish" msgstr "Polonais" -#: conf/global_settings.py:87 +#: conf/global_settings.py:88 msgid "Portuguese" msgstr "Portugais" -#: conf/global_settings.py:88 +#: conf/global_settings.py:89 msgid "Brazilian Portuguese" msgstr "Portugais brésilien" -#: conf/global_settings.py:89 +#: conf/global_settings.py:90 msgid "Romanian" msgstr "Roumain" -#: conf/global_settings.py:90 +#: conf/global_settings.py:91 msgid "Russian" msgstr "Russe" -#: conf/global_settings.py:91 +#: conf/global_settings.py:92 msgid "Slovak" msgstr "Slovaque" -#: conf/global_settings.py:92 +#: conf/global_settings.py:93 msgid "Slovenian" msgstr "Slovène" -#: conf/global_settings.py:93 +#: conf/global_settings.py:94 msgid "Albanian" msgstr "Albanais" -#: conf/global_settings.py:94 +#: conf/global_settings.py:95 msgid "Serbian" msgstr "Serbe" -#: conf/global_settings.py:95 +#: conf/global_settings.py:96 msgid "Serbian Latin" msgstr "Serbe latin" -#: conf/global_settings.py:96 +#: conf/global_settings.py:97 msgid "Swedish" msgstr "Suédois" -#: conf/global_settings.py:97 +#: conf/global_settings.py:98 msgid "Tamil" msgstr "Tamoul" -#: conf/global_settings.py:98 +#: conf/global_settings.py:99 msgid "Telugu" msgstr "Télougou" -#: conf/global_settings.py:99 +#: conf/global_settings.py:100 msgid "Thai" msgstr "Thaï" -#: conf/global_settings.py:100 +#: conf/global_settings.py:101 msgid "Turkish" msgstr "Turc" -#: conf/global_settings.py:101 +#: conf/global_settings.py:102 msgid "Ukrainian" msgstr "Ukrainien" -#: conf/global_settings.py:102 +#: conf/global_settings.py:103 msgid "Vietnamese" msgstr "Vietnamien" -#: conf/global_settings.py:103 +#: conf/global_settings.py:104 msgid "Simplified Chinese" msgstr "Chinois simplifié" -#: conf/global_settings.py:104 +#: conf/global_settings.py:105 msgid "Traditional Chinese" msgstr "Chinois traditionnel" @@ -311,15 +315,15 @@ msgstr "Ce mois-ci" msgid "This year" msgstr "Cette année" -#: contrib/admin/filterspecs.py:147 forms/widgets.py:469 +#: contrib/admin/filterspecs.py:147 forms/widgets.py:478 msgid "Yes" msgstr "Oui" -#: contrib/admin/filterspecs.py:147 forms/widgets.py:469 +#: contrib/admin/filterspecs.py:147 forms/widgets.py:478 msgid "No" msgstr "Non" -#: contrib/admin/filterspecs.py:154 forms/widgets.py:469 +#: contrib/admin/filterspecs.py:154 forms/widgets.py:478 msgid "Unknown" msgstr "Inconnu" @@ -697,7 +701,7 @@ msgid "Filter" msgstr "Filtre" #: contrib/admin/templates/admin/delete_confirmation.html:10 -#: contrib/admin/templates/admin/submit_line.html:4 forms/formsets.py:302 +#: contrib/admin/templates/admin/submit_line.html:4 forms/formsets.py:300 msgid "Delete" msgstr "Supprimer" @@ -796,7 +800,7 @@ msgstr "" #: contrib/admin/templates/admin/login.html:19 msgid "Username:" -msgstr "Nom d'utilisateur :" +msgstr "Nom d'utilisateur :" #: contrib/admin/templates/admin/login.html:22 msgid "Password:" @@ -859,7 +863,7 @@ msgstr "Enregistrer et ajouter un nouveau" msgid "Save and continue editing" msgstr "Enregistrer et continuer les modifications" -#: contrib/admin/templates/admin/auth/user/add_form.html:5 +#: contrib/admin/templates/admin/auth/user/add_form.html:6 msgid "" "First, enter a username and password. Then, you'll be able to edit more user " "options." @@ -867,6 +871,10 @@ msgstr "" "Saisissez tout d'abord un nom d'utilisateur et un mot de passe. Vous pourrez " "ensuite modifier plus d'options." +#: contrib/admin/templates/admin/auth/user/add_form.html:8 +msgid "Enter a username and password." +msgstr "Saisissez un nom d'utilisateur et un mot de passe." + #: contrib/admin/templates/admin/auth/user/change_password.html:28 #, python-format msgid "Enter a new password for the user <strong>%(username)s</strong>." @@ -1441,8 +1449,8 @@ msgstr "message" msgid "Logged out" msgstr "Déconnecté" -#: contrib/auth/management/commands/createsuperuser.py:23 -#: core/validators.py:120 forms/fields.py:428 +#: contrib/auth/management/commands/createsuperuser.py:24 +#: core/validators.py:120 forms/fields.py:427 msgid "Enter a valid e-mail address." msgstr "Entrez une adresse de courriel valide." @@ -1511,7 +1519,7 @@ msgid "Email address" msgstr "Adresse électronique" #: contrib/comments/forms.py:95 contrib/flatpages/admin.py:8 -#: contrib/flatpages/models.py:7 db/models/fields/__init__.py:1101 +#: contrib/flatpages/models.py:7 db/models/fields/__init__.py:1112 msgid "URL" msgstr "URL" @@ -1563,7 +1571,7 @@ msgstr "commentaire" msgid "date/time submitted" msgstr "date et heure soumises" -#: contrib/comments/models.py:60 db/models/fields/__init__.py:896 +#: contrib/comments/models.py:60 db/models/fields/__init__.py:907 msgid "IP address" msgstr "adresse IP" @@ -4513,26 +4521,26 @@ msgstr "sites" msgid "Enter a valid value." msgstr "Saisissez une valeur valide." -#: core/validators.py:87 forms/fields.py:529 +#: core/validators.py:87 forms/fields.py:528 msgid "Enter a valid URL." msgstr "Saisissez une URL valide." -#: core/validators.py:89 forms/fields.py:530 +#: core/validators.py:89 forms/fields.py:529 msgid "This URL appears to be a broken link." msgstr "Cette URL semble être cassée." -#: core/validators.py:123 forms/fields.py:873 +#: core/validators.py:123 forms/fields.py:877 msgid "" "Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens." msgstr "" "Ce champ ne doit contenir que des lettres, des nombres, des tirets bas _ et " "des traits d'union." -#: core/validators.py:126 forms/fields.py:866 +#: core/validators.py:126 forms/fields.py:870 msgid "Enter a valid IPv4 address." msgstr "Saisissez une adresse IPv4 valide." -#: core/validators.py:129 db/models/fields/__init__.py:572 +#: core/validators.py:129 db/models/fields/__init__.py:575 msgid "Enter only digits separated by commas." msgstr "Saisissez uniquement des chiffres séparés par des virgules." @@ -4543,13 +4551,13 @@ msgstr "" "Assurez-vous que cette valeur est %(limit_value)s (actuellement %(show_value)" "s)." -#: core/validators.py:153 forms/fields.py:205 forms/fields.py:257 +#: core/validators.py:153 forms/fields.py:204 forms/fields.py:256 #, python-format msgid "Ensure this value is less than or equal to %(limit_value)s." msgstr "" "Assurez-vous que cette valeur est inférieure ou égale à %(limit_value)s." -#: core/validators.py:158 forms/fields.py:206 forms/fields.py:258 +#: core/validators.py:158 forms/fields.py:205 forms/fields.py:257 #, python-format msgid "Ensure this value is greater than or equal to %(limit_value)s." msgstr "" @@ -4602,134 +4610,134 @@ msgstr "Ce champ ne peut pas être vide." msgid "Field of type: %(field_type)s" msgstr "Champ de type : %(field_type)s" -#: db/models/fields/__init__.py:451 db/models/fields/__init__.py:852 -#: db/models/fields/__init__.py:961 db/models/fields/__init__.py:972 -#: db/models/fields/__init__.py:999 +#: db/models/fields/__init__.py:451 db/models/fields/__init__.py:863 +#: db/models/fields/__init__.py:972 db/models/fields/__init__.py:983 +#: db/models/fields/__init__.py:1010 msgid "Integer" msgstr "Entier" -#: db/models/fields/__init__.py:455 db/models/fields/__init__.py:850 +#: db/models/fields/__init__.py:455 db/models/fields/__init__.py:861 msgid "This value must be an integer." msgstr "Cette valeur doit être un entier." -#: db/models/fields/__init__.py:490 +#: db/models/fields/__init__.py:493 msgid "This value must be either True or False." msgstr "Cette valeur doit être soit vraie (True) soit fausse (False)." -#: db/models/fields/__init__.py:492 +#: db/models/fields/__init__.py:495 msgid "Boolean (Either True or False)" msgstr "Booléen (soit vrai ou faux)" -#: db/models/fields/__init__.py:539 db/models/fields/__init__.py:982 +#: db/models/fields/__init__.py:542 db/models/fields/__init__.py:993 #, python-format msgid "String (up to %(max_length)s)" msgstr "Chaîne de caractère (jusqu'à %(max_length)s)" -#: db/models/fields/__init__.py:567 +#: db/models/fields/__init__.py:570 msgid "Comma-separated integers" msgstr "Des entiers séparés par une virgule" -#: db/models/fields/__init__.py:581 +#: db/models/fields/__init__.py:584 msgid "Date (without time)" msgstr "Date (sans l'heure)" -#: db/models/fields/__init__.py:585 +#: db/models/fields/__init__.py:588 msgid "Enter a valid date in YYYY-MM-DD format." msgstr "Saisissez une date valide au format AAAA-MM-JJ." -#: db/models/fields/__init__.py:586 +#: db/models/fields/__init__.py:589 #, python-format msgid "Invalid date: %s" msgstr "Date non valide : %s" -#: db/models/fields/__init__.py:667 +#: db/models/fields/__init__.py:670 msgid "Enter a valid date/time in YYYY-MM-DD HH:MM[:ss[.uuuuuu]] format." msgstr "" "Saisissez une date et une heure valides au format AAAA-MM-JJ HH:MM[:ss[." "uuuuuu]]." -#: db/models/fields/__init__.py:669 +#: db/models/fields/__init__.py:672 msgid "Date (with time)" msgstr "Date (avec l'heure)" -#: db/models/fields/__init__.py:735 +#: db/models/fields/__init__.py:738 msgid "This value must be a decimal number." msgstr "Cette valeur doit être un nombre décimal." -#: db/models/fields/__init__.py:737 +#: db/models/fields/__init__.py:740 msgid "Decimal number" msgstr "Nombre décimal" -#: db/models/fields/__init__.py:792 +#: db/models/fields/__init__.py:795 msgid "E-mail address" msgstr "Adresse électronique" -#: db/models/fields/__init__.py:799 db/models/fields/files.py:220 +#: db/models/fields/__init__.py:810 db/models/fields/files.py:220 #: db/models/fields/files.py:331 msgid "File path" msgstr "Chemin vers le fichier" -#: db/models/fields/__init__.py:822 +#: db/models/fields/__init__.py:833 msgid "This value must be a float." msgstr "Cette valeur doit être un nombre réel." -#: db/models/fields/__init__.py:824 +#: db/models/fields/__init__.py:835 msgid "Floating point number" msgstr "Nombre à virgule flottante" -#: db/models/fields/__init__.py:883 +#: db/models/fields/__init__.py:894 msgid "Big (8 byte) integer" msgstr "Grand entier (8 octets)" -#: db/models/fields/__init__.py:912 +#: db/models/fields/__init__.py:923 msgid "This value must be either None, True or False." msgstr "Cette valeur doit être nulle (None), vraie (True) ou fausse (False)." -#: db/models/fields/__init__.py:914 +#: db/models/fields/__init__.py:925 msgid "Boolean (Either True, False or None)" msgstr "Booléen (soit vrai, faux ou nul)" -#: db/models/fields/__init__.py:1005 +#: db/models/fields/__init__.py:1016 msgid "Text" msgstr "Texte" -#: db/models/fields/__init__.py:1021 +#: db/models/fields/__init__.py:1032 msgid "Time" msgstr "Heure" -#: db/models/fields/__init__.py:1025 +#: db/models/fields/__init__.py:1036 msgid "Enter a valid time in HH:MM[:ss[.uuuuuu]] format." msgstr "Saisissez une heure valide au format HH:MM[:ss[.uuuuuu]]." -#: db/models/fields/__init__.py:1109 +#: db/models/fields/__init__.py:1128 msgid "XML text" msgstr "Texte XML" -#: db/models/fields/related.py:799 +#: db/models/fields/related.py:801 #, python-format msgid "Model %(model)s with pk %(pk)r does not exist." msgstr "Le modèle %(model)s avec la clef primaire %(pk)r n'existe pas." -#: db/models/fields/related.py:801 +#: db/models/fields/related.py:803 msgid "Foreign Key (type determined by related field)" msgstr "Clé étrangère (type défini par le champ lié)" -#: db/models/fields/related.py:918 +#: db/models/fields/related.py:921 msgid "One-to-one relationship" msgstr "Relation un à un" -#: db/models/fields/related.py:980 +#: db/models/fields/related.py:983 msgid "Many-to-many relationship" msgstr "Relation plusieurs à plusieurs" -#: db/models/fields/related.py:1000 +#: db/models/fields/related.py:1003 msgid "" "Hold down \"Control\", or \"Command\" on a Mac, to select more than one." msgstr "" "Maintenez appuyé « Ctrl », ou « Commande (touche pomme) » sur un Mac, pour en " "sélectionner plusieurs." -#: db/models/fields/related.py:1061 +#: db/models/fields/related.py:1064 #, python-format msgid "Please enter valid %(self)s IDs. The value %(value)r is invalid." msgid_plural "" @@ -4743,55 +4751,55 @@ msgstr[1] "" msgid "This field is required." msgstr "Ce champ est obligatoire." -#: forms/fields.py:204 +#: forms/fields.py:203 msgid "Enter a whole number." msgstr "Saisissez un nombre entier." -#: forms/fields.py:235 forms/fields.py:256 +#: forms/fields.py:234 forms/fields.py:255 msgid "Enter a number." msgstr "Saisissez un nombre." -#: forms/fields.py:259 +#: forms/fields.py:258 #, python-format msgid "Ensure that there are no more than %s digits in total." msgstr "Assurez-vous qu'il n'y a pas plus de %s chiffres au total." -#: forms/fields.py:260 +#: forms/fields.py:259 #, python-format msgid "Ensure that there are no more than %s decimal places." msgstr "Assurez-vous qu'il n'y a pas plus de %s chiffres après la virgule." -#: forms/fields.py:261 +#: forms/fields.py:260 #, python-format msgid "Ensure that there are no more than %s digits before the decimal point." msgstr "Assurez-vous qu'il n'y a pas plus de %s chiffres avant la virgule." -#: forms/fields.py:323 forms/fields.py:838 +#: forms/fields.py:322 forms/fields.py:837 msgid "Enter a valid date." msgstr "Saisissez une date valide." -#: forms/fields.py:351 forms/fields.py:839 +#: forms/fields.py:350 forms/fields.py:838 msgid "Enter a valid time." msgstr "Saisissez une heure valide." -#: forms/fields.py:377 +#: forms/fields.py:376 msgid "Enter a valid date/time." msgstr "Saisissez une date et une heure valides." -#: forms/fields.py:435 +#: forms/fields.py:434 msgid "No file was submitted. Check the encoding type on the form." msgstr "" "Aucun fichier n'a été soumis. Vérifiez le type d'encodage du formulaire." -#: forms/fields.py:436 +#: forms/fields.py:435 msgid "No file was submitted." msgstr "Aucun fichier n'a été soumis." -#: forms/fields.py:437 +#: forms/fields.py:436 msgid "The submitted file is empty." msgstr "Le fichier soumis est vide." -#: forms/fields.py:438 +#: forms/fields.py:437 #, python-format msgid "" "Ensure this filename has at most %(max)d characters (it has %(length)d)." @@ -4799,7 +4807,7 @@ msgstr "" "Assurez-vous que ce nom de fichier ne contient pas plus de %(max)d " "caractères (actuellement %(length)d caractères)." -#: forms/fields.py:473 +#: forms/fields.py:472 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." @@ -4807,17 +4815,17 @@ msgstr "" "Téléversez une image valide. Le fichier que vous avez transféré n'est pas " "une image ou bien est corrompu." -#: forms/fields.py:596 forms/fields.py:671 +#: forms/fields.py:595 forms/fields.py:670 #, python-format msgid "Select a valid choice. %(value)s is not one of the available choices." msgstr "Sélectionnez un choix valide. %(value)s n'en fait pas partie." -#: forms/fields.py:672 forms/fields.py:734 forms/models.py:1002 +#: forms/fields.py:671 forms/fields.py:733 forms/models.py:1002 msgid "Enter a list of values." msgstr "Saisissez une liste de valeurs." # Si « : » est requis, créer un ticket -#: forms/formsets.py:298 forms/formsets.py:300 +#: forms/formsets.py:296 forms/formsets.py:298 msgid "Order" msgstr "Ordre" @@ -4868,28 +4876,28 @@ msgstr "Sélectionnez un choix valide ; %s n'en fait pas partie." msgid "\"%s\" is not a valid value for a primary key." msgstr "« %s » n'est pas une valeur correcte pour une clé primaire." -#: template/defaultfilters.py:776 +#: template/defaultfilters.py:780 msgid "yes,no,maybe" msgstr "oui, non, peut-être" -#: template/defaultfilters.py:807 +#: template/defaultfilters.py:811 #, python-format msgid "%(size)d byte" msgid_plural "%(size)d bytes" msgstr[0] "%(size)d octet" msgstr[1] "%(size)d octets" -#: template/defaultfilters.py:809 +#: template/defaultfilters.py:813 #, python-format msgid "%.1f KB" msgstr "%.1f Ko" -#: template/defaultfilters.py:811 +#: template/defaultfilters.py:815 #, python-format msgid "%.1f MB" msgstr "%.1f Mo" -#: template/defaultfilters.py:812 +#: template/defaultfilters.py:816 #, python-format msgid "%.1f GB" msgstr "%.1f Go" @@ -5100,7 +5108,7 @@ msgstr "nov." msgid "Dec." msgstr "déc." -#: utils/text.py:130 +#: utils/text.py:136 msgid "or" msgstr "ou" diff --git a/django/conf/locale/ml/LC_MESSAGES/django.mo b/django/conf/locale/ml/LC_MESSAGES/django.mo Binary files differnew file mode 100644 index 0000000000..654b4fc4ee --- /dev/null +++ b/django/conf/locale/ml/LC_MESSAGES/django.mo diff --git a/django/conf/locale/ml/LC_MESSAGES/django.po b/django/conf/locale/ml/LC_MESSAGES/django.po new file mode 100644 index 0000000000..92ef53f099 --- /dev/null +++ b/django/conf/locale/ml/LC_MESSAGES/django.po @@ -0,0 +1,5044 @@ +# Translation of Django to Malayalam. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# RAJEESH R NAIR <rajeeshrnair@gmail.com>, 2010. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Django SVN\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2010-05-27 11:01+0530\n" +"PO-Revision-Date: 2010-05-28 15:09+0530\n" +"Last-Translator: Rajeesh Nair <rajeeshrnair@gmail.com>\n" +"Language-Team: MALAYALAM <ML@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-Language: Malayalam\n" +"X-Poedit-Country: INDIA\n" + +#: conf/global_settings.py:44 +msgid "Arabic" +msgstr "അറബി" + +#: conf/global_settings.py:45 +msgid "Bulgarian" +msgstr "ബളàµâ€à´—േറിയനàµâ€" + +#: conf/global_settings.py:46 +msgid "Bengali" +msgstr "ബംഗാളി" + +#: conf/global_settings.py:47 +msgid "Bosnian" +msgstr "ബോസàµà´¨à´¿à´¯à´¨àµâ€" + +#: conf/global_settings.py:48 +msgid "Catalan" +msgstr "കാറàµà´±à´²à´¨àµâ€" + +#: conf/global_settings.py:49 +msgid "Czech" +msgstr "ചെകàµ" + +#: conf/global_settings.py:50 +msgid "Welsh" +msgstr "വെലàµâ€à´·àµ" + +#: conf/global_settings.py:51 +msgid "Danish" +msgstr "ഡാനിഷàµ" + +#: conf/global_settings.py:52 +msgid "German" +msgstr "ജരàµâ€à´®à´¨àµâ€" + +#: conf/global_settings.py:53 +msgid "Greek" +msgstr "à´—àµà´°àµ€à´•àµà´•àµ" + +#: conf/global_settings.py:54 +msgid "English" +msgstr "ഇംഗàµà´³àµ€à´·àµ" + +#: conf/global_settings.py:55 +msgid "British English" +msgstr "à´¬àµà´°à´¿à´Ÿàµà´Ÿàµ€à´·àµ ഇംഗàµà´³àµ€à´·àµ" + +#: conf/global_settings.py:56 +msgid "Spanish" +msgstr "à´¸àµà´ªà´¾à´¨à´¿à´·àµ" + +#: conf/global_settings.py:57 +msgid "Argentinean Spanish" +msgstr "à´…à´°àµâ€à´œà´¨àµà´±àµ€à´¨à´¿à´¯à´¨àµâ€ à´¸àµà´ªà´¾à´¨à´¿à´·àµ" + +#: conf/global_settings.py:58 +msgid "Estonian" +msgstr "à´Žà´¸àµà´Ÿàµ‹à´£à´¿à´¯à´¨àµâ€ à´¸àµà´ªà´¾à´¨à´¿à´·àµ" + +#: conf/global_settings.py:59 +msgid "Basque" +msgstr "ബാസàµà´•àµà´¯àµ" + +#: conf/global_settings.py:60 +msgid "Persian" +msgstr "പേരàµâ€à´·àµà´¯à´¨àµâ€" + +#: conf/global_settings.py:61 +msgid "Finnish" +msgstr "à´«à´¿à´¨àµà´¨à´¿à´·àµ" + +#: conf/global_settings.py:62 +msgid "French" +msgstr "à´«àµà´°àµ†à´žàµà´šàµ" + +#: conf/global_settings.py:63 +msgid "Frisian" +msgstr "à´«àµà´°à´¿à´¸à´¿à´¯à´¨àµâ€" + +#: conf/global_settings.py:64 +msgid "Irish" +msgstr "à´à´±à´¿à´·àµ" + +#: conf/global_settings.py:65 +msgid "Galician" +msgstr "ഗലിഷàµà´¯à´¨àµâ€" + +#: conf/global_settings.py:66 +msgid "Hebrew" +msgstr "ഹീബàµà´±àµ" + +#: conf/global_settings.py:67 +msgid "Hindi" +msgstr "ഹിനàµà´¦à´¿" + +#: conf/global_settings.py:68 +msgid "Croatian" +msgstr "à´•àµà´°àµŠà´¯àµ‡à´·àµà´¯à´¨àµâ€" + +#: conf/global_settings.py:69 +msgid "Hungarian" +msgstr "ഹംഗേറിയനàµâ€" + +#: conf/global_settings.py:70 +msgid "Indonesian" +msgstr "ഇനàµâ€à´¦àµŠà´¨àµ‡à´·àµà´¯à´¨àµâ€" + +#: conf/global_settings.py:71 +msgid "Icelandic" +msgstr "à´à´¸àµà´²à´¾à´¨àµâ€à´¡à´¿à´•àµ" + +#: conf/global_settings.py:72 +msgid "Italian" +msgstr "ഇറàµà´±à´¾à´²à´¿à´¯à´¨àµâ€" + +#: conf/global_settings.py:73 +msgid "Japanese" +msgstr "ജാപàµà´ªà´¨àµ€à´¸àµ" + +#: conf/global_settings.py:74 +msgid "Georgian" +msgstr "ജോരàµâ€à´œà´¿à´¯à´¨àµâ€" + +#: conf/global_settings.py:75 +msgid "Khmer" +msgstr "" + +#: conf/global_settings.py:76 +msgid "Kannada" +msgstr "à´•à´¨àµà´¨à´¡" + +#: conf/global_settings.py:77 +msgid "Korean" +msgstr "കൊറിയനàµâ€" + +#: conf/global_settings.py:78 +msgid "Lithuanian" +msgstr "ലിതàµà´µà´¾à´¨à´¿à´¯à´¨àµâ€" + +#: conf/global_settings.py:79 +msgid "Latvian" +msgstr "ലാറàµà´±àµà´µà´¿à´¯à´¨àµâ€" + +#: conf/global_settings.py:80 +msgid "Macedonian" +msgstr "മാസിഡോണിയനàµâ€" + +#: conf/global_settings.py:81 +msgid "Mongolian" +msgstr "മംഗോളിയനàµâ€" + +#: conf/global_settings.py:82 +msgid "Dutch" +msgstr "à´¡à´šàµà´šàµ" + +#: conf/global_settings.py:83 +msgid "Norwegian" +msgstr "" + +#: conf/global_settings.py:84 +msgid "Norwegian Bokmal" +msgstr "" + +#: conf/global_settings.py:85 +msgid "Norwegian Nynorsk" +msgstr "" + +#: conf/global_settings.py:86 +msgid "Polish" +msgstr "" + +#: conf/global_settings.py:87 +msgid "Portuguese" +msgstr "" + +#: conf/global_settings.py:88 +msgid "Brazilian Portuguese" +msgstr "" + +#: conf/global_settings.py:89 +msgid "Romanian" +msgstr "" + +#: conf/global_settings.py:90 +msgid "Russian" +msgstr "" + +#: conf/global_settings.py:91 +msgid "Slovak" +msgstr "" + +#: conf/global_settings.py:92 +msgid "Slovenian" +msgstr "" + +#: conf/global_settings.py:93 +msgid "Albanian" +msgstr "" + +#: conf/global_settings.py:94 +msgid "Serbian" +msgstr "" + +#: conf/global_settings.py:95 +msgid "Serbian Latin" +msgstr "" + +#: conf/global_settings.py:96 +msgid "Swedish" +msgstr "" + +#: conf/global_settings.py:97 +msgid "Tamil" +msgstr "" + +#: conf/global_settings.py:98 +msgid "Telugu" +msgstr "" + +#: conf/global_settings.py:99 +msgid "Thai" +msgstr "" + +#: conf/global_settings.py:100 +msgid "Turkish" +msgstr "" + +#: conf/global_settings.py:101 +msgid "Ukrainian" +msgstr "" + +#: conf/global_settings.py:102 +msgid "Vietnamese" +msgstr "" + +#: conf/global_settings.py:103 +msgid "Simplified Chinese" +msgstr "" + +#: conf/global_settings.py:104 +msgid "Traditional Chinese" +msgstr "" + +#: contrib/admin/actions.py:48 +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "%(count)d %(items)s വിജയകരമായി ഡിലീറàµà´±àµ ചെയàµà´¤àµ." + +#: contrib/admin/actions.py:55 contrib/admin/options.py:1125 +msgid "Are you sure?" +msgstr "തീരàµâ€à´šàµà´šà´¯à´¾à´£àµ‹?" + +#: contrib/admin/actions.py:73 +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "തെരഞàµà´žàµ†à´Ÿàµà´¤àµà´¤ %(verbose_name_plural)s ഡിലീറàµà´±àµ ചെയàµà´¯àµà´•." + +#: contrib/admin/filterspecs.py:44 +#, python-format +msgid "" +"<h3>By %s:</h3>\n" +"<ul>\n" +msgstr "" +"<h3>By %s:</h3>\n" +"<ul>\n" + +#: contrib/admin/filterspecs.py:75 contrib/admin/filterspecs.py:92 +#: contrib/admin/filterspecs.py:147 contrib/admin/filterspecs.py:173 +msgid "All" +msgstr "à´Žà´²àµà´²à´¾à´‚" + +#: contrib/admin/filterspecs.py:113 +msgid "Any date" +msgstr "à´à´¤àµ†à´™àµà´•à´¿à´²àµà´‚ തീയതി" + +#: contrib/admin/filterspecs.py:114 +msgid "Today" +msgstr "ഇനàµà´¨àµ" + +#: contrib/admin/filterspecs.py:117 +msgid "Past 7 days" +msgstr "à´•à´´à´¿à´žàµà´ž à´à´´àµ ദിവസം" + +#: contrib/admin/filterspecs.py:119 +msgid "This month" +msgstr "à´ˆ മാസം" + +#: contrib/admin/filterspecs.py:121 +msgid "This year" +msgstr "à´ˆ വരàµâ€à´·à´‚" + +#: contrib/admin/filterspecs.py:147 forms/widgets.py:466 +msgid "Yes" +msgstr "അതെ" + +#: contrib/admin/filterspecs.py:147 forms/widgets.py:466 +msgid "No" +msgstr "à´…à´²àµà´²" + +#: contrib/admin/filterspecs.py:154 forms/widgets.py:466 +msgid "Unknown" +msgstr "à´…à´œàµà´žà´¾à´¤à´‚" + +#: contrib/admin/helpers.py:20 +msgid "Action:" +msgstr "ആകàµà´·à´¨àµâ€" + +#: contrib/admin/models.py:19 +msgid "action time" +msgstr "ആകàµà´·à´¨àµâ€ സമയം" + +#: contrib/admin/models.py:22 +msgid "object id" +msgstr "à´’à´¬àµà´œàµ†à´•àµà´Ÿàµ à´à´¡à´¿" + +#: contrib/admin/models.py:23 +msgid "object repr" +msgstr "à´’à´¬àµà´œàµ†à´•àµà´Ÿàµ സൂചന" + +#: contrib/admin/models.py:24 +msgid "action flag" +msgstr "ആകàµà´·à´¨àµâ€ à´«àµà´³à´¾à´—àµ" + +#: contrib/admin/models.py:25 +msgid "change message" +msgstr "സനàµà´¦àµ‡à´¶à´‚ മാറàµà´±àµà´•" + +#: contrib/admin/models.py:28 +msgid "log entry" +msgstr "ലോഗൠഎനàµà´Ÿàµà´°à´¿" + +#: contrib/admin/models.py:29 +msgid "log entries" +msgstr "ലോഗൠഎനàµà´Ÿàµà´°à´¿à´•à´³àµâ€" + +#: contrib/admin/options.py:138 contrib/admin/options.py:153 +msgid "None" +msgstr "à´’à´¨àµà´¨àµà´®à´¿à´²àµà´²" + +#: contrib/admin/options.py:559 +#, python-format +msgid "Changed %s." +msgstr "%s മാറàµà´±à´¿." + +#: contrib/admin/options.py:559 contrib/admin/options.py:569 +#: contrib/comments/templates/comments/preview.html:16 db/models/base.py:845 +#: forms/models.py:568 +msgid "and" +msgstr "ഉം" + +#: contrib/admin/options.py:564 +#, python-format +msgid "Added %(name)s \"%(object)s\"." +msgstr "%(name)s \"%(object)s\" ചേരàµâ€à´¤àµà´¤àµ." + +#: contrib/admin/options.py:568 +#, python-format +msgid "Changed %(list)s for %(name)s \"%(object)s\"." +msgstr "%(name)s \"%(object)s\" à´¨àµà´±àµ† %(list)s മാറàµà´±à´¿." + +#: contrib/admin/options.py:573 +#, python-format +msgid "Deleted %(name)s \"%(object)s\"." +msgstr "%(name)s \"%(object)s\" ഡിലീറàµà´±àµ ചെയàµà´¤àµ." + +#: contrib/admin/options.py:577 +msgid "No fields changed." +msgstr "ഒരൠമാറàµà´±à´µàµà´®à´¿à´²àµà´²." + +#: contrib/admin/options.py:643 +#, python-format +msgid "The %(name)s \"%(obj)s\" was added successfully." +msgstr "%(name)s \"%(obj)s\" വിജയകരമായി കൂടàµà´Ÿà´¿à´šàµà´šàµ‡à´°àµà´¤àµà´¤àµ." + +#: contrib/admin/options.py:647 contrib/admin/options.py:680 +msgid "You may edit it again below." +msgstr "താഴെ നിനàµà´¨àµ വീണàµà´Ÿàµà´‚ മാറàµà´±à´‚ വരàµà´¤àµà´¤à´¾à´‚" + +#: contrib/admin/options.py:657 contrib/admin/options.py:690 +#, python-format +msgid "You may add another %s below." +msgstr "%s à´’à´¨àµà´¨àµ കൂടി ചേരàµâ€à´•àµà´•à´¾à´‚" + +#: contrib/admin/options.py:678 +#, python-format +msgid "The %(name)s \"%(obj)s\" was changed successfully." +msgstr "%(name)s \"%(obj)s\" à´²àµâ€ മാറàµà´±à´‚ വരàµà´¤àµà´¤à´¿." + +#: contrib/admin/options.py:686 +#, python-format +msgid "" +"The %(name)s \"%(obj)s\" was added successfully. You may edit it again below." +msgstr "%(name)s \"%(obj)s\" കൂടàµà´Ÿà´¿ ചേരàµâ€à´¤àµà´¤àµ. താഴെ നിനàµà´¨àµà´‚ മാറàµà´±à´‚ വരàµà´¤àµà´¤à´¾à´‚." + +#: contrib/admin/options.py:740 contrib/admin/options.py:997 +msgid "" +"Items must be selected in order to perform actions on them. No items have " +"been changed." +msgstr "ആകàµà´·à´¨àµâ€ നടപàµà´ªà´¿à´²à´¾à´•àµà´•àµ‡à´£àµà´Ÿ വകകളàµâ€ തെരഞàµà´žàµ†à´Ÿàµà´•àµà´•à´£à´‚. à´’à´¨àµà´¨àµà´‚ മാറàµà´±à´¿à´¯à´¿à´Ÿàµà´Ÿà´¿à´²àµà´²." + +#: contrib/admin/options.py:759 +msgid "No action selected." +msgstr "ആകàµà´·à´¨àµŠà´¨àµà´¨àµà´‚ തെരഞàµà´žàµ†à´Ÿàµà´¤àµà´¤à´¿à´²àµà´²." + +#: contrib/admin/options.py:840 +#, python-format +msgid "Add %s" +msgstr "%s ചേരàµâ€à´•àµà´•àµà´•" + +#: contrib/admin/options.py:866 contrib/admin/options.py:1105 +#, python-format +msgid "%(name)s object with primary key %(key)r does not exist." +msgstr "%(key)r à´Žà´¨àµà´¨ à´ªàµà´°à´¾à´¥à´®à´¿à´• കീ ഉളàµà´³ %(name)s വസàµà´¤àµ à´’à´¨àµà´¨àµà´‚ നിലവിലിലàµà´²." + +#: contrib/admin/options.py:931 +#, python-format +msgid "Change %s" +msgstr "%s മാറàµà´±à´¾à´‚" + +#: contrib/admin/options.py:977 +msgid "Database error" +msgstr "ഡേറàµà´±à´¾à´¬àµ‡à´¸àµ തകരാറാണàµ." + +#: contrib/admin/options.py:1039 +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "%(count)s %(name)s à´²àµâ€ മാറàµà´±à´‚ വരàµà´¤àµà´¤à´¿." +msgstr[1] "%(count)s %(name)s à´²àµâ€ മാറàµà´±à´‚ വരàµà´¤àµà´¤à´¿." + +#: contrib/admin/options.py:1066 +#, python-format +msgid "%(total_count)s selected" +msgid_plural "All %(total_count)s selected" +msgstr[0] "%(total_count)s തെരഞàµà´žàµ†à´Ÿàµà´¤àµà´¤àµ." +msgstr[1] "%(total_count)sഉം തെരഞàµà´žàµ†à´Ÿàµà´¤àµà´¤àµ." + +#: contrib/admin/options.py:1071 +#, python-format +msgid "0 of %(cnt)s selected" +msgstr "%(cnt)s à´²àµâ€ à´’à´¨àµà´¨àµà´‚ തെരഞàµà´žàµ†à´Ÿàµà´¤àµà´¤à´¿à´²àµà´²." + +#: contrib/admin/options.py:1118 +#, python-format +msgid "The %(name)s \"%(obj)s\" was deleted successfully." +msgstr "%(name)s \"%(obj)s\" ഡിലീറàµà´±àµ ചെയàµà´¤àµ." + +#: contrib/admin/options.py:1155 +#, python-format +msgid "Change history: %s" +msgstr "%s ലെ മാറàµà´±à´™àµà´™à´³àµâ€." + +#: contrib/admin/sites.py:18 contrib/admin/views/decorators.py:14 +#: contrib/auth/forms.py:81 +msgid "" +"Please enter a correct username and password. Note that both fields are case-" +"sensitive." +msgstr "ദയവായി ശരിയായ യൂസരàµâ€à´¨à´¾à´®à´µàµà´‚ പാസàµà´µàµ‡à´°àµà´¡àµà´‚ നലàµà´•àµà´•. ഇംഗàµà´³àµ€à´·àµ à´…à´•àµà´·à´°à´™àµà´™à´³àµâ€ വലàµà´¯à´•àµà´·à´°à´®à´¾à´£àµ‹ à´…à´²àµà´²à´¯àµ‹ à´Žà´¨àµà´¨à´¤àµ " +"à´¶àµà´°à´¦àµà´§à´¿à´•àµà´•à´£à´‚" + +#: contrib/admin/sites.py:307 contrib/admin/views/decorators.py:40 +msgid "Please log in again, because your session has expired." +msgstr "താങàµà´•à´³àµà´Ÿàµ† സെഷനàµà´±àµ† കാലാവധി à´•à´´à´¿à´žàµà´žàµ. വീണàµà´Ÿàµà´‚ ലോഗിനàµâ€ ചെയàµà´¯à´£à´‚." + +#: contrib/admin/sites.py:314 contrib/admin/views/decorators.py:47 +msgid "" +"Looks like your browser isn't configured to accept cookies. Please enable " +"cookies, reload this page, and try again." +msgstr "നിങàµà´™à´³àµà´Ÿàµ† à´¬àµà´°àµ—സരàµâ€ à´•àµà´•àµà´•àµ€à´¸àµ à´¸àµà´µàµ€à´•à´°à´¿à´•àµà´•à´¾à´¨àµâ€ തയàµà´¯à´¾à´±à´²àµà´²àµ†à´¨àµà´¨àµ തോനàµà´¨àµà´¨àµà´¨àµ. à´•àµà´•àµà´•àµ€à´¸àµ à´ªàµà´°à´µà´°àµâ€à´¤àµà´¤à´¨à´•àµà´·à´®à´®à´¾à´•àµà´•à´¿à´¯ ശേഷം " +"à´ˆ പേജൠരീലോഡൠചെയàµà´¤àµ വീണàµà´Ÿàµà´‚ à´¶àµà´°à´®à´¿à´•àµà´•àµà´•." + +#: contrib/admin/sites.py:330 contrib/admin/sites.py:336 +#: contrib/admin/views/decorators.py:66 +msgid "Usernames cannot contain the '@' character." +msgstr "യൂസരàµâ€à´¨à´¾à´®à´¤àµà´¤à´¿à´²àµâ€ '@' à´Žà´¨àµà´¨ à´šà´¿à´¹àµà´¨à´‚ പാടിലàµà´²." + +#: contrib/admin/sites.py:333 contrib/admin/views/decorators.py:62 +#, python-format +msgid "Your e-mail address is not your username. Try '%s' instead." +msgstr "നിങàµà´™à´³àµà´Ÿàµ† à´‡-മെയിലàµâ€ à´…à´¡àµà´°à´¸àµà´¸àµ à´…à´²àµà´² യൂസരàµâ€à´¨à´¾à´®à´‚. പകരം '%s' ഉപയോഗിചàµà´šàµ നോകàµà´•àµà´•." + +#: contrib/admin/sites.py:389 +msgid "Site administration" +msgstr "സൈറàµà´±àµ à´à´°à´£à´‚" + +#: contrib/admin/sites.py:403 contrib/admin/templates/admin/login.html:26 +#: contrib/admin/templates/registration/password_reset_complete.html:14 +#: contrib/admin/views/decorators.py:20 +msgid "Log in" +msgstr "ലോഗàµ-ഇനàµâ€" + +#: contrib/admin/sites.py:448 +#, python-format +msgid "%s administration" +msgstr "%s à´à´°à´£à´‚" + +#: contrib/admin/widgets.py:75 +msgid "Date:" +msgstr "തീയതി:" + +#: contrib/admin/widgets.py:75 +msgid "Time:" +msgstr "സമയം:" + +#: contrib/admin/widgets.py:99 +msgid "Currently:" +msgstr "" + +#: contrib/admin/widgets.py:99 +msgid "Change:" +msgstr "മാറàµà´±àµà´•:" + +#: contrib/admin/widgets.py:129 +msgid "Lookup" +msgstr "തിരയàµà´•" + +#: contrib/admin/widgets.py:244 +msgid "Add Another" +msgstr "à´’à´¨àµà´¨àµ കൂടി ചേരàµâ€à´•àµà´•àµà´•" + +#: contrib/admin/templates/admin/404.html:4 +#: contrib/admin/templates/admin/404.html:8 +msgid "Page not found" +msgstr "പേജൠകണàµà´Ÿà´¿à´²àµà´²" + +#: contrib/admin/templates/admin/404.html:10 +msgid "We're sorry, but the requested page could not be found." +msgstr "à´•àµà´·à´®à´¿à´•àµà´•à´£à´‚, ആവശàµà´¯à´ªàµà´ªàµ†à´Ÿàµà´Ÿ പേജൠകണàµà´Ÿàµ†à´¤àµà´¤à´¾à´¨àµâ€ à´•à´´à´¿à´žàµà´žà´¿à´²àµà´²." + +#: contrib/admin/templates/admin/500.html:4 +#: contrib/admin/templates/admin/app_index.html:8 +#: contrib/admin/templates/admin/base.html:55 +#: contrib/admin/templates/admin/change_form.html:18 +#: contrib/admin/templates/admin/change_list.html:42 +#: contrib/admin/templates/admin/delete_confirmation.html:6 +#: contrib/admin/templates/admin/delete_selected_confirmation.html:6 +#: contrib/admin/templates/admin/invalid_setup.html:4 +#: contrib/admin/templates/admin/object_history.html:6 +#: contrib/admin/templates/admin/auth/user/change_password.html:11 +#: contrib/admin/templates/registration/logged_out.html:4 +#: contrib/admin/templates/registration/password_change_done.html:4 +#: contrib/admin/templates/registration/password_change_form.html:5 +#: contrib/admin/templates/registration/password_reset_complete.html:4 +#: contrib/admin/templates/registration/password_reset_confirm.html:4 +#: contrib/admin/templates/registration/password_reset_done.html:4 +#: contrib/admin/templates/registration/password_reset_form.html:4 +#: contrib/admindocs/templates/admin_doc/bookmarklets.html:3 +msgid "Home" +msgstr "പൂമàµà´–à´‚" + +#: contrib/admin/templates/admin/500.html:4 +msgid "Server error" +msgstr "സെരàµâ€à´µà´°àµâ€ തകരാറാണàµ" + +#: contrib/admin/templates/admin/500.html:6 +msgid "Server error (500)" +msgstr "സെരàµâ€à´µà´°àµâ€ തകരാറാണൠ(500)" + +#: contrib/admin/templates/admin/500.html:9 +msgid "Server Error <em>(500)</em>" +msgstr "സെരàµâ€à´µà´°àµâ€ തകരാറാണൠ<em>(500)</em>" + +#: contrib/admin/templates/admin/500.html:10 +msgid "" +"There's been an error. It's been reported to the site administrators via e-" +"mail and should be fixed shortly. Thanks for your patience." +msgstr "à´Žà´¨àµà´¤àµ‹ തകരാറàµà´£àµà´Ÿàµ. ഉടനàµâ€ പരിഹരികàµà´•à´¾à´¨à´¾à´¯à´¿ സൈറàµà´±àµ നിയനàµà´¤àµà´°à´•à´°àµâ€à´•àµà´•àµ à´‡-മെയിലàµâ€ വഴി à´°à´¿à´ªàµà´ªàµ‹à´°àµâ€à´Ÿàµà´Ÿàµ ചെയàµà´¤à´¿à´Ÿàµà´Ÿàµà´£àµà´Ÿàµ." +"ദയവായി കാതàµà´¤à´¿à´°à´¿à´•àµà´•àµà´•." + +#: contrib/admin/templates/admin/actions.html:4 +msgid "Run the selected action" +msgstr "തെരഞàµà´žàµ†à´Ÿàµà´¤àµà´¤ ആകàµà´·à´¨àµâ€ നടപàµà´ªà´¿à´²à´¾à´•àµà´•àµà´•" + +#: contrib/admin/templates/admin/actions.html:4 +msgid "Go" +msgstr "" + +#: contrib/admin/templates/admin/actions.html:11 +msgid "Click here to select the objects across all pages" +msgstr "à´Žà´²àµà´²à´¾ പേജിലേയàµà´‚ വസàµà´¤àµà´•àµà´•à´³àµâ€ തെരഞàµà´žàµ†à´Ÿàµà´•àµà´•à´¾à´¨àµâ€ ഇവിടെ à´•àµà´²à´¿à´•àµ ചെയàµà´¯àµà´•." + +#: contrib/admin/templates/admin/actions.html:11 +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "à´®àµà´´àµà´µà´¨àµâ€ %(total_count)s %(module_name)s ഉം തെരഞàµà´žàµ†à´Ÿàµà´•àµà´•àµà´•" + +#: contrib/admin/templates/admin/actions.html:13 +msgid "Clear selection" +msgstr "തെരഞàµà´žàµ†à´Ÿàµà´¤àµà´¤à´¤àµ റദàµà´¦à´¾à´•àµà´•àµà´•." + +#: contrib/admin/templates/admin/app_index.html:10 +#: contrib/admin/templates/admin/index.html:19 +#, python-format +msgid "%(name)s" +msgstr "" + +#: contrib/admin/templates/admin/base.html:28 +msgid "Welcome," +msgstr "à´¸àµà´µà´¾à´—തം, " + +#: contrib/admin/templates/admin/base.html:33 +#: contrib/admin/templates/registration/password_change_done.html:3 +#: contrib/admin/templates/registration/password_change_form.html:4 +#: contrib/admindocs/templates/admin_doc/bookmarklets.html:3 +msgid "Documentation" +msgstr "സഹായകàµà´•àµà´±à´¿à´ªàµà´ªàµà´•à´³àµâ€" + +#: contrib/admin/templates/admin/base.html:41 +#: contrib/admin/templates/admin/auth/user/change_password.html:15 +#: contrib/admin/templates/admin/auth/user/change_password.html:48 +#: contrib/admin/templates/registration/password_change_done.html:3 +#: contrib/admin/templates/registration/password_change_form.html:4 +msgid "Change password" +msgstr "പാസൠവേരàµâ€à´¡àµ മാറàµà´±àµà´•." + +#: contrib/admin/templates/admin/base.html:48 +#: contrib/admin/templates/registration/password_change_done.html:3 +#: contrib/admin/templates/registration/password_change_form.html:4 +msgid "Log out" +msgstr "à´ªàµà´±à´¤àµà´¤àµ à´•à´Ÿà´•àµà´•àµà´•." + +#: contrib/admin/templates/admin/base_site.html:4 +msgid "Django site admin" +msgstr "ജാംഗോ സൈറàµà´±àµ à´…à´¡àµà´®à´¿à´¨àµâ€" + +#: contrib/admin/templates/admin/base_site.html:7 +msgid "Django administration" +msgstr "ജാംഗോ à´à´°à´£à´‚" + +#: contrib/admin/templates/admin/change_form.html:21 +#: contrib/admin/templates/admin/index.html:29 +msgid "Add" +msgstr "" + +#: contrib/admin/templates/admin/change_form.html:28 +#: contrib/admin/templates/admin/object_history.html:10 +msgid "History" +msgstr "à´šà´°à´¿à´¤àµà´°à´‚" + +#: contrib/admin/templates/admin/change_form.html:29 +#: contrib/admin/templates/admin/edit_inline/stacked.html:9 +#: contrib/admin/templates/admin/edit_inline/tabular.html:28 +msgid "View on site" +msgstr "" + +#: contrib/admin/templates/admin/change_form.html:39 +#: contrib/admin/templates/admin/change_list.html:71 +#: contrib/admin/templates/admin/auth/user/change_password.html:24 +#: contrib/admin/templates/registration/password_change_form.html:15 +msgid "Please correct the error below." +msgid_plural "Please correct the errors below." +msgstr[0] "ദയവായി താഴെയàµà´³àµà´³ തെറàµà´±àµ പരിഹരികàµà´•àµà´•." +msgstr[1] "ദയവായി താഴെയàµà´³àµà´³ തെറàµà´±àµà´•à´³àµâ€ പരിഹരികàµà´•àµà´•." + +#: contrib/admin/templates/admin/change_list.html:63 +#, python-format +msgid "Add %(name)s" +msgstr "" + +#: contrib/admin/templates/admin/change_list.html:82 +msgid "Filter" +msgstr "" + +#: contrib/admin/templates/admin/delete_confirmation.html:10 +#: contrib/admin/templates/admin/submit_line.html:4 forms/formsets.py:302 +msgid "Delete" +msgstr "" + +#: contrib/admin/templates/admin/delete_confirmation.html:16 +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "%(object_name)s '%(escaped_object)s ഡിലീറàµà´±àµ ചെയàµà´¯àµà´®àµà´ªàµ‹à´³àµâ€ à´…à´¤àµà´®à´¾à´¯à´¿ ബനàµà´§à´®àµà´³àµà´³ വസàµà´¤àµà´•àµà´•à´³àµà´‚" +"ഡിലീറàµà´±àµ ആവàµà´‚. പകàµà´·àµ‡ നിങàµà´™à´³àµâ€à´•àµà´•àµ താഴെ പറഞàµà´ž തരം വസàµà´¤àµà´•àµà´•à´³àµâ€ ഡിലീറàµà´±àµ ചെയàµà´¯à´¾à´¨àµà´³àµà´³ à´…à´¨àµà´®à´¤à´¿ ഇലàµà´²:" + +#: contrib/admin/templates/admin/delete_confirmation.html:23 +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "%(object_name)s \"%(escaped_object)s\" ഡിലീറàµà´±àµ ചെയàµà´¯à´£à´®àµ†à´¨àµà´¨àµ തീരàµâ€à´šàµà´šà´¯à´¾à´£àµ‹?" +"à´…à´¤àµà´®à´¾à´¯à´¿ ബനàµà´§à´®àµà´³àµà´³ താഴെപàµà´ªà´±à´¯àµà´¨àµà´¨ വസàµà´¤àµà´•àµà´•à´³àµ†à´²àµà´²à´¾à´‚ ഡിലീറàµà´±àµ ആവàµà´‚:" + +#: contrib/admin/templates/admin/delete_confirmation.html:28 +#: contrib/admin/templates/admin/delete_selected_confirmation.html:33 +msgid "Yes, I'm sure" +msgstr "അതെ, തീരàµâ€à´šàµà´šà´¯à´¾à´£àµ" + +#: contrib/admin/templates/admin/delete_selected_confirmation.html:9 +msgid "Delete multiple objects" +msgstr "à´’à´¨àµà´¨à´¿à´²àµ‡à´±àµ† വസàµà´¤àµà´•àµà´•à´³àµâ€ ഡിലീറàµà´±àµ ചെയàµà´¤àµ‹à´³àµ‚" + +#: contrib/admin/templates/admin/delete_selected_confirmation.html:15 +#, python-format +msgid "" +"Deleting the %(object_name)s would result in deleting related objects, but " +"your account doesn't have permission to delete the following types of " +"objects:" +msgstr "%(object_name)s ഡിലീറàµà´±àµ ചെയàµà´¯àµà´®àµà´ªàµ‹à´³àµâ€ à´…à´¤àµà´®à´¾à´¯à´¿ ബനàµà´§à´®àµà´³àµà´³ വസàµà´¤àµà´•àµà´•à´³àµà´‚" +"ഡിലീറàµà´±àµ ആവàµà´‚. പകàµà´·àµ‡ നിങàµà´™à´³àµâ€à´•àµà´•àµ താഴെ പറഞàµà´ž തരം വസàµà´¤àµà´•àµà´•à´³àµâ€ ഡിലീറàµà´±àµ ചെയàµà´¯à´¾à´¨àµà´³àµà´³ à´…à´¨àµà´®à´¤à´¿ ഇലàµà´²:" + +#: contrib/admin/templates/admin/delete_selected_confirmation.html:22 +#, python-format +msgid "" +"Are you sure you want to delete the selected %(object_name)s objects? All of " +"the following objects and their related items will be deleted:" +msgstr "തെരഞàµà´žàµ†à´Ÿàµà´¤àµà´¤ %(object_name)s à´Žà´²àµà´²à´¾à´‚ ഡിലീറàµà´±àµ ചെയàµà´¯à´£à´®àµ†à´¨àµà´¨àµ തീരàµâ€à´šàµà´šà´¯à´¾à´£àµ‹?" +"താഴെപàµà´ªà´±à´¯àµà´¨àµà´¨ വസàµà´¤àµà´•àµà´•à´³àµà´‚ à´…à´¤àµà´®à´¾à´¯à´¿ ബനàµà´§à´®àµà´³àµà´³à´¤àµ†à´²àµà´²à´¾à´‚ ഡിലീറàµà´±àµ ആവàµà´‚:" + +#: contrib/admin/templates/admin/filter.html:2 +#, python-format +msgid " By %(filter_title)s " +msgstr "" + +#: contrib/admin/templates/admin/index.html:18 +#, python-format +msgid "Models available in the %(name)s application." +msgstr "" + +#: contrib/admin/templates/admin/index.html:35 +msgid "Change" +msgstr "മാറàµà´±àµà´•" + +#: contrib/admin/templates/admin/index.html:45 +msgid "You don't have permission to edit anything." +msgstr "à´’à´¨àµà´¨à´¿à´²àµà´‚ മാറàµà´±à´‚ വരàµà´¤àµà´¤à´¾à´¨àµà´³àµà´³ à´…à´¨àµà´®à´¤à´¿ ഇലàµà´²." + +#: contrib/admin/templates/admin/index.html:53 +msgid "Recent Actions" +msgstr "സമീപകാല à´ªàµà´°à´µàµà´°àµà´¤àµà´¤à´¿à´•à´³àµâ€" + +#: contrib/admin/templates/admin/index.html:54 +msgid "My Actions" +msgstr "à´Žà´¨àµà´±àµ† à´ªàµà´°à´µàµà´°àµà´¤àµà´¤à´¿à´•à´³àµâ€" + +#: contrib/admin/templates/admin/index.html:58 +msgid "None available" +msgstr "à´’à´¨àµà´¨àµà´‚ à´²à´àµà´¯à´®à´²àµà´²" + +#: contrib/admin/templates/admin/index.html:72 +msgid "Unknown content" +msgstr "ഉളàµà´³à´Ÿà´•àµà´•à´‚ അറിയിലàµà´²." + +#: contrib/admin/templates/admin/invalid_setup.html:7 +msgid "" +"Something's wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "നിങàµà´™à´³àµà´Ÿàµ† ഡേറàµà´±à´¾à´¬àµ‡à´¸àµ ഇനàµâ€à´¸àµà´Ÿà´¾à´²àµ‡à´·à´¨à´¿à´²àµâ€ à´Žà´¨àµà´¤àµ‹ പിശകàµà´£àµà´Ÿàµ. ശരിയായ ടേബിളàµà´•à´³àµâ€ ഉണàµà´Ÿàµ†à´¨àµà´¨àµà´‚ ഡേറàµà´±à´¾à´¬àµ‡à´¸àµ " +"വായനായോഗàµà´¯à´®à´¾à´£àµ†à´¨àµà´¨àµà´‚ ഉറപàµà´ªàµ വരàµà´¤àµà´¤àµà´•." + +#: contrib/admin/templates/admin/login.html:19 +msgid "Username:" +msgstr "യൂസരàµâ€ നാമം" + +#: contrib/admin/templates/admin/login.html:22 +msgid "Password:" +msgstr "പാസൠവേരàµâ€à´¡àµ" + +#: contrib/admin/templates/admin/object_history.html:22 +msgid "Date/time" +msgstr "തീയതി/സമയം" + +#: contrib/admin/templates/admin/object_history.html:23 +msgid "User" +msgstr "യൂസരàµâ€" + +#: contrib/admin/templates/admin/object_history.html:24 +msgid "Action" +msgstr "ആകàµà´·à´¨àµâ€" + +#: contrib/admin/templates/admin/object_history.html:38 +msgid "" +"This object doesn't have a change history. It probably wasn't added via this " +"admin site." +msgstr "à´ˆ വസàµà´¤àµà´µà´¿à´¨àµà´±àµ† മാറàµà´±à´™àµà´™à´³àµà´Ÿàµ† à´šà´°à´¿à´¤àµà´°à´‚ à´²à´àµà´¯à´®à´²àµà´². à´’à´°àµà´ªà´•àµà´·àµ† ഇതൠഅഡàµà´®à´¿à´¨àµâ€ സൈറàµà´±àµ വഴി ചേരàµâ€à´¤àµà´¤à´¤à´¾à´¯à´¿à´°à´¿à´•àµà´•à´¿à´²àµà´²." + +#: contrib/admin/templates/admin/pagination.html:10 +msgid "Show all" +msgstr "à´Žà´²àµà´²à´¾à´‚ കാണടàµà´Ÿàµ†" + +#: contrib/admin/templates/admin/pagination.html:11 +#: contrib/admin/templates/admin/submit_line.html:3 +msgid "Save" +msgstr "സേവൠചെയàµà´¯à´£à´‚" + +#: contrib/admin/templates/admin/search_form.html:8 +msgid "Search" +msgstr "പരതàµà´•" + +#: contrib/admin/templates/admin/search_form.html:10 +#, python-format +msgid "1 result" +msgid_plural "%(counter)s results" +msgstr[0] "1 ഫലം" +msgstr[1] "%(counter)s ഫലങàµà´™à´³àµâ€" + +#: contrib/admin/templates/admin/search_form.html:10 +#, python-format +msgid "%(full_result_count)s total" +msgstr "ആകെ %(full_result_count)s" + +#: contrib/admin/templates/admin/submit_line.html:5 +msgid "Save as new" +msgstr "à´ªàµà´¤à´¿à´¯à´¤à´¾à´¯à´¿ സേവൠചെയàµà´¯à´£à´‚" + +#: contrib/admin/templates/admin/submit_line.html:6 +msgid "Save and add another" +msgstr "സേവൠചെയàµà´¤ ശേഷം വേറെ ചേരàµâ€à´•àµà´•à´£à´‚" + +#: contrib/admin/templates/admin/submit_line.html:7 +msgid "Save and continue editing" +msgstr "സേവൠചെയàµà´¤ ശേഷം മാറàµà´±à´‚ വരàµà´¤àµà´¤à´¾à´‚" + +#: contrib/admin/templates/admin/auth/user/add_form.html:5 +msgid "" +"First, enter a username and password. Then, you'll be able to edit more user " +"options." +msgstr "ആദàµà´¯à´‚, യൂസരàµâ€ നാമവàµà´‚ പാസൠവേരàµâ€à´¡àµà´‚ നലàµà´•à´£à´‚. പിനàµà´¨àµ†, കൂടàµà´¤à´²àµâ€ കാരàµà´¯à´™àµà´™à´³àµâ€ മാറàµà´±à´¾à´µàµà´¨àµà´¨à´¤à´¾à´£àµ." + +#: contrib/admin/templates/admin/auth/user/change_password.html:28 +#, python-format +msgid "Enter a new password for the user <strong>%(username)s</strong>." +msgstr "<strong>%(username)s</strong> നൠപàµà´¤à´¿à´¯ പാസൠവേരàµâ€à´¡àµ നലàµà´•àµà´•." + +#: contrib/admin/templates/admin/auth/user/change_password.html:35 +#: contrib/auth/forms.py:17 contrib/auth/forms.py:61 contrib/auth/forms.py:186 +msgid "Password" +msgstr "പാസൠവേരàµâ€à´¡àµ" + +#: contrib/admin/templates/admin/auth/user/change_password.html:41 +#: contrib/admin/templates/registration/password_change_form.html:37 +#: contrib/auth/forms.py:187 +msgid "Password (again)" +msgstr "പാസൠവേരàµâ€à´¡àµ (വീണàµà´Ÿàµà´‚)" + +#: contrib/admin/templates/admin/auth/user/change_password.html:42 +#: contrib/auth/forms.py:19 +msgid "Enter the same password as above, for verification." +msgstr "പാസൠവേരàµâ€à´¡àµ à´®àµà´•à´³à´¿à´²àµ† പോലെ തനàµà´¨àµ† നലàµà´•àµà´•. (ഉറപàµà´ªàµ വരàµà´¤àµà´¤à´¾à´¨à´¾à´£àµ.)" + +#: contrib/admin/templates/admin/edit_inline/stacked.html:64 +#: contrib/admin/templates/admin/edit_inline/tabular.html:110 +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "%(verbose_name)s à´’à´¨àµà´¨àµ കൂടി ചേരàµâ€à´•àµà´•àµà´•" + +#: contrib/admin/templates/admin/edit_inline/stacked.html:67 +#: contrib/admin/templates/admin/edit_inline/tabular.html:113 +#: contrib/comments/templates/comments/delete.html:12 +msgid "Remove" +msgstr "നീകàµà´•à´‚ ചെയàµà´¯àµà´•" + +#: contrib/admin/templates/admin/edit_inline/tabular.html:15 +msgid "Delete?" +msgstr "ഡിലീറàµà´±àµ ചെയàµà´¯à´Ÿàµà´Ÿàµ†?" + +#: contrib/admin/templates/registration/logged_out.html:8 +msgid "Thanks for spending some quality time with the Web site today." +msgstr "à´ˆ വെബൠസൈറàµà´±à´¿à´²àµâ€ à´•àµà´±àµ† നലàµà´² സമയം ചെലവഴിചàµà´šà´¤à´¿à´¨àµ നനàµà´¦à´¿." + +#: contrib/admin/templates/registration/logged_out.html:10 +msgid "Log in again" +msgstr "വീണàµà´Ÿàµà´‚ ലോഗàµ-ഇനàµâ€ ചെയàµà´¯àµà´•." + +#: contrib/admin/templates/registration/password_change_done.html:4 +#: contrib/admin/templates/registration/password_change_form.html:5 +#: contrib/admin/templates/registration/password_change_form.html:7 +#: contrib/admin/templates/registration/password_change_form.html:19 +msgid "Password change" +msgstr "പാസൠവേരàµâ€à´¡àµ മാറàµà´±à´‚" + +#: contrib/admin/templates/registration/password_change_done.html:6 +#: contrib/admin/templates/registration/password_change_done.html:10 +msgid "Password change successful" +msgstr "പാസൠവേരàµâ€à´¡àµ മാറàµà´±à´‚ വിജയിചàµà´šàµ" + +#: contrib/admin/templates/registration/password_change_done.html:12 +msgid "Your password was changed." +msgstr "നിങàµà´™à´³àµà´Ÿàµ† പാസൠവേരàµâ€à´¡àµ മാറàµà´±à´¿à´•àµà´•à´´à´¿à´žàµà´žàµ." + +#: contrib/admin/templates/registration/password_change_form.html:21 +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 "à´¸àµà´°à´•àµà´·à´¯àµà´•àµà´•à´¾à´¯à´¿ നിങàµà´™à´³àµà´Ÿàµ† പഴയ പാസൠവേരàµâ€à´¡àµ നലàµà´•àµà´•. പിനàµà´¨àµ†, à´ªàµà´¤à´¿à´¯ പാസൠവേരàµâ€à´¡àµ à´°à´£àµà´Ÿàµ തവണ നലàµà´•àµà´•. " +"(à´Ÿà´¯àµà´ªàµ ചെയàµà´¤à´¤àµ ശരിയാണെനàµà´¨àµ ഉറപàµà´ªà´¾à´•àµà´•à´¾à´¨àµâ€)" + +#: contrib/admin/templates/registration/password_change_form.html:27 +#: contrib/auth/forms.py:170 +msgid "Old password" +msgstr "പഴയ പാസൠവേരàµâ€à´¡àµ" + +#: contrib/admin/templates/registration/password_change_form.html:32 +#: contrib/auth/forms.py:144 +msgid "New password" +msgstr "à´ªàµà´¤à´¿à´¯ പാസൠവേരàµâ€à´¡àµ" + +#: contrib/admin/templates/registration/password_change_form.html:43 +#: contrib/admin/templates/registration/password_reset_confirm.html:21 +msgid "Change my password" +msgstr "à´Žà´¨àµà´±àµ† പാസൠവേരàµâ€à´¡àµ മാറàµà´±à´£à´‚" + +#: contrib/admin/templates/registration/password_reset_complete.html:4 +#: contrib/admin/templates/registration/password_reset_confirm.html:6 +#: contrib/admin/templates/registration/password_reset_done.html:4 +#: 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 +msgid "Password reset" +msgstr "പാസൠവേരàµâ€à´¡àµ à´ªàµà´¨à´¸àµà´¥à´¾à´ªà´¿à´•àµà´•à´²àµâ€" + +#: contrib/admin/templates/registration/password_reset_complete.html:6 +#: contrib/admin/templates/registration/password_reset_complete.html:10 +msgid "Password reset complete" +msgstr "പാസൠവേരàµâ€à´¡àµ à´ªàµà´¨à´¸àµà´¥à´¾à´ªà´¿à´•àµà´•à´²àµâ€ പൂരàµâ€à´£à´‚" + +#: contrib/admin/templates/registration/password_reset_complete.html:12 +msgid "Your password has been set. You may go ahead and log in now." +msgstr "നിങàµà´™à´³àµà´Ÿàµ† പാസൠവേരàµâ€à´¡àµ തയàµà´¯à´¾à´°àµâ€. ഇനി ലോഗàµ-ഇനàµâ€ ചെയàµà´¯à´¾à´‚." + +#: contrib/admin/templates/registration/password_reset_confirm.html:4 +msgid "Password reset confirmation" +msgstr "പാസൠവേരàµâ€à´¡àµ à´ªàµà´¨à´¸àµà´¥à´¾à´ªà´¿à´•àµà´•à´²àµâ€ ഉറപàµà´ªà´¾à´•àµà´•à´²àµâ€" + +#: contrib/admin/templates/registration/password_reset_confirm.html:12 +msgid "Enter new password" +msgstr "à´ªàµà´¤à´¿à´¯ പാസൠവേരàµâ€à´¡àµ നലàµà´•àµ‚" + +#: contrib/admin/templates/registration/password_reset_confirm.html:14 +msgid "" +"Please enter your new password twice so we can verify you typed it in " +"correctly." +msgstr "ദയവായി നിങàµà´™à´³àµà´Ÿàµ† à´ªàµà´¤à´¿à´¯ പാസൠവേരàµâ€à´¡àµ à´°à´£àµà´Ÿàµ തവണ നലàµà´•à´£à´‚. ശരിയായാണൠടൈപàµà´ªàµ ചെയàµà´¤à´¤àµ à´Žà´¨àµà´¨àµ ഉറപàµà´ªà´¿à´•àµà´•à´¾à´¨à´¾à´£àµ." + +#: contrib/admin/templates/registration/password_reset_confirm.html:18 +msgid "New password:" +msgstr "à´ªàµà´¤à´¿à´¯ പാസൠവേരàµâ€à´¡àµ:" + +#: contrib/admin/templates/registration/password_reset_confirm.html:20 +msgid "Confirm password:" +msgstr "പാസൠവേരàµâ€à´¡àµ ഉറപàµà´ªà´¾à´•àµà´•àµ‚:" + +#: contrib/admin/templates/registration/password_reset_confirm.html:26 +msgid "Password reset unsuccessful" +msgstr "പാസൠവേരàµâ€à´¡àµ à´ªàµà´¨à´¸àµà´¥à´¾à´ªà´¿à´•àµà´•à´²àµâ€ പരാജയം" + +#: contrib/admin/templates/registration/password_reset_confirm.html:28 +msgid "" +"The password reset link was invalid, possibly because it has already been " +"used. Please request a new password reset." +msgstr "പാസൠവേരàµâ€à´¡àµ à´ªàµà´¨à´¸àµà´¥à´¾à´ªà´¿à´•àµà´•à´¾à´¨àµâ€ നലàµà´•à´¿à´¯ ലിങàµà´•àµ യോഗàµà´¯à´®à´²àµà´². ഒരൠപകàµà´·àµ‡, അതൠമàµà´¨àµà´ªàµ തനàµà´¨àµ† ഉപയോഗിചàµà´šàµ " +"à´•à´´à´¿à´žàµà´žà´¤à´¾à´µà´¾à´‚. à´ªàµà´¤à´¿à´¯ ഒരൠലിങàµà´•à´¿à´¨àµ അപേകàµà´·à´¿à´•àµà´•àµ‚." + +#: contrib/admin/templates/registration/password_reset_done.html:6 +#: contrib/admin/templates/registration/password_reset_done.html:10 +msgid "Password reset successful" +msgstr "പാസൠവേരàµâ€à´¡àµ à´ªàµà´¨à´¸àµà´¥à´¾à´ªà´¿à´•àµà´•à´²àµâ€ വിജയം" + +#: contrib/admin/templates/registration/password_reset_done.html:12 +msgid "" +"We've e-mailed you instructions for setting your password to the e-mail " +"address you submitted. You should be receiving it shortly." +msgstr "നിങàµà´™à´³àµà´Ÿàµ† പാസൠവേരàµâ€à´¡àµ à´ªàµà´¨à´¸àµà´¥à´¾à´ªà´¿à´•àµà´•à´¾à´¨à´¾à´¯à´¿ നിരàµâ€à´¦àµà´¦àµ‡à´¶à´™àµà´™à´³àµâ€ à´…à´Ÿà´™àµà´™à´¿à´¯ ഒരൠഈ-മെയിലàµâ€ നിങàµà´™à´³àµâ€ നലàµà´•à´¿à´¯" +"വിലാസതàµà´¤à´¿à´²àµâ€ അയചàµà´šà´¿à´Ÿàµà´Ÿàµà´£àµà´Ÿàµ. അതൠനിങàµà´™à´³àµâ€à´•àµà´•àµ ഉടനàµâ€ à´²à´à´¿à´•àµà´•àµ‡à´£àµà´Ÿà´¤à´¾à´£àµ." + +#: contrib/admin/templates/registration/password_reset_email.html:2 +msgid "You're receiving this e-mail because you requested a password reset" +msgstr "നിങàµà´™à´³àµâ€ പാസൠവേരàµâ€à´¡àµ à´ªàµà´¨à´¸àµà´¥à´¾à´ªà´¿à´•àµà´•à´¾à´¨àµâ€ അപേകàµà´·à´¿à´šàµà´šà´¤àµ കൊണàµà´Ÿà´¾à´£àµ à´ˆ à´‡-മെയിലàµâ€ നിങàµà´™à´³àµâ€à´•àµà´•àµ à´²à´à´¿à´•àµà´•àµà´¨àµà´¨à´¤àµ." + +#: contrib/admin/templates/registration/password_reset_email.html:3 +#, python-format +msgid "for your user account at %(site_name)s" +msgstr "നിങàµà´™à´³àµà´Ÿàµ† %(site_name)s à´Žà´¨àµà´¨ സൈറàµà´±à´¿à´²àµ† à´…à´•àµà´•àµ—à´£àµà´Ÿà´¿à´¨àµ വേണàµà´Ÿà´¿." + +#: contrib/admin/templates/registration/password_reset_email.html:5 +msgid "Please go to the following page and choose a new password:" +msgstr "ദയവായി താഴെ പറയàµà´¨àµà´¨ പേജൠസനàµà´¦à´°àµâ€à´¶à´¿à´šàµà´šàµ à´ªàµà´¤à´¿à´¯ പാസൠവേരàµâ€à´¡àµ തെരഞàµà´žàµ†à´Ÿàµà´•àµà´•àµà´•:" + +#: contrib/admin/templates/registration/password_reset_email.html:9 +msgid "Your username, in case you've forgotten:" +msgstr "നിങàµà´™à´³àµâ€ മറനàµà´¨àµ†à´™àµà´•à´¿à´²àµâ€, നിങàµà´™à´³àµà´Ÿàµ† യൂസരàµâ€ നാമം, :" + +#: contrib/admin/templates/registration/password_reset_email.html:11 +msgid "Thanks for using our site!" +msgstr "à´žà´™àµà´™à´³àµà´Ÿàµ† സൈറàµà´±àµ ഉപയോഗിചàµà´šà´¤à´¿à´¨àµ നനàµà´¦à´¿!" + +#: contrib/admin/templates/registration/password_reset_email.html:13 +#, python-format +msgid "The %(site_name)s team" +msgstr "" + +#: contrib/admin/templates/registration/password_reset_form.html:12 +msgid "" +"Forgotten your password? Enter your e-mail address below, and we'll e-mail " +"instructions for setting a new one." +msgstr "പാസൠവേരàµâ€à´¡àµ മറനàµà´¨àµ‹? നിങàµà´™à´³àµà´Ÿàµ† à´‡-മെയിലàµâ€ വിലാസം നലàµà´•àµ‚, à´ªàµà´¤à´¿à´¯ പാസൠവേരàµâ€à´¡àµ à´¸àµà´¥à´¾à´ªà´¿à´•àµà´•à´¾à´¨à´¾à´¯à´¿ " +"à´žà´™àµà´™à´³àµâ€ നിരàµâ€à´¦àµà´¦àµ‡à´¶à´™àµà´™à´³àµâ€ à´† വിലാസതàµà´¤à´¿à´²àµâ€ അയകàµà´•à´¾à´‚." + +#: contrib/admin/templates/registration/password_reset_form.html:16 +msgid "E-mail address:" +msgstr "à´‡-മെയിലàµâ€ വിലാസം:" + +#: contrib/admin/templates/registration/password_reset_form.html:16 +msgid "Reset my password" +msgstr "à´Žà´¨àµà´±àµ† പാസൠവേരàµâ€à´¡àµ à´ªàµà´¨à´¸àµà´¥à´¾à´ªà´¿à´•àµà´•àµ‚" + +#: contrib/admin/templatetags/admin_list.py:257 +msgid "All dates" +msgstr "à´Žà´²àµà´²à´¾ തീയതികളàµà´‚" + +#: contrib/admin/views/main.py:65 +#, python-format +msgid "Select %s" +msgstr "%s തെരഞàµà´žàµ†à´Ÿàµà´•àµà´•àµ‚" + +#: contrib/admin/views/main.py:65 +#, python-format +msgid "Select %s to change" +msgstr "മാറàµà´±à´¾à´¨àµà´³àµà´³ %s തെരഞàµà´žàµ†à´Ÿàµà´•àµà´•àµ‚" + +#: contrib/admin/views/template.py:38 contrib/sites/models.py:38 +msgid "site" +msgstr "സൈറàµà´±àµ" + +#: contrib/admin/views/template.py:40 +msgid "template" +msgstr "ടെമàµà´ªàµà´²àµ‡à´±àµà´±àµ" + +#: contrib/admindocs/views.py:61 contrib/admindocs/views.py:63 +#: contrib/admindocs/views.py:65 +msgid "tag:" +msgstr "ടാഗàµ:" + +#: contrib/admindocs/views.py:94 contrib/admindocs/views.py:96 +#: contrib/admindocs/views.py:98 +msgid "filter:" +msgstr "à´…à´°à´¿à´ªàµà´ª:" + +#: contrib/admindocs/views.py:158 contrib/admindocs/views.py:160 +#: contrib/admindocs/views.py:162 +msgid "view:" +msgstr "à´µàµà´¯àµ‚" + +#: contrib/admindocs/views.py:190 +#, python-format +msgid "App %r not found" +msgstr "%r à´Žà´¨àµà´¨ App à´•à´£àµà´Ÿà´¿à´²àµà´²." + +#: contrib/admindocs/views.py:197 +#, python-format +msgid "Model %(model_name)r not found in app %(app_label)r" +msgstr "%(app_label)r à´Žà´¨àµà´¨ Appà´²àµâ€ %(model_name)r à´Žà´¨àµà´¨ മാതàµà´°àµà´• à´•à´£àµà´Ÿà´¿à´²àµà´²." + +#: contrib/admindocs/views.py:209 +#, python-format +msgid "the related `%(app_label)s.%(data_type)s` object" +msgstr "ബനàµà´§à´ªàµà´ªàµ†à´Ÿàµà´Ÿ `%(app_label)s.%(data_type)s` വസàµà´¤àµ" + +#: contrib/admindocs/views.py:209 contrib/admindocs/views.py:228 +#: contrib/admindocs/views.py:233 contrib/admindocs/views.py:247 +#: contrib/admindocs/views.py:261 contrib/admindocs/views.py:266 +msgid "model:" +msgstr "മാതàµà´°àµà´•:" + +#: contrib/admindocs/views.py:224 contrib/admindocs/views.py:256 +#, python-format +msgid "related `%(app_label)s.%(object_name)s` objects" +msgstr "ബനàµà´§à´ªàµà´ªàµ†à´Ÿàµà´Ÿ `%(app_label)s.%(object_name)s` വസàµà´¤àµà´•àµà´•à´³àµâ€" + +#: contrib/admindocs/views.py:228 contrib/admindocs/views.py:261 +#, python-format +msgid "all %s" +msgstr "%s à´Žà´²àµà´²à´¾à´‚" + +#: contrib/admindocs/views.py:233 contrib/admindocs/views.py:266 +#, python-format +msgid "number of %s" +msgstr "%sà´¨àµà´±àµ† à´Žà´£àµà´£à´‚" + +#: contrib/admindocs/views.py:271 +#, python-format +msgid "Fields on %s objects" +msgstr "%s വസàµà´¤àµà´•àµà´•à´³à´¿à´²àµ† വിവരങàµà´™à´³àµâ€" + +#: contrib/admindocs/views.py:361 +#, python-format +msgid "%s does not appear to be a urlpattern object" +msgstr "%s വിലാസ മാതàµà´°àµà´• (urlpattern object) ആണെനàµà´¨àµ തോനàµà´¨àµà´¨àµà´¨à´¿à´²àµà´²." + +#: contrib/admindocs/templates/admin_doc/bookmarklets.html:3 +msgid "Bookmarklets" +msgstr "à´¬àµà´•àµà´•àµ മാരàµâ€à´•àµà´•àµà´•à´³àµâ€" + +#: contrib/admindocs/templates/admin_doc/bookmarklets.html:4 +msgid "Documentation bookmarklets" +msgstr "സഹായകàµà´•àµà´±à´¿à´ªàµà´ªàµà´•à´³àµà´Ÿàµ† à´¬àµà´•àµà´•àµà´®à´¾à´°àµâ€à´•àµà´•àµà´•à´³àµâ€" + +#: contrib/admindocs/templates/admin_doc/bookmarklets.html:8 +msgid "" +"\n" +"<p class=\"help\">To install bookmarklets, drag the link to your bookmarks\n" +"toolbar, or right-click the link and add it to your bookmarks. Now you can\n" +"select the bookmarklet from any page in the site. Note that some of these\n" +"bookmarklets require you to be viewing the site from a computer designated\n" +"as \"internal\" (talk to your system administrator if you aren't sure if\n" +"your computer is \"internal\").</p>\n" +msgstr "" +"\n" +"<p class=\"help\">à´¬àµà´•àµà´•àµà´®à´¾à´°àµâ€à´•àµà´•àµà´²àµ†à´±àµà´±àµà´•à´³àµâ€ ഇനàµâ€à´¸àµà´±àµà´±à´¾à´³àµâ€ ചെയàµà´¯à´¾à´¨àµâ€, ലിങàµà´•à´¿à´¨àµ† നിങàµà´™à´³àµà´Ÿàµ† à´¬àµà´•àµà´•àµà´®à´¾à´°àµâ€à´•àµ ടൂളàµâ€à´¬à´¾à´±à´¿à´²àµ‡à´•àµà´•àµ \n" +"വലിചàµà´šà´¿à´Ÿàµà´•à´¯àµ‹, ലിങàµà´•à´¿à´¨àµâ€à´®àµ‡à´²àµâ€ റൈറàµà´±àµà´•àµà´³à´¿à´•àµ ചെയàµà´¤àµ à´¬àµà´•àµà´•àµà´®à´¾à´°àµâ€à´•àµà´•à´¾à´¯à´¿ ചേരàµâ€à´•àµà´•àµà´•à´¯àµ‹ ചെയàµà´¯àµà´•. ഇനി സൈറàµà´±à´¿à´²àµ† à´à´¤àµ പേജിലàµâ€ നിനàµà´¨àµà´‚\n" +" à´ˆ à´¬àµà´•àµà´•àµà´®à´¾à´°àµà´•àµ തെരഞàµà´žàµ†à´Ÿàµà´•àµà´•à´¾à´‚. à´šà´¿à´² à´¬àµà´•àµà´•àµà´®à´¾à´°àµâ€à´•àµà´•àµà´•à´³àµâ€ ഇനàµà´±àµ‡à´£à´²àµâ€ ആയ à´•à´®àµà´ªàµà´¯àµ‚à´Ÿàµà´Ÿà´±à´¿à´²àµâ€ നിനàµà´¨àµ‡ à´²à´àµà´¯à´®à´¾à´µàµ‚ à´Žà´¨àµà´¨àµ à´¶àµà´°à´¦àµà´§à´¿à´•àµà´•à´£à´‚.\n" +"നിങàµà´™à´³àµà´Ÿàµ† à´•à´‚à´ªàµà´¯àµ‚à´Ÿàµà´Ÿà´°àµâ€ à´…à´¤àµà´¤à´°à´¤àµà´¤à´¿à´²àµâ€ പെടàµà´Ÿà´¤à´¾à´£àµ‹ à´Žà´¨àµà´¨à´±à´¿à´¯à´¾à´¨àµâ€ സിസàµà´±àµà´±à´‚ à´…à´¡àµà´®à´¿à´¨à´¿à´¸àµà´Ÿàµà´°àµ‡à´Ÿàµà´Ÿà´±àµ† ബനàµà´§à´ªàµà´ªàµ†à´Ÿàµà´•.</p>\n" + +#: contrib/admindocs/templates/admin_doc/bookmarklets.html:18 +msgid "Documentation for this page" +msgstr "à´ˆ പേജിനàµà´±àµ† സഹായകàµà´•àµà´±à´¿à´ªàµà´ªàµà´•à´³àµâ€" + +#: contrib/admindocs/templates/admin_doc/bookmarklets.html:19 +msgid "" +"Jumps you from any page to the documentation for the view that generates " +"that page." +msgstr "à´à´¤àµ പേജിലàµâ€ നിനàµà´¨àµà´‚ അതിനàµà´±àµ† ഉദàµà´à´µà´®à´¾à´¯ à´µàµà´¯àµ‚വിനàµà´±àµ† സഹായകàµà´•àµà´±à´¿à´ªàµà´ªà´¿à´²àµ‡à´•àµà´•àµ ചാടാനàµâ€" + +#: contrib/admindocs/templates/admin_doc/bookmarklets.html:21 +msgid "Show object ID" +msgstr "വസàµà´¤àµà´µà´¿à´¨àµà´±àµ† à´à´¡à´¿ കാണികàµà´•àµà´•." + +#: contrib/admindocs/templates/admin_doc/bookmarklets.html:22 +msgid "" +"Shows the content-type and unique ID for pages that represent a single " +"object." +msgstr "à´’à´±àµà´± വസàµà´¤àµà´µà´¿à´¨àµ† à´ªàµà´°à´¤à´¿à´¨à´¿à´§àµ€à´•à´°à´¿à´•àµà´•àµà´¨àµà´¨ പേജàµà´•à´³àµà´Ÿàµ† ഉളàµà´³à´Ÿà´•àµà´•à´¤àµà´¤à´¿à´¨àµà´±àµ† തരവàµà´‚ തനതായ IDà´¯àµà´‚ കാണികàµà´•àµà´¨àµà´¨àµ." + +#: contrib/admindocs/templates/admin_doc/bookmarklets.html:24 +msgid "Edit this object (current window)" +msgstr "à´ˆ വസàµà´¤àµà´µà´¿à´²àµ മാറàµà´±à´‚ വരàµà´¤àµà´¤àµà´• (ഇതേ വിനàµâ€à´¡àµ‹)" + +#: contrib/admindocs/templates/admin_doc/bookmarklets.html:25 +msgid "Jumps to the admin page for pages that represent a single object." +msgstr "à´’à´±àµà´± വസàµà´¤àµà´µà´¿à´¨àµ† à´ªàµà´°à´¤à´¿à´¨à´¿à´§àµ€à´•à´°à´¿à´•àµà´•àµà´¨àµà´¨ പേജàµà´•à´³àµâ€à´•àµà´•àµà´³àµà´³ à´…à´¡àµà´®à´¿à´¨àµâ€ പേജിലേകàµà´•àµ ചാടàµà´¨àµà´¨àµ." + +#: contrib/admindocs/templates/admin_doc/bookmarklets.html:27 +msgid "Edit this object (new window)" +msgstr "à´ˆ വസàµà´¤àµà´µà´¿à´²àµ മാറàµà´±à´‚ വരàµà´¤àµà´¤àµà´• (à´ªàµà´¤à´¿à´¯ വിനàµâ€à´¡àµ‹)" + +#: contrib/admindocs/templates/admin_doc/bookmarklets.html:28 +msgid "As above, but opens the admin page in a new window." +msgstr "à´®àµà´•à´³à´¿à´²àµ‡à´¤àµ പോലെ, പകàµà´·àµ†, à´…à´¡àµà´®à´¿à´¨àµâ€ പേജൠപàµà´¤à´¿à´¯ വിനàµà´¡àµ‹à´µà´¿à´²à´¾à´£àµ à´¤àµà´±à´•àµà´•àµà´•." + +#: contrib/auth/admin.py:29 +msgid "Personal info" +msgstr "à´µàµà´¯à´•àµà´¤à´¿à´ªà´°à´®à´¾à´¯ വിവരങàµà´™à´³àµâ€" + +#: contrib/auth/admin.py:30 +msgid "Permissions" +msgstr "à´…à´¨àµà´®à´¤à´¿à´•à´³àµâ€" + +#: contrib/auth/admin.py:31 +msgid "Important dates" +msgstr "à´ªàµà´°à´§à´¾à´¨ തീയതികളàµâ€" + +#: contrib/auth/admin.py:32 +msgid "Groups" +msgstr "à´—àµà´°àµ‚à´ªàµà´ªàµà´•à´³àµâ€" + +#: contrib/auth/admin.py:114 +msgid "Password changed successfully." +msgstr "പാസൠവേരàµâ€à´¡àµ മാറàµà´±à´¿à´¯à´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨àµ." + +#: contrib/auth/admin.py:124 +#, python-format +msgid "Change password: %s" +msgstr "പാസൠവേരàµâ€à´¡àµ മാറàµà´±àµà´•: %s" + +#: contrib/auth/forms.py:14 contrib/auth/forms.py:48 contrib/auth/forms.py:60 +msgid "Username" +msgstr "യൂസരàµâ€ നാമം (ഉപയോകàµà´¤àµà´°àµ നാമം)" + +#: contrib/auth/forms.py:15 contrib/auth/forms.py:49 +msgid "Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only." +msgstr "നിരàµâ€à´¬à´¨àµà´§à´‚. 30 à´“ അതിലàµâ€ à´•àµà´±à´µàµ‹ à´šà´¿à´¹àµà´¨à´™àµà´™à´³àµâ€. à´…à´•àµà´·à´°à´™àµà´™à´³àµâ€, à´…à´•àµà´•à´™àµà´™à´³àµâ€, " +"പിനàµà´¨àµ† @/./+/-/_à´Žà´¨àµà´¨à´¿à´µà´¯àµà´‚ മാതàµà´°à´‚." + +#: contrib/auth/forms.py:16 contrib/auth/forms.py:50 +msgid "This value may contain only letters, numbers and @/./+/-/_ characters." +msgstr "à´…à´•àµà´·à´°à´™àµà´™à´³àµâ€, à´…à´•àµà´•à´™àµà´™à´³àµâ€, പിനàµà´¨àµ† @/./+/-/_à´Žà´¨àµà´¨à´¿à´µà´¯àµà´‚ മാതàµà´°à´‚." + +#: contrib/auth/forms.py:18 +msgid "Password confirmation" +msgstr "പാസൠവേരàµâ€à´¡àµ ഉറപàµà´ªà´¾à´•àµà´•à´²àµâ€" + +#: contrib/auth/forms.py:31 +msgid "A user with that username already exists." +msgstr "à´† പേരàµà´³àµà´³ ഒരൠഉപയോകàµà´¤à´¾à´µàµ നിലവിലàµà´£àµà´Ÿàµ." + +#: contrib/auth/forms.py:37 contrib/auth/forms.py:156 +#: contrib/auth/forms.py:198 +msgid "The two password fields didn't match." +msgstr "പാസൠവേരàµâ€à´¡àµ നലàµà´•à´¿à´¯ à´•à´³àµà´³à´¿à´•à´³àµâ€ à´°à´£àµà´Ÿàµà´‚ തമàµà´®à´¿à´²àµâ€ സാമàµà´¯à´®à´¿à´²àµà´²." + +#: contrib/auth/forms.py:83 +msgid "This account is inactive." +msgstr "à´ˆ à´…à´•àµà´•àµ—à´£àµà´Ÿàµ മരവിപàµà´ªà´¿à´šàµà´šà´¤à´¾à´£àµ." + +#: contrib/auth/forms.py:88 +msgid "" +"Your Web browser doesn't appear to have cookies enabled. Cookies are " +"required for logging in." +msgstr "നിങàµà´™à´³àµà´Ÿàµ† വെബàµ-à´¬àµà´°àµ—സറിലെ à´•àµà´•àµà´•àµ€à´¸àµŠà´¨àµà´¨àµà´‚ à´ªàµà´°à´µà´°àµâ€à´¤àµà´¤à´¿à´•àµà´•àµà´¨àµà´¨à´¿à´²àµà´². ഇതിലേകàµà´•àµ à´ªàµà´°à´µàµ‡à´¶à´¿à´•àµà´•à´¾à´¨àµâ€ à´…à´µ ആവശàµà´¯à´®à´¾à´£àµ." + +#: contrib/auth/forms.py:101 +msgid "E-mail" +msgstr "à´‡-മെയിലàµâ€" + +#: contrib/auth/forms.py:110 +msgid "" +"That e-mail address doesn't have an associated user account. Are you sure " +"you've registered?" +msgstr "à´† à´‡-മെയിലàµà´®à´¾à´¯à´¿ ബനàµà´§à´ªàµà´ªàµ†à´Ÿàµà´Ÿ യൂസരൠഅകàµà´•àµ—à´£àµà´ŸàµŠà´¨àµà´¨àµà´‚ നിലവിലിലàµà´². രജിസàµà´±àµà´±à´°àµâ€ ചെയàµà´¤àµ†à´¨àµà´¨àµ തീരàµâ€à´šàµà´šà´¯à´¾à´£àµ‹?" + +#: contrib/auth/forms.py:136 +#, python-format +msgid "Password reset on %s" +msgstr "%s ലെ പാസൠവേരàµâ€à´¡àµ à´ªàµà´¨à´¸àµà´¥à´¾à´ªà´¿à´šàµà´šàµ." + +#: contrib/auth/forms.py:145 +msgid "New password confirmation" +msgstr "à´ªàµà´¤à´¿à´¯ പാസൠവേരàµâ€à´¡àµ ഉറപàµà´ªà´¾à´•àµà´•à´²àµâ€" + +#: contrib/auth/forms.py:178 +msgid "Your old password was entered incorrectly. Please enter it again." +msgstr "നിങàµà´™à´³àµà´Ÿàµ† പഴയ പാസൠവേരàµâ€à´¡àµ തെറàµà´±à´¾à´¯à´¾à´£àµ നലàµà´•à´¿à´¯à´¤àµ. തിരàµà´¤àµà´¤àµà´•." + +#: contrib/auth/models.py:66 contrib/auth/models.py:94 +msgid "name" +msgstr "പേരàµ" + +#: contrib/auth/models.py:68 +msgid "codename" +msgstr "കോഡàµ-നാമം" + +#: contrib/auth/models.py:72 +msgid "permission" +msgstr "à´…à´¨àµà´®à´¤à´¿" + +#: contrib/auth/models.py:73 contrib/auth/models.py:95 +msgid "permissions" +msgstr "à´…à´¨àµà´®à´¤à´¿à´•à´³àµâ€" + +#: contrib/auth/models.py:98 +msgid "group" +msgstr "à´—àµà´°àµ‚à´ªàµà´ªàµ" + +#: contrib/auth/models.py:99 contrib/auth/models.py:206 +msgid "groups" +msgstr "à´—àµà´°àµ‚à´ªàµà´ªàµà´•à´³àµâ€" + +#: contrib/auth/models.py:196 +msgid "username" +msgstr "യൂസരàµâ€ നാമം (ഉപയോകàµà´¤àµà´°àµ നാമം)" + +#: contrib/auth/models.py:196 +msgid "" +"Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters" +msgstr "നിരàµâ€à´¬à´¨àµà´§à´‚. 30 à´“ അതിലàµâ€ à´•àµà´±à´µàµ‹ à´šà´¿à´¹àµà´¨à´™àµà´™à´³àµâ€. à´…à´•àµà´·à´°à´™àµà´™à´³àµâ€, à´…à´•àµà´•à´™àµà´™à´³àµâ€, " +"പിനàµà´¨àµ† @/./+/-/_à´Žà´¨àµà´¨à´¿à´µà´¯àµà´‚ മാതàµà´°à´‚." + +#: contrib/auth/models.py:197 +msgid "first name" +msgstr "പേരൠ- ആദàµà´¯à´à´¾à´—à´‚" + +#: contrib/auth/models.py:198 +msgid "last name" +msgstr "പേരൠ- à´…à´¨àµà´¤àµà´¯à´à´¾à´—à´‚" + +#: contrib/auth/models.py:199 +msgid "e-mail address" +msgstr "à´‡-മെയിലàµâ€ വിലാസം" + +#: contrib/auth/models.py:200 +msgid "password" +msgstr "പാസൠവേരàµâ€à´¡àµ" + +#: contrib/auth/models.py:200 +msgid "" +"Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change " +"password form</a>." +msgstr "'[algo]$[salt]$[hexdigest]' à´…à´²àµà´²àµ†à´™àµà´•à´¿à´²àµâ€ <a href=\"password/\">പാസൠവേരàµâ€à´¡àµ " +"മാറàµà´±à´¾à´¨àµà´³àµà´³ ഫോം</a> ഉപയോഗികàµà´•àµà´•." + +#: contrib/auth/models.py:201 +msgid "staff status" +msgstr "à´¸àµà´±àµà´±à´¾à´«àµ പദവി" + +#: contrib/auth/models.py:201 +msgid "Designates whether the user can log into this admin site." +msgstr "à´ˆ യൂസരàµâ€à´•àµà´•àµ à´ˆ à´…à´¡àµà´®à´¿à´¨àµ സൈറàµà´±à´¿à´²àµ‡à´•àµà´•àµ à´ªàµà´°à´µàµ‡à´¶à´¿à´•àµà´•à´¾à´®àµ‹ à´Žà´¨àµà´¨àµ à´µàµà´¯à´•àµà´¤à´®à´¾à´•àµà´•à´¾à´¨àµâ€" + +#: contrib/auth/models.py:202 +msgid "active" +msgstr "സജീവം" + +#: contrib/auth/models.py:202 +msgid "" +"Designates whether this user should be treated as active. Unselect this " +"instead of deleting accounts." +msgstr "à´ˆ യൂസരàµâ€ സജീവമാണോയെനàµà´¨àµ à´µàµà´¯à´•àµà´¤à´®à´¾à´•àµà´•àµà´¨àµà´¨àµ. à´…à´•àµà´•àµ—à´£àµà´Ÿàµ ഡിലീറàµà´±àµ ചെയàµà´¯àµà´¨àµà´¨à´¤à´¿à´¨àµ പകരം ഇതൠഒഴിവാകàµà´•àµà´•." + +#: contrib/auth/models.py:203 +msgid "superuser status" +msgstr "സൂപàµà´ªà´°àµâ€-യൂസരàµâ€ പദവി" + +#: contrib/auth/models.py:203 +msgid "" +"Designates that this user has all permissions without explicitly assigning " +"them." +msgstr "à´ˆ ഉപയോകàµà´¤à´¾à´µà´¿à´¨àµ à´Žà´Ÿàµà´¤àµà´¤àµ പറയാതെ തനàµà´¨àµ† à´Žà´²àµà´²à´¾ à´…à´¨àµà´®à´¤à´¿à´•à´³àµà´‚ à´²à´à´¿à´•àµà´•àµà´¨àµà´¨à´¤à´¾à´£àµ†à´¨àµà´¨àµ à´µàµà´¯à´•àµà´¤à´®à´¾à´•àµà´•àµà´¨àµà´¨àµ" + +#: contrib/auth/models.py:204 +msgid "last login" +msgstr "അവസാനമായി ലോഗിനàµâ€ ചെയàµà´¤à´¤àµ" + +#: contrib/auth/models.py:205 +msgid "date joined" +msgstr "ചേരàµâ€à´¨àµà´¨ തീയതി" + +#: contrib/auth/models.py:207 +msgid "" +"In addition to the permissions manually assigned, this user will also get " +"all permissions granted to each group he/she is in." +msgstr "à´ˆ ഉപയോകàµà´¤à´¾à´µà´¿à´¨àµ നേരിടàµà´Ÿàµ à´²à´à´¿à´šàµà´šà´¤àµ കൂടാതെ അവരàµâ€ അംഗമായ à´—àµà´°àµ‚à´ªàµà´ªà´¿à´¨àµ à´²à´à´¿à´šàµà´š à´…à´¨àµà´®à´¤à´¿à´•à´³àµà´‚ à´…à´¨àµà´à´µà´¿à´•àµà´•à´¾à´‚" + +#: contrib/auth/models.py:208 +msgid "user permissions" +msgstr "യൂസരàµâ€ (ഉപയോകàµà´¤à´¾à´µàµ)à´¨àµà´³àµà´³ à´…à´¨àµà´®à´¤à´¿à´•à´³àµâ€" + +#: contrib/auth/models.py:212 contrib/comments/models.py:50 +#: contrib/comments/models.py:168 +msgid "user" +msgstr "യൂസരàµâ€ (ഉപയോകàµà´¤à´¾à´µàµ)" + +#: contrib/auth/models.py:213 +msgid "users" +msgstr "യൂസേരàµâ€à´¸àµ (ഉപയോകàµà´¤à´¾à´•àµà´•à´³àµâ€)" + +#: contrib/auth/models.py:394 +msgid "message" +msgstr "സനàµà´¦àµ‡à´¶à´‚" + +#: contrib/auth/views.py:79 +msgid "Logged out" +msgstr "ലോഗàµ-ഔടàµà´Ÿàµ ചെയàµà´¤àµ (à´ªàµà´±à´¤àµà´¤à´¿à´±à´™àµà´™à´¿)" + +#: contrib/auth/management/commands/createsuperuser.py:24 +#: core/validators.py:120 forms/fields.py:427 +msgid "Enter a valid e-mail address." +msgstr "ശരിയായ à´‡-മെയിലàµâ€ വിലാസം നലàµà´•àµà´•." + +#: contrib/comments/admin.py:12 +msgid "Content" +msgstr "ഉളàµà´³à´Ÿà´•àµà´•à´‚" + +#: contrib/comments/admin.py:15 +msgid "Metadata" +msgstr "" + +#: contrib/comments/admin.py:40 +msgid "flagged" +msgid_plural "flagged" +msgstr[0] "അടയാളപàµà´ªàµ†à´Ÿàµà´¤àµà´¤à´¿" +msgstr[1] "അടയാളപàµà´ªàµ†à´Ÿàµà´¤àµà´¤à´¿" + +#: contrib/comments/admin.py:41 +msgid "Flag selected comments" +msgstr "തെരഞàµà´žàµ†à´Ÿàµà´¤àµà´¤ à´…à´à´¿à´ªàµà´°à´¾à´¯à´™àµà´™à´³àµâ€ അടയാളപàµà´ªàµ†à´Ÿàµà´¤àµà´¤àµà´•" + +#: contrib/comments/admin.py:45 +msgid "approved" +msgid_plural "approved" +msgstr[0] "അംഗീകരിചàµà´šàµ" +msgstr[1] "അംഗീകരിചàµà´šàµ" + +#: contrib/comments/admin.py:46 +msgid "Approve selected comments" +msgstr "തെരഞàµà´žàµ†à´Ÿàµà´¤àµà´¤ à´…à´à´¿à´ªàµà´°à´¾à´¯à´™àµà´™à´³àµâ€ അംഗീകരികàµà´•àµà´•" + +#: contrib/comments/admin.py:50 +msgid "removed" +msgid_plural "removed" +msgstr[0] "നീകàµà´•à´‚ ചെയàµà´¤àµ" +msgstr[1] "നീകàµà´•à´‚ ചെയàµà´¤àµ" + +#: contrib/comments/admin.py:51 +msgid "Remove selected comments" +msgstr "തെരഞàµà´žàµ†à´Ÿàµà´¤àµà´¤ à´…à´à´¿à´ªàµà´°à´¾à´¯à´™àµà´™à´³àµâ€ നീകàµà´•à´‚ ചെയàµà´¯àµà´•" + +#: contrib/comments/admin.py:63 +#, python-format +msgid "1 comment was successfully %(action)s." +msgid_plural "%(count)s comments were successfully %(action)s." +msgstr[0] "1 à´…à´à´¿à´ªàµà´°à´¾à´¯à´‚ വിജയകരമായി %(action)s." +msgstr[1] "%(count)s à´…à´à´¿à´ªàµà´°à´¾à´¯à´™àµà´™à´³àµâ€ വിജയകരമായി %(action)s." + +#: contrib/comments/feeds.py:13 +#, python-format +msgid "%(site_name)s comments" +msgstr "%(site_name)s à´…à´à´¿à´ªàµà´°à´¾à´¯à´™àµà´™à´³àµâ€" + +#: contrib/comments/feeds.py:23 +#, python-format +msgid "Latest comments on %(site_name)s" +msgstr "%(site_name)s ലെ à´à´±àµà´±à´µàµà´‚ à´ªàµà´¤à´¿à´¯ à´…à´à´¿à´ªàµà´°à´¾à´¯à´™àµà´™à´³àµâ€" + +#: contrib/comments/forms.py:93 +msgid "Name" +msgstr "പേരàµ" + +#: contrib/comments/forms.py:94 +msgid "Email address" +msgstr "à´‡-മെയിലàµâ€ വിലാസം" + +#: contrib/comments/forms.py:95 contrib/flatpages/admin.py:8 +#: contrib/flatpages/models.py:7 db/models/fields/__init__.py:1101 +msgid "URL" +msgstr "URL(വെബàµ-വിലാസം)" + +#: contrib/comments/forms.py:96 +msgid "Comment" +msgstr "à´…à´à´¿à´ªàµà´°à´¾à´¯à´‚" + +#: contrib/comments/forms.py:175 +#, 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] "à´¶àµà´¶àµà´¶àµ! %s à´Žà´¨àµà´¨ വാകàµà´•àµ ഇവിടെ à´…à´¨àµà´µà´¦à´¨àµ€à´¯à´®à´²àµà´²." +msgstr[1] "à´¶àµà´¶àµà´¶àµ! %s à´Žà´¨àµà´¨àµ€ വാകàµà´•àµà´•à´³àµâ€ ഇവിടെ à´…à´¨àµà´µà´¦à´¨àµ€à´¯à´®à´²àµà´²." + +#: contrib/comments/forms.py:182 +msgid "" +"If you enter anything in this field your comment will be treated as spam" +msgstr "à´ˆ à´•à´³àµà´³à´¿à´¯à´¿à´²àµâ€ à´Žà´¨àµà´¤àµ†à´™àµà´•à´¿à´²àµà´‚ രേഖപàµà´ªàµ†à´Ÿàµà´¤àµà´¤à´¿à´¯à´¾à´²àµâ€ നിങàµà´™à´³àµà´Ÿàµ† à´…à´à´¿à´ªàµà´°à´¾à´¯à´‚ à´¸àµà´ªà´¾à´‚ ആയി കണകàµà´•à´¾à´•àµà´•àµà´‚" + +#: contrib/comments/models.py:22 contrib/contenttypes/models.py:81 +msgid "content type" +msgstr "à´à´¤àµ തരം ഉളàµà´³à´Ÿà´•àµà´•à´‚" + +#: contrib/comments/models.py:24 +msgid "object ID" +msgstr "വസàµà´¤àµ ID" + +#: contrib/comments/models.py:52 +msgid "user's name" +msgstr "യൂസറàµà´Ÿàµ† പേരàµ" + +#: contrib/comments/models.py:53 +msgid "user's email address" +msgstr "യൂസറàµà´Ÿàµ† à´‡-മെയിലàµâ€ വിലാസം" + +#: contrib/comments/models.py:54 +msgid "user's URL" +msgstr "യൂസറàµà´Ÿàµ† URL" + +#: contrib/comments/models.py:56 contrib/comments/models.py:76 +#: contrib/comments/models.py:169 +msgid "comment" +msgstr "à´…à´à´¿à´ªàµà´°à´¾à´¯à´‚" + +#: contrib/comments/models.py:59 +msgid "date/time submitted" +msgstr "സമരàµâ€à´ªàµà´ªà´¿à´šàµà´š തീയതി/സമയം" + +#: contrib/comments/models.py:60 db/models/fields/__init__.py:896 +msgid "IP address" +msgstr "IP വിലാസം" + +#: contrib/comments/models.py:61 +msgid "is public" +msgstr "പരസàµà´¯à´®à´¾à´£àµ" + +#: contrib/comments/models.py:62 +msgid "" +"Uncheck this box to make the comment effectively disappear from the site." +msgstr "à´…à´à´¿à´ªàµà´°à´¾à´¯à´‚ സൈറàµà´±à´¿à´²àµâ€ നിനàµà´¨àµà´‚ ഫലപàµà´°à´¦à´®à´¾à´¯à´¿ നീകàµà´•à´‚ ചെയàµà´¯à´¾à´¨àµâ€ à´ˆ ബോകàµà´¸à´¿à´²àµ† ടികൠഒഴിവാകàµà´•àµà´•." + +#: contrib/comments/models.py:64 +msgid "is removed" +msgstr "നീകàµà´•à´‚ ചെയàµà´¤àµ." + +#: contrib/comments/models.py:65 +msgid "" +"Check this box if the comment is inappropriate. A \"This comment has been " +"removed\" message will be displayed instead." +msgstr "à´…à´à´¿à´ªàµà´°à´¾à´¯à´‚ à´…à´¨àµà´šà´¿à´¤à´®àµ†à´™àµà´•à´¿à´²àµâ€ à´ˆ ബോകàµà´¸àµ ടികൠചെയàµà´¯àµà´•. \"à´ˆ à´…à´à´¿à´ªàµà´°à´¾à´¯à´‚ നീകàµà´•à´‚ ചെയàµà´¤àµ \" à´Žà´¨àµà´¨ സനàµà´¦àµ‡à´¶à´‚" +"ആയിരികàµà´•àµà´‚ പകരം കാണàµà´•." + +#: contrib/comments/models.py:77 +msgid "comments" +msgstr "à´…à´à´¿à´ªàµà´°à´¾à´¯à´™àµà´™à´³àµâ€" + +#: contrib/comments/models.py:119 +msgid "" +"This comment was posted by an authenticated user and thus the name is read-" +"only." +msgstr "à´ˆ à´…à´à´¿à´ªàµà´°à´¾à´¯à´‚ ഒരൠഅംഗീകൃത യൂസരàµâ€ രേഖപàµà´ªàµ†à´Ÿàµà´¤àµà´¤à´¿à´¯à´¤à´¾à´£àµ. അതിനാലàµâ€ പേരൠവായികàµà´•à´¾à´¨àµâ€ മാതàµà´°à´‚." + +#: contrib/comments/models.py:128 +msgid "" +"This comment was posted by an authenticated user and thus the email is read-" +"only." +msgstr "à´ˆ à´…à´à´¿à´ªàµà´°à´¾à´¯à´‚ ഒരൠഅംഗീകൃത യൂസരàµâ€ രേഖപàµà´ªàµ†à´Ÿàµà´¤àµà´¤à´¿à´¯à´¤à´¾à´£àµ. അതിനാലàµâ€ à´‡-മെയിലàµâ€ വിലാസം വായികàµà´•à´¾à´¨àµâ€ മാതàµà´°à´‚." + +#: contrib/comments/models.py:153 +#, python-format +msgid "" +"Posted by %(user)s at %(date)s\n" +"\n" +"%(comment)s\n" +"\n" +"http://%(domain)s%(url)s" +msgstr "" +"%(date)sനൠ%(user)s രേഖപàµà´ªàµ†à´Ÿàµà´¤àµà´¤à´¿à´¯à´¤àµ:\n" +"\n" +"%(comment)s\n" +"\n" +"http://%(domain)s%(url)s" + +#: contrib/comments/models.py:170 +msgid "flag" +msgstr "അടയാളം" + +#: contrib/comments/models.py:171 +msgid "date" +msgstr "തീയതി" + +#: contrib/comments/models.py:181 +msgid "comment flag" +msgstr "à´…à´à´¿à´ªàµà´°à´¾à´¯ അടയാളം" + +#: contrib/comments/models.py:182 +msgid "comment flags" +msgstr "à´…à´à´¿à´ªàµà´°à´¾à´¯ അടയാളങàµà´™à´³àµâ€" + +#: contrib/comments/templates/comments/approve.html:4 +msgid "Approve a comment" +msgstr "à´…à´à´¿à´ªàµà´°à´¾à´¯à´‚ അംഗീകരികàµà´•àµ‚" + +#: contrib/comments/templates/comments/approve.html:7 +msgid "Really make this comment public?" +msgstr "ശരികàµà´•àµà´‚ à´ˆ à´…à´à´¿à´ªàµà´°à´¾à´¯à´‚ പരസàµà´¯à´®à´¾à´•àµà´•à´£àµ‹?" + +#: contrib/comments/templates/comments/approve.html:12 +msgid "Approve" +msgstr "അംഗീകരികàµà´•àµ‚" + +#: contrib/comments/templates/comments/approved.html:4 +msgid "Thanks for approving" +msgstr "അംഗീകരിചàµà´šà´¤à´¿à´¨àµ നനàµà´¦à´¿" + +#: contrib/comments/templates/comments/approved.html:7 +#: contrib/comments/templates/comments/deleted.html:7 +#: contrib/comments/templates/comments/flagged.html:7 +msgid "" +"Thanks for taking the time to improve the quality of discussion on our site" +msgstr "നമàµà´®àµà´Ÿàµ† സൈറàµà´±à´¿à´²àµ† à´šà´°àµâ€à´šàµà´šà´•à´³àµà´Ÿàµ† നിലവാരം ഉയരàµâ€à´¤àµà´¤à´¾à´¨àµâ€ സമയം ചെലവഴിചàµà´šà´¤à´¿à´¨àµ നനàµà´¦à´¿." + +#: contrib/comments/templates/comments/delete.html:4 +msgid "Remove a comment" +msgstr "à´…à´à´¿à´ªàµà´°à´¾à´¯à´‚ നീകàµà´•à´‚ ചെയàµà´¯àµ‚" + +#: contrib/comments/templates/comments/delete.html:7 +msgid "Really remove this comment?" +msgstr "à´ˆ à´…à´à´¿à´ªàµà´°à´¾à´¯à´‚ ശരികàµà´•àµà´‚ നീകàµà´•à´‚ ചെയàµà´¯à´£àµ‹?" + +#: contrib/comments/templates/comments/deleted.html:4 +msgid "Thanks for removing" +msgstr "നീകàµà´•à´‚ ചെയàµà´¤à´¤à´¿à´¨àµ നനàµà´¦à´¿" + +#: contrib/comments/templates/comments/flag.html:4 +msgid "Flag this comment" +msgstr "à´ˆ à´…à´à´¿à´ªàµà´°à´¾à´¯à´‚ അടയാളപàµà´ªàµ†à´Ÿàµà´¤àµà´¤àµ‚" + +#: contrib/comments/templates/comments/flag.html:7 +msgid "Really flag this comment?" +msgstr "à´ˆ à´…à´à´¿à´ªàµà´°à´¾à´¯à´‚ ശരികàµà´•àµà´‚ അടയാളപàµà´ªàµ†à´Ÿàµà´¤àµà´¤à´£àµ‹?" + +#: contrib/comments/templates/comments/flag.html:12 +msgid "Flag" +msgstr "അടയാളം" + +#: contrib/comments/templates/comments/flagged.html:4 +msgid "Thanks for flagging" +msgstr "അടയാളപàµà´ªàµ†à´Ÿàµà´¤àµà´¤à´¿à´¯à´¤à´¿à´¨àµ നനàµà´¦à´¿" + +#: contrib/comments/templates/comments/form.html:17 +#: contrib/comments/templates/comments/preview.html:32 +msgid "Post" +msgstr "രേഖപàµà´ªàµ†à´Ÿàµà´¤àµà´¤àµ‚" + +#: contrib/comments/templates/comments/form.html:18 +#: contrib/comments/templates/comments/preview.html:33 +msgid "Preview" +msgstr "അവലോകനം" + +#: contrib/comments/templates/comments/posted.html:4 +msgid "Thanks for commenting" +msgstr "à´…à´à´¿à´ªàµà´°à´¾à´¯à´‚ രേഖപàµà´ªàµ†à´Ÿàµà´¤àµà´¤à´¿à´¯à´¤à´¿à´¨àµ നനàµà´¦à´¿" + +#: contrib/comments/templates/comments/posted.html:7 +msgid "Thank you for your comment" +msgstr "à´…à´à´¿à´ªàµà´°à´¾à´¯à´¤àµà´¤à´¿à´¨àµ നനàµà´¦à´¿" + +#: contrib/comments/templates/comments/preview.html:4 +#: contrib/comments/templates/comments/preview.html:13 +msgid "Preview your comment" +msgstr "à´…à´à´¿à´ªàµà´°à´¾à´¯à´‚ അവലോകനം ചെയàµà´¯àµà´•" + +#: contrib/comments/templates/comments/preview.html:11 +msgid "Please correct the error below" +msgid_plural "Please correct the errors below" +msgstr[0] "ദയവായി താഴെ പറയàµà´¨àµà´¨ തെറàµà´±àµ തിരàµà´¤àµà´¤àµà´•" +msgstr[1] "ദയവായി താഴെ പറയàµà´¨àµà´¨ തെറàµà´±àµà´•à´³àµâ€ തിരàµà´¤àµà´¤àµà´•" + +#: contrib/comments/templates/comments/preview.html:16 +msgid "Post your comment" +msgstr "à´…à´à´¿à´ªàµà´°à´¾à´¯à´‚ രേഖപàµà´ªàµ†à´Ÿàµà´¤àµà´¤àµà´•" + +#: contrib/comments/templates/comments/preview.html:16 +msgid "or make changes" +msgstr "à´…à´²àµà´²àµ†à´™àµà´•à´¿à´²àµâ€ മാറàµà´±à´‚ വരàµà´¤àµà´¤àµà´•." + +#: contrib/contenttypes/models.py:77 +msgid "python model class name" +msgstr "" + +#: contrib/contenttypes/models.py:82 +msgid "content types" +msgstr "ഉളàµà´³à´Ÿà´•àµà´•à´‚ à´à´¤àµŠà´•àµà´•àµ† തരം" + +#: contrib/flatpages/admin.py:9 +msgid "" +"Example: '/about/contact/'. Make sure to have leading and trailing slashes." +msgstr "ഉദാ: '/about/contact/'. ആദàµà´¯à´µàµà´‚ അവസാനവàµà´‚ à´¸àµà´³à´¾à´·àµà´•à´³àµâ€ നിരàµâ€à´¬à´¨àµà´§à´‚." + +#: contrib/flatpages/admin.py:11 +msgid "" +"This value must contain only letters, numbers, underscores, dashes or " +"slashes." +msgstr "ഇതിലàµâ€ à´…à´•àµà´·à´°à´™àµà´™à´³àµâ€, à´…à´•àµà´•à´™àµà´™à´³àµâ€, à´…à´£àµà´Ÿà´°àµâ€à´¸àµà´•àµ‡à´¾à´°àµâ€, വരകളàµâ€, à´¸àµà´³à´¾à´·àµà´•à´³àµâ€ à´Žà´¨àµà´¨à´¿à´µ മാതàµà´°à´®àµ‡ പാടàµà´³àµà´³àµ‚. " + +#: contrib/flatpages/admin.py:22 +msgid "Advanced options" +msgstr "ഉയരàµâ€à´¨àµà´¨ സൗകരàµà´¯à´™àµà´™à´³àµâ€" + +#: contrib/flatpages/models.py:8 +msgid "title" +msgstr "ശീരàµâ€à´·à´•à´‚" + +#: contrib/flatpages/models.py:9 +msgid "content" +msgstr "ഉളàµà´³à´Ÿà´•àµà´•à´‚" + +#: contrib/flatpages/models.py:10 +msgid "enable comments" +msgstr "à´…à´à´¿à´ªàµà´°à´¾à´¯à´™àµà´™à´³àµâ€ à´…à´¨àµà´µà´¦à´¿à´•àµà´•àµà´•" + +#: contrib/flatpages/models.py:11 +msgid "template name" +msgstr "ടെമàµà´ªàµà´²àµ‡à´±àµà´±à´¿à´¨àµà´±àµ† പേരàµ" + +#: contrib/flatpages/models.py:12 +msgid "" +"Example: 'flatpages/contact_page.html'. If this isn't provided, the system " +"will use 'flatpages/default.html'." +msgstr "" +"ഉദാ: 'flatpages/contact_page.html'. ഇതൠനലàµà´•à´¿à´¯à´¿à´²àµà´²àµ†à´™àµà´•à´¿à´²àµâ€, 'flatpages/default.html' à´Žà´¨àµà´¨ " +"വിലാസം ഉപയോഗികàµà´•à´ªàµà´ªàµ†à´Ÿàµà´‚." + +#: contrib/flatpages/models.py:13 +msgid "registration required" +msgstr "രജിസàµà´Ÿàµà´°àµ‡à´·à´¨àµâ€ ആവശàµà´¯à´®à´¾à´£àµ" + +#: contrib/flatpages/models.py:13 +msgid "If this is checked, only logged-in users will be able to view the page." +msgstr "ഇതൠടികൠചെയàµà´¤à´¾à´²àµâ€ പിനàµà´¨àµ† ലോഗàµ-ഇനàµâ€ ചെയàµà´¤ യൂസരàµâ€à´•àµà´•àµ മാതàµà´°à´®àµ‡ à´ˆ പേജൠകാണാനàµâ€ കഴിയൂ." + +#: contrib/flatpages/models.py:18 +msgid "flat page" +msgstr "à´«àµà´³à´¾à´±àµà´±àµ പേജàµ" + +#: contrib/flatpages/models.py:19 +msgid "flat pages" +msgstr "à´«àµà´³à´¾à´±àµà´±àµ പേജàµà´•à´³àµâ€" + +#: contrib/formtools/wizard.py:140 +msgid "" +"We apologize, but your form has expired. Please continue filling out the " +"form from this page." +msgstr "à´•àµà´·à´®à´¿à´•àµà´•à´£à´‚, താങàµà´•à´³àµà´Ÿàµ† ഫോം കാലഹരണപàµà´ªàµ†à´Ÿàµà´Ÿàµ à´•à´´à´¿à´žàµà´žàµ. ദയവായി à´ˆ പേജിലെ ഫോം പൂരിപàµà´ªà´¿à´šàµà´šàµ à´¤àµà´Ÿà´°àµà´•." + +#: contrib/gis/db/models/fields.py:50 +msgid "The base GIS field -- maps to the OpenGIS Specification Geometry type." +msgstr "" + +#: contrib/gis/db/models/fields.py:270 +msgid "Point" +msgstr "ബിനàµà´¦àµ" + +#: contrib/gis/db/models/fields.py:274 +msgid "Line string" +msgstr "" + +#: contrib/gis/db/models/fields.py:278 +msgid "Polygon" +msgstr "ബഹàµà´àµà´œà´‚" + +#: contrib/gis/db/models/fields.py:282 +msgid "Multi-point" +msgstr "ബഹàµà´¬à´¿à´¨àµà´¦àµ" + +#: contrib/gis/db/models/fields.py:286 +msgid "Multi-line string" +msgstr "" + +#: contrib/gis/db/models/fields.py:290 +msgid "Multi polygon" +msgstr "ബഹൠബഹàµà´àµà´œà´‚" + +#: contrib/gis/db/models/fields.py:294 +msgid "Geometry collection" +msgstr "à´œàµà´¯à´¾à´®à´¿à´¤à´¿ ശേഖരം" + +#: contrib/gis/forms/fields.py:17 +msgid "No geometry value provided." +msgstr "" + +#: contrib/gis/forms/fields.py:18 +msgid "Invalid geometry value." +msgstr "" + +#: contrib/gis/forms/fields.py:19 +msgid "Invalid geometry type." +msgstr "" + +#: contrib/gis/forms/fields.py:20 +msgid "" +"An error occurred when transforming the geometry to the SRID of the geometry " +"form field." +msgstr "" + +#: contrib/humanize/templatetags/humanize.py:19 +msgid "th" +msgstr "ആം" + +#: contrib/humanize/templatetags/humanize.py:19 +msgid "st" +msgstr "ആം" + +#: contrib/humanize/templatetags/humanize.py:19 +msgid "nd" +msgstr "ആം" + +#: contrib/humanize/templatetags/humanize.py:19 +msgid "rd" +msgstr "ആം" + +#: contrib/humanize/templatetags/humanize.py:51 +#, python-format +msgid "%(value).1f million" +msgid_plural "%(value).1f million" +msgstr[0] "%(value).1f മിലàµà´²àµà´¯à´£àµâ€ (ദശലകàµà´·à´‚)" +msgstr[1] "%(value).1f മിലàµà´²àµà´¯à´£àµâ€ (ദശലകàµà´·à´‚)" + +#: contrib/humanize/templatetags/humanize.py:54 +#, python-format +msgid "%(value).1f billion" +msgid_plural "%(value).1f billion" +msgstr[0] "%(value).1f ബിലàµà´²àµà´¯à´£àµâ€ (ശതകോടി)" +msgstr[1] "%(value).1f ബിലàµà´²àµà´¯à´£àµâ€ (ശതകോടി)" + +#: contrib/humanize/templatetags/humanize.py:57 +#, python-format +msgid "%(value).1f trillion" +msgid_plural "%(value).1f trillion" +msgstr[0] "%(value).1f à´Ÿàµà´°à´¿à´²àµà´²àµà´¯à´£àµâ€ (ലകàµà´·à´‚ കോടി)" +msgstr[1] "%(value).1f à´Ÿàµà´°à´¿à´²àµà´²àµà´¯à´£àµâ€ (ലകàµà´·à´‚ കോടി)" + +#: contrib/humanize/templatetags/humanize.py:73 +msgid "one" +msgstr "à´’à´¨àµà´¨àµ" + +#: contrib/humanize/templatetags/humanize.py:73 +msgid "two" +msgstr "à´°à´£àµà´Ÿàµ" + +#: contrib/humanize/templatetags/humanize.py:73 +msgid "three" +msgstr "മൂനàµà´¨àµ" + +#: contrib/humanize/templatetags/humanize.py:73 +msgid "four" +msgstr "നാലàµ" + +#: contrib/humanize/templatetags/humanize.py:73 +msgid "five" +msgstr "à´…à´žàµà´šàµ" + +#: contrib/humanize/templatetags/humanize.py:73 +msgid "six" +msgstr "ആറàµ" + +#: contrib/humanize/templatetags/humanize.py:73 +msgid "seven" +msgstr "à´à´´àµ" + +#: contrib/humanize/templatetags/humanize.py:73 +msgid "eight" +msgstr "à´Žà´Ÿàµà´Ÿàµ" + +#: contrib/humanize/templatetags/humanize.py:73 +msgid "nine" +msgstr "à´’à´¨àµâ€à´ªà´¤àµ" + +#: contrib/humanize/templatetags/humanize.py:93 +msgid "today" +msgstr "ഇനàµà´¨àµ" + +#: contrib/humanize/templatetags/humanize.py:95 +msgid "tomorrow" +msgstr "നാളെ" + +#: contrib/humanize/templatetags/humanize.py:97 +msgid "yesterday" +msgstr "ഇനàµà´¨à´²àµ†" + +#: contrib/localflavor/ar/forms.py:28 +msgid "Enter a postal code in the format NNNN or ANNNNAAA." +msgstr "" + +#: contrib/localflavor/ar/forms.py:50 contrib/localflavor/br/forms.py:92 +#: contrib/localflavor/br/forms.py:131 contrib/localflavor/pe/forms.py:24 +#: contrib/localflavor/pe/forms.py:52 +msgid "This field requires only numbers." +msgstr "" + +#: contrib/localflavor/ar/forms.py:51 +msgid "This field requires 7 or 8 digits." +msgstr "" + +#: contrib/localflavor/ar/forms.py:80 +msgid "Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format." +msgstr "" + +#: contrib/localflavor/ar/forms.py:81 +msgid "Invalid CUIT." +msgstr "" + +#: contrib/localflavor/at/at_states.py:5 +msgid "Burgenland" +msgstr "" + +#: contrib/localflavor/at/at_states.py:6 +msgid "Carinthia" +msgstr "" + +#: contrib/localflavor/at/at_states.py:7 +msgid "Lower Austria" +msgstr "" + +#: contrib/localflavor/at/at_states.py:8 +msgid "Upper Austria" +msgstr "" + +#: contrib/localflavor/at/at_states.py:9 +msgid "Salzburg" +msgstr "" + +#: contrib/localflavor/at/at_states.py:10 +msgid "Styria" +msgstr "" + +#: contrib/localflavor/at/at_states.py:11 +msgid "Tyrol" +msgstr "" + +#: contrib/localflavor/at/at_states.py:12 +msgid "Vorarlberg" +msgstr "" + +#: contrib/localflavor/at/at_states.py:13 +msgid "Vienna" +msgstr "" + +#: contrib/localflavor/at/forms.py:20 contrib/localflavor/ch/forms.py:17 +#: contrib/localflavor/no/forms.py:13 +msgid "Enter a zip code in the format XXXX." +msgstr "" + +#: contrib/localflavor/at/forms.py:48 +msgid "Enter a valid Austrian Social Security Number in XXXX XXXXXX format." +msgstr "" + +#: contrib/localflavor/au/forms.py:17 +msgid "Enter a 4 digit post code." +msgstr "" + +#: contrib/localflavor/br/forms.py:17 +msgid "Enter a zip code in the format XXXXX-XXX." +msgstr "" + +#: contrib/localflavor/br/forms.py:26 +msgid "Phone numbers must be in XX-XXXX-XXXX format." +msgstr "" + +#: contrib/localflavor/br/forms.py:54 +msgid "" +"Select a valid brazilian state. That state is not one of the available " +"states." +msgstr "" + +#: contrib/localflavor/br/forms.py:90 +msgid "Invalid CPF number." +msgstr "" + +#: contrib/localflavor/br/forms.py:91 +msgid "This field requires at most 11 digits or 14 characters." +msgstr "" + +#: contrib/localflavor/br/forms.py:130 +msgid "Invalid CNPJ number." +msgstr "" + +#: contrib/localflavor/br/forms.py:132 +msgid "This field requires at least 14 digits" +msgstr "" + +#: contrib/localflavor/ca/forms.py:25 +msgid "Enter a postal code in the format XXX XXX." +msgstr "" + +#: contrib/localflavor/ca/forms.py:96 +msgid "Enter a valid Canadian Social Insurance number in XXX-XXX-XXX format." +msgstr "" + +#: contrib/localflavor/ch/ch_states.py:5 +msgid "Aargau" +msgstr "" + +#: contrib/localflavor/ch/ch_states.py:6 +msgid "Appenzell Innerrhoden" +msgstr "" + +#: contrib/localflavor/ch/ch_states.py:7 +msgid "Appenzell Ausserrhoden" +msgstr "" + +#: contrib/localflavor/ch/ch_states.py:8 +msgid "Basel-Stadt" +msgstr "" + +#: contrib/localflavor/ch/ch_states.py:9 +msgid "Basel-Land" +msgstr "" + +#: contrib/localflavor/ch/ch_states.py:10 +msgid "Berne" +msgstr "" + +#: contrib/localflavor/ch/ch_states.py:11 +msgid "Fribourg" +msgstr "" + +#: contrib/localflavor/ch/ch_states.py:12 +msgid "Geneva" +msgstr "" + +#: contrib/localflavor/ch/ch_states.py:13 +msgid "Glarus" +msgstr "" + +#: contrib/localflavor/ch/ch_states.py:14 +msgid "Graubuenden" +msgstr "" + +#: contrib/localflavor/ch/ch_states.py:15 +msgid "Jura" +msgstr "" + +#: contrib/localflavor/ch/ch_states.py:16 +msgid "Lucerne" +msgstr "" + +#: contrib/localflavor/ch/ch_states.py:17 +msgid "Neuchatel" +msgstr "" + +#: contrib/localflavor/ch/ch_states.py:18 +msgid "Nidwalden" +msgstr "" + +#: contrib/localflavor/ch/ch_states.py:19 +msgid "Obwalden" +msgstr "" + +#: contrib/localflavor/ch/ch_states.py:20 +msgid "Schaffhausen" +msgstr "" + +#: contrib/localflavor/ch/ch_states.py:21 +msgid "Schwyz" +msgstr "" + +#: contrib/localflavor/ch/ch_states.py:22 +msgid "Solothurn" +msgstr "" + +#: contrib/localflavor/ch/ch_states.py:23 +msgid "St. Gallen" +msgstr "" + +#: contrib/localflavor/ch/ch_states.py:24 +msgid "Thurgau" +msgstr "" + +#: contrib/localflavor/ch/ch_states.py:25 +msgid "Ticino" +msgstr "" + +#: contrib/localflavor/ch/ch_states.py:26 +msgid "Uri" +msgstr "" + +#: contrib/localflavor/ch/ch_states.py:27 +msgid "Valais" +msgstr "" + +#: contrib/localflavor/ch/ch_states.py:28 +msgid "Vaud" +msgstr "" + +#: contrib/localflavor/ch/ch_states.py:29 +msgid "Zug" +msgstr "" + +#: contrib/localflavor/ch/ch_states.py:30 +msgid "Zurich" +msgstr "" + +#: contrib/localflavor/ch/forms.py:65 +msgid "" +"Enter a valid Swiss identity or passport card number in X1234567<0 or " +"1234567890 format." +msgstr "" + +#: contrib/localflavor/cl/forms.py:30 +msgid "Enter a valid Chilean RUT." +msgstr "" + +#: contrib/localflavor/cl/forms.py:31 +msgid "Enter a valid Chilean RUT. The format is XX.XXX.XXX-X." +msgstr "" + +#: contrib/localflavor/cl/forms.py:32 +msgid "The Chilean RUT is not valid." +msgstr "" + +#: contrib/localflavor/cz/cz_regions.py:8 +msgid "Prague" +msgstr "" + +#: contrib/localflavor/cz/cz_regions.py:9 +msgid "Central Bohemian Region" +msgstr "" + +#: contrib/localflavor/cz/cz_regions.py:10 +msgid "South Bohemian Region" +msgstr "" + +#: contrib/localflavor/cz/cz_regions.py:11 +msgid "Pilsen Region" +msgstr "" + +#: contrib/localflavor/cz/cz_regions.py:12 +msgid "Carlsbad Region" +msgstr "" + +#: contrib/localflavor/cz/cz_regions.py:13 +msgid "Usti Region" +msgstr "" + +#: contrib/localflavor/cz/cz_regions.py:14 +msgid "Liberec Region" +msgstr "" + +#: contrib/localflavor/cz/cz_regions.py:15 +msgid "Hradec Region" +msgstr "" + +#: contrib/localflavor/cz/cz_regions.py:16 +msgid "Pardubice Region" +msgstr "" + +#: contrib/localflavor/cz/cz_regions.py:17 +msgid "Vysocina Region" +msgstr "" + +#: contrib/localflavor/cz/cz_regions.py:18 +msgid "South Moravian Region" +msgstr "" + +#: contrib/localflavor/cz/cz_regions.py:19 +msgid "Olomouc Region" +msgstr "" + +#: contrib/localflavor/cz/cz_regions.py:20 +msgid "Zlin Region" +msgstr "" + +#: contrib/localflavor/cz/cz_regions.py:21 +msgid "Moravian-Silesian Region" +msgstr "" + +#: contrib/localflavor/cz/forms.py:28 contrib/localflavor/sk/forms.py:30 +msgid "Enter a postal code in the format XXXXX or XXX XX." +msgstr "" + +#: contrib/localflavor/cz/forms.py:48 +msgid "Enter a birth number in the format XXXXXX/XXXX or XXXXXXXXXX." +msgstr "" + +#: contrib/localflavor/cz/forms.py:49 +msgid "Invalid optional parameter Gender, valid values are 'f' and 'm'" +msgstr "" + +#: contrib/localflavor/cz/forms.py:50 +msgid "Enter a valid birth number." +msgstr "" + +#: contrib/localflavor/cz/forms.py:107 +msgid "Enter a valid IC number." +msgstr "" + +#: contrib/localflavor/de/de_states.py:5 +msgid "Baden-Wuerttemberg" +msgstr "" + +#: contrib/localflavor/de/de_states.py:6 +msgid "Bavaria" +msgstr "" + +#: contrib/localflavor/de/de_states.py:7 +msgid "Berlin" +msgstr "" + +#: contrib/localflavor/de/de_states.py:8 +msgid "Brandenburg" +msgstr "" + +#: contrib/localflavor/de/de_states.py:9 +msgid "Bremen" +msgstr "" + +#: contrib/localflavor/de/de_states.py:10 +msgid "Hamburg" +msgstr "" + +#: contrib/localflavor/de/de_states.py:11 +msgid "Hessen" +msgstr "" + +#: contrib/localflavor/de/de_states.py:12 +msgid "Mecklenburg-Western Pomerania" +msgstr "" + +#: contrib/localflavor/de/de_states.py:13 +msgid "Lower Saxony" +msgstr "" + +#: contrib/localflavor/de/de_states.py:14 +msgid "North Rhine-Westphalia" +msgstr "" + +#: contrib/localflavor/de/de_states.py:15 +msgid "Rhineland-Palatinate" +msgstr "" + +#: contrib/localflavor/de/de_states.py:16 +msgid "Saarland" +msgstr "" + +#: contrib/localflavor/de/de_states.py:17 +msgid "Saxony" +msgstr "" + +#: contrib/localflavor/de/de_states.py:18 +msgid "Saxony-Anhalt" +msgstr "" + +#: contrib/localflavor/de/de_states.py:19 +msgid "Schleswig-Holstein" +msgstr "" + +#: contrib/localflavor/de/de_states.py:20 +msgid "Thuringia" +msgstr "" + +#: contrib/localflavor/de/forms.py:15 contrib/localflavor/fi/forms.py:13 +#: contrib/localflavor/fr/forms.py:16 +msgid "Enter a zip code in the format XXXXX." +msgstr "" + +#: contrib/localflavor/de/forms.py:42 +msgid "" +"Enter a valid German identity card number in XXXXXXXXXXX-XXXXXXX-XXXXXXX-X " +"format." +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:5 +msgid "Arava" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:6 +msgid "Albacete" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:7 +msgid "Alacant" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:8 +msgid "Almeria" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:9 +msgid "Avila" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:10 +msgid "Badajoz" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:11 +msgid "Illes Balears" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:12 +msgid "Barcelona" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:13 +msgid "Burgos" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:14 +msgid "Caceres" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:15 +msgid "Cadiz" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:16 +msgid "Castello" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:17 +msgid "Ciudad Real" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:18 +msgid "Cordoba" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:19 +msgid "A Coruna" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:20 +msgid "Cuenca" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:21 +msgid "Girona" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:22 +msgid "Granada" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:23 +msgid "Guadalajara" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:24 +msgid "Guipuzkoa" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:25 +msgid "Huelva" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:26 +msgid "Huesca" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:27 +msgid "Jaen" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:28 +msgid "Leon" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:29 +msgid "Lleida" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:30 +#: contrib/localflavor/es/es_regions.py:17 +msgid "La Rioja" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:31 +msgid "Lugo" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:32 +#: contrib/localflavor/es/es_regions.py:18 +msgid "Madrid" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:33 +msgid "Malaga" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:34 +msgid "Murcia" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:35 +msgid "Navarre" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:36 +msgid "Ourense" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:37 +msgid "Asturias" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:38 +msgid "Palencia" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:39 +msgid "Las Palmas" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:40 +msgid "Pontevedra" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:41 +msgid "Salamanca" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:42 +msgid "Santa Cruz de Tenerife" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:43 +#: contrib/localflavor/es/es_regions.py:11 +msgid "Cantabria" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:44 +msgid "Segovia" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:45 +msgid "Seville" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:46 +msgid "Soria" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:47 +msgid "Tarragona" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:48 +msgid "Teruel" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:49 +msgid "Toledo" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:50 +msgid "Valencia" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:51 +msgid "Valladolid" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:52 +msgid "Bizkaia" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:53 +msgid "Zamora" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:54 +msgid "Zaragoza" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:55 +msgid "Ceuta" +msgstr "" + +#: contrib/localflavor/es/es_provinces.py:56 +msgid "Melilla" +msgstr "" + +#: contrib/localflavor/es/es_regions.py:5 +msgid "Andalusia" +msgstr "" + +#: contrib/localflavor/es/es_regions.py:6 +msgid "Aragon" +msgstr "" + +#: contrib/localflavor/es/es_regions.py:7 +msgid "Principality of Asturias" +msgstr "" + +#: contrib/localflavor/es/es_regions.py:8 +msgid "Balearic Islands" +msgstr "" + +#: contrib/localflavor/es/es_regions.py:9 +msgid "Basque Country" +msgstr "" + +#: contrib/localflavor/es/es_regions.py:10 +msgid "Canary Islands" +msgstr "" + +#: contrib/localflavor/es/es_regions.py:12 +msgid "Castile-La Mancha" +msgstr "" + +#: contrib/localflavor/es/es_regions.py:13 +msgid "Castile and Leon" +msgstr "" + +#: contrib/localflavor/es/es_regions.py:14 +msgid "Catalonia" +msgstr "" + +#: contrib/localflavor/es/es_regions.py:15 +msgid "Extremadura" +msgstr "" + +#: contrib/localflavor/es/es_regions.py:16 +msgid "Galicia" +msgstr "" + +#: contrib/localflavor/es/es_regions.py:19 +msgid "Region of Murcia" +msgstr "" + +#: contrib/localflavor/es/es_regions.py:20 +msgid "Foral Community of Navarre" +msgstr "" + +#: contrib/localflavor/es/es_regions.py:21 +msgid "Valencian Community" +msgstr "" + +#: contrib/localflavor/es/forms.py:20 +msgid "Enter a valid postal code in the range and format 01XXX - 52XXX." +msgstr "" + +#: contrib/localflavor/es/forms.py:40 +msgid "" +"Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or " +"9XXXXXXXX." +msgstr "" + +#: contrib/localflavor/es/forms.py:67 +msgid "Please enter a valid NIF, NIE, or CIF." +msgstr "" + +#: contrib/localflavor/es/forms.py:68 +msgid "Please enter a valid NIF or NIE." +msgstr "" + +#: contrib/localflavor/es/forms.py:69 +msgid "Invalid checksum for NIF." +msgstr "" + +#: contrib/localflavor/es/forms.py:70 +msgid "Invalid checksum for NIE." +msgstr "" + +#: contrib/localflavor/es/forms.py:71 +msgid "Invalid checksum for CIF." +msgstr "" + +#: contrib/localflavor/es/forms.py:143 +msgid "" +"Please enter a valid bank account number in format XXXX-XXXX-XX-XXXXXXXXXX." +msgstr "" + +#: contrib/localflavor/es/forms.py:144 +msgid "Invalid checksum for bank account number." +msgstr "" + +#: contrib/localflavor/fi/forms.py:29 +msgid "Enter a valid Finnish social security number." +msgstr "" + +#: contrib/localflavor/fr/forms.py:31 +msgid "Phone numbers must be in 0X XX XX XX XX format." +msgstr "" + +#: contrib/localflavor/id/forms.py:28 +msgid "Enter a valid post code" +msgstr "" + +#: contrib/localflavor/id/forms.py:68 contrib/localflavor/nl/forms.py:53 +msgid "Enter a valid phone number" +msgstr "" + +#: contrib/localflavor/id/forms.py:107 +msgid "Enter a valid vehicle license plate number" +msgstr "" + +#: contrib/localflavor/id/forms.py:170 +msgid "Enter a valid NIK/KTP number" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:9 +#: contrib/localflavor/id/id_choices.py:73 +msgid "Bali" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:10 +#: contrib/localflavor/id/id_choices.py:45 +msgid "Banten" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:11 +#: contrib/localflavor/id/id_choices.py:54 +msgid "Bengkulu" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:12 +#: contrib/localflavor/id/id_choices.py:47 +msgid "Yogyakarta" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:13 +#: contrib/localflavor/id/id_choices.py:51 +msgid "Jakarta" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:14 +#: contrib/localflavor/id/id_choices.py:75 +msgid "Gorontalo" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:15 +#: contrib/localflavor/id/id_choices.py:57 +msgid "Jambi" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:16 +msgid "Jawa Barat" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:17 +msgid "Jawa Tengah" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:18 +msgid "Jawa Timur" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:19 +#: contrib/localflavor/id/id_choices.py:88 +msgid "Kalimantan Barat" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:20 +#: contrib/localflavor/id/id_choices.py:66 +msgid "Kalimantan Selatan" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:21 +#: contrib/localflavor/id/id_choices.py:89 +msgid "Kalimantan Tengah" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:22 +#: contrib/localflavor/id/id_choices.py:90 +msgid "Kalimantan Timur" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:23 +msgid "Kepulauan Bangka-Belitung" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:24 +#: contrib/localflavor/id/id_choices.py:62 +msgid "Kepulauan Riau" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:25 +#: contrib/localflavor/id/id_choices.py:55 +msgid "Lampung" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:26 +#: contrib/localflavor/id/id_choices.py:70 +msgid "Maluku" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:27 +#: contrib/localflavor/id/id_choices.py:71 +msgid "Maluku Utara" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:28 +#: contrib/localflavor/id/id_choices.py:59 +msgid "Nanggroe Aceh Darussalam" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:29 +msgid "Nusa Tenggara Barat" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:30 +msgid "Nusa Tenggara Timur" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:31 +msgid "Papua" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:32 +msgid "Papua Barat" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:33 +#: contrib/localflavor/id/id_choices.py:60 +msgid "Riau" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:34 +#: contrib/localflavor/id/id_choices.py:68 +msgid "Sulawesi Barat" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:35 +#: contrib/localflavor/id/id_choices.py:69 +msgid "Sulawesi Selatan" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:36 +#: contrib/localflavor/id/id_choices.py:76 +msgid "Sulawesi Tengah" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:37 +#: contrib/localflavor/id/id_choices.py:79 +msgid "Sulawesi Tenggara" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:38 +msgid "Sulawesi Utara" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:39 +#: contrib/localflavor/id/id_choices.py:52 +msgid "Sumatera Barat" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:40 +#: contrib/localflavor/id/id_choices.py:56 +msgid "Sumatera Selatan" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:41 +#: contrib/localflavor/id/id_choices.py:58 +msgid "Sumatera Utara" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:46 +msgid "Magelang" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:48 +msgid "Surakarta - Solo" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:49 +msgid "Madiun" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:50 +msgid "Kediri" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:53 +msgid "Tapanuli" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:61 +msgid "Kepulauan Bangka Belitung" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:63 +msgid "Corps Consulate" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:64 +msgid "Corps Diplomatic" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:65 +msgid "Bandung" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:67 +msgid "Sulawesi Utara Daratan" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:72 +msgid "NTT - Timor" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:74 +msgid "Sulawesi Utara Kepulauan" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:77 +msgid "NTB - Lombok" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:78 +msgid "Papua dan Papua Barat" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:80 +msgid "Cirebon" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:81 +msgid "NTB - Sumbawa" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:82 +msgid "NTT - Flores" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:83 +msgid "NTT - Sumba" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:84 +msgid "Bogor" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:85 +msgid "Pekalongan" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:86 +msgid "Semarang" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:87 +msgid "Pati" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:91 +msgid "Surabaya" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:92 +msgid "Madura" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:93 +msgid "Malang" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:94 +msgid "Jember" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:95 +msgid "Banyumas" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:96 +msgid "Federal Government" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:97 +msgid "Bojonegoro" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:98 +msgid "Purwakarta" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:99 +msgid "Sidoarjo" +msgstr "" + +#: contrib/localflavor/id/id_choices.py:100 +msgid "Garut" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:8 +msgid "Antrim" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:9 +msgid "Armagh" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:10 +msgid "Carlow" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:11 +msgid "Cavan" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:12 +msgid "Clare" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:13 +msgid "Cork" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:14 +msgid "Derry" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:15 +msgid "Donegal" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:16 +msgid "Down" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:17 +msgid "Dublin" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:18 +msgid "Fermanagh" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:19 +msgid "Galway" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:20 +msgid "Kerry" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:21 +msgid "Kildare" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:22 +msgid "Kilkenny" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:23 +msgid "Laois" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:24 +msgid "Leitrim" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:25 +msgid "Limerick" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:26 +msgid "Longford" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:27 +msgid "Louth" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:28 +msgid "Mayo" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:29 +msgid "Meath" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:30 +msgid "Monaghan" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:31 +msgid "Offaly" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:32 +msgid "Roscommon" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:33 +msgid "Sligo" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:34 +msgid "Tipperary" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:35 +msgid "Tyrone" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:36 +msgid "Waterford" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:37 +msgid "Westmeath" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:38 +msgid "Wexford" +msgstr "" + +#: contrib/localflavor/ie/ie_counties.py:39 +msgid "Wicklow" +msgstr "" + +#: contrib/localflavor/in_/forms.py:15 +msgid "Enter a zip code in the format XXXXXXX." +msgstr "പിനàµâ€-കോഡൠXXXXXXX à´Žà´¨àµà´¨ മാതàµà´°àµà´•à´¯à´¿à´²àµâ€ നലàµà´•àµà´•." + +#: contrib/localflavor/is_/forms.py:18 +msgid "" +"Enter a valid Icelandic identification number. The format is XXXXXX-XXXX." +msgstr "" + +#: contrib/localflavor/is_/forms.py:19 +msgid "The Icelandic identification number is not valid." +msgstr "" + +#: contrib/localflavor/it/forms.py:15 +msgid "Enter a valid zip code." +msgstr "" + +#: contrib/localflavor/it/forms.py:44 +msgid "Enter a valid Social Security number." +msgstr "" + +#: contrib/localflavor/it/forms.py:69 +msgid "Enter a valid VAT number." +msgstr "" + +#: contrib/localflavor/jp/forms.py:16 +msgid "Enter a postal code in the format XXXXXXX or XXX-XXXX." +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:4 +msgid "Hokkaido" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:5 +msgid "Aomori" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:6 +msgid "Iwate" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:7 +msgid "Miyagi" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:8 +msgid "Akita" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:9 +msgid "Yamagata" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:10 +msgid "Fukushima" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:11 +msgid "Ibaraki" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:12 +msgid "Tochigi" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:13 +msgid "Gunma" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:14 +msgid "Saitama" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:15 +msgid "Chiba" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:16 +msgid "Tokyo" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:17 +msgid "Kanagawa" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:18 +msgid "Yamanashi" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:19 +msgid "Nagano" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:20 +msgid "Niigata" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:21 +msgid "Toyama" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:22 +msgid "Ishikawa" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:23 +msgid "Fukui" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:24 +msgid "Gifu" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:25 +msgid "Shizuoka" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:26 +msgid "Aichi" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:27 +msgid "Mie" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:28 +msgid "Shiga" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:29 +msgid "Kyoto" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:30 +msgid "Osaka" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:31 +msgid "Hyogo" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:32 +msgid "Nara" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:33 +msgid "Wakayama" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:34 +msgid "Tottori" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:35 +msgid "Shimane" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:36 +msgid "Okayama" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:37 +msgid "Hiroshima" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:38 +msgid "Yamaguchi" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:39 +msgid "Tokushima" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:40 +msgid "Kagawa" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:41 +msgid "Ehime" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:42 +msgid "Kochi" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:43 +msgid "Fukuoka" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:44 +msgid "Saga" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:45 +msgid "Nagasaki" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:46 +msgid "Kumamoto" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:47 +msgid "Oita" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:48 +msgid "Miyazaki" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:49 +msgid "Kagoshima" +msgstr "" + +#: contrib/localflavor/jp/jp_prefectures.py:50 +msgid "Okinawa" +msgstr "" + +#: contrib/localflavor/kw/forms.py:25 +msgid "Enter a valid Kuwaiti Civil ID number" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:12 +msgid "Aguascalientes" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:13 +msgid "Baja California" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:14 +msgid "Baja California Sur" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:15 +msgid "Campeche" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:16 +msgid "Chihuahua" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:17 +msgid "Chiapas" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:18 +msgid "Coahuila" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:19 +msgid "Colima" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:20 +msgid "Distrito Federal" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:21 +msgid "Durango" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:22 +msgid "Guerrero" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:23 +msgid "Guanajuato" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:24 +msgid "Hidalgo" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:25 +msgid "Jalisco" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:26 +msgid "Estado de México" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:27 +msgid "Michoacán" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:28 +msgid "Morelos" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:29 +msgid "Nayarit" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:30 +msgid "Nuevo León" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:31 +msgid "Oaxaca" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:32 +msgid "Puebla" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:33 +msgid "Querétaro" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:34 +msgid "Quintana Roo" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:35 +msgid "Sinaloa" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:36 +msgid "San Luis PotosÃ" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:37 +msgid "Sonora" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:38 +msgid "Tabasco" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:39 +msgid "Tamaulipas" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:40 +msgid "Tlaxcala" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:41 +msgid "Veracruz" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:42 +msgid "Yucatán" +msgstr "" + +#: contrib/localflavor/mx/mx_states.py:43 +msgid "Zacatecas" +msgstr "" + +#: contrib/localflavor/nl/forms.py:22 +msgid "Enter a valid postal code" +msgstr "" + +#: contrib/localflavor/nl/forms.py:79 +msgid "Enter a valid SoFi number" +msgstr "" + +#: contrib/localflavor/nl/nl_provinces.py:4 +msgid "Drenthe" +msgstr "" + +#: contrib/localflavor/nl/nl_provinces.py:5 +msgid "Flevoland" +msgstr "" + +#: contrib/localflavor/nl/nl_provinces.py:6 +msgid "Friesland" +msgstr "" + +#: contrib/localflavor/nl/nl_provinces.py:7 +msgid "Gelderland" +msgstr "" + +#: contrib/localflavor/nl/nl_provinces.py:8 +msgid "Groningen" +msgstr "" + +#: contrib/localflavor/nl/nl_provinces.py:9 +msgid "Limburg" +msgstr "" + +#: contrib/localflavor/nl/nl_provinces.py:10 +msgid "Noord-Brabant" +msgstr "" + +#: contrib/localflavor/nl/nl_provinces.py:11 +msgid "Noord-Holland" +msgstr "" + +#: contrib/localflavor/nl/nl_provinces.py:12 +msgid "Overijssel" +msgstr "" + +#: contrib/localflavor/nl/nl_provinces.py:13 +msgid "Utrecht" +msgstr "" + +#: contrib/localflavor/nl/nl_provinces.py:14 +msgid "Zeeland" +msgstr "" + +#: contrib/localflavor/nl/nl_provinces.py:15 +msgid "Zuid-Holland" +msgstr "" + +#: contrib/localflavor/no/forms.py:34 +msgid "Enter a valid Norwegian social security number." +msgstr "" + +#: contrib/localflavor/pe/forms.py:25 +msgid "This field requires 8 digits." +msgstr "" + +#: contrib/localflavor/pe/forms.py:53 +msgid "This field requires 11 digits." +msgstr "" + +#: contrib/localflavor/pl/forms.py:38 +msgid "National Identification Number consists of 11 digits." +msgstr "" + +#: contrib/localflavor/pl/forms.py:39 +msgid "Wrong checksum for the National Identification Number." +msgstr "" + +#: contrib/localflavor/pl/forms.py:71 +msgid "" +"Enter a tax number field (NIP) in the format XXX-XXX-XX-XX or XX-XX-XXX-XXX." +msgstr "" + +#: contrib/localflavor/pl/forms.py:72 +msgid "Wrong checksum for the Tax Number (NIP)." +msgstr "" + +#: contrib/localflavor/pl/forms.py:109 +msgid "National Business Register Number (REGON) consists of 9 or 14 digits." +msgstr "" + +#: contrib/localflavor/pl/forms.py:110 +msgid "Wrong checksum for the National Business Register Number (REGON)." +msgstr "" + +#: contrib/localflavor/pl/forms.py:148 +msgid "Enter a postal code in the format XX-XXX." +msgstr "" + +#: contrib/localflavor/pl/pl_voivodeships.py:8 +msgid "Lower Silesia" +msgstr "" + +#: contrib/localflavor/pl/pl_voivodeships.py:9 +msgid "Kuyavia-Pomerania" +msgstr "" + +#: contrib/localflavor/pl/pl_voivodeships.py:10 +msgid "Lublin" +msgstr "" + +#: contrib/localflavor/pl/pl_voivodeships.py:11 +msgid "Lubusz" +msgstr "" + +#: contrib/localflavor/pl/pl_voivodeships.py:12 +msgid "Lodz" +msgstr "" + +#: contrib/localflavor/pl/pl_voivodeships.py:13 +msgid "Lesser Poland" +msgstr "" + +#: contrib/localflavor/pl/pl_voivodeships.py:14 +msgid "Masovia" +msgstr "" + +#: contrib/localflavor/pl/pl_voivodeships.py:15 +msgid "Opole" +msgstr "" + +#: contrib/localflavor/pl/pl_voivodeships.py:16 +msgid "Subcarpatia" +msgstr "" + +#: contrib/localflavor/pl/pl_voivodeships.py:17 +msgid "Podlasie" +msgstr "" + +#: contrib/localflavor/pl/pl_voivodeships.py:18 +msgid "Pomerania" +msgstr "" + +#: contrib/localflavor/pl/pl_voivodeships.py:19 +msgid "Silesia" +msgstr "" + +#: contrib/localflavor/pl/pl_voivodeships.py:20 +msgid "Swietokrzyskie" +msgstr "" + +#: contrib/localflavor/pl/pl_voivodeships.py:21 +msgid "Warmia-Masuria" +msgstr "" + +#: contrib/localflavor/pl/pl_voivodeships.py:22 +msgid "Greater Poland" +msgstr "" + +#: contrib/localflavor/pl/pl_voivodeships.py:23 +msgid "West Pomerania" +msgstr "" + +#: contrib/localflavor/pt/forms.py:17 +msgid "Enter a zip code in the format XXXX-XXX." +msgstr "" + +#: contrib/localflavor/pt/forms.py:37 +msgid "Phone numbers must have 9 digits, or start by + or 00." +msgstr "" + +#: contrib/localflavor/ro/forms.py:19 +msgid "Enter a valid CIF." +msgstr "" + +#: contrib/localflavor/ro/forms.py:56 +msgid "Enter a valid CNP." +msgstr "" + +#: contrib/localflavor/ro/forms.py:141 +msgid "Enter a valid IBAN in ROXX-XXXX-XXXX-XXXX-XXXX-XXXX format" +msgstr "" + +#: contrib/localflavor/ro/forms.py:171 +msgid "Phone numbers must be in XXXX-XXXXXX format." +msgstr "" + +#: contrib/localflavor/ro/forms.py:194 +msgid "Enter a valid postal code in the format XXXXXX" +msgstr "" + +#: contrib/localflavor/se/forms.py:50 +msgid "Enter a valid Swedish organisation number." +msgstr "" + +#: contrib/localflavor/se/forms.py:107 +msgid "Enter a valid Swedish personal identity number." +msgstr "" + +#: contrib/localflavor/se/forms.py:108 +msgid "Co-ordination numbers are not allowed." +msgstr "" + +#: contrib/localflavor/se/forms.py:150 +msgid "Enter a Swedish postal code in the format XXXXX." +msgstr "" + +#: contrib/localflavor/se/se_counties.py:15 +msgid "Stockholm" +msgstr "" + +#: contrib/localflavor/se/se_counties.py:16 +msgid "Västerbotten" +msgstr "" + +#: contrib/localflavor/se/se_counties.py:17 +msgid "Norrbotten" +msgstr "" + +#: contrib/localflavor/se/se_counties.py:18 +msgid "Uppsala" +msgstr "" + +#: contrib/localflavor/se/se_counties.py:19 +msgid "Södermanland" +msgstr "" + +#: contrib/localflavor/se/se_counties.py:20 +msgid "Östergötland" +msgstr "" + +#: contrib/localflavor/se/se_counties.py:21 +msgid "Jönköping" +msgstr "" + +#: contrib/localflavor/se/se_counties.py:22 +msgid "Kronoberg" +msgstr "" + +#: contrib/localflavor/se/se_counties.py:23 +msgid "Kalmar" +msgstr "" + +#: contrib/localflavor/se/se_counties.py:24 +msgid "Gotland" +msgstr "" + +#: contrib/localflavor/se/se_counties.py:25 +msgid "Blekinge" +msgstr "" + +#: contrib/localflavor/se/se_counties.py:26 +msgid "SkÃ¥ne" +msgstr "" + +#: contrib/localflavor/se/se_counties.py:27 +msgid "Halland" +msgstr "" + +#: contrib/localflavor/se/se_counties.py:28 +msgid "Västra Götaland" +msgstr "" + +#: contrib/localflavor/se/se_counties.py:29 +msgid "Värmland" +msgstr "" + +#: contrib/localflavor/se/se_counties.py:30 +msgid "Örebro" +msgstr "" + +#: contrib/localflavor/se/se_counties.py:31 +msgid "Västmanland" +msgstr "" + +#: contrib/localflavor/se/se_counties.py:32 +msgid "Dalarna" +msgstr "" + +#: contrib/localflavor/se/se_counties.py:33 +msgid "Gävleborg" +msgstr "" + +#: contrib/localflavor/se/se_counties.py:34 +msgid "Västernorrland" +msgstr "" + +#: contrib/localflavor/se/se_counties.py:35 +msgid "Jämtland" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:8 +msgid "Banska Bystrica" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:9 +msgid "Banska Stiavnica" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:10 +msgid "Bardejov" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:11 +msgid "Banovce nad Bebravou" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:12 +msgid "Brezno" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:13 +msgid "Bratislava I" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:14 +msgid "Bratislava II" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:15 +msgid "Bratislava III" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:16 +msgid "Bratislava IV" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:17 +msgid "Bratislava V" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:18 +msgid "Bytca" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:19 +msgid "Cadca" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:20 +msgid "Detva" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:21 +msgid "Dolny Kubin" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:22 +msgid "Dunajska Streda" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:23 +msgid "Galanta" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:24 +msgid "Gelnica" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:25 +msgid "Hlohovec" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:26 +msgid "Humenne" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:27 +msgid "Ilava" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:28 +msgid "Kezmarok" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:29 +msgid "Komarno" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:30 +msgid "Kosice I" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:31 +msgid "Kosice II" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:32 +msgid "Kosice III" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:33 +msgid "Kosice IV" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:34 +msgid "Kosice - okolie" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:35 +msgid "Krupina" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:36 +msgid "Kysucke Nove Mesto" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:37 +msgid "Levice" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:38 +msgid "Levoca" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:39 +msgid "Liptovsky Mikulas" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:40 +msgid "Lucenec" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:41 +msgid "Malacky" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:42 +msgid "Martin" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:43 +msgid "Medzilaborce" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:44 +msgid "Michalovce" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:45 +msgid "Myjava" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:46 +msgid "Namestovo" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:47 +msgid "Nitra" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:48 +msgid "Nove Mesto nad Vahom" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:49 +msgid "Nove Zamky" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:50 +msgid "Partizanske" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:51 +msgid "Pezinok" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:52 +msgid "Piestany" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:53 +msgid "Poltar" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:54 +msgid "Poprad" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:55 +msgid "Povazska Bystrica" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:56 +msgid "Presov" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:57 +msgid "Prievidza" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:58 +msgid "Puchov" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:59 +msgid "Revuca" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:60 +msgid "Rimavska Sobota" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:61 +msgid "Roznava" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:62 +msgid "Ruzomberok" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:63 +msgid "Sabinov" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:64 +msgid "Senec" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:65 +msgid "Senica" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:66 +msgid "Skalica" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:67 +msgid "Snina" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:68 +msgid "Sobrance" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:69 +msgid "Spisska Nova Ves" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:70 +msgid "Stara Lubovna" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:71 +msgid "Stropkov" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:72 +msgid "Svidnik" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:73 +msgid "Sala" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:74 +msgid "Topolcany" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:75 +msgid "Trebisov" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:76 +msgid "Trencin" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:77 +msgid "Trnava" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:78 +msgid "Turcianske Teplice" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:79 +msgid "Tvrdosin" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:80 +msgid "Velky Krtis" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:81 +msgid "Vranov nad Toplou" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:82 +msgid "Zlate Moravce" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:83 +msgid "Zvolen" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:84 +msgid "Zarnovica" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:85 +msgid "Ziar nad Hronom" +msgstr "" + +#: contrib/localflavor/sk/sk_districts.py:86 +msgid "Zilina" +msgstr "" + +#: contrib/localflavor/sk/sk_regions.py:8 +msgid "Banska Bystrica region" +msgstr "" + +#: contrib/localflavor/sk/sk_regions.py:9 +msgid "Bratislava region" +msgstr "" + +#: contrib/localflavor/sk/sk_regions.py:10 +msgid "Kosice region" +msgstr "" + +#: contrib/localflavor/sk/sk_regions.py:11 +msgid "Nitra region" +msgstr "" + +#: contrib/localflavor/sk/sk_regions.py:12 +msgid "Presov region" +msgstr "" + +#: contrib/localflavor/sk/sk_regions.py:13 +msgid "Trencin region" +msgstr "" + +#: contrib/localflavor/sk/sk_regions.py:14 +msgid "Trnava region" +msgstr "" + +#: contrib/localflavor/sk/sk_regions.py:15 +msgid "Zilina region" +msgstr "" + +#: contrib/localflavor/uk/forms.py:21 +msgid "Enter a valid postcode." +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:11 +msgid "Bedfordshire" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:12 +msgid "Buckinghamshire" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:14 +msgid "Cheshire" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:15 +msgid "Cornwall and Isles of Scilly" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:16 +msgid "Cumbria" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:17 +msgid "Derbyshire" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:18 +msgid "Devon" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:19 +msgid "Dorset" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:20 +msgid "Durham" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:21 +msgid "East Sussex" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:22 +msgid "Essex" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:23 +msgid "Gloucestershire" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:24 +msgid "Greater London" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:25 +msgid "Greater Manchester" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:26 +msgid "Hampshire" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:27 +msgid "Hertfordshire" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:28 +msgid "Kent" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:29 +msgid "Lancashire" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:30 +msgid "Leicestershire" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:31 +msgid "Lincolnshire" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:32 +msgid "Merseyside" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:33 +msgid "Norfolk" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:34 +msgid "North Yorkshire" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:35 +msgid "Northamptonshire" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:36 +msgid "Northumberland" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:37 +msgid "Nottinghamshire" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:38 +msgid "Oxfordshire" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:39 +msgid "Shropshire" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:40 +msgid "Somerset" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:41 +msgid "South Yorkshire" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:42 +msgid "Staffordshire" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:43 +msgid "Suffolk" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:44 +msgid "Surrey" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:45 +msgid "Tyne and Wear" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:46 +msgid "Warwickshire" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:47 +msgid "West Midlands" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:48 +msgid "West Sussex" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:49 +msgid "West Yorkshire" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:50 +msgid "Wiltshire" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:51 +msgid "Worcestershire" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:55 +msgid "County Antrim" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:56 +msgid "County Armagh" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:57 +msgid "County Down" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:58 +msgid "County Fermanagh" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:59 +msgid "County Londonderry" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:60 +msgid "County Tyrone" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:64 +msgid "Clwyd" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:65 +msgid "Dyfed" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:66 +msgid "Gwent" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:67 +msgid "Gwynedd" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:68 +msgid "Mid Glamorgan" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:69 +msgid "Powys" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:70 +msgid "South Glamorgan" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:71 +msgid "West Glamorgan" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:75 +msgid "Borders" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:76 +msgid "Central Scotland" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:77 +msgid "Dumfries and Galloway" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:78 +msgid "Fife" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:79 +msgid "Grampian" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:80 +msgid "Highland" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:81 +msgid "Lothian" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:82 +msgid "Orkney Islands" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:83 +msgid "Shetland Islands" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:84 +msgid "Strathclyde" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:85 +msgid "Tayside" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:86 +msgid "Western Isles" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:90 +msgid "England" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:91 +msgid "Northern Ireland" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:92 +msgid "Scotland" +msgstr "" + +#: contrib/localflavor/uk/uk_regions.py:93 +msgid "Wales" +msgstr "" + +#: contrib/localflavor/us/forms.py:17 +msgid "Enter a zip code in the format XXXXX or XXXXX-XXXX." +msgstr "" + +#: contrib/localflavor/us/forms.py:26 +msgid "Phone numbers must be in XXX-XXX-XXXX format." +msgstr "" + +#: contrib/localflavor/us/forms.py:55 +msgid "Enter a valid U.S. Social Security number in XXX-XX-XXXX format." +msgstr "" + +#: contrib/localflavor/us/forms.py:88 +msgid "Enter a U.S. state or territory." +msgstr "" + +#: contrib/localflavor/us/models.py:8 +msgid "U.S. state (two uppercase letters)" +msgstr "" + +#: contrib/localflavor/us/models.py:17 +msgid "Phone number" +msgstr "" + +#: contrib/localflavor/uy/forms.py:28 +msgid "Enter a valid CI number in X.XXX.XXX-X,XXXXXXX-X or XXXXXXXX format." +msgstr "" + +#: contrib/localflavor/uy/forms.py:30 +msgid "Enter a valid CI number." +msgstr "" + +#: contrib/localflavor/za/forms.py:21 +msgid "Enter a valid South African ID number" +msgstr "" + +#: contrib/localflavor/za/forms.py:55 +msgid "Enter a valid South African postal code" +msgstr "" + +#: contrib/localflavor/za/za_provinces.py:4 +msgid "Eastern Cape" +msgstr "" + +#: contrib/localflavor/za/za_provinces.py:5 +msgid "Free State" +msgstr "" + +#: contrib/localflavor/za/za_provinces.py:6 +msgid "Gauteng" +msgstr "" + +#: contrib/localflavor/za/za_provinces.py:7 +msgid "KwaZulu-Natal" +msgstr "" + +#: contrib/localflavor/za/za_provinces.py:8 +msgid "Limpopo" +msgstr "" + +#: contrib/localflavor/za/za_provinces.py:9 +msgid "Mpumalanga" +msgstr "" + +#: contrib/localflavor/za/za_provinces.py:10 +msgid "Northern Cape" +msgstr "" + +#: contrib/localflavor/za/za_provinces.py:11 +msgid "North West" +msgstr "" + +#: contrib/localflavor/za/za_provinces.py:12 +msgid "Western Cape" +msgstr "" + +#: contrib/messages/tests/base.py:101 +msgid "lazy message" +msgstr "അലസ സനàµà´¦àµ‡à´¶à´‚" + +#: contrib/redirects/models.py:7 +msgid "redirect from" +msgstr "പഴയ വിലാസം" + +#: contrib/redirects/models.py:8 +msgid "" +"This should be an absolute path, excluding the domain name. Example: '/" +"events/search/'." +msgstr "ഇതൠഡൊമൈനàµâ€ നാമം ഉളàµâ€à´ªàµà´ªàµ†à´Ÿà´¾à´¤àµà´¤ ഒരൠകേവലമാരàµâ€à´—à´‚ (വിലാസം) ആവണം. " +"ഉദാ: '/events/search/'." + +#: contrib/redirects/models.py:9 +msgid "redirect to" +msgstr "à´ªàµà´¤à´¿à´¯ വിലാസം" + +#: contrib/redirects/models.py:10 +msgid "" +"This can be either an absolute path (as above) or a full URL starting with " +"'http://'." +msgstr "ഇതൊരൠകേവല മാരàµâ€à´—മോ 'http://' à´Žà´¨àµà´¨àµ à´¤àµà´Ÿà´™àµà´™àµà´¨àµà´¨ പൂരàµâ€à´£àµà´£ വിലാസമോ (URL) ആവാം" + +#: contrib/redirects/models.py:13 +msgid "redirect" +msgstr "വിലാസമാറàµà´±à´‚" + +#: contrib/redirects/models.py:14 +msgid "redirects" +msgstr "വിലാസമാറàµà´±à´™àµà´™à´³àµâ€" + +#: contrib/sessions/models.py:45 +msgid "session key" +msgstr "സെഷനàµâ€ കീ" + +#: contrib/sessions/models.py:47 +msgid "session data" +msgstr "സെഷനàµâ€ വിവരം" + +#: contrib/sessions/models.py:48 +msgid "expire date" +msgstr "കാലാവധി (തീയതി)" + +#: contrib/sessions/models.py:53 +msgid "session" +msgstr "സെഷനàµâ€" + +#: contrib/sessions/models.py:54 +msgid "sessions" +msgstr "സെഷനàµà´•à´³àµâ€" + +#: contrib/sites/models.py:32 +msgid "domain name" +msgstr "ഡൊമൈനàµâ€ നാമം" + +#: contrib/sites/models.py:33 +msgid "display name" +msgstr "à´ªàµà´°à´¦à´°àµâ€à´¶à´¨ നാമം" + +#: contrib/sites/models.py:39 +msgid "sites" +msgstr "സൈറàµà´±àµà´•à´³àµâ€" + +#: core/validators.py:20 forms/fields.py:66 +msgid "Enter a valid value." +msgstr "ശരിയായ മൂലàµà´¯à´‚ നലàµà´•à´£à´‚." + +#: core/validators.py:87 forms/fields.py:528 +msgid "Enter a valid URL." +msgstr "ശരിയായ URL നലàµà´•à´£à´‚." + +#: core/validators.py:89 forms/fields.py:529 +msgid "This URL appears to be a broken link." +msgstr "à´ˆ URL നിലവിലàµà´²à´¾à´¤àµà´¤ വിലാസമാണൠകാണികàµà´•àµà´¨àµà´¨à´¤àµ." + +#: core/validators.py:123 forms/fields.py:877 +msgid "" +"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens." +msgstr "ശരിയായ à´¸àµà´³à´—ൠനലàµà´•àµà´• (à´…à´•àµà´·à´°à´™àµà´™à´³àµâ€, à´…à´•àµà´•à´™àµà´™à´³àµâ€, à´…à´£àµà´Ÿà´°àµâ€à´¸àµà´•àµ‹à´°àµâ€, ഹൈഫനàµâ€ à´Žà´¨àµà´¨à´¿à´µ മാതàµà´°à´‚ ചേരàµâ€à´¨àµà´¨à´¤àµ)." + +#: core/validators.py:126 forms/fields.py:870 +msgid "Enter a valid IPv4 address." +msgstr "ശരിയായ IPv4 വിലാസം നലàµà´•à´£à´‚" + +#: core/validators.py:129 db/models/fields/__init__.py:572 +msgid "Enter only digits separated by commas." +msgstr "à´…à´•àµà´•à´™àµà´™à´³àµâ€ മാതàµà´°à´‚ (കോമയിടàµà´Ÿàµ വേരàµâ€à´¤à´¿à´°à´¿à´šàµà´šà´¤àµ)" + +#: core/validators.py:135 +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "ഇതൠ%(limit_value)s ആവണം. (ഇപàµà´ªàµ‹à´³àµâ€ %(show_value)s)." + +#: core/validators.py:153 forms/fields.py:204 forms/fields.py:256 +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "ഇതൠ%(limit_value)s-à´“ അതിലàµâ€ à´•àµà´±à´µàµ‹ ആവണം" + +#: core/validators.py:158 forms/fields.py:205 forms/fields.py:257 +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "ഇതൠ%(limit_value)s-à´“ അതിലàµâ€ കൂടàµà´¤à´²àµ‹ ആവണം" + +#: core/validators.py:164 +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d characters (it has %" +"(show_value)d)." +msgstr "ഇതിനൠà´à´±àµà´±à´µàµà´‚ à´•àµà´±à´žàµà´žà´¤àµ %(limit_value)d à´…à´•àµà´·à´°à´™àµà´™à´³àµâ€ വേണം. (ഇപàµà´ªàµ‹à´³àµâ€ " +"%(show_value)d à´…à´•àµà´·à´°à´™àµà´™à´³àµà´£àµà´Ÿàµ.)" + +#: core/validators.py:170 +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d characters (it has %" +"(show_value)d)." +msgstr "ഇതിനൠപരമാവധി %(limit_value)d à´…à´•àµà´·à´°à´™àµà´™à´³àµ‡ ഉളàµà´³àµ‚ à´Žà´¨àµà´¨àµ ഉറപàµà´ªà´¾à´•àµà´•àµà´•. (ഇപàµà´ªàµ‹à´³àµâ€ " +"%(show_value)d à´…à´•àµà´·à´°à´™àµà´™à´³àµà´£àµà´Ÿàµ.)" + +#: db/models/base.py:823 +#, python-format +msgid "%(field_name)s must be unique for %(date_field)s %(lookup)s." +msgstr "%(date_field)s %(lookup)s-നൠ%(field_name)s ആവരàµâ€à´¤àµà´¤à´¿à´•àµà´•à´¾à´¨àµâ€ പാടിലàµà´²." + +#: db/models/base.py:838 db/models/base.py:846 +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "%(field_label)s-ഓടൠകൂടിയ %(model_name)s നിലവിലàµà´£àµà´Ÿàµ." + +#: db/models/fields/__init__.py:63 +#, python-format +msgid "Value %r is not a valid choice." +msgstr "%r à´…à´¨àµà´¯àµ‹à´œàµà´¯à´®à´²àµà´²." + +#: db/models/fields/__init__.py:64 +msgid "This field cannot be null." +msgstr "à´ˆ à´•à´³àµà´³à´¿ à´’à´´à´¿à´šàµà´šà´¿à´Ÿà´°àµà´¤àµ." + +#: db/models/fields/__init__.py:65 +msgid "This field cannot be blank." +msgstr "à´ˆ à´•à´³àµà´³à´¿ à´’à´´à´¿à´šàµà´šà´¿à´Ÿà´°àµà´¤àµ." + +#: db/models/fields/__init__.py:70 +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "" + +#: db/models/fields/__init__.py:451 db/models/fields/__init__.py:852 +#: db/models/fields/__init__.py:961 db/models/fields/__init__.py:972 +#: db/models/fields/__init__.py:999 +msgid "Integer" +msgstr "പൂരàµâ€à´£àµà´£à´¸à´‚à´–àµà´¯" + +#: db/models/fields/__init__.py:455 db/models/fields/__init__.py:850 +msgid "This value must be an integer." +msgstr "പൂരàµâ€à´£àµà´£à´¸à´‚à´–àµà´¯ മാതàµà´°à´‚" + +#: db/models/fields/__init__.py:490 +msgid "This value must be either True or False." +msgstr "ശരിയോ തെറàµà´±àµ‹ à´Žà´¨àµà´¨àµ മാതàµà´°à´‚" + +#: db/models/fields/__init__.py:492 +msgid "Boolean (Either True or False)" +msgstr "ശരിയോ തെറàµà´±àµ‹ à´Žà´¨àµà´¨àµ മാതàµà´°à´‚" + +#: db/models/fields/__init__.py:539 db/models/fields/__init__.py:982 +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "" + +#: db/models/fields/__init__.py:567 +msgid "Comma-separated integers" +msgstr "കോമയിടàµà´Ÿàµ വേരàµâ€à´¤à´¿à´°à´¿à´šàµà´š സംഖàµà´¯à´•à´³àµâ€" + +#: db/models/fields/__init__.py:581 +msgid "Date (without time)" +msgstr "തീയതി (സമയം വേണàµà´Ÿ)" + +#: db/models/fields/__init__.py:585 +msgid "Enter a valid date in YYYY-MM-DD format." +msgstr "ശരിയായ തീയതി YYYY-MM-DD à´Žà´¨àµà´¨ മാതàµà´°àµà´•à´¯à´¿à´²àµâ€ നലàµà´•à´£à´‚." + +#: db/models/fields/__init__.py:586 +#, python-format +msgid "Invalid date: %s" +msgstr "തെറàµà´±à´¾à´¯ തീയതി: %s" + +#: db/models/fields/__init__.py:667 +msgid "Enter a valid date/time in YYYY-MM-DD HH:MM[:ss[.uuuuuu]] format." +msgstr "ശരിയായ തീയതി/സമയം YYYY-MM-DD HH:MM[:ss[.uuuuuu]]à´Žà´¨àµà´¨ മാതàµà´°àµà´•à´¯à´¿à´²àµâ€ നലàµà´•à´£à´‚." + +#: db/models/fields/__init__.py:669 +msgid "Date (with time)" +msgstr "തീയതി (സമയതàµà´¤àµ‹à´ŸàµŠà´ªàµà´ªà´‚)" + +#: db/models/fields/__init__.py:735 +msgid "This value must be a decimal number." +msgstr "à´ˆ വില ദശാംശമാവണം." + +#: db/models/fields/__init__.py:737 +msgid "Decimal number" +msgstr "ദശാംശസംഖàµà´¯" + +#: db/models/fields/__init__.py:792 +msgid "E-mail address" +msgstr "à´‡-മെയിലàµâ€ വിലാസം" + +#: db/models/fields/__init__.py:799 db/models/fields/files.py:220 +#: db/models/fields/files.py:331 +msgid "File path" +msgstr "ഫയലàµâ€ à´¸àµà´¥à´¾à´¨à´‚" + +#: db/models/fields/__init__.py:822 +msgid "This value must be a float." +msgstr "à´ˆ വില ദശാംശമാവണം." + +#: db/models/fields/__init__.py:824 +msgid "Floating point number" +msgstr "ദശാംശസംഖàµà´¯" + +#: db/models/fields/__init__.py:883 +msgid "Big (8 byte) integer" +msgstr "8 ബൈറàµà´±àµ പൂരàµâ€à´£à´¸à´‚à´–àµà´¯." + +#: db/models/fields/__init__.py:912 +msgid "This value must be either None, True or False." +msgstr "à´ˆ മൂലàµà´¯à´‚ None, True, False à´Žà´¨àµà´¨à´¿à´µà´¯à´¿à´²àµâ€ à´à´¤àµ†à´™àµà´•à´¿à´²àµà´‚ à´’à´¨àµà´¨à´¾à´µà´£à´‚" + +#: db/models/fields/__init__.py:914 +msgid "Boolean (Either True, False or None)" +msgstr "ശരിയോ തെറàµà´±àµ‹ à´Žà´¨àµà´¨àµ മാതàµà´°à´‚" + +#: db/models/fields/__init__.py:1005 +msgid "Text" +msgstr "ടെകàµà´¸àµà´±àµà´±àµ" + +#: db/models/fields/__init__.py:1021 +msgid "Time" +msgstr "സമയം" + +#: db/models/fields/__init__.py:1025 +msgid "Enter a valid time in HH:MM[:ss[.uuuuuu]] format." +msgstr "ശരിയായ സമയം HH:MM[:ss[.uuuuuu]] à´Žà´¨àµà´¨ മാതàµà´°àµà´•à´¯à´¿à´²àµâ€ നലàµà´•à´£à´‚." + +#: db/models/fields/__init__.py:1109 +msgid "XML text" +msgstr "" + +#: db/models/fields/related.py:799 +#, python-format +msgid "Model %(model)s with pk %(pk)r does not exist." +msgstr "" + +#: db/models/fields/related.py:801 +msgid "Foreign Key (type determined by related field)" +msgstr "" + +#: db/models/fields/related.py:918 +msgid "One-to-one relationship" +msgstr "" + +#: db/models/fields/related.py:980 +msgid "Many-to-many relationship" +msgstr "" + +#: db/models/fields/related.py:1000 +msgid "" +"Hold down \"Control\", or \"Command\" on a Mac, to select more than one." +msgstr "\"Control\" à´Žà´¨àµà´¨ കീ അമരàµâ€à´¤àµà´¤à´¿à´ªàµà´ªà´¿à´Ÿà´¿à´•àµà´•àµà´•. (Macലാണെങàµà´•à´¿à´²àµâ€ \"Command\")." + +#: db/models/fields/related.py:1061 +#, python-format +msgid "Please enter valid %(self)s IDs. The value %(value)r is invalid." +msgid_plural "" +"Please enter valid %(self)s IDs. The values %(value)r are invalid." +msgstr[0] "ദയവായി ശരിയായ %(self)s IDà´•à´³àµâ€ നലàµà´•àµà´•. %(value)r à´…à´¨àµà´¯àµ‹à´œàµà´¯à´®à´²àµà´²." +msgstr[1] "ദയവായി ശരിയായ %(self)s IDà´•à´³àµâ€ നലàµà´•àµà´•. %(value)r à´…à´¨àµà´¯àµ‹à´œàµà´¯à´®à´²àµà´²." + +#: forms/fields.py:65 +msgid "This field is required." +msgstr "à´ˆ à´•à´³àµà´³à´¿ നിരàµâ€à´¬à´¨àµà´§à´®à´¾à´£àµ." + +#: forms/fields.py:203 +msgid "Enter a whole number." +msgstr "ഒരൠപൂരàµâ€à´£à´¸à´‚à´–àµà´¯ നലàµà´•àµà´•." + +#: forms/fields.py:234 forms/fields.py:255 +msgid "Enter a number." +msgstr "ഒരൠസംഖàµà´¯ നലàµà´•àµà´•." + +#: forms/fields.py:258 +#, python-format +msgid "Ensure that there are no more than %s digits in total." +msgstr "മൊതàµà´¤à´‚ %s ലേറെ à´…à´•àµà´•à´™àµà´™à´³àµâ€ ഇലàµà´²àµ†à´¨àµà´¨àµ ഉറപàµà´ªàµ വരàµà´¤àµà´¤àµà´•." + +#: forms/fields.py:259 +#, python-format +msgid "Ensure that there are no more than %s decimal places." +msgstr "%s ലേറെ ദശാംശസàµà´¥à´¾à´¨à´™àµà´™à´³àµâ€ ഇലàµà´²àµ†à´¨àµà´¨àµ ഉറപàµà´ªàµ വരàµà´¤àµà´¤àµà´•." + +#: forms/fields.py:260 +#, python-format +msgid "Ensure that there are no more than %s digits before the decimal point." +msgstr "ദശാംശബിനàµà´¦àµà´µà´¿à´¨àµ à´®àµà´®àµà´ªàµ %sലേറെ à´…à´•àµà´•à´™àµà´™à´³àµâ€ ഇലàµà´²àµ†à´¨àµà´¨àµ ഉറപàµà´ªàµ വരàµà´¤àµà´¤àµà´•." + +#: forms/fields.py:322 forms/fields.py:837 +msgid "Enter a valid date." +msgstr "ശരിയായ തീയതി നലàµà´•àµà´•." + +#: forms/fields.py:350 forms/fields.py:838 +msgid "Enter a valid time." +msgstr "ശരിയായ സമയം നലàµà´•àµà´•." + +#: forms/fields.py:376 +msgid "Enter a valid date/time." +msgstr "ശരിയായ തീയതിയàµà´‚ സമയവàµà´‚ നലàµà´•àµà´•." + +#: forms/fields.py:434 +msgid "No file was submitted. Check the encoding type on the form." +msgstr "ഫയലൊനàµà´¨àµà´‚ à´²à´à´¿à´šàµà´šà´¿à´²àµà´². ഫോമിലെ à´Žà´¨àµâ€-കോഡിംഗൠപരിശോധികàµà´•àµà´•." + +#: forms/fields.py:435 +msgid "No file was submitted." +msgstr "ഫയലൊനàµà´¨àµà´‚ à´²à´à´¿à´šàµà´šà´¿à´²àµà´²." + +#: forms/fields.py:436 +msgid "The submitted file is empty." +msgstr "à´²à´à´¿à´šàµà´š ഫയലàµâ€ ശൂനàµà´¯à´®à´¾à´£àµ." + +#: forms/fields.py:437 +#, python-format +msgid "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr "à´ˆ ഫയലിനàµà´±àµ† പേരൠപരമാവധി %(max)d à´…à´•àµà´·à´°à´™àµà´™à´³àµà´³àµà´³à´¤à´¾à´¯à´¿à´°à´¿à´•àµà´•à´£à´‚. " +"(ഇപàµà´ªàµ‹à´³àµâ€ %(length)d à´…à´•àµà´·à´°à´™àµà´™à´³àµâ€ ഉണàµà´Ÿàµ)." + +#: forms/fields.py:472 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "ശരിയായ à´šà´¿à´¤àµà´°à´‚ അപൠലോഡൠചെയàµà´¯àµà´•. നിങàµà´™à´³àµâ€ നലàµà´•à´¿à´¯ ഫയലàµâ€ à´’à´¨àµà´¨àµà´•à´¿à´²àµâ€ ഒരൠചിതàµà´°à´®à´²àµà´², " +"à´…à´²àµà´²àµ†à´™àµà´•à´¿à´²àµâ€ വികലമാണàµ." + +#: forms/fields.py:595 forms/fields.py:670 +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "യോഗàµà´¯à´®à´¾à´¯à´¤àµ തെരഞàµà´žàµ†à´Ÿàµà´•àµà´•àµà´•. %(value)s à´²à´àµà´¯à´®à´¾à´¯à´µà´¯à´¿à´²àµâ€ ഉളàµâ€à´ªàµà´ªàµ†à´Ÿàµà´¨àµà´¨à´¿à´²àµà´²." + +#: forms/fields.py:671 forms/fields.py:733 forms/models.py:1002 +msgid "Enter a list of values." +msgstr "മൂലàµà´¯à´™àµà´™à´³àµà´Ÿàµ† പടàµà´Ÿà´¿à´•(ലിസàµà´±àµà´±àµ) നലàµà´•àµà´•." + +#: forms/formsets.py:298 forms/formsets.py:300 +msgid "Order" +msgstr "à´•àµà´°à´®à´‚" + +#: forms/models.py:562 +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "%(field)s-നായി നലàµà´•àµà´¨àµà´¨ വിവരം ആവരàµâ€à´¤àµà´¤à´¿à´šàµà´šà´¤àµ ദയവായി തിരàµà´¤àµà´¤àµà´•." + +#: forms/models.py:566 +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "%(field)s-നായി നലàµà´•àµà´¨àµà´¨ വിവരം ആവരàµâ€à´¤àµà´¤à´¿à´•àµà´•à´¾à´¨àµâ€ പാടിലàµà´². ദയവായി തിരàµà´¤àµà´¤àµà´•." + +#: forms/models.py:572 +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "%(date_field)s ലെ %(lookup)s നൠവേണàµà´Ÿà´¿ %(field_name)s നൠനലàµà´•àµà´¨àµà´¨ വിവരം " +"ആവരàµâ€à´¤àµà´¤à´¿à´•àµà´•à´¾à´¨àµâ€ പാടിലàµà´². ദയവായി തിരàµà´¤àµà´¤àµà´•." + +#: forms/models.py:580 +msgid "Please correct the duplicate values below." +msgstr "താഴെ കൊടàµà´¤àµà´¤à´µà´¯à´¿à´²àµâ€ ആവരàµâ€à´¤àµà´¤à´¨à´‚ ഒഴിവാകàµà´•àµà´•." + +#: forms/models.py:855 +msgid "The inline foreign key did not match the parent instance primary key." +msgstr "" + +#: forms/models.py:921 +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "യോഗàµà´¯à´®à´¾à´¯à´¤àµ തെരഞàµà´žàµ†à´Ÿàµà´•àµà´•àµà´•. നിങàµà´™à´³àµâ€ നലàµà´•à´¿à´¯à´¤àµ à´²à´àµà´¯à´®à´¾à´¯à´µà´¯à´¿à´²àµâ€ ഉളàµâ€à´ªàµà´ªàµ†à´Ÿàµà´¨àµà´¨à´¿à´²àµà´²." + +#: forms/models.py:1003 +#, python-format +msgid "Select a valid choice. %s is not one of the available choices." +msgstr "യോഗàµà´¯à´®à´¾à´¯à´¤àµ തെരഞàµà´žàµ†à´Ÿàµà´•àµà´•àµà´•. %s തനàµà´¨à´¿à´°à´¿à´•àµà´•àµà´¨àµà´¨à´µà´¯à´¿à´²àµâ€ ഉളàµâ€à´ªàµà´ªàµ†à´Ÿàµà´¨àµà´¨à´¿à´²àµà´²." + +#: forms/models.py:1005 +#, python-format +msgid "\"%s\" is not a valid value for a primary key." +msgstr "\"%s\" à´ªàµà´°à´¾à´¥à´®à´¿à´• കീ ആവാനàµâ€ à´…à´¨àµà´¯àµ‹à´œàµà´¯à´®à´¾à´¯ മൂലàµà´¯à´®à´²àµà´²." + +#: template/defaultfilters.py:776 +msgid "yes,no,maybe" +msgstr "ഉണàµà´Ÿàµ, ഇലàµà´², ഉണàµà´Ÿà´¾à´µà´¾à´‚" + +#: template/defaultfilters.py:807 +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "" +msgstr[1] "" + +#: template/defaultfilters.py:809 +#, python-format +msgid "%.1f KB" +msgstr "" + +#: template/defaultfilters.py:811 +#, python-format +msgid "%.1f MB" +msgstr "" + +#: template/defaultfilters.py:812 +#, python-format +msgid "%.1f GB" +msgstr "" + +#: utils/dateformat.py:42 +msgid "p.m." +msgstr "" + +#: utils/dateformat.py:43 +msgid "a.m." +msgstr "" + +#: utils/dateformat.py:48 +msgid "PM" +msgstr "PM" + +#: utils/dateformat.py:49 +msgid "AM" +msgstr "AM" + +#: utils/dateformat.py:98 +msgid "midnight" +msgstr "à´…à´°àµâ€à´§à´°à´¾à´¤àµà´°à´¿" + +#: utils/dateformat.py:100 +msgid "noon" +msgstr "ഉചàµà´š" + +#: utils/dates.py:6 +msgid "Monday" +msgstr "തിങàµà´•à´³àµâ€" + +#: utils/dates.py:6 +msgid "Tuesday" +msgstr "ചൊവàµà´µ" + +#: utils/dates.py:6 +msgid "Wednesday" +msgstr "à´¬àµà´§à´¨àµâ€" + +#: utils/dates.py:6 +msgid "Thursday" +msgstr "à´µàµà´¯à´¾à´´à´‚" + +#: utils/dates.py:6 +msgid "Friday" +msgstr "വെളàµà´³à´¿" + +#: utils/dates.py:7 +msgid "Saturday" +msgstr "ശനി" + +#: utils/dates.py:7 +msgid "Sunday" +msgstr "ഞായരàµâ€" + +#: utils/dates.py:10 +msgid "Mon" +msgstr "തിങàµà´•à´³àµâ€" + +#: utils/dates.py:10 +msgid "Tue" +msgstr "ചൊവàµà´µ" + +#: utils/dates.py:10 +msgid "Wed" +msgstr "à´¬àµà´§à´¨àµâ€" + +#: utils/dates.py:10 +msgid "Thu" +msgstr "à´µàµà´¯à´¾à´´à´‚" + +#: utils/dates.py:10 +msgid "Fri" +msgstr "വെളàµà´³à´¿" + +#: utils/dates.py:11 +msgid "Sat" +msgstr "ശനി" + +#: utils/dates.py:11 +msgid "Sun" +msgstr "ഞായരàµâ€" + +#: utils/dates.py:18 +msgid "January" +msgstr "ജനàµà´µà´°à´¿" + +#: utils/dates.py:18 +msgid "February" +msgstr "ഫെബàµà´°àµà´µà´°à´¿" + +#: utils/dates.py:18 utils/dates.py:31 +msgid "March" +msgstr "മാരàµâ€à´šàµà´šàµ" + +#: utils/dates.py:18 utils/dates.py:31 +msgid "April" +msgstr "à´à´ªàµà´°à´¿à´²àµâ€" + +#: utils/dates.py:18 utils/dates.py:31 +msgid "May" +msgstr "മേയàµ" + +#: utils/dates.py:18 utils/dates.py:31 +msgid "June" +msgstr "ജൂണàµâ€" + +#: utils/dates.py:19 utils/dates.py:31 +msgid "July" +msgstr "ജൂലൈ" + +#: utils/dates.py:19 +msgid "August" +msgstr "ആഗസàµà´¤àµ" + +#: utils/dates.py:19 +msgid "September" +msgstr "സെപàµà´¤à´‚ബരàµâ€" + +#: utils/dates.py:19 +msgid "October" +msgstr "à´’à´•àµà´Ÿàµ‹à´¬à´°àµâ€" + +#: utils/dates.py:19 +msgid "November" +msgstr "നവംബരàµâ€" + +#: utils/dates.py:20 +msgid "December" +msgstr "ഡിസംബരàµâ€" + +#: utils/dates.py:23 +msgid "jan" +msgstr "ജനàµ." + +#: utils/dates.py:23 +msgid "feb" +msgstr "ഫെബàµà´°àµ." + +#: utils/dates.py:23 +msgid "mar" +msgstr "മാരàµâ€à´šàµà´šàµ" + +#: utils/dates.py:23 +msgid "apr" +msgstr "à´à´ªàµà´°à´¿à´²àµâ€" + +#: utils/dates.py:23 +msgid "may" +msgstr "മേയàµ" + +#: utils/dates.py:23 +msgid "jun" +msgstr "ജൂണàµâ€" + +#: utils/dates.py:24 +msgid "jul" +msgstr "ജൂലൈ" + +#: utils/dates.py:24 +msgid "aug" +msgstr "ആഗസàµà´¤àµ" + +#: utils/dates.py:24 +msgid "sep" +msgstr "സെപàµà´Ÿà´‚." + +#: utils/dates.py:24 +msgid "oct" +msgstr "à´’à´•àµà´Ÿàµ‹." + +#: utils/dates.py:24 +msgid "nov" +msgstr "നവം." + +#: utils/dates.py:24 +msgid "dec" +msgstr "ഡിസം." + +#: utils/dates.py:31 +msgid "Jan." +msgstr "ജനàµ." + +#: utils/dates.py:31 +msgid "Feb." +msgstr "ഫെബàµà´°àµ." + +#: utils/dates.py:32 +msgid "Aug." +msgstr "ആഗസàµà´¤àµ" + +#: utils/dates.py:32 +msgid "Sept." +msgstr "സെപàµà´Ÿà´‚." + +#: utils/dates.py:32 +msgid "Oct." +msgstr "à´’à´•àµà´Ÿàµ‹." + +#: utils/dates.py:32 +msgid "Nov." +msgstr "നവം." + +#: utils/dates.py:32 +msgid "Dec." +msgstr "ഡിസം." + +#: utils/text.py:130 +msgid "or" +msgstr "അഥവാ" + +#: utils/timesince.py:21 +msgid "year" +msgid_plural "years" +msgstr[0] "വരàµâ€à´·à´‚" +msgstr[1] "വരàµâ€à´·à´™àµà´™à´³àµâ€" + +#: utils/timesince.py:22 +msgid "month" +msgid_plural "months" +msgstr[0] "മാസം" +msgstr[1] "മാസങàµà´™à´³àµâ€" + +#: utils/timesince.py:23 +msgid "week" +msgid_plural "weeks" +msgstr[0] "ആഴàµà´šàµà´š" +msgstr[1] "ആഴàµà´šàµà´šà´•à´³àµâ€" + +#: utils/timesince.py:24 +msgid "day" +msgid_plural "days" +msgstr[0] "ദിവസം" +msgstr[1] "ദിവസങàµà´™à´³àµâ€" + +#: utils/timesince.py:25 +msgid "hour" +msgid_plural "hours" +msgstr[0] "മണികàµà´•àµ‚à´°àµâ€" +msgstr[1] "മണികàµà´•àµ‚à´±àµà´•à´³àµâ€" + +#: utils/timesince.py:26 +msgid "minute" +msgid_plural "minutes" +msgstr[0] "മിനàµà´Ÿàµà´Ÿàµ" +msgstr[1] "മിനàµà´Ÿàµà´Ÿàµà´•à´³àµâ€" + +#: utils/timesince.py:45 +msgid "minutes" +msgstr "മിനàµà´Ÿàµà´Ÿàµà´•à´³àµâ€" + +#: utils/timesince.py:50 +#, python-format +msgid "%(number)d %(type)s" +msgstr "" + +#: utils/timesince.py:56 +#, python-format +msgid ", %(number)d %(type)s" +msgstr "" + +#: utils/translation/trans_real.py:519 +msgid "DATE_FORMAT" +msgstr "" + +#: utils/translation/trans_real.py:520 +msgid "DATETIME_FORMAT" +msgstr "" + +#: utils/translation/trans_real.py:521 +msgid "TIME_FORMAT" +msgstr "" + +#: utils/translation/trans_real.py:542 +msgid "YEAR_MONTH_FORMAT" +msgstr "" + +#: utils/translation/trans_real.py:543 +msgid "MONTH_DAY_FORMAT" +msgstr "" + +#: views/generic/create_update.py:115 +#, python-format +msgid "The %(verbose_name)s was created successfully." +msgstr "%(verbose_name)s നെ à´¸àµà´°àµà´·àµà´Ÿà´¿à´šàµà´šàµ." + +#: views/generic/create_update.py:158 +#, python-format +msgid "The %(verbose_name)s was updated successfully." +msgstr "%(verbose_name)s നെ മെചàµà´šà´ªàµà´ªàµ†à´Ÿàµà´¤àµà´¤à´¿." + +#: views/generic/create_update.py:201 +#, python-format +msgid "The %(verbose_name)s was deleted." +msgstr "%(verbose_name)s ഡിലീറàµà´±àµ ചെയàµà´¯à´ªàµà´ªàµ†à´Ÿàµà´Ÿàµ." diff --git a/django/conf/locale/ml/LC_MESSAGES/djangojs.mo b/django/conf/locale/ml/LC_MESSAGES/djangojs.mo Binary files differnew file mode 100644 index 0000000000..644d8bf949 --- /dev/null +++ b/django/conf/locale/ml/LC_MESSAGES/djangojs.mo diff --git a/django/conf/locale/ml/LC_MESSAGES/djangojs.po b/django/conf/locale/ml/LC_MESSAGES/djangojs.po new file mode 100644 index 0000000000..aedc919ba3 --- /dev/null +++ b/django/conf/locale/ml/LC_MESSAGES/djangojs.po @@ -0,0 +1,156 @@ +# Translation of Django Js files to malayalam. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Rajeesh Nair <rajeeshrnair@gmail.com>, 2010. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Django SVN\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2010-05-28 15:32+0530\n" +"PO-Revision-Date: 2010-05-28 15:45+0530\n" +"Last-Translator: Rajeesh Nair <rajeeshrnair@gmail.com>\n" +"Language-Team: Malayalam <ml@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-Language: Malayalam\n" +"X-Poedit-Country: INDIA\n" + +#: contrib/admin/media/js/SelectFilter2.js:37 +#, perl-format +msgid "Available %s" +msgstr "à´²à´àµà´¯à´®à´¾à´¯ %s" + +#: contrib/admin/media/js/SelectFilter2.js:45 +msgid "Choose all" +msgstr "à´Žà´²àµà´²à´¾à´‚ തെരഞàµà´žàµ†à´Ÿàµà´•àµà´•àµà´•" + +#: contrib/admin/media/js/SelectFilter2.js:50 +msgid "Add" +msgstr "à´ªàµà´¤à´¿à´¯à´¤àµ ചേരàµâ€à´•àµà´•àµ‚" + +#: contrib/admin/media/js/SelectFilter2.js:52 +msgid "Remove" +msgstr "നീകàµà´•à´‚ ചെയàµà´¯àµ‚" + +#: contrib/admin/media/js/SelectFilter2.js:57 +#, perl-format +msgid "Chosen %s" +msgstr "തെരഞàµà´žàµ†à´Ÿàµà´¤àµà´¤ %s" + +#: contrib/admin/media/js/SelectFilter2.js:58 +msgid "Select your choice(s) and click " +msgstr "ഉചിതമായതൠതെരഞàµà´žàµ†à´Ÿàµà´¤àµà´¤ ശേഷം à´•àµà´³à´¿à´•àµ ചെയàµà´¯àµ‚" + +#: contrib/admin/media/js/SelectFilter2.js:63 +msgid "Clear all" +msgstr "à´Žà´²àµà´²à´¾à´‚ à´•àµà´³à´¿à´¯à´°àµâ€ ചെയàµà´¯àµ‚" + +#: contrib/admin/media/js/actions.js:18 +#: contrib/admin/media/js/actions.min.js:1 +msgid "%(sel)s of %(cnt)s selected" +msgid_plural "%(sel)s of %(cnt)s selected" +msgstr[0] "%(cnt)sà´²àµâ€ %(sel)s തെരഞàµà´žàµ†à´Ÿàµà´¤àµà´¤àµ" +msgstr[1] "%(cnt)sà´²àµâ€ %(sel)s à´Žà´£àµà´£à´‚ തെരഞàµà´žàµ†à´Ÿàµà´¤àµà´¤àµ" + +#: contrib/admin/media/js/actions.js:109 +#: contrib/admin/media/js/actions.min.js:5 +msgid "" +"You have unsaved changes on individual editable fields. If you run an " +"action, your unsaved changes will be lost." +msgstr "വരàµà´¤àµà´¤à´¿à´¯ മാറàµà´±à´™àµà´™à´³àµâ€ സേവൠചെയàµà´¤à´¿à´Ÿàµà´Ÿà´¿à´²àµà´². ഒരൠആകàµà´·à´¨àµâ€ à´ªàµà´°à´¯àµ‹à´—à´¿à´šàµà´šà´¾à´²àµâ€ സേവൠചെയàµà´¯à´¾à´¤àµà´¤ " +"മാറàµà´±à´™àµà´™à´³àµ†à´²àµà´²à´¾à´‚ നഷàµà´Ÿà´ªàµà´ªàµ†à´Ÿàµà´‚." + +#: contrib/admin/media/js/actions.js:121 +#: contrib/admin/media/js/actions.min.js:6 +msgid "" +"You have selected an action, but you haven't saved your changes to " +"individual fields yet. Please click OK to save. You'll need to re-run the " +"action." +msgstr "" +"നിങàµà´™à´³àµâ€ ഒരൠആകàµà´·à´¨àµâ€ തെരഞàµà´žàµ†à´Ÿàµà´¤àµà´¤à´¿à´Ÿàµà´Ÿàµà´£àµà´Ÿàµ. പകàµà´·àµ‡, കളങàµà´™à´³à´¿à´²àµ† മാറàµà´±à´™àµà´™à´³àµâ€ ഇനിയàµà´‚ സേവൠചെയàµà´¯à´¾à´¨àµà´£àµà´Ÿàµ. ആദàµà´¯à´‚ സേവàµ" +"ചെയàµà´¯à´¾à´¨à´¾à´¯à´¿ OK à´•àµà´²à´¿à´•àµ ചെയàµà´¯àµà´•. അതിനൠശേഷം ആകàµà´·à´¨àµâ€ à´’à´¨àµà´¨àµ കൂടി à´ªàµà´°à´¯àµ‹à´—à´¿à´•àµà´•àµ‡à´£àµà´Ÿà´¿ വരàµà´‚." + +#: contrib/admin/media/js/actions.js:123 +#: contrib/admin/media/js/actions.min.js:6 +msgid "" +"You have selected an action, and you haven't made any changes on individual " +"fields. You're probably looking for the Go button rather than the Save " +"button." +msgstr "" +"നിങàµà´™à´³àµâ€ ഒരൠആകàµà´·à´¨àµâ€ തെരഞàµà´žàµ†à´Ÿàµà´¤àµà´¤à´¿à´Ÿàµà´Ÿàµà´£àµà´Ÿàµ. കളങàµà´™à´³à´¿à´²àµâ€ സേവൠചെയàµà´¯à´¾à´¤àµà´¤ മാറàµà´±à´™àµà´™à´³àµâ€ ഇലàµà´². നിങàµà´™à´³àµâ€" +"സേവൠബടàµà´Ÿà´£àµâ€ തനàµà´¨àµ†à´¯à´¾à´£àµ‹ അതോ ഗോ ബടàµà´Ÿà´£à´¾à´£àµ‹ ഉദàµà´¦àµ‡à´¶à´¿à´šàµà´šà´¤àµ." + +#: contrib/admin/media/js/calendar.js:24 +#: contrib/admin/media/js/dateparse.js:32 +msgid "" +"January February March April May June July August September October November " +"December" +msgstr "ജനàµà´µà´°à´¿ ഫെബൃവരി മാരàµâ€à´šàµà´šàµ à´à´ªàµà´°à´¿à´²àµâ€ മെയൠജൂണàµâ€ ജൂലൈ ആഗസàµà´¤àµ സെപàµà´¤à´‚ബരàµâ€ à´’à´•àµà´Ÿàµ‹à´¬à´°àµâ€ നവംബരàµâ€ ഡിസംബരàµâ€" + +#: contrib/admin/media/js/calendar.js:25 +msgid "S M T W T F S" +msgstr "à´žà´¾ തി ചൊ ബൠവàµà´¯à´¾ വെ à´¶" + +#: contrib/admin/media/js/collapse.js:9 contrib/admin/media/js/collapse.js:21 +#: contrib/admin/media/js/collapse.min.js:1 +msgid "Show" +msgstr "കാണടàµà´Ÿàµ†" + +#: contrib/admin/media/js/collapse.js:16 +#: contrib/admin/media/js/collapse.min.js:1 +msgid "Hide" +msgstr "മറയടàµà´Ÿàµ†" + +#: contrib/admin/media/js/dateparse.js:33 +msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday" +msgstr "ഞായരàµâ€ തിങàµà´•à´³àµâ€ ചൊവàµà´µ à´¬àµà´§à´¨àµâ€ à´µàµà´¯à´¾à´´à´‚ വെളàµà´³à´¿ ശനി" + +#: contrib/admin/media/js/admin/DateTimeShortcuts.js:48 +#: contrib/admin/media/js/admin/DateTimeShortcuts.js:83 +msgid "Now" +msgstr "ഇപàµà´ªàµ‹à´³àµâ€" + +#: contrib/admin/media/js/admin/DateTimeShortcuts.js:52 +msgid "Clock" +msgstr "ഘടികാരം (à´•àµà´²àµ‹à´•àµà´•àµ)" + +#: contrib/admin/media/js/admin/DateTimeShortcuts.js:79 +msgid "Choose a time" +msgstr "സമയം തെരഞàµà´žàµ†à´Ÿàµà´•àµà´•àµ‚" + +#: contrib/admin/media/js/admin/DateTimeShortcuts.js:84 +msgid "Midnight" +msgstr "à´…à´°àµâ€à´§à´°à´¾à´¤àµà´°à´¿" + +#: contrib/admin/media/js/admin/DateTimeShortcuts.js:85 +msgid "6 a.m." +msgstr "6 a.m." + +#: contrib/admin/media/js/admin/DateTimeShortcuts.js:86 +msgid "Noon" +msgstr "ഉചàµà´š" + +#: contrib/admin/media/js/admin/DateTimeShortcuts.js:90 +#: contrib/admin/media/js/admin/DateTimeShortcuts.js:187 +msgid "Cancel" +msgstr "റദàµà´¦à´¾à´•àµà´•àµ‚" + +#: contrib/admin/media/js/admin/DateTimeShortcuts.js:132 +#: contrib/admin/media/js/admin/DateTimeShortcuts.js:181 +msgid "Today" +msgstr "ഇനàµà´¨àµ" + +#: contrib/admin/media/js/admin/DateTimeShortcuts.js:136 +msgid "Calendar" +msgstr "കലണàµà´Ÿà´°àµâ€" + +#: contrib/admin/media/js/admin/DateTimeShortcuts.js:179 +msgid "Yesterday" +msgstr "ഇനàµà´¨à´²àµ†" + +#: contrib/admin/media/js/admin/DateTimeShortcuts.js:183 +msgid "Tomorrow" +msgstr "നാളെ" diff --git a/django/conf/locale/ml/__init__.py b/django/conf/locale/ml/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/django/conf/locale/ml/__init__.py diff --git a/django/conf/locale/ml/formats.py b/django/conf/locale/ml/formats.py new file mode 100644 index 0000000000..141f705fb9 --- /dev/null +++ b/django/conf/locale/ml/formats.py @@ -0,0 +1,38 @@ +# -*- encoding: utf-8 -*- +# This file is distributed under the same license as the Django package. +# + +DATE_FORMAT = 'N j, Y' +TIME_FORMAT = 'P' +DATETIME_FORMAT = 'N j, Y, P' +YEAR_MONTH_FORMAT = 'F Y' +MONTH_DAY_FORMAT = 'F j' +SHORT_DATE_FORMAT = 'm/d/Y' +SHORT_DATETIME_FORMAT = 'm/d/Y P' +FIRST_DAY_OF_WEEK = 0 # Sunday +DATE_INPUT_FORMATS = ( + '%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06' + # '%b %d %Y', '%b %d, %Y', # 'Oct 25 2006', 'Oct 25, 2006' + # '%d %b %Y', '%d %b, %Y', # '25 Oct 2006', '25 Oct, 2006' + # '%B %d %Y', '%B %d, %Y', # 'October 25 2006', 'October 25, 2006' + # '%d %B %Y', '%d %B, %Y', # '25 October 2006', '25 October, 2006' +) +TIME_INPUT_FORMATS = ( + '%H:%M:%S', # '14:30:59' + '%H:%M', # '14:30' +) +DATETIME_INPUT_FORMATS = ( + '%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59' + '%Y-%m-%d %H:%M', # '2006-10-25 14:30' + '%Y-%m-%d', # '2006-10-25' + '%m/%d/%Y %H:%M:%S', # '10/25/2006 14:30:59' + '%m/%d/%Y %H:%M', # '10/25/2006 14:30' + '%m/%d/%Y', # '10/25/2006' + '%m/%d/%y %H:%M:%S', # '10/25/06 14:30:59' + '%m/%d/%y %H:%M', # '10/25/06 14:30' + '%m/%d/%y', # '10/25/06' +) +DECIMAL_SEPARATOR = '.' +THOUSAND_SEPARATOR = ',' +NUMBER_GROUPING = 3 + diff --git a/django/conf/locale/nn/LC_MESSAGES/django.mo b/django/conf/locale/nn/LC_MESSAGES/django.mo Binary files differindex a3898d06cb..0534d7f0cf 100644 --- a/django/conf/locale/nn/LC_MESSAGES/django.mo +++ b/django/conf/locale/nn/LC_MESSAGES/django.mo diff --git a/django/conf/locale/nn/LC_MESSAGES/django.po b/django/conf/locale/nn/LC_MESSAGES/django.po index 07ee018fad..c691be791e 100644 --- a/django/conf/locale/nn/LC_MESSAGES/django.po +++ b/django/conf/locale/nn/LC_MESSAGES/django.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-05-16 22:39+0200\n" +"POT-Creation-Date: 2010-08-07 21:14+0200\n" "PO-Revision-Date: 2010-03-04 12:13+0200\n" "Last-Translator: HÃ¥vard Grimelid\n" "Language-Team: Norsk nynorsk <nn@li.org>\n" @@ -67,7 +67,7 @@ msgid "Spanish" msgstr "Spansk" #: conf/global_settings.py:57 -msgid "Argentinean Spanish" +msgid "Argentinian Spanish" msgstr "Argentinsk spansk" #: conf/global_settings.py:58 @@ -163,98 +163,102 @@ msgid "Macedonian" msgstr "Makedonsk" #: conf/global_settings.py:81 +msgid "Malayalam" +msgstr "Malayalam" + +#: conf/global_settings.py:82 msgid "Mongolian" msgstr "Mongolsk" -#: conf/global_settings.py:82 +#: conf/global_settings.py:83 msgid "Dutch" msgstr "Nederlandsk" -#: conf/global_settings.py:83 +#: conf/global_settings.py:84 msgid "Norwegian" msgstr "Norsk" -#: conf/global_settings.py:84 +#: conf/global_settings.py:85 msgid "Norwegian Bokmal" msgstr "Norsk (bokmÃ¥l)" -#: conf/global_settings.py:85 +#: conf/global_settings.py:86 msgid "Norwegian Nynorsk" msgstr "Norsk (nynorsk)" -#: conf/global_settings.py:86 +#: conf/global_settings.py:87 msgid "Polish" msgstr "Polsk" -#: conf/global_settings.py:87 +#: conf/global_settings.py:88 msgid "Portuguese" msgstr "Portugisisk" -#: conf/global_settings.py:88 +#: conf/global_settings.py:89 msgid "Brazilian Portuguese" msgstr "Brasiliansk portugisisk" -#: conf/global_settings.py:89 +#: conf/global_settings.py:90 msgid "Romanian" msgstr "Rumensk" -#: conf/global_settings.py:90 +#: conf/global_settings.py:91 msgid "Russian" msgstr "Russisk" -#: conf/global_settings.py:91 +#: conf/global_settings.py:92 msgid "Slovak" msgstr "Slovakisk" -#: conf/global_settings.py:92 +#: conf/global_settings.py:93 msgid "Slovenian" msgstr "Slovensk" -#: conf/global_settings.py:93 +#: conf/global_settings.py:94 msgid "Albanian" msgstr "Albansk" -#: conf/global_settings.py:94 +#: conf/global_settings.py:95 msgid "Serbian" msgstr "Serbisk" -#: conf/global_settings.py:95 +#: conf/global_settings.py:96 msgid "Serbian Latin" msgstr "Serbisk latin" -#: conf/global_settings.py:96 +#: conf/global_settings.py:97 msgid "Swedish" msgstr "Svensk" -#: conf/global_settings.py:97 +#: conf/global_settings.py:98 msgid "Tamil" msgstr "Tamil" -#: conf/global_settings.py:98 +#: conf/global_settings.py:99 msgid "Telugu" msgstr "Telugu" -#: conf/global_settings.py:99 +#: conf/global_settings.py:100 msgid "Thai" msgstr "Thai" -#: conf/global_settings.py:100 +#: conf/global_settings.py:101 msgid "Turkish" msgstr "Tyrkisk" -#: conf/global_settings.py:101 +#: conf/global_settings.py:102 msgid "Ukrainian" msgstr "Ukrainsk" -#: conf/global_settings.py:102 +#: conf/global_settings.py:103 msgid "Vietnamese" msgstr "Vietnamesisk" -#: conf/global_settings.py:103 +#: conf/global_settings.py:104 msgid "Simplified Chinese" msgstr "Simplifisert kinesisk" -#: conf/global_settings.py:104 +#: conf/global_settings.py:105 msgid "Traditional Chinese" msgstr "Tradisjonell kinesisk" @@ -306,15 +310,15 @@ msgstr "Denne mÃ¥naden" msgid "This year" msgstr "I Ã¥r" -#: contrib/admin/filterspecs.py:147 forms/widgets.py:469 +#: contrib/admin/filterspecs.py:147 forms/widgets.py:478 msgid "Yes" msgstr "Ja" -#: contrib/admin/filterspecs.py:147 forms/widgets.py:469 +#: contrib/admin/filterspecs.py:147 forms/widgets.py:478 msgid "No" msgstr "Nei" -#: contrib/admin/filterspecs.py:154 forms/widgets.py:469 +#: contrib/admin/filterspecs.py:154 forms/widgets.py:478 msgid "Unknown" msgstr "Ukjend" @@ -451,8 +455,8 @@ msgstr[1] "%(count)s %(name)s vart endra." #, python-format msgid "%(total_count)s selected" msgid_plural "All %(total_count)s selected" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%(total_count)s valde" +msgstr[1] "Alle %(total_count)s valde" #: contrib/admin/options.py:1071 #, python-format @@ -684,7 +688,7 @@ msgid "Filter" msgstr "Filtrering" #: contrib/admin/templates/admin/delete_confirmation.html:10 -#: contrib/admin/templates/admin/submit_line.html:4 forms/formsets.py:302 +#: contrib/admin/templates/admin/submit_line.html:4 forms/formsets.py:300 msgid "Delete" msgstr "Slett" @@ -843,7 +847,7 @@ msgstr "Lagre og opprett ny" msgid "Save and continue editing" msgstr "Lagre og hald fram Ã¥ redigere" -#: contrib/admin/templates/admin/auth/user/add_form.html:5 +#: contrib/admin/templates/admin/auth/user/add_form.html:6 msgid "" "First, enter a username and password. Then, you'll be able to edit more user " "options." @@ -851,6 +855,10 @@ msgstr "" "Skriv først inn brukernamn og passord. Deretter vil du fÃ¥ høve til Ã¥ endre " "fleire brukarinnstillingar." +#: contrib/admin/templates/admin/auth/user/add_form.html:8 +msgid "Enter a username and password." +msgstr "Skriv inn nytt brukarnamn og passord." + #: contrib/admin/templates/admin/auth/user/change_password.html:28 #, python-format msgid "Enter a new password for the user <strong>%(username)s</strong>." @@ -1407,8 +1415,8 @@ msgstr "melding" msgid "Logged out" msgstr "Logga ut" -#: contrib/auth/management/commands/createsuperuser.py:23 -#: core/validators.py:120 forms/fields.py:428 +#: contrib/auth/management/commands/createsuperuser.py:24 +#: core/validators.py:120 forms/fields.py:427 msgid "Enter a valid e-mail address." msgstr "Oppgje ei gyldig e-postadresse." @@ -1476,7 +1484,7 @@ msgid "Email address" msgstr "E-postadresse" #: contrib/comments/forms.py:95 contrib/flatpages/admin.py:8 -#: contrib/flatpages/models.py:7 db/models/fields/__init__.py:1101 +#: contrib/flatpages/models.py:7 db/models/fields/__init__.py:1112 msgid "URL" msgstr "Nettadresse" @@ -1526,7 +1534,7 @@ msgstr "kommentar" msgid "date/time submitted" msgstr "dato/tid for innsending" -#: contrib/comments/models.py:60 db/models/fields/__init__.py:896 +#: contrib/comments/models.py:60 db/models/fields/__init__.py:907 msgid "IP address" msgstr "IP-adresse" @@ -4453,26 +4461,26 @@ msgstr "nettstader" msgid "Enter a valid value." msgstr "Oppgje ein gyldig verdi." -#: core/validators.py:87 forms/fields.py:529 +#: core/validators.py:87 forms/fields.py:528 msgid "Enter a valid URL." msgstr "Oppgje ei gyldig nettadresse." -#: core/validators.py:89 forms/fields.py:530 +#: core/validators.py:89 forms/fields.py:529 msgid "This URL appears to be a broken link." msgstr "Nettadressa fører til ei side som ikkje eksisterar." -#: core/validators.py:123 forms/fields.py:873 +#: core/validators.py:123 forms/fields.py:877 msgid "" "Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens." msgstr "" "Oppgje ein gyldig 'slug' som bestÃ¥r av bokstavar, nummer, understrekar eller " "bindestrekar." -#: core/validators.py:126 forms/fields.py:866 +#: core/validators.py:126 forms/fields.py:870 msgid "Enter a valid IPv4 address." msgstr "Oppgje ei gyldig IPv4-adresse." -#: core/validators.py:129 db/models/fields/__init__.py:572 +#: core/validators.py:129 db/models/fields/__init__.py:575 msgid "Enter only digits separated by commas." msgstr "Oppgje berre tall skild med komma." @@ -4481,12 +4489,12 @@ msgstr "Oppgje berre tall skild med komma." msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." msgstr "Verdien mÃ¥ minimum ha %(limit_value)s teikn (den er %(show_value)s)." -#: core/validators.py:153 forms/fields.py:205 forms/fields.py:257 +#: core/validators.py:153 forms/fields.py:204 forms/fields.py:256 #, python-format msgid "Ensure this value is less than or equal to %(limit_value)s." msgstr "Verdien mÃ¥ vere mindre enn eller lik %(limit_value)s." -#: core/validators.py:158 forms/fields.py:206 forms/fields.py:258 +#: core/validators.py:158 forms/fields.py:205 forms/fields.py:257 #, python-format msgid "Ensure this value is greater than or equal to %(limit_value)s." msgstr "Verdien mÃ¥ vere større enn eller lik %(limit_value)s." @@ -4534,132 +4542,132 @@ msgstr "Feltet kan ikkje vere tomt." msgid "Field of type: %(field_type)s" msgstr "Felt av typen: %(field_type)s" -#: db/models/fields/__init__.py:451 db/models/fields/__init__.py:852 -#: db/models/fields/__init__.py:961 db/models/fields/__init__.py:972 -#: db/models/fields/__init__.py:999 +#: db/models/fields/__init__.py:451 db/models/fields/__init__.py:863 +#: db/models/fields/__init__.py:972 db/models/fields/__init__.py:983 +#: db/models/fields/__init__.py:1010 msgid "Integer" msgstr "Heiltal" -#: db/models/fields/__init__.py:455 db/models/fields/__init__.py:850 +#: db/models/fields/__init__.py:455 db/models/fields/__init__.py:861 msgid "This value must be an integer." msgstr "Verdien mÃ¥ vere eit heltall." -#: db/models/fields/__init__.py:490 +#: db/models/fields/__init__.py:493 msgid "This value must be either True or False." msgstr "Verdien mÃ¥ vere True eller False." -#: db/models/fields/__init__.py:492 +#: db/models/fields/__init__.py:495 msgid "Boolean (Either True or False)" msgstr "Boolsk (True eller False)" -#: db/models/fields/__init__.py:539 db/models/fields/__init__.py:982 +#: db/models/fields/__init__.py:542 db/models/fields/__init__.py:993 #, python-format msgid "String (up to %(max_length)s)" msgstr "Tekst (opp til %(max_length)s teikn)" -#: db/models/fields/__init__.py:567 +#: db/models/fields/__init__.py:570 msgid "Comma-separated integers" msgstr "Heiltal skild med komma" -#: db/models/fields/__init__.py:581 +#: db/models/fields/__init__.py:584 msgid "Date (without time)" msgstr "Dato (utan tid)" -#: db/models/fields/__init__.py:585 +#: db/models/fields/__init__.py:588 msgid "Enter a valid date in YYYY-MM-DD format." msgstr "Oppgje ein gyldig dato pÃ¥ forma Ã…Ã…Ã…Ã…-MM-DD." -#: db/models/fields/__init__.py:586 +#: db/models/fields/__init__.py:589 #, python-format msgid "Invalid date: %s" msgstr "Ugyldig dato: %s" -#: db/models/fields/__init__.py:667 +#: db/models/fields/__init__.py:670 msgid "Enter a valid date/time in YYYY-MM-DD HH:MM[:ss[.uuuuuu]] format." msgstr "Oppgje dato og tid pÃ¥ forma Ã…Ã…Ã…Ã…-MM-DD TT:MM[:ss[.uuuuuu]]." -#: db/models/fields/__init__.py:669 +#: db/models/fields/__init__.py:672 msgid "Date (with time)" msgstr "Dato (med tid)" -#: db/models/fields/__init__.py:735 +#: db/models/fields/__init__.py:738 msgid "This value must be a decimal number." msgstr "Verdien mÃ¥ vere eit desimaltall." -#: db/models/fields/__init__.py:737 +#: db/models/fields/__init__.py:740 msgid "Decimal number" msgstr "Desimaltall" -#: db/models/fields/__init__.py:792 +#: db/models/fields/__init__.py:795 msgid "E-mail address" msgstr "E-postadresse" -#: db/models/fields/__init__.py:799 db/models/fields/files.py:220 +#: db/models/fields/__init__.py:810 db/models/fields/files.py:220 #: db/models/fields/files.py:331 msgid "File path" msgstr "Filsti" -#: db/models/fields/__init__.py:822 +#: db/models/fields/__init__.py:833 msgid "This value must be a float." msgstr "Verdien mÃ¥ vere eit flyttall." -#: db/models/fields/__init__.py:824 +#: db/models/fields/__init__.py:835 msgid "Floating point number" msgstr "Flyttall" -#: db/models/fields/__init__.py:883 +#: db/models/fields/__init__.py:894 msgid "Big (8 byte) integer" msgstr "" -#: db/models/fields/__init__.py:912 +#: db/models/fields/__init__.py:923 msgid "This value must be either None, True or False." msgstr "Verdien mÃ¥ vere None, True eller False." -#: db/models/fields/__init__.py:914 +#: db/models/fields/__init__.py:925 msgid "Boolean (Either True, False or None)" msgstr "Boolsk (True, False eller None)" -#: db/models/fields/__init__.py:1005 +#: db/models/fields/__init__.py:1016 msgid "Text" msgstr "Tekst" -#: db/models/fields/__init__.py:1021 +#: db/models/fields/__init__.py:1032 msgid "Time" msgstr "Tid" -#: db/models/fields/__init__.py:1025 +#: db/models/fields/__init__.py:1036 msgid "Enter a valid time in HH:MM[:ss[.uuuuuu]] format." msgstr "Oppgje tida pÃ¥ forma TT:MM[:ss[.uuuuuu]]." -#: db/models/fields/__init__.py:1109 +#: db/models/fields/__init__.py:1128 msgid "XML text" msgstr "XML-tekst" -#: db/models/fields/related.py:799 +#: db/models/fields/related.py:801 #, python-format msgid "Model %(model)s with pk %(pk)r does not exist." msgstr "Modellen %(model)s med primærnøkkelen %(pk)r eksisterer ikkje." -#: db/models/fields/related.py:801 +#: db/models/fields/related.py:803 msgid "Foreign Key (type determined by related field)" msgstr "Primærnøkkel (type bestemt av relatert felt)" -#: db/models/fields/related.py:918 +#: db/models/fields/related.py:921 msgid "One-to-one relationship" msgstr "Ein-til-ein-forhold" -#: db/models/fields/related.py:980 +#: db/models/fields/related.py:983 msgid "Many-to-many relationship" msgstr "Mange-til-mange-forhold" -#: db/models/fields/related.py:1000 +#: db/models/fields/related.py:1003 msgid "" "Hold down \"Control\", or \"Command\" on a Mac, to select more than one." msgstr "" "Hald nede \"Control\", eller \"Command\" pÃ¥ ein Mac, for Ã¥ velge meir enn " "éin." -#: db/models/fields/related.py:1061 +#: db/models/fields/related.py:1064 #, python-format msgid "Please enter valid %(self)s IDs. The value %(value)r is invalid." msgid_plural "" @@ -4671,60 +4679,60 @@ msgstr[1] "Oppgje gyldige %(self)s-ID-ar. Verdiane %(value)r er ugyldige." msgid "This field is required." msgstr "Feltet er pÃ¥kravd." -#: forms/fields.py:204 +#: forms/fields.py:203 msgid "Enter a whole number." msgstr "Oppgje eit heiltall." -#: forms/fields.py:235 forms/fields.py:256 +#: forms/fields.py:234 forms/fields.py:255 msgid "Enter a number." msgstr "Oppgje eit tall." -#: forms/fields.py:259 +#: forms/fields.py:258 #, python-format msgid "Ensure that there are no more than %s digits in total." msgstr "Verdien kan ikkje ha meir enn %s siffer totalt." -#: forms/fields.py:260 +#: forms/fields.py:259 #, python-format msgid "Ensure that there are no more than %s decimal places." msgstr "Verdien kan ikkie ha meir enn %s desimalar." -#: forms/fields.py:261 +#: forms/fields.py:260 #, python-format msgid "Ensure that there are no more than %s digits before the decimal point." msgstr "Verdien kan ikkje ha meir enn %s siffer framfor komma." -#: forms/fields.py:323 forms/fields.py:838 +#: forms/fields.py:322 forms/fields.py:837 msgid "Enter a valid date." msgstr "Oppgje ein gyldig dato." -#: forms/fields.py:351 forms/fields.py:839 +#: forms/fields.py:350 forms/fields.py:838 msgid "Enter a valid time." msgstr "Oppgje eit gyldig tidspunkt." -#: forms/fields.py:377 +#: forms/fields.py:376 msgid "Enter a valid date/time." msgstr "Oppgje gyldig dato og tidspunkt." -#: forms/fields.py:435 +#: forms/fields.py:434 msgid "No file was submitted. Check the encoding type on the form." msgstr "Inga fil vart sendt. Sjekk \"encoding\"-typen pÃ¥ skjemaet." -#: forms/fields.py:436 +#: forms/fields.py:435 msgid "No file was submitted." msgstr "Inga fil vart sendt." -#: forms/fields.py:437 +#: forms/fields.py:436 msgid "The submitted file is empty." msgstr "Fila er tom." -#: forms/fields.py:438 +#: forms/fields.py:437 #, python-format msgid "" "Ensure this filename has at most %(max)d characters (it has %(length)d)." msgstr "Filnamnet kan maksimalt ha %(max)d teikn (det har %(length)d)." -#: forms/fields.py:473 +#: forms/fields.py:472 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." @@ -4732,17 +4740,17 @@ msgstr "" "Last opp eit gyldig bilete. Fila du lasta opp var ødelagt eller ikkje eit " "bilete." -#: forms/fields.py:596 forms/fields.py:671 +#: forms/fields.py:595 forms/fields.py:670 #, python-format msgid "Select a valid choice. %(value)s is not one of the available choices." msgstr "" "Velg eit gyldig valg. %(value)s er ikkje eit av dei tilgjengelege valga." -#: forms/fields.py:672 forms/fields.py:734 forms/models.py:1002 +#: forms/fields.py:671 forms/fields.py:733 forms/models.py:1002 msgid "Enter a list of values." msgstr "Oppgje ei liste med verdiar." -#: forms/formsets.py:298 forms/formsets.py:300 +#: forms/formsets.py:296 forms/formsets.py:298 msgid "Order" msgstr "Rekkefølge" @@ -4788,28 +4796,28 @@ msgstr "Velg eit gyldig valg. %s er ikkje eit av dei tilgjengelege valga." msgid "\"%s\" is not a valid value for a primary key." msgstr "\"%s\" er ikkje ein gyldig verdi for ein primærnøkkel." -#: template/defaultfilters.py:776 +#: template/defaultfilters.py:780 msgid "yes,no,maybe" msgstr "ja,nei,kanskje" -#: template/defaultfilters.py:807 +#: template/defaultfilters.py:811 #, python-format msgid "%(size)d byte" msgid_plural "%(size)d bytes" msgstr[0] "%(size)d byte" msgstr[1] "%(size)d bytes" -#: template/defaultfilters.py:809 +#: template/defaultfilters.py:813 #, python-format msgid "%.1f KB" msgstr "%.1f KB" -#: template/defaultfilters.py:811 +#: template/defaultfilters.py:815 #, python-format msgid "%.1f MB" msgstr "%.1f MB" -#: template/defaultfilters.py:812 +#: template/defaultfilters.py:816 #, python-format msgid "%.1f GB" msgstr "%.1f GB" @@ -4880,7 +4888,7 @@ msgstr "ons" #: utils/dates.py:10 msgid "Thu" -msgstr "tors" +msgstr "tor" #: utils/dates.py:10 msgid "Fri" @@ -4888,7 +4896,7 @@ msgstr "fre" #: utils/dates.py:11 msgid "Sat" -msgstr "laur" +msgstr "lau" #: utils/dates.py:11 msgid "Sun" @@ -5018,7 +5026,7 @@ msgstr "nov." msgid "Dec." msgstr "des." -#: utils/text.py:130 +#: utils/text.py:136 msgid "or" msgstr "eller" diff --git a/django/conf/locale/pl/formats.py b/django/conf/locale/pl/formats.py index a4a2961185..4520f7ce4e 100644 --- a/django/conf/locale/pl/formats.py +++ b/django/conf/locale/pl/formats.py @@ -28,5 +28,5 @@ DATETIME_INPUT_FORMATS = ( '%Y-%m-%d', # '2006-10-25' ) DECIMAL_SEPARATOR = ',' -THOUSAND_SEPARATOR = '.' -NUMBER_GROUPING = 3
\ No newline at end of file +THOUSAND_SEPARATOR = ' ' +NUMBER_GROUPING = 3 diff --git a/django/conf/locale/sr/LC_MESSAGES/django.mo b/django/conf/locale/sr/LC_MESSAGES/django.mo Binary files differindex 3b4121580c..28995bfad7 100644 --- a/django/conf/locale/sr/LC_MESSAGES/django.mo +++ b/django/conf/locale/sr/LC_MESSAGES/django.mo diff --git a/django/conf/locale/sr/LC_MESSAGES/django.po b/django/conf/locale/sr/LC_MESSAGES/django.po index 51e9ab54dd..23c68cbfe1 100644 --- a/django/conf/locale/sr/LC_MESSAGES/django.po +++ b/django/conf/locale/sr/LC_MESSAGES/django.po @@ -4,17 +4,18 @@ msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-05-07 20:44+0200\n" -"PO-Revision-Date: 2010-03-23 23:39+0100\n" +"POT-Creation-Date: 2010-08-06 19:53+0200\n" +"PO-Revision-Date: 2010-08-06 19:47+0100\n" "Last-Translator: Janos Guljas <janos@janos.in.rs>\n" "Language-Team: Branko Vukelic <bg.branko@gmail.com> & Janos Guljas " "<janos@janos.in.rs> & Nesh <djnesh@gmail.com> & Petar <petar.maric@gmail." "com>\n" +"Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%" -"10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" #: conf/global_settings.py:44 msgid "Arabic" @@ -69,7 +70,7 @@ msgid "Spanish" msgstr "шпанÑки" #: conf/global_settings.py:57 -msgid "Argentinean Spanish" +msgid "Argentinian Spanish" msgstr "аргентинÑки шпанÑки" #: conf/global_settings.py:58 @@ -121,138 +122,146 @@ msgid "Hungarian" msgstr "мађарÑки" #: conf/global_settings.py:70 +msgid "Indonesian" +msgstr "индонежанÑки" + +#: conf/global_settings.py:71 msgid "Icelandic" msgstr "иÑландÑки" -#: conf/global_settings.py:71 +#: conf/global_settings.py:72 msgid "Italian" msgstr "италијанÑки" -#: conf/global_settings.py:72 +#: conf/global_settings.py:73 msgid "Japanese" msgstr "јапанÑки" -#: conf/global_settings.py:73 +#: conf/global_settings.py:74 msgid "Georgian" msgstr "грузијÑки" -#: conf/global_settings.py:74 +#: conf/global_settings.py:75 msgid "Khmer" msgstr "камбодијÑки" -#: conf/global_settings.py:75 +#: conf/global_settings.py:76 msgid "Kannada" msgstr "канада" -#: conf/global_settings.py:76 +#: conf/global_settings.py:77 msgid "Korean" msgstr "корејÑки" -#: conf/global_settings.py:77 +#: conf/global_settings.py:78 msgid "Lithuanian" msgstr "литванÑки" -#: conf/global_settings.py:78 +#: conf/global_settings.py:79 msgid "Latvian" msgstr "латвијÑки" -#: conf/global_settings.py:79 +#: conf/global_settings.py:80 msgid "Macedonian" msgstr "македонÑки" -#: conf/global_settings.py:80 +#: conf/global_settings.py:81 +msgid "Malayalam" +msgstr "малајаламÑки" + +#: conf/global_settings.py:82 msgid "Mongolian" msgstr "монголÑки" -#: conf/global_settings.py:81 +#: conf/global_settings.py:83 msgid "Dutch" msgstr "холандÑки" -#: conf/global_settings.py:82 +#: conf/global_settings.py:84 msgid "Norwegian" msgstr "норвешки" -#: conf/global_settings.py:83 +#: conf/global_settings.py:85 msgid "Norwegian Bokmal" msgstr "норвешки кнјжевни" -#: conf/global_settings.py:84 +#: conf/global_settings.py:86 msgid "Norwegian Nynorsk" msgstr "норвешки нови" -#: conf/global_settings.py:85 +#: conf/global_settings.py:87 msgid "Polish" msgstr "пољÑки" -#: conf/global_settings.py:86 +#: conf/global_settings.py:88 msgid "Portuguese" msgstr "португалÑки" -#: conf/global_settings.py:87 +#: conf/global_settings.py:89 msgid "Brazilian Portuguese" msgstr "бразилÑки португалÑки" -#: conf/global_settings.py:88 +#: conf/global_settings.py:90 msgid "Romanian" msgstr "румунÑки" -#: conf/global_settings.py:89 +#: conf/global_settings.py:91 msgid "Russian" msgstr "руÑки" -#: conf/global_settings.py:90 +#: conf/global_settings.py:92 msgid "Slovak" msgstr "Ñловачки" -#: conf/global_settings.py:91 +#: conf/global_settings.py:93 msgid "Slovenian" msgstr "Ñловеначки" -#: conf/global_settings.py:92 +#: conf/global_settings.py:94 msgid "Albanian" msgstr "албанÑки" -#: conf/global_settings.py:93 +#: conf/global_settings.py:95 msgid "Serbian" msgstr "ÑрпÑки" -#: conf/global_settings.py:94 +#: conf/global_settings.py:96 msgid "Serbian Latin" msgstr "ÑрпÑки (латиница)" -#: conf/global_settings.py:95 +#: conf/global_settings.py:97 msgid "Swedish" msgstr "шведÑки" -#: conf/global_settings.py:96 +#: conf/global_settings.py:98 msgid "Tamil" msgstr "тамилÑки" -#: conf/global_settings.py:97 +#: conf/global_settings.py:99 msgid "Telugu" msgstr "телугу" -#: conf/global_settings.py:98 +#: conf/global_settings.py:100 msgid "Thai" msgstr "тајландÑки" -#: conf/global_settings.py:99 +#: conf/global_settings.py:101 msgid "Turkish" msgstr "турÑки" -#: conf/global_settings.py:100 +#: conf/global_settings.py:102 msgid "Ukrainian" msgstr "украјинÑки" -#: conf/global_settings.py:101 +#: conf/global_settings.py:103 msgid "Vietnamese" msgstr "вијетнамÑки" -#: conf/global_settings.py:102 +#: conf/global_settings.py:104 msgid "Simplified Chinese" msgstr "новокинеÑки" -#: conf/global_settings.py:103 +#: conf/global_settings.py:105 msgid "Traditional Chinese" msgstr "ÑтарокинеÑки" @@ -304,15 +313,15 @@ msgstr "Овај меÑец" msgid "This year" msgstr "Ова година" -#: contrib/admin/filterspecs.py:147 forms/widgets.py:469 +#: contrib/admin/filterspecs.py:147 forms/widgets.py:478 msgid "Yes" msgstr "Да" -#: contrib/admin/filterspecs.py:147 forms/widgets.py:469 +#: contrib/admin/filterspecs.py:147 forms/widgets.py:478 msgid "No" msgstr "Ðе" -#: contrib/admin/filterspecs.py:154 forms/widgets.py:469 +#: contrib/admin/filterspecs.py:154 forms/widgets.py:478 msgid "Unknown" msgstr "Ðепознато" @@ -358,7 +367,7 @@ msgid "Changed %s." msgstr "Измењена поља %s" #: contrib/admin/options.py:559 contrib/admin/options.py:569 -#: contrib/comments/templates/comments/preview.html:16 db/models/base.py:844 +#: contrib/comments/templates/comments/preview.html:16 db/models/base.py:845 #: forms/models.py:568 msgid "and" msgstr "и" @@ -457,9 +466,9 @@ msgstr[1] "%(total_count)s изабрано" msgstr[2] "%(total_count)s изабраних" #: contrib/admin/options.py:1071 -#, fuzzy, python-format +#, python-format msgid "0 of %(cnt)s selected" -msgstr "0 од %(cnt)d изабрано" +msgstr "0 од %(cnt)s изабрано" #: contrib/admin/options.py:1118 #, python-format @@ -687,7 +696,7 @@ msgid "Filter" msgstr "Филтер" #: contrib/admin/templates/admin/delete_confirmation.html:10 -#: contrib/admin/templates/admin/submit_line.html:4 forms/formsets.py:302 +#: contrib/admin/templates/admin/submit_line.html:4 forms/formsets.py:300 msgid "Delete" msgstr "Обриши" @@ -849,7 +858,7 @@ msgstr "Сачувај и додај Ñледећи" msgid "Save and continue editing" msgstr "Сачувај и наÑтави Ñа изменама" -#: contrib/admin/templates/admin/auth/user/add_form.html:5 +#: contrib/admin/templates/admin/auth/user/add_form.html:6 msgid "" "First, enter a username and password. Then, you'll be able to edit more user " "options." @@ -857,6 +866,10 @@ msgstr "" "Прво унеÑите кориÑничко име и лозинку. Потом ћете моћи да мењате још " "кориÑничких подешавања." +#: contrib/admin/templates/admin/auth/user/add_form.html:8 +msgid "Enter a username and password." +msgstr "УнеÑите кориÑничко име и лозинку" + #: contrib/admin/templates/admin/auth/user/change_password.html:28 #, python-format msgid "Enter a new password for the user <strong>%(username)s</strong>." @@ -1050,7 +1063,7 @@ msgstr "Имејл адреÑа:" msgid "Reset my password" msgstr "РеÑетуј моју лозинку" -#: contrib/admin/templatetags/admin_list.py:239 +#: contrib/admin/templatetags/admin_list.py:257 msgid "All dates" msgstr "Сви датуми" @@ -1424,8 +1437,8 @@ msgstr "порука" msgid "Logged out" msgstr "Одјављен" -#: contrib/auth/management/commands/createsuperuser.py:23 -#: core/validators.py:120 forms/fields.py:428 +#: contrib/auth/management/commands/createsuperuser.py:24 +#: core/validators.py:120 forms/fields.py:427 msgid "Enter a valid e-mail address." msgstr "УнеÑите важећу имејл адреÑу." @@ -1497,7 +1510,7 @@ msgid "Email address" msgstr "Имејл адреÑа" #: contrib/comments/forms.py:95 contrib/flatpages/admin.py:8 -#: contrib/flatpages/models.py:7 db/models/fields/__init__.py:1101 +#: contrib/flatpages/models.py:7 db/models/fields/__init__.py:1109 msgid "URL" msgstr "URL" @@ -1547,7 +1560,7 @@ msgstr "коментар" msgid "date/time submitted" msgstr "датум/време поÑтављања" -#: contrib/comments/models.py:60 db/models/fields/__init__.py:896 +#: contrib/comments/models.py:60 db/models/fields/__init__.py:904 msgid "IP address" msgstr "IP адреÑа" @@ -4471,22 +4484,22 @@ msgstr "Ñајтови" msgid "Enter a valid value." msgstr "УнеÑите иÑправну вредноÑÑ‚." -#: core/validators.py:87 forms/fields.py:529 +#: core/validators.py:87 forms/fields.py:528 msgid "Enter a valid URL." msgstr "УнеÑите иÑправан URL." -#: core/validators.py:89 forms/fields.py:530 +#: core/validators.py:89 forms/fields.py:529 msgid "This URL appears to be a broken link." msgstr "Овај URL изгледа не води никуда." -#: core/validators.py:123 forms/fields.py:873 +#: core/validators.py:123 forms/fields.py:877 msgid "" "Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens." msgstr "" "УнеÑите иÑрпаван „Ñлаг“, који Ñе ÑаÑтоји од Ñлова, бројки, доњих црта или " "циртица." -#: core/validators.py:126 forms/fields.py:866 +#: core/validators.py:126 forms/fields.py:870 msgid "Enter a valid IPv4 address." msgstr "УнеÑите иÑправну IPv4 адреÑу." @@ -4499,12 +4512,12 @@ msgstr "УнеÑите Ñамо бројке раздвојене запетам msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." msgstr "Ово поље мора да буде %(limit_value)s (тренутно има %(show_value)s)." -#: core/validators.py:153 forms/fields.py:205 forms/fields.py:257 +#: core/validators.py:153 forms/fields.py:204 forms/fields.py:256 #, python-format msgid "Ensure this value is less than or equal to %(limit_value)s." msgstr "Ова вредноÑÑ‚ мора да буде мања од %(limit_value)s. или тачно толико." -#: core/validators.py:158 forms/fields.py:206 forms/fields.py:258 +#: core/validators.py:158 forms/fields.py:205 forms/fields.py:257 #, python-format msgid "Ensure this value is greater than or equal to %(limit_value)s." msgstr "Ова вредноÑÑ‚ мора бити већа од %(limit_value)s или тачно толико." @@ -4512,27 +4525,27 @@ msgstr "Ова вредноÑÑ‚ мора бити већа од %(limit_value)s #: core/validators.py:164 #, python-format msgid "" -"Ensure this value has at least %(limit_value)d characters (it has %" -"(show_value)d)." +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." msgstr "" -"Ово поље мора да Ñадржи најмање %(limit_value)d Ñловних меÑта (тренутно има %" -"(show_value)d)." +"Ово поље мора да Ñадржи најмање %(limit_value)d Ñловних меÑта (тренутно има " +"%(show_value)d)." #: core/validators.py:170 #, python-format msgid "" -"Ensure this value has at most %(limit_value)d characters (it has %" -"(show_value)d)." +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." msgstr "" -"Ово поље мора да Ñадржи највише %(limit_value)d Ñловних меÑта (тренутно има %" -"(show_value)d)." +"Ово поље мора да Ñадржи највише %(limit_value)d Ñловних меÑта (тренутно има " +"%(show_value)d)." -#: db/models/base.py:822 +#: db/models/base.py:823 #, python-format msgid "%(field_name)s must be unique for %(date_field)s %(lookup)s." msgstr "%(field_name)s мора да буде јединÑтвен за %(date_field)s %(lookup)s." -#: db/models/base.py:837 db/models/base.py:845 +#: db/models/base.py:838 db/models/base.py:846 #, python-format msgid "%(model_name)s with this %(field_label)s already exists." msgstr "%(model_name)s Ñа овом вредношћу %(field_label)s већ поÑтоји." @@ -4555,13 +4568,13 @@ msgstr "Ово поље не може да оÑтане празно." msgid "Field of type: %(field_type)s" msgstr "Поње типа: %(field_type)s" -#: db/models/fields/__init__.py:451 db/models/fields/__init__.py:852 -#: db/models/fields/__init__.py:961 db/models/fields/__init__.py:972 -#: db/models/fields/__init__.py:999 +#: db/models/fields/__init__.py:451 db/models/fields/__init__.py:860 +#: db/models/fields/__init__.py:969 db/models/fields/__init__.py:980 +#: db/models/fields/__init__.py:1007 msgid "Integer" msgstr "Цео број" -#: db/models/fields/__init__.py:455 db/models/fields/__init__.py:850 +#: db/models/fields/__init__.py:455 db/models/fields/__init__.py:858 msgid "This value must be an integer." msgstr "Ова вредноÑÑ‚ мора бити целобројна." @@ -4573,7 +4586,7 @@ msgstr "Ова вредноÑÑ‚ мора бити True или False." msgid "Boolean (Either True or False)" msgstr "Булова вредноÑÑ‚ (True или False)" -#: db/models/fields/__init__.py:539 db/models/fields/__init__.py:982 +#: db/models/fields/__init__.py:539 db/models/fields/__init__.py:990 #, python-format msgid "String (up to %(max_length)s)" msgstr "Стринг (највише %(max_length)s знакова)" @@ -4615,44 +4628,44 @@ msgstr "Децимални број" msgid "E-mail address" msgstr "Имејл адреÑа" -#: db/models/fields/__init__.py:799 db/models/fields/files.py:220 +#: db/models/fields/__init__.py:807 db/models/fields/files.py:220 #: db/models/fields/files.py:331 msgid "File path" msgstr "Путања фајла" -#: db/models/fields/__init__.py:822 +#: db/models/fields/__init__.py:830 msgid "This value must be a float." msgstr "Ова вредноÑÑ‚ мора бити број Ñа клизећом запетом" -#: db/models/fields/__init__.py:824 +#: db/models/fields/__init__.py:832 msgid "Floating point number" msgstr "Број Ñа покреном запетом" -#: db/models/fields/__init__.py:883 +#: db/models/fields/__init__.py:891 msgid "Big (8 byte) integer" msgstr "Велики цео број" -#: db/models/fields/__init__.py:912 +#: db/models/fields/__init__.py:920 msgid "This value must be either None, True or False." msgstr "Ова вредноÑÑ‚ мора бити или None, или True, или False." -#: db/models/fields/__init__.py:914 +#: db/models/fields/__init__.py:922 msgid "Boolean (Either True, False or None)" msgstr "Булова вредноÑÑ‚ (True, False или None)" -#: db/models/fields/__init__.py:1005 +#: db/models/fields/__init__.py:1013 msgid "Text" msgstr "ТекÑÑ‚" -#: db/models/fields/__init__.py:1021 +#: db/models/fields/__init__.py:1029 msgid "Time" msgstr "Време" -#: db/models/fields/__init__.py:1025 +#: db/models/fields/__init__.py:1033 msgid "Enter a valid time in HH:MM[:ss[.uuuuuu]] format." msgstr "УнеÑите иÑправно време у формату ЧЧ:ММ[:ÑÑ[.уууууу]]." -#: db/models/fields/__init__.py:1109 +#: db/models/fields/__init__.py:1125 msgid "XML text" msgstr "XML текÑÑ‚" @@ -4665,22 +4678,22 @@ msgstr "Објекат клаÑе %(model)s Ñа примарним кључем msgid "Foreign Key (type determined by related field)" msgstr "Страни кључ (тип одређује референтно поље)" -#: db/models/fields/related.py:918 +#: db/models/fields/related.py:919 msgid "One-to-one relationship" msgstr "Релација један на један" -#: db/models/fields/related.py:980 +#: db/models/fields/related.py:981 msgid "Many-to-many relationship" msgstr "Релација више на више" -#: db/models/fields/related.py:1000 +#: db/models/fields/related.py:1001 msgid "" "Hold down \"Control\", or \"Command\" on a Mac, to select more than one." msgstr "" "Држите „Control“, или „Command“ на Mac-у да биÑте обележили више од једне " "Ñтавке." -#: db/models/fields/related.py:1061 +#: db/models/fields/related.py:1062 #, python-format msgid "Please enter valid %(self)s IDs. The value %(value)r is invalid." msgid_plural "" @@ -4693,62 +4706,62 @@ msgstr[2] "УнеÑите иÑправан %(self)s IDs. ВредноÑти %(va msgid "This field is required." msgstr "Ово поље Ñе мора попунити." -#: forms/fields.py:204 +#: forms/fields.py:203 msgid "Enter a whole number." msgstr "УнеÑите цео број." -#: forms/fields.py:235 forms/fields.py:256 +#: forms/fields.py:234 forms/fields.py:255 msgid "Enter a number." msgstr "УнеÑите број." -#: forms/fields.py:259 +#: forms/fields.py:258 #, python-format msgid "Ensure that there are no more than %s digits in total." msgstr "Ðе Ñме бити укупно више од %s цифара. Проверите." -#: forms/fields.py:260 +#: forms/fields.py:259 #, python-format msgid "Ensure that there are no more than %s decimal places." msgstr "Ðе Ñме бити укупно више од %s децималних меÑта. Проверите." -#: forms/fields.py:261 +#: forms/fields.py:260 #, python-format msgid "Ensure that there are no more than %s digits before the decimal point." msgstr "Ðе Ñме бити укупно више од %s цифара пре запете. Проверите." -#: forms/fields.py:323 forms/fields.py:838 +#: forms/fields.py:322 forms/fields.py:837 msgid "Enter a valid date." msgstr "УнеÑите иÑправан датум." -#: forms/fields.py:351 forms/fields.py:839 +#: forms/fields.py:350 forms/fields.py:838 msgid "Enter a valid time." msgstr "УнеÑите иÑправно време" -#: forms/fields.py:377 +#: forms/fields.py:376 msgid "Enter a valid date/time." msgstr "УнеÑите иÑправан датум/време." -#: forms/fields.py:435 +#: forms/fields.py:434 msgid "No file was submitted. Check the encoding type on the form." msgstr "Фајл није пребачен. Проверите тип енкодирања формулара." -#: forms/fields.py:436 +#: forms/fields.py:435 msgid "No file was submitted." msgstr "Фајл није пребачен." -#: forms/fields.py:437 +#: forms/fields.py:436 msgid "The submitted file is empty." msgstr "Пребачен фајл је празан." -#: forms/fields.py:438 +#: forms/fields.py:437 #, python-format msgid "" "Ensure this filename has at most %(max)d characters (it has %(length)d)." msgstr "" -"Ðазив фајла мора да Ñадржи бар %(max)d Ñловних меÑта (тренутно има %(length)" -"d)." +"Ðазив фајла мора да Ñадржи бар %(max)d Ñловних меÑта (тренутно има " +"%(length)d)." -#: forms/fields.py:473 +#: forms/fields.py:472 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." @@ -4756,17 +4769,17 @@ msgstr "" "Пребаците иÑправан фајл. Фајл који је пребачен или није Ñлика, или је " "оштећен." -#: forms/fields.py:596 forms/fields.py:671 +#: forms/fields.py:595 forms/fields.py:670 #, python-format msgid "Select a valid choice. %(value)s is not one of the available choices." msgstr "" "%(value)s није међу понуђеним вредноÑтима. Одаберите једну од понуђених." -#: forms/fields.py:672 forms/fields.py:734 forms/models.py:1002 +#: forms/fields.py:671 forms/fields.py:733 forms/models.py:1002 msgid "Enter a list of values." msgstr "УнеÑите лиÑту вредноÑти." -#: forms/formsets.py:298 forms/formsets.py:300 +#: forms/formsets.py:296 forms/formsets.py:298 msgid "Order" msgstr "РедоÑлед" @@ -5103,23 +5116,23 @@ msgstr "%(number)d %(type)s" msgid ", %(number)d %(type)s" msgstr ", %(number)d %(type)s" -#: utils/translation/trans_real.py:518 +#: utils/translation/trans_real.py:519 msgid "DATE_FORMAT" msgstr "j. F Y." -#: utils/translation/trans_real.py:519 +#: utils/translation/trans_real.py:520 msgid "DATETIME_FORMAT" msgstr "j. F Y. H:i Т" -#: utils/translation/trans_real.py:520 +#: utils/translation/trans_real.py:521 msgid "TIME_FORMAT" msgstr "G:i" -#: utils/translation/trans_real.py:541 +#: utils/translation/trans_real.py:542 msgid "YEAR_MONTH_FORMAT" msgstr "F Y." -#: utils/translation/trans_real.py:542 +#: utils/translation/trans_real.py:543 msgid "MONTH_DAY_FORMAT" msgstr "j. F" diff --git a/django/conf/locale/sr/LC_MESSAGES/djangojs.mo b/django/conf/locale/sr/LC_MESSAGES/djangojs.mo Binary files differindex 6de25a218c..b74e78cc4b 100644 --- a/django/conf/locale/sr/LC_MESSAGES/djangojs.mo +++ b/django/conf/locale/sr/LC_MESSAGES/djangojs.mo diff --git a/django/conf/locale/sr/LC_MESSAGES/djangojs.po b/django/conf/locale/sr/LC_MESSAGES/djangojs.po index 56a550b375..aecb4267dd 100644 --- a/django/conf/locale/sr/LC_MESSAGES/djangojs.po +++ b/django/conf/locale/sr/LC_MESSAGES/djangojs.po @@ -4,50 +4,24 @@ msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-05-07 20:46+0200\n" +"POT-Creation-Date: 2010-08-06 19:53+0200\n" "PO-Revision-Date: 2009-03-30 14:04+0200\n" "Last-Translator: Janos Guljas <janos@janos.in.rs>\n" "Language-Team: Branko Vukelic <bg.branko@gmail.com> & Janos Guljas " "<janos@janos.in.rs> & Nesh <djnesh@gmail.com> & Petar <petar.maric@gmail." "com>\n" +"Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%" -"10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" - -#: contrib/admin/media/js/SelectFilter2.js:37 -#, perl-format -msgid "Available %s" -msgstr "ДоÑтупни %s" - -#: contrib/admin/media/js/SelectFilter2.js:45 -msgid "Choose all" -msgstr "Додај Ñве" - -#: contrib/admin/media/js/SelectFilter2.js:50 -msgid "Add" -msgstr "Додај" - -#: contrib/admin/media/js/SelectFilter2.js:52 -msgid "Remove" -msgstr "Уклони" - -#: contrib/admin/media/js/SelectFilter2.js:57 -#, perl-format -msgid "Chosen %s" -msgstr "Одабрани %s" - -#: contrib/admin/media/js/SelectFilter2.js:58 -msgid "Select your choice(s) and click " -msgstr "Ðаправите избор и кликните " +"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" #: contrib/admin/media/js/SelectFilter2.js:63 msgid "Clear all" msgstr "Врати Ñве" #: contrib/admin/media/js/actions.js:18 -#: contrib/admin/media/js/actions.min.js:1 msgid "%(sel)s of %(cnt)s selected" msgid_plural "%(sel)s of %(cnt)s selected" msgstr[0] "%(sel)s од %(cnt)s изабран" @@ -55,7 +29,6 @@ msgstr[1] "%(sel)s од %(cnt)s изабрано" msgstr[2] "%(sel)s од %(cnt)s изабраних" #: contrib/admin/media/js/actions.js:109 -#: contrib/admin/media/js/actions.min.js:5 msgid "" "You have unsaved changes on individual editable fields. If you run an " "action, your unsaved changes will be lost." @@ -151,3 +124,21 @@ msgstr "Јуче" #: contrib/admin/media/js/admin/DateTimeShortcuts.js:184 msgid "Tomorrow" msgstr "Сутра" + +#~ msgid "Available %s" +#~ msgstr "ДоÑтупни %s" + +#~ msgid "Choose all" +#~ msgstr "Додај Ñве" + +#~ msgid "Add" +#~ msgstr "Додај" + +#~ msgid "Remove" +#~ msgstr "Уклони" + +#~ msgid "Chosen %s" +#~ msgstr "Одабрани %s" + +#~ msgid "Select your choice(s) and click " +#~ msgstr "Ðаправите избор и кликните " diff --git a/django/conf/locale/sr/formats.py b/django/conf/locale/sr/formats.py index 63a20f4574..cb0478ed0f 100644 --- a/django/conf/locale/sr/formats.py +++ b/django/conf/locale/sr/formats.py @@ -11,9 +11,9 @@ SHORT_DATE_FORMAT = 'j.m.Y.' SHORT_DATETIME_FORMAT = 'j.m.Y. H:i' FIRST_DAY_OF_WEEK = 1 DATE_INPUT_FORMATS = ( - '%Y-%m-%d', # '2006-10-25' '%d.%m.%Y.', '%d.%m.%y.', # '25.10.2006.', '25.10.06.' '%d. %m. %Y.', '%d. %m. %y.', # '25. 10. 2006.', '25. 10. 06.' + '%Y-%m-%d', # '2006-10-25' # '%d. %b %y.', '%d. %B %y.', # '25. Oct 06.', '25. October 06.' # '%d. %b \'%y.', '%d. %B \'%y.', # '25. Oct '06.', '25. October '06.' # '%d. %b %Y.', '%d. %B %Y.', # '25. Oct 2006.', '25. October 2006.' @@ -23,9 +23,6 @@ TIME_INPUT_FORMATS = ( '%H:%M', # '14:30' ) DATETIME_INPUT_FORMATS = ( - '%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59' - '%Y-%m-%d %H:%M', # '2006-10-25 14:30' - '%Y-%m-%d', # '2006-10-25' '%d.%m.%Y. %H:%M:%S', # '25.10.2006. 14:30:59' '%d.%m.%Y. %H:%M', # '25.10.2006. 14:30' '%d.%m.%Y.', # '25.10.2006.' @@ -38,7 +35,10 @@ DATETIME_INPUT_FORMATS = ( '%d. %m. %y. %H:%M:%S', # '25. 10. 06. 14:30:59' '%d. %m. %y. %H:%M', # '25. 10. 06. 14:30' '%d. %m. %y.', # '25. 10. 06.' + '%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59' + '%Y-%m-%d %H:%M', # '2006-10-25 14:30' + '%Y-%m-%d', # '2006-10-25' ) -DECIMAL_SEPARATOR = '.' -THOUSAND_SEPARATOR = ',' +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '.' NUMBER_GROUPING = 3 diff --git a/django/conf/locale/sr_Latn/LC_MESSAGES/django.mo b/django/conf/locale/sr_Latn/LC_MESSAGES/django.mo Binary files differindex ad6193b38b..ad04e754de 100644 --- a/django/conf/locale/sr_Latn/LC_MESSAGES/django.mo +++ b/django/conf/locale/sr_Latn/LC_MESSAGES/django.mo diff --git a/django/conf/locale/sr_Latn/LC_MESSAGES/django.po b/django/conf/locale/sr_Latn/LC_MESSAGES/django.po index 7947647f8c..7a289c3d23 100644 --- a/django/conf/locale/sr_Latn/LC_MESSAGES/django.po +++ b/django/conf/locale/sr_Latn/LC_MESSAGES/django.po @@ -4,17 +4,15 @@ msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-05-07 20:46+0200\n" -"PO-Revision-Date: 2010-03-23 23:39+0100\n" +"POT-Creation-Date: 2010-08-06 19:43+0200\n" +"PO-Revision-Date: 2010-08-06 19:47+0100\n" "Last-Translator: Janos Guljas <janos@janos.in.rs>\n" -"Language-Team: Branko Vukelic <bg.branko@gmail.com> & Janos Guljas " -"<janos@janos.in.rs> & Nesh <djnesh@gmail.com> & Petar <petar.maric@gmail." -"com>\n" +"Language-Team: Branko Vukelic <bg.branko@gmail.com> & Janos Guljas <janos@janos.in.rs> & Nesh <djnesh@gmail.com> & Petar <petar.maric@gmail.com>\n" +"Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%" -"10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" #: conf/global_settings.py:44 msgid "Arabic" @@ -69,7 +67,7 @@ msgid "Spanish" msgstr "Å¡panski" #: conf/global_settings.py:57 -msgid "Argentinean Spanish" +msgid "Argentinian Spanish" msgstr "argentinski Å¡panski" #: conf/global_settings.py:58 @@ -121,138 +119,146 @@ msgid "Hungarian" msgstr "maÄ‘arski" #: conf/global_settings.py:70 +msgid "Indonesian" +msgstr "indonežanski" + +#: conf/global_settings.py:71 msgid "Icelandic" msgstr "islandski" -#: conf/global_settings.py:71 +#: conf/global_settings.py:72 msgid "Italian" msgstr "italijanski" -#: conf/global_settings.py:72 +#: conf/global_settings.py:73 msgid "Japanese" msgstr "japanski" -#: conf/global_settings.py:73 +#: conf/global_settings.py:74 msgid "Georgian" msgstr "gruzijski" -#: conf/global_settings.py:74 +#: conf/global_settings.py:75 msgid "Khmer" msgstr "kambodijski" -#: conf/global_settings.py:75 +#: conf/global_settings.py:76 msgid "Kannada" msgstr "kanada" -#: conf/global_settings.py:76 +#: conf/global_settings.py:77 msgid "Korean" msgstr "korejski" -#: conf/global_settings.py:77 +#: conf/global_settings.py:78 msgid "Lithuanian" msgstr "litvanski" -#: conf/global_settings.py:78 +#: conf/global_settings.py:79 msgid "Latvian" msgstr "latvijski" -#: conf/global_settings.py:79 +#: conf/global_settings.py:80 msgid "Macedonian" msgstr "makedonski" -#: conf/global_settings.py:80 +#: conf/global_settings.py:81 +msgid "Malayalam" +msgstr "malajalamski" + +#: conf/global_settings.py:82 msgid "Mongolian" msgstr "mongolski" -#: conf/global_settings.py:81 +#: conf/global_settings.py:83 msgid "Dutch" msgstr "holandski" -#: conf/global_settings.py:82 +#: conf/global_settings.py:84 msgid "Norwegian" msgstr "norveÅ¡ki" -#: conf/global_settings.py:83 +#: conf/global_settings.py:85 msgid "Norwegian Bokmal" msgstr "norveÅ¡ki knjževni" -#: conf/global_settings.py:84 +#: conf/global_settings.py:86 msgid "Norwegian Nynorsk" msgstr "norveÅ¡ki novi" -#: conf/global_settings.py:85 +#: conf/global_settings.py:87 msgid "Polish" msgstr "poljski" -#: conf/global_settings.py:86 +#: conf/global_settings.py:88 msgid "Portuguese" msgstr "portugalski" -#: conf/global_settings.py:87 +#: conf/global_settings.py:89 msgid "Brazilian Portuguese" msgstr "brazilski portugalski" -#: conf/global_settings.py:88 +#: conf/global_settings.py:90 msgid "Romanian" msgstr "rumunski" -#: conf/global_settings.py:89 +#: conf/global_settings.py:91 msgid "Russian" msgstr "ruski" -#: conf/global_settings.py:90 +#: conf/global_settings.py:92 msgid "Slovak" msgstr "slovaÄki" -#: conf/global_settings.py:91 +#: conf/global_settings.py:93 msgid "Slovenian" msgstr "slovenaÄki" -#: conf/global_settings.py:92 +#: conf/global_settings.py:94 msgid "Albanian" msgstr "albanski" -#: conf/global_settings.py:93 +#: conf/global_settings.py:95 msgid "Serbian" msgstr "srpski" -#: conf/global_settings.py:94 +#: conf/global_settings.py:96 msgid "Serbian Latin" msgstr "srpski (latinica)" -#: conf/global_settings.py:95 +#: conf/global_settings.py:97 msgid "Swedish" msgstr "Å¡vedski" -#: conf/global_settings.py:96 +#: conf/global_settings.py:98 msgid "Tamil" msgstr "tamilski" -#: conf/global_settings.py:97 +#: conf/global_settings.py:99 msgid "Telugu" msgstr "telugu" -#: conf/global_settings.py:98 +#: conf/global_settings.py:100 msgid "Thai" msgstr "tajlandski" -#: conf/global_settings.py:99 +#: conf/global_settings.py:101 msgid "Turkish" msgstr "turski" -#: conf/global_settings.py:100 +#: conf/global_settings.py:102 msgid "Ukrainian" msgstr "ukrajinski" -#: conf/global_settings.py:101 +#: conf/global_settings.py:103 msgid "Vietnamese" msgstr "vijetnamski" -#: conf/global_settings.py:102 +#: conf/global_settings.py:104 msgid "Simplified Chinese" msgstr "novokineski" -#: conf/global_settings.py:103 +#: conf/global_settings.py:105 msgid "Traditional Chinese" msgstr "starokineski" @@ -261,7 +267,8 @@ msgstr "starokineski" msgid "Successfully deleted %(count)d %(items)s." msgstr "UspeÅ¡no obrisano: %(count)d %(items)s." -#: contrib/admin/actions.py:55 contrib/admin/options.py:1125 +#: contrib/admin/actions.py:55 +#: contrib/admin/options.py:1125 msgid "Are you sure?" msgstr "Da li ste sigurni?" @@ -279,8 +286,10 @@ msgstr "" "<h3>%s:</h3>\n" "<ul>\n" -#: contrib/admin/filterspecs.py:75 contrib/admin/filterspecs.py:92 -#: contrib/admin/filterspecs.py:147 contrib/admin/filterspecs.py:173 +#: contrib/admin/filterspecs.py:75 +#: contrib/admin/filterspecs.py:92 +#: contrib/admin/filterspecs.py:147 +#: contrib/admin/filterspecs.py:173 msgid "All" msgstr "Svi" @@ -304,15 +313,18 @@ msgstr "Ovaj mesec" msgid "This year" msgstr "Ova godina" -#: contrib/admin/filterspecs.py:147 forms/widgets.py:469 +#: contrib/admin/filterspecs.py:147 +#: forms/widgets.py:478 msgid "Yes" msgstr "Da" -#: contrib/admin/filterspecs.py:147 forms/widgets.py:469 +#: contrib/admin/filterspecs.py:147 +#: forms/widgets.py:478 msgid "No" msgstr "Ne" -#: contrib/admin/filterspecs.py:154 forms/widgets.py:469 +#: contrib/admin/filterspecs.py:154 +#: forms/widgets.py:478 msgid "Unknown" msgstr "Nepoznato" @@ -348,7 +360,8 @@ msgstr "zapis u logovima" msgid "log entries" msgstr "zapisi u logovima" -#: contrib/admin/options.py:138 contrib/admin/options.py:153 +#: contrib/admin/options.py:138 +#: contrib/admin/options.py:153 msgid "None" msgstr "NiÅ¡ta" @@ -357,8 +370,10 @@ msgstr "NiÅ¡ta" msgid "Changed %s." msgstr "Izmenjena polja %s" -#: contrib/admin/options.py:559 contrib/admin/options.py:569 -#: contrib/comments/templates/comments/preview.html:16 db/models/base.py:844 +#: contrib/admin/options.py:559 +#: contrib/admin/options.py:569 +#: contrib/comments/templates/comments/preview.html:16 +#: db/models/base.py:845 #: forms/models.py:568 msgid "and" msgstr "i" @@ -387,11 +402,13 @@ msgstr "Bez izmena u poljima." msgid "The %(name)s \"%(obj)s\" was added successfully." msgstr "Objekat „%(obj)s“ klase %(name)s saÄuvan je uspeÅ¡no." -#: contrib/admin/options.py:647 contrib/admin/options.py:680 +#: contrib/admin/options.py:647 +#: contrib/admin/options.py:680 msgid "You may edit it again below." msgstr "Dole možete ponovo da unosite izmene." -#: contrib/admin/options.py:657 contrib/admin/options.py:690 +#: contrib/admin/options.py:657 +#: contrib/admin/options.py:690 #, python-format msgid "You may add another %s below." msgstr "Dole možete da dodate novi objekat klase %s" @@ -403,19 +420,13 @@ msgstr "Objekat „%(obj)s“ klase %(name)s izmenjen je uspeÅ¡no." #: contrib/admin/options.py:686 #, python-format -msgid "" -"The %(name)s \"%(obj)s\" was added successfully. You may edit it again below." -msgstr "" -"Objekat „%(obj)s“ klase %(name)s dodat je uspeÅ¡no. Dole možete uneti dodatne " -"izmene." +msgid "The %(name)s \"%(obj)s\" was added successfully. You may edit it again below." +msgstr "Objekat „%(obj)s“ klase %(name)s dodat je uspeÅ¡no. Dole možete uneti dodatne izmene." -#: contrib/admin/options.py:740 contrib/admin/options.py:997 -msgid "" -"Items must be selected in order to perform actions on them. No items have " -"been changed." -msgstr "" -"Potrebno je izabrati objekte da bi se izvrÅ¡ila akcija nad njima. Nijedan " -"objekat nije promenjen." +#: contrib/admin/options.py:740 +#: contrib/admin/options.py:997 +msgid "Items must be selected in order to perform actions on them. No items have been changed." +msgstr "Potrebno je izabrati objekte da bi se izvrÅ¡ila akcija nad njima. Nijedan objekat nije promenjen." #: contrib/admin/options.py:759 msgid "No action selected." @@ -426,7 +437,8 @@ msgstr "Nije izabrana nijedna akcija." msgid "Add %s" msgstr "Dodaj objekat klase %s" -#: contrib/admin/options.py:866 contrib/admin/options.py:1105 +#: contrib/admin/options.py:866 +#: contrib/admin/options.py:1105 #, python-format msgid "%(name)s object with primary key %(key)r does not exist." msgstr "Objekat klase %(name)s sa primarnim kljuÄem %(key)r ne postoji." @@ -457,9 +469,9 @@ msgstr[1] "%(total_count)s izabrano" msgstr[2] "%(total_count)s izabranih" #: contrib/admin/options.py:1071 -#, fuzzy, python-format +#, python-format msgid "0 of %(cnt)s selected" -msgstr "0 od %(cnt)d izabrano" +msgstr "0 od %(cnt)s izabrano" #: contrib/admin/options.py:1118 #, python-format @@ -471,33 +483,30 @@ msgstr "Objekat „%(obj)s“ klase %(name)s uspeÅ¡no je obrisan." msgid "Change history: %s" msgstr "Istorijat izmena: %s" -#: contrib/admin/sites.py:18 contrib/admin/views/decorators.py:14 +#: contrib/admin/sites.py:18 +#: contrib/admin/views/decorators.py:14 #: contrib/auth/forms.py:81 -msgid "" -"Please enter a correct username and password. Note that both fields are case-" -"sensitive." -msgstr "" -"Unesite taÄno korisniÄko ime i lozinku. Pazite na razliku izmeÄ‘u malih i " -"velikih slova u oba polja." +msgid "Please enter a correct username and password. Note that both fields are case-sensitive." +msgstr "Unesite taÄno korisniÄko ime i lozinku. Pazite na razliku izmeÄ‘u malih i velikih slova u oba polja." -#: contrib/admin/sites.py:307 contrib/admin/views/decorators.py:40 +#: contrib/admin/sites.py:307 +#: contrib/admin/views/decorators.py:40 msgid "Please log in again, because your session has expired." msgstr "Prijavite se ponovo poÅ¡to je vaÅ¡a sesija istekla." -#: contrib/admin/sites.py:314 contrib/admin/views/decorators.py:47 -msgid "" -"Looks like your browser isn't configured to accept cookies. Please enable " -"cookies, reload this page, and try again." -msgstr "" -"Izgleda da vaÅ¡ brauzer nije podeÅ¡en da prima kolaÄiće. UkljuÄite kolaÄiće, " -"osvežite ovu stranicu i probajte ponovo." +#: contrib/admin/sites.py:314 +#: contrib/admin/views/decorators.py:47 +msgid "Looks like your browser isn't configured to accept cookies. Please enable cookies, reload this page, and try again." +msgstr "Izgleda da vaÅ¡ brauzer nije podeÅ¡en da prima kolaÄiće. UkljuÄite kolaÄiće, osvežite ovu stranicu i probajte ponovo." -#: contrib/admin/sites.py:330 contrib/admin/sites.py:336 +#: contrib/admin/sites.py:330 +#: contrib/admin/sites.py:336 #: contrib/admin/views/decorators.py:66 msgid "Usernames cannot contain the '@' character." msgstr "KorisniÄka imena ne smeju da sadrže znak „@“." -#: contrib/admin/sites.py:333 contrib/admin/views/decorators.py:62 +#: contrib/admin/sites.py:333 +#: contrib/admin/views/decorators.py:62 #, python-format msgid "Your e-mail address is not your username. Try '%s' instead." msgstr "VaÅ¡a imejl adresa nije vaÅ¡e korisniÄko ime. Probajte sa „%s“." @@ -506,7 +515,8 @@ msgstr "VaÅ¡a imejl adresa nije vaÅ¡e korisniÄko ime. Probajte sa „%s“." msgid "Site administration" msgstr "Administracija sistema" -#: contrib/admin/sites.py:403 contrib/admin/templates/admin/login.html:26 +#: contrib/admin/sites.py:403 +#: contrib/admin/templates/admin/login.html:26 #: contrib/admin/templates/registration/password_reset_complete.html:14 #: contrib/admin/views/decorators.py:20 msgid "Log in" @@ -584,12 +594,8 @@ msgid "Server Error <em>(500)</em>" msgstr "GreÅ¡ka na serveru <em>(500)</em>" #: contrib/admin/templates/admin/500.html:10 -msgid "" -"There's been an error. It's been reported to the site administrators via e-" -"mail and should be fixed shortly. Thanks for your patience." -msgstr "" -"DoÅ¡lo je do greÅ¡ke. Administrator sajta je obaveÅ¡ten imejlom i greÅ¡ka će " -"biti uskoro otklonjena. Hvala na strpljenju." +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 "DoÅ¡lo je do greÅ¡ke. Administrator sajta je obaveÅ¡ten imejlom i greÅ¡ka će biti uskoro otklonjena. Hvala na strpljenju." #: contrib/admin/templates/admin/actions.html:4 msgid "Run the selected action" @@ -687,29 +693,20 @@ msgid "Filter" msgstr "Filter" #: contrib/admin/templates/admin/delete_confirmation.html:10 -#: contrib/admin/templates/admin/submit_line.html:4 forms/formsets.py:302 +#: contrib/admin/templates/admin/submit_line.html:4 +#: forms/formsets.py:300 msgid "Delete" msgstr "ObriÅ¡i" #: contrib/admin/templates/admin/delete_confirmation.html:16 #, python-format -msgid "" -"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " -"related objects, but your account doesn't have permission to delete the " -"following types of objects:" -msgstr "" -"Uklanjanje %(object_name)s „%(escaped_object)s“ povlaÄi uklanjanje svih " -"objekata koji su povezani sa ovim objektom, ali vaÅ¡ nalog nema dozvole za " -"brisanje sledećih tipova objekata:" +msgid "Deleting the %(object_name)s '%(escaped_object)s' would result in deleting related objects, but your account doesn't have permission to delete the following types of objects:" +msgstr "Uklanjanje %(object_name)s „%(escaped_object)s“ povlaÄi uklanjanje svih objekata koji su povezani sa ovim objektom, ali vaÅ¡ nalog nema dozvole za brisanje sledećih tipova objekata:" #: contrib/admin/templates/admin/delete_confirmation.html:23 #, python-format -msgid "" -"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " -"All of the following related items will be deleted:" -msgstr "" -"Da sigurni da želite da obriÅ¡ete %(object_name)s „%(escaped_object)s“? " -"Sledeći objekti koji su u vezi sa ovim objektom će takoÄ‘e biti obrisani:" +msgid "Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? All of the following related items will be deleted:" +msgstr "Da sigurni da želite da obriÅ¡ete %(object_name)s „%(escaped_object)s“? Sledeći objekti koji su u vezi sa ovim objektom će takoÄ‘e biti obrisani:" #: contrib/admin/templates/admin/delete_confirmation.html:28 #: contrib/admin/templates/admin/delete_selected_confirmation.html:33 @@ -722,23 +719,13 @@ msgstr "Brisanje viÅ¡e objekata" #: contrib/admin/templates/admin/delete_selected_confirmation.html:15 #, python-format -msgid "" -"Deleting the %(object_name)s would result in deleting related objects, but " -"your account doesn't have permission to delete the following types of " -"objects:" -msgstr "" -"Uklanjanje %(object_name)s povlaÄi uklanjanje svih objekata koji su povezani " -"sa ovim objektom, ali vaÅ¡ nalog nema dozvole za brisanje sledećih tipova " -"objekata:" +msgid "Deleting the %(object_name)s would result in deleting related objects, but your account doesn't have permission to delete the following types of objects:" +msgstr "Uklanjanje %(object_name)s povlaÄi uklanjanje svih objekata koji su povezani sa ovim objektom, ali vaÅ¡ nalog nema dozvole za brisanje sledećih tipova objekata:" #: contrib/admin/templates/admin/delete_selected_confirmation.html:22 #, python-format -msgid "" -"Are you sure you want to delete the selected %(object_name)s objects? All of " -"the following objects and their related items will be deleted:" -msgstr "" -"Da sigurni da želite da obriÅ¡ete odabrane %(object_name)s? Sledeći objekti " -"koji su u vezi sa ovim objektom će takoÄ‘e biti obrisani:" +msgid "Are you sure you want to delete the selected %(object_name)s objects? All of the following objects and their related items will be deleted:" +msgstr "Da sigurni da želite da obriÅ¡ete odabrane %(object_name)s? Sledeći objekti koji su u vezi sa ovim objektom će takoÄ‘e biti obrisani:" #: contrib/admin/templates/admin/filter.html:2 #, python-format @@ -775,13 +762,8 @@ msgid "Unknown content" msgstr "Nepoznat sadržaj" #: contrib/admin/templates/admin/invalid_setup.html:7 -msgid "" -"Something's wrong with your database installation. Make sure the appropriate " -"database tables have been created, and make sure the database is readable by " -"the appropriate user." -msgstr "" -"NeÅ¡to nije uredu sa vaÅ¡om bazom podataka. Proverite da li postoje " -"odgovarajuće tabele i da li odgovarajući korisnik ima pristup bazi." +msgid "Something's wrong with your database installation. Make sure the appropriate database tables have been created, and make sure the database is readable by the appropriate user." +msgstr "NeÅ¡to nije uredu sa vaÅ¡om bazom podataka. Proverite da li postoje odgovarajuće tabele i da li odgovarajući korisnik ima pristup bazi." #: contrib/admin/templates/admin/login.html:19 msgid "Username:" @@ -804,12 +786,8 @@ msgid "Action" msgstr "Radnja" #: contrib/admin/templates/admin/object_history.html:38 -msgid "" -"This object doesn't have a change history. It probably wasn't added via this " -"admin site." -msgstr "" -"Ovaj objekat nema zabeležen istorijat izmena. Verovatno nije dodat kroz ovaj " -"sajt za administraciju." +msgid "This object doesn't have a change history. It probably wasn't added via this admin site." +msgstr "Ovaj objekat nema zabeležen istorijat izmena. Verovatno nije dodat kroz ovaj sajt za administraciju." #: contrib/admin/templates/admin/pagination.html:10 msgid "Show all" @@ -849,13 +827,13 @@ msgstr "SaÄuvaj i dodaj sledeći" msgid "Save and continue editing" msgstr "SaÄuvaj i nastavi sa izmenama" -#: contrib/admin/templates/admin/auth/user/add_form.html:5 -msgid "" -"First, enter a username and password. Then, you'll be able to edit more user " -"options." -msgstr "" -"Prvo unesite korisniÄko ime i lozinku. Potom ćete moći da menjate joÅ¡ " -"korisniÄkih podeÅ¡avanja." +#: contrib/admin/templates/admin/auth/user/add_form.html:6 +msgid "First, enter a username and password. Then, you'll be able to edit more user options." +msgstr "Prvo unesite korisniÄko ime i lozinku. Potom ćete moći da menjate joÅ¡ korisniÄkih podeÅ¡avanja." + +#: contrib/admin/templates/admin/auth/user/add_form.html:8 +msgid "Enter a username and password." +msgstr "Unesite korisniÄko ime i lozinku" #: contrib/admin/templates/admin/auth/user/change_password.html:28 #, python-format @@ -863,7 +841,9 @@ msgid "Enter a new password for the user <strong>%(username)s</strong>." msgstr "Unesite novu lozinku za korisnika <strong>%(username)s</strong>." #: contrib/admin/templates/admin/auth/user/change_password.html:35 -#: contrib/auth/forms.py:17 contrib/auth/forms.py:61 contrib/auth/forms.py:186 +#: contrib/auth/forms.py:17 +#: contrib/auth/forms.py:61 +#: contrib/auth/forms.py:186 msgid "Password" msgstr "Lozinka" @@ -919,12 +899,8 @@ msgid "Your password was changed." msgstr "VaÅ¡a lozinka je izmenjena." #: contrib/admin/templates/registration/password_change_form.html:21 -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 "" -"Iz bezbednosnih razloga prvo unesite svoju staru lozinku, a novu zatim " -"unesite dva puta da bismo mogli da proverimo da li ste je pravilno uneli." +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 "Iz bezbednosnih razloga prvo unesite svoju staru lozinku, a novu zatim unesite dva puta da bismo mogli da proverimo da li ste je pravilno uneli." #: contrib/admin/templates/registration/password_change_form.html:27 #: contrib/auth/forms.py:170 @@ -968,12 +944,8 @@ msgid "Enter new password" msgstr "Unesite novu lozinku" #: contrib/admin/templates/registration/password_reset_confirm.html:14 -msgid "" -"Please enter your new password twice so we can verify you typed it in " -"correctly." -msgstr "" -"Unesite novu lozinku dva puta kako bismo mogli da proverimo da li ste je " -"pravilno uneli." +msgid "Please enter your new password twice so we can verify you typed it in correctly." +msgstr "Unesite novu lozinku dva puta kako bismo mogli da proverimo da li ste je pravilno uneli." #: contrib/admin/templates/registration/password_reset_confirm.html:18 msgid "New password:" @@ -988,12 +960,8 @@ msgid "Password reset unsuccessful" msgstr "Resetovanje lozinke neuspeÅ¡no" #: contrib/admin/templates/registration/password_reset_confirm.html:28 -msgid "" -"The password reset link was invalid, possibly because it has already been " -"used. Please request a new password reset." -msgstr "" -"Link za resetovanje lozinke nije važeći, verovatno zato Å¡to je već " -"iskorišćen. Ponovo zatražite resetovanje lozinke." +msgid "The password reset link was invalid, possibly because it has already been used. Please request a new password reset." +msgstr "Link za resetovanje lozinke nije važeći, verovatno zato Å¡to je već iskorišćen. Ponovo zatražite resetovanje lozinke." #: contrib/admin/templates/registration/password_reset_done.html:6 #: contrib/admin/templates/registration/password_reset_done.html:10 @@ -1001,12 +969,8 @@ msgid "Password reset successful" msgstr "Resetovanje lozinke uspeÅ¡no." #: contrib/admin/templates/registration/password_reset_done.html:12 -msgid "" -"We've e-mailed you instructions for setting your password to the e-mail " -"address you submitted. You should be receiving it shortly." -msgstr "" -"Poslali smo uputstva za postavljanje nove lozinke na imejl adresu koju ste " -"nam dali. Uputstva ćete dobiti uskoro." +msgid "We've e-mailed you instructions for setting your password to the e-mail address you submitted. You should be receiving it shortly." +msgstr "Poslali smo uputstva za postavljanje nove lozinke na imejl adresu koju ste nam dali. Uputstva ćete dobiti uskoro." #: contrib/admin/templates/registration/password_reset_email.html:2 msgid "You're receiving this e-mail because you requested a password reset" @@ -1035,12 +999,8 @@ msgid "The %(site_name)s team" msgstr "Ekipa sajta %(site_name)s" #: contrib/admin/templates/registration/password_reset_form.html:12 -msgid "" -"Forgotten your password? Enter your e-mail address below, and we'll e-mail " -"instructions for setting a new one." -msgstr "" -"Zaboravili ste lozinku? Unesite svoju imejl adresu dole i poslaćemo vam " -"uputstva za postavljanje nove." +msgid "Forgotten your password? Enter your e-mail address below, and we'll e-mail instructions for setting a new one." +msgstr "Zaboravili ste lozinku? Unesite svoju imejl adresu dole i poslaćemo vam uputstva za postavljanje nove." #: contrib/admin/templates/registration/password_reset_form.html:16 msgid "E-mail address:" @@ -1050,7 +1010,7 @@ msgstr "Imejl adresa:" msgid "Reset my password" msgstr "Resetuj moju lozinku" -#: contrib/admin/templatetags/admin_list.py:239 +#: contrib/admin/templatetags/admin_list.py:257 msgid "All dates" msgstr "Svi datumi" @@ -1064,7 +1024,8 @@ msgstr "Odaberi objekat klase %s" msgid "Select %s to change" msgstr "Odaberi objekat klase %s za izmenu" -#: contrib/admin/views/template.py:38 contrib/sites/models.py:38 +#: contrib/admin/views/template.py:38 +#: contrib/sites/models.py:38 msgid "site" msgstr "sajt" @@ -1072,17 +1033,20 @@ msgstr "sajt" msgid "template" msgstr "templejt" -#: contrib/admindocs/views.py:61 contrib/admindocs/views.py:63 +#: contrib/admindocs/views.py:61 +#: contrib/admindocs/views.py:63 #: contrib/admindocs/views.py:65 msgid "tag:" msgstr "tag:" -#: contrib/admindocs/views.py:94 contrib/admindocs/views.py:96 +#: contrib/admindocs/views.py:94 +#: contrib/admindocs/views.py:96 #: contrib/admindocs/views.py:98 msgid "filter:" msgstr "filter:" -#: contrib/admindocs/views.py:158 contrib/admindocs/views.py:160 +#: contrib/admindocs/views.py:158 +#: contrib/admindocs/views.py:160 #: contrib/admindocs/views.py:162 msgid "view:" msgstr "vju:" @@ -1102,28 +1066,34 @@ msgstr "Model %(model_name)r nije pronaÄ‘en u aplikaciji %(app_label)r" msgid "the related `%(app_label)s.%(data_type)s` object" msgstr "povezani objekti klase `%(app_label)s.%(data_type)s`" -#: contrib/admindocs/views.py:209 contrib/admindocs/views.py:228 -#: contrib/admindocs/views.py:233 contrib/admindocs/views.py:247 -#: contrib/admindocs/views.py:261 contrib/admindocs/views.py:266 +#: contrib/admindocs/views.py:209 +#: contrib/admindocs/views.py:228 +#: contrib/admindocs/views.py:233 +#: contrib/admindocs/views.py:247 +#: contrib/admindocs/views.py:261 +#: contrib/admindocs/views.py:266 msgid "model:" msgstr "model:" # WARN: possible breakage in future # This string is interpolated in strings below, which can cause breakage in # future releases. -#: contrib/admindocs/views.py:224 contrib/admindocs/views.py:256 +#: contrib/admindocs/views.py:224 +#: contrib/admindocs/views.py:256 #, python-format msgid "related `%(app_label)s.%(object_name)s` objects" msgstr "klase `%(app_label)s.%(object_name)s`" # WARN: possible breakage in future -#: contrib/admindocs/views.py:228 contrib/admindocs/views.py:261 +#: contrib/admindocs/views.py:228 +#: contrib/admindocs/views.py:261 #, python-format msgid "all %s" msgstr "svi povezani objekti %s" # WARN: possible breakage in future -#: contrib/admindocs/views.py:233 contrib/admindocs/views.py:266 +#: contrib/admindocs/views.py:233 +#: contrib/admindocs/views.py:266 #, python-format msgid "number of %s" msgstr "broj povezanih objekata %s" @@ -1170,24 +1140,16 @@ msgid "Documentation for this page" msgstr "Dokumentacija za ovu stranicu" #: contrib/admindocs/templates/admin_doc/bookmarklets.html:19 -msgid "" -"Jumps you from any page to the documentation for the view that generates " -"that page." -msgstr "" -"Vodi od bilo koje stranice do dokumentaicje pogleda koji je generisao tu " -"stranicu." +msgid "Jumps you from any page to the documentation for the view that generates that page." +msgstr "Vodi od bilo koje stranice do dokumentaicje pogleda koji je generisao tu stranicu." #: contrib/admindocs/templates/admin_doc/bookmarklets.html:21 msgid "Show object ID" msgstr "Prikaži ID objekta" #: contrib/admindocs/templates/admin_doc/bookmarklets.html:22 -msgid "" -"Shows the content-type and unique ID for pages that represent a single " -"object." -msgstr "" -"Prikazuje content-type i jedinstveni ID za stranicu koja prestavlja jedan " -"objekat." +msgid "Shows the content-type and unique ID for pages that represent a single object." +msgstr "Prikazuje content-type i jedinstveni ID za stranicu koja prestavlja jedan objekat." #: contrib/admindocs/templates/admin_doc/bookmarklets.html:24 msgid "Edit this object (current window)" @@ -1195,8 +1157,7 @@ msgstr "Izmeni ovaj objekat (u ovom prozoru)" #: contrib/admindocs/templates/admin_doc/bookmarklets.html:25 msgid "Jumps to the admin page for pages that represent a single object." -msgstr "" -"Vodi u administracioni stranicu za stranice koje prestavljaju jedan objekat" +msgstr "Vodi u administracioni stranicu za stranice koje prestavljaju jedan objekat" #: contrib/admindocs/templates/admin_doc/bookmarklets.html:27 msgid "Edit this object (new window)" @@ -1204,8 +1165,7 @@ msgstr "Izmeni ovaj objekat (novi prozor)" #: contrib/admindocs/templates/admin_doc/bookmarklets.html:28 msgid "As above, but opens the admin page in a new window." -msgstr "" -"Isto kao prethodni, ali otvara administracionu stranicu u novom prozoru." +msgstr "Isto kao prethodni, ali otvara administracionu stranicu u novom prozoru." #: contrib/auth/admin.py:29 msgid "Personal info" @@ -1232,17 +1192,19 @@ msgstr "Lozinka uspeÅ¡no izmenjena." msgid "Change password: %s" msgstr "Izmeni lozinku: %s" -#: contrib/auth/forms.py:14 contrib/auth/forms.py:48 contrib/auth/forms.py:60 +#: contrib/auth/forms.py:14 +#: contrib/auth/forms.py:48 +#: contrib/auth/forms.py:60 msgid "Username" msgstr "Korisnik" -#: contrib/auth/forms.py:15 contrib/auth/forms.py:49 +#: contrib/auth/forms.py:15 +#: contrib/auth/forms.py:49 msgid "Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only." -msgstr "" -"Neophodno. NajviÅ¡e 30 slovnih mesta. Samo alfanumeriÄki znaci (slova, brojke " -"i @/./+/-/_)." +msgstr "Neophodno. NajviÅ¡e 30 slovnih mesta. Samo alfanumeriÄki znaci (slova, brojke i @/./+/-/_)." -#: contrib/auth/forms.py:16 contrib/auth/forms.py:50 +#: contrib/auth/forms.py:16 +#: contrib/auth/forms.py:50 msgid "This value may contain only letters, numbers and @/./+/-/_ characters." msgstr "Ova vrednost može sadržati samo slova, brojke i @/./+/-/_." @@ -1254,7 +1216,8 @@ msgstr "Potvrda lozinke" msgid "A user with that username already exists." msgstr "Korisnik sa tim korisniÄkim imenom već postoji." -#: contrib/auth/forms.py:37 contrib/auth/forms.py:156 +#: contrib/auth/forms.py:37 +#: contrib/auth/forms.py:156 #: contrib/auth/forms.py:198 msgid "The two password fields didn't match." msgstr "Dva polja za lozinke se nisu poklopila." @@ -1264,24 +1227,16 @@ msgid "This account is inactive." msgstr "Ovaj nalog je neaktivan." #: contrib/auth/forms.py:88 -msgid "" -"Your Web browser doesn't appear to have cookies enabled. Cookies are " -"required for logging in." -msgstr "" -"Izgleda da su kolaÄići iskljuÄeni u vaÅ¡em brauzeru. Oni moraju biti " -"ukljuÄeni da bi ste se prijavili." +msgid "Your Web browser doesn't appear to have cookies enabled. Cookies are required for logging in." +msgstr "Izgleda da su kolaÄići iskljuÄeni u vaÅ¡em brauzeru. Oni moraju biti ukljuÄeni da bi ste se prijavili." #: contrib/auth/forms.py:101 msgid "E-mail" msgstr "Imejl adresa" #: contrib/auth/forms.py:110 -msgid "" -"That e-mail address doesn't have an associated user account. Are you sure " -"you've registered?" -msgstr "" -"Ta imejl adresa nije u vezi ni sa jednim nalogom. Da li ste sigurni da ste " -"se već registrovali?" +msgid "That e-mail address doesn't have an associated user account. Are you sure you've registered?" +msgstr "Ta imejl adresa nije u vezi ni sa jednim nalogom. Da li ste sigurni da ste se već registrovali?" #: contrib/auth/forms.py:136 #, python-format @@ -1296,7 +1251,8 @@ msgstr "Potvrda nove lozinke" msgid "Your old password was entered incorrectly. Please enter it again." msgstr "VaÅ¡a stara loznka nije pravilno unesena. Unesite je ponovo." -#: contrib/auth/models.py:66 contrib/auth/models.py:94 +#: contrib/auth/models.py:66 +#: contrib/auth/models.py:94 msgid "name" msgstr "ime" @@ -1308,7 +1264,8 @@ msgstr "Å¡ifra dozvole" msgid "permission" msgstr "dozvola" -#: contrib/auth/models.py:73 contrib/auth/models.py:95 +#: contrib/auth/models.py:73 +#: contrib/auth/models.py:95 msgid "permissions" msgstr "dozvole" @@ -1316,7 +1273,8 @@ msgstr "dozvole" msgid "group" msgstr "grupa" -#: contrib/auth/models.py:99 contrib/auth/models.py:206 +#: contrib/auth/models.py:99 +#: contrib/auth/models.py:206 msgid "groups" msgstr "grupe" @@ -1325,11 +1283,8 @@ msgid "username" msgstr "korisniÄko ime" #: contrib/auth/models.py:196 -msgid "" -"Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters" -msgstr "" -"Neophodno. NajviÅ¡e 30 slovnih mesta. Samo alfanumeriÄki znaci (slova, brojke " -"i @/./+/-/_)." +msgid "Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters" +msgstr "Neophodno. NajviÅ¡e 30 slovnih mesta. Samo alfanumeriÄki znaci (slova, brojke i @/./+/-/_)." #: contrib/auth/models.py:197 msgid "first name" @@ -1348,12 +1303,8 @@ msgid "password" msgstr "lozinka" #: contrib/auth/models.py:200 -msgid "" -"Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change " -"password form</a>." -msgstr "" -"Koristite '[algo]$[salt]$[hexdigest]' ili <a href=\"password/\">formular za " -"unos lozinke</a>." +msgid "Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change password form</a>." +msgstr "Koristite '[algo]$[salt]$[hexdigest]' ili <a href=\"password/\">formular za unos lozinke</a>." #: contrib/auth/models.py:201 msgid "staff status" @@ -1361,32 +1312,23 @@ msgstr "status Älana posade" #: contrib/auth/models.py:201 msgid "Designates whether the user can log into this admin site." -msgstr "" -"OznaÄava da li korisnik može da se prijavi na ovaj sajt za administraciju." +msgstr "OznaÄava da li korisnik može da se prijavi na ovaj sajt za administraciju." #: contrib/auth/models.py:202 msgid "active" msgstr "aktivan" #: contrib/auth/models.py:202 -msgid "" -"Designates whether this user should be treated as active. Unselect this " -"instead of deleting accounts." -msgstr "" -"OznaÄava da li se korisnik smatra aktivnim. Deselektujte ovo umesto da " -"briÅ¡ete nalog." +msgid "Designates whether this user should be treated as active. Unselect this instead of deleting accounts." +msgstr "OznaÄava da li se korisnik smatra aktivnim. Deselektujte ovo umesto da briÅ¡ete nalog." #: contrib/auth/models.py:203 msgid "superuser status" msgstr "status administratora" #: contrib/auth/models.py:203 -msgid "" -"Designates that this user has all permissions without explicitly assigning " -"them." -msgstr "" -"OznaÄava da li korisnik ima sve dozvole bez dodeljivanja pojedinaÄnih " -"dozvola." +msgid "Designates that this user has all permissions without explicitly assigning them." +msgstr "OznaÄava da li korisnik ima sve dozvole bez dodeljivanja pojedinaÄnih dozvola." #: contrib/auth/models.py:204 msgid "last login" @@ -1397,18 +1339,15 @@ msgid "date joined" msgstr "datum registracije" #: contrib/auth/models.py:207 -msgid "" -"In addition to the permissions manually assigned, this user will also get " -"all permissions granted to each group he/she is in." -msgstr "" -"Pored ruÄno dodeljenih dozvola, ovaj korisnik će imati i dozvole dodeljene " -"gurpama kojima pripada." +msgid "In addition to the permissions manually assigned, this user will also get all permissions granted to each group he/she is in." +msgstr "Pored ruÄno dodeljenih dozvola, ovaj korisnik će imati i dozvole dodeljene gurpama kojima pripada." #: contrib/auth/models.py:208 msgid "user permissions" msgstr "korisniÄke dozvole" -#: contrib/auth/models.py:212 contrib/comments/models.py:50 +#: contrib/auth/models.py:212 +#: contrib/comments/models.py:50 #: contrib/comments/models.py:168 msgid "user" msgstr "korisnik" @@ -1425,8 +1364,9 @@ msgstr "poruka" msgid "Logged out" msgstr "Odjavljen" -#: contrib/auth/management/commands/createsuperuser.py:23 -#: core/validators.py:120 forms/fields.py:428 +#: contrib/auth/management/commands/createsuperuser.py:24 +#: core/validators.py:120 +#: forms/fields.py:427 msgid "Enter a valid e-mail address." msgstr "Unesite važeću imejl adresu." @@ -1497,8 +1437,10 @@ msgstr "Ime" msgid "Email address" msgstr "Imejl adresa" -#: contrib/comments/forms.py:95 contrib/flatpages/admin.py:8 -#: contrib/flatpages/models.py:7 db/models/fields/__init__.py:1101 +#: contrib/comments/forms.py:95 +#: contrib/flatpages/admin.py:8 +#: contrib/flatpages/models.py:7 +#: db/models/fields/__init__.py:1109 msgid "URL" msgstr "URL" @@ -1515,11 +1457,11 @@ msgstr[1] "Pazi na jezik! ReÄi „%s“ ovde nisu dozvoljene." msgstr[2] "Pazi na jezik! ReÄi „%s“ ovde nisu dozvoljene." #: contrib/comments/forms.py:182 -msgid "" -"If you enter anything in this field your comment will be treated as spam" +msgid "If you enter anything in this field your comment will be treated as spam" msgstr "Ako iÅ¡ta unesete u ovo polje, VaÅ¡ komentar će se smatrati spamom." -#: contrib/comments/models.py:22 contrib/contenttypes/models.py:81 +#: contrib/comments/models.py:22 +#: contrib/contenttypes/models.py:81 msgid "content type" msgstr "tip sadržaja" @@ -1539,7 +1481,8 @@ msgstr "korisnikova imejl adresa" msgid "user's URL" msgstr "korisnikov URL" -#: contrib/comments/models.py:56 contrib/comments/models.py:76 +#: contrib/comments/models.py:56 +#: contrib/comments/models.py:76 #: contrib/comments/models.py:169 msgid "comment" msgstr "komentar" @@ -1548,7 +1491,8 @@ msgstr "komentar" msgid "date/time submitted" msgstr "datum/vreme postavljanja" -#: contrib/comments/models.py:60 db/models/fields/__init__.py:896 +#: contrib/comments/models.py:60 +#: db/models/fields/__init__.py:904 msgid "IP address" msgstr "IP adresa" @@ -1557,42 +1501,28 @@ msgid "is public" msgstr "javno" #: contrib/comments/models.py:62 -msgid "" -"Uncheck this box to make the comment effectively disappear from the site." -msgstr "" -"Deselektujte ovo polje ako želite da poruka faktiÄki nestane sa ovog sajta." +msgid "Uncheck this box to make the comment effectively disappear from the site." +msgstr "Deselektujte ovo polje ako želite da poruka faktiÄki nestane sa ovog sajta." #: contrib/comments/models.py:64 msgid "is removed" msgstr "uklonjen" #: contrib/comments/models.py:65 -msgid "" -"Check this box if the comment is inappropriate. A \"This comment has been " -"removed\" message will be displayed instead." -msgstr "" -"Obeležite ovu kućicu ako je komentar neprikladan. Poruka o uklanjanju će " -"biti prikazana umesto komentara." +msgid "Check this box if the comment is inappropriate. A \"This comment has been removed\" message will be displayed instead." +msgstr "Obeležite ovu kućicu ako je komentar neprikladan. Poruka o uklanjanju će biti prikazana umesto komentara." #: contrib/comments/models.py:77 msgid "comments" msgstr "komentari" #: contrib/comments/models.py:119 -msgid "" -"This comment was posted by an authenticated user and thus the name is read-" -"only." -msgstr "" -"Ovaj komentar je postavio prijavljen korisnik i zato je polje sa imenom " -"zakljuÄano." +msgid "This comment was posted by an authenticated user and thus the name is read-only." +msgstr "Ovaj komentar je postavio prijavljen korisnik i zato je polje sa imenom zakljuÄano." #: contrib/comments/models.py:128 -msgid "" -"This comment was posted by an authenticated user and thus the email is read-" -"only." -msgstr "" -"Ovaj komentar je postavio prijavljen korisnik i zato je polje sa imejl " -"adresom zakljuÄano." +msgid "This comment was posted by an authenticated user and thus the email is read-only." +msgstr "Ovaj komentar je postavio prijavljen korisnik i zato je polje sa imejl adresom zakljuÄano." #: contrib/comments/models.py:153 #, python-format @@ -1644,8 +1574,7 @@ msgstr "Hvala na odobrenju!" #: contrib/comments/templates/comments/approved.html:7 #: contrib/comments/templates/comments/deleted.html:7 #: contrib/comments/templates/comments/flagged.html:7 -msgid "" -"Thanks for taking the time to improve the quality of discussion on our site" +msgid "Thanks for taking the time to improve the quality of discussion on our site" msgstr "Hvala na uÄešću u unapreÄ‘enju kvaliteta diskusija na naÅ¡em sajtu." #: contrib/comments/templates/comments/delete.html:4 @@ -1723,19 +1652,12 @@ msgid "content types" msgstr "tipovi sadržaja" #: contrib/flatpages/admin.py:9 -msgid "" -"Example: '/about/contact/'. Make sure to have leading and trailing slashes." -msgstr "" -"Primer: '/about/contact/'. Pazite na to da postoje i poÄetne i zavrÅ¡ne kose " -"crte." +msgid "Example: '/about/contact/'. Make sure to have leading and trailing slashes." +msgstr "Primer: '/about/contact/'. Pazite na to da postoje i poÄetne i zavrÅ¡ne kose crte." #: contrib/flatpages/admin.py:11 -msgid "" -"This value must contain only letters, numbers, underscores, dashes or " -"slashes." -msgstr "" -"Ova vrednost može sadržati samo slova, brojke, donje crte, crtice ili kose " -"crte." +msgid "This value must contain only letters, numbers, underscores, dashes or slashes." +msgstr "Ova vrednost može sadržati samo slova, brojke, donje crte, crtice ili kose crte." #: contrib/flatpages/admin.py:22 msgid "Advanced options" @@ -1758,12 +1680,8 @@ msgid "template name" msgstr "naziv templejta" #: contrib/flatpages/models.py:12 -msgid "" -"Example: 'flatpages/contact_page.html'. If this isn't provided, the system " -"will use 'flatpages/default.html'." -msgstr "" -"Primer: 'flatpages/contact_page.html'. Ako ovo ostavite praznim, sistem će " -"koristiti 'flatpages/default.html'." +msgid "Example: 'flatpages/contact_page.html'. If this isn't provided, the system will use 'flatpages/default.html'." +msgstr "Primer: 'flatpages/contact_page.html'. Ako ovo ostavite praznim, sistem će koristiti 'flatpages/default.html'." #: contrib/flatpages/models.py:13 msgid "registration required" @@ -1771,9 +1689,7 @@ msgstr "potrebna registracija" #: contrib/flatpages/models.py:13 msgid "If this is checked, only logged-in users will be able to view the page." -msgstr "" -"Ako je ovo obeleženo, samo će prijavljeni korisnici moći da vide ovu " -"stranicu." +msgstr "Ako je ovo obeleženo, samo će prijavljeni korisnici moći da vide ovu stranicu." #: contrib/flatpages/models.py:18 msgid "flat page" @@ -1784,17 +1700,12 @@ msgid "flat pages" msgstr "flet stranice" #: contrib/formtools/wizard.py:140 -msgid "" -"We apologize, but your form has expired. Please continue filling out the " -"form from this page." -msgstr "" -"Žao nam je, ali VaÅ¡a sesija je istekla. Popunjavanje formulara nastavite na " -"ovoj stranici." +msgid "We apologize, but your form has expired. Please continue filling out the form from this page." +msgstr "Žao nam je, ali VaÅ¡a sesija je istekla. Popunjavanje formulara nastavite na ovoj stranici." #: contrib/gis/db/models/fields.py:50 msgid "The base GIS field -- maps to the OpenGIS Specification Geometry type." -msgstr "" -"Osnovno „GIS“ polje koje mapira tip geometrije po „OpenGIS“ specifikaciji." +msgstr "Osnovno „GIS“ polje koje mapira tip geometrije po „OpenGIS“ specifikaciji." #: contrib/gis/db/models/fields.py:270 msgid "Point" @@ -1837,9 +1748,7 @@ msgid "Invalid geometry type." msgstr "Nepostojeći tip geometrije." #: contrib/gis/forms/fields.py:20 -msgid "" -"An error occurred when transforming the geometry to the SRID of the geometry " -"form field." +msgid "An error occurred when transforming the geometry to the SRID of the geometry form field." msgstr "GreÅ¡ka se desila tokom transformacije geometrije na „SRID“ tip polja." #: contrib/humanize/templatetags/humanize.py:19 @@ -1934,8 +1843,10 @@ msgstr "juÄe" msgid "Enter a postal code in the format NNNN or ANNNNAAA." msgstr "Unesite poÅ¡tanski broj u formatu NNNN ili ANNNNAAA." -#: contrib/localflavor/ar/forms.py:50 contrib/localflavor/br/forms.py:92 -#: contrib/localflavor/br/forms.py:131 contrib/localflavor/pe/forms.py:24 +#: contrib/localflavor/ar/forms.py:50 +#: contrib/localflavor/br/forms.py:92 +#: contrib/localflavor/br/forms.py:131 +#: contrib/localflavor/pe/forms.py:24 #: contrib/localflavor/pe/forms.py:52 msgid "This field requires only numbers." msgstr "Ovo polje mora da sadrži samo brojke." @@ -1988,15 +1899,15 @@ msgstr "Voralber" msgid "Vienna" msgstr "BeÄ" -#: contrib/localflavor/at/forms.py:20 contrib/localflavor/ch/forms.py:17 +#: contrib/localflavor/at/forms.py:20 +#: contrib/localflavor/ch/forms.py:17 #: contrib/localflavor/no/forms.py:13 msgid "Enter a zip code in the format XXXX." msgstr "Unesite poÅ¡tanski broj u formatu XXXX." #: contrib/localflavor/at/forms.py:48 msgid "Enter a valid Austrian Social Security Number in XXXX XXXXXX format." -msgstr "" -"Unesite važeći austrijski broj socijalnog osiguranja u formatu XXXX XXXXXX." +msgstr "Unesite važeći austrijski broj socijalnog osiguranja u formatu XXXX XXXXXX." #: contrib/localflavor/au/forms.py:17 msgid "Enter a 4 digit post code." @@ -2011,9 +1922,7 @@ msgid "Phone numbers must be in XX-XXXX-XXXX format." msgstr "Broj telefona mora biti u formatu XX-XXXX-XXXX." #: contrib/localflavor/br/forms.py:54 -msgid "" -"Select a valid brazilian state. That state is not one of the available " -"states." +msgid "Select a valid brazilian state. That state is not one of the available states." msgstr "Odaberite postojeću brazilsku državu. Ta država nije meÄ‘u ponuÄ‘enima." #: contrib/localflavor/br/forms.py:90 @@ -2145,9 +2054,7 @@ msgid "Zurich" msgstr "" #: contrib/localflavor/ch/forms.py:65 -msgid "" -"Enter a valid Swiss identity or passport card number in X1234567<0 or " -"1234567890 format." +msgid "Enter a valid Swiss identity or passport card number in X1234567<0 or 1234567890 format." msgstr "" #: contrib/localflavor/cl/forms.py:30 @@ -2218,7 +2125,8 @@ msgstr "" msgid "Moravian-Silesian Region" msgstr "" -#: contrib/localflavor/cz/forms.py:28 contrib/localflavor/sk/forms.py:30 +#: contrib/localflavor/cz/forms.py:28 +#: contrib/localflavor/sk/forms.py:30 msgid "Enter a postal code in the format XXXXX or XXX XX." msgstr "" @@ -2302,15 +2210,14 @@ msgstr "" msgid "Thuringia" msgstr "" -#: contrib/localflavor/de/forms.py:15 contrib/localflavor/fi/forms.py:13 +#: contrib/localflavor/de/forms.py:15 +#: contrib/localflavor/fi/forms.py:13 #: contrib/localflavor/fr/forms.py:16 msgid "Enter a zip code in the format XXXXX." msgstr "" #: contrib/localflavor/de/forms.py:42 -msgid "" -"Enter a valid German identity card number in XXXXXXXXXXX-XXXXXXX-XXXXXXX-X " -"format." +msgid "Enter a valid German identity card number in XXXXXXXXXXX-XXXXXXX-XXXXXXX-X format." msgstr "" #: contrib/localflavor/es/es_provinces.py:5 @@ -2585,9 +2492,7 @@ msgid "Enter a valid postal code in the range and format 01XXX - 52XXX." msgstr "" #: contrib/localflavor/es/forms.py:40 -msgid "" -"Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or " -"9XXXXXXXX." +msgid "Enter a valid phone number in one of the formats 6XXXXXXXX, 8XXXXXXXX or 9XXXXXXXX." msgstr "" #: contrib/localflavor/es/forms.py:67 @@ -2611,8 +2516,7 @@ msgid "Invalid checksum for CIF." msgstr "" #: contrib/localflavor/es/forms.py:143 -msgid "" -"Please enter a valid bank account number in format XXXX-XXXX-XX-XXXXXXXXXX." +msgid "Please enter a valid bank account number in format XXXX-XXXX-XX-XXXXXXXXXX." msgstr "" #: contrib/localflavor/es/forms.py:144 @@ -2631,7 +2535,8 @@ msgstr "" msgid "Enter a valid post code" msgstr "" -#: contrib/localflavor/id/forms.py:68 contrib/localflavor/nl/forms.py:53 +#: contrib/localflavor/id/forms.py:68 +#: contrib/localflavor/nl/forms.py:53 msgid "Enter a valid phone number" msgstr "" @@ -3060,8 +2965,7 @@ msgid "Enter a zip code in the format XXXXXXX." msgstr "" #: contrib/localflavor/is_/forms.py:18 -msgid "" -"Enter a valid Icelandic identification number. The format is XXXXXX-XXXX." +msgid "Enter a valid Icelandic identification number. The format is XXXXXX-XXXX." msgstr "" #: contrib/localflavor/is_/forms.py:19 @@ -3481,8 +3385,7 @@ msgid "Wrong checksum for the National Identification Number." msgstr "" #: contrib/localflavor/pl/forms.py:71 -msgid "" -"Enter a tax number field (NIP) in the format XXX-XXX-XX-XX or XX-XX-XXX-XXX." +msgid "Enter a tax number field (NIP) in the format XXX-XXX-XX-XX or XX-XX-XXX-XXX." msgstr "" #: contrib/localflavor/pl/forms.py:72 @@ -4410,24 +4313,16 @@ msgid "redirect from" msgstr "preusmeren sa" #: contrib/redirects/models.py:8 -msgid "" -"This should be an absolute path, excluding the domain name. Example: '/" -"events/search/'." -msgstr "" -"Ovo mora biti apsolutna putanja bez imena domena. Na primer: '/events/" -"search/'." +msgid "This should be an absolute path, excluding the domain name. Example: '/events/search/'." +msgstr "Ovo mora biti apsolutna putanja bez imena domena. Na primer: '/events/search/'." #: contrib/redirects/models.py:9 msgid "redirect to" msgstr "preusmeri ka" #: contrib/redirects/models.py:10 -msgid "" -"This can be either an absolute path (as above) or a full URL starting with " -"'http://'." -msgstr "" -"Ovo može biti ili apsolutna putanja (kao gore) ili pun URL koji poÄinje sa " -"'http://'." +msgid "This can be either an absolute path (as above) or a full URL starting with 'http://'." +msgstr "Ovo može biti ili apsolutna putanja (kao gore) ili pun URL koji poÄinje sa 'http://'." #: contrib/redirects/models.py:13 msgid "redirect" @@ -4469,30 +4364,33 @@ msgstr "prikazano ime" msgid "sites" msgstr "sajtovi" -#: core/validators.py:20 forms/fields.py:66 +#: core/validators.py:20 +#: forms/fields.py:66 msgid "Enter a valid value." msgstr "Unesite ispravnu vrednost." -#: core/validators.py:87 forms/fields.py:529 +#: core/validators.py:87 +#: forms/fields.py:528 msgid "Enter a valid URL." msgstr "Unesite ispravan URL." -#: core/validators.py:89 forms/fields.py:530 +#: core/validators.py:89 +#: forms/fields.py:529 msgid "This URL appears to be a broken link." msgstr "Ovaj URL izgleda ne vodi nikuda." -#: core/validators.py:123 forms/fields.py:873 -msgid "" -"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens." -msgstr "" -"Unesite isrpavan „slag“, koji se sastoji od slova, brojki, donjih crta ili " -"cirtica." +#: core/validators.py:123 +#: forms/fields.py:877 +msgid "Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens." +msgstr "Unesite isrpavan „slag“, koji se sastoji od slova, brojki, donjih crta ili cirtica." -#: core/validators.py:126 forms/fields.py:866 +#: core/validators.py:126 +#: forms/fields.py:870 msgid "Enter a valid IPv4 address." msgstr "Unesite ispravnu IPv4 adresu." -#: core/validators.py:129 db/models/fields/__init__.py:572 +#: core/validators.py:129 +#: db/models/fields/__init__.py:572 msgid "Enter only digits separated by commas." msgstr "Unesite samo brojke razdvojene zapetama." @@ -4501,40 +4399,37 @@ msgstr "Unesite samo brojke razdvojene zapetama." msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." msgstr "Ovo polje mora da bude %(limit_value)s (trenutno ima %(show_value)s)." -#: core/validators.py:153 forms/fields.py:205 forms/fields.py:257 +#: core/validators.py:153 +#: forms/fields.py:204 +#: forms/fields.py:256 #, python-format msgid "Ensure this value is less than or equal to %(limit_value)s." msgstr "Ova vrednost mora da bude manja od %(limit_value)s. ili taÄno toliko." -#: core/validators.py:158 forms/fields.py:206 forms/fields.py:258 +#: core/validators.py:158 +#: forms/fields.py:205 +#: forms/fields.py:257 #, python-format msgid "Ensure this value is greater than or equal to %(limit_value)s." msgstr "Ova vrednost mora biti veća od %(limit_value)s ili taÄno toliko." #: core/validators.py:164 #, python-format -msgid "" -"Ensure this value has at least %(limit_value)d characters (it has %" -"(show_value)d)." -msgstr "" -"Ovo polje mora da sadrži najmanje %(limit_value)d slovnih mesta (trenutno " -"ima %(show_value)d)." +msgid "Ensure this value has at least %(limit_value)d characters (it has %(show_value)d)." +msgstr "Ovo polje mora da sadrži najmanje %(limit_value)d slovnih mesta (trenutno ima %(show_value)d)." #: core/validators.py:170 #, python-format -msgid "" -"Ensure this value has at most %(limit_value)d characters (it has %" -"(show_value)d)." -msgstr "" -"Ovo polje mora da sadrži najviÅ¡e %(limit_value)d slovnih mesta (trenutno ima " -"%(show_value)d)." +msgid "Ensure this value has at most %(limit_value)d characters (it has %(show_value)d)." +msgstr "Ovo polje mora da sadrži najviÅ¡e %(limit_value)d slovnih mesta (trenutno ima %(show_value)d)." -#: db/models/base.py:822 +#: db/models/base.py:823 #, python-format msgid "%(field_name)s must be unique for %(date_field)s %(lookup)s." msgstr "%(field_name)s mora da bude jedinstven za %(date_field)s %(lookup)s." -#: db/models/base.py:837 db/models/base.py:845 +#: db/models/base.py:838 +#: db/models/base.py:846 #, python-format msgid "%(model_name)s with this %(field_label)s already exists." msgstr "%(model_name)s sa ovom vrednošću %(field_label)s već postoji." @@ -4557,13 +4452,16 @@ msgstr "Ovo polje ne može da ostane prazno." msgid "Field of type: %(field_type)s" msgstr "Ponje tipa: %(field_type)s" -#: db/models/fields/__init__.py:451 db/models/fields/__init__.py:852 -#: db/models/fields/__init__.py:961 db/models/fields/__init__.py:972 -#: db/models/fields/__init__.py:999 +#: db/models/fields/__init__.py:451 +#: db/models/fields/__init__.py:860 +#: db/models/fields/__init__.py:969 +#: db/models/fields/__init__.py:980 +#: db/models/fields/__init__.py:1007 msgid "Integer" msgstr "Ceo broj" -#: db/models/fields/__init__.py:455 db/models/fields/__init__.py:850 +#: db/models/fields/__init__.py:455 +#: db/models/fields/__init__.py:858 msgid "This value must be an integer." msgstr "Ova vrednost mora biti celobrojna." @@ -4575,7 +4473,8 @@ msgstr "Ova vrednost mora biti True ili False." msgid "Boolean (Either True or False)" msgstr "Bulova vrednost (True ili False)" -#: db/models/fields/__init__.py:539 db/models/fields/__init__.py:982 +#: db/models/fields/__init__.py:539 +#: db/models/fields/__init__.py:990 #, python-format msgid "String (up to %(max_length)s)" msgstr "String (najviÅ¡e %(max_length)s znakova)" @@ -4617,44 +4516,45 @@ msgstr "Decimalni broj" msgid "E-mail address" msgstr "Imejl adresa" -#: db/models/fields/__init__.py:799 db/models/fields/files.py:220 +#: db/models/fields/__init__.py:807 +#: db/models/fields/files.py:220 #: db/models/fields/files.py:331 msgid "File path" msgstr "Putanja fajla" -#: db/models/fields/__init__.py:822 +#: db/models/fields/__init__.py:830 msgid "This value must be a float." msgstr "Ova vrednost mora biti broj sa klizećom zapetom" -#: db/models/fields/__init__.py:824 +#: db/models/fields/__init__.py:832 msgid "Floating point number" msgstr "Broj sa pokrenom zapetom" -#: db/models/fields/__init__.py:883 +#: db/models/fields/__init__.py:891 msgid "Big (8 byte) integer" msgstr "Veliki ceo broj" -#: db/models/fields/__init__.py:912 +#: db/models/fields/__init__.py:920 msgid "This value must be either None, True or False." msgstr "Ova vrednost mora biti ili None, ili True, ili False." -#: db/models/fields/__init__.py:914 +#: db/models/fields/__init__.py:922 msgid "Boolean (Either True, False or None)" msgstr "Bulova vrednost (True, False ili None)" -#: db/models/fields/__init__.py:1005 +#: db/models/fields/__init__.py:1013 msgid "Text" msgstr "Tekst" -#: db/models/fields/__init__.py:1021 +#: db/models/fields/__init__.py:1029 msgid "Time" msgstr "Vreme" -#: db/models/fields/__init__.py:1025 +#: db/models/fields/__init__.py:1033 msgid "Enter a valid time in HH:MM[:ss[.uuuuuu]] format." msgstr "Unesite ispravno vreme u formatu ČČ:MM[:ss[.uuuuuu]]." -#: db/models/fields/__init__.py:1109 +#: db/models/fields/__init__.py:1125 msgid "XML text" msgstr "XML tekst" @@ -4667,26 +4567,22 @@ msgstr "Objekat klase %(model)s sa primarnim kljuÄem %(pk)r ne postoji." msgid "Foreign Key (type determined by related field)" msgstr "Strani kljuÄ (tip odreÄ‘uje referentno polje)" -#: db/models/fields/related.py:918 +#: db/models/fields/related.py:919 msgid "One-to-one relationship" msgstr "Relacija jedan na jedan" -#: db/models/fields/related.py:980 +#: db/models/fields/related.py:981 msgid "Many-to-many relationship" msgstr "Relacija viÅ¡e na viÅ¡e" -#: db/models/fields/related.py:1000 -msgid "" -"Hold down \"Control\", or \"Command\" on a Mac, to select more than one." -msgstr "" -"Držite „Control“, ili „Command“ na Mac-u da biste obeležili viÅ¡e od jedne " -"stavke." +#: db/models/fields/related.py:1001 +msgid "Hold down \"Control\", or \"Command\" on a Mac, to select more than one." +msgstr "Držite „Control“, ili „Command“ na Mac-u da biste obeležili viÅ¡e od jedne stavke." -#: db/models/fields/related.py:1061 +#: db/models/fields/related.py:1062 #, python-format msgid "Please enter valid %(self)s IDs. The value %(value)r is invalid." -msgid_plural "" -"Please enter valid %(self)s IDs. The values %(value)r are invalid." +msgid_plural "Please enter valid %(self)s IDs. The values %(value)r are invalid." msgstr[0] "Unesite ispravan %(self)s IDs. Vrednost %(value)r je neispravna." msgstr[1] "Unesite ispravan %(self)s IDs. Vrednosti %(value)r su neispravne." msgstr[2] "Unesite ispravan %(self)s IDs. Vrednosti %(value)r su neispravne." @@ -4695,80 +4591,79 @@ msgstr[2] "Unesite ispravan %(self)s IDs. Vrednosti %(value)r su neispravne." msgid "This field is required." msgstr "Ovo polje se mora popuniti." -#: forms/fields.py:204 +#: forms/fields.py:203 msgid "Enter a whole number." msgstr "Unesite ceo broj." -#: forms/fields.py:235 forms/fields.py:256 +#: forms/fields.py:234 +#: forms/fields.py:255 msgid "Enter a number." msgstr "Unesite broj." -#: forms/fields.py:259 +#: forms/fields.py:258 #, python-format msgid "Ensure that there are no more than %s digits in total." msgstr "Ne sme biti ukupno viÅ¡e od %s cifara. Proverite." -#: forms/fields.py:260 +#: forms/fields.py:259 #, python-format msgid "Ensure that there are no more than %s decimal places." msgstr "Ne sme biti ukupno viÅ¡e od %s decimalnih mesta. Proverite." -#: forms/fields.py:261 +#: forms/fields.py:260 #, python-format msgid "Ensure that there are no more than %s digits before the decimal point." msgstr "Ne sme biti ukupno viÅ¡e od %s cifara pre zapete. Proverite." -#: forms/fields.py:323 forms/fields.py:838 +#: forms/fields.py:322 +#: forms/fields.py:837 msgid "Enter a valid date." msgstr "Unesite ispravan datum." -#: forms/fields.py:351 forms/fields.py:839 +#: forms/fields.py:350 +#: forms/fields.py:838 msgid "Enter a valid time." msgstr "Unesite ispravno vreme" -#: forms/fields.py:377 +#: forms/fields.py:376 msgid "Enter a valid date/time." msgstr "Unesite ispravan datum/vreme." -#: forms/fields.py:435 +#: forms/fields.py:434 msgid "No file was submitted. Check the encoding type on the form." msgstr "Fajl nije prebaÄen. Proverite tip enkodiranja formulara." -#: forms/fields.py:436 +#: forms/fields.py:435 msgid "No file was submitted." msgstr "Fajl nije prebaÄen." -#: forms/fields.py:437 +#: forms/fields.py:436 msgid "The submitted file is empty." msgstr "PrebaÄen fajl je prazan." -#: forms/fields.py:438 +#: forms/fields.py:437 #, python-format -msgid "" -"Ensure this filename has at most %(max)d characters (it has %(length)d)." -msgstr "" -"Naziv fajla mora da sadrži bar %(max)d slovnih mesta (trenutno ima %(length)" -"d)." +msgid "Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr "Naziv fajla mora da sadrži bar %(max)d slovnih mesta (trenutno ima %(length)d)." -#: forms/fields.py:473 -msgid "" -"Upload a valid image. The file you uploaded was either not an image or a " -"corrupted image." -msgstr "" -"Prebacite ispravan fajl. Fajl koji je prebaÄen ili nije slika, ili je " -"oÅ¡tećen." +#: forms/fields.py:472 +msgid "Upload a valid image. The file you uploaded was either not an image or a corrupted image." +msgstr "Prebacite ispravan fajl. Fajl koji je prebaÄen ili nije slika, ili je oÅ¡tećen." -#: forms/fields.py:596 forms/fields.py:671 +#: forms/fields.py:595 +#: forms/fields.py:670 #, python-format msgid "Select a valid choice. %(value)s is not one of the available choices." -msgstr "" -"%(value)s nije meÄ‘u ponuÄ‘enim vrednostima. Odaberite jednu od ponuÄ‘enih." +msgstr "%(value)s nije meÄ‘u ponuÄ‘enim vrednostima. Odaberite jednu od ponuÄ‘enih." -#: forms/fields.py:672 forms/fields.py:734 forms/models.py:1002 +#: forms/fields.py:671 +#: forms/fields.py:733 +#: forms/models.py:1002 msgid "Enter a list of values." msgstr "Unesite listu vrednosti." -#: forms/formsets.py:298 forms/formsets.py:300 +#: forms/formsets.py:296 +#: forms/formsets.py:298 msgid "Order" msgstr "Redosled" @@ -4780,17 +4675,12 @@ msgstr "Ispravite dupliran sadržaj za polja: %(field)s." #: forms/models.py:566 #, python-format msgid "Please correct the duplicate data for %(field)s, which must be unique." -msgstr "" -"Ispravite dupliran sadržaj za polja: %(field)s, koji mora da bude jedinstven." +msgstr "Ispravite dupliran sadržaj za polja: %(field)s, koji mora da bude jedinstven." #: forms/models.py:572 #, python-format -msgid "" -"Please correct the duplicate data for %(field_name)s which must be unique " -"for the %(lookup)s in %(date_field)s." -msgstr "" -"Ispravite dupliran sadržaj za polja: %(field_name)s, koji mora da bude " -"jedinstven za %(lookup)s u %(date_field)s." +msgid "Please correct the duplicate data for %(field_name)s which must be unique for the %(lookup)s in %(date_field)s." +msgstr "Ispravite dupliran sadržaj za polja: %(field_name)s, koji mora da bude jedinstven za %(lookup)s u %(date_field)s." #: forms/models.py:580 msgid "Please correct the duplicate values below." @@ -4929,23 +4819,28 @@ msgstr "januar" msgid "February" msgstr "februar" -#: utils/dates.py:18 utils/dates.py:31 +#: utils/dates.py:18 +#: utils/dates.py:31 msgid "March" msgstr "mart" -#: utils/dates.py:18 utils/dates.py:31 +#: utils/dates.py:18 +#: utils/dates.py:31 msgid "April" msgstr "april" -#: utils/dates.py:18 utils/dates.py:31 +#: utils/dates.py:18 +#: utils/dates.py:31 msgid "May" msgstr "maj" -#: utils/dates.py:18 utils/dates.py:31 +#: utils/dates.py:18 +#: utils/dates.py:31 msgid "June" msgstr "jun" -#: utils/dates.py:19 utils/dates.py:31 +#: utils/dates.py:19 +#: utils/dates.py:31 msgid "July" msgstr "jul" @@ -5105,23 +5000,23 @@ msgstr "%(number)d %(type)s" msgid ", %(number)d %(type)s" msgstr ", %(number)d %(type)s" -#: utils/translation/trans_real.py:518 +#: utils/translation/trans_real.py:519 msgid "DATE_FORMAT" msgstr "j. F Y." -#: utils/translation/trans_real.py:519 +#: utils/translation/trans_real.py:520 msgid "DATETIME_FORMAT" msgstr "j. F Y. H:i T" -#: utils/translation/trans_real.py:520 +#: utils/translation/trans_real.py:521 msgid "TIME_FORMAT" msgstr "G:i" -#: utils/translation/trans_real.py:541 +#: utils/translation/trans_real.py:542 msgid "YEAR_MONTH_FORMAT" msgstr "F Y." -#: utils/translation/trans_real.py:542 +#: utils/translation/trans_real.py:543 msgid "MONTH_DAY_FORMAT" msgstr "j. F" @@ -5139,3 +5034,4 @@ msgstr "%(verbose_name)s je uspeÅ¡no ažuriran." #, python-format msgid "The %(verbose_name)s was deleted." msgstr "%(verbose_name)s je obrisan." + diff --git a/django/conf/locale/sr_Latn/formats.py b/django/conf/locale/sr_Latn/formats.py index 63a20f4574..cb0478ed0f 100644 --- a/django/conf/locale/sr_Latn/formats.py +++ b/django/conf/locale/sr_Latn/formats.py @@ -11,9 +11,9 @@ SHORT_DATE_FORMAT = 'j.m.Y.' SHORT_DATETIME_FORMAT = 'j.m.Y. H:i' FIRST_DAY_OF_WEEK = 1 DATE_INPUT_FORMATS = ( - '%Y-%m-%d', # '2006-10-25' '%d.%m.%Y.', '%d.%m.%y.', # '25.10.2006.', '25.10.06.' '%d. %m. %Y.', '%d. %m. %y.', # '25. 10. 2006.', '25. 10. 06.' + '%Y-%m-%d', # '2006-10-25' # '%d. %b %y.', '%d. %B %y.', # '25. Oct 06.', '25. October 06.' # '%d. %b \'%y.', '%d. %B \'%y.', # '25. Oct '06.', '25. October '06.' # '%d. %b %Y.', '%d. %B %Y.', # '25. Oct 2006.', '25. October 2006.' @@ -23,9 +23,6 @@ TIME_INPUT_FORMATS = ( '%H:%M', # '14:30' ) DATETIME_INPUT_FORMATS = ( - '%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59' - '%Y-%m-%d %H:%M', # '2006-10-25 14:30' - '%Y-%m-%d', # '2006-10-25' '%d.%m.%Y. %H:%M:%S', # '25.10.2006. 14:30:59' '%d.%m.%Y. %H:%M', # '25.10.2006. 14:30' '%d.%m.%Y.', # '25.10.2006.' @@ -38,7 +35,10 @@ DATETIME_INPUT_FORMATS = ( '%d. %m. %y. %H:%M:%S', # '25. 10. 06. 14:30:59' '%d. %m. %y. %H:%M', # '25. 10. 06. 14:30' '%d. %m. %y.', # '25. 10. 06.' + '%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59' + '%Y-%m-%d %H:%M', # '2006-10-25 14:30' + '%Y-%m-%d', # '2006-10-25' ) -DECIMAL_SEPARATOR = '.' -THOUSAND_SEPARATOR = ',' +DECIMAL_SEPARATOR = ',' +THOUSAND_SEPARATOR = '.' NUMBER_GROUPING = 3 diff --git a/django/conf/project_template/settings.py b/django/conf/project_template/settings.py index ee4c9b5e96..c49df24ce5 100644 --- a/django/conf/project_template/settings.py +++ b/django/conf/project_template/settings.py @@ -91,4 +91,6 @@ INSTALLED_APPS = ( 'django.contrib.messages', # Uncomment the next line to enable the admin: # 'django.contrib.admin', + # Uncomment the next line to enable admin documentation: + # 'django.contrib.admindocs', ) diff --git a/django/conf/project_template/urls.py b/django/conf/project_template/urls.py index dfb49d3bdc..3d0ff636a5 100644 --- a/django/conf/project_template/urls.py +++ b/django/conf/project_template/urls.py @@ -8,8 +8,7 @@ urlpatterns = patterns('', # Example: # (r'^{{ project_name }}/', include('{{ project_name }}.foo.urls')), - # Uncomment the admin/doc line below and add 'django.contrib.admindocs' - # to INSTALLED_APPS to enable admin documentation: + # Uncomment the admin/doc line below to enable admin documentation: # (r'^admin/doc/', include('django.contrib.admindocs.urls')), # Uncomment the next line to enable the admin: diff --git a/django/contrib/admin/media/css/base.css b/django/contrib/admin/media/css/base.css index da502f357a..9c1f71f810 100644 --- a/django/contrib/admin/media/css/base.css +++ b/django/contrib/admin/media/css/base.css @@ -445,6 +445,14 @@ ul.messagelist li { background: #ffc url(../img/admin/icon_success.gif) 5px .3em no-repeat; } +ul.messagelist li.warning{ + background-image: url(../img/admin/icon_alert.gif); +} + +ul.messagelist li.error{ + background-image: url(../img/admin/icon_error.gif); +} + .errornote { font-size: 12px !important; display: block; @@ -470,6 +478,11 @@ ul.errorlist { background: red url(../img/admin/icon_alert.gif) 5px .3em no-repeat; } +.errorlist li a { + color: white; + text-decoration: underline; +} + td ul.errorlist { margin: 0 !important; padding: 0 !important; diff --git a/django/contrib/admin/media/css/changelists.css b/django/contrib/admin/media/css/changelists.css index 99ff8bc0cf..282833c440 100644 --- a/django/contrib/admin/media/css/changelists.css +++ b/django/contrib/admin/media/css/changelists.css @@ -9,6 +9,8 @@ width: 100%; } +.change-list .hiddenfields { display:none; } + .change-list .filtered table { border-right: 1px solid #ddd; } @@ -21,7 +23,7 @@ background: white url(../img/admin/changelist-bg.gif) top right repeat-y !important; } -.change-list .filtered table, .change-list .filtered .paginator, .filtered #toolbar, .filtered div.xfull { +.change-list .filtered .results, .change-list .filtered .paginator, .filtered #toolbar, .filtered div.xfull { margin-right: 160px !important; width: auto !important; } diff --git a/django/contrib/admin/media/js/dateparse.js b/django/contrib/admin/media/js/dateparse.js index e1c870e146..3cb82dea13 100644 --- a/django/contrib/admin/media/js/dateparse.js +++ b/django/contrib/admin/media/js/dateparse.js @@ -100,8 +100,9 @@ var dateParsePatterns = [ { re: /^(\d{1,2})(?:st|nd|rd|th)? (\w+)$/i, handler: function(bits) { var d = new Date(); - d.setDate(parseInt(bits[1], 10)); + d.setDate(1); d.setMonth(parseMonth(bits[2])); + d.setDate(parseInt(bits[1], 10)); return d; } }, @@ -109,9 +110,10 @@ var dateParsePatterns = [ { re: /^(\d{1,2})(?:st|nd|rd|th)? (\w+),? (\d{4})$/i, handler: function(bits) { var d = new Date(); - d.setDate(parseInt(bits[1], 10)); - d.setMonth(parseMonth(bits[2])); + d.setDate(1); d.setYear(bits[3]); + d.setMonth(parseMonth(bits[2])); + d.setDate(parseInt(bits[1], 10)); return d; } }, @@ -119,8 +121,9 @@ var dateParsePatterns = [ { re: /^(\w+) (\d{1,2})(?:st|nd|rd|th)?$/i, handler: function(bits) { var d = new Date(); - d.setDate(parseInt(bits[2], 10)); + d.setDate(1); d.setMonth(parseMonth(bits[1])); + d.setDate(parseInt(bits[2], 10)); return d; } }, @@ -128,9 +131,10 @@ var dateParsePatterns = [ { re: /^(\w+) (\d{1,2})(?:st|nd|rd|th)?,? (\d{4})$/i, handler: function(bits) { var d = new Date(); - d.setDate(parseInt(bits[2], 10)); - d.setMonth(parseMonth(bits[1])); + d.setDate(1); d.setYear(bits[3]); + d.setMonth(parseMonth(bits[1])); + d.setDate(parseInt(bits[2], 10)); return d; } }, @@ -158,9 +162,10 @@ var dateParsePatterns = [ { re: /(\d{1,2})\/(\d{1,2})\/(\d{4})/, handler: function(bits) { var d = new Date(); + d.setDate(1); d.setYear(bits[3]); - d.setDate(parseInt(bits[2], 10)); d.setMonth(parseInt(bits[1], 10) - 1); // Because months indexed from 0 + d.setDate(parseInt(bits[2], 10)); return d; } }, @@ -168,6 +173,7 @@ var dateParsePatterns = [ { re: /(\d{4})-(\d{1,2})-(\d{1,2})/, handler: function(bits) { var d = new Date(); + d.setDate(1); d.setYear(parseInt(bits[1])); d.setMonth(parseInt(bits[2], 10) - 1); d.setDate(parseInt(bits[3], 10)); diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index abbe14c948..ffa3a533e9 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -108,7 +108,13 @@ class BaseModelAdmin(object): # rendered output. formfield can be None if it came from a # OneToOneField with parent_link=True or a M2M intermediary. if formfield and db_field.name not in self.raw_id_fields: - formfield.widget = widgets.RelatedFieldWidgetWrapper(formfield.widget, db_field.rel, self.admin_site) + related_modeladmin = self.admin_site._registry.get( + db_field.rel.to) + can_add_related = bool(related_modeladmin and + related_modeladmin.has_add_permission(request)) + formfield.widget = widgets.RelatedFieldWidgetWrapper( + formfield.widget, db_field.rel, self.admin_site, + can_add_related=can_add_related) return formfield @@ -502,7 +508,7 @@ class ModelAdmin(BaseModelAdmin): # Convert the actions into a SortedDict keyed by name # and sorted by description. - actions.sort(lambda a,b: cmp(a[2].lower(), b[2].lower())) + actions.sort(key=lambda k: k[2].lower()) actions = SortedDict([ (name, (func, name, desc)) for func, name, desc in actions @@ -755,7 +761,7 @@ class ModelAdmin(BaseModelAdmin): if isinstance(response, HttpResponse): return response else: - return HttpResponseRedirect(".") + return HttpResponseRedirect(request.get_full_path()) else: msg = _("No action selected.") self.message_user(request, msg) diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py index f4b6953347..ddc4f8c84b 100644 --- a/django/contrib/admin/sites.py +++ b/django/contrib/admin/sites.py @@ -380,11 +380,11 @@ class AdminSite(object): # Sort the apps alphabetically. app_list = app_dict.values() - app_list.sort(lambda x, y: cmp(x['name'], y['name'])) + app_list.sort(key=lambda x: x['name']) # Sort the models alphabetically within each app. for app in app_list: - app['models'].sort(lambda x, y: cmp(x['name'], y['name'])) + app['models'].sort(key=lambda x: x['name']) context = { 'title': _('Site administration'), @@ -445,7 +445,7 @@ class AdminSite(object): if not app_dict: raise http.Http404('The requested admin page does not exist.') # Sort the models alphabetically within each app. - app_dict['models'].sort(lambda x, y: cmp(x['name'], y['name'])) + app_dict['models'].sort(key=lambda x: x['name']) context = { 'title': _('%s administration') % app_instance.verbose_name, 'app_list': [app_dict], diff --git a/django/contrib/admin/templates/admin/auth/user/add_form.html b/django/contrib/admin/templates/admin/auth/user/add_form.html index b57f59b82e..c8889eb069 100644 --- a/django/contrib/admin/templates/admin/auth/user/add_form.html +++ b/django/contrib/admin/templates/admin/auth/user/add_form.html @@ -2,8 +2,11 @@ {% load i18n %} {% block form_top %} + {% if not is_popup %} <p>{% trans "First, enter a username and password. Then, you'll be able to edit more user options." %}</p> - <input type="hidden" name="_continue" value="1" /> + {% else %} + <p>{% trans "Enter a username and password." %}</p> + {% endif %} {% endblock %} {% block after_field_sets %} diff --git a/django/contrib/admin/templates/admin/base.html b/django/contrib/admin/templates/admin/base.html index 3fd9eb3770..4221e2d1a7 100644 --- a/django/contrib/admin/templates/admin/base.html +++ b/django/contrib/admin/templates/admin/base.html @@ -56,7 +56,9 @@ {% endif %} {% if messages %} - <ul class="messagelist">{% for message in messages %}<li>{{ message }}</li>{% endfor %}</ul> + <ul class="messagelist">{% for message in messages %} + <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li> + {% endfor %}</ul> {% endif %} <!-- Content --> diff --git a/django/contrib/admin/templates/admin/change_list_results.html b/django/contrib/admin/templates/admin/change_list_results.html index 0efcc9b24a..6d4de2df0e 100644 --- a/django/contrib/admin/templates/admin/change_list_results.html +++ b/django/contrib/admin/templates/admin/change_list_results.html @@ -1,4 +1,10 @@ +{% if result_hidden_fields %} +<div class="hiddenfields"> {# DIV for HTML validation #} +{% for item in result_hidden_fields %}{{ item }}{% endfor %} +</div> +{% endif %} {% if results %} +<div class="results"> <table cellspacing="0" id="result_list"> <thead> <tr> @@ -14,4 +20,5 @@ {% endfor %} </tbody> </table> +</div> {% endif %} diff --git a/django/contrib/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py index 565db32251..c05af0b7fe 100644 --- a/django/contrib/admin/templatetags/admin_list.py +++ b/django/contrib/admin/templatetags/admin_list.py @@ -189,7 +189,7 @@ def items_for_result(cl, result, form): else: result_repr = conditional_escape(result_repr) yield mark_safe(u'<td%s>%s</td>' % (row_class, result_repr)) - if form: + if form and not form[cl.model._meta.pk.name].is_hidden: yield mark_safe(u'<td>%s</td>' % force_unicode(form[cl.model._meta.pk.name])) def results(cl): @@ -200,11 +200,18 @@ def results(cl): for res in cl.result_list: yield list(items_for_result(cl, res, None)) +def result_hidden_fields(cl): + if cl.formset: + for res, form in zip(cl.result_list, cl.formset.forms): + if form[cl.model._meta.pk.name].is_hidden: + yield mark_safe(force_unicode(form[cl.model._meta.pk.name])) + def result_list(cl): """ Displays the headers and data list together """ return {'cl': cl, + 'result_hidden_fields': list(result_hidden_fields(cl)), 'result_headers': list(result_headers(cl)), 'results': list(results(cl))} result_list = register.inclusion_tag("admin/change_list_results.html")(result_list) diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py index 1d321d0620..2c7ac5c794 100644 --- a/django/contrib/admin/widgets.py +++ b/django/contrib/admin/widgets.py @@ -154,9 +154,9 @@ class ForeignKeyRawIdWidget(forms.TextInput): key = self.rel.get_related_field().name try: obj = self.rel.to._default_manager.using(self.db).get(**{key: value}) - except self.rel.to.DoesNotExist: + return ' <strong>%s</strong>' % escape(truncate_words(obj, 14)) + except (ValueError, self.rel.to.DoesNotExist): return '' - return ' <strong>%s</strong>' % escape(truncate_words(obj, 14)) class ManyToManyRawIdWidget(ForeignKeyRawIdWidget): """ @@ -169,7 +169,7 @@ class ManyToManyRawIdWidget(ForeignKeyRawIdWidget): def render(self, name, value, attrs=None): attrs['class'] = 'vManyToManyRawIdAdminField' if value: - value = ','.join([str(v) for v in value]) + value = ','.join([force_unicode(v) for v in value]) else: value = '' return super(ManyToManyRawIdWidget, self).render(name, value, attrs) @@ -205,13 +205,18 @@ class RelatedFieldWidgetWrapper(forms.Widget): This class is a wrapper to a given widget to add the add icon for the admin interface. """ - def __init__(self, widget, rel, admin_site): + def __init__(self, widget, rel, admin_site, can_add_related=None): self.is_hidden = widget.is_hidden self.needs_multipart_form = widget.needs_multipart_form self.attrs = widget.attrs self.choices = widget.choices self.widget = widget self.rel = rel + # Backwards compatible check for whether a user can add related + # objects. + if can_add_related is None: + can_add_related = rel_to in self.admin_site._registry + self.can_add_related = can_add_related # so we can check if the related object is registered with this AdminSite self.admin_site = admin_site @@ -236,7 +241,7 @@ class RelatedFieldWidgetWrapper(forms.Widget): related_url = '%s%s/%s/add/' % info self.widget.choices = self.choices output = [self.widget.render(name, value, *args, **kwargs)] - if rel_to in self.admin_site._registry: # If the related object has an admin interface: + if self.can_add_related: # TODO: "id_" is hard-coded here. This should instead use the correct # API to determine the ID dynamically. output.append(u'<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' % \ diff --git a/django/contrib/admindocs/templates/admin_doc/template_filter_index.html b/django/contrib/admindocs/templates/admin_doc/template_filter_index.html index 53383edc23..7470762775 100644 --- a/django/contrib/admindocs/templates/admin_doc/template_filter_index.html +++ b/django/contrib/admindocs/templates/admin_doc/template_filter_index.html @@ -15,7 +15,7 @@ <h2>{% firstof library.grouper "Built-in filters" %}</h2> {% if library.grouper %}<p class="small quiet">To use these filters, put <code>{% templatetag openblock %} load {{ library.grouper }} {% templatetag closeblock %}</code> in your template before using the filter.</p><hr />{% endif %} {% for filter in library.list|dictsort:"name" %} - <h3 id="{{ filter.name }}">{{ filter.name }}</h3> + <h3 id="{{ library.grouper|default_if_none:"built_in" }}-{{ filter.name }}">{{ filter.name }}</h3> <p>{{ filter.title }}</p> <p>{{ filter.body }}</p> {% if not forloop.last %}<hr />{% endif %} @@ -36,7 +36,7 @@ <h2>{% firstof library.grouper "Built-in filters" %}</h2> <ul> {% for filter in library.list|dictsort:"name" %} - <li><a href="#{{ filter.name }}">{{ filter.name }}</a></li> + <li><a href="#{{ library.grouper|default_if_none:"built_in" }}-{{ filter.name }}">{{ filter.name }}</a></li> {% endfor %} </ul> </div> diff --git a/django/contrib/admindocs/templates/admin_doc/template_tag_index.html b/django/contrib/admindocs/templates/admin_doc/template_tag_index.html index a4ad66fd96..774130bd70 100644 --- a/django/contrib/admindocs/templates/admin_doc/template_tag_index.html +++ b/django/contrib/admindocs/templates/admin_doc/template_tag_index.html @@ -15,7 +15,7 @@ <h2>{% firstof library.grouper "Built-in tags" %}</h2> {% if library.grouper %}<p class="small quiet">To use these tags, put <code>{% templatetag openblock %} load {{ library.grouper }} {% templatetag closeblock %}</code> in your template before using the tag.</p><hr />{% endif %} {% for tag in library.list|dictsort:"name" %} - <h3 id="{{ tag.name }}">{{ tag.name }}</h3> + <h3 id="{{ library.grouper|default_if_none:"built_in" }}-{{ tag.name }}">{{ tag.name }}</h3> <h4>{{ tag.title }}</h4> <p>{{ tag.body }}</p> {% if not forloop.last %}<hr />{% endif %} @@ -36,7 +36,7 @@ <h2>{% firstof library.grouper "Built-in tags" %}</h2> <ul> {% for tag in library.list|dictsort:"name" %} - <li><a href="#{{ tag.name }}">{{ tag.name }}</a></li> + <li><a href="#{{ library.grouper|default_if_none:"built_in" }}-{{ tag.name }}">{{ tag.name }}</a></li> {% endfor %} </ul> </div> diff --git a/django/contrib/admindocs/views.py b/django/contrib/admindocs/views.py index e154c9299a..5bfa0f7184 100644 --- a/django/contrib/admindocs/views.py +++ b/django/contrib/admindocs/views.py @@ -54,7 +54,9 @@ def template_tag_index(request): load_all_installed_template_libraries() tags = [] - for module_name, library in template.libraries.items(): + app_libs = template.libraries.items() + builtin_libs = [(None, lib) for lib in template.builtins] + for module_name, library in builtin_libs + app_libs: for tag_name, tag_func in library.tags.items(): title, body, metadata = utils.parse_docstring(tag_func.__doc__) if title: @@ -87,7 +89,9 @@ def template_filter_index(request): load_all_installed_template_libraries() filters = [] - for module_name, library in template.libraries.items(): + app_libs = template.libraries.items() + builtin_libs = [(None, lib) for lib in template.builtins] + for module_name, library in builtin_libs + app_libs: for filter_name, filter_func in library.filters.items(): title, body, metadata = utils.parse_docstring(filter_func.__doc__) if title: diff --git a/django/contrib/auth/decorators.py b/django/contrib/auth/decorators.py index 09dcf42e42..7d7a0cddb7 100644 --- a/django/contrib/auth/decorators.py +++ b/django/contrib/auth/decorators.py @@ -30,13 +30,14 @@ def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIE return decorator -def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME): +def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None): """ Decorator for views that checks that the user is logged in, redirecting to the log-in page if necessary. """ actual_decorator = user_passes_test( lambda u: u.is_authenticated(), + login_url=login_url, redirect_field_name=redirect_field_name ) if function: diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py index 086acf3349..cc0f7071c3 100644 --- a/django/contrib/auth/forms.py +++ b/django/contrib/auth/forms.py @@ -52,6 +52,12 @@ class UserChangeForm(forms.ModelForm): class Meta: model = User + def __init__(self, *args, **kwargs): + super(UserChangeForm, self).__init__(*args, **kwargs) + f = self.fields.get('user_permissions', None) + if f is not None: + f.queryset = f.queryset.select_related('content_type') + class AuthenticationForm(forms.Form): """ Base class for authenticating users. Extend this to get a form that accepts @@ -111,7 +117,7 @@ class PasswordResetForm(forms.Form): return email def save(self, domain_override=None, email_template_name='registration/password_reset_email.html', - use_https=False, token_generator=default_token_generator): + use_https=False, token_generator=default_token_generator, from_email=None): """ Generates a one-use only link for resetting password and sends to the user """ @@ -134,7 +140,7 @@ class PasswordResetForm(forms.Form): 'protocol': use_https and 'https' or 'http', } send_mail(_("Password reset on %s") % site_name, - t.render(Context(c)), None, [user.email]) + t.render(Context(c)), from_email, [user.email]) class SetPasswordForm(forms.Form): """ diff --git a/django/contrib/auth/tests/__init__.py b/django/contrib/auth/tests/__init__.py index 965ea2d6be..a1d02b6014 100644 --- a/django/contrib/auth/tests/__init__.py +++ b/django/contrib/auth/tests/__init__.py @@ -1,7 +1,7 @@ from django.contrib.auth.tests.auth_backends import BackendTest, RowlevelBackendTest, AnonymousUserBackendTest, NoAnonymousUserBackendTest from django.contrib.auth.tests.basic import BASIC_TESTS from django.contrib.auth.tests.decorators import LoginRequiredTestCase -from django.contrib.auth.tests.forms import FORM_TESTS +from django.contrib.auth.tests.forms import UserCreationFormTest, AuthenticationFormTest, SetPasswordFormTest, PasswordChangeFormTest, UserChangeFormTest, PasswordResetFormTest from django.contrib.auth.tests.remote_user \ import RemoteUserTest, RemoteUserNoCreateTest, RemoteUserCustomTest from django.contrib.auth.tests.models import ProfileTestCase @@ -13,6 +13,5 @@ from django.contrib.auth.tests.views \ __test__ = { 'BASIC_TESTS': BASIC_TESTS, - 'FORM_TESTS': FORM_TESTS, 'TOKEN_GENERATOR_TESTS': TOKEN_GENERATOR_TESTS, } diff --git a/django/contrib/auth/tests/decorators.py b/django/contrib/auth/tests/decorators.py index 7efd9d8ccf..0240a76eb7 100644 --- a/django/contrib/auth/tests/decorators.py +++ b/django/contrib/auth/tests/decorators.py @@ -1,12 +1,12 @@ -from unittest import TestCase - from django.contrib.auth.decorators import login_required +from django.contrib.auth.tests.views import AuthViewsTestCase - -class LoginRequiredTestCase(TestCase): +class LoginRequiredTestCase(AuthViewsTestCase): """ Tests the login_required decorators """ + urls = 'django.contrib.auth.tests.urls' + def testCallable(self): """ Check that login_required is assignable to callable objects. @@ -22,4 +22,24 @@ class LoginRequiredTestCase(TestCase): """ def normal_view(request): pass - login_required(normal_view)
\ No newline at end of file + login_required(normal_view) + + def testLoginRequired(self, view_url='/login_required/', login_url='/login/'): + """ + Check that login_required works on a simple view wrapped in a + login_required decorator. + """ + response = self.client.get(view_url) + self.assertEqual(response.status_code, 302) + self.assert_(login_url in response['Location']) + self.login() + response = self.client.get(view_url) + self.assertEqual(response.status_code, 200) + + def testLoginRequiredNextUrl(self): + """ + Check that login_required works on a simple view wrapped in a + login_required decorator with a login_url set. + """ + self.testLoginRequired(view_url='/login_required_login_url/', + login_url='/somewhere/')
\ No newline at end of file diff --git a/django/contrib/auth/tests/forms.py b/django/contrib/auth/tests/forms.py index b691c560be..5aa49e09c3 100644 --- a/django/contrib/auth/tests/forms.py +++ b/django/contrib/auth/tests/forms.py @@ -1,231 +1,252 @@ - -FORM_TESTS = """ ->>> from django.contrib.auth.models import User ->>> from django.contrib.auth.forms import UserCreationForm, AuthenticationForm ->>> from django.contrib.auth.forms import PasswordChangeForm, SetPasswordForm - -# The user already exists. - ->>> user = User.objects.create_user("jsmith", "jsmith@example.com", "test123") ->>> data = { -... 'username': 'jsmith', -... 'password1': 'test123', -... 'password2': 'test123', -... } ->>> form = UserCreationForm(data) ->>> form.is_valid() -False ->>> form["username"].errors -[u'A user with that username already exists.'] - -# The username contains invalid data. - ->>> data = { -... 'username': 'jsmith!', -... 'password1': 'test123', -... 'password2': 'test123', -... } ->>> form = UserCreationForm(data) ->>> form.is_valid() -False ->>> form["username"].errors -[u'This value may contain only letters, numbers and @/./+/-/_ characters.'] - -# The verification password is incorrect. - ->>> data = { -... 'username': 'jsmith2', -... 'password1': 'test123', -... 'password2': 'test', -... } ->>> form = UserCreationForm(data) ->>> form.is_valid() -False ->>> form["password2"].errors -[u"The two password fields didn't match."] - -# One (or both) passwords weren't given - ->>> data = {'username': 'jsmith2'} ->>> form = UserCreationForm(data) ->>> form.is_valid() -False ->>> form['password1'].errors -[u'This field is required.'] ->>> form['password2'].errors -[u'This field is required.'] - ->>> data['password2'] = 'test123' ->>> form = UserCreationForm(data) ->>> form.is_valid() -False ->>> form['password1'].errors -[u'This field is required.'] - -# The success case. - ->>> data = { -... 'username': 'jsmith2@example.com', -... 'password1': 'test123', -... 'password2': 'test123', -... } ->>> form = UserCreationForm(data) ->>> form.is_valid() -True ->>> form.save() -<User: jsmith2@example.com> - -# The user submits an invalid username. - ->>> data = { -... 'username': 'jsmith_does_not_exist', -... 'password': 'test123', -... } - ->>> form = AuthenticationForm(None, data) ->>> form.is_valid() -False ->>> form.non_field_errors() -[u'Please enter a correct username and password. Note that both fields are case-sensitive.'] - -# The user is inactive. - ->>> data = { -... 'username': 'jsmith', -... 'password': 'test123', -... } ->>> user.is_active = False ->>> user.save() ->>> form = AuthenticationForm(None, data) ->>> form.is_valid() -False ->>> form.non_field_errors() -[u'This account is inactive.'] - ->>> user.is_active = True ->>> user.save() - -# The success case - ->>> form = AuthenticationForm(None, data) ->>> form.is_valid() -True ->>> form.non_field_errors() -[] - -### SetPasswordForm: - -# The two new passwords do not match. - ->>> data = { -... 'new_password1': 'abc123', -... 'new_password2': 'abc', -... } ->>> form = SetPasswordForm(user, data) ->>> form.is_valid() -False ->>> form["new_password2"].errors -[u"The two password fields didn't match."] - -# The success case. - ->>> data = { -... 'new_password1': 'abc123', -... 'new_password2': 'abc123', -... } ->>> form = SetPasswordForm(user, data) ->>> form.is_valid() -True - -### PasswordChangeForm: - -The old password is incorrect. - ->>> data = { -... 'old_password': 'test', -... 'new_password1': 'abc123', -... 'new_password2': 'abc123', -... } ->>> form = PasswordChangeForm(user, data) ->>> form.is_valid() -False ->>> form["old_password"].errors -[u'Your old password was entered incorrectly. Please enter it again.'] - -# The two new passwords do not match. - ->>> data = { -... 'old_password': 'test123', -... 'new_password1': 'abc123', -... 'new_password2': 'abc', -... } ->>> form = PasswordChangeForm(user, data) ->>> form.is_valid() -False ->>> form["new_password2"].errors -[u"The two password fields didn't match."] - -# The success case. - ->>> data = { -... 'old_password': 'test123', -... 'new_password1': 'abc123', -... 'new_password2': 'abc123', -... } ->>> form = PasswordChangeForm(user, data) ->>> form.is_valid() -True - -# Regression test - check the order of fields: - ->>> PasswordChangeForm(user, {}).fields.keys() -['old_password', 'new_password1', 'new_password2'] - -### UserChangeForm - ->>> from django.contrib.auth.forms import UserChangeForm ->>> data = {'username': 'not valid'} ->>> form = UserChangeForm(data, instance=user) ->>> form.is_valid() -False ->>> form['username'].errors -[u'This value may contain only letters, numbers and @/./+/-/_ characters.'] - - -### PasswordResetForm - ->>> from django.contrib.auth.forms import PasswordResetForm ->>> data = {'email':'not valid'} ->>> form = PasswordResetForm(data) ->>> form.is_valid() -False ->>> form['email'].errors -[u'Enter a valid e-mail address.'] - -# Test nonexistant email address ->>> data = {'email':'foo@bar.com'} ->>> form = PasswordResetForm(data) ->>> form.is_valid() -False ->>> form.errors -{'email': [u"That e-mail address doesn't have an associated user account. Are you sure you've registered?"]} - -# Test cleaned_data bug fix ->>> user = User.objects.create_user("jsmith3", "jsmith3@example.com", "test123") ->>> data = {'email':'jsmith3@example.com'} ->>> form = PasswordResetForm(data) ->>> form.is_valid() -True ->>> form.cleaned_data['email'] -u'jsmith3@example.com' - -# bug #5605, preserve the case of the user name (before the @ in the email address) -# when creating a user. ->>> user = User.objects.create_user('forms_test2', 'tesT@EXAMple.com', 'test') ->>> user.email -'tesT@example.com' ->>> user = User.objects.create_user('forms_test3', 'tesT', 'test') ->>> user.email -'tesT' - -""" +from django.contrib.auth.models import User +from django.contrib.auth.forms import UserCreationForm, AuthenticationForm, PasswordChangeForm, SetPasswordForm, UserChangeForm, PasswordResetForm +from django.test import TestCase + + +class UserCreationFormTest(TestCase): + + fixtures = ['authtestdata.json'] + + def test_user_already_exists(self): + data = { + 'username': 'testclient', + 'password1': 'test123', + 'password2': 'test123', + } + form = UserCreationForm(data) + self.assertFalse(form.is_valid()) + self.assertEqual(form["username"].errors, + [u'A user with that username already exists.']) + + def test_invalid_data(self): + data = { + 'username': 'jsmith!', + 'password1': 'test123', + 'password2': 'test123', + } + form = UserCreationForm(data) + self.assertFalse(form.is_valid()) + self.assertEqual(form["username"].errors, + [u'This value may contain only letters, numbers and @/./+/-/_ characters.']) + + + def test_password_verification(self): + # The verification password is incorrect. + data = { + 'username': 'jsmith', + 'password1': 'test123', + 'password2': 'test', + } + form = UserCreationForm(data) + self.assertFalse(form.is_valid()) + self.assertEqual(form["password2"].errors, + [u"The two password fields didn't match."]) + + + def test_both_passwords(self): + # One (or both) passwords weren't given + data = {'username': 'jsmith'} + form = UserCreationForm(data) + self.assertFalse(form.is_valid()) + self.assertEqual(form['password1'].errors, + [u'This field is required.']) + self.assertEqual(form['password2'].errors, + [u'This field is required.']) + + + data['password2'] = 'test123' + form = UserCreationForm(data) + self.assertFalse(form.is_valid()) + self.assertEqual(form['password1'].errors, + [u'This field is required.']) + + def test_success(self): + # The success case. + + data = { + 'username': 'jsmith@example.com', + 'password1': 'test123', + 'password2': 'test123', + } + form = UserCreationForm(data) + self.assertTrue(form.is_valid()) + u = form.save() + self.assertEqual(repr(u), '<User: jsmith@example.com>') + + +class AuthenticationFormTest(TestCase): + + fixtures = ['authtestdata.json'] + + def test_invalid_username(self): + # The user submits an invalid username. + + data = { + 'username': 'jsmith_does_not_exist', + 'password': 'test123', + } + form = AuthenticationForm(None, data) + self.assertFalse(form.is_valid()) + self.assertEqual(form.non_field_errors(), + [u'Please enter a correct username and password. Note that both fields are case-sensitive.']) + + def test_inactive_user(self): + # The user is inactive. + data = { + 'username': 'inactive', + 'password': 'password', + } + form = AuthenticationForm(None, data) + self.assertFalse(form.is_valid()) + self.assertEqual(form.non_field_errors(), + [u'This account is inactive.']) + + + def test_success(self): + # The success case + data = { + 'username': 'testclient', + 'password': 'password', + } + form = AuthenticationForm(None, data) + self.assertTrue(form.is_valid()) + self.assertEqual(form.non_field_errors(), []) + + +class SetPasswordFormTest(TestCase): + + fixtures = ['authtestdata.json'] + + def test_password_verification(self): + # The two new passwords do not match. + user = User.objects.get(username='testclient') + data = { + 'new_password1': 'abc123', + 'new_password2': 'abc', + } + form = SetPasswordForm(user, data) + self.assertFalse(form.is_valid()) + self.assertEqual(form["new_password2"].errors, + [u"The two password fields didn't match."]) + + def test_success(self): + user = User.objects.get(username='testclient') + data = { + 'new_password1': 'abc123', + 'new_password2': 'abc123', + } + form = SetPasswordForm(user, data) + self.assertTrue(form.is_valid()) + + +class PasswordChangeFormTest(TestCase): + + fixtures = ['authtestdata.json'] + + def test_incorrect_password(self): + user = User.objects.get(username='testclient') + data = { + 'old_password': 'test', + 'new_password1': 'abc123', + 'new_password2': 'abc123', + } + form = PasswordChangeForm(user, data) + self.assertFalse(form.is_valid()) + self.assertEqual(form["old_password"].errors, + [u'Your old password was entered incorrectly. Please enter it again.']) + + + def test_password_verification(self): + # The two new passwords do not match. + user = User.objects.get(username='testclient') + data = { + 'old_password': 'password', + 'new_password1': 'abc123', + 'new_password2': 'abc', + } + form = PasswordChangeForm(user, data) + self.assertFalse(form.is_valid()) + self.assertEqual(form["new_password2"].errors, + [u"The two password fields didn't match."]) + + + def test_success(self): + # The success case. + user = User.objects.get(username='testclient') + data = { + 'old_password': 'password', + 'new_password1': 'abc123', + 'new_password2': 'abc123', + } + form = PasswordChangeForm(user, data) + self.assertTrue(form.is_valid()) + + def test_field_order(self): + # Regression test - check the order of fields: + user = User.objects.get(username='testclient') + self.assertEqual(PasswordChangeForm(user, {}).fields.keys(), + ['old_password', 'new_password1', 'new_password2']) + +class UserChangeFormTest(TestCase): + + fixtures = ['authtestdata.json'] + + def test_username_validity(self): + user = User.objects.get(username='testclient') + data = {'username': 'not valid'} + form = UserChangeForm(data, instance=user) + self.assertFalse(form.is_valid()) + self.assertEqual(form['username'].errors, + [u'This value may contain only letters, numbers and @/./+/-/_ characters.']) + + def test_bug_14242(self): + # A regression test, introduce by adding an optimization for the + # UserChangeForm. + + class MyUserForm(UserChangeForm): + def __init__(self, *args, **kwargs): + super(MyUserForm, self).__init__(*args, **kwargs) + self.fields['groups'].help_text = 'These groups give users different permissions' + + class Meta(UserChangeForm.Meta): + fields = ('groups',) + + # Just check we can create it + form = MyUserForm({}) + + +class PasswordResetFormTest(TestCase): + + fixtures = ['authtestdata.json'] + + def test_invalid_email(self): + data = {'email':'not valid'} + form = PasswordResetForm(data) + self.assertFalse(form.is_valid()) + self.assertEqual(form['email'].errors, + [u'Enter a valid e-mail address.']) + + def test_nonexistant_email(self): + # Test nonexistant email address + data = {'email':'foo@bar.com'} + form = PasswordResetForm(data) + self.assertFalse(form.is_valid()) + self.assertEqual(form.errors, + {'email': [u"That e-mail address doesn't have an associated user account. Are you sure you've registered?"]}) + + def test_cleaned_data(self): + # Regression test + user = User.objects.create_user("jsmith3", "jsmith3@example.com", "test123") + data = {'email':'jsmith3@example.com'} + form = PasswordResetForm(data) + self.assertTrue(form.is_valid()) + self.assertEqual(form.cleaned_data['email'], u'jsmith3@example.com') + + + def test_bug_5605(self): + # bug #5605, preserve the case of the user name (before the @ in the + # email address) when creating a user. + user = User.objects.create_user('forms_test2', 'tesT@EXAMple.com', 'test') + self.assertEqual(user.email, 'tesT@example.com') + user = User.objects.create_user('forms_test3', 'tesT', 'test') + self.assertEqual(user.email, 'tesT') diff --git a/django/contrib/auth/tests/urls.py b/django/contrib/auth/tests/urls.py index f94b8daa7f..bc2f7e705f 100644 --- a/django/contrib/auth/tests/urls.py +++ b/django/contrib/auth/tests/urls.py @@ -1,5 +1,7 @@ from django.conf.urls.defaults import patterns from django.contrib.auth.urls import urlpatterns +from django.contrib.auth.views import password_reset +from django.contrib.auth.decorators import login_required from django.http import HttpResponse from django.template import Template, RequestContext @@ -14,5 +16,8 @@ urlpatterns += patterns('', (r'^logout/custom_query/$', 'django.contrib.auth.views.logout', dict(redirect_field_name='follow')), (r'^logout/next_page/$', 'django.contrib.auth.views.logout', dict(next_page='/somewhere/')), (r'^remote_user/$', remote_user_auth_view), + (r'^password_reset_from_email/$', 'django.contrib.auth.views.password_reset', dict(from_email='staffmember@example.com')), + (r'^login_required/$', login_required(password_reset)), + (r'^login_required_login_url/$', login_required(password_reset, login_url='/somewhere/')), ) diff --git a/django/contrib/auth/tests/views.py b/django/contrib/auth/tests/views.py index d894e6dafd..e20afbc5b0 100644 --- a/django/contrib/auth/tests/views.py +++ b/django/contrib/auth/tests/views.py @@ -36,6 +36,16 @@ class AuthViewsTestCase(TestCase): settings.LANGUAGE_CODE = self.old_LANGUAGE_CODE settings.TEMPLATE_DIRS = self.old_TEMPLATE_DIRS + def login(self, password='password'): + response = self.client.post('/login/', { + 'username': 'testclient', + 'password': password + } + ) + self.assertEquals(response.status_code, 302) + self.assert_(response['Location'].endswith(settings.LOGIN_REDIRECT_URL)) + self.assert_(SESSION_KEY in self.client.session) + class PasswordResetTest(AuthViewsTestCase): def test_email_not_found(self): @@ -52,6 +62,14 @@ class PasswordResetTest(AuthViewsTestCase): self.assertEquals(response.status_code, 302) self.assertEquals(len(mail.outbox), 1) self.assert_("http://" in mail.outbox[0].body) + self.assertEquals(settings.DEFAULT_FROM_EMAIL, mail.outbox[0].from_email) + + def test_email_found_custom_from(self): + "Email is sent if a valid email address is provided for password reset when a custom from_email is provided." + response = self.client.post('/password_reset_from_email/', {'email': 'staffmember@example.com'}) + self.assertEquals(response.status_code, 302) + self.assertEquals(len(mail.outbox), 1) + self.assertEquals("staffmember@example.com", mail.outbox[0].from_email) def _test_confirm_start(self): # Start by creating the email @@ -118,15 +136,6 @@ class PasswordResetTest(AuthViewsTestCase): class ChangePasswordTest(AuthViewsTestCase): - def login(self, password='password'): - response = self.client.post('/login/', { - 'username': 'testclient', - 'password': password - } - ) - self.assertEquals(response.status_code, 302) - self.assert_(response['Location'].endswith(settings.LOGIN_REDIRECT_URL)) - def fail_login(self, password='password'): response = self.client.post('/login/', { 'username': 'testclient', @@ -228,16 +237,6 @@ class LoginTest(AuthViewsTestCase): class LogoutTest(AuthViewsTestCase): urls = 'django.contrib.auth.tests.urls' - def login(self, password='password'): - response = self.client.post('/login/', { - 'username': 'testclient', - 'password': password - } - ) - self.assertEquals(response.status_code, 302) - self.assert_(response['Location'].endswith(settings.LOGIN_REDIRECT_URL)) - self.assert_(SESSION_KEY in self.client.session) - def confirm_logged_out(self): self.assert_(SESSION_KEY not in self.client.session) diff --git a/django/contrib/auth/views.py b/django/contrib/auth/views.py index b2e875a869..eaa0dbeba2 100644 --- a/django/contrib/auth/views.py +++ b/django/contrib/auth/views.py @@ -105,7 +105,7 @@ def redirect_to_login(next, login_url=None, redirect_field_name=REDIRECT_FIELD_N def password_reset(request, is_admin_site=False, template_name='registration/password_reset_form.html', email_template_name='registration/password_reset_email.html', password_reset_form=PasswordResetForm, token_generator=default_token_generator, - post_reset_redirect=None): + post_reset_redirect=None, from_email=None): if post_reset_redirect is None: post_reset_redirect = reverse('django.contrib.auth.views.password_reset_done') if request.method == "POST": @@ -114,6 +114,7 @@ def password_reset(request, is_admin_site=False, template_name='registration/pas opts = {} opts['use_https'] = request.is_secure() opts['token_generator'] = token_generator + opts['from_email'] = from_email if is_admin_site: opts['domain_override'] = request.META['HTTP_HOST'] else: diff --git a/django/contrib/databrowse/plugins/calendars.py b/django/contrib/databrowse/plugins/calendars.py index 9bbd02da26..8a08cfd8eb 100644 --- a/django/contrib/databrowse/plugins/calendars.py +++ b/django/contrib/databrowse/plugins/calendars.py @@ -60,7 +60,7 @@ class CalendarPlugin(DatabrowsePlugin): def homepage_view(self, request): easy_model = EasyModel(self.site, self.model) field_list = self.fields.values() - field_list.sort(lambda x, y: cmp(x.verbose_name, y.verbose_name)) + field_list.sort(key=lambda k:k.verbose_name) return render_to_response('databrowse/calendar_homepage.html', {'root_url': self.site.root_url, 'model': easy_model, 'field_list': field_list}) def calendar_view(self, request, field, year=None, month=None, day=None): diff --git a/django/contrib/databrowse/plugins/fieldchoices.py b/django/contrib/databrowse/plugins/fieldchoices.py index 8f77792579..e0c01e95e6 100644 --- a/django/contrib/databrowse/plugins/fieldchoices.py +++ b/django/contrib/databrowse/plugins/fieldchoices.py @@ -61,7 +61,7 @@ class FieldChoicePlugin(DatabrowsePlugin): def homepage_view(self, request): easy_model = EasyModel(self.site, self.model) field_list = self.fields.values() - field_list.sort(lambda x, y: cmp(x.verbose_name, y.verbose_name)) + field_list.sort(key=lambda k: k.verbose_name) return render_to_response('databrowse/fieldchoice_homepage.html', {'root_url': self.site.root_url, 'model': easy_model, 'field_list': field_list}) def field_view(self, request, field, value=None): diff --git a/django/contrib/flatpages/admin.py b/django/contrib/flatpages/admin.py index b6fdba3d53..1b377e967b 100644 --- a/django/contrib/flatpages/admin.py +++ b/django/contrib/flatpages/admin.py @@ -5,11 +5,11 @@ from django.utils.translation import ugettext_lazy as _ class FlatpageForm(forms.ModelForm): - url = forms.RegexField(label=_("URL"), max_length=100, regex=r'^[-\w/]+$', + url = forms.RegexField(label=_("URL"), max_length=100, regex=r'^[-\w/\.~]+$', help_text = _("Example: '/about/contact/'. Make sure to have leading" " and trailing slashes."), error_message = _("This value must contain only letters, numbers," - " underscores, dashes or slashes.")) + " dots, underscores, dashes, slashes or tildes.")) class Meta: model = FlatPage diff --git a/django/contrib/flatpages/fixtures/sample_flatpages.json b/django/contrib/flatpages/fixtures/sample_flatpages.json new file mode 100644 index 0000000000..885af1eb60 --- /dev/null +++ b/django/contrib/flatpages/fixtures/sample_flatpages.json @@ -0,0 +1,63 @@ +[ + { + "pk": 1, + "model": "flatpages.flatpage", + "fields": { + "registration_required": false, + "title": "A Flatpage", + "url": "/flatpage/", + "template_name": "", + "sites": [ + 1 + ], + "content": "Isn't it flat!", + "enable_comments": false + } + }, + { + "pk": 2, + "model": "flatpages.flatpage", + "fields": { + "registration_required": false, + "title": "A Nested Flatpage", + "url": "/location/flatpage/", + "template_name": "", + "sites": [ + 1 + ], + "content": "Isn't it flat and deep!", + "enable_comments": false + } + }, + + { + "pk": 101, + "model": "flatpages.flatpage", + "fields": { + "registration_required": true, + "title": "Sekrit Flatpage", + "url": "/sekrit/", + "template_name": "", + "sites": [ + 1 + ], + "content": "Isn't it sekrit!", + "enable_comments": false + } + }, + { + "pk": 102, + "model": "flatpages.flatpage", + "fields": { + "registration_required": true, + "title": "Sekrit Nested Flatpage", + "url": "/location/sekrit/", + "template_name": "", + "sites": [ + 1 + ], + "content": "Isn't it sekrit and deep!", + "enable_comments": false + } + } +]
\ No newline at end of file diff --git a/django/contrib/flatpages/templatetags/__init__.py b/django/contrib/flatpages/templatetags/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/django/contrib/flatpages/templatetags/__init__.py diff --git a/django/contrib/flatpages/templatetags/flatpages.py b/django/contrib/flatpages/templatetags/flatpages.py new file mode 100644 index 0000000000..5c76699b79 --- /dev/null +++ b/django/contrib/flatpages/templatetags/flatpages.py @@ -0,0 +1,99 @@ +from django import template +from django.contrib.flatpages.models import FlatPage +from django.utils.translation import ugettext as _ +from django.conf import settings + + +register = template.Library() + + +class FlatpageNode(template.Node): + def __init__(self, context_name, starts_with=None, user=None): + self.context_name = context_name + if starts_with: + self.starts_with = template.Variable(starts_with) + else: + self.starts_with = None + if user: + self.user = template.Variable(user) + else: + self.user = None + + def render(self, context): + flatpages = FlatPage.objects.filter(sites__id=settings.SITE_ID) + # If a prefix was specified, add a filter + if self.starts_with: + flatpages = flatpages.filter( + url__startswith=self.starts_with.resolve(context)) + + # If the provided user is not authenticated, or no user + # was provided, filter the list to only public flatpages. + if self.user: + user = self.user.resolve(context) + if not user.is_authenticated(): + flatpages = flatpages.filter(registration_required=False) + else: + flatpages = flatpages.filter(registration_required=False) + + context[self.context_name] = flatpages + return '' + + +def get_flatpages(parser, token): + """ + Retrieves all flatpage objects available for the current site and + visible to the specific user (or visible to all users if no user is + specified). Populates the template context with them in a variable + whose name is defined by the ``as`` clause. + + An optional ``for`` clause can be used to control the user whose + permissions are to be used in determining which flatpages are visible. + + An optional argument, ``starts_with``, can be applied to limit the + returned flatpages to those beginning with a particular base URL. + This argument can be passed as a variable or a string, as it resolves + from the template context. + + Syntax:: + + {% get_flatpages ['url_starts_with'] [for user] as context_name %} + + Example usage:: + + {% get_flatpages as flatpages %} + {% get_flatpages for someuser as flatpages %} + {% get_flatpages '/about/' as about_pages %} + {% get_flatpages prefix as about_pages %} + {% get_flatpages '/about/' for someuser as about_pages %} + """ + bits = token.split_contents() + syntax_message = _("%(tag_name)s expects a syntax of %(tag_name)s " + "['url_starts_with'] [for user] as context_name" % + dict(tag_name=bits[0])) + # Must have at 3-6 bits in the tag + if len(bits) >= 3 and len(bits) <= 6: + + # If there's an even number of bits, there's no prefix + if len(bits) % 2 == 0: + prefix = bits[1] + else: + prefix = None + + # The very last bit must be the context name + if bits[-2] != 'as': + raise template.TemplateSyntaxError(syntax_message) + context_name = bits[-1] + + # If there are 5 or 6 bits, there is a user defined + if len(bits) >= 5: + if bits[-4] != 'for': + raise template.TemplateSyntaxError(syntax_message) + user = bits[-3] + else: + user = None + + return FlatpageNode(context_name, starts_with=prefix, user=user) + else: + raise template.TemplateSyntaxError(syntax_message) + +register.tag('get_flatpages', get_flatpages) diff --git a/django/contrib/flatpages/tests/__init__.py b/django/contrib/flatpages/tests/__init__.py new file mode 100644 index 0000000000..5dd5e89dca --- /dev/null +++ b/django/contrib/flatpages/tests/__init__.py @@ -0,0 +1,5 @@ +from django.contrib.flatpages.tests.csrf import * +from django.contrib.flatpages.tests.forms import * +from django.contrib.flatpages.tests.middleware import * +from django.contrib.flatpages.tests.templatetags import * +from django.contrib.flatpages.tests.views import * diff --git a/django/contrib/flatpages/tests/csrf.py b/django/contrib/flatpages/tests/csrf.py new file mode 100644 index 0000000000..b65ee382a6 --- /dev/null +++ b/django/contrib/flatpages/tests/csrf.py @@ -0,0 +1,76 @@ +import os +from django.conf import settings +from django.contrib.auth.models import User +from django.test import TestCase, Client + +class FlatpageCSRFTests(TestCase): + fixtures = ['sample_flatpages'] + urls = 'django.contrib.flatpages.tests.urls' + + def setUp(self): + self.client = Client(enforce_csrf_checks=True) + self.old_MIDDLEWARE_CLASSES = settings.MIDDLEWARE_CLASSES + flatpage_middleware_class = 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware' + csrf_middleware_class = 'django.middleware.csrf.CsrfViewMiddleware' + if csrf_middleware_class not in settings.MIDDLEWARE_CLASSES: + settings.MIDDLEWARE_CLASSES += (csrf_middleware_class,) + if flatpage_middleware_class not in settings.MIDDLEWARE_CLASSES: + settings.MIDDLEWARE_CLASSES += (flatpage_middleware_class,) + self.old_TEMPLATE_DIRS = settings.TEMPLATE_DIRS + settings.TEMPLATE_DIRS = ( + os.path.join( + os.path.dirname(__file__), + 'templates' + ), + ) + + def tearDown(self): + settings.MIDDLEWARE_CLASSES = self.old_MIDDLEWARE_CLASSES + settings.TEMPLATE_DIRS = self.old_TEMPLATE_DIRS + + def test_view_flatpage(self): + "A flatpage can be served through a view, even when the middleware is in use" + response = self.client.get('/flatpage_root/flatpage/') + self.assertEquals(response.status_code, 200) + self.assertContains(response, "<p>Isn't it flat!</p>") + + def test_view_non_existent_flatpage(self): + "A non-existent flatpage raises 404 when served through a view, even when the middleware is in use" + response = self.client.get('/flatpage_root/no_such_flatpage/') + self.assertEquals(response.status_code, 404) + + def test_view_authenticated_flatpage(self): + "A flatpage served through a view can require authentication" + response = self.client.get('/flatpage_root/sekrit/') + self.assertRedirects(response, '/accounts/login/?next=/flatpage_root/sekrit/') + User.objects.create_user('testuser', 'test@example.com', 's3krit') + self.client.login(username='testuser',password='s3krit') + response = self.client.get('/flatpage_root/sekrit/') + self.assertEquals(response.status_code, 200) + self.assertContains(response, "<p>Isn't it sekrit!</p>") + + def test_fallback_flatpage(self): + "A flatpage can be served by the fallback middlware" + response = self.client.get('/flatpage/') + self.assertEquals(response.status_code, 200) + self.assertContains(response, "<p>Isn't it flat!</p>") + + def test_fallback_non_existent_flatpage(self): + "A non-existent flatpage raises a 404 when served by the fallback middlware" + response = self.client.get('/no_such_flatpage/') + self.assertEquals(response.status_code, 404) + + def test_post_view_flatpage(self): + "POSTing to a flatpage served through a view will raise a CSRF error if no token is provided (Refs #14156)" + response = self.client.post('/flatpage_root/flatpage/') + self.assertEquals(response.status_code, 403) + + def test_post_fallback_flatpage(self): + "POSTing to a flatpage served by the middleware will raise a CSRF error if no token is provided (Refs #14156)" + response = self.client.post('/flatpage/') + self.assertEquals(response.status_code, 403) + + def test_post_unknown_page(self): + "POSTing to an unknown page isn't caught as a 403 CSRF error" + response = self.client.post('/no_such_page/') + self.assertEquals(response.status_code, 404) diff --git a/django/contrib/flatpages/tests/forms.py b/django/contrib/flatpages/tests/forms.py new file mode 100644 index 0000000000..969d347b39 --- /dev/null +++ b/django/contrib/flatpages/tests/forms.py @@ -0,0 +1,22 @@ +from django.contrib.flatpages.admin import FlatpageForm +from django.test import TestCase + +class FlatpageAdminFormTests(TestCase): + def setUp(self): + self.form_data = { + 'title': "A test page", + 'content': "This is a test", + 'sites': [1], + } + + def test_flatpage_admin_form_url_validation(self): + "The flatpage admin form validates correctly validates urls" + self.assertTrue(FlatpageForm(data=dict(url='/new_flatpage/', **self.form_data)).is_valid()) + self.assertTrue(FlatpageForm(data=dict(url='/some.special~chars/', **self.form_data)).is_valid()) + self.assertTrue(FlatpageForm(data=dict(url='/some.very_special~chars-here/', **self.form_data)).is_valid()) + + self.assertFalse(FlatpageForm(data=dict(url='/a space/', **self.form_data)).is_valid()) + self.assertFalse(FlatpageForm(data=dict(url='/a % char/', **self.form_data)).is_valid()) + self.assertFalse(FlatpageForm(data=dict(url='/a ! char/', **self.form_data)).is_valid()) + self.assertFalse(FlatpageForm(data=dict(url='/a & char/', **self.form_data)).is_valid()) + self.assertFalse(FlatpageForm(data=dict(url='/a ? char/', **self.form_data)).is_valid()) diff --git a/django/contrib/flatpages/tests/middleware.py b/django/contrib/flatpages/tests/middleware.py new file mode 100644 index 0000000000..bedaffc8ac --- /dev/null +++ b/django/contrib/flatpages/tests/middleware.py @@ -0,0 +1,67 @@ +import os +from django.conf import settings +from django.contrib.auth.models import User +from django.test import TestCase + +class FlatpageMiddlewareTests(TestCase): + fixtures = ['sample_flatpages'] + urls = 'django.contrib.flatpages.tests.urls' + + def setUp(self): + self.old_MIDDLEWARE_CLASSES = settings.MIDDLEWARE_CLASSES + flatpage_middleware_class = 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware' + if flatpage_middleware_class not in settings.MIDDLEWARE_CLASSES: + settings.MIDDLEWARE_CLASSES += (flatpage_middleware_class,) + self.old_TEMPLATE_DIRS = settings.TEMPLATE_DIRS + settings.TEMPLATE_DIRS = ( + os.path.join( + os.path.dirname(__file__), + 'templates' + ), + ) + + def tearDown(self): + settings.MIDDLEWARE_CLASSES = self.old_MIDDLEWARE_CLASSES + settings.TEMPLATE_DIRS = self.old_TEMPLATE_DIRS + + def test_view_flatpage(self): + "A flatpage can be served through a view, even when the middleware is in use" + response = self.client.get('/flatpage_root/flatpage/') + self.assertEquals(response.status_code, 200) + self.assertContains(response, "<p>Isn't it flat!</p>") + + def test_view_non_existent_flatpage(self): + "A non-existent flatpage raises 404 when served through a view, even when the middleware is in use" + response = self.client.get('/flatpage_root/no_such_flatpage/') + self.assertEquals(response.status_code, 404) + + def test_view_authenticated_flatpage(self): + "A flatpage served through a view can require authentication" + response = self.client.get('/flatpage_root/sekrit/') + self.assertRedirects(response, '/accounts/login/?next=/flatpage_root/sekrit/') + User.objects.create_user('testuser', 'test@example.com', 's3krit') + self.client.login(username='testuser',password='s3krit') + response = self.client.get('/flatpage_root/sekrit/') + self.assertEquals(response.status_code, 200) + self.assertContains(response, "<p>Isn't it sekrit!</p>") + + def test_fallback_flatpage(self): + "A flatpage can be served by the fallback middlware" + response = self.client.get('/flatpage/') + self.assertEquals(response.status_code, 200) + self.assertContains(response, "<p>Isn't it flat!</p>") + + def test_fallback_non_existent_flatpage(self): + "A non-existent flatpage raises a 404 when served by the fallback middlware" + response = self.client.get('/no_such_flatpage/') + self.assertEquals(response.status_code, 404) + + def test_fallback_authenticated_flatpage(self): + "A flatpage served by the middleware can require authentication" + response = self.client.get('/sekrit/') + self.assertRedirects(response, '/accounts/login/?next=/sekrit/') + User.objects.create_user('testuser', 'test@example.com', 's3krit') + self.client.login(username='testuser',password='s3krit') + response = self.client.get('/sekrit/') + self.assertEquals(response.status_code, 200) + self.assertContains(response, "<p>Isn't it sekrit!</p>") diff --git a/django/contrib/flatpages/tests/templates/404.html b/django/contrib/flatpages/tests/templates/404.html new file mode 100644 index 0000000000..5fd5f3cf3b --- /dev/null +++ b/django/contrib/flatpages/tests/templates/404.html @@ -0,0 +1 @@ +<h1>Oh Noes!</h1>
\ No newline at end of file diff --git a/django/contrib/flatpages/tests/templates/flatpages/default.html b/django/contrib/flatpages/tests/templates/flatpages/default.html new file mode 100644 index 0000000000..1410e17adf --- /dev/null +++ b/django/contrib/flatpages/tests/templates/flatpages/default.html @@ -0,0 +1,10 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" + "http://www.w3.org/TR/REC-html40/loose.dtd"> +<html> +<head> +<title>{{ flatpage.title }}</title> +</head> +<body> +<p>{{ flatpage.content }}</p> +</body> +</html> diff --git a/django/contrib/flatpages/tests/templates/registration/login.html b/django/contrib/flatpages/tests/templates/registration/login.html new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/django/contrib/flatpages/tests/templates/registration/login.html diff --git a/django/contrib/flatpages/tests/templatetags.py b/django/contrib/flatpages/tests/templatetags.py new file mode 100644 index 0000000000..9f42381c29 --- /dev/null +++ b/django/contrib/flatpages/tests/templatetags.py @@ -0,0 +1,134 @@ +import os +from django.conf import settings +from django.contrib.auth.models import AnonymousUser, User +from django.template import Template, Context, TemplateSyntaxError +from django.test import TestCase + +class FlatpageTemplateTagTests(TestCase): + fixtures = ['sample_flatpages'] + urls = 'django.contrib.flatpages.tests.urls' + + def setUp(self): + self.old_MIDDLEWARE_CLASSES = settings.MIDDLEWARE_CLASSES + flatpage_middleware_class = 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware' + if flatpage_middleware_class not in settings.MIDDLEWARE_CLASSES: + settings.MIDDLEWARE_CLASSES += (flatpage_middleware_class,) + self.old_TEMPLATE_DIRS = settings.TEMPLATE_DIRS + settings.TEMPLATE_DIRS = ( + os.path.join( + os.path.dirname(__file__), + 'templates' + ), + ) + self.me = User.objects.create_user('testuser', 'test@example.com', 's3krit') + + def tearDown(self): + settings.MIDDLEWARE_CLASSES = self.old_MIDDLEWARE_CLASSES + settings.TEMPLATE_DIRS = self.old_TEMPLATE_DIRS + + def test_get_flatpages_tag(self): + "The flatpage template tag retrives unregistered prefixed flatpages by default" + out = Template( + "{% load flatpages %}" + "{% get_flatpages as flatpages %}" + "{% for page in flatpages %}" + "{{ page.title }}," + "{% endfor %}" + ).render(Context()) + self.assertEquals(out, "A Flatpage,A Nested Flatpage,") + + def test_get_flatpages_tag_for_anon_user(self): + "The flatpage template tag retrives unregistered flatpages for an anonymous user" + out = Template( + "{% load flatpages %}" + "{% get_flatpages for anonuser as flatpages %}" + "{% for page in flatpages %}" + "{{ page.title }}," + "{% endfor %}" + ).render(Context({ + 'anonuser': AnonymousUser() + })) + self.assertEquals(out, "A Flatpage,A Nested Flatpage,") + + def test_get_flatpages_tag_for_user(self): + "The flatpage template tag retrives all flatpages for an authenticated user" + out = Template( + "{% load flatpages %}" + "{% get_flatpages for me as flatpages %}" + "{% for page in flatpages %}" + "{{ page.title }}," + "{% endfor %}" + ).render(Context({ + 'me': self.me + })) + self.assertEquals(out, "A Flatpage,A Nested Flatpage,Sekrit Nested Flatpage,Sekrit Flatpage,") + + def test_get_flatpages_with_prefix(self): + "The flatpage template tag retrives unregistered prefixed flatpages by default" + out = Template( + "{% load flatpages %}" + "{% get_flatpages '/location/' as location_flatpages %}" + "{% for page in location_flatpages %}" + "{{ page.title }}," + "{% endfor %}" + ).render(Context()) + self.assertEquals(out, "A Nested Flatpage,") + + def test_get_flatpages_with_prefix_for_anon_user(self): + "The flatpage template tag retrives unregistered prefixed flatpages for an anonymous user" + out = Template( + "{% load flatpages %}" + "{% get_flatpages '/location/' for anonuser as location_flatpages %}" + "{% for page in location_flatpages %}" + "{{ page.title }}," + "{% endfor %}" + ).render(Context({ + 'anonuser': AnonymousUser() + })) + self.assertEquals(out, "A Nested Flatpage,") + + def test_get_flatpages_with_prefix_for_user(self): + "The flatpage template tag retrive prefixed flatpages for an authenticated user" + out = Template( + "{% load flatpages %}" + "{% get_flatpages '/location/' for me as location_flatpages %}" + "{% for page in location_flatpages %}" + "{{ page.title }}," + "{% endfor %}" + ).render(Context({ + 'me': self.me + })) + self.assertEquals(out, "A Nested Flatpage,Sekrit Nested Flatpage,") + + def test_get_flatpages_with_variable_prefix(self): + "The prefix for the flatpage template tag can be a template variable" + out = Template( + "{% load flatpages %}" + "{% get_flatpages location_prefix as location_flatpages %}" + "{% for page in location_flatpages %}" + "{{ page.title }}," + "{% endfor %}" + ).render(Context({ + 'location_prefix': '/location/' + })) + self.assertEquals(out, "A Nested Flatpage,") + + def test_parsing_errors(self): + "There are various ways that the flatpages template tag won't parse" + render = lambda t: Template(t).render(Context()) + + self.assertRaises(TemplateSyntaxError, render, + "{% load flatpages %}{% get_flatpages %}") + self.assertRaises(TemplateSyntaxError, render, + "{% load flatpages %}{% get_flatpages as %}") + self.assertRaises(TemplateSyntaxError, render, + "{% load flatpages %}{% get_flatpages cheesecake flatpages %}") + self.assertRaises(TemplateSyntaxError, render, + "{% load flatpages %}{% get_flatpages as flatpages asdf%}") + self.assertRaises(TemplateSyntaxError, render, + "{% load flatpages %}{% get_flatpages cheesecake user as flatpages %}") + self.assertRaises(TemplateSyntaxError, render, + "{% load flatpages %}{% get_flatpages for user as flatpages asdf%}") + self.assertRaises(TemplateSyntaxError, render, + "{% load flatpages %}{% get_flatpages prefix for user as flatpages asdf%}") + diff --git a/django/contrib/flatpages/tests/urls.py b/django/contrib/flatpages/tests/urls.py new file mode 100644 index 0000000000..3cffd09d0f --- /dev/null +++ b/django/contrib/flatpages/tests/urls.py @@ -0,0 +1,8 @@ +from django.conf.urls.defaults import * + +# special urls for flatpage test cases +urlpatterns = patterns('', + (r'^flatpage_root', include('django.contrib.flatpages.urls')), + (r'^accounts/', include('django.contrib.auth.urls')), +) + diff --git a/django/contrib/flatpages/tests/views.py b/django/contrib/flatpages/tests/views.py new file mode 100644 index 0000000000..89bdde2d92 --- /dev/null +++ b/django/contrib/flatpages/tests/views.py @@ -0,0 +1,72 @@ +import os +from django.conf import settings +from django.contrib.auth.models import User +from django.contrib.flatpages.models import FlatPage +from django.test import TestCase + +class FlatpageViewTests(TestCase): + fixtures = ['sample_flatpages'] + urls = 'django.contrib.flatpages.tests.urls' + + def setUp(self): + self.old_MIDDLEWARE_CLASSES = settings.MIDDLEWARE_CLASSES + flatpage_middleware_class = 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware' + if flatpage_middleware_class in settings.MIDDLEWARE_CLASSES: + settings.MIDDLEWARE_CLASSES = tuple(m for m in settings.MIDDLEWARE_CLASSES if m != flatpage_middleware_class) + self.old_TEMPLATE_DIRS = settings.TEMPLATE_DIRS + settings.TEMPLATE_DIRS = ( + os.path.join( + os.path.dirname(__file__), + 'templates' + ), + ) + + def tearDown(self): + settings.MIDDLEWARE_CLASSES = self.old_MIDDLEWARE_CLASSES + settings.TEMPLATE_DIRS = self.old_TEMPLATE_DIRS + + def test_view_flatpage(self): + "A flatpage can be served through a view" + response = self.client.get('/flatpage_root/flatpage/') + self.assertEquals(response.status_code, 200) + self.assertContains(response, "<p>Isn't it flat!</p>") + + def test_view_non_existent_flatpage(self): + "A non-existent flatpage raises 404 when served through a view" + response = self.client.get('/flatpage_root/no_such_flatpage/') + self.assertEquals(response.status_code, 404) + + def test_view_authenticated_flatpage(self): + "A flatpage served through a view can require authentication" + response = self.client.get('/flatpage_root/sekrit/') + self.assertRedirects(response, '/accounts/login/?next=/flatpage_root/sekrit/') + User.objects.create_user('testuser', 'test@example.com', 's3krit') + self.client.login(username='testuser',password='s3krit') + response = self.client.get('/flatpage_root/sekrit/') + self.assertEquals(response.status_code, 200) + self.assertContains(response, "<p>Isn't it sekrit!</p>") + + def test_fallback_flatpage(self): + "A fallback flatpage won't be served if the middleware is disabled" + response = self.client.get('/flatpage/') + self.assertEquals(response.status_code, 404) + + def test_fallback_non_existent_flatpage(self): + "A non-existent flatpage won't be served if the fallback middlware is disabled" + response = self.client.get('/no_such_flatpage/') + self.assertEquals(response.status_code, 404) + + def test_view_flatpage_special_chars(self): + "A flatpage with special chars in the URL can be served through a view" + fp = FlatPage.objects.create( + url="/some.very_special~chars-here/", + title="A very special page", + content="Isn't it special!", + enable_comments=False, + registration_required=False, + ) + fp.sites.add(1) + + response = self.client.get('/flatpage_root/some.very_special~chars-here/') + self.assertEquals(response.status_code, 200) + self.assertContains(response, "<p>Isn't it special!</p>") diff --git a/django/contrib/flatpages/views.py b/django/contrib/flatpages/views.py index 336600328d..88ef4da65e 100644 --- a/django/contrib/flatpages/views.py +++ b/django/contrib/flatpages/views.py @@ -13,10 +13,13 @@ DEFAULT_TEMPLATE = 'flatpages/default.html' # when a 404 is raised, which often means CsrfViewMiddleware.process_view # has not been called even if CsrfViewMiddleware is installed. So we need # to use @csrf_protect, in case the template needs {% csrf_token %}. -@csrf_protect +# However, we can't just wrap this view; if no matching flatpage exists, +# or a redirect is required for authentication, the 404 needs to be returned +# without any CSRF checks. Therefore, we only +# CSRF protect the internal implementation. def flatpage(request, url): """ - Flat page view. + Public interface to the flat page view. Models: `flatpages.flatpages` Templates: Uses the template defined by the ``template_name`` field, @@ -30,6 +33,13 @@ def flatpage(request, url): if not url.startswith('/'): url = "/" + url f = get_object_or_404(FlatPage, url__exact=url, sites__id__exact=settings.SITE_ID) + return render_flatpage(request, f) + +@csrf_protect +def render_flatpage(request, f): + """ + Internal interface to the flat page view. + """ # If registration is required for accessing this page, and the user isn't # logged in, redirect to the login page. if f.registration_required and not request.user.is_authenticated(): diff --git a/django/contrib/formtools/wizard.py b/django/contrib/formtools/wizard.py index 02d8fd71d4..32e27df574 100644 --- a/django/contrib/formtools/wizard.py +++ b/django/contrib/formtools/wizard.py @@ -27,7 +27,7 @@ class FormWizard(object): def __init__(self, form_list, initial=None): """ Start a new wizard with a list of forms. - + form_list should be a list of Form classes (not instances). """ self.form_list = form_list[:] @@ -37,7 +37,7 @@ class FormWizard(object): self.extra_context = {} # A zero-based counter keeping track of which step we're in. - self.step = 0 + self.step = 0 def __repr__(self): return "step: %d\nform_list: %s\ninitial_data: %s" % (self.step, self.form_list, self.initial) @@ -48,7 +48,7 @@ class FormWizard(object): def num_steps(self): "Helper method that returns the number of steps." - # You might think we should just set "self.form_list = len(form_list)" + # You might think we should just set "self.num_steps = len(form_list)" # in __init__(), but this calculation needs to be dynamic, because some # hook methods might alter self.form_list. return len(self.form_list) diff --git a/django/contrib/gis/db/backends/mysql/creation.py b/django/contrib/gis/db/backends/mysql/creation.py index 93fd2e6166..dda77ea6ab 100644 --- a/django/contrib/gis/db/backends/mysql/creation.py +++ b/django/contrib/gis/db/backends/mysql/creation.py @@ -6,7 +6,7 @@ class MySQLCreation(DatabaseCreation): from django.contrib.gis.db.models.fields import GeometryField output = super(MySQLCreation, self).sql_indexes_for_field(model, f, style) - if isinstance(f, GeometryField): + if isinstance(f, GeometryField) and f.spatial_index: qn = self.connection.ops.quote_name db_table = model._meta.db_table idx_name = '%s_%s_id' % (db_table, f.column) diff --git a/django/contrib/gis/db/backends/postgis/operations.py b/django/contrib/gis/db/backends/postgis/operations.py index b1a9e31529..5affcf9a18 100644 --- a/django/contrib/gis/db/backends/postgis/operations.py +++ b/django/contrib/gis/db/backends/postgis/operations.py @@ -233,8 +233,6 @@ class PostGISOperations(DatabaseOperations, BaseSpatialOperations): }) self.geography_operators = { 'bboverlaps' : PostGISOperator('&&'), - 'exact' : PostGISOperator('~='), - 'same_as' : PostGISOperator('~='), } # Creating a dictionary lookup of all GIS terms for PostGIS. diff --git a/django/contrib/gis/db/backends/spatialite/base.py b/django/contrib/gis/db/backends/spatialite/base.py index d419dab5e1..729fc152e7 100644 --- a/django/contrib/gis/db/backends/spatialite/base.py +++ b/django/contrib/gis/db/backends/spatialite/base.py @@ -51,7 +51,7 @@ class DatabaseWrapper(SqliteDatabaseWrapper): self.connection.create_function("django_extract", 2, _sqlite_extract) self.connection.create_function("django_date_trunc", 2, _sqlite_date_trunc) self.connection.create_function("regexp", 2, _sqlite_regexp) - connection_created.send(sender=self.__class__) + connection_created.send(sender=self.__class__, connection=self) ## From here on, customized for GeoDjango ## diff --git a/django/contrib/gis/db/models/sql/compiler.py b/django/contrib/gis/db/models/sql/compiler.py index 78eeeafe19..55dc4a66ec 100644 --- a/django/contrib/gis/db/models/sql/compiler.py +++ b/django/contrib/gis/db/models/sql/compiler.py @@ -95,7 +95,7 @@ class GeoSQLCompiler(compiler.SQLCompiler): return result def get_default_columns(self, with_aliases=False, col_aliases=None, - start_alias=None, opts=None, as_pairs=False): + start_alias=None, opts=None, as_pairs=False, local_only=False): """ Computes the default columns for selecting every field in the base model. Will sometimes be called to pull in related models (e.g. via @@ -121,6 +121,8 @@ class GeoSQLCompiler(compiler.SQLCompiler): if start_alias: seen = {None: start_alias} for field, model in opts.get_fields_with_model(): + if local_only and model is not None: + continue if start_alias: try: alias = seen[model] diff --git a/django/contrib/gis/gdal/libgdal.py b/django/contrib/gis/gdal/libgdal.py index 6589c5647c..a7a5658c49 100644 --- a/django/contrib/gis/gdal/libgdal.py +++ b/django/contrib/gis/gdal/libgdal.py @@ -14,10 +14,10 @@ if lib_path: lib_names = None elif os.name == 'nt': # Windows NT shared library - lib_names = ['gdal16', 'gdal15'] + lib_names = ['gdal17', 'gdal16', 'gdal15'] elif os.name == 'posix': # *NIX library names. - lib_names = ['gdal', 'GDAL', 'gdal1.6.0', 'gdal1.5.0', 'gdal1.4.0'] + lib_names = ['gdal', 'GDAL', 'gdal1.7.0', 'gdal1.6.0', 'gdal1.5.0', 'gdal1.4.0'] else: raise OGRException('Unsupported OS "%s"' % os.name) diff --git a/django/contrib/gis/tests/__init__.py b/django/contrib/gis/tests/__init__.py index 0d862190c0..44d62c2a98 100644 --- a/django/contrib/gis/tests/__init__.py +++ b/django/contrib/gis/tests/__init__.py @@ -1,115 +1,108 @@ import sys +import unittest + +from django.conf import settings +from django.db.models import get_app +from django.test.simple import build_suite, DjangoTestSuiteRunner def run_tests(*args, **kwargs): from django.test.simple import run_tests as base_run_tests return base_run_tests(*args, **kwargs) -def geo_suite(): - """ - Builds a test suite for the GIS package. This is not named - `suite` so it will not interfere with the Django test suite (since - spatial database tables are required to execute these tests on - some backends). - """ - from django.conf import settings - from django.contrib.gis.geos import GEOS_PREPARE - from django.contrib.gis.gdal import HAS_GDAL - from django.contrib.gis.utils import HAS_GEOIP - from django.contrib.gis.tests.utils import postgis, mysql - from django.db import connection - from django.utils.importlib import import_module - - gis_tests = [] - - # Adding the GEOS tests. - from django.contrib.gis.geos import tests as geos_tests - gis_tests.append(geos_tests.suite()) - - # Tests that require use of a spatial database (e.g., creation of models) - test_apps = ['geoapp', 'relatedapp'] - if postgis and connection.ops.geography: - # Test geography support with PostGIS 1.5+. - test_apps.append('geogapp') - - # Tests that do not require setting up and tearing down a spatial database. - test_suite_names = [ - 'test_measure', - ] - - if HAS_GDAL: - # These tests require GDAL. - if not mysql: - test_apps.append('distapp') - - # Only PostGIS using GEOS 3.1+ can support 3D so far. - if postgis and GEOS_PREPARE: - test_apps.append('geo3d') - - test_suite_names.extend(['test_spatialrefsys', 'test_geoforms']) - test_apps.append('layermap') +def run_gis_tests(test_labels, verbosity=1, interactive=True, failfast=False, extra_tests=None): + import warnings + warnings.warn( + 'The run_gis_tests() test runner has been deprecated in favor of GeoDjangoTestSuiteRunner.', + PendingDeprecationWarning + ) + test_runner = GeoDjangoTestSuiteRunner(verbosity=verbosity, interactive=interactive, failfast=failfast) + return test_runner.run_tests(test_labels, extra_tests=extra_tests) + +class GeoDjangoTestSuiteRunner(DjangoTestSuiteRunner): + + def setup_test_environment(self, **kwargs): + super(GeoDjangoTestSuiteRunner, self).setup_test_environment(**kwargs) - # Adding the GDAL tests. - from django.contrib.gis.gdal import tests as gdal_tests - gis_tests.append(gdal_tests.suite()) - else: - print >>sys.stderr, "GDAL not available - no tests requiring GDAL will be run." - - if HAS_GEOIP and hasattr(settings, 'GEOIP_PATH'): - test_suite_names.append('test_geoip') - - # Adding the rest of the suites from the modules specified - # in the `test_suite_names`. - for suite_name in test_suite_names: - tsuite = import_module('django.contrib.gis.tests.' + suite_name) - gis_tests.append(tsuite.suite()) - - return gis_tests, test_apps - -def run_gis_tests(test_labels, **kwargs): - """ - Use this routine as the TEST_RUNNER in your settings in order to run the - GeoDjango test suite. This must be done as a database superuser for - PostGIS, so read the docstring in `run_test()` below for more details. - """ - from django.conf import settings - from django.db.models import loading - from django.contrib.gis.tests.utils import mysql - - # Getting initial values. - old_installed = settings.INSTALLED_APPS - old_root_urlconf = settings.ROOT_URLCONF - - # Overridding the INSTALLED_APPS with only what we need, - # to prevent unnecessary database table creation. - new_installed = ['django.contrib.sites', - 'django.contrib.sitemaps', - 'django.contrib.gis', - ] - - # Setting the URLs. - settings.ROOT_URLCONF = 'django.contrib.gis.tests.urls' - - # Creating the test suite, adding the test models to INSTALLED_APPS - # so they will be tested. - gis_tests, test_apps = geo_suite() - for test_model in test_apps: - module_name = 'django.contrib.gis.tests.%s' % test_model - new_installed.append(module_name) - - # Resetting the loaded flag to take into account what we appended to - # the INSTALLED_APPS (since this routine is invoked through - # django/core/management, it caches the apps; this ensures that syncdb - # will see our appended models) - settings.INSTALLED_APPS = new_installed - loading.cache.loaded = False - - kwargs['extra_tests'] = gis_tests - - # Running the tests using the GIS test runner. - result = run_tests(test_labels, **kwargs) - - # Restoring modified settings. - settings.INSTALLED_APPS = old_installed - settings.ROOT_URLCONF = old_root_urlconf - - return result + from django.db import connection + from django.contrib.gis.geos import GEOS_PREPARE + from django.contrib.gis.gdal import HAS_GDAL + + # Getting and storing the original values of INSTALLED_APPS and + # the ROOT_URLCONF. + self.old_installed = settings.INSTALLED_APPS + self.old_root_urlconf = settings.ROOT_URLCONF + + # Tests that require use of a spatial database (e.g., creation of models) + self.geo_apps = ['geoapp', 'relatedapp'] + if connection.ops.postgis and connection.ops.geography: + # Test geography support with PostGIS 1.5+. + self.geo_apps.append('geogapp') + + if HAS_GDAL: + # The following GeoDjango test apps depend on GDAL support. + if not connection.ops.mysql: + self.geo_apps.append('distapp') + + # 3D apps use LayerMapping, which uses GDAL. + if connection.ops.postgis and GEOS_PREPARE: + self.geo_apps.append('geo3d') + + self.geo_apps.append('layermap') + + # Constructing the new INSTALLED_APPS, and including applications + # within the GeoDjango test namespace (`self.geo_apps`). + new_installed = ['django.contrib.sites', + 'django.contrib.sitemaps', + 'django.contrib.gis', + ] + new_installed.extend(['django.contrib.gis.tests.%s' % app + for app in self.geo_apps]) + settings.INSTALLED_APPS = new_installed + + # Setting the URLs. + settings.ROOT_URLCONF = 'django.contrib.gis.tests.urls' + + def teardown_test_environment(self, **kwargs): + super(GeoDjangoTestSuiteRunner, self).teardown_test_environment(**kwargs) + settings.INSTALLED_APPS = self.old_installed + settings.ROOT_URLCONF = self.old_root_urlconf + + def build_suite(self, test_labels, extra_tests=None, **kwargs): + """ + This method is overridden to construct a suite consisting only of tests + for GeoDjango. + """ + suite = unittest.TestSuite() + + # Adding the GEOS tests. + from django.contrib.gis.geos import tests as geos_tests + suite.addTest(geos_tests.suite()) + + # Adding the measurment tests. + from django.contrib.gis.tests import test_measure + suite.addTest(test_measure.suite()) + + # Adding GDAL tests, and any test suite that depends on GDAL, to the + # suite if GDAL is available. + from django.contrib.gis.gdal import HAS_GDAL + if HAS_GDAL: + from django.contrib.gis.gdal import tests as gdal_tests + suite.addTest(gdal_tests.suite()) + + from django.contrib.gis.tests import test_spatialrefsys, test_geoforms + suite.addTest(test_spatialrefsys.suite()) + suite.addTest(test_geoforms.suite()) + else: + sys.stderr.write('GDAL not available - no tests requiring GDAL will be run.\n') + + # Add GeoIP tests to the suite, if the library and data is available. + from django.contrib.gis.utils import HAS_GEOIP + if HAS_GEOIP and hasattr(settings, 'GEOIP_PATH'): + from django.contrib.gis.tests import test_geoip + suite.addTest(test_geoip.suite()) + + # Finally, adding the suites for each of the GeoDjango test apps. + for app_name in self.geo_apps: + suite.addTest(build_suite(get_app(app_name))) + + return suite diff --git a/django/contrib/gis/tests/geogapp/tests.py b/django/contrib/gis/tests/geogapp/tests.py index 7be5193103..3dea930afd 100644 --- a/django/contrib/gis/tests/geogapp/tests.py +++ b/django/contrib/gis/tests/geogapp/tests.py @@ -44,6 +44,10 @@ class GeographyTest(TestCase): # `@` operator not available. self.assertRaises(ValueError, City.objects.filter(point__contained=z.poly).count) + # Regression test for #14060, `~=` was never really implemented for PostGIS. + htown = City.objects.get(name='Houston') + self.assertRaises(ValueError, City.objects.get, point__exact=htown.point) + def test05_geography_layermapping(self): "Testing LayerMapping support on models with geography fields." # There is a similar test in `layermap` that uses the same data set, diff --git a/django/contrib/gis/tests/relatedapp/models.py b/django/contrib/gis/tests/relatedapp/models.py index 726f9826c0..2e9a62b61f 100644 --- a/django/contrib/gis/tests/relatedapp/models.py +++ b/django/contrib/gis/tests/relatedapp/models.py @@ -38,6 +38,11 @@ class Author(models.Model): name = models.CharField(max_length=100) objects = models.GeoManager() +class Article(models.Model): + title = models.CharField(max_length=100) + author = models.ForeignKey(Author, unique=True) + objects = models.GeoManager() + class Book(models.Model): title = models.CharField(max_length=100) author = models.ForeignKey(Author, related_name='books', null=True) diff --git a/django/contrib/gis/tests/relatedapp/tests.py b/django/contrib/gis/tests/relatedapp/tests.py index 184b65b9c7..5d3d00f08d 100644 --- a/django/contrib/gis/tests/relatedapp/tests.py +++ b/django/contrib/gis/tests/relatedapp/tests.py @@ -4,7 +4,7 @@ from django.contrib.gis.db.models import Collect, Count, Extent, F, Union from django.contrib.gis.geometry.backend import Geometry from django.contrib.gis.tests.utils import mysql, oracle, postgis, spatialite, no_mysql, no_oracle, no_spatialite from django.conf import settings -from models import City, Location, DirectoryEntry, Parcel, Book, Author +from models import City, Location, DirectoryEntry, Parcel, Book, Author, Article cities = (('Aurora', 'TX', -97.516111, 33.058333), ('Roswell', 'NM', -104.528056, 33.387222), @@ -291,6 +291,14 @@ class RelatedGeoModelTest(unittest.TestCase): self.assertEqual(4, len(coll)) self.assertEqual(ref_geom, coll) + def test15_invalid_select_related(self): + "Testing doing select_related on the related name manager of a unique FK. See #13934." + qs = Article.objects.select_related('author__article') + # This triggers TypeError when `get_default_columns` has no `local_only` + # keyword. The TypeError is swallowed if QuerySet is actually + # evaluated as list generation swallows TypeError in CPython. + sql = str(qs.query) + # TODO: Related tests for KML, GML, and distance lookups. def suite(): diff --git a/django/contrib/localflavor/in_/in_states.py b/django/contrib/localflavor/in_/in_states.py index 498efe7069..bb4a7482ca 100644 --- a/django/contrib/localflavor/in_/in_states.py +++ b/django/contrib/localflavor/in_/in_states.py @@ -7,43 +7,43 @@ when explicitly needed. """ STATE_CHOICES = ( - 'KA', 'Karnataka', - 'AP', 'Andhra Pradesh', - 'KL', 'Kerala', - 'TN', 'Tamil Nadu', - 'MH', 'Maharashtra', - 'UP', 'Uttar Pradesh', - 'GA', 'Goa', - 'GJ', 'Gujarat', - 'RJ', 'Rajasthan', - 'HP', 'Himachal Pradesh', - 'JK', 'Jammu and Kashmir', - 'AR', 'Arunachal Pradesh', - 'AS', 'Assam', - 'BR', 'Bihar', - 'CG', 'Chattisgarh', - 'HR', 'Haryana', - 'JH', 'Jharkhand', - 'MP', 'Madhya Pradesh', - 'MN', 'Manipur', - 'ML', 'Meghalaya', - 'MZ', 'Mizoram', - 'NL', 'Nagaland', - 'OR', 'Orissa', - 'PB', 'Punjab', - 'SK', 'Sikkim', - 'TR', 'Tripura', - 'UA', 'Uttarakhand', - 'WB', 'West Bengal', + ('KA', 'Karnataka'), + ('AP', 'Andhra Pradesh'), + ('KL', 'Kerala'), + ('TN', 'Tamil Nadu'), + ('MH', 'Maharashtra'), + ('UP', 'Uttar Pradesh'), + ('GA', 'Goa'), + ('GJ', 'Gujarat'), + ('RJ', 'Rajasthan'), + ('HP', 'Himachal Pradesh'), + ('JK', 'Jammu and Kashmir'), + ('AR', 'Arunachal Pradesh'), + ('AS', 'Assam'), + ('BR', 'Bihar'), + ('CG', 'Chattisgarh'), + ('HR', 'Haryana'), + ('JH', 'Jharkhand'), + ('MP', 'Madhya Pradesh'), + ('MN', 'Manipur'), + ('ML', 'Meghalaya'), + ('MZ', 'Mizoram'), + ('NL', 'Nagaland'), + ('OR', 'Orissa'), + ('PB', 'Punjab'), + ('SK', 'Sikkim'), + ('TR', 'Tripura'), + ('UA', 'Uttarakhand'), + ('WB', 'West Bengal'), # Union Territories - 'AN', 'Andaman and Nicobar', - 'CH', 'Chandigarh', - 'DN', 'Dadra and Nagar Haveli', - 'DD', 'Daman and Diu', - 'DL', 'Delhi', - 'LD', 'Lakshadweep', - 'PY', 'Pondicherry', + ('AN', 'Andaman and Nicobar'), + ('CH', 'Chandigarh'), + ('DN', 'Dadra and Nagar Haveli'), + ('DD', 'Daman and Diu'), + ('DL', 'Delhi'), + ('LD', 'Lakshadweep'), + ('PY', 'Pondicherry'), ) STATES_NORMALIZED = { diff --git a/django/contrib/markup/tests.py b/django/contrib/markup/tests.py index 9a96f8cda4..6a22e530ba 100644 --- a/django/contrib/markup/tests.py +++ b/django/contrib/markup/tests.py @@ -22,7 +22,7 @@ Paragraph 2 with "quotes" and @code@""" t = Template("{{ textile_content|textile }}") rendered = t.render(Context(locals())).strip() if textile: - self.assertEqual(rendered, """<p>Paragraph 1</p> + self.assertEqual(rendered.replace('\t', ''), """<p>Paragraph 1</p> <p>Paragraph 2 with “quotes” and <code>code</code></p>""") else: diff --git a/django/contrib/sessions/backends/file.py b/django/contrib/sessions/backends/file.py index 3f6350345f..843edca9cf 100644 --- a/django/contrib/sessions/backends/file.py +++ b/django/contrib/sessions/backends/file.py @@ -58,7 +58,7 @@ class SessionStore(SessionBase): finally: session_file.close() except IOError: - pass + self.create() return session_data def create(self): diff --git a/django/contrib/sessions/tests.py b/django/contrib/sessions/tests.py index f0a3c4ec8c..e645b73817 100644 --- a/django/contrib/sessions/tests.py +++ b/django/contrib/sessions/tests.py @@ -1,388 +1,273 @@ -r""" - ->>> from django.conf import settings ->>> from django.contrib.sessions.backends.db import SessionStore as DatabaseSession ->>> from django.contrib.sessions.backends.cache import SessionStore as CacheSession ->>> from django.contrib.sessions.backends.cached_db import SessionStore as CacheDBSession ->>> from django.contrib.sessions.backends.file import SessionStore as FileSession ->>> from django.contrib.sessions.backends.base import SessionBase ->>> from django.contrib.sessions.models import Session - ->>> db_session = DatabaseSession() ->>> db_session.modified -False ->>> db_session.get('cat') ->>> db_session['cat'] = "dog" ->>> db_session.modified -True ->>> db_session.pop('cat') -'dog' ->>> db_session.pop('some key', 'does not exist') -'does not exist' ->>> db_session.save() ->>> db_session.exists(db_session.session_key) -True ->>> db_session.delete(db_session.session_key) ->>> db_session.exists(db_session.session_key) -False - ->>> db_session['foo'] = 'bar' ->>> db_session.save() ->>> db_session.exists(db_session.session_key) -True ->>> prev_key = db_session.session_key ->>> db_session.flush() ->>> db_session.exists(prev_key) -False ->>> db_session.session_key == prev_key -False ->>> db_session.modified, db_session.accessed -(True, True) ->>> db_session['a'], db_session['b'] = 'c', 'd' ->>> db_session.save() ->>> prev_key = db_session.session_key ->>> prev_data = db_session.items() ->>> db_session.cycle_key() ->>> db_session.session_key == prev_key -False ->>> db_session.items() == prev_data -True - -# Submitting an invalid session key (either by guessing, or if the db has -# removed the key) results in a new key being generated. ->>> Session.objects.filter(pk=db_session.session_key).delete() ->>> db_session = DatabaseSession(db_session.session_key) ->>> db_session.save() ->>> DatabaseSession('1').get('cat') - -# -# Cached DB session tests -# - ->>> cdb_session = CacheDBSession() ->>> cdb_session.modified -False ->>> cdb_session['cat'] = "dog" ->>> cdb_session.modified -True ->>> cdb_session.pop('cat') -'dog' ->>> cdb_session.pop('some key', 'does not exist') -'does not exist' ->>> cdb_session.save() ->>> cdb_session.exists(cdb_session.session_key) -True ->>> cdb_session.delete(cdb_session.session_key) ->>> cdb_session.exists(cdb_session.session_key) -False - -# -# File session tests. -# - -# Do file session tests in an isolated directory, and kill it after we're done. ->>> original_session_file_path = settings.SESSION_FILE_PATH ->>> import tempfile ->>> temp_session_store = settings.SESSION_FILE_PATH = tempfile.mkdtemp() - ->>> file_session = FileSession() ->>> file_session.modified -False ->>> file_session['cat'] = "dog" ->>> file_session.modified -True ->>> file_session.pop('cat') -'dog' ->>> file_session.pop('some key', 'does not exist') -'does not exist' ->>> file_session.save() ->>> file_session.exists(file_session.session_key) -True ->>> file_session.delete(file_session.session_key) ->>> file_session.exists(file_session.session_key) -False ->>> FileSession('1').get('cat') - ->>> file_session['foo'] = 'bar' ->>> file_session.save() ->>> file_session.exists(file_session.session_key) -True ->>> prev_key = file_session.session_key ->>> file_session.flush() ->>> file_session.exists(prev_key) -False ->>> file_session.session_key == prev_key -False ->>> file_session.modified, file_session.accessed -(True, True) ->>> file_session['a'], file_session['b'] = 'c', 'd' ->>> file_session.save() ->>> prev_key = file_session.session_key ->>> prev_data = file_session.items() ->>> file_session.cycle_key() ->>> file_session.session_key == prev_key -False ->>> file_session.items() == prev_data -True - ->>> Session.objects.filter(pk=file_session.session_key).delete() ->>> file_session = FileSession(file_session.session_key) ->>> file_session.save() - -# Make sure the file backend checks for a good storage dir ->>> settings.SESSION_FILE_PATH = "/if/this/directory/exists/you/have/a/weird/computer" ->>> FileSession() -Traceback (innermost last): - ... -ImproperlyConfigured: The session storage path '/if/this/directory/exists/you/have/a/weird/computer' doesn't exist. Please set your SESSION_FILE_PATH setting to an existing directory in which Django can store session data. - -# Clean up after the file tests ->>> settings.SESSION_FILE_PATH = original_session_file_path ->>> import shutil ->>> shutil.rmtree(temp_session_store) - -# -# Cache-based tests -# NB: be careful to delete any sessions created; stale sessions fill up the -# /tmp and eventually overwhelm it after lots of runs (think buildbots) -# - ->>> cache_session = CacheSession() ->>> cache_session.modified -False ->>> cache_session['cat'] = "dog" ->>> cache_session.modified -True ->>> cache_session.pop('cat') -'dog' ->>> cache_session.pop('some key', 'does not exist') -'does not exist' ->>> cache_session.save() ->>> cache_session.delete(cache_session.session_key) ->>> cache_session.exists(cache_session.session_key) -False ->>> cache_session['foo'] = 'bar' ->>> cache_session.save() ->>> cache_session.exists(cache_session.session_key) -True ->>> prev_key = cache_session.session_key ->>> cache_session.flush() ->>> cache_session.exists(prev_key) -False ->>> cache_session.session_key == prev_key -False ->>> cache_session.modified, cache_session.accessed -(True, True) ->>> cache_session['a'], cache_session['b'] = 'c', 'd' ->>> cache_session.save() ->>> prev_key = cache_session.session_key ->>> prev_data = cache_session.items() ->>> cache_session.cycle_key() ->>> cache_session.session_key == prev_key -False ->>> cache_session.items() == prev_data -True ->>> cache_session = CacheSession() ->>> cache_session.save() ->>> key = cache_session.session_key ->>> cache_session.exists(key) -True - ->>> Session.objects.filter(pk=cache_session.session_key).delete() ->>> cache_session = CacheSession(cache_session.session_key) ->>> cache_session.save() ->>> cache_session.delete(cache_session.session_key) - ->>> s = SessionBase() ->>> s._session['some key'] = 'exists' # Pre-populate the session with some data ->>> s.accessed = False # Reset to pretend this wasn't accessed previously - ->>> s.accessed, s.modified -(False, False) - ->>> s.pop('non existant key', 'does not exist') -'does not exist' ->>> s.accessed, s.modified -(True, False) - ->>> s.setdefault('foo', 'bar') -'bar' ->>> s.setdefault('foo', 'baz') -'bar' - ->>> s.accessed = False # Reset the accessed flag - ->>> s.pop('some key') -'exists' ->>> s.accessed, s.modified -(True, True) - ->>> s.pop('some key', 'does not exist') -'does not exist' - - ->>> s.get('update key', None) - -# test .update() ->>> s.modified = s.accessed = False # Reset to pretend this wasn't accessed previously ->>> s.update({'update key':1}) ->>> s.accessed, s.modified -(True, True) ->>> s.get('update key', None) -1 - -# test .has_key() ->>> s.modified = s.accessed = False # Reset to pretend this wasn't accessed previously ->>> s.has_key('update key') -True ->>> s.accessed, s.modified -(True, False) - -# test .values() ->>> s = SessionBase() ->>> s.values() -[] ->>> s.accessed -True ->>> s['x'] = 1 ->>> s.values() -[1] - -# test .iterkeys() ->>> s.accessed = False ->>> i = s.iterkeys() ->>> hasattr(i,'__iter__') -True ->>> s.accessed -True ->>> list(i) -['x'] - -# test .itervalues() ->>> s.accessed = False ->>> i = s.itervalues() ->>> hasattr(i,'__iter__') -True ->>> s.accessed -True ->>> list(i) -[1] - -# test .iteritems() ->>> s.accessed = False ->>> i = s.iteritems() ->>> hasattr(i,'__iter__') -True ->>> s.accessed -True ->>> list(i) -[('x', 1)] - -# test .clear() ->>> s.modified = s.accessed = False ->>> s.items() -[('x', 1)] ->>> s.clear() ->>> s.items() -[] ->>> s.accessed, s.modified -(True, True) - -######################### -# Custom session expiry # -######################### - ->>> from django.conf import settings ->>> from datetime import datetime, timedelta - ->>> td10 = timedelta(seconds=10) - -# A normal session has a max age equal to settings ->>> s.get_expiry_age() == settings.SESSION_COOKIE_AGE -True - -# So does a custom session with an idle expiration time of 0 (but it'll expire -# at browser close) ->>> s.set_expiry(0) ->>> s.get_expiry_age() == settings.SESSION_COOKIE_AGE -True - -# Custom session idle expiration time ->>> s.set_expiry(10) ->>> delta = s.get_expiry_date() - datetime.now() ->>> delta.seconds in (9, 10) -True ->>> age = s.get_expiry_age() ->>> age in (9, 10) -True - -# Custom session fixed expiry date (timedelta) ->>> s.set_expiry(td10) ->>> delta = s.get_expiry_date() - datetime.now() ->>> delta.seconds in (9, 10) -True ->>> age = s.get_expiry_age() ->>> age in (9, 10) -True - -# Custom session fixed expiry date (fixed datetime) ->>> s.set_expiry(datetime.now() + td10) ->>> delta = s.get_expiry_date() - datetime.now() ->>> delta.seconds in (9, 10) -True ->>> age = s.get_expiry_age() ->>> age in (9, 10) -True - -# Set back to default session age ->>> s.set_expiry(None) ->>> s.get_expiry_age() == settings.SESSION_COOKIE_AGE -True - -# Allow to set back to default session age even if no alternate has been set ->>> s.set_expiry(None) - - -# We're changing the setting then reverting back to the original setting at the -# end of these tests. ->>> original_expire_at_browser_close = settings.SESSION_EXPIRE_AT_BROWSER_CLOSE ->>> settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = False - -# Custom session age ->>> s.set_expiry(10) ->>> s.get_expire_at_browser_close() -False - -# Custom expire-at-browser-close ->>> s.set_expiry(0) ->>> s.get_expire_at_browser_close() -True - -# Default session age ->>> s.set_expiry(None) ->>> s.get_expire_at_browser_close() -False - ->>> settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = True - -# Custom session age ->>> s.set_expiry(10) ->>> s.get_expire_at_browser_close() -False - -# Custom expire-at-browser-close ->>> s.set_expiry(0) ->>> s.get_expire_at_browser_close() -True - -# Default session age ->>> s.set_expiry(None) ->>> s.get_expire_at_browser_close() -True - ->>> settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = original_expire_at_browser_close -""" - -if __name__ == '__main__': - import doctest - doctest.testmod() +from datetime import datetime, timedelta +from django.conf import settings +from django.contrib.sessions.backends.db import SessionStore as DatabaseSession +from django.contrib.sessions.backends.cache import SessionStore as CacheSession +from django.contrib.sessions.backends.cached_db import SessionStore as CacheDBSession +from django.contrib.sessions.backends.file import SessionStore as FileSession +from django.contrib.sessions.backends.base import SessionBase +from django.contrib.sessions.models import Session +from django.core.exceptions import ImproperlyConfigured +from django.test import TestCase +import shutil +import tempfile +import unittest + + +class SessionTestsMixin(object): + # This does not inherit from TestCase to avoid any tests being run with this + # class, which wouldn't work, and to allow different TestCase subclasses to + # be used. + + backend = None # subclasses must specify + + def setUp(self): + self.session = self.backend() + + def tearDown(self): + # NB: be careful to delete any sessions created; stale sessions fill up + # the /tmp (with some backends) and eventually overwhelm it after lots + # of runs (think buildbots) + self.session.delete() + + def test_new_session(self): + self.assertFalse(self.session.modified) + self.assertFalse(self.session.accessed) + + def test_get_empty(self): + self.assertEqual(self.session.get('cat'), None) + + def test_store(self): + self.session['cat'] = "dog" + self.assertTrue(self.session.modified) + self.assertEqual(self.session.pop('cat'), 'dog') + + def test_pop(self): + self.session['some key'] = 'exists' + # Need to reset these to pretend we haven't accessed it: + self.accessed = False + self.modified = False + + self.assertEqual(self.session.pop('some key'), 'exists') + self.assertTrue(self.session.accessed) + self.assertTrue(self.session.modified) + self.assertEqual(self.session.get('some key'), None) + + def test_pop_default(self): + self.assertEqual(self.session.pop('some key', 'does not exist'), + 'does not exist') + self.assertTrue(self.session.accessed) + self.assertFalse(self.session.modified) + + def test_setdefault(self): + self.assertEqual(self.session.setdefault('foo', 'bar'), 'bar') + self.assertEqual(self.session.setdefault('foo', 'baz'), 'bar') + self.assertTrue(self.session.accessed) + self.assertTrue(self.session.modified) + + def test_update(self): + self.session.update({'update key': 1}) + self.assertTrue(self.session.accessed) + self.assertTrue(self.session.modified) + self.assertEqual(self.session.get('update key', None), 1) + + def test_has_key(self): + self.session['some key'] = 1 + self.session.modified = False + self.session.accessed = False + self.assertTrue(self.session.has_key('some key')) + self.assertTrue(self.session.accessed) + self.assertFalse(self.session.modified) + + def test_values(self): + self.assertEqual(self.session.values(), []) + self.assertTrue(self.session.accessed) + self.session['some key'] = 1 + self.assertEqual(self.session.values(), [1]) + + def test_iterkeys(self): + self.session['x'] = 1 + self.session.modified = False + self.session.accessed = False + i = self.session.iterkeys() + self.assertTrue(hasattr(i, '__iter__')) + self.assertTrue(self.session.accessed) + self.assertFalse(self.session.modified) + self.assertEqual(list(i), ['x']) + + def test_iterkeys(self): + self.session['x'] = 1 + self.session.modified = False + self.session.accessed = False + i = self.session.itervalues() + self.assertTrue(hasattr(i, '__iter__')) + self.assertTrue(self.session.accessed) + self.assertFalse(self.session.modified) + self.assertEqual(list(i), [1]) + + def test_iteritems(self): + self.session['x'] = 1 + self.session.modified = False + self.session.accessed = False + i = self.session.iteritems() + self.assertTrue(hasattr(i, '__iter__')) + self.assertTrue(self.session.accessed) + self.assertFalse(self.session.modified) + self.assertEqual(list(i), [('x',1)]) + + def test_clear(self): + self.session['x'] = 1 + self.session.modified = False + self.session.accessed = False + self.assertEqual(self.session.items(), [('x',1)]) + self.session.clear() + self.assertEqual(self.session.items(), []) + self.assertTrue(self.session.accessed) + self.assertTrue(self.session.modified) + + def test_save(self): + self.session.save() + self.assertTrue(self.session.exists(self.session.session_key)) + + def test_delete(self): + self.session.delete(self.session.session_key) + self.assertFalse(self.session.exists(self.session.session_key)) + + def test_flush(self): + self.session['foo'] = 'bar' + self.session.save() + prev_key = self.session.session_key + self.session.flush() + self.assertFalse(self.session.exists(prev_key)) + self.assertNotEqual(self.session.session_key, prev_key) + self.assertTrue(self.session.modified) + self.assertTrue(self.session.accessed) + + def test_cycle(self): + self.session['a'], self.session['b'] = 'c', 'd' + self.session.save() + prev_key = self.session.session_key + prev_data = self.session.items() + self.session.cycle_key() + self.assertNotEqual(self.session.session_key, prev_key) + self.assertEqual(self.session.items(), prev_data) + + def test_invalid_key(self): + # Submitting an invalid session key (either by guessing, or if the db has + # removed the key) results in a new key being generated. + session = self.backend('1') + session.save() + self.assertNotEqual(session.session_key, '1') + self.assertEqual(session.get('cat'), None) + session.delete() + + # Custom session expiry + def test_default_expiry(self): + # A normal session has a max age equal to settings + self.assertEqual(self.session.get_expiry_age(), settings.SESSION_COOKIE_AGE) + + # So does a custom session with an idle expiration time of 0 (but it'll + # expire at browser close) + self.session.set_expiry(0) + self.assertEqual(self.session.get_expiry_age(), settings.SESSION_COOKIE_AGE) + + def test_custom_expiry_seconds(self): + # Using seconds + self.session.set_expiry(10) + delta = self.session.get_expiry_date() - datetime.now() + self.assertTrue(delta.seconds in (9, 10)) + + age = self.session.get_expiry_age() + self.assertTrue(age in (9, 10)) + + def test_custom_expiry_timedelta(self): + # Using timedelta + self.session.set_expiry(timedelta(seconds=10)) + delta = self.session.get_expiry_date() - datetime.now() + self.assertTrue(delta.seconds in (9, 10)) + + age = self.session.get_expiry_age() + self.assertTrue(age in (9, 10)) + + def test_custom_expiry_timedelta(self): + # Using timedelta + self.session.set_expiry(datetime.now() + timedelta(seconds=10)) + delta = self.session.get_expiry_date() - datetime.now() + self.assertTrue(delta.seconds in (9, 10)) + + age = self.session.get_expiry_age() + self.assertTrue(age in (9, 10)) + + def test_custom_expiry_reset(self): + self.session.set_expiry(None) + self.session.set_expiry(10) + self.session.set_expiry(None) + self.assertEqual(self.session.get_expiry_age(), settings.SESSION_COOKIE_AGE) + + def test_get_expire_at_browser_close(self): + # Tests get_expire_at_browser_close with different settings and different + # set_expiry calls + try: + try: + original_expire_at_browser_close = settings.SESSION_EXPIRE_AT_BROWSER_CLOSE + settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = False + + self.session.set_expiry(10) + self.assertFalse(self.session.get_expire_at_browser_close()) + + self.session.set_expiry(0) + self.assertTrue(self.session.get_expire_at_browser_close()) + + self.session.set_expiry(None) + self.assertFalse(self.session.get_expire_at_browser_close()) + + settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = True + + self.session.set_expiry(10) + self.assertFalse(self.session.get_expire_at_browser_close()) + + self.session.set_expiry(0) + self.assertTrue(self.session.get_expire_at_browser_close()) + + self.session.set_expiry(None) + self.assertTrue(self.session.get_expire_at_browser_close()) + + except: + raise + finally: + settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = original_expire_at_browser_close + + +class DatabaseSessionTests(SessionTestsMixin, TestCase): + + backend = DatabaseSession + + +class CacheDBSessionTests(SessionTestsMixin, TestCase): + + backend = CacheDBSession + +# Don't need DB flushing for these tests, so can use unittest.TestCase as base class +class FileSessionTests(SessionTestsMixin, unittest.TestCase): + + backend = FileSession + + def setUp(self): + super(FileSessionTests, self).setUp() + # Do file session tests in an isolated directory, and kill it after we're done. + self.original_session_file_path = settings.SESSION_FILE_PATH + self.temp_session_store = settings.SESSION_FILE_PATH = tempfile.mkdtemp() + + def tearDown(self): + settings.SESSION_FILE_PATH = self.original_session_file_path + shutil.rmtree(self.temp_session_store) + super(FileSessionTests, self).tearDown() + + def test_configuration_check(self): + # Make sure the file backend checks for a good storage dir + settings.SESSION_FILE_PATH = "/if/this/directory/exists/you/have/a/weird/computer" + self.assertRaises(ImproperlyConfigured, self.backend) + + +class CacheSessionTests(SessionTestsMixin, unittest.TestCase): + + backend = CacheSession diff --git a/django/contrib/sitemaps/__init__.py b/django/contrib/sitemaps/__init__.py index f877317f16..eaa7f85de9 100644 --- a/django/contrib/sitemaps/__init__.py +++ b/django/contrib/sitemaps/__init__.py @@ -65,11 +65,12 @@ class Sitemap(object): urls = [] for item in self.paginator.page(page).object_list: loc = "http://%s%s" % (current_site.domain, self.__get('location', item)) + priority = self.__get('priority', item, None) url_info = { 'location': loc, 'lastmod': self.__get('lastmod', item, None), 'changefreq': self.__get('changefreq', item, None), - 'priority': self.__get('priority', item, None) + 'priority': str(priority is not None and priority or '') } urls.append(url_info) return urls @@ -78,7 +79,7 @@ class FlatPageSitemap(Sitemap): def items(self): from django.contrib.sites.models import Site current_site = Site.objects.get_current() - return current_site.flatpage_set.all() + return current_site.flatpage_set.filter(registration_required=False) class GenericSitemap(Sitemap): priority = None diff --git a/django/contrib/sitemaps/models.py b/django/contrib/sitemaps/models.py new file mode 100644 index 0000000000..7ff128fa69 --- /dev/null +++ b/django/contrib/sitemaps/models.py @@ -0,0 +1 @@ +# This file intentionally left blank
\ No newline at end of file diff --git a/django/contrib/sitemaps/tests/__init__.py b/django/contrib/sitemaps/tests/__init__.py new file mode 100644 index 0000000000..c5b483cde2 --- /dev/null +++ b/django/contrib/sitemaps/tests/__init__.py @@ -0,0 +1 @@ +from django.contrib.sitemaps.tests.basic import * diff --git a/django/contrib/sitemaps/tests/basic.py b/django/contrib/sitemaps/tests/basic.py new file mode 100644 index 0000000000..ad04db258f --- /dev/null +++ b/django/contrib/sitemaps/tests/basic.py @@ -0,0 +1,77 @@ +from datetime import date +from django.conf import settings +from django.contrib.auth.models import User +from django.contrib.flatpages.models import FlatPage +from django.test import TestCase +from django.utils.formats import localize +from django.utils.translation import activate + + +class SitemapTests(TestCase): + urls = 'django.contrib.sitemaps.tests.urls' + + def setUp(self): + self.old_USE_L10N = settings.USE_L10N + # Create a user that will double as sitemap content + User.objects.create_user('testuser', 'test@example.com', 's3krit') + + def tearDown(self): + settings.USE_L10N = self.old_USE_L10N + + def test_simple_sitemap(self): + "A simple sitemap can be rendered" + # Retrieve the sitemap. + response = self.client.get('/simple/sitemap.xml') + # Check for all the important bits: + self.assertEquals(response.content, """<?xml version="1.0" encoding="UTF-8"?> +<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> +<url><loc>http://example.com/location/</loc><lastmod>%s</lastmod><changefreq>never</changefreq><priority>0.5</priority></url> +</urlset> +""" % date.today().strftime('%Y-%m-%d')) + + def test_localized_priority(self): + "The priority value should not be localized (Refs #14164)" + # Localization should be active + settings.USE_L10N = True + activate('fr') + self.assertEqual(u'0,3', localize(0.3)) + + # Retrieve the sitemap. Check that priorities + # haven't been rendered in localized format + response = self.client.get('/simple/sitemap.xml') + self.assertContains(response, '<priority>0.5</priority>') + self.assertContains(response, '<lastmod>%s</lastmod>' % date.today().strftime('%Y-%m-%d')) + + def test_generic_sitemap(self): + "A minimal generic sitemap can be rendered" + # Retrieve the sitemap. + response = self.client.get('/generic/sitemap.xml') + # Check for all the important bits: + self.assertEquals(response.content, """<?xml version="1.0" encoding="UTF-8"?> +<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> +<url><loc>http://example.com/users/testuser/</loc></url> +</urlset> +""") + + def test_flatpage_sitemap(self): + "Basic FlatPage sitemap test" + public = FlatPage.objects.create( + url=u'/public/', + title=u'Public Page', + enable_comments=True, + registration_required=False, + ) + public.sites.add(settings.SITE_ID) + private = FlatPage.objects.create( + url=u'/private/', + title=u'Public Page', + enable_comments=True, + registration_required=True + ) + private.sites.add(settings.SITE_ID) + response = self.client.get('/flatpages/sitemap.xml') + # Public flatpage should be in the sitemap + self.assertContains(response, '<loc>http://example.com%s</loc>' % public.url) + # Private flatpage should not be in the sitemap + self.assertNotContains(response, '<loc>http://example.com%s</loc>' % private.url) + diff --git a/django/contrib/sitemaps/tests/urls.py b/django/contrib/sitemaps/tests/urls.py new file mode 100644 index 0000000000..6cdba36b02 --- /dev/null +++ b/django/contrib/sitemaps/tests/urls.py @@ -0,0 +1,33 @@ +from datetime import datetime +from django.conf.urls.defaults import * +from django.contrib.sitemaps import Sitemap, GenericSitemap, FlatPageSitemap +from django.contrib.auth.models import User + +class SimpleSitemap(Sitemap): + changefreq = "never" + priority = 0.5 + location = '/location/' + lastmod = datetime.now() + + def items(self): + return [object()] + +simple_sitemaps = { + 'simple': SimpleSitemap, +} + +generic_sitemaps = { + 'generic': GenericSitemap({ + 'queryset': User.objects.all() + }), +} + +flatpage_sitemaps = { + 'flatpages': FlatPageSitemap, +} + +urlpatterns = patterns('django.contrib.sitemaps.views', + (r'^simple/sitemap\.xml$', 'sitemap', {'sitemaps': simple_sitemaps}), + (r'^generic/sitemap\.xml$', 'sitemap', {'sitemaps': generic_sitemaps}), + (r'^flatpages/sitemap\.xml$', 'sitemap', {'sitemaps': flatpage_sitemaps}), +) diff --git a/django/core/cache/__init__.py b/django/core/cache/__init__.py index 1b602908cb..680f724f94 100644 --- a/django/core/cache/__init__.py +++ b/django/core/cache/__init__.py @@ -18,7 +18,7 @@ See docs/cache.txt for information on the public API. from cgi import parse_qsl from django.conf import settings from django.core import signals -from django.core.cache.backends.base import InvalidCacheBackendError +from django.core.cache.backends.base import InvalidCacheBackendError, CacheKeyWarning from django.utils import importlib # Name for use in settings file --> name of module in "backends" directory. diff --git a/django/core/cache/backends/base.py b/django/core/cache/backends/base.py index e58267a2e9..83dd461804 100644 --- a/django/core/cache/backends/base.py +++ b/django/core/cache/backends/base.py @@ -1,10 +1,18 @@ "Base Cache class." -from django.core.exceptions import ImproperlyConfigured +import warnings + +from django.core.exceptions import ImproperlyConfigured, DjangoRuntimeWarning class InvalidCacheBackendError(ImproperlyConfigured): pass +class CacheKeyWarning(DjangoRuntimeWarning): + pass + +# Memcached does not accept keys longer than this. +MEMCACHE_MAX_KEY_LENGTH = 250 + class BaseCache(object): def __init__(self, params): timeout = params.get('timeout', 300) @@ -116,3 +124,21 @@ class BaseCache(object): def clear(self): """Remove *all* values from the cache at once.""" raise NotImplementedError + + def validate_key(self, key): + """ + Warn about keys that would not be portable to the memcached + backend. This encourages (but does not force) writing backend-portable + cache code. + + """ + if len(key) > MEMCACHE_MAX_KEY_LENGTH: + warnings.warn('Cache key will cause errors if used with memcached: ' + '%s (longer than %s)' % (key, MEMCACHE_MAX_KEY_LENGTH), + CacheKeyWarning) + for char in key: + if ord(char) < 33 or ord(char) == 127: + warnings.warn('Cache key contains characters that will cause ' + 'errors if used with memcached: %r' % key, + CacheKeyWarning) + diff --git a/django/core/cache/backends/db.py b/django/core/cache/backends/db.py index 3398e6a85b..c4429c80b3 100644 --- a/django/core/cache/backends/db.py +++ b/django/core/cache/backends/db.py @@ -1,7 +1,7 @@ "Database cache backend." from django.core.cache.backends.base import BaseCache -from django.db import connection, transaction, DatabaseError +from django.db import connections, router, transaction, DatabaseError import base64, time from datetime import datetime try: @@ -9,10 +9,31 @@ try: except ImportError: import pickle +class Options(object): + """A class that will quack like a Django model _meta class. + + This allows cache operations to be controlled by the router + """ + def __init__(self, table): + self.db_table = table + self.app_label = 'django_cache' + self.module_name = 'cacheentry' + self.verbose_name = 'cache entry' + self.verbose_name_plural = 'cache entries' + self.object_name = 'CacheEntry' + self.abstract = False + self.managed = True + self.proxy = False + class CacheClass(BaseCache): def __init__(self, table, params): BaseCache.__init__(self, params) - self._table = connection.ops.quote_name(table) + self._table = table + + class CacheEntry(object): + _meta = Options(table) + self.cache_model_class = CacheEntry + max_entries = params.get('max_entries', 300) try: self._max_entries = int(max_entries) @@ -25,78 +46,100 @@ class CacheClass(BaseCache): self._cull_frequency = 3 def get(self, key, default=None): - cursor = connection.cursor() - cursor.execute("SELECT cache_key, value, expires FROM %s WHERE cache_key = %%s" % self._table, [key]) + self.validate_key(key) + db = router.db_for_read(self.cache_model_class) + table = connections[db].ops.quote_name(self._table) + cursor = connections[db].cursor() + + cursor.execute("SELECT cache_key, value, expires FROM %s WHERE cache_key = %%s" % table, [key]) row = cursor.fetchone() if row is None: return default now = datetime.now() if row[2] < now: - cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % self._table, [key]) - transaction.commit_unless_managed() + db = router.db_for_write(self.cache_model_class) + cursor = connections[db].cursor() + cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % table, [key]) + transaction.commit_unless_managed(using=db) return default - value = connection.ops.process_clob(row[1]) + value = connections[db].ops.process_clob(row[1]) return pickle.loads(base64.decodestring(value)) def set(self, key, value, timeout=None): + self.validate_key(key) self._base_set('set', key, value, timeout) def add(self, key, value, timeout=None): + self.validate_key(key) return self._base_set('add', key, value, timeout) def _base_set(self, mode, key, value, timeout=None): if timeout is None: timeout = self.default_timeout - cursor = connection.cursor() - cursor.execute("SELECT COUNT(*) FROM %s" % self._table) + db = router.db_for_write(self.cache_model_class) + table = connections[db].ops.quote_name(self._table) + cursor = connections[db].cursor() + + cursor.execute("SELECT COUNT(*) FROM %s" % table) num = cursor.fetchone()[0] now = datetime.now().replace(microsecond=0) exp = datetime.fromtimestamp(time.time() + timeout).replace(microsecond=0) if num > self._max_entries: - self._cull(cursor, now) + self._cull(db, cursor, now) encoded = base64.encodestring(pickle.dumps(value, 2)).strip() - cursor.execute("SELECT cache_key, expires FROM %s WHERE cache_key = %%s" % self._table, [key]) + cursor.execute("SELECT cache_key, expires FROM %s WHERE cache_key = %%s" % table, [key]) try: result = cursor.fetchone() if result and (mode == 'set' or (mode == 'add' and result[1] < now)): - cursor.execute("UPDATE %s SET value = %%s, expires = %%s WHERE cache_key = %%s" % self._table, - [encoded, connection.ops.value_to_db_datetime(exp), key]) + cursor.execute("UPDATE %s SET value = %%s, expires = %%s WHERE cache_key = %%s" % table, + [encoded, connections[db].ops.value_to_db_datetime(exp), key]) else: - cursor.execute("INSERT INTO %s (cache_key, value, expires) VALUES (%%s, %%s, %%s)" % self._table, - [key, encoded, connection.ops.value_to_db_datetime(exp)]) + cursor.execute("INSERT INTO %s (cache_key, value, expires) VALUES (%%s, %%s, %%s)" % table, + [key, encoded, connections[db].ops.value_to_db_datetime(exp)]) except DatabaseError: # To be threadsafe, updates/inserts are allowed to fail silently - transaction.rollback_unless_managed() + transaction.rollback_unless_managed(using=db) return False else: - transaction.commit_unless_managed() + transaction.commit_unless_managed(using=db) return True def delete(self, key): - cursor = connection.cursor() - cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % self._table, [key]) - transaction.commit_unless_managed() + self.validate_key(key) + db = router.db_for_write(self.cache_model_class) + table = connections[db].ops.quote_name(self._table) + cursor = connections[db].cursor() + + cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % table, [key]) + transaction.commit_unless_managed(using=db) def has_key(self, key): + self.validate_key(key) + db = router.db_for_read(self.cache_model_class) + table = connections[db].ops.quote_name(self._table) + cursor = connections[db].cursor() + now = datetime.now().replace(microsecond=0) - cursor = connection.cursor() - cursor.execute("SELECT cache_key FROM %s WHERE cache_key = %%s and expires > %%s" % self._table, - [key, connection.ops.value_to_db_datetime(now)]) + cursor.execute("SELECT cache_key FROM %s WHERE cache_key = %%s and expires > %%s" % table, + [key, connections[db].ops.value_to_db_datetime(now)]) return cursor.fetchone() is not None - def _cull(self, cursor, now): + def _cull(self, db, cursor, now): if self._cull_frequency == 0: self.clear() else: - cursor.execute("DELETE FROM %s WHERE expires < %%s" % self._table, - [connection.ops.value_to_db_datetime(now)]) - cursor.execute("SELECT COUNT(*) FROM %s" % self._table) + table = connections[db].ops.quote_name(self._table) + cursor.execute("DELETE FROM %s WHERE expires < %%s" % table, + [connections[db].ops.value_to_db_datetime(now)]) + cursor.execute("SELECT COUNT(*) FROM %s" % table) num = cursor.fetchone()[0] if num > self._max_entries: - cursor.execute("SELECT cache_key FROM %s ORDER BY cache_key LIMIT 1 OFFSET %%s" % self._table, [num / self._cull_frequency]) - cursor.execute("DELETE FROM %s WHERE cache_key < %%s" % self._table, [cursor.fetchone()[0]]) + cursor.execute("SELECT cache_key FROM %s ORDER BY cache_key LIMIT 1 OFFSET %%s" % table, [num / self._cull_frequency]) + cursor.execute("DELETE FROM %s WHERE cache_key < %%s" % table, [cursor.fetchone()[0]]) def clear(self): - cursor = connection.cursor() - cursor.execute('DELETE FROM %s' % self._table) + db = router.db_for_write(self.cache_model_class) + table = connections[db].ops.quote_name(self._table) + cursor = connections[db].cursor() + cursor.execute('DELETE FROM %s' % table) diff --git a/django/core/cache/backends/dummy.py b/django/core/cache/backends/dummy.py index 4337484cb1..f73b7408bc 100644 --- a/django/core/cache/backends/dummy.py +++ b/django/core/cache/backends/dummy.py @@ -6,22 +6,25 @@ class CacheClass(BaseCache): def __init__(self, *args, **kwargs): pass - def add(self, *args, **kwargs): + def add(self, key, *args, **kwargs): + self.validate_key(key) return True def get(self, key, default=None): + self.validate_key(key) return default - def set(self, *args, **kwargs): - pass + def set(self, key, *args, **kwargs): + self.validate_key(key) - def delete(self, *args, **kwargs): - pass + def delete(self, key, *args, **kwargs): + self.validate_key(key) def get_many(self, *args, **kwargs): return {} - def has_key(self, *args, **kwargs): + def has_key(self, key, *args, **kwargs): + self.validate_key(key) return False def set_many(self, *args, **kwargs): diff --git a/django/core/cache/backends/filebased.py b/django/core/cache/backends/filebased.py index fe833336d0..46e69f3091 100644 --- a/django/core/cache/backends/filebased.py +++ b/django/core/cache/backends/filebased.py @@ -32,6 +32,7 @@ class CacheClass(BaseCache): self._createdir() def add(self, key, value, timeout=None): + self.validate_key(key) if self.has_key(key): return False @@ -39,6 +40,7 @@ class CacheClass(BaseCache): return True def get(self, key, default=None): + self.validate_key(key) fname = self._key_to_file(key) try: f = open(fname, 'rb') @@ -56,6 +58,7 @@ class CacheClass(BaseCache): return default def set(self, key, value, timeout=None): + self.validate_key(key) fname = self._key_to_file(key) dirname = os.path.dirname(fname) @@ -79,6 +82,7 @@ class CacheClass(BaseCache): pass def delete(self, key): + self.validate_key(key) try: self._delete(self._key_to_file(key)) except (IOError, OSError): @@ -95,6 +99,7 @@ class CacheClass(BaseCache): pass def has_key(self, key): + self.validate_key(key) fname = self._key_to_file(key) try: f = open(fname, 'rb') @@ -116,7 +121,7 @@ class CacheClass(BaseCache): return try: - filelist = os.listdir(self._dir) + filelist = sorted(os.listdir(self._dir)) except (IOError, OSError): return diff --git a/django/core/cache/backends/locmem.py b/django/core/cache/backends/locmem.py index eff1201b97..fe33d33307 100644 --- a/django/core/cache/backends/locmem.py +++ b/django/core/cache/backends/locmem.py @@ -30,6 +30,7 @@ class CacheClass(BaseCache): self._lock = RWLock() def add(self, key, value, timeout=None): + self.validate_key(key) self._lock.writer_enters() try: exp = self._expire_info.get(key) @@ -44,6 +45,7 @@ class CacheClass(BaseCache): self._lock.writer_leaves() def get(self, key, default=None): + self.validate_key(key) self._lock.reader_enters() try: exp = self._expire_info.get(key) @@ -76,6 +78,7 @@ class CacheClass(BaseCache): self._expire_info[key] = time.time() + timeout def set(self, key, value, timeout=None): + self.validate_key(key) self._lock.writer_enters() # Python 2.4 doesn't allow combined try-except-finally blocks. try: @@ -87,6 +90,7 @@ class CacheClass(BaseCache): self._lock.writer_leaves() def has_key(self, key): + self.validate_key(key) self._lock.reader_enters() try: exp = self._expire_info.get(key) @@ -127,6 +131,7 @@ class CacheClass(BaseCache): pass def delete(self, key): + self.validate_key(key) self._lock.writer_enters() try: self._delete(key) diff --git a/django/core/exceptions.py b/django/core/exceptions.py index ee6d5fe37b..21be8702fa 100644 --- a/django/core/exceptions.py +++ b/django/core/exceptions.py @@ -1,4 +1,9 @@ -"Global Django exceptions" +""" +Global Django exception and warning classes. +""" + +class DjangoRuntimeWarning(RuntimeWarning): + pass class ObjectDoesNotExist(Exception): "The requested object does not exist" diff --git a/django/core/files/images.py b/django/core/files/images.py index 55008b548a..228a7118c5 100644 --- a/django/core/files/images.py +++ b/django/core/files/images.py @@ -23,23 +23,26 @@ class ImageFile(File): if not hasattr(self, '_dimensions_cache'): close = self.closed self.open() - self._dimensions_cache = get_image_dimensions(self) - if close: - self.close() + self._dimensions_cache = get_image_dimensions(self, close=close) return self._dimensions_cache -def get_image_dimensions(file_or_path): - """Returns the (width, height) of an image, given an open file or a path.""" +def get_image_dimensions(file_or_path, close=False): + """ + Returns the (width, height) of an image, given an open file or a path. Set + 'close' to True to close the file at the end if it is initially in an open + state. + """ # Try to import PIL in either of the two ways it can end up installed. try: from PIL import ImageFile as PILImageFile except ImportError: import ImageFile as PILImageFile - + p = PILImageFile.Parser() - close = False if hasattr(file_or_path, 'read'): file = file_or_path + file_pos = file.tell() + file.seek(0) else: file = open(file_or_path, 'rb') close = True @@ -55,3 +58,5 @@ def get_image_dimensions(file_or_path): finally: if close: file.close() + else: + file.seek(file_pos) diff --git a/django/core/handlers/base.py b/django/core/handlers/base.py index 79f6607214..b03c2fd71e 100644 --- a/django/core/handlers/base.py +++ b/django/core/handlers/base.py @@ -137,9 +137,8 @@ class BaseHandler(object): raise except: # Handle everything else, including SuspiciousOperation, etc. # Get the exception info now, in case another exception is thrown later. - exc_info = sys.exc_info() receivers = signals.got_request_exception.send(sender=self.__class__, request=request) - return self.handle_uncaught_exception(request, resolver, exc_info) + return self.handle_uncaught_exception(request, resolver, sys.exc_info()) finally: # Reset URLconf for this thread on the way out for complete # isolation of request.urlconf diff --git a/django/core/handlers/modpython.py b/django/core/handlers/modpython.py index b1e3e17227..17e739600c 100644 --- a/django/core/handlers/modpython.py +++ b/django/core/handlers/modpython.py @@ -1,5 +1,6 @@ import os from pprint import pformat +from warnings import warn from django import http from django.core import signals @@ -179,6 +180,9 @@ class ModPythonHandler(BaseHandler): request_class = ModPythonRequest def __call__(self, req): + warn(('The mod_python handler is deprecated; use a WSGI or FastCGI server instead.'), + PendingDeprecationWarning) + # mod_python fakes the environ, and thus doesn't process SetEnv. This fixes that os.environ.update(req.subprocess_env) diff --git a/django/core/management/__init__.py b/django/core/management/__init__.py index a61b648674..dabde96377 100644 --- a/django/core/management/__init__.py +++ b/django/core/management/__init__.py @@ -250,15 +250,15 @@ class ManagementUtility(object): """ try: app_name = get_commands()[subcommand] - if isinstance(app_name, BaseCommand): - # If the command is already loaded, use it directly. - klass = app_name - else: - klass = load_command_class(app_name, subcommand) except KeyError: sys.stderr.write("Unknown command: %r\nType '%s help' for usage.\n" % \ (subcommand, self.prog_name)) sys.exit(1) + if isinstance(app_name, BaseCommand): + # If the command is already loaded, use it directly. + klass = app_name + else: + klass = load_command_class(app_name, subcommand) return klass def autocomplete(self): diff --git a/django/core/management/base.py b/django/core/management/base.py index 4016faaa7a..dcd83ff300 100644 --- a/django/core/management/base.py +++ b/django/core/management/base.py @@ -118,7 +118,7 @@ class BaseCommand(object): # Metadata about this command. option_list = ( make_option('-v', '--verbosity', action='store', dest='verbosity', default='1', - type='choice', choices=['0', '1', '2'], + type='choice', choices=['0', '1', '2', '3'], help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'), make_option('--settings', help='The Python path to a settings module, e.g. "myproject.settings.main". If this isn\'t provided, the DJANGO_SETTINGS_MODULE environment variable will be used.'), @@ -213,20 +213,24 @@ class BaseCommand(object): sys.stderr.write(smart_str(self.style.ERROR('Error: %s\n' % e))) sys.exit(1) try: + self.stdout = options.get('stdout', sys.stdout) + self.stderr = options.get('stderr', sys.stderr) if self.requires_model_validation: self.validate() output = self.handle(*args, **options) if output: if self.output_transaction: - # This needs to be imported here, because it relies on settings. - from django.db import connection + # This needs to be imported here, because it relies on + # settings. + from django.db import connections, DEFAULT_DB_ALIAS + connection = connections[options.get('database', DEFAULT_DB_ALIAS)] if connection.ops.start_transaction_sql(): - print self.style.SQL_KEYWORD(connection.ops.start_transaction_sql()) - print output + self.stdout.write(self.style.SQL_KEYWORD(connection.ops.start_transaction_sql())) + self.stdout.write(output) if self.output_transaction: - print self.style.SQL_KEYWORD("COMMIT;") + self.stdout.write(self.style.SQL_KEYWORD("COMMIT;") + '\n') except CommandError, e: - sys.stderr.write(smart_str(self.style.ERROR('Error: %s\n' % e))) + self.stderr.write(smart_str(self.style.ERROR('Error: %s\n' % e))) sys.exit(1) def validate(self, app=None, display_num_errors=False): @@ -248,7 +252,7 @@ class BaseCommand(object): error_text = s.read() raise CommandError("One or more models did not validate:\n%s" % error_text) if display_num_errors: - print "%s error%s found" % (num_errors, num_errors != 1 and 's' or '') + self.stdout.write("%s error%s found\n" % (num_errors, num_errors != 1 and 's' or '')) def handle(self, *args, **options): """ @@ -390,9 +394,9 @@ def copy_helper(style, app_or_project, name, directory, other_name=''): relative_dir = d[len(template_dir)+1:].replace('%s_name' % app_or_project, name) if relative_dir: os.mkdir(os.path.join(top_dir, relative_dir)) - for i, subdir in enumerate(subdirs): + for subdir in subdirs[:]: if subdir.startswith('.'): - del subdirs[i] + subdirs.remove(subdir) for f in files: if not f.endswith('.py'): # Ignore .pyc, .pyo, .py.class etc, as they cause various diff --git a/django/core/management/commands/dumpdata.py b/django/core/management/commands/dumpdata.py index aaaa5845a5..706bf601f9 100644 --- a/django/core/management/commands/dumpdata.py +++ b/django/core/management/commands/dumpdata.py @@ -16,11 +16,15 @@ class Command(BaseCommand): default=DEFAULT_DB_ALIAS, help='Nominates a specific database to load ' 'fixtures into. Defaults to the "default" database.'), make_option('-e', '--exclude', dest='exclude',action='append', default=[], - help='App to exclude (use multiple --exclude to exclude multiple apps).'), + help='An appname or appname.ModelName to exclude (use multiple --exclude to exclude multiple apps/models).'), make_option('-n', '--natural', action='store_true', dest='use_natural_keys', default=False, help='Use natural keys if they are available.'), + make_option('-a', '--all', action='store_true', dest='use_base_manager', default=False, + help="Use Django's base manager to dump all models stored in the database, including those that would otherwise be filtered or modified by a custom manager."), ) - help = 'Output the contents of the database as a fixture of the given format.' + help = ("Output the contents of the database as a fixture of the given " + "format (using each model's default manager unless --all is " + "specified).") args = '[appname appname.ModelName ...]' def handle(self, *app_labels, **options): @@ -30,11 +34,26 @@ class Command(BaseCommand): indent = options.get('indent',None) using = options.get('database', DEFAULT_DB_ALIAS) connection = connections[using] - exclude = options.get('exclude',[]) + excludes = options.get('exclude',[]) show_traceback = options.get('traceback', False) use_natural_keys = options.get('use_natural_keys', False) - - excluded_apps = set(get_app(app_label) for app_label in exclude) + use_base_manager = options.get('use_base_manager', False) + + excluded_apps = set() + excluded_models = set() + for exclude in excludes: + if '.' in exclude: + app_label, model_name = exclude.split('.', 1) + model_obj = get_model(app_label, model_name) + if not model_obj: + raise CommandError('Unknown model in excludes: %s' % exclude) + excluded_models.add(model_obj) + else: + try: + app_obj = get_app(exclude) + excluded_apps.add(app_obj) + except ImproperlyConfigured: + raise CommandError('Unknown app in excludes: %s' % exclude) if len(app_labels) == 0: app_list = SortedDict((app, None) for app in get_apps() if app not in excluded_apps) @@ -47,7 +66,8 @@ class Command(BaseCommand): app = get_app(app_label) except ImproperlyConfigured: raise CommandError("Unknown application: %s" % app_label) - + if app in excluded_apps: + continue model = get_model(app_label, model_label) if model is None: raise CommandError("Unknown model: %s.%s" % (app_label, model_label)) @@ -64,6 +84,8 @@ class Command(BaseCommand): app = get_app(app_label) except ImproperlyConfigured: raise CommandError("Unknown application: %s" % app_label) + if app in excluded_apps: + continue app_list[app] = None # Check that the serialization format exists; this is a shortcut to @@ -79,8 +101,13 @@ class Command(BaseCommand): # Now collate the objects to be serialized. objects = [] for model in sort_dependencies(app_list.items()): + if model in excluded_models: + continue if not model._meta.proxy and router.allow_syncdb(using, model): - objects.extend(model._default_manager.using(using).all()) + if use_base_manager: + objects.extend(model._base_manager.using(using).all()) + else: + objects.extend(model._default_manager.using(using).all()) try: return serializers.serialize(format, objects, indent=indent, @@ -163,4 +190,4 @@ def sort_dependencies(app_list): ) model_dependencies = skipped - return model_list
\ No newline at end of file + return model_list diff --git a/django/core/management/commands/flush.py b/django/core/management/commands/flush.py index 6836fe35ca..093c6db7b9 100644 --- a/django/core/management/commands/flush.py +++ b/django/core/management/commands/flush.py @@ -1,7 +1,7 @@ from optparse import make_option from django.conf import settings -from django.db import connections, transaction, models, DEFAULT_DB_ALIAS +from django.db import connections, router, transaction, models, DEFAULT_DB_ALIAS from django.core.management import call_command from django.core.management.base import NoArgsCommand, CommandError from django.core.management.color import no_style @@ -66,7 +66,13 @@ The full error: %s""" % (connection.settings_dict['NAME'], e)) # Emit the post sync signal. This allows individual # applications to respond as if the database had been # sync'd from scratch. - emit_post_sync_signal(models.get_models(), verbosity, interactive, db) + all_models = [] + for app in models.get_apps(): + all_models.extend([ + m for m in models.get_models(app, include_auto_created=True) + if router.allow_syncdb(db, m) + ]) + emit_post_sync_signal(set(all_models), verbosity, interactive, db) # Reinstall the initial_data fixture. kwargs = options.copy() diff --git a/django/core/management/commands/loaddata.py b/django/core/management/commands/loaddata.py index 6212d6151d..0b752b57e2 100644 --- a/django/core/management/commands/loaddata.py +++ b/django/core/management/commands/loaddata.py @@ -47,7 +47,8 @@ class Command(BaseCommand): # Keep a count of the installed objects and fixtures fixture_count = 0 - object_count = 0 + loaded_object_count = 0 + fixture_object_count = 0 models = set() humanize = lambda dirname: dirname and "'%s'" % dirname or 'absolute path' @@ -111,11 +112,11 @@ class Command(BaseCommand): formats = [] if formats: - if verbosity > 1: - print "Loading '%s' fixtures..." % fixture_name + if verbosity >= 2: + self.stdout.write("Loading '%s' fixtures...\n" % fixture_name) else: - sys.stderr.write( - self.style.ERROR("Problem installing fixture '%s': %s is not a known serialization format." % + self.stderr.write( + self.style.ERROR("Problem installing fixture '%s': %s is not a known serialization format.\n" % (fixture_name, format))) transaction.rollback(using=using) transaction.leave_transaction_management(using=using) @@ -127,8 +128,8 @@ class Command(BaseCommand): fixture_dirs = app_fixtures + list(settings.FIXTURE_DIRS) + [''] for fixture_dir in fixture_dirs: - if verbosity > 1: - print "Checking %s for fixtures..." % humanize(fixture_dir) + if verbosity >= 2: + self.stdout.write("Checking %s for fixtures...\n" % humanize(fixture_dir)) label_found = False for combo in product([using, None], formats, compression_formats): @@ -140,34 +141,37 @@ class Command(BaseCommand): if p ) - if verbosity > 1: - print "Trying %s for %s fixture '%s'..." % \ - (humanize(fixture_dir), file_name, fixture_name) + if verbosity >= 3: + self.stdout.write("Trying %s for %s fixture '%s'...\n" % \ + (humanize(fixture_dir), file_name, fixture_name)) full_path = os.path.join(fixture_dir, file_name) open_method = compression_types[compression_format] try: fixture = open_method(full_path, 'r') if label_found: fixture.close() - print self.style.ERROR("Multiple fixtures named '%s' in %s. Aborting." % - (fixture_name, humanize(fixture_dir))) + self.stderr.write(self.style.ERROR("Multiple fixtures named '%s' in %s. Aborting.\n" % + (fixture_name, humanize(fixture_dir)))) transaction.rollback(using=using) transaction.leave_transaction_management(using=using) return else: fixture_count += 1 objects_in_fixture = 0 - if verbosity > 0: - print "Installing %s fixture '%s' from %s." % \ - (format, fixture_name, humanize(fixture_dir)) + loaded_objects_in_fixture = 0 + if verbosity >= 2: + self.stdout.write("Installing %s fixture '%s' from %s.\n" % \ + (format, fixture_name, humanize(fixture_dir))) try: objects = serializers.deserialize(format, fixture, using=using) for obj in objects: + objects_in_fixture += 1 if router.allow_syncdb(using, obj.object.__class__): - objects_in_fixture += 1 + loaded_objects_in_fixture += 1 models.add(obj.object.__class__) obj.save(using=using) - object_count += objects_in_fixture + loaded_object_count += loaded_objects_in_fixture + fixture_object_count += objects_in_fixture label_found = True except (SystemExit, KeyboardInterrupt): raise @@ -179,7 +183,7 @@ class Command(BaseCommand): if show_traceback: traceback.print_exc() else: - sys.stderr.write( + self.stderr.write( self.style.ERROR("Problem installing fixture '%s': %s\n" % (full_path, ''.join(traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback))))) @@ -189,25 +193,25 @@ class Command(BaseCommand): # If the fixture we loaded contains 0 objects, assume that an # error was encountered during fixture loading. if objects_in_fixture == 0: - sys.stderr.write( - self.style.ERROR("No fixture data found for '%s'. (File format may be invalid.)" % + self.stderr.write( + self.style.ERROR("No fixture data found for '%s'. (File format may be invalid.)\n" % (fixture_name))) transaction.rollback(using=using) transaction.leave_transaction_management(using=using) return except Exception, e: - if verbosity > 1: - print "No %s fixture '%s' in %s." % \ - (format, fixture_name, humanize(fixture_dir)) + if verbosity >= 2: + self.stdout.write("No %s fixture '%s' in %s.\n" % \ + (format, fixture_name, humanize(fixture_dir))) # If we found even one object in a fixture, we need to reset the # database sequences. - if object_count > 0: + if loaded_object_count > 0: sequence_sql = connection.ops.sequence_reset_sql(self.style, models) if sequence_sql: - if verbosity > 1: - print "Resetting sequences" + if verbosity >= 2: + self.stdout.write("Resetting sequences\n") for line in sequence_sql: cursor.execute(line) @@ -215,12 +219,17 @@ class Command(BaseCommand): transaction.commit(using=using) transaction.leave_transaction_management(using=using) - if object_count == 0: - if verbosity > 0: - print "No fixtures found." + if fixture_object_count == 0: + if verbosity >= 1: + self.stdout.write("No fixtures found.\n") else: - if verbosity > 0: - print "Installed %d object(s) from %d fixture(s)" % (object_count, fixture_count) + if verbosity >= 1: + if fixture_object_count == loaded_object_count: + self.stdout.write("Installed %d object(s) from %d fixture(s)\n" % ( + loaded_object_count, fixture_count)) + else: + self.stdout.write("Installed %d object(s) (of %d) from %d fixture(s)\n" % ( + loaded_object_count, fixture_object_count, fixture_count)) # Close the DB connection. This is required as a workaround for an # edge case in MySQL: if the same connection is used to diff --git a/django/core/management/commands/syncdb.py b/django/core/management/commands/syncdb.py index 6f1a198653..835e06f24a 100644 --- a/django/core/management/commands/syncdb.py +++ b/django/core/management/commands/syncdb.py @@ -77,10 +77,12 @@ class Command(NoArgsCommand): ) # Create the tables for each model + if verbosity >= 1: + print "Creating tables ..." for app_name, model_list in manifest.items(): for model in model_list: # Create the model's database table, if it doesn't already exist. - if verbosity >= 2: + if verbosity >= 3: print "Processing %s.%s model" % (app_name, model._meta.object_name) sql, references = connection.creation.sql_create_model(model, self.style, seen_models) seen_models.add(model) @@ -108,12 +110,14 @@ class Command(NoArgsCommand): # Install custom SQL for the app (but only if this # is a model we've just created) + if verbosity >= 1: + print "Installing custom SQL ..." for app_name, model_list in manifest.items(): for model in model_list: if model in created_models: custom_sql = custom_sql_for_model(model, self.style, connection) if custom_sql: - if verbosity >= 1: + if verbosity >= 2: print "Installing custom SQL for %s.%s model" % (app_name, model._meta.object_name) try: for sql in custom_sql: @@ -128,16 +132,18 @@ class Command(NoArgsCommand): else: transaction.commit_unless_managed(using=db) else: - if verbosity >= 2: + if verbosity >= 3: print "No custom SQL for %s.%s model" % (app_name, model._meta.object_name) + if verbosity >= 1: + print "Installing indexes ..." # Install SQL indicies for all newly created models for app_name, model_list in manifest.items(): for model in model_list: if model in created_models: index_sql = connection.creation.sql_indexes_for_model(model, self.style) if index_sql: - if verbosity >= 1: + if verbosity >= 2: print "Installing index for %s.%s model" % (app_name, model._meta.object_name) try: for sql in index_sql: diff --git a/django/core/management/commands/testserver.py b/django/core/management/commands/testserver.py index 6c75a3e2b6..d3d9698c9a 100644 --- a/django/core/management/commands/testserver.py +++ b/django/core/management/commands/testserver.py @@ -4,6 +4,8 @@ from optparse import make_option class Command(BaseCommand): option_list = BaseCommand.option_list + ( + make_option('--noinput', action='store_false', dest='interactive', default=True, + help='Tells Django to NOT prompt the user for input of any kind.'), make_option('--addrport', action='store', dest='addrport', type='string', default='', help='port number or ipaddr:port to run the server on'), @@ -18,10 +20,11 @@ class Command(BaseCommand): from django.db import connection verbosity = int(options.get('verbosity', 1)) + interactive = options.get('interactive', True) addrport = options.get('addrport') # Create a test database. - db_name = connection.creation.create_test_db(verbosity=verbosity) + db_name = connection.creation.create_test_db(verbosity=verbosity, autoclobber=not interactive) # Import the fixture data into the test database. call_command('loaddata', *fixture_labels, **{'verbosity': verbosity}) diff --git a/django/core/urlresolvers.py b/django/core/urlresolvers.py index cad57a5d9f..5e565f75d6 100644 --- a/django/core/urlresolvers.py +++ b/django/core/urlresolvers.py @@ -30,6 +30,40 @@ _prefixes = {} # Overridden URLconfs for each thread are stored here. _urlconfs = {} +class ResolverMatch(object): + def __init__(self, func, args, kwargs, url_name=None, app_name=None, namespaces=None): + self.func = func + self.args = args + self.kwargs = kwargs + self.app_name = app_name + if namespaces: + self.namespaces = [x for x in namespaces if x] + else: + self.namespaces = [] + if not url_name: + if not hasattr(func, '__name__'): + # An instance of a callable class + url_name = '.'.join([func.__class__.__module__, func.__class__.__name__]) + else: + # A function + url_name = '.'.join([func.__module__, func.__name__]) + self.url_name = url_name + + def namespace(self): + return ':'.join(self.namespaces) + namespace = property(namespace) + + def view_name(self): + return ':'.join([ x for x in [ self.namespace, self.url_name ] if x ]) + view_name = property(view_name) + + def __getitem__(self, index): + return (self.func, self.args, self.kwargs)[index] + + def __repr__(self): + return "ResolverMatch(func=%s, args=%s, kwargs=%s, url_name='%s', app_name='%s', namespace='%s')" % ( + self.func, self.args, self.kwargs, self.url_name, self.app_name, self.namespace) + class Resolver404(Http404): pass @@ -120,7 +154,7 @@ class RegexURLPattern(object): # In both cases, pass any extra_kwargs as **kwargs. kwargs.update(self.default_args) - return self.callback, args, kwargs + return ResolverMatch(self.callback, args, kwargs, self.name) def _get_callback(self): if self._callback is not None: @@ -183,7 +217,8 @@ class RegexURLResolver(object): else: bits = normalize(p_pattern) lookups.appendlist(pattern.callback, (bits, p_pattern)) - lookups.appendlist(pattern.name, (bits, p_pattern)) + if pattern.name is not None: + lookups.appendlist(pattern.name, (bits, p_pattern)) self._reverse_dict = lookups self._namespace_dict = namespaces self._app_dict = apps @@ -217,17 +252,17 @@ class RegexURLResolver(object): except Resolver404, e: sub_tried = e.args[0].get('tried') if sub_tried is not None: - tried.extend([(pattern.regex.pattern + ' ' + t) for t in sub_tried]) + tried.extend([[pattern] + t for t in sub_tried]) else: - tried.append(pattern.regex.pattern) + tried.append([pattern]) else: if sub_match: sub_match_dict = dict([(smart_str(k), v) for k, v in match.groupdict().items()]) sub_match_dict.update(self.default_kwargs) - for k, v in sub_match[2].iteritems(): + for k, v in sub_match.kwargs.iteritems(): sub_match_dict[smart_str(k)] = v - return sub_match[0], sub_match[1], sub_match_dict - tried.append(pattern.regex.pattern) + return ResolverMatch(sub_match.func, sub_match.args, sub_match_dict, sub_match.url_name, self.app_name or sub_match.app_name, [self.namespace] + sub_match.namespaces) + tried.append([pattern]) raise Resolver404({'tried': tried, 'path': new_path}) raise Resolver404({'path' : path}) @@ -249,7 +284,12 @@ class RegexURLResolver(object): url_patterns = property(_get_url_patterns) def _resolve_special(self, view_type): - callback = getattr(self.urlconf_module, 'handler%s' % view_type) + callback = getattr(self.urlconf_module, 'handler%s' % view_type, None) + if not callback: + # No handler specified in file; use default + # Lazy import, since urls.defaults imports this file + from django.conf.urls import defaults + callback = getattr(defaults, 'handler%s' % view_type) try: return get_callable(callback), {} except (ImportError, AttributeError), e: diff --git a/django/db/__init__.py b/django/db/__init__.py index 4bae04ab9a..4c4faef694 100644 --- a/django/db/__init__.py +++ b/django/db/__init__.py @@ -35,6 +35,8 @@ if DEFAULT_DB_ALIAS not in settings.DATABASES: raise ImproperlyConfigured("You must default a '%s' database" % DEFAULT_DB_ALIAS) for alias, database in settings.DATABASES.items(): + if 'ENGINE' not in database: + raise ImproperlyConfigured("You must specify a 'ENGINE' for database '%s'" % alias) if database['ENGINE'] in ("postgresql", "postgresql_psycopg2", "sqlite3", "mysql", "oracle"): import warnings if 'django.contrib.gis' in settings.INSTALLED_APPS: diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py index 5918935899..fe2c7c451b 100644 --- a/django/db/backends/__init__.py +++ b/django/db/backends/__init__.py @@ -353,6 +353,11 @@ class BaseDatabaseOperations(object): """ return "BEGIN;" + def end_transaction_sql(self, success=True): + if not success: + return "ROLLBACK;" + return "COMMIT;" + def tablespace_sql(self, tablespace, inline=False): """ Returns the SQL that will be appended to tables or rows to define diff --git a/django/db/backends/creation.py b/django/db/backends/creation.py index 492ac1e3e9..6cdf415f7a 100644 --- a/django/db/backends/creation.py +++ b/django/db/backends/creation.py @@ -350,12 +350,17 @@ class BaseDatabaseCreation(object): can_rollback = self._rollback_works() self.connection.settings_dict["SUPPORTS_TRANSACTIONS"] = can_rollback - call_command('syncdb', verbosity=verbosity, interactive=False, database=self.connection.alias) + # Report syncdb messages at one level lower than that requested. + # This ensures we don't get flooded with messages during testing + # (unless you really ask to be flooded) + call_command('syncdb', verbosity=max(verbosity - 1, 0), interactive=False, database=self.connection.alias) if settings.CACHE_BACKEND.startswith('db://'): - from django.core.cache import parse_backend_uri - _, cache_name, _ = parse_backend_uri(settings.CACHE_BACKEND) - call_command('createcachetable', cache_name) + from django.core.cache import parse_backend_uri, cache + from django.db import router + if router.allow_syncdb(self.connection.alias, cache.cache_model_class): + _, cache_name, _ = parse_backend_uri(settings.CACHE_BACKEND) + call_command('createcachetable', cache_name, database=self.connection.alias) # Get a cursor (even though we don't need one yet). This has # the side effect of initializing the test database. @@ -388,10 +393,8 @@ class BaseDatabaseCreation(object): if autoclobber or confirm == 'yes': try: if verbosity >= 1: - print "Destroying old test database..." + print "Destroying old test database '%s'..." % self.connection.alias cursor.execute("DROP DATABASE %s" % qn(test_database_name)) - if verbosity >= 1: - print "Creating test database..." cursor.execute("CREATE DATABASE %s %s" % (qn(test_database_name), suffix)) except Exception, e: sys.stderr.write("Got an error recreating the test database: %s\n" % e) diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index e94e24bff9..a39c41f8d8 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -297,7 +297,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): self.connection = Database.connect(**kwargs) self.connection.encoders[SafeUnicode] = self.connection.encoders[unicode] self.connection.encoders[SafeString] = self.connection.encoders[str] - connection_created.send(sender=self.__class__) + connection_created.send(sender=self.__class__, connection=self) cursor = CursorWrapper(self.connection.cursor()) return cursor diff --git a/django/db/backends/mysql/client.py b/django/db/backends/mysql/client.py index ff5b64d1e0..24dc642abe 100644 --- a/django/db/backends/mysql/client.py +++ b/django/db/backends/mysql/client.py @@ -24,7 +24,10 @@ class DatabaseClient(BaseDatabaseClient): if passwd: args += ["--password=%s" % passwd] if host: - args += ["--host=%s" % host] + if '/' in host: + args += ["--socket=%s" % host] + else: + args += ["--host=%s" % host] if port: args += ["--port=%s" % port] if db: diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py index 369e65baf7..0cf26c406d 100644 --- a/django/db/backends/oracle/base.py +++ b/django/db/backends/oracle/base.py @@ -384,7 +384,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): # Django docs specify cx_Oracle version 4.3.1 or higher, but # stmtcachesize is available only in 4.3.2 and up. pass - connection_created.send(sender=self.__class__) + connection_created.send(sender=self.__class__, connection=self) if not cursor: cursor = FormatStylePlaceholderCursor(self.connection) return cursor diff --git a/django/db/backends/oracle/creation.py b/django/db/backends/oracle/creation.py index e6e242b9f7..8167640aac 100644 --- a/django/db/backends/oracle/creation.py +++ b/django/db/backends/oracle/creation.py @@ -61,8 +61,6 @@ class DatabaseCreation(BaseDatabaseCreation): cursor = self.connection.cursor() if self._test_database_create(): - if verbosity >= 1: - print 'Creating test database...' try: self._execute_test_db_creation(cursor, parameters, verbosity) except Exception, e: @@ -72,10 +70,8 @@ class DatabaseCreation(BaseDatabaseCreation): if autoclobber or confirm == 'yes': try: if verbosity >= 1: - print "Destroying old test database..." + print "Destroying old test database '%s'..." % self.connection.alias self._execute_test_db_destruction(cursor, parameters, verbosity) - if verbosity >= 1: - print "Creating test database..." self._execute_test_db_creation(cursor, parameters, verbosity) except Exception, e: sys.stderr.write("Got an error recreating the test database: %s\n" % e) diff --git a/django/db/backends/postgresql/base.py b/django/db/backends/postgresql/base.py index a1c858bd8f..f84ad1da61 100644 --- a/django/db/backends/postgresql/base.py +++ b/django/db/backends/postgresql/base.py @@ -135,8 +135,9 @@ class DatabaseWrapper(BaseDatabaseWrapper): if settings_dict['PORT']: conn_string += " port=%s" % settings_dict['PORT'] self.connection = Database.connect(conn_string, **settings_dict['OPTIONS']) - self.connection.set_isolation_level(1) # make transactions transparent to all cursors - connection_created.send(sender=self.__class__) + # make transactions transparent to all cursors + self.connection.set_isolation_level(1) + connection_created.send(sender=self.__class__, connection=self) cursor = self.connection.cursor() if new_connection: if set_tz: diff --git a/django/db/backends/postgresql/creation.py b/django/db/backends/postgresql/creation.py index af26d0b78f..9984716389 100644 --- a/django/db/backends/postgresql/creation.py +++ b/django/db/backends/postgresql/creation.py @@ -1,4 +1,5 @@ from django.db.backends.creation import BaseDatabaseCreation +from django.db.backends.util import truncate_name class DatabaseCreation(BaseDatabaseCreation): # This dictionary maps Field objects to their associated PostgreSQL column @@ -51,7 +52,7 @@ class DatabaseCreation(BaseDatabaseCreation): def get_index_sql(index_name, opclass=''): return (style.SQL_KEYWORD('CREATE INDEX') + ' ' + - style.SQL_TABLE(qn(index_name)) + ' ' + + style.SQL_TABLE(qn(truncate_name(index_name,self.connection.ops.max_name_length()))) + ' ' + style.SQL_KEYWORD('ON') + ' ' + style.SQL_TABLE(qn(db_table)) + ' ' + "(%s%s)" % (style.SQL_FIELD(qn(f.column)), opclass) + @@ -63,7 +64,7 @@ class DatabaseCreation(BaseDatabaseCreation): # a second index that specifies their operator class, which is # needed when performing correct LIKE queries outside the # C locale. See #12234. - db_type = f.db_type() + db_type = f.db_type(connection=self.connection) if db_type.startswith('varchar'): output.append(get_index_sql('%s_%s_like' % (db_table, f.column), ' varchar_pattern_ops')) diff --git a/django/db/backends/postgresql/operations.py b/django/db/backends/postgresql/operations.py index 2951c33db9..e8ce3f242b 100644 --- a/django/db/backends/postgresql/operations.py +++ b/django/db/backends/postgresql/operations.py @@ -54,7 +54,10 @@ class DatabaseOperations(BaseDatabaseOperations): return '%s' def last_insert_id(self, cursor, table_name, pk_name): - cursor.execute("SELECT CURRVAL('\"%s_%s_seq\"')" % (table_name, pk_name)) + # Use pg_get_serial_sequence to get the underlying sequence name + # from the table name and column name (available since PostgreSQL 8) + cursor.execute("SELECT CURRVAL(pg_get_serial_sequence('%s','%s'))" % ( + self.quote_name(table_name), pk_name)) return cursor.fetchone()[0] def no_limit_value(self): @@ -90,13 +93,14 @@ class DatabaseOperations(BaseDatabaseOperations): for sequence_info in sequences: table_name = sequence_info['table'] column_name = sequence_info['column'] - if column_name and len(column_name) > 0: - sequence_name = '%s_%s_seq' % (table_name, column_name) - else: - sequence_name = '%s_id_seq' % table_name - sql.append("%s setval('%s', 1, false);" % \ + if not (column_name and len(column_name) > 0): + # This will be the case if it's an m2m using an autogenerated + # intermediate table (see BaseDatabaseIntrospection.sequence_list) + column_name = 'id' + sql.append("%s setval(pg_get_serial_sequence('%s','%s'), 1, false);" % \ (style.SQL_KEYWORD('SELECT'), - style.SQL_FIELD(self.quote_name(sequence_name))) + style.SQL_TABLE(self.quote_name(table_name)), + style.SQL_FIELD(column_name)) ) return sql else: @@ -110,11 +114,15 @@ class DatabaseOperations(BaseDatabaseOperations): # Use `coalesce` to set the sequence for each model to the max pk value if there are records, # or 1 if there are none. Set the `is_called` property (the third argument to `setval`) to true # if there are records (as the max pk value is already in use), otherwise set it to false. + # Use pg_get_serial_sequence to get the underlying sequence name from the table name + # and column name (available since PostgreSQL 8) + for f in model._meta.local_fields: if isinstance(f, models.AutoField): - output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \ + output.append("%s setval(pg_get_serial_sequence('%s','%s'), coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \ (style.SQL_KEYWORD('SELECT'), - style.SQL_FIELD(qn('%s_%s_seq' % (model._meta.db_table, f.column))), + style.SQL_TABLE(qn(model._meta.db_table)), + style.SQL_FIELD(f.column), style.SQL_FIELD(qn(f.column)), style.SQL_FIELD(qn(f.column)), style.SQL_KEYWORD('IS NOT'), @@ -123,9 +131,10 @@ class DatabaseOperations(BaseDatabaseOperations): break # Only one AutoField is allowed per model, so don't bother continuing. for f in model._meta.many_to_many: if not f.rel.through: - output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \ + output.append("%s setval(pg_get_serial_sequence('%s','%s'), coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \ (style.SQL_KEYWORD('SELECT'), - style.SQL_FIELD(qn('%s_id_seq' % f.m2m_db_table())), + style.SQL_TABLE(qn(f.m2m_db_table())), + style.SQL_FIELD('id'), style.SQL_FIELD(qn('id')), style.SQL_FIELD(qn('id')), style.SQL_KEYWORD('IS NOT'), diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py index 29b7e7ff1a..ce4e48330e 100644 --- a/django/db/backends/postgresql_psycopg2/base.py +++ b/django/db/backends/postgresql_psycopg2/base.py @@ -136,7 +136,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): self.connection = Database.connect(**conn_params) self.connection.set_client_encoding('UTF8') self.connection.set_isolation_level(self.isolation_level) - connection_created.send(sender=self.__class__) + connection_created.send(sender=self.__class__, connection=self) cursor = self.connection.cursor() cursor.tzinfo_factory = None if new_connection: diff --git a/django/db/backends/signals.py b/django/db/backends/signals.py index a8079d0d76..c16a63f9f6 100644 --- a/django/db/backends/signals.py +++ b/django/db/backends/signals.py @@ -1,3 +1,3 @@ from django.dispatch import Signal -connection_created = Signal() +connection_created = Signal(providing_args=["connection"]) diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py index bc97f5cfd8..1ab2557627 100644 --- a/django/db/backends/sqlite3/base.py +++ b/django/db/backends/sqlite3/base.py @@ -176,7 +176,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): self.connection.create_function("django_extract", 2, _sqlite_extract) self.connection.create_function("django_date_trunc", 2, _sqlite_date_trunc) self.connection.create_function("regexp", 2, _sqlite_regexp) - connection_created.send(sender=self.__class__) + connection_created.send(sender=self.__class__, connection=self) return self.connection.cursor(factory=SQLiteCursorWrapper) def close(self): diff --git a/django/db/backends/sqlite3/creation.py b/django/db/backends/sqlite3/creation.py index 03897078a2..a65db1160b 100644 --- a/django/db/backends/sqlite3/creation.py +++ b/django/db/backends/sqlite3/creation.py @@ -43,14 +43,12 @@ class DatabaseCreation(BaseDatabaseCreation): if test_database_name and test_database_name != ":memory:": # Erase the old test database if verbosity >= 1: - print "Destroying old test database..." + print "Destroying old test database '%s'..." % self.connection.alias if os.access(test_database_name, os.F_OK): if not autoclobber: confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % test_database_name) if autoclobber or confirm == 'yes': try: - if verbosity >= 1: - print "Destroying old test database..." os.remove(test_database_name) except Exception, e: sys.stderr.write("Got an error deleting the old test database: %s\n" % e) @@ -58,8 +56,6 @@ class DatabaseCreation(BaseDatabaseCreation): else: print "Tests cancelled." sys.exit(1) - if verbosity >= 1: - print "Creating test database..." else: test_database_name = ":memory:" return test_database_name diff --git a/django/db/models/base.py b/django/db/models/base.py index 6304e009d3..b3deda13d4 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -1,6 +1,5 @@ import types import sys -import os from itertools import izip import django.db.models.manager # Imported to register signal handler. from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, FieldError, ValidationError, NON_FIELD_ERRORS @@ -16,7 +15,7 @@ from django.db.models.loading import register_models, get_model from django.utils.translation import ugettext_lazy as _ import django.utils.copycompat as copy from django.utils.functional import curry, update_wrapper -from django.utils.encoding import smart_str, force_unicode, smart_unicode +from django.utils.encoding import smart_str, force_unicode from django.utils.text import get_text_list, capfirst from django.conf import settings @@ -456,7 +455,7 @@ class Model(object): meta = cls._meta if origin and not meta.auto_created: - signals.pre_save.send(sender=origin, instance=self, raw=raw) + signals.pre_save.send(sender=origin, instance=self, raw=raw, using=using) # If we are in a raw save, save the object exactly as presented. # That means that we don't try to be smart about saving attributes @@ -540,7 +539,7 @@ class Model(object): # Signal that the save is complete if origin and not meta.auto_created: signals.post_save.send(sender=origin, instance=self, - created=(not record_exists), raw=raw) + created=(not record_exists), raw=raw, using=using) save_base.alters_data = True @@ -645,6 +644,8 @@ class Model(object): return force_unicode(dict(field.flatchoices).get(value, value), strings_only=True) def _get_next_or_previous_by_FIELD(self, field, is_next, **kwargs): + if not self.pk: + raise ValueError("get_next/get_previous cannot be used on unsaved objects.") op = is_next and 'gt' or 'lt' order = not is_next and '-' or '' param = smart_str(getattr(self, field.attname)) @@ -744,11 +745,11 @@ class Model(object): continue if f.unique: unique_checks.append((model_class, (name,))) - if f.unique_for_date: + if f.unique_for_date and f.unique_for_date not in exclude: date_checks.append((model_class, 'date', name, f.unique_for_date)) - if f.unique_for_year: + if f.unique_for_year and f.unique_for_year not in exclude: date_checks.append((model_class, 'year', name, f.unique_for_year)) - if f.unique_for_month: + if f.unique_for_month and f.unique_for_month not in exclude: date_checks.append((model_class, 'month', name, f.unique_for_month)) return unique_checks, date_checks diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index 65b60a0173..3c58873035 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -459,6 +459,9 @@ class AutoField(Field): kwargs['blank'] = True Field.__init__(self, *args, **kwargs) + def get_internal_type(self): + return "AutoField" + def to_python(self, value): if value is None: return value @@ -795,6 +798,14 @@ class EmailField(CharField): kwargs['max_length'] = kwargs.get('max_length', 75) CharField.__init__(self, *args, **kwargs) + def formfield(self, **kwargs): + # As with CharField, this will cause email validation to be performed twice + defaults = { + 'form_class': forms.EmailField, + } + defaults.update(kwargs) + return super(EmailField, self).formfield(**defaults) + class FilePathField(Field): description = _("File path") @@ -1105,6 +1116,14 @@ class URLField(CharField): CharField.__init__(self, verbose_name, name, **kwargs) self.validators.append(validators.URLValidator(verify_exists=verify_exists)) + def formfield(self, **kwargs): + # As with CharField, this will cause URL validation to be performed twice + defaults = { + 'form_class': forms.URLField, + } + defaults.update(kwargs) + return super(URLField, self).formfield(**defaults) + class XMLField(TextField): description = _("XML text") diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index 5830a794df..1634d7d974 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -566,7 +566,7 @@ def create_many_related_manager(superclass, rel=False): # duplicate data row for symmetrical reverse entries. signals.m2m_changed.send(sender=rel.through, action='pre_add', instance=self.instance, reverse=self.reverse, - model=self.model, pk_set=new_ids) + model=self.model, pk_set=new_ids, using=db) # Add the ones that aren't there already for obj_id in new_ids: self.through._default_manager.using(db).create(**{ @@ -578,7 +578,7 @@ def create_many_related_manager(superclass, rel=False): # duplicate data row for symmetrical reverse entries. signals.m2m_changed.send(sender=rel.through, action='post_add', instance=self.instance, reverse=self.reverse, - model=self.model, pk_set=new_ids) + model=self.model, pk_set=new_ids, using=db) def _remove_items(self, source_field_name, target_field_name, *objs): # source_col_name: the PK colname in join_table for the source object @@ -594,14 +594,16 @@ def create_many_related_manager(superclass, rel=False): old_ids.add(obj.pk) else: old_ids.add(obj) + # Work out what DB we're operating on + db = router.db_for_write(self.through.__class__, instance=self.instance) + # Send a signal to the other end if need be. if self.reverse or source_field_name == self.source_field_name: # Don't send the signal when we are deleting the # duplicate data row for symmetrical reverse entries. signals.m2m_changed.send(sender=rel.through, action="pre_remove", instance=self.instance, reverse=self.reverse, - model=self.model, pk_set=old_ids) + model=self.model, pk_set=old_ids, using=db) # Remove the specified objects from the join table - db = router.db_for_write(self.through.__class__, instance=self.instance) self.through._default_manager.using(db).filter(**{ source_field_name: self._pk_val, '%s__in' % target_field_name: old_ids @@ -611,17 +613,17 @@ def create_many_related_manager(superclass, rel=False): # duplicate data row for symmetrical reverse entries. signals.m2m_changed.send(sender=rel.through, action="post_remove", instance=self.instance, reverse=self.reverse, - model=self.model, pk_set=old_ids) + model=self.model, pk_set=old_ids, using=db) def _clear_items(self, source_field_name): + db = router.db_for_write(self.through.__class__, instance=self.instance) # source_col_name: the PK colname in join_table for the source object if self.reverse or source_field_name == self.source_field_name: # Don't send the signal when we are clearing the # duplicate data rows for symmetrical reverse entries. signals.m2m_changed.send(sender=rel.through, action="pre_clear", instance=self.instance, reverse=self.reverse, - model=self.model, pk_set=None) - db = router.db_for_write(self.through.__class__, instance=self.instance) + model=self.model, pk_set=None, using=db) self.through._default_manager.using(db).filter(**{ source_field_name: self._pk_val }).delete() @@ -630,7 +632,7 @@ def create_many_related_manager(superclass, rel=False): # duplicate data rows for symmetrical reverse entries. signals.m2m_changed.send(sender=rel.through, action="post_clear", instance=self.instance, reverse=self.reverse, - model=self.model, pk_set=None) + model=self.model, pk_set=None, using=db) return ManyRelatedManager @@ -812,6 +814,9 @@ class ForeignKey(RelatedField, Field): to_field = to_field or (to._meta.pk and to._meta.pk.name) kwargs['verbose_name'] = kwargs.get('verbose_name', None) + if 'db_index' not in kwargs: + kwargs['db_index'] = True + kwargs['rel'] = rel_class(to, to_field, related_name=kwargs.pop('related_name', None), limit_choices_to=kwargs.pop('limit_choices_to', None), @@ -819,8 +824,6 @@ class ForeignKey(RelatedField, Field): parent_link=kwargs.pop('parent_link', False)) Field.__init__(self, **kwargs) - self.db_index = True - def validate(self, value, model_instance): if self.rel.parent_link: return diff --git a/django/db/models/query.py b/django/db/models/query.py index d9fbd9b8cb..9ecfb745fd 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -1311,7 +1311,8 @@ def delete_objects(seen_objs, using): # Pre-notify all instances to be deleted. for pk_val, instance in items: if not cls._meta.auto_created: - signals.pre_delete.send(sender=cls, instance=instance) + signals.pre_delete.send(sender=cls, instance=instance, + using=using) pk_list = [pk for pk,instance in items] @@ -1343,7 +1344,7 @@ def delete_objects(seen_objs, using): setattr(instance, field.attname, None) if not cls._meta.auto_created: - signals.post_delete.send(sender=cls, instance=instance) + signals.post_delete.send(sender=cls, instance=instance, using=using) setattr(instance, cls._meta.pk.attname, None) if forced_managed: diff --git a/django/db/models/signals.py b/django/db/models/signals.py index cd0350bc01..48872e7e7f 100644 --- a/django/db/models/signals.py +++ b/django/db/models/signals.py @@ -5,12 +5,12 @@ class_prepared = Signal(providing_args=["class"]) pre_init = Signal(providing_args=["instance", "args", "kwargs"]) post_init = Signal(providing_args=["instance"]) -pre_save = Signal(providing_args=["instance", "raw"]) -post_save = Signal(providing_args=["instance", "raw", "created"]) +pre_save = Signal(providing_args=["instance", "raw", "using"]) +post_save = Signal(providing_args=["instance", "raw", "created", "using"]) -pre_delete = Signal(providing_args=["instance"]) -post_delete = Signal(providing_args=["instance"]) +pre_delete = Signal(providing_args=["instance", "using"]) +post_delete = Signal(providing_args=["instance", "using"]) post_syncdb = Signal(providing_args=["class", "app", "created_models", "verbosity", "interactive"]) -m2m_changed = Signal(providing_args=["action", "instance", "reverse", "model", "pk_set"]) +m2m_changed = Signal(providing_args=["action", "instance", "reverse", "model", "pk_set", "using"]) diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index 0913399e2a..ec477447f3 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -1090,10 +1090,7 @@ class Query(object): # exclude the "foo__in=[]" case from this handling, because # it's short-circuited in the Where class. # We also need to handle the case where a subquery is provided - entry = self.where_class() - entry.add((Constraint(alias, col, None), 'isnull', True), AND) - entry.negate() - self.where.add(entry, AND) + self.where.add((Constraint(alias, col, None), 'isnull', False), AND) if can_reuse is not None: can_reuse.update(join_list) diff --git a/django/dispatch/__init__.py b/django/dispatch/__init__.py index 0798acc7db..9aeb83c5e1 100644 --- a/django/dispatch/__init__.py +++ b/django/dispatch/__init__.py @@ -6,4 +6,4 @@ See license.txt for original license. Heavily modified for Django's purposes. """ -from django.dispatch.dispatcher import Signal
\ No newline at end of file +from django.dispatch.dispatcher import Signal, receiver
\ No newline at end of file diff --git a/django/dispatch/dispatcher.py b/django/dispatch/dispatcher.py index de09334637..c4516d88b0 100644 --- a/django/dispatch/dispatcher.py +++ b/django/dispatch/dispatcher.py @@ -235,3 +235,19 @@ class Signal(object): for idx, (r_key, _) in enumerate(self.receivers): if r_key == key: del self.receivers[idx] + + +def receiver(signal, **kwargs): + """ + A decorator for connecting receivers to signals. Used by passing in the + signal and keyword arguments to connect:: + + @receiver(post_save, sender=MyModel) + def signal_receiver(sender, **kwargs): + ... + + """ + def _decorator(func): + signal.connect(func, **kwargs) + return func + return _decorator diff --git a/django/forms/fields.py b/django/forms/fields.py index 0bae4ba157..de14a5c8a8 100644 --- a/django/forms/fields.py +++ b/django/forms/fields.py @@ -127,6 +127,9 @@ class Field(object): self.validators = self.default_validators + validators + def prepare_value(self, value): + return value + def to_python(self, value): return value @@ -396,6 +399,8 @@ class DateTimeField(Field): # components: date and time. if len(value) != 2: raise ValidationError(self.error_messages['invalid']) + if value[0] in validators.EMPTY_VALUES and value[1] in validators.EMPTY_VALUES: + return None value = '%s %s' % tuple(value) for format in self.input_formats or formats.get_format('DATETIME_INPUT_FORMATS'): try: diff --git a/django/forms/forms.py b/django/forms/forms.py index b3718efa9a..7b2bc66fc7 100644 --- a/django/forms/forms.py +++ b/django/forms/forms.py @@ -18,10 +18,10 @@ __all__ = ('BaseForm', 'Form') NON_FIELD_ERRORS = '__all__' def pretty_name(name): - """Converts 'first_name' to 'First name'""" - if not name: - return u'' - return name.replace('_', ' ').capitalize() + """Converts 'first_name' to 'First name'""" + if not name: + return u'' + return name.replace('_', ' ').capitalize() def get_declared_fields(bases, attrs, with_base_fields=True): """ @@ -35,7 +35,7 @@ def get_declared_fields(bases, attrs, with_base_fields=True): Also integrates any additional media definitions """ fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)] - fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter)) + fields.sort(key=lambda x: x[1].creation_counter) # If this class is subclassing another Form, add that Form's fields. # Note that we loop over the bases in *reverse*. This is necessary in @@ -213,7 +213,7 @@ class BaseForm(StrAndUnicode): normal_row = u'<tr%(html_class_attr)s><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s</td></tr>', error_row = u'<tr><td colspan="2">%s</td></tr>', row_ender = u'</td></tr>', - help_text_html = u'<br />%s', + help_text_html = u'<br /><span class="helptext">%s</span>', errors_on_separate_row = False) def as_ul(self): @@ -222,7 +222,7 @@ class BaseForm(StrAndUnicode): normal_row = u'<li%(html_class_attr)s>%(errors)s%(label)s %(field)s%(help_text)s</li>', error_row = u'<li>%s</li>', row_ender = '</li>', - help_text_html = u' %s', + help_text_html = u' <span class="helptext">%s</span>', errors_on_separate_row = False) def as_p(self): @@ -231,7 +231,7 @@ class BaseForm(StrAndUnicode): normal_row = u'<p%(html_class_attr)s>%(label)s %(field)s%(help_text)s</p>', error_row = u'%s', row_ender = '</p>', - help_text_html = u' %s', + help_text_html = u' <span class="helptext">%s</span>', errors_on_separate_row = True) def non_field_errors(self): @@ -423,6 +423,7 @@ class BoundField(StrAndUnicode): """ if not widget: widget = self.field.widget + attrs = attrs or {} auto_id = self.auto_id if auto_id and 'id' not in attrs and 'id' not in widget.attrs: @@ -430,6 +431,7 @@ class BoundField(StrAndUnicode): attrs['id'] = auto_id else: attrs['id'] = self.html_initial_id + if not self.form.is_bound: data = self.form.initial.get(self.name, self.field.initial) if callable(data): @@ -439,6 +441,8 @@ class BoundField(StrAndUnicode): data = self.form.initial.get(self.name, self.field.initial) else: data = self.data + data = self.field.prepare_value(data) + if not only_initial: name = self.html_name else: diff --git a/django/forms/formsets.py b/django/forms/formsets.py index 3804f2b3c1..508950eee9 100644 --- a/django/forms/formsets.py +++ b/django/forms/formsets.py @@ -199,14 +199,12 @@ class BaseFormSet(StrAndUnicode): # A sort function to order things numerically ascending, but # None should be sorted below anything else. Allowing None as # a comparison value makes it so we can leave ordering fields - # blamk. - def compare_ordering_values(x, y): - if x[1] is None: - return 1 - if y[1] is None: - return -1 - return x[1] - y[1] - self._ordering.sort(compare_ordering_values) + # blank. + def compare_ordering_key(k): + if k[1] is None: + return (1, 0) # +infinity, larger than any number + return (0, k[1]) + self._ordering.sort(key=compare_ordering_key) # Return a list of form.cleaned_data dicts in the order spcified by # the form data. return [self.forms[i[0]] for i in self._ordering] diff --git a/django/forms/models.py b/django/forms/models.py index 8accd61f30..cf465ad30c 100644 --- a/django/forms/models.py +++ b/django/forms/models.py @@ -9,7 +9,8 @@ from django.utils.datastructures import SortedDict from django.utils.text import get_text_list, capfirst from django.utils.translation import ugettext_lazy as _, ugettext -from django.core.exceptions import ValidationError, NON_FIELD_ERRORS +from django.core.exceptions import ValidationError, NON_FIELD_ERRORS, \ + FieldError from django.core.validators import EMPTY_VALUES from util import ErrorList from forms import BaseForm, get_declared_fields @@ -150,7 +151,7 @@ def model_to_dict(instance, fields=None, exclude=None): data[f.name] = f.value_from_object(instance) return data -def fields_for_model(model, fields=None, exclude=None, widgets=None, formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)): +def fields_for_model(model, fields=None, exclude=None, widgets=None, formfield_callback=None): """ Returns a ``SortedDict`` containing form fields for the given model. @@ -175,7 +176,14 @@ def fields_for_model(model, fields=None, exclude=None, widgets=None, formfield_c kwargs = {'widget': widgets[f.name]} else: kwargs = {} - formfield = formfield_callback(f, **kwargs) + + if formfield_callback is None: + formfield = f.formfield(**kwargs) + elif not callable(formfield_callback): + raise TypeError('formfield_callback must be a function or callable') + else: + formfield = formfield_callback(f, **kwargs) + if formfield: field_list.append((f.name, formfield)) else: @@ -198,8 +206,7 @@ class ModelFormOptions(object): class ModelFormMetaclass(type): def __new__(cls, name, bases, attrs): - formfield_callback = attrs.pop('formfield_callback', - lambda f, **kwargs: f.formfield(**kwargs)) + formfield_callback = attrs.pop('formfield_callback', None) try: parents = [b for b in bases if issubclass(b, ModelForm)] except NameError: @@ -218,6 +225,15 @@ class ModelFormMetaclass(type): # If a model is defined, extract form fields from it. fields = fields_for_model(opts.model, opts.fields, opts.exclude, opts.widgets, formfield_callback) + # make sure opts.fields doesn't specify an invalid field + none_model_fields = [k for k, v in fields.iteritems() if not v] + missing_fields = set(none_model_fields) - \ + set(declared_fields.keys()) + if missing_fields: + message = 'Unknown field(s) (%s) specified for %s' + message = message % (', '.join(missing_fields), + opts.model.__name__) + raise FieldError(message) # Override default model fields with any custom declared ones # (plus, include all the other declared fields). fields.update(declared_fields) @@ -376,7 +392,7 @@ class ModelForm(BaseModelForm): __metaclass__ = ModelFormMetaclass def modelform_factory(model, form=ModelForm, fields=None, exclude=None, - formfield_callback=lambda f: f.formfield()): + formfield_callback=None): # Create the inner Meta class. FIXME: ideally, we should be able to # construct a ModelForm without creating and passing in a temporary # inner class. @@ -658,7 +674,7 @@ class BaseModelFormSet(BaseFormSet): form.fields[self._pk_field.name] = ModelChoiceField(qs, initial=pk_value, required=False, widget=HiddenInput) super(BaseModelFormSet, self).add_fields(form, index) -def modelformset_factory(model, form=ModelForm, formfield_callback=lambda f: f.formfield(), +def modelformset_factory(model, form=ModelForm, formfield_callback=None, formset=BaseModelFormSet, extra=1, can_delete=False, can_order=False, max_num=None, fields=None, exclude=None): @@ -813,7 +829,7 @@ def inlineformset_factory(parent_model, model, form=ModelForm, formset=BaseInlineFormSet, fk_name=None, fields=None, exclude=None, extra=3, can_order=False, can_delete=True, max_num=None, - formfield_callback=lambda f: f.formfield()): + formfield_callback=None): """ Returns an ``InlineFormSet`` for the given kwargs. @@ -906,12 +922,7 @@ class ModelChoiceIterator(object): return len(self.queryset) def choice(self, obj): - if self.field.to_field_name: - key = obj.serializable_value(self.field.to_field_name) - else: - key = obj.pk - return (key, self.field.label_from_instance(obj)) - + return (self.field.prepare_value(obj), self.field.label_from_instance(obj)) class ModelChoiceField(ChoiceField): """A ChoiceField whose choices are a model QuerySet.""" @@ -971,8 +982,8 @@ class ModelChoiceField(ChoiceField): return self._choices # Otherwise, execute the QuerySet in self.queryset to determine the - # choices dynamically. Return a fresh QuerySetIterator that has not been - # consumed. Note that we're instantiating a new QuerySetIterator *each* + # choices dynamically. Return a fresh ModelChoiceIterator that has not been + # consumed. Note that we're instantiating a new ModelChoiceIterator *each* # time _get_choices() is called (and, thus, each time self.choices is # accessed) so that we can ensure the QuerySet has not been consumed. This # construct might look complicated but it allows for lazy evaluation of @@ -981,13 +992,21 @@ class ModelChoiceField(ChoiceField): choices = property(_get_choices, ChoiceField._set_choices) + def prepare_value(self, value): + if hasattr(value, '_meta'): + if self.to_field_name: + return value.serializable_value(self.to_field_name) + else: + return value.pk + return super(ModelChoiceField, self).prepare_value(value) + def to_python(self, value): if value in EMPTY_VALUES: return None try: key = self.to_field_name or 'pk' value = self.queryset.get(**{key: value}) - except self.queryset.model.DoesNotExist: + except (ValueError, self.queryset.model.DoesNotExist): raise ValidationError(self.error_messages['invalid_choice']) return value @@ -1030,3 +1049,8 @@ class ModelMultipleChoiceField(ModelChoiceField): if force_unicode(val) not in pks: raise ValidationError(self.error_messages['invalid_choice'] % val) return qs + + def prepare_value(self, value): + if hasattr(value, '__iter__'): + return [super(ModelMultipleChoiceField, self).prepare_value(v) for v in value] + return super(ModelMultipleChoiceField, self).prepare_value(value) diff --git a/django/forms/widgets.py b/django/forms/widgets.py index e3799c69ad..2e16c35d8b 100644 --- a/django/forms/widgets.py +++ b/django/forms/widgets.py @@ -229,7 +229,7 @@ class TextInput(Input): class PasswordInput(Input): input_type = 'password' - def __init__(self, attrs=None, render_value=True): + def __init__(self, attrs=None, render_value=False): super(PasswordInput, self).__init__(attrs) self.render_value = render_value @@ -308,9 +308,13 @@ class DateInput(Input): super(DateInput, self).__init__(attrs) if format: self.format = format + self.manual_format = True + else: + self.format = formats.get_format('DATE_INPUT_FORMATS')[0] + self.manual_format = False def _format_value(self, value): - if self.is_localized: + if self.is_localized and not self.manual_format: return formats.localize_input(value) elif hasattr(value, 'strftime'): value = datetime_safe.new_date(value) @@ -336,9 +340,13 @@ class DateTimeInput(Input): super(DateTimeInput, self).__init__(attrs) if format: self.format = format + self.manual_format = True + else: + self.format = formats.get_format('DATETIME_INPUT_FORMATS')[0] + self.manual_format = False def _format_value(self, value): - if self.is_localized: + if self.is_localized and not self.manual_format: return formats.localize_input(value) elif hasattr(value, 'strftime'): value = datetime_safe.new_datetime(value) @@ -364,9 +372,13 @@ class TimeInput(Input): super(TimeInput, self).__init__(attrs) if format: self.format = format + self.manual_format = True + else: + self.format = formats.get_format('TIME_INPUT_FORMATS')[0] + self.manual_format = False def _format_value(self, value): - if self.is_localized: + if self.is_localized and not self.manual_format: return formats.localize_input(value) elif hasattr(value, 'strftime'): return value.strftime(self.format) @@ -438,13 +450,14 @@ class Select(Widget): output.append(u'</select>') return mark_safe(u'\n'.join(output)) + def render_option(self, selected_choices, option_value, option_label): + option_value = force_unicode(option_value) + selected_html = (option_value in selected_choices) and u' selected="selected"' or '' + return u'<option value="%s"%s>%s</option>' % ( + escape(option_value), selected_html, + conditional_escape(force_unicode(option_label))) + def render_options(self, choices, selected_choices): - def render_option(option_value, option_label): - option_value = force_unicode(option_value) - selected_html = (option_value in selected_choices) and u' selected="selected"' or '' - return u'<option value="%s"%s>%s</option>' % ( - escape(option_value), selected_html, - conditional_escape(force_unicode(option_label))) # Normalize to strings. selected_choices = set([force_unicode(v) for v in selected_choices]) output = [] @@ -452,10 +465,10 @@ class Select(Widget): if isinstance(option_label, (list, tuple)): output.append(u'<optgroup label="%s">' % escape(force_unicode(option_value))) for option in option_label: - output.append(render_option(*option)) + output.append(self.render_option(selected_choices, *option)) output.append(u'</optgroup>') else: - output.append(render_option(option_value, option_label)) + output.append(self.render_option(selected_choices, option_value, option_label)) return u'\n'.join(output) class NullBooleanSelect(Select): @@ -751,12 +764,8 @@ class SplitDateTimeWidget(MultiWidget): time_format = TimeInput.format def __init__(self, attrs=None, date_format=None, time_format=None): - if date_format: - self.date_format = date_format - if time_format: - self.time_format = time_format - widgets = (DateInput(attrs=attrs, format=self.date_format), - TimeInput(attrs=attrs, format=self.time_format)) + widgets = (DateInput(attrs=attrs, format=date_format), + TimeInput(attrs=attrs, format=time_format)) super(SplitDateTimeWidget, self).__init__(widgets, attrs) def decompress(self, value): diff --git a/django/http/__init__.py b/django/http/__init__.py index 8756d046b5..46232bee18 100644 --- a/django/http/__init__.py +++ b/django/http/__init__.py @@ -1,5 +1,7 @@ +import datetime import os import re +import time from Cookie import BaseCookie, SimpleCookie, CookieError from pprint import pformat from urllib import urlencode @@ -12,6 +14,7 @@ except ImportError: from django.utils.datastructures import MultiValueDict, ImmutableList from django.utils.encoding import smart_str, iri_to_uri, force_unicode +from django.utils.http import cookie_date from django.http.multipartparser import MultiPartParser from django.conf import settings from django.core.files import uploadhandler @@ -177,14 +180,14 @@ class QueryDict(MultiValueDict): super(QueryDict, self).__delitem__(key) def __copy__(self): - result = self.__class__('', mutable=True) + result = self.__class__('', mutable=True, encoding=self.encoding) for key, value in dict.items(self): dict.__setitem__(result, key, value) return result def __deepcopy__(self, memo): import django.utils.copycompat as copy - result = self.__class__('', mutable=True) + result = self.__class__('', mutable=True, encoding=self.encoding) memo[id(self)] = result for key, value in dict.items(self): dict.__setitem__(result, copy.deepcopy(key, memo), copy.deepcopy(value, memo)) @@ -303,13 +306,16 @@ class HttpResponse(object): def __init__(self, content='', mimetype=None, status=None, content_type=None): - from django.conf import settings + # _headers is a mapping of the lower-case name to the original case of + # the header (required for working with legacy systems) and the header + # value. Both the name of the header and its value are ASCII strings. + self._headers = {} self._charset = settings.DEFAULT_CHARSET if mimetype: content_type = mimetype # For backwards compatibility if not content_type: content_type = "%s; charset=%s" % (settings.DEFAULT_CONTENT_TYPE, - settings.DEFAULT_CHARSET) + self._charset) if not isinstance(content, basestring) and hasattr(content, '__iter__'): self._container = content self._is_string = False @@ -320,10 +326,7 @@ class HttpResponse(object): if status: self.status_code = status - # _headers is a mapping of the lower-case name to the original case of - # the header (required for working with legacy systems) and the header - # value. - self._headers = {'content-type': ('Content-Type', content_type)} + self['Content-Type'] = content_type def __str__(self): """Full HTTP message, including headers.""" @@ -373,11 +376,32 @@ class HttpResponse(object): def set_cookie(self, key, value='', max_age=None, expires=None, path='/', domain=None, secure=False): + """ + Sets a cookie. + + ``expires`` can be a string in the correct format or a + ``datetime.datetime`` object in UTC. If ``expires`` is a datetime + object then ``max_age`` will be calculated. + """ self.cookies[key] = value + if expires is not None: + if isinstance(expires, datetime.datetime): + delta = expires - expires.utcnow() + # Add one second so the date matches exactly (a fraction of + # time gets lost between converting to a timedelta and + # then the date string). + delta = delta + datetime.timedelta(seconds=1) + # Just set max_age - the max_age logic will set expires. + expires = None + max_age = max(0, delta.days * 86400 + delta.seconds) + else: + self.cookies[key]['expires'] = expires if max_age is not None: self.cookies[key]['max-age'] = max_age - if expires is not None: - self.cookies[key]['expires'] = expires + # IE requires expires, so set it if hasn't been already. + if not expires: + self.cookies[key]['expires'] = cookie_date(time.time() + + max_age) if path is not None: self.cookies[key]['path'] = path if domain is not None: diff --git a/django/middleware/csrf.py b/django/middleware/csrf.py index 9ca727fca9..5d3a871adb 100644 --- a/django/middleware/csrf.py +++ b/django/middleware/csrf.py @@ -27,22 +27,33 @@ else: randrange = random.randrange _MAX_CSRF_KEY = 18446744073709551616L # 2 << 63 +REASON_NO_REFERER = "Referer checking failed - no Referer." +REASON_BAD_REFERER = "Referer checking failed - %s does not match %s." +REASON_NO_COOKIE = "No CSRF or session cookie." +REASON_NO_CSRF_COOKIE = "CSRF cookie not set." +REASON_BAD_TOKEN = "CSRF token missing or incorrect." + + def _get_failure_view(): """ Returns the view to be used for CSRF rejections """ return get_callable(settings.CSRF_FAILURE_VIEW) + def _get_new_csrf_key(): return md5_constructor("%s%s" % (randrange(0, _MAX_CSRF_KEY), settings.SECRET_KEY)).hexdigest() + def _make_legacy_session_token(session_id): return md5_constructor(settings.SECRET_KEY + session_id).hexdigest() + def get_token(request): """ - Returns the the CSRF token required for a POST form. + Returns the the CSRF token required for a POST form. The token is an + alphanumeric value. A side effect of calling this function is to make the the csrf_protect decorator and the CsrfViewMiddleware add a CSRF cookie and a 'Vary: Cookie' @@ -52,6 +63,18 @@ def get_token(request): request.META["CSRF_COOKIE_USED"] = True return request.META.get("CSRF_COOKIE", None) + +def _sanitize_token(token): + # Allow only alphanum, and ensure we return a 'str' for the sake of the post + # processing middleware. + token = re.sub('[^a-zA-Z0-9]', '', str(token.decode('ascii', 'ignore'))) + if token == "": + # In case the cookie has been truncated to nothing at some point. + return _get_new_csrf_key() + else: + return token + + class CsrfViewMiddleware(object): """ Middleware that requires a present and correct csrfmiddlewaretoken @@ -62,9 +85,6 @@ class CsrfViewMiddleware(object): tag. """ def process_view(self, request, callback, callback_args, callback_kwargs): - if getattr(callback, 'csrf_exempt', False): - return None - if getattr(request, 'csrf_processing_done', False): return None @@ -80,7 +100,10 @@ class CsrfViewMiddleware(object): # request, so it's available to the view. We'll store it in a cookie when # we reach the response. try: - request.META["CSRF_COOKIE"] = request.COOKIES[settings.CSRF_COOKIE_NAME] + # In case of cookies from untrusted sources, we strip anything + # dangerous at this point, so that the cookie + token will have the + # same, sanitized value. + request.META["CSRF_COOKIE"] = _sanitize_token(request.COOKIES[settings.CSRF_COOKIE_NAME]) cookie_is_new = False except KeyError: # No cookie, so create one. This will be sent with the next @@ -90,6 +113,11 @@ class CsrfViewMiddleware(object): # place of a CSRF cookie for this request only. cookie_is_new = True + # Wait until request.META["CSRF_COOKIE"] has been manipulated before + # bailing out, so that get_token still works + if getattr(callback, 'csrf_exempt', False): + return None + if request.method == 'POST': if getattr(request, '_dont_enforce_csrf_checks', False): # Mechanism to turn off CSRF checks for test suite. It comes after @@ -124,16 +152,30 @@ class CsrfViewMiddleware(object): return accept() if request.is_secure(): - # Strict referer checking for HTTPS + # Suppose user visits http://example.com/ + # An active network attacker,(man-in-the-middle, MITM) sends a + # POST form which targets https://example.com/detonate-bomb/ and + # submits it via javascript. + # + # The attacker will need to provide a CSRF cookie and token, but + # that is no problem for a MITM and the session independent + # nonce we are using. So the MITM can circumvent the CSRF + # protection. This is true for any HTTP connection, but anyone + # using HTTPS expects better! For this reason, for + # https://example.com/ we need additional protection that treats + # http://example.com/ as completely untrusted. Under HTTPS, + # Barth et al. found that the Referer header is missing for + # same-domain requests in only about 0.2% of cases or less, so + # we can use strict Referer checking. referer = request.META.get('HTTP_REFERER') if referer is None: - return reject("Referer checking failed - no Referer.") + return reject(REASON_NO_REFERER) # The following check ensures that the referer is HTTPS, - # the domains match and the ports match. This might be too strict. + # the domains match and the ports match - the same origin policy. good_referer = 'https://%s/' % request.get_host() if not referer.startswith(good_referer): - return reject("Referer checking failed - %s does not match %s." % + return reject(REASON_BAD_REFERER % (referer, good_referer)) # If the user didn't already have a CSRF cookie, then fall back to @@ -148,7 +190,7 @@ class CsrfViewMiddleware(object): # No CSRF cookie and no session cookie. For POST requests, # we insist on a CSRF cookie, and in this way we can avoid # all CSRF attacks, including login CSRF. - return reject("No CSRF or session cookie.") + return reject(REASON_NO_COOKIE) else: csrf_token = request.META["CSRF_COOKIE"] @@ -157,9 +199,9 @@ class CsrfViewMiddleware(object): if request_csrf_token != csrf_token: if cookie_is_new: # probably a problem setting the CSRF cookie - return reject("CSRF cookie not set.") + return reject(REASON_NO_CSRF_COOKIE) else: - return reject("CSRF token missing or incorrect.") + return reject(REASON_BAD_TOKEN) return accept() @@ -185,6 +227,7 @@ class CsrfViewMiddleware(object): response.csrf_processing_done = True return response + class CsrfResponseMiddleware(object): """ DEPRECATED @@ -235,6 +278,7 @@ class CsrfResponseMiddleware(object): del response['ETag'] return response + class CsrfMiddleware(object): """ Django middleware that adds protection against Cross Site @@ -262,4 +306,3 @@ class CsrfMiddleware(object): def process_view(self, request, callback, callback_args, callback_kwargs): return self.view_middleware.process_view(request, callback, callback_args, callback_kwargs) - diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py index 4b720093b3..739f50f235 100644 --- a/django/template/defaultfilters.py +++ b/django/template/defaultfilters.py @@ -11,9 +11,10 @@ except ImportError: from django.template import Variable, Library from django.conf import settings from django.utils import formats -from django.utils.translation import ugettext, ungettext from django.utils.encoding import force_unicode, iri_to_uri +from django.utils.html import conditional_escape from django.utils.safestring import mark_safe, SafeData +from django.utils.translation import ugettext, ungettext register = Library() @@ -255,6 +256,8 @@ def truncatewords(value, arg): Truncates a string after a certain number of words. Argument: Number of words to truncate after. + + Newlines within the string are removed. """ from django.utils.text import truncate_words try: @@ -270,6 +273,8 @@ def truncatewords_html(value, arg): Truncates HTML after a certain number of words. Argument: Number of words to truncate after. + + Newlines in the HTML are preserved. """ from django.utils.text import truncate_html_words try: @@ -496,10 +501,9 @@ def join(value, arg, autoescape=None): """ value = map(force_unicode, value) if autoescape: - from django.utils.html import conditional_escape value = [conditional_escape(v) for v in value] try: - data = arg.join(value) + data = conditional_escape(arg).join(value) except AttributeError: # fail silently but nicely return value return mark_safe(data) @@ -809,7 +813,11 @@ def filesizeformat(bytes): return ugettext("%.1f KB") % (bytes / 1024) if bytes < 1024 * 1024 * 1024: return ugettext("%.1f MB") % (bytes / (1024 * 1024)) - return ugettext("%.1f GB") % (bytes / (1024 * 1024 * 1024)) + if bytes < 1024 * 1024 * 1024 * 1024: + return ugettext("%.1f GB") % (bytes / (1024 * 1024 * 1024)) + if bytes < 1024 * 1024 * 1024 * 1024 * 1024: + return ugettext("%.1f TB") % (bytes / (1024 * 1024 * 1024 * 1024)) + return ugettext("%.1f PB") % (bytes / (1024 * 1024 * 1024 * 1024 * 1024)) filesizeformat.is_safe = True def pluralize(value, arg=u's'): diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py index 318ae5ffd2..1b07413530 100644 --- a/django/template/defaulttags.py +++ b/django/template/defaulttags.py @@ -42,7 +42,7 @@ class CsrfTokenNode(Node): if csrf_token == 'NOTPROVIDED': return mark_safe(u"") else: - return mark_safe(u"<div style='display:none'><input type='hidden' name='csrfmiddlewaretoken' value='%s' /></div>" % (csrf_token)) + return mark_safe(u"<div style='display:none'><input type='hidden' name='csrfmiddlewaretoken' value='%s' /></div>" % csrf_token) else: # It's very probable that the token is missing because of # misconfiguration, so we raise a warning @@ -157,15 +157,22 @@ class ForNode(Node): loop_dict['first'] = (i == 0) loop_dict['last'] = (i == len_values - 1) + pop_context = False if unpack: # If there are multiple loop variables, unpack the item into # them. - context.update(dict(zip(self.loopvars, item))) + try: + unpacked_vars = dict(zip(self.loopvars, item)) + except TypeError: + pass + else: + pop_context = True + context.update(unpacked_vars) else: context[self.loopvars[0]] = item for node in self.nodelist_loop: nodelist.append(node.render(context)) - if unpack: + if pop_context: # The loop variables were pushed on to the context so pop them # off again. This is necessary because the tag lets the length # of loopvars differ to the length of each set of items and we diff --git a/django/test/__init__.py b/django/test/__init__.py index 957b293e12..c996ed49d6 100644 --- a/django/test/__init__.py +++ b/django/test/__init__.py @@ -4,3 +4,4 @@ Django Unit Test and Doctest framework. from django.test.client import Client from django.test.testcases import TestCase, TransactionTestCase +from django.test.utils import Approximate diff --git a/django/test/client.py b/django/test/client.py index e5a16b6e79..08e3ff6b71 100644 --- a/django/test/client.py +++ b/django/test/client.py @@ -3,6 +3,7 @@ from urlparse import urlparse, urlunparse, urlsplit import sys import os import re +import mimetypes try: from cStringIO import StringIO except ImportError: @@ -54,6 +55,10 @@ class ClientHandler(BaseHandler): Uses the WSGI interface to compose requests, but returns the raw HttpResponse object """ + def __init__(self, enforce_csrf_checks=True, *args, **kwargs): + self.enforce_csrf_checks = enforce_csrf_checks + super(ClientHandler, self).__init__(*args, **kwargs) + def __call__(self, environ): from django.conf import settings from django.core import signals @@ -70,7 +75,7 @@ class ClientHandler(BaseHandler): # CsrfViewMiddleware. This makes life easier, and is probably # required for backwards compatibility with external tests against # admin views. - request._dont_enforce_csrf_checks = True + request._dont_enforce_csrf_checks = not self.enforce_csrf_checks response = self.get_response(request) # Apply response middleware. @@ -138,11 +143,14 @@ def encode_multipart(boundary, data): def encode_file(boundary, key, file): to_str = lambda s: smart_str(s, settings.DEFAULT_CHARSET) + content_type = mimetypes.guess_type(file.name)[0] + if content_type is None: + content_type = 'application/octet-stream' return [ '--' + boundary, 'Content-Disposition: form-data; name="%s"; filename="%s"' \ % (to_str(key), to_str(os.path.basename(file.name))), - 'Content-Type: application/octet-stream', + 'Content-Type: %s' % content_type, '', file.read() ] @@ -165,8 +173,8 @@ class Client(object): contexts and templates produced by a view, rather than the HTML rendered to the end-user. """ - def __init__(self, **defaults): - self.handler = ClientHandler() + def __init__(self, enforce_csrf_checks=False, **defaults): + self.handler = ClientHandler(enforce_csrf_checks) self.defaults = defaults self.cookies = SimpleCookie() self.exc_info = None @@ -289,7 +297,7 @@ class Client(object): response = self.request(**r) if follow: - response = self._handle_redirects(response) + response = self._handle_redirects(response, **extra) return response def post(self, path, data={}, content_type=MULTIPART_CONTENT, @@ -321,7 +329,7 @@ class Client(object): response = self.request(**r) if follow: - response = self._handle_redirects(response) + response = self._handle_redirects(response, **extra) return response def head(self, path, data={}, follow=False, **extra): @@ -340,7 +348,7 @@ class Client(object): response = self.request(**r) if follow: - response = self._handle_redirects(response) + response = self._handle_redirects(response, **extra) return response def options(self, path, data={}, follow=False, **extra): @@ -358,7 +366,7 @@ class Client(object): response = self.request(**r) if follow: - response = self._handle_redirects(response) + response = self._handle_redirects(response, **extra) return response def put(self, path, data={}, content_type=MULTIPART_CONTENT, @@ -390,7 +398,7 @@ class Client(object): response = self.request(**r) if follow: - response = self._handle_redirects(response) + response = self._handle_redirects(response, **extra) return response def delete(self, path, data={}, follow=False, **extra): @@ -408,7 +416,7 @@ class Client(object): response = self.request(**r) if follow: - response = self._handle_redirects(response) + response = self._handle_redirects(response, **extra) return response def login(self, **credentials): @@ -463,7 +471,7 @@ class Client(object): session.delete(session_key=session_cookie.value) self.cookies = SimpleCookie() - def _handle_redirects(self, response): + def _handle_redirects(self, response, **extra): "Follows any redirects by requesting responses from the server using GET." response.redirect_chain = [] @@ -474,7 +482,6 @@ class Client(object): redirect_chain = response.redirect_chain redirect_chain.append((url, response.status_code)) - extra = {} if scheme: extra['wsgi.url_scheme'] = scheme diff --git a/django/test/testcases.py b/django/test/testcases.py index 2f8acad68c..10bd6c6c9f 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -347,7 +347,7 @@ class TransactionTestCase(unittest.TestCase): def assertContains(self, response, text, count=None, status_code=200, msg_prefix=''): """ - Asserts that a response indicates that a page was retrieved + Asserts that a response indicates that some content was retrieved successfully, (i.e., the HTTP status code was as expected), and that ``text`` occurs ``count`` times in the content of the response. If ``count`` is None, the count doesn't matter - the assertion is true @@ -357,7 +357,7 @@ class TransactionTestCase(unittest.TestCase): msg_prefix += ": " self.assertEqual(response.status_code, status_code, - msg_prefix + "Couldn't retrieve page: Response code was %d" + msg_prefix + "Couldn't retrieve content: Response code was %d" " (expected %d)" % (response.status_code, status_code)) text = smart_str(text, response._charset) real_count = response.content.count(text) @@ -372,7 +372,7 @@ class TransactionTestCase(unittest.TestCase): def assertNotContains(self, response, text, status_code=200, msg_prefix=''): """ - Asserts that a response indicates that a page was retrieved + Asserts that a response indicates that some content was retrieved successfully, (i.e., the HTTP status code was as expected), and that ``text`` doesn't occurs in the content of the response. """ @@ -380,7 +380,7 @@ class TransactionTestCase(unittest.TestCase): msg_prefix += ": " self.assertEqual(response.status_code, status_code, - msg_prefix + "Couldn't retrieve page: Response code was %d" + msg_prefix + "Couldn't retrieve content: Response code was %d" " (expected %d)" % (response.status_code, status_code)) text = smart_str(text, response._charset) self.assertEqual(response.content.count(text), 0, @@ -466,6 +466,9 @@ class TransactionTestCase(unittest.TestCase): msg_prefix + "Template '%s' was used unexpectedly in rendering" " the response" % template_name) + def assertQuerysetEqual(self, qs, values, transform=repr): + return self.assertEqual(map(transform, qs), values) + def connections_support_transactions(): """ Returns True if all connections support transactions. This is messy diff --git a/django/test/utils.py b/django/test/utils.py index b6ab39901b..8ecb5a0e60 100644 --- a/django/test/utils.py +++ b/django/test/utils.py @@ -1,4 +1,6 @@ -import sys, time, os +import sys +import time +import os from django.conf import settings from django.core import mail from django.core.mail.backends import locmem @@ -6,6 +8,21 @@ from django.test import signals from django.template import Template from django.utils.translation import deactivate + +class Approximate(object): + def __init__(self, val, places=7): + self.val = val + self.places = places + + def __repr__(self): + return repr(self.val) + + def __eq__(self, other): + if self.val == other: + return True + return round(abs(self.val-other), self.places) == 0 + + class ContextList(list): """A wrapper that provides direct key access to context items contained in a list of context objects. @@ -19,6 +36,12 @@ class ContextList(list): else: return super(ContextList, self).__getitem__(key) + def __contains__(self, key): + try: + value = self[key] + except KeyError: + return False + return True def instrumented_test_render(self, context): """ diff --git a/django/utils/datastructures.py b/django/utils/datastructures.py index 3cbbe27b91..30ce28d38d 100644 --- a/django/utils/datastructures.py +++ b/django/utils/datastructures.py @@ -77,6 +77,27 @@ class MergeDict(object): """Returns a copy of this object.""" return self.__copy__() + def __str__(self): + ''' + Returns something like + + "{'key1': 'val1', 'key2': 'val2', 'key3': 'val3'}" + + instead of the generic "<object meta-data>" inherited from object. + ''' + return str(dict(self.items())) + + def __repr__(self): + ''' + Returns something like + + MergeDict({'key1': 'val1', 'key2': 'val2'}, {'key3': 'val3'}) + + instead of generic "<object meta-data>" inherited from object. + ''' + dictreprs = ', '.join(repr(d) for d in self.dicts) + return '%s(%s)' % (self.__class__.__name__, dictreprs) + class SortedDict(dict): """ A dictionary that keeps its keys in the order in which they're inserted. @@ -99,9 +120,11 @@ class SortedDict(dict): self.keyOrder = data.keys() else: self.keyOrder = [] + seen = set() for key, value in data: - if key not in self.keyOrder: + if key not in seen: self.keyOrder.append(key) + seen.add(key) def __deepcopy__(self, memo): return self.__class__([(key, deepcopy(value, memo)) diff --git a/django/utils/formats.py b/django/utils/formats.py index 31027abd23..372998f221 100644 --- a/django/utils/formats.py +++ b/django/utils/formats.py @@ -79,16 +79,16 @@ def localize(value): Checks if value is a localizable type (date, number...) and returns it formatted as a string using current locale format """ - if settings.USE_L10N: - if isinstance(value, (decimal.Decimal, float, int)): - return number_format(value) - elif isinstance(value, datetime.datetime): - return date_format(value, 'DATETIME_FORMAT') - elif isinstance(value, datetime.date): - return date_format(value) - elif isinstance(value, datetime.time): - return time_format(value, 'TIME_FORMAT') - return value + if isinstance(value, (decimal.Decimal, float, int)): + return number_format(value) + elif isinstance(value, datetime.datetime): + return date_format(value, 'DATETIME_FORMAT') + elif isinstance(value, datetime.date): + return date_format(value) + elif isinstance(value, datetime.time): + return time_format(value, 'TIME_FORMAT') + else: + return value def localize_input(value, default=None): """ diff --git a/django/utils/hashcompat.py b/django/utils/hashcompat.py index b1e6021890..4d9b76f3a6 100644 --- a/django/utils/hashcompat.py +++ b/django/utils/hashcompat.py @@ -1,17 +1,17 @@ """ The md5 and sha modules are deprecated since Python 2.5, replaced by the hashlib module containing both hash algorithms. Here, we provide a common -interface to the md5 and sha constructors, preferring the hashlib module when -available. +interface to the md5 and sha constructors, depending on system version. """ -try: +import sys +if sys.version_info >= (2, 5): import hashlib md5_constructor = hashlib.md5 md5_hmac = md5_constructor sha_constructor = hashlib.sha1 sha_hmac = sha_constructor -except ImportError: +else: import md5 md5_constructor = md5.new md5_hmac = md5 diff --git a/django/utils/text.py b/django/utils/text.py index 5d633b7afe..b05460486d 100644 --- a/django/utils/text.py +++ b/django/utils/text.py @@ -39,7 +39,10 @@ wrap = allow_lazy(wrap, unicode) def truncate_words(s, num, end_text='...'): """Truncates a string after a certain number of words. Takes an optional argument of what should be used to notify that the string has been - truncated, defaults to ellipsis (...)""" + truncated, defaulting to ellipsis (...) + + Newlines in the string will be stripped. + """ s = force_unicode(s) length = int(num) words = s.split() @@ -51,10 +54,13 @@ def truncate_words(s, num, end_text='...'): truncate_words = allow_lazy(truncate_words, unicode) def truncate_html_words(s, num, end_text='...'): - """Truncates html to a certain number of words (not counting tags and + """Truncates HTML to a certain number of words (not counting tags and comments). Closes opened tags if they were correctly closed in the given html. Takes an optional argument of what should be used to notify that the - string has been truncated, defaults to ellipsis (...).""" + string has been truncated, defaulting to ellipsis (...). + + Newlines in the HTML are preserved. + """ s = force_unicode(s) length = int(num) if length <= 0: diff --git a/django/utils/translation/trans_real.py b/django/utils/translation/trans_real.py index 017b99a8ca..f732cd931e 100644 --- a/django/utils/translation/trans_real.py +++ b/django/utils/translation/trans_real.py @@ -502,7 +502,7 @@ def parse_accept_lang_header(lang_string): return [] priority = priority and float(priority) or 1.0 result.append((lang, priority)) - result.sort(lambda x, y: -cmp(x[1], y[1])) + result.sort(key=lambda k: k[1], reverse=True) return result # get_date_formats and get_partial_date_formats aren't used anymore by Django diff --git a/django/views/csrf.py b/django/views/csrf.py index fa996fff24..c627812dcb 100644 --- a/django/views/csrf.py +++ b/django/views/csrf.py @@ -23,7 +23,7 @@ CSRF_FAILRE_TEMPLATE = """ h1 span { font-size:60%; color:#666; font-weight:normal; } #info { background:#f6f6f6; } #info ul { margin: 0.5em 4em; } - #info p { padding-top:10px; } + #info p, #summary p { padding-top:10px; } #summary { background: #ffc; } #explanation { background:#eee; border-bottom: 0px none; } </style> @@ -32,6 +32,16 @@ CSRF_FAILRE_TEMPLATE = """ <div id="summary"> <h1>Forbidden <span>(403)</span></h1> <p>CSRF verification failed. Request aborted.</p> +{% if no_referer %} + <p>You are seeing this message because this HTTPS site requires a 'Referer + header' to be sent by your web browser, but none was sent. This header is + required for security reasons, to ensure that your browser is not being + hijacked by third parties.</p> + + <p>If you have configured your browser to disable 'Referer' headers, please + re-enable them, at least for this site, or for HTTPS connections, or for + 'same-origin' requests.</p> +{% endif %} </div> {% if DEBUG %} <div id="info"> @@ -83,7 +93,10 @@ def csrf_failure(request, reason=""): """ Default view used when request fails CSRF protection """ + from django.middleware.csrf import REASON_NO_REFERER t = Template(CSRF_FAILRE_TEMPLATE) c = Context({'DEBUG': settings.DEBUG, - 'reason': reason}) + 'reason': reason, + 'no_referer': reason == REASON_NO_REFERER + }) return HttpResponseForbidden(t.render(c), mimetype='text/html') diff --git a/django/views/debug.py b/django/views/debug.py index 6604bd3dae..7050ea38fb 100644 --- a/django/views/debug.py +++ b/django/views/debug.py @@ -12,7 +12,7 @@ from django.utils.importlib import import_module from django.utils.encoding import smart_unicode, smart_str -HIDDEN_SETTINGS = re.compile('SECRET|PASSWORD|PROFANITIES_LIST') +HIDDEN_SETTINGS = re.compile('SECRET|PASSWORD|PROFANITIES_LIST|SIGNATURE') def linebreak_iter(template_source): yield 0 @@ -412,7 +412,7 @@ TECHNICAL_500_TEMPLATE = """ <body> <div id="summary"> <h1>{{ exception_type }} at {{ request.path_info|escape }}</h1> - <pre class="exception_value">{{ exception_value|escape }}</pre> + <pre class="exception_value">{{ exception_value|force_escape }}</pre> <table class="meta"> <tr> <th>Request Method:</th> @@ -432,7 +432,7 @@ TECHNICAL_500_TEMPLATE = """ </tr> <tr> <th>Exception Value:</th> - <td><pre>{{ exception_value|escape }}</pre></td> + <td><pre>{{ exception_value|force_escape }}</pre></td> </tr> <tr> <th>Exception Location:</th> @@ -459,7 +459,7 @@ TECHNICAL_500_TEMPLATE = """ {% if unicode_hint %} <div id="unicode-hint"> <h2>Unicode error hint</h2> - <p>The string that could not be encoded/decoded was: <strong>{{ unicode_hint|escape }}</strong></p> + <p>The string that could not be encoded/decoded was: <strong>{{ unicode_hint|force_escape }}</strong></p> </div> {% endif %} {% if template_does_not_exist %} @@ -532,8 +532,8 @@ TECHNICAL_500_TEMPLATE = """ <tbody> {% for var in frame.vars|dictsort:"0" %} <tr> - <td>{{ var.0|escape }}</td> - <td class="code"><div>{{ var.1|pprint|escape }}</div></td> + <td>{{ var.0|force_escape }}</td> + <td class="code"><div>{{ var.1|pprint|force_escape }}</div></td> </tr> {% endfor %} </tbody> @@ -582,7 +582,7 @@ Traceback: {% if frame.context_line %} {{ frame.lineno }}. {{ frame.context_line|escape }}{% endif %} {% endfor %} Exception Type: {{ exception_type|escape }} at {{ request.path_info|escape }} -Exception Value: {{ exception_value|escape }} +Exception Value: {{ exception_value|force_escape }} </textarea> <br><br> <input type="submit" value="Share this traceback on a public Web site"> @@ -778,7 +778,12 @@ TECHNICAL_404_TEMPLATE = """ </p> <ol> {% for pattern in urlpatterns %} - <li>{{ pattern }}</li> + <li> + {% for pat in pattern %} + {{ pat.regex.pattern }} + {% if forloop.last and pat.name %}[name='{{ pat.name }}']{% endif %} + {% endfor %} + </li> {% endfor %} </ol> <p>The current URL, <code>{{ request_path|escape }}</code>, didn't match any of these.</p> diff --git a/django/views/generic/simple.py b/django/views/generic/simple.py index 3b5309df96..435cd7623d 100644 --- a/django/views/generic/simple.py +++ b/django/views/generic/simple.py @@ -17,7 +17,7 @@ def direct_to_template(request, template, extra_context=None, mimetype=None, **k t = loader.get_template(template) return HttpResponse(t.render(c), mimetype=mimetype) -def redirect_to(request, url, permanent=True, **kwargs): +def redirect_to(request, url, permanent=True, query_string=False, **kwargs): """ Redirect to a given URL. @@ -33,7 +33,15 @@ def redirect_to(request, url, permanent=True, **kwargs): If the ``permanent`` argument is False, then the response will have a 302 HTTP status code. Otherwise, the status code will be 301. + + If the ``query_string`` argument is True, then the GET query string + from the request is appended to the URL. + """ + args = request.META["QUERY_STRING"] + if args and query_string and url is not None: + url = "%s?%s" % (url, args) + if url is not None: klass = permanent and HttpResponsePermanentRedirect or HttpResponseRedirect return klass(url % kwargs) diff --git a/docs/Makefile b/docs/Makefile index f6a92b6835..9301315040 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -12,20 +12,26 @@ PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest help: @echo "Please use \`make <target>' where <target> is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* @@ -40,6 +46,11 @@ dirhtml: @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @@ -65,12 +76,42 @@ qthelp: @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django.qhc" +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/django" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/django" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ - "run these through (pdf)latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + make -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes diff --git a/docs/_ext/djangodocs.py b/docs/_ext/djangodocs.py index efcd94d4bc..325ed76cdc 100644 --- a/docs/_ext/djangodocs.py +++ b/docs/_ext/djangodocs.py @@ -1,9 +1,9 @@ """ Sphinx plugins for Django documentation. """ +import os -import docutils.nodes -import docutils.transforms +from docutils import nodes, transforms try: import json except ImportError: @@ -14,26 +14,13 @@ except ImportError: from django.utils import simplejson as json except ImportError: json = None -import os -import sphinx -import sphinx.addnodes -try: - from sphinx import builders -except ImportError: - import sphinx.builder as builders -try: - import sphinx.builders.html as builders_html -except ImportError: - builders_html = builders + +from sphinx import addnodes, roles +from sphinx.builders.html import StandaloneHTMLBuilder +from sphinx.writers.html import SmartyPantsHTMLTranslator from sphinx.util.console import bold -import sphinx.directives -import sphinx.environment -try: - import sphinx.writers.html as sphinx_htmlwriter -except ImportError: - import sphinx.htmlwriter as sphinx_htmlwriter -import sphinx.roles -from docutils import nodes +from sphinx.util.compat import Directive + def setup(app): app.add_crossref_type( @@ -69,63 +56,70 @@ def setup(app): parse_node = parse_django_adminopt_node, ) app.add_config_value('django_next_version', '0.0', True) - app.add_directive('versionadded', parse_version_directive, 1, (1, 1, 1)) - app.add_directive('versionchanged', parse_version_directive, 1, (1, 1, 1)) + app.add_directive('versionadded', VersionDirective) + app.add_directive('versionchanged', VersionDirective) app.add_transform(SuppressBlockquotes) app.add_builder(DjangoStandaloneHTMLBuilder) - # Monkeypatch PickleHTMLBuilder so that it doesn't die in Sphinx 0.4.2 - if sphinx.__version__ == '0.4.2': - monkeypatch_pickle_builder() - -def parse_version_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - env = state.document.settings.env - is_nextversion = env.config.django_next_version == arguments[0] - ret = [] - node = sphinx.addnodes.versionmodified() - ret.append(node) - if not is_nextversion: - if len(arguments) == 1: - linktext = 'Please, see the release notes <releases-%s>' % (arguments[0]) - xrefs = sphinx.roles.xfileref_role('ref', linktext, linktext, lineno, state) - node.extend(xrefs[0]) - node['version'] = arguments[0] - else: - node['version'] = "Development version" - node['type'] = name - if len(arguments) == 2: - inodes, messages = state.inline_text(arguments[1], lineno+1) - node.extend(inodes) - if content: - state.nested_parse(content, content_offset, node) - ret = ret + messages - env.note_versionchange(node['type'], node['version'], node, lineno) - return ret - -class SuppressBlockquotes(docutils.transforms.Transform): +class VersionDirective(Directive): + has_content = True + required_arguments = 1 + optional_arguments = 1 + final_argument_whitespace = True + option_spec = {} + + def run(self): + env = self.state.document.settings.env + arg0 = self.arguments[0] + is_nextversion = env.config.django_next_version == arg0 + ret = [] + node = addnodes.versionmodified() + ret.append(node) + if not is_nextversion: + if len(self.arguments) == 1: + linktext = 'Please, see the release notes </releases/%s>' % (arg0) + try: + xrefs = roles.XRefRole()('doc', linktext, linktext, self.lineno, self.state) # Sphinx >= 1.0 + except AttributeError: + xrefs = roles.xfileref_role('doc', linktext, linktext, self.lineno, self.state) # Sphinx < 1.0 + node.extend(xrefs[0]) + node['version'] = arg0 + else: + node['version'] = "Development version" + node['type'] = self.name + if len(self.arguments) == 2: + inodes, messages = self.state.inline_text(self.arguments[1], self.lineno+1) + node.extend(inodes) + if self.content: + self.state.nested_parse(self.content, self.content_offset, node) + ret = ret + messages + env.note_versionchange(node['type'], node['version'], node, self.lineno) + return ret + + +class SuppressBlockquotes(transforms.Transform): """ Remove the default blockquotes that encase indented list, tables, etc. """ default_priority = 300 - + suppress_blockquote_child_nodes = ( - docutils.nodes.bullet_list, - docutils.nodes.enumerated_list, - docutils.nodes.definition_list, - docutils.nodes.literal_block, - docutils.nodes.doctest_block, - docutils.nodes.line_block, - docutils.nodes.table + nodes.bullet_list, + nodes.enumerated_list, + nodes.definition_list, + nodes.literal_block, + nodes.doctest_block, + nodes.line_block, + nodes.table ) - + def apply(self): - for node in self.document.traverse(docutils.nodes.block_quote): + for node in self.document.traverse(nodes.block_quote): if len(node.children) == 1 and isinstance(node.children[0], self.suppress_blockquote_child_nodes): node.replace_self(node.children[0]) -class DjangoHTMLTranslator(sphinx_htmlwriter.SmartyPantsHTMLTranslator): +class DjangoHTMLTranslator(SmartyPantsHTMLTranslator): """ Django-specific reST to HTML tweaks. """ @@ -133,42 +127,41 @@ class DjangoHTMLTranslator(sphinx_htmlwriter.SmartyPantsHTMLTranslator): # Don't use border=1, which docutils does by default. def visit_table(self, node): self.body.append(self.starttag(node, 'table', CLASS='docutils')) - + # <big>? Really? def visit_desc_parameterlist(self, node): self.body.append('(') self.first_param = 1 - + def depart_desc_parameterlist(self, node): self.body.append(')') - pass - + # # Don't apply smartypants to literal blocks # def visit_literal_block(self, node): self.no_smarty += 1 - sphinx_htmlwriter.SmartyPantsHTMLTranslator.visit_literal_block(self, node) + SmartyPantsHTMLTranslator.visit_literal_block(self, node) def depart_literal_block(self, node): - sphinx_htmlwriter.SmartyPantsHTMLTranslator.depart_literal_block(self, node) + SmartyPantsHTMLTranslator.depart_literal_block(self, node) self.no_smarty -= 1 - + # - # Turn the "new in version" stuff (versoinadded/versionchanged) into a + # Turn the "new in version" stuff (versionadded/versionchanged) into a # better callout -- the Sphinx default is just a little span, # which is a bit less obvious that I'd like. # - # FIXME: these messages are all hardcoded in English. We need to chanage + # FIXME: these messages are all hardcoded in English. We need to change # that to accomodate other language docs, but I can't work out how to make - # that work and I think it'll require Sphinx 0.5 anyway. + # that work. # version_text = { 'deprecated': 'Deprecated in Django %s', 'versionchanged': 'Changed in Django %s', 'versionadded': 'New in Django %s', } - + def visit_versionmodified(self, node): self.body.append( self.starttag(node, 'div', CLASS=node['type']) @@ -178,41 +171,31 @@ class DjangoHTMLTranslator(sphinx_htmlwriter.SmartyPantsHTMLTranslator): len(node) and ":" or "." ) self.body.append('<span class="title">%s</span> ' % title) - + def depart_versionmodified(self, node): self.body.append("</div>\n") - - # Give each section a unique ID -- nice for custom CSS hooks - # This is different on docutils 0.5 vs. 0.4... - - if hasattr(sphinx_htmlwriter.SmartyPantsHTMLTranslator, 'start_tag_with_title') and sphinx.__version__ == '0.4.2': - def start_tag_with_title(self, node, tagname, **atts): - node = { - 'classes': node.get('classes', []), - 'ids': ['s-%s' % i for i in node.get('ids', [])] - } - return self.starttag(node, tagname, **atts) - else: - def visit_section(self, node): - old_ids = node.get('ids', []) - node['ids'] = ['s-' + i for i in old_ids] - if sphinx.__version__ != '0.4.2': - node['ids'].extend(old_ids) - sphinx_htmlwriter.SmartyPantsHTMLTranslator.visit_section(self, node) - node['ids'] = old_ids + # Give each section a unique ID -- nice for custom CSS hooks + def visit_section(self, node): + old_ids = node.get('ids', []) + node['ids'] = ['s-' + i for i in old_ids] + node['ids'].extend(old_ids) + SmartyPantsHTMLTranslator.visit_section(self, node) + node['ids'] = old_ids def parse_django_admin_node(env, sig, signode): command = sig.split(' ')[0] env._django_curr_admin_command = command title = "django-admin.py %s" % sig - signode += sphinx.addnodes.desc_name(title, title) + signode += addnodes.desc_name(title, title) return sig def parse_django_adminopt_node(env, sig, signode): """A copy of sphinx.directives.CmdoptionDesc.parse_signature()""" - from sphinx import addnodes - from sphinx.directives.desc import option_desc_re + try: + from sphinx.domains.std import option_desc_re # Sphinx >= 1.0 + except ImportError: + from sphinx.directives.desc import option_desc_re # Sphinx < 1.0 count = 0 firstname = '' for m in option_desc_re.finditer(sig): @@ -228,44 +211,8 @@ def parse_django_adminopt_node(env, sig, signode): raise ValueError return firstname -def monkeypatch_pickle_builder(): - import shutil - from os import path - try: - import cPickle as pickle - except ImportError: - import pickle - - def handle_finish(self): - # dump the global context - outfilename = path.join(self.outdir, 'globalcontext.pickle') - f = open(outfilename, 'wb') - try: - pickle.dump(self.globalcontext, f, 2) - finally: - f.close() - - self.info(bold('dumping search index...')) - self.indexer.prune(self.env.all_docs) - f = open(path.join(self.outdir, 'searchindex.pickle'), 'wb') - try: - self.indexer.dump(f, 'pickle') - finally: - f.close() - - # copy the environment file from the doctree dir to the output dir - # as needed by the web app - shutil.copyfile(path.join(self.doctreedir, builders.ENV_PICKLE_FILENAME), - path.join(self.outdir, builders.ENV_PICKLE_FILENAME)) - # touch 'last build' file, used by the web application to determine - # when to reload its environment and clear the cache - open(path.join(self.outdir, builders.LAST_BUILD_FILENAME), 'w').close() - - builders.PickleHTMLBuilder.handle_finish = handle_finish - - -class DjangoStandaloneHTMLBuilder(builders_html.StandaloneHTMLBuilder): +class DjangoStandaloneHTMLBuilder(StandaloneHTMLBuilder): """ Subclass to add some extra things we need. """ @@ -278,9 +225,14 @@ class DjangoStandaloneHTMLBuilder(builders_html.StandaloneHTMLBuilder): self.warn("cannot create templatebuiltins.js due to missing simplejson dependency") return self.info(bold("writing templatebuiltins.js...")) - xrefs = self.env.reftargets.keys() - templatebuiltins = dict([('ttags', [n for (t,n) in xrefs if t == 'ttag']), - ('tfilters', [n for (t,n) in xrefs if t == 'tfilter'])]) + try: + xrefs = self.env.reftargets.keys() + templatebuiltins = dict([('ttags', [n for (t,n) in xrefs if t == 'ttag']), + ('tfilters', [n for (t,n) in xrefs if t == 'tfilter'])]) + except AttributeError: + xrefs = self.env.domaindata["std"]["objects"] + templatebuiltins = dict([('ttags', [n for (t,n) in xrefs if t == 'templatetag']), + ('tfilters', [n for (t,n) in xrefs if t == 'templatefilter'])]) outfilename = os.path.join(self.outdir, "templatebuiltins.js") f = open(outfilename, 'wb') f.write('var django_template_builtins = ') diff --git a/docs/_templates/genindex.html b/docs/_templates/genindex.html deleted file mode 100644 index 60c19efd45..0000000000 --- a/docs/_templates/genindex.html +++ /dev/null @@ -1,4 +0,0 @@ -{% extends "!genindex.html" %} - -{% block bodyclass %}{% endblock %} -{% block sidebarwrapper %}{% endblock %}
\ No newline at end of file diff --git a/docs/_templates/modindex.html b/docs/_templates/modindex.html deleted file mode 100644 index 96a1d2080a..0000000000 --- a/docs/_templates/modindex.html +++ /dev/null @@ -1,3 +0,0 @@ -{% extends "!modindex.html" %} -{% block bodyclass %}{% endblock %} -{% block sidebarwrapper %}{% endblock %}
\ No newline at end of file diff --git a/docs/_templates/search.html b/docs/_templates/search.html deleted file mode 100644 index 8bd6dbd332..0000000000 --- a/docs/_templates/search.html +++ /dev/null @@ -1,3 +0,0 @@ -{% extends "!search.html" %} -{% block bodyclass %}{% endblock %} -{% block sidebarwrapper %}{% endblock %}
\ No newline at end of file diff --git a/docs/_theme/djangodocs/genindex.html b/docs/_theme/djangodocs/genindex.html new file mode 100644 index 0000000000..486994ae91 --- /dev/null +++ b/docs/_theme/djangodocs/genindex.html @@ -0,0 +1,4 @@ +{% extends "basic/genindex.html" %} + +{% block bodyclass %}{% endblock %} +{% block sidebarwrapper %}{% endblock %}
\ No newline at end of file diff --git a/docs/_templates/layout.html b/docs/_theme/djangodocs/layout.html index 70e029c3ac..ef91dd77a9 100644 --- a/docs/_templates/layout.html +++ b/docs/_theme/djangodocs/layout.html @@ -1,4 +1,4 @@ -{% extends "!layout.html" %} +{% extends "basic/layout.html" %} {%- macro secondnav() %} {%- if prev %} @@ -61,7 +61,7 @@ <a title="Home page" href="{{ pathto('index') }}">Home</a> {{ reldelim2 }} <a title="Table of contents" href="{{ pathto('contents') }}">Table of contents</a> {{ reldelim2 }} <a title="Global index" href="{{ pathto('genindex') }}">Index</a> {{ reldelim2 }} - <a title="Module index" href="{{ pathto('modindex') }}">Modules</a> + <a title="Module index" href="{{ pathto('py-modindex') }}">Modules</a> </div> <div class="nav">{{ secondnav() }}</div> </div> diff --git a/docs/_theme/djangodocs/modindex.html b/docs/_theme/djangodocs/modindex.html new file mode 100644 index 0000000000..59a5cb31bd --- /dev/null +++ b/docs/_theme/djangodocs/modindex.html @@ -0,0 +1,3 @@ +{% extends "basic/modindex.html" %} +{% block bodyclass %}{% endblock %} +{% block sidebarwrapper %}{% endblock %}
\ No newline at end of file diff --git a/docs/_theme/djangodocs/search.html b/docs/_theme/djangodocs/search.html new file mode 100644 index 0000000000..943478ce75 --- /dev/null +++ b/docs/_theme/djangodocs/search.html @@ -0,0 +1,3 @@ +{% extends "basic/search.html" %} +{% block bodyclass %}{% endblock %} +{% block sidebarwrapper %}{% endblock %}
\ No newline at end of file diff --git a/docs/_static/default.css b/docs/_theme/djangodocs/static/default.css index 9dc69ee785..9dc69ee785 100644 --- a/docs/_static/default.css +++ b/docs/_theme/djangodocs/static/default.css diff --git a/docs/_static/djangodocs.css b/docs/_theme/djangodocs/static/djangodocs.css index 4adb8387cc..4adb8387cc 100644 --- a/docs/_static/djangodocs.css +++ b/docs/_theme/djangodocs/static/djangodocs.css diff --git a/docs/_static/docicons-behindscenes.png b/docs/_theme/djangodocs/static/docicons-behindscenes.png Binary files differindex dc901bc867..dc901bc867 100644 --- a/docs/_static/docicons-behindscenes.png +++ b/docs/_theme/djangodocs/static/docicons-behindscenes.png diff --git a/docs/_static/docicons-note.png b/docs/_theme/djangodocs/static/docicons-note.png Binary files differindex 357545fb32..357545fb32 100644 --- a/docs/_static/docicons-note.png +++ b/docs/_theme/djangodocs/static/docicons-note.png diff --git a/docs/_static/docicons-philosophy.png b/docs/_theme/djangodocs/static/docicons-philosophy.png Binary files differindex 09f16c785b..09f16c785b 100644 --- a/docs/_static/docicons-philosophy.png +++ b/docs/_theme/djangodocs/static/docicons-philosophy.png diff --git a/docs/_static/homepage.css b/docs/_theme/djangodocs/static/homepage.css index 276c5470ab..276c5470ab 100644 --- a/docs/_static/homepage.css +++ b/docs/_theme/djangodocs/static/homepage.css diff --git a/docs/_static/reset-fonts-grids.css b/docs/_theme/djangodocs/static/reset-fonts-grids.css index f5238d7c91..f5238d7c91 100644 --- a/docs/_static/reset-fonts-grids.css +++ b/docs/_theme/djangodocs/static/reset-fonts-grids.css diff --git a/docs/_theme/djangodocs/theme.conf b/docs/_theme/djangodocs/theme.conf new file mode 100644 index 0000000000..be43c723ae --- /dev/null +++ b/docs/_theme/djangodocs/theme.conf @@ -0,0 +1,4 @@ +[theme] +inherit = basic +stylesheet = default.css +pygments_style = trac diff --git a/docs/conf.py b/docs/conf.py index 90e0a6bcb5..606ee6b5ad 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -8,28 +8,35 @@ # The contents of this file are pickled, so don't put values in the namespace # that aren't pickleable (module imports are okay, they're removed automatically). # -# All configuration values have a default value; values that are commented out -# serve to show the default value. +# All configuration values have a default; values that are commented out +# serve to show the default. import sys import os -# If your extensions are in another directory, add it here. -sys.path.append(os.path.join(os.path.dirname(__file__), "_ext")) +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "_ext"))) -# General configuration -# --------------------- +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ["djangodocs"] # Add any paths that contain templates here, relative to this directory. -templates_path = ["_templates"] +# templates_path = [] # The suffix of source filenames. source_suffix = '.txt' +# The encoding of source files. +#source_encoding = 'utf-8-sig' + # The master toctree document. master_doc = 'contents' @@ -37,8 +44,10 @@ master_doc = 'contents' project = 'Django' copyright = 'Django Software Foundation and contributors' -# The default replacements for |version| and |release|, also used in various -# other places throughout the built documents. + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. # # The short X.Y version. version = '1.2' @@ -47,14 +56,22 @@ release = version # The next version to be released django_next_version = '1.3' +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. today_fmt = '%B %d, %Y' -# List of documents that shouldn't be included in the build. -#unused_docs = [] +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = True @@ -75,13 +92,35 @@ pygments_style = 'trac' # Note: exclude_dirnames is new in Sphinx 0.5 exclude_dirnames = ['.svn'] -# Options for HTML output -# ----------------------- +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = "djangodocs" + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +html_theme_path = ["_theme"] + +# The name for this set of Sphinx documents. If None, it defaults to +# "<project> v<release> documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None -# The style sheet to use for HTML and HTML Help pages. A file of that name -# must exist either in Sphinx' static/ path, or in one of the custom paths -# given in html_static_path. -html_style = 'default.css' +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, @@ -110,17 +149,38 @@ html_translator_class = "djangodocs.DjangoHTMLTranslator" html_additional_pages = {} # If false, no module index is generated. -#html_use_modindex = True +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True -# If true, the reST sources are included in the HTML build as _sources/<name>. -html_copy_source = True +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a <link> tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'Djangodoc' +modindex_common_prefix = ["django."] + -# Options for LaTeX output -# ------------------------ +# -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' @@ -132,9 +192,24 @@ htmlhelp_basename = 'Djangodoc' # (source start file, target name, title, author, document class [howto/manual]). #latex_documents = [] latex_documents = [ - ('contents', 'django.tex', 'Django Documentation', 'Django Software Foundation', 'manual'), + ('contents', 'django.tex', u'Django Documentation', + u'Django Software Foundation', 'manual'), ] +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + # Additional stuff for the LaTeX preamble. #latex_preamble = '' @@ -142,10 +217,53 @@ latex_documents = [ #latex_appendices = [] # If false, no module index is generated. -#latex_use_modindex = True +#latex_domain_indices = True -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -# If this isn't set to True, the LaTex writer can only handle six levels of headers. -latex_use_parts = True +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('contents', 'django', 'Django Documentation', ['Django Software Foundation'], 1) +] + + +# -- Options for Epub output --------------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = u'Django' +epub_author = u'Django Software Foundation' +epub_publisher = u'Django Software Foundation' +epub_copyright = u'2010, Django Software Foundation' + +# The language of the text. It defaults to the language option +# or en if the language is not set. +#epub_language = '' + +# The scheme of the identifier. Typical schemes are ISBN or URL. +#epub_scheme = '' + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +#epub_identifier = '' + +# A unique identification for the text. +#epub_uid = '' + +# HTML files that should be inserted before the pages created by sphinx. +# The format is a list of tuples containing the path and title. +#epub_pre_files = [] + +# HTML files shat should be inserted after the pages created by sphinx. +# The format is a list of tuples containing the path and title. +#epub_post_files = [] + +# A list of files that should not be packed into the epub file. +#epub_exclude_files = [] + +# The depth of the table of contents in toc.ncx. +#epub_tocdepth = 3 + +# Allow duplicate toc entries. +#epub_tocdup = True diff --git a/docs/faq/admin.txt b/docs/faq/admin.txt index ed705d5f21..ac2e594ed2 100644 --- a/docs/faq/admin.txt +++ b/docs/faq/admin.txt @@ -1,5 +1,3 @@ -.. _faq-admin: - FAQ: The admin ============== @@ -32,30 +30,33 @@ How can I prevent the cache middleware from caching the admin site? ------------------------------------------------------------------- Set the :setting:`CACHE_MIDDLEWARE_ANONYMOUS_ONLY` setting to ``True``. See the -:ref:`cache documentation <topics-cache>` for more information. +:doc:`cache documentation </topics/cache>` for more information. How do I automatically set a field's value to the user who last edited the object in the admin? ----------------------------------------------------------------------------------------------- -The :class:`ModelAdmin` class provides customization hooks that allow you to transform -an object as it saved, using details from the request. By extracting the current user -from the request, and customizing the :meth:`ModelAdmin.save_model` hook, you can update -an object to reflect the user that edited it. See :ref:`the documentation on ModelAdmin -methods <model-admin-methods>` for an example. +The :class:`~django.contrib.admin.ModelAdmin` class provides customization hooks +that allow you to transform an object as it saved, using details from the +request. By extracting the current user from the request, and customizing the +:meth:`~django.contrib.admin.ModelAdmin.save_model` hook, you can update an +object to reflect the user that edited it. See :ref:`the documentation on +ModelAdmin methods <model-admin-methods>` for an example. How do I limit admin access so that objects can only be edited by the users who created them? --------------------------------------------------------------------------------------------- -The :class:`ModelAdmin` class also provides customization hooks that allow you to control the -visibility and editability of objects in the admin. Using the same trick of extracting the -user from the request, the :meth:`ModelAdmin.queryset` and :meth:`ModelAdmin.has_change_permission` -can be used to control the visibility and editability of objects in the admin. +The :class:`~django.contrib.admin.ModelAdmin` class also provides customization +hooks that allow you to control the visibility and editability of objects in the +admin. Using the same trick of extracting the user from the request, the +:meth:`~django.contrib.admin.ModelAdmin.queryset` and +:meth:`~django.contrib.admin.ModelAdmin.has_change_permission` can be used to +control the visibility and editability of objects in the admin. -My admin-site CSS and images showed up fine using the development server, but they're not displaying when using mod_python. +My admin-site CSS and images showed up fine using the development server, but they're not displaying when using mod_wsgi. --------------------------------------------------------------------------------------------------------------------------- -See :ref:`serving the admin files <howto-deployment-modpython-serving-the-admin-files>` -in the "How to use Django with mod_python" documentation. +See :ref:`serving the admin files <serving-the-admin-files>` +in the "How to use Django with mod_wsgi" documentation. My "list_filter" contains a ManyToManyField, but the filter doesn't display. ---------------------------------------------------------------------------- @@ -91,5 +92,5 @@ We like it, but if you don't agree, you can modify the admin site's presentation by editing the CSS stylesheet and/or associated image files. The site is built using semantic HTML and plenty of CSS hooks, so any changes you'd like to make should be possible by editing the stylesheet. We've got a -:ref:`guide to the CSS used in the admin <obsolete-admin-css>` to get you started. +:doc:`guide to the CSS used in the admin </obsolete/admin-css>` to get you started. diff --git a/docs/faq/contributing.txt b/docs/faq/contributing.txt index 51a9bc2c6a..81c06f365f 100644 --- a/docs/faq/contributing.txt +++ b/docs/faq/contributing.txt @@ -1,5 +1,3 @@ -.. _faq-contributing: - FAQ: Contributing code ====================== @@ -7,7 +5,7 @@ How can I get started contributing code to Django? -------------------------------------------------- Thanks for asking! We've written an entire document devoted to this question. -It's titled :ref:`Contributing to Django <internals-contributing>`. +It's titled :doc:`Contributing to Django </internals/contributing>`. I submitted a bug fix in the ticket system several weeks ago. Why are you ignoring my patch? -------------------------------------------------------------------------------------------- diff --git a/docs/faq/general.txt b/docs/faq/general.txt index 1181d261be..1fc0f1882a 100644 --- a/docs/faq/general.txt +++ b/docs/faq/general.txt @@ -1,5 +1,3 @@ -.. _faq-general: - FAQ: General ============ @@ -63,15 +61,15 @@ at any level -- database servers, caching servers or Web/application servers. The framework cleanly separates components such as its database layer and application layer. And it ships with a simple-yet-powerful -:ref:`cache framework <topics-cache>`. +:doc:`cache framework </topics/cache>`. Who's behind this? ------------------ Django was originally developed at World Online, the Web department of a newspaper in Lawrence, Kansas, USA. Django's now run by an international team of -volunteers; you can read all about them over at the :ref:`list of committers -<internals-committers>` +volunteers; you can read all about them over at the :doc:`list of committers +</internals/committers>` Which sites use Django? ----------------------- @@ -146,7 +144,7 @@ philosophies 100%. Like we said: We're picky. We've documented our philosophies on the -:ref:`design philosophies page <misc-design-philosophies>`. +:doc:`design philosophies page </misc/design-philosophies>`. Is Django a content-management-system (CMS)? -------------------------------------------- diff --git a/docs/faq/help.txt b/docs/faq/help.txt index 5d7faf6fec..d84b3f529f 100644 --- a/docs/faq/help.txt +++ b/docs/faq/help.txt @@ -1,5 +1,3 @@ -.. _faq-help: - FAQ: Getting Help ================= diff --git a/docs/faq/index.txt b/docs/faq/index.txt index d357a3ebb0..347cabaabc 100644 --- a/docs/faq/index.txt +++ b/docs/faq/index.txt @@ -1,5 +1,3 @@ -.. _faq-index: - ========== Django FAQ ========== diff --git a/docs/faq/install.txt b/docs/faq/install.txt index f20b2bc187..3fbcb3842d 100644 --- a/docs/faq/install.txt +++ b/docs/faq/install.txt @@ -1,5 +1,3 @@ -.. _faq-install: - FAQ: Installation ================= @@ -7,9 +5,9 @@ How do I get started? --------------------- #. `Download the code`_. - #. Install Django (read the :ref:`installation guide <intro-install>`). - #. Walk through the :ref:`tutorial <intro-tutorial01>`. - #. Check out the rest of the :ref:`documentation <index>`, and `ask questions`_ if you + #. Install Django (read the :doc:`installation guide </intro/install>`). + #. Walk through the :doc:`tutorial </intro/tutorial01>`. + #. Check out the rest of the :doc:`documentation </index>`, and `ask questions`_ if you run into trouble. .. _`Download the code`: http://www.djangoproject.com/download/ @@ -19,14 +17,14 @@ What are Django's prerequisites? -------------------------------- Django requires Python_, specifically any version of Python from 2.4 -through 2.6. No other Python libraries are required for basic Django +through 2.7. No other Python libraries are required for basic Django usage. For a development environment -- if you just want to experiment with Django -- you don't need to have a separate Web server installed; Django comes with its own lightweight development server. For a production environment, Django follows the WSGI_ spec, which means it can run on a variety of server -platforms. See :ref:`Deploying Django <howto-deployment-index>` for some +platforms. See :doc:`Deploying Django </howto/deployment/index>` for some popular alternatives. Also, the `server arrangements wiki page`_ contains details for several deployment strategies. @@ -46,7 +44,7 @@ Do I lose anything by using Python 2.4 versus newer Python versions, such as Pyt ----------------------------------------------------------------------------------------------- Not in the core framework. Currently, Django itself officially supports any -version of Python from 2.4 through 2.6, inclusive. However, newer versions of +version of Python from 2.4 through 2.7, inclusive. However, newer versions of Python are often faster, have more features, and are better supported. Third-party applications for use with Django are, of course, free to set their own version requirements. @@ -56,7 +54,7 @@ versions as part of a migration which will end with Django running on Python 3 (see below for details). All else being equal, we recommend that you use the latest 2.x release -(currently Python 2.6). This will let you take advantage of the numerous +(currently Python 2.7). This will let you take advantage of the numerous improvements and optimizations to the Python language since version 2.4, and will help ease the process of dropping support for older Python versions on the road to Python 3. diff --git a/docs/faq/models.txt b/docs/faq/models.txt index 2732c0b8e1..f00d453d88 100644 --- a/docs/faq/models.txt +++ b/docs/faq/models.txt @@ -1,5 +1,3 @@ -.. _faq-models: - FAQ: Databases and models ========================= @@ -30,7 +28,7 @@ backend, and not all backends provide a way to retrieve the SQL after quoting. .. versionadded:: 1.2 -If you are using :ref:`multiple databases<topics-db-multi-db>`, you can use the +If you are using :doc:`multiple databases</topics/db/multi-db>`, you can use the same interface on each member of the ``connections`` dictionary:: >>> from django.db import connections @@ -39,7 +37,7 @@ same interface on each member of the ``connections`` dictionary:: Can I use Django with a pre-existing database? ---------------------------------------------- -Yes. See :ref:`Integrating with a legacy database <howto-legacy-databases>`. +Yes. See :doc:`Integrating with a legacy database </howto/legacy-databases>`. If I make changes to a model, how do I update the database? ----------------------------------------------------------- diff --git a/docs/faq/usage.txt b/docs/faq/usage.txt index 6c3c518bb2..856b97c35c 100644 --- a/docs/faq/usage.txt +++ b/docs/faq/usage.txt @@ -1,5 +1,3 @@ -.. _faq-usage: - FAQ: Using Django ================= diff --git a/docs/glossary.txt b/docs/glossary.txt index 67a62ca31a..b8f7a6b904 100644 --- a/docs/glossary.txt +++ b/docs/glossary.txt @@ -9,19 +9,19 @@ Glossary field An attribute on a :term:`model`; a given field usually maps directly to a single database column. - - See :ref:`topics-db-models`. + + See :doc:`/topics/db/models`. generic view A higher-order :term:`view` function that provides an abstract/generic implementation of a common idiom or pattern found in view development. - - See :ref:`ref-generic-views`. + + See :doc:`/ref/generic-views`. model Models store your application's data. - - See :ref:`topics-db-models`. + + See :doc:`/topics/db/models`. MTV See :ref:`mtv`. @@ -41,7 +41,7 @@ Glossary property Also known as "managed attributes", and a feature of Python since version 2.2. From `the property documentation`__: - + Properties are a neat way to implement attributes whose usage resembles attribute access, but whose implementation uses method calls. [...] You @@ -56,26 +56,26 @@ Glossary queryset An object representing some set of rows to be fetched from the database. - - See :ref:`topics-db-queries`. + + See :doc:`/topics/db/queries`. slug A short label for something, containing only letters, numbers, underscores or hyphens. They're generally used in URLs. For example, in a typical blog entry URL: - + .. parsed-literal:: - + http://www.djangoproject.com/weblog/2008/apr/12/**spring**/ - + the last bit (``spring``) is the slug. template A chunk of text that acts as formatting for representing data. A template helps to abstract the presentation of data from the data itself. - - See :ref:`topics-templates`. - + + See :doc:`/topics/templates`. + view - A function responsible for rending a page.
\ No newline at end of file + A function responsible for rending a page. diff --git a/docs/howto/apache-auth.txt b/docs/howto/apache-auth.txt index 8fd3da2612..b3723f92c6 100644 --- a/docs/howto/apache-auth.txt +++ b/docs/howto/apache-auth.txt @@ -1,12 +1,17 @@ -.. _howto-apache-auth: - ========================================================= Authenticating against Django's user database from Apache ========================================================= +.. warning:: + + Support for mod_python has been deprecated within Django. At that + time, this method of authentication will no longer be provided by + Django. The community is welcome to offer its own alternate + solutions using WSGI middleware or other approaches. + Since keeping multiple authentication databases in sync is a common problem when dealing with Apache, you can configuring Apache to authenticate against Django's -:ref:`authentication system <topics-auth>` directly. For example, you +:doc:`authentication system </topics/auth>` directly. For example, you could: * Serve static/media files directly from Apache only to authenticated users. diff --git a/docs/howto/auth-remote-user.txt b/docs/howto/auth-remote-user.txt index f0e83c0ba5..9dbde29e5c 100644 --- a/docs/howto/auth-remote-user.txt +++ b/docs/howto/auth-remote-user.txt @@ -1,5 +1,3 @@ -.. _howto-auth-remote-user: - ==================================== Authentication using ``REMOTE_USER`` ==================================== diff --git a/docs/howto/custom-file-storage.txt b/docs/howto/custom-file-storage.txt index 5005feaa80..1b0f32fb3f 100644 --- a/docs/howto/custom-file-storage.txt +++ b/docs/howto/custom-file-storage.txt @@ -1,5 +1,3 @@ -.. _howto-custom-file-storage: - Writing a custom storage system =============================== @@ -37,7 +35,7 @@ You'll need to follow these steps: the ``path()`` method. Your custom storage system may override any of the storage methods explained in -:ref:`ref-files-storage`, but you **must** implement the following methods: +:doc:`/ref/files/storage`, but you **must** implement the following methods: * :meth:`Storage.delete` * :meth:`Storage.exists` @@ -63,7 +61,7 @@ backend storage system. Called by ``Storage.save()``. The ``name`` will already have gone through ``get_valid_name()`` and ``get_available_name()``, and the ``content`` will be a -``File`` object itself. +``File`` object itself. Should return the actual name of name of the file saved (usually the ``name`` passed in, but if the storage needs to change the file name return the new name diff --git a/docs/howto/custom-management-commands.txt b/docs/howto/custom-management-commands.txt index f8b173cafa..7b25d6bdbe 100644 --- a/docs/howto/custom-management-commands.txt +++ b/docs/howto/custom-management-commands.txt @@ -1,5 +1,3 @@ -.. _howto-custom-management-commands: - ==================================== Writing custom django-admin commands ==================================== @@ -8,9 +6,9 @@ Writing custom django-admin commands Applications can register their own actions with ``manage.py``. For example, you might want to add a ``manage.py`` action for a Django app that you're -distributing. In this document, we will be building a custom ``closepoll`` +distributing. In this document, we will be building a custom ``closepoll`` command for the ``polls`` application from the -:ref:`tutorial<intro-tutorial01>`. +:doc:`tutorial</intro/tutorial01>`. To do this, just add a ``management/commands`` directory to the application. Each Python module in that directory will be auto-discovered and registered as @@ -62,15 +60,22 @@ look like this: poll.opened = False poll.save() - print 'Successfully closed poll "%s"' % poll_id + self.stdout.write('Successfully closed poll "%s"\n' % poll_id) + +.. note:: + When you are using management commands and wish to provide console + output, you should write to ``self.stdout`` and ``self.stderr``, + instead of printing to ``stdout`` and ``stderr`` directly. By + using these proxies, it becomes much easier to test your custom + command. -The new custom command can be called using ``python manage.py closepoll +The new custom command can be called using ``python manage.py closepoll <poll_id>``. The ``handle()`` method takes zero or more ``poll_ids`` and sets ``poll.opened`` to ``False`` for each one. If the user referenced any nonexistant polls, a :class:`CommandError` is raised. The ``poll.opened`` attribute does not exist -in the :ref:`tutorial<intro-tutorial01>` and was added to +in the :doc:`tutorial</intro/tutorial01>` and was added to ``polls.models.Poll`` for this example. The same ``closepoll`` could be easily modified to delete a given poll instead @@ -91,8 +96,8 @@ must be added to :attr:`~BaseCommand.option_list` like this: ) # ... -In addition to being able to add custom command line options, all -:ref:`management commands<ref-django-admin>` can accept some +In addition to being able to add custom command line options, all +:doc:`management commands</ref/django-admin>` can accept some default options such as :djadminopt:`--verbosity` and :djadminopt:`--traceback`. Command objects @@ -113,7 +118,7 @@ Subclassing the :class:`BaseCommand` class requires that you implement the Attributes ---------- -All attributes can be set in your derived class and can be used in +All attributes can be set in your derived class and can be used in :class:`BaseCommand`'s :ref:`subclasses<ref-basecommand-subclasses>`. .. attribute:: BaseCommand.args @@ -133,7 +138,7 @@ All attributes can be set in your derived class and can be used in .. attribute:: BaseCommand.help A short description of the command, which will be printed in the - help message when the user runs the command + help message when the user runs the command ``python manage.py help <command>``. .. attribute:: BaseCommand.option_list @@ -230,7 +235,7 @@ Rather than implementing :meth:`~BaseCommand.handle`, subclasses must implement A command which takes no arguments on the command line. Rather than implementing :meth:`~BaseCommand.handle`, subclasses must implement -:meth:`~NoArgsCommand.handle_noargs`; :meth:`~BaseCommand.handle` itself is +:meth:`~NoArgsCommand.handle_noargs`; :meth:`~BaseCommand.handle` itself is overridden to ensure no arguments are passed to the command. .. method:: NoArgsCommand.handle_noargs(**options) diff --git a/docs/howto/custom-model-fields.txt b/docs/howto/custom-model-fields.txt index 90851459c1..fa4c07fed2 100644 --- a/docs/howto/custom-model-fields.txt +++ b/docs/howto/custom-model-fields.txt @@ -1,5 +1,3 @@ -.. _howto-custom-model-fields: - =========================== Writing custom model fields =========================== @@ -10,7 +8,7 @@ Writing custom model fields Introduction ============ -The :ref:`model reference <topics-db-models>` documentation explains how to use +The :doc:`model reference </topics/db/models>` documentation explains how to use Django's standard field classes -- :class:`~django.db.models.CharField`, :class:`~django.db.models.DateField`, etc. For many purposes, those classes are all you'll need. Sometimes, though, the Django version won't meet your precise @@ -109,7 +107,7 @@ What does a field class do? --------------------------- All of Django's fields (and when we say *fields* in this document, we always -mean model fields and not :ref:`form fields <ref-forms-fields>`) are subclasses +mean model fields and not :doc:`form fields </ref/forms/fields>`) are subclasses of :class:`django.db.models.Field`. Most of the information that Django records about a field is common to all fields -- name, help text, uniqueness and so forth. Storing all that information is handled by ``Field``. We'll get into the @@ -124,7 +122,7 @@ when the model class is created (the precise details of how this is done are unimportant here). This is because the field classes aren't necessary when you're just creating and modifying attributes. Instead, they provide the machinery for converting between the attribute value and what is stored in the -database or sent to the :ref:`serializer <topics-serialization>`. +database or sent to the :doc:`serializer </topics/serialization>`. Keep this in mind when creating your own custom fields. The Django ``Field`` subclass you write provides the machinery for converting between your Python @@ -209,8 +207,8 @@ parameters: * :attr:`~django.db.models.Field.default` * :attr:`~django.db.models.Field.editable` * :attr:`~django.db.models.Field.serialize`: If ``False``, the field will - not be serialized when the model is passed to Django's :ref:`serializers - <topics-serialization>`. Defaults to ``True``. + not be serialized when the model is passed to Django's :doc:`serializers + </topics/serialization>`. Defaults to ``True``. * :attr:`~django.db.models.Field.unique_for_date` * :attr:`~django.db.models.Field.unique_for_month` * :attr:`~django.db.models.Field.unique_for_year` @@ -225,8 +223,8 @@ parameters: inheritance. For advanced use only. All of the options without an explanation in the above list have the same -meaning they do for normal Django fields. See the :ref:`field documentation -<ref-models-fields>` for examples and details. +meaning they do for normal Django fields. See the :doc:`field documentation +</ref/models/fields>` for examples and details. The ``SubfieldBase`` metaclass ------------------------------ @@ -270,8 +268,8 @@ value. This means that whenever a value may be assigned to the field, you need to ensure that it will be of the correct datatype, or that you handle any exceptions. -This is especially important if you use :ref:`ModelForms -<topics-forms-modelforms>`. When saving a ModelForm, Django will use +This is especially important if you use :doc:`ModelForms +</topics/forms/modelforms>`. When saving a ModelForm, Django will use form values to instantiate model instances. However, if the cleaned form data can't be used as valid input to the field, the normal form validation process will break. @@ -611,8 +609,8 @@ All of the ``kwargs`` dictionary is passed directly to the form field's :meth:`~django.forms.Field__init__` method. Normally, all you need to do is set up a good default for the ``form_class`` argument and then delegate further handling to the parent class. This might require you to write a custom form -field (and even a form widget). See the :ref:`forms documentation -<topics-forms-index>` for information about this, and take a look at the code in +field (and even a form widget). See the :doc:`forms documentation +</topics/forms/index>` for information about this, and take a look at the code in :mod:`django.contrib.localflavor` for some examples of custom widgets. Continuing our ongoing example, we can write the :meth:`formfield` method as:: @@ -721,7 +719,7 @@ Django provides a ``File`` class, which is used as a proxy to the file's contents and operations. This can be subclassed to customize how the file is accessed, and what methods are available. It lives at ``django.db.models.fields.files``, and its default behavior is explained in the -:ref:`file documentation <ref-files-file>`. +:doc:`file documentation </ref/files/file>`. Once a subclass of ``File`` is created, the new ``FileField`` subclass must be told to use it. To do so, simply assign the new ``File`` subclass to the special diff --git a/docs/howto/custom-template-tags.txt b/docs/howto/custom-template-tags.txt index 1406d19b58..c4d2315bd4 100644 --- a/docs/howto/custom-template-tags.txt +++ b/docs/howto/custom-template-tags.txt @@ -1,5 +1,3 @@ -.. _howto-custom-template-tags: - ================================ Custom template tags and filters ================================ @@ -7,8 +5,8 @@ Custom template tags and filters Introduction ============ -Django's template system comes with a wide variety of :ref:`built-in -tags and filters <ref-templates-builtins>` designed to address the +Django's template system comes with a wide variety of :doc:`built-in +tags and filters </ref/templates/builtins>` designed to address the presentation logic needs of your application. Nevertheless, you may find yourself needing functionality that is not covered by the core set of template primitives. You can extend the template engine by diff --git a/docs/howto/deployment/fastcgi.txt b/docs/howto/deployment/fastcgi.txt index cf05174390..c2b8aa8b53 100644 --- a/docs/howto/deployment/fastcgi.txt +++ b/docs/howto/deployment/fastcgi.txt @@ -1,13 +1,11 @@ -.. _howto-deployment-fastcgi: - ============================================ How to use Django with FastCGI, SCGI, or AJP ============================================ .. highlight:: bash -Although the current preferred setup for running Django is :ref:`Apache with -mod_wsgi <howto-deployment-modwsgi>`, many people use shared hosting, on +Although the current preferred setup for running Django is :doc:`Apache with +mod_wsgi </howto/deployment/modwsgi>`, many people use shared hosting, on which protocols such as FastCGI, SCGI or AJP are the only viable options. In some setups, these protocols may provide better performance than mod_wsgi_. @@ -22,14 +20,14 @@ serve pages to a Web server. The Web server delegates the incoming Web requests (via a socket) to FastCGI, which executes the code and passes the response back to the Web server, which, in turn, passes it back to the client's Web browser. -Like mod_python, FastCGI allows code to stay in memory, allowing requests to be -served with no startup time. Unlike mod_python_ (or `mod_perl`_), a FastCGI -process doesn't run inside the Web server process, but in a separate, +Like mod_wsgi, FastCGI allows code to stay in memory, allowing requests to be +served with no startup time. While mod_wsgi can either be configured embedded +in the Apache webserver process or as a separate daemon process, a FastCGI +process never runs inside the Web server process, always in a separate, persistent process. .. _mod_wsgi: http://code.google.com/p/modwsgi/ .. _mod_perl: http://perl.apache.org/ -.. _mod_python: http://www.modpython.org/ .. admonition:: Why run code in a separate process? @@ -37,8 +35,7 @@ persistent process. languages (most notably PHP, Python and Perl) inside the process space of your Web server. Although this lowers startup time -- because code doesn't have to be read off disk for every request -- it comes at the cost of - memory use. For mod_python, for example, every Apache process gets its own - Python interpreter, which uses up a considerable amount of RAM. + memory use. Due to the nature of FastCGI, it's even possible to have processes that run under a different user account than the Web server process. That's a nice @@ -74,7 +71,7 @@ TCP socket. What you choose is a manner of preference; a TCP socket is usually easier due to permissions issues. To start your server, first change into the directory of your project (wherever -your :ref:`manage.py <ref-django-admin>` is), and then run the +your :doc:`manage.py </ref/django-admin>` is), and then run the :djadmin:`runfcgi` command:: ./manage.py runfcgi [options] @@ -363,7 +360,7 @@ Serving admin media files Regardless of the server and configuration you eventually decide to use, you will also need to give some thought to how to serve the admin media files. The -advice given in the :ref:`modpython <serving-the-admin-files>` documentation +advice given in the :ref:`mod_wsgi <serving-the-admin-files>` documentation is also applicable in the setups detailed above. Forcing the URL prefix to a particular value diff --git a/docs/howto/deployment/index.txt b/docs/howto/deployment/index.txt index 78cfb037f5..2eff3e6ace 100644 --- a/docs/howto/deployment/index.txt +++ b/docs/howto/deployment/index.txt @@ -1,5 +1,3 @@ -.. _howto-deployment-index: - Deploying Django ================ @@ -10,18 +8,18 @@ ways to easily deploy Django: .. toctree:: :maxdepth: 1 - + modwsgi - modpython fastcgi - + mod_python (deprecated) <modpython> + If you're new to deploying Django and/or Python, we'd recommend you try -:ref:`mod_wsgi <howto-deployment-modwsgi>` first. In most cases it'll be the easiest, +:doc:`mod_wsgi </howto/deployment/modwsgi>` first. In most cases it'll be the easiest, fastest, and most stable deployment choice. .. seealso:: * `Chapter 12 of The Django Book`_ discusses deployment and especially scaling in more detail. - + .. _chapter 12 of the django book: http://djangobook.com/en/2.0/chapter12/ diff --git a/docs/howto/deployment/modpython.txt b/docs/howto/deployment/modpython.txt index 143a6d5ae3..a9b665f331 100644 --- a/docs/howto/deployment/modpython.txt +++ b/docs/howto/deployment/modpython.txt @@ -4,11 +4,18 @@ How to use Django with Apache and mod_python ============================================ +.. warning:: + + Support for mod_python will be deprecated in a future release of Django. If + you are configuring a new deployment, you are strongly encouraged to + consider using :doc:`mod_wsgi </howto/deployment/modwsgi>` or any of the + other :doc:`supported backends </howto/deployment/index>`. + .. highlight:: apache The `mod_python`_ module for Apache_ can be used to deploy Django to a production server, although it has been mostly superseded by the simpler -:ref:`mod_wsgi deployment option <howto-deployment-modwsgi>`. +:doc:`mod_wsgi deployment option </howto/deployment/modwsgi>`. mod_python is similar to (and inspired by) `mod_perl`_ : It embeds Python within Apache and loads Python code into memory when the server starts. Code stays in @@ -25,8 +32,8 @@ Django requires Apache 2.x and mod_python 3.x, and you should use Apache's Apache, there's no better source than `Apache's own official documentation`_ - * You may also be interested in :ref:`How to use Django with FastCGI, SCGI, - or AJP <howto-deployment-fastcgi>`. + * You may also be interested in :doc:`How to use Django with FastCGI, SCGI, + or AJP </howto/deployment/fastcgi>`. .. _Apache: http://httpd.apache.org/ .. _mod_python: http://www.modpython.org/ @@ -216,8 +223,6 @@ Or add the debugging information to the template of your page. .. _mod_python documentation: http://modpython.org/live/current/doc-html/directives.html -.. _serving-media-files: - Serving media files =================== @@ -269,10 +274,6 @@ the ``media`` subdirectory and any URL that ends with ``.jpg``, ``.gif`` or .. _Apache: http://httpd.apache.org/ .. _Cherokee: http://www.cherokee-project.com/ -.. _howto-deployment-modpython-serving-the-admin-files: - -.. _serving-the-admin-files: - Serving the admin files ======================= @@ -383,7 +384,7 @@ If you get a UnicodeEncodeError =============================== If you're taking advantage of the internationalization features of Django (see -:ref:`topics-i18n`) and you intend to allow users to upload files, you must +:doc:`/topics/i18n/index`) and you intend to allow users to upload files, you must ensure that the environment used to start Apache is configured to accept non-ASCII file names. If your environment is not correctly configured, you will trigger ``UnicodeEncodeError`` exceptions when calling functions like diff --git a/docs/howto/deployment/modwsgi.txt b/docs/howto/deployment/modwsgi.txt index 12de53f53d..e873006af0 100644 --- a/docs/howto/deployment/modwsgi.txt +++ b/docs/howto/deployment/modwsgi.txt @@ -1,5 +1,3 @@ -.. _howto-deployment-modwsgi: - ========================================== How to use Django with Apache and mod_wsgi ========================================== @@ -55,6 +53,8 @@ just above the final ``import`` line to place your project on the path. Remember replace 'mysite.settings' with your correct settings file, and '/usr/local/django' with your own project's location. +.. _serving-media-files: + Serving media files =================== @@ -108,6 +108,29 @@ in the mod_wsgi documentation on `hosting static files`_. .. _hosting static files: http://code.google.com/p/modwsgi/wiki/ConfigurationGuidelines#Hosting_Of_Static_Files +.. _serving-the-admin-files: + +Serving the admin files +======================= + +Note that the Django development server automagically serves admin media files, +but this is not the case when you use any other server arrangement. You're +responsible for setting up Apache, or whichever media server you're using, to +serve the admin files. + +The admin files live in (:file:`django/contrib/admin/media`) of the Django +distribution. + +Here are two recommended approaches: + + 1. Create a symbolic link to the admin media files from within your + document root. This way, all of your Django-related files -- code **and** + templates -- stay in one place, and you'll still be able to ``svn + update`` your code to get the latest admin templates, if they change. + + 2. Or, copy the admin media files so that they live within your Apache + document root. + Details ======= diff --git a/docs/howto/error-reporting.txt b/docs/howto/error-reporting.txt index 97842d7263..1ec009dd2a 100644 --- a/docs/howto/error-reporting.txt +++ b/docs/howto/error-reporting.txt @@ -1,5 +1,3 @@ -.. _howto-error-reporting: - Error reporting via e-mail ========================== @@ -30,8 +28,8 @@ the HTTP request that caused the error. to specify :setting:`EMAIL_HOST` and possibly :setting:`EMAIL_HOST_USER` and :setting:`EMAIL_HOST_PASSWORD`, though other settings may be also required depending on your mail - server's configuration. Consult :ref:`the Django settings - documentation <ref-settings>` for a full list of email-related + server's configuration. Consult :doc:`the Django settings + documentation </ref/settings>` for a full list of email-related settings. By default, Django will send email from root@localhost. However, some mail diff --git a/docs/howto/i18n.txt b/docs/howto/i18n.txt index 853162aa70..6bec531177 100644 --- a/docs/howto/i18n.txt +++ b/docs/howto/i18n.txt @@ -1,5 +1,3 @@ -.. _howto-i18n: - .. _using-translations-in-your-own-projects: =============================================== @@ -46,7 +44,7 @@ To create message files, you use the :djadmin:`django-admin.py makemessages <mak tool. You only need to be in the same directory where the ``locale/`` directory is located. And you use :djadmin:`django-admin.py compilemessages <compilemessages>` to produce the binary ``.mo`` files that are used by ``gettext``. Read the -:ref:`topics-i18n-localization` document for more details. +:doc:`/topics/i18n/localization` document for more details. You can also run ``django-admin.py compilemessages --settings=path.to.settings`` to make the compiler process all the directories in your :setting:`LOCALE_PATHS` diff --git a/docs/howto/index.txt b/docs/howto/index.txt index c582c8ed17..49d0644034 100644 --- a/docs/howto/index.txt +++ b/docs/howto/index.txt @@ -1,11 +1,9 @@ -.. _howto-index: - "How-to" guides =============== Here you'll find short answers to "How do I....?" types of questions. These how-to guides don't cover topics in depth -- you'll find that material in the -:ref:`topics-index` and the :ref:`ref-index`. However, these guides will help +:doc:`/topics/index` and the :doc:`/ref/index`. However, these guides will help you quickly accomplish common tasks. .. toctree:: diff --git a/docs/howto/initial-data.txt b/docs/howto/initial-data.txt index b071d6d529..cf3f65d299 100644 --- a/docs/howto/initial-data.txt +++ b/docs/howto/initial-data.txt @@ -1,5 +1,3 @@ -.. _howto-initial-data: - ================================= Providing initial data for models ================================= @@ -20,10 +18,10 @@ Providing initial data with fixtures A fixture is a collection of data that Django knows how to import into a database. The most straightforward way of creating a fixture if you've already -got some data is to use the :djadmin:`manage.py dumpdata` command. Or, you can -write fixtures by hand; fixtures can be written as XML, YAML, or JSON documents. -The :ref:`serialization documentation <topics-serialization>` has more details -about each of these supported :ref:`serialization formats +got some data is to use the :djadmin:`manage.py dumpdata <dumpdata>` command. +Or, you can write fixtures by hand; fixtures can be written as XML, YAML, or +JSON documents. The :doc:`serialization documentation </topics/serialization>` +has more details about each of these supported :ref:`serialization formats <serialization-formats>`. As an example, though, here's what a fixture for a simple ``Person`` model might @@ -114,9 +112,9 @@ which will insert the desired data (e.g., properly-formatted ``INSERT`` statements separated by semicolons). The SQL files are read by the :djadmin:`sqlcustom`, :djadmin:`sqlreset`, -:djadmin:`sqlall` and :djadmin:`reset` commands in :ref:`manage.py -<ref-django-admin>`. Refer to the :ref:`manage.py documentation -<ref-django-admin>` for more information. +:djadmin:`sqlall` and :djadmin:`reset` commands in :doc:`manage.py +</ref/django-admin>`. Refer to the :doc:`manage.py documentation +</ref/django-admin>` for more information. Note that if you have multiple SQL data files, there's no guarantee of the order in which they're executed. The only thing you can assume is diff --git a/docs/howto/jython.txt b/docs/howto/jython.txt index 385790e9e6..673c9360bd 100644 --- a/docs/howto/jython.txt +++ b/docs/howto/jython.txt @@ -1,5 +1,3 @@ -.. _howto-jython: - ======================== Running Django on Jython ======================== diff --git a/docs/howto/legacy-databases.txt b/docs/howto/legacy-databases.txt index b2aa7e4ea6..2121871fa2 100644 --- a/docs/howto/legacy-databases.txt +++ b/docs/howto/legacy-databases.txt @@ -1,5 +1,3 @@ -.. _howto-legacy-databases: - ========================================= Integrating Django with a legacy database ========================================= @@ -9,7 +7,7 @@ possible to integrate it into legacy databases. Django includes a couple of utilities to automate as much of this process as possible. This document assumes you know the Django basics, as covered in the -:ref:`tutorial <intro-tutorial01>`. +:doc:`tutorial </intro/tutorial01>`. Once you've got Django set up, you'll follow this general process to integrate with an existing database. diff --git a/docs/howto/outputting-csv.txt b/docs/howto/outputting-csv.txt index 234454c265..169114ff95 100644 --- a/docs/howto/outputting-csv.txt +++ b/docs/howto/outputting-csv.txt @@ -1,5 +1,3 @@ -.. _howto-outputting-csv: - ========================== Outputting CSV with Django ========================== @@ -61,7 +59,7 @@ mention: Using the template system ========================= -Alternatively, you can use the :ref:`Django template system <topics-templates>` +Alternatively, you can use the :doc:`Django template system </topics/templates>` to generate CSV. This is lower-level than using the convenient CSV, but the solution is presented here for completeness. @@ -113,4 +111,4 @@ Other text-based formats Notice that there isn't very much specific to CSV here -- just the specific output format. You can use either of these techniques to output any text-based format you can dream of. You can also use a similar technique to generate -arbitrary binary data; see :ref:`howto-outputting-pdf` for an example. +arbitrary binary data; see :doc:`/howto/outputting-pdf` for an example. diff --git a/docs/howto/outputting-pdf.txt b/docs/howto/outputting-pdf.txt index 94acab8311..32e38aebc6 100644 --- a/docs/howto/outputting-pdf.txt +++ b/docs/howto/outputting-pdf.txt @@ -1,5 +1,3 @@ -.. _howto-outputting-pdf: - =========================== Outputting PDFs with Django =========================== @@ -154,5 +152,5 @@ Other formats Notice that there isn't a lot in these examples that's PDF-specific -- just the bits using ``reportlab``. You can use a similar technique to generate any arbitrary format that you can find a Python library for. Also see -:ref:`howto-outputting-csv` for another example and some techniques you can use +:doc:`/howto/outputting-csv` for another example and some techniques you can use when generated text-based formats. diff --git a/docs/howto/static-files.txt b/docs/howto/static-files.txt index f93a4e9ba4..209cf38c36 100644 --- a/docs/howto/static-files.txt +++ b/docs/howto/static-files.txt @@ -1,5 +1,3 @@ -.. _howto-static-files: - ========================= How to serve static files ========================= @@ -33,7 +31,7 @@ Using this method is **inefficient** and **insecure**. Do not use this in a production setting. Use this only for development. For information on serving static files in an Apache production environment, -see the :ref:`Django mod_python documentation <serving-media-files>`. +see the :ref:`Django mod_wsgi documentation <serving-media-files>`. How to do it ============ @@ -42,7 +40,7 @@ Here's the formal definition of the :func:`~django.views.static.serve` view: .. function:: def serve(request, path, document_root, show_indexes=False) -To use it, just put this in your :ref:`URLconf <topics-http-urls>`:: +To use it, just put this in your :doc:`URLconf </topics/http/urls>`:: (r'^site_media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': '/path/to/media'}), @@ -71,7 +69,7 @@ required. For example, if we have a line in ``settings.py`` that says:: STATIC_DOC_ROOT = '/path/to/media' -...we could write the above :ref:`URLconf <topics-http-urls>` entry as:: +...we could write the above :doc:`URLconf </topics/http/urls>` entry as:: from django.conf import settings ... diff --git a/docs/index.txt b/docs/index.txt index aae2e27cb6..c031b03f54 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -12,10 +12,10 @@ Getting help Having trouble? We'd like to help! -* Try the :ref:`FAQ <faq-index>` -- it's got answers to many common questions. +* Try the :doc:`FAQ <faq/index>` -- it's got answers to many common questions. * Looking for specific information? Try the :ref:`genindex`, :ref:`modindex` or - the :ref:`detailed table of contents <contents>`. + the :doc:`detailed table of contents <contents>`. * Search for information in the `archives of the django-users mailing list`_, or `post a question`_. @@ -35,179 +35,179 @@ First steps =========== * **From scratch:** - :ref:`Overview <intro-overview>` | - :ref:`Installation <intro-install>` + :doc:`Overview <intro/overview>` | + :doc:`Installation <intro/install>` * **Tutorial:** - :ref:`Part 1 <intro-tutorial01>` | - :ref:`Part 2 <intro-tutorial02>` | - :ref:`Part 3 <intro-tutorial03>` | - :ref:`Part 4 <intro-tutorial04>` + :doc:`Part 1 <intro/tutorial01>` | + :doc:`Part 2 <intro/tutorial02>` | + :doc:`Part 3 <intro/tutorial03>` | + :doc:`Part 4 <intro/tutorial04>` The model layer =============== * **Models:** - :ref:`Model syntax <topics-db-models>` | - :ref:`Field types <ref-models-fields>` | - :ref:`Meta options <ref-models-options>` + :doc:`Model syntax <topics/db/models>` | + :doc:`Field types <ref/models/fields>` | + :doc:`Meta options <ref/models/options>` * **QuerySets:** - :ref:`Executing queries <topics-db-queries>` | - :ref:`QuerySet method reference <ref-models-querysets>` + :doc:`Executing queries <topics/db/queries>` | + :doc:`QuerySet method reference <ref/models/querysets>` * **Model instances:** - :ref:`Instance methods <ref-models-instances>` | - :ref:`Accessing related objects <ref-models-relations>` + :doc:`Instance methods <ref/models/instances>` | + :doc:`Accessing related objects <ref/models/relations>` * **Advanced:** - :ref:`Managers <topics-db-managers>` | - :ref:`Raw SQL <topics-db-sql>` | - :ref:`Transactions <topics-db-transactions>` | - :ref:`Aggregation <topics-db-aggregation>` | - :ref:`Custom fields <howto-custom-model-fields>` | - :ref:`Multiple databases <topics-db-multi-db>` + :doc:`Managers <topics/db/managers>` | + :doc:`Raw SQL <topics/db/sql>` | + :doc:`Transactions <topics/db/transactions>` | + :doc:`Aggregation <topics/db/aggregation>` | + :doc:`Custom fields <howto/custom-model-fields>` | + :doc:`Multiple databases <topics/db/multi-db>` * **Other:** - :ref:`Supported databases <ref-databases>` | - :ref:`Legacy databases <howto-legacy-databases>` | - :ref:`Providing initial data <howto-initial-data>` | - :ref:`Optimize database access <topics-db-optimization>` + :doc:`Supported databases <ref/databases>` | + :doc:`Legacy databases <howto/legacy-databases>` | + :doc:`Providing initial data <howto/initial-data>` | + :doc:`Optimize database access <topics/db/optimization>` The template layer ================== * **For designers:** - :ref:`Syntax overview <topics-templates>` | - :ref:`Built-in tags and filters <ref-templates-builtins>` + :doc:`Syntax overview <topics/templates>` | + :doc:`Built-in tags and filters <ref/templates/builtins>` * **For programmers:** - :ref:`Template API <ref-templates-api>` | - :ref:`Custom tags and filters <howto-custom-template-tags>` + :doc:`Template API <ref/templates/api>` | + :doc:`Custom tags and filters <howto/custom-template-tags>` The view layer ============== * **The basics:** - :ref:`URLconfs <topics-http-urls>` | - :ref:`View functions <topics-http-views>` | - :ref:`Shortcuts <topics-http-shortcuts>` + :doc:`URLconfs <topics/http/urls>` | + :doc:`View functions <topics/http/views>` | + :doc:`Shortcuts <topics/http/shortcuts>` - * **Reference:** :ref:`Request/response objects <ref-request-response>` + * **Reference:** :doc:`Request/response objects <ref/request-response>` * **File uploads:** - :ref:`Overview <topics-http-file-uploads>` | - :ref:`File objects <ref-files-file>` | - :ref:`Storage API <ref-files-storage>` | - :ref:`Managing files <topics-files>` | - :ref:`Custom storage <howto-custom-file-storage>` + :doc:`Overview <topics/http/file-uploads>` | + :doc:`File objects <ref/files/file>` | + :doc:`Storage API <ref/files/storage>` | + :doc:`Managing files <topics/files>` | + :doc:`Custom storage <howto/custom-file-storage>` * **Generic views:** - :ref:`Overview<topics-generic-views>` | - :ref:`Built-in generic views<ref-generic-views>` + :doc:`Overview<topics/generic-views>` | + :doc:`Built-in generic views<ref/generic-views>` * **Advanced:** - :ref:`Generating CSV <howto-outputting-csv>` | - :ref:`Generating PDF <howto-outputting-pdf>` + :doc:`Generating CSV <howto/outputting-csv>` | + :doc:`Generating PDF <howto/outputting-pdf>` * **Middleware:** - :ref:`Overview <topics-http-middleware>` | - :ref:`Built-in middleware classes <ref-middleware>` + :doc:`Overview <topics/http/middleware>` | + :doc:`Built-in middleware classes <ref/middleware>` Forms ===== * **The basics:** - :ref:`Overview <topics-forms-index>` | - :ref:`Form API <ref-forms-api>` | - :ref:`Built-in fields <ref-forms-fields>` | - :ref:`Built-in widgets <ref-forms-widgets>` + :doc:`Overview <topics/forms/index>` | + :doc:`Form API <ref/forms/api>` | + :doc:`Built-in fields <ref/forms/fields>` | + :doc:`Built-in widgets <ref/forms/widgets>` * **Advanced:** - :ref:`Forms for models <topics-forms-modelforms>` | - :ref:`Integrating media <topics-forms-media>` | - :ref:`Formsets <topics-forms-formsets>` | - :ref:`Customizing validation <ref-forms-validation>` + :doc:`Forms for models <topics/forms/modelforms>` | + :doc:`Integrating media <topics/forms/media>` | + :doc:`Formsets <topics/forms/formsets>` | + :doc:`Customizing validation <ref/forms/validation>` * **Extras:** - :ref:`Form preview <ref-contrib-formtools-form-preview>` | - :ref:`Form wizard <ref-contrib-formtools-form-wizard>` + :doc:`Form preview <ref/contrib/formtools/form-preview>` | + :doc:`Form wizard <ref/contrib/formtools/form-wizard>` The development process ======================= * **Settings:** - :ref:`Overview <topics-settings>` | - :ref:`Full list of settings <ref-settings>` + :doc:`Overview <topics/settings>` | + :doc:`Full list of settings <ref/settings>` * **Exceptions:** - :ref:`Overview <ref-exceptions>` + :doc:`Overview <ref/exceptions>` * **django-admin.py and manage.py:** - :ref:`Overview <ref-django-admin>` | - :ref:`Adding custom commands <howto-custom-management-commands>` + :doc:`Overview <ref/django-admin>` | + :doc:`Adding custom commands <howto/custom-management-commands>` - * **Testing:** :ref:`Overview <topics-testing>` + * **Testing:** :doc:`Overview <topics/testing>` * **Deployment:** - :ref:`Overview <howto-deployment-index>` | - :ref:`Apache/mod_wsgi <howto-deployment-modwsgi>` | - :ref:`Apache/mod_python <howto-deployment-modpython>` | - :ref:`FastCGI/SCGI/AJP <howto-deployment-fastcgi>` | - :ref:`Apache authentication <howto-apache-auth>` | - :ref:`Serving static files <howto-static-files>` | - :ref:`Tracking code errors by e-mail <howto-error-reporting>` + :doc:`Overview <howto/deployment/index>` | + :doc:`Apache/mod_wsgi <howto/deployment/modwsgi>` | + :doc:`Apache/mod_python <howto/deployment/modpython>` | + :doc:`FastCGI/SCGI/AJP <howto/deployment/fastcgi>` | + :doc:`Apache authentication <howto/apache-auth>` | + :doc:`Serving static files <howto/static-files>` | + :doc:`Tracking code errors by e-mail <howto/error-reporting>` Other batteries included ======================== - * :ref:`Admin site <ref-contrib-admin>` | :ref:`Admin actions <ref-contrib-admin-actions>` - * :ref:`Authentication <topics-auth>` - * :ref:`Cache system <topics-cache>` - * :ref:`Conditional content processing <topics-conditional-processing>` - * :ref:`Comments <ref-contrib-comments-index>` | :ref:`Moderation <ref-contrib-comments-moderation>` | :ref:`Custom comments <ref-contrib-comments-custom>` - * :ref:`Content types <ref-contrib-contenttypes>` - * :ref:`Cross Site Request Forgery protection <ref-contrib-csrf>` - * :ref:`Databrowse <ref-contrib-databrowse>` - * :ref:`E-mail (sending) <topics-email>` - * :ref:`Flatpages <ref-contrib-flatpages>` - * :ref:`GeoDjango <ref-contrib-gis>` - * :ref:`Humanize <ref-contrib-humanize>` - * :ref:`Internationalization <topics-i18n>` - * :ref:`Jython support <howto-jython>` - * :ref:`"Local flavor" <ref-contrib-localflavor>` - * :ref:`Messages <ref-contrib-messages>` - * :ref:`Pagination <topics-pagination>` - * :ref:`Redirects <ref-contrib-redirects>` - * :ref:`Serialization <topics-serialization>` - * :ref:`Sessions <topics-http-sessions>` - * :ref:`Signals <topics-signals>` - * :ref:`Sitemaps <ref-contrib-sitemaps>` - * :ref:`Sites <ref-contrib-sites>` - * :ref:`Syndication feeds (RSS/Atom) <ref-contrib-syndication>` - * :ref:`Unicode in Django <ref-unicode>` - * :ref:`Web design helpers <ref-contrib-webdesign>` - * :ref:`Validators <ref-validators>` + * :doc:`Admin site <ref/contrib/admin/index>` | :doc:`Admin actions <ref/contrib/admin/actions>` + * :doc:`Authentication <topics/auth>` + * :doc:`Cache system <topics/cache>` + * :doc:`Conditional content processing <topics/conditional-view-processing>` + * :doc:`Comments <ref/contrib/comments/index>` | :doc:`Moderation <ref/contrib/comments/moderation>` | :doc:`Custom comments <ref/contrib/comments/custom>` + * :doc:`Content types <ref/contrib/contenttypes>` + * :doc:`Cross Site Request Forgery protection <ref/contrib/csrf>` + * :doc:`Databrowse <ref/contrib/databrowse>` + * :doc:`E-mail (sending) <topics/email>` + * :doc:`Flatpages <ref/contrib/flatpages>` + * :doc:`GeoDjango <ref/contrib/gis/index>` + * :doc:`Humanize <ref/contrib/humanize>` + * :doc:`Internationalization <topics/i18n/index>` + * :doc:`Jython support <howto/jython>` + * :doc:`"Local flavor" <ref/contrib/localflavor>` + * :doc:`Messages <ref/contrib/messages>` + * :doc:`Pagination <topics/pagination>` + * :doc:`Redirects <ref/contrib/redirects>` + * :doc:`Serialization <topics/serialization>` + * :doc:`Sessions <topics/http/sessions>` + * :doc:`Signals <topics/signals>` + * :doc:`Sitemaps <ref/contrib/sitemaps>` + * :doc:`Sites <ref/contrib/sites>` + * :doc:`Syndication feeds (RSS/Atom) <ref/contrib/syndication>` + * :doc:`Unicode in Django <ref/unicode>` + * :doc:`Web design helpers <ref/contrib/webdesign>` + * :doc:`Validators <ref/validators>` The Django open-source project ============================== * **Community:** - :ref:`How to get involved <internals-contributing>` | - :ref:`The release process <internals-release-process>` | - :ref:`Team of committers <internals-committers>` | - :ref:`The Django source code repository <internals-svn>` + :doc:`How to get involved <internals/contributing>` | + :doc:`The release process <internals/release-process>` | + :doc:`Team of committers <internals/committers>` | + :doc:`The Django source code repository <internals/svn>` * **Design philosophies:** - :ref:`Overview <misc-design-philosophies>` + :doc:`Overview <misc/design-philosophies>` * **Documentation:** - :ref:`About this documentation <internals-documentation>` + :doc:`About this documentation <internals/documentation>` * **Third-party distributions:** - :ref:`Overview <misc-distributions>` + :doc:`Overview <misc/distributions>` * **Django over time:** - :ref:`API stability <misc-api-stability>` | - :ref:`Release notes and upgrading instructions <releases-index>` | - :ref:`Deprecation Timeline <internals-deprecation>` + :doc:`API stability <misc/api-stability>` | + :doc:`Release notes and upgrading instructions <releases/index>` | + :doc:`Deprecation Timeline <internals/deprecation>` diff --git a/docs/internals/committers.txt b/docs/internals/committers.txt index d2eb80c710..b0bb18b955 100644 --- a/docs/internals/committers.txt +++ b/docs/internals/committers.txt @@ -1,5 +1,3 @@ -.. _internals-committers: - ================= Django committers ================= diff --git a/docs/internals/contributing.txt b/docs/internals/contributing.txt index c555f205b1..399e458c2a 100644 --- a/docs/internals/contributing.txt +++ b/docs/internals/contributing.txt @@ -1,5 +1,3 @@ -.. _internals-contributing: - ====================== Contributing to Django ====================== @@ -42,7 +40,7 @@ amount of overhead involved in working with any bug tracking system, so your help in keeping our ticket tracker as useful as possible is appreciated. In particular: - * **Do** read the :ref:`FAQ <faq-index>` to see if your issue might be a well-known question. + * **Do** read the :doc:`FAQ </faq/index>` to see if your issue might be a well-known question. * **Do** `search the tracker`_ to see if your issue has already been filed. @@ -145,7 +143,11 @@ and time availability), claim it by following these steps: * Claim the ticket by clicking the radio button next to "Accept ticket" near the bottom of the page, then clicking "Submit changes." +If you have an account but have forgotten your password, you can reset it +using the `password reset page`_. + .. _Create an account: http://www.djangoproject.com/accounts/register/ +.. _password reset page: http://www.djangoproject.com/accounts/password/reset/ Ticket claimers' responsibility ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -394,7 +396,7 @@ Various parts of Django, such as the admin site and validation error messages, are internationalized. This means they display different text depending on a user's language setting. For this, Django uses the same internationalization infrastructure available to Django applications described in the -:ref:`i18n documentation<topics-i18n>`. +:doc:`i18n documentation</topics/i18n/index>`. These translations are contributed by Django users worldwide. If you find an incorrect translation, or if you'd like to add a language that isn't yet @@ -405,7 +407,7 @@ translated, here's what to do: * Make sure you read the notes about :ref:`specialties-of-django-i18n`. * Create translations using the methods described in the - :ref:`localization documentation <topics-i18n-localization>`. For this + :doc:`localization documentation </topics/i18n/localization>`. For this you will use the ``django-admin.py makemessages`` tool. In this particular case it should be run from the top-level ``django`` directory of the Django source tree. @@ -531,8 +533,8 @@ Please follow these coding standards when writing code for inclusion in Django: * Use ``InitialCaps`` for class names (or for factory functions that return classes). - * Mark all strings for internationalization; see the :ref:`i18n - documentation <topics-i18n>` for details. + * Mark all strings for internationalization; see the :doc:`i18n + documentation </topics/i18n/index>` for details. * In docstrings, use "action words" such as:: @@ -694,8 +696,8 @@ General improvements, or other changes to the APIs that should be emphasized should use the ".. versionchanged:: X.Y" directive (with the same format as the ``versionadded`` mentioned above. -There's a full page of information about the :ref:`Django documentation -system <internals-documentation>` that you should read prior to working on the +There's a full page of information about the :doc:`Django documentation +system </internals/documentation>` that you should read prior to working on the documentation. Guidelines for ReST files @@ -825,7 +827,7 @@ The tests cover: We appreciate any and all contributions to the test suite! The Django tests all use the testing infrastructure that ships with Django for -testing applications. See :ref:`Testing Django applications <topics-testing>` +testing applications. See :doc:`Testing Django applications </topics/testing>` for an explanation of how to write new tests. Running the unit tests @@ -1013,8 +1015,8 @@ for feature branches: public, please add the branch to the `Django branches`_ wiki page. 2. Feature branches using SVN have a higher bar. If you want a branch in SVN - itself, you'll need a "mentor" among the :ref:`core committers - <internals-committers>`. This person is responsible for actually creating + itself, you'll need a "mentor" among the :doc:`core committers + </internals/committers>`. This person is responsible for actually creating the branch, monitoring your process (see below), and ultimately merging the branch into trunk. diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index 8479a32bcf..b313871128 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -1,5 +1,3 @@ -.. _internals-deprecation: - =========================== Django Deprecation Timeline =========================== @@ -52,7 +50,7 @@ their deprecation, as per the :ref:`Django deprecation policy associated methods (``user.message_set.create()`` and ``user.get_and_delete_messages()``), which have been deprecated since the 1.2 release, will be removed. The - :ref:`messages framework <ref-contrib-messages>` should be used + :doc:`messages framework </ref/contrib/messages>` should be used instead. * Authentication backends need to support the ``obj`` parameter for @@ -100,6 +98,10 @@ their deprecation, as per the :ref:`Django deprecation policy * The ``no`` language code has been deprecated in favor of the ``nb`` language code. + * 1.5 + * The ``mod_python`` request handler has been deprecated since the 1.3 + release. The ``mod_wsgi`` handler should be used instead. + * 2.0 * ``django.views.defaults.shortcut()``. This function has been moved to ``django.contrib.contenttypes.views.shortcut()`` as part of the diff --git a/docs/internals/documentation.txt b/docs/internals/documentation.txt index 81480abf9a..63f248d3a9 100644 --- a/docs/internals/documentation.txt +++ b/docs/internals/documentation.txt @@ -1,5 +1,3 @@ -.. _internals-documentation: - How the Django documentation works ================================== @@ -15,6 +13,11 @@ __ http://docutils.sourceforge.net/ To actually build the documentation locally, you'll currently need to install Sphinx -- ``easy_install Sphinx`` should do the trick. +.. note:: + + Generation of the Django documentation will work with Sphinx version 0.6 + or newer, but we recommend going straigh to Sphinx 1.0.2 or newer. + Then, building the html is easy; just ``make html`` from the ``docs`` directory. To get started contributing, you'll want to read the `ReStructuredText @@ -83,27 +86,55 @@ __ http://sphinx.pocoo.org/markup/desc.html An example ---------- -For a quick example of how it all fits together, check this out: +For a quick example of how it all fits together, consider this hypothetical +example: - * First, the ``ref/settings.txt`` document starts out like this:: + * First, the ``ref/settings.txt`` document could have an overall layout + like this: - .. _ref-settings: + .. code-block:: rst - Available settings - ================== + ======== + Settings + ======== ... - * Next, if you look at the ``topics/settings.txt`` document, you can see how - a link to ``ref/settings`` works:: + .. _available-settings: Available settings ================== - For a full list of available settings, see the :ref:`settings reference - <ref-settings>`. + ... + + .. _deprecated-settings: + + Deprecated settings + =================== + + ... + + * Next, the ``topics/settings.txt`` document could contain something like + this: + + .. code-block:: rst + + You can access a :ref:`listing of all available settings + <available-settings>`. For a list of deprecated settings see + :ref:`deprecated-settings`. + + You can find both in the :doc:`settings reference document </ref/settings>`. + + We use the Sphinx doc_ cross reference element when we want to link to + another document as a whole and the ref_ element when we want to link to + an arbitrary location in a document. + +.. _doc: http://sphinx.pocoo.org/markup/inline.html#role-doc +.. _ref: http://sphinx.pocoo.org/markup/inline.html#role-ref + + * Next, notice how the settings are annotated: - * Next, notice how the settings (right now just the top few) are annotated:: + .. code-block:: rst .. setting:: ADMIN_FOR diff --git a/docs/internals/index.txt b/docs/internals/index.txt index 4f9007705e..26c941a096 100644 --- a/docs/internals/index.txt +++ b/docs/internals/index.txt @@ -1,5 +1,3 @@ -.. _internals-index: - Django internals ================ diff --git a/docs/internals/release-process.txt b/docs/internals/release-process.txt index 20bc365844..2a56f0be92 100644 --- a/docs/internals/release-process.txt +++ b/docs/internals/release-process.txt @@ -1,5 +1,3 @@ -.. _internals-release-process: - ======================== Django's release process ======================== diff --git a/docs/internals/svn.txt b/docs/internals/svn.txt index 372fbd1202..c66e494e5f 100644 --- a/docs/internals/svn.txt +++ b/docs/internals/svn.txt @@ -1,5 +1,3 @@ -.. _internals-svn: - ================================= The Django source code repository ================================= @@ -87,8 +85,8 @@ the ``django`` module within your checkout. If you're going to be working on Django's code (say, to fix a bug or develop a new feature), you can probably stop reading here and move -over to :ref:`the documentation for contributing to Django -<internals-contributing>`, which covers things like the preferred +over to :doc:`the documentation for contributing to Django +</internals/contributing>`, which covers things like the preferred coding style and how to generate and submit a patch. @@ -129,20 +127,20 @@ part of Django itself, and so are no longer separately maintained: object-relational mapper. This has been part of Django since the 1.0 release, as the bundled application ``django.contrib.gis``. -* ``i18n``: Added :ref:`internationalization support <topics-i18n>` to +* ``i18n``: Added :doc:`internationalization support </topics/i18n/index>` to Django. This has been part of Django since the 0.90 release. * ``magic-removal``: A major refactoring of both the internals and public APIs of Django's object-relational mapper. This has been part of Django since the 0.95 release. -* ``multi-auth``: A refactoring of :ref:`Django's bundled - authentication framework <topics-auth>` which added support for +* ``multi-auth``: A refactoring of :doc:`Django's bundled + authentication framework </topics/auth>` which added support for :ref:`authentication backends <authentication-backends>`. This has been part of Django since the 0.95 release. -* ``new-admin``: A refactoring of :ref:`Django's bundled - administrative application <ref-contrib-admin>`. This became part of +* ``new-admin``: A refactoring of :doc:`Django's bundled + administrative application </ref/contrib/admin/index>`. This became part of Django as of the 0.91 release, but was superseded by another refactoring (see next listing) prior to the Django 1.0 release. diff --git a/docs/intro/index.txt b/docs/intro/index.txt index 2135bc7fe9..90ee627ba6 100644 --- a/docs/intro/index.txt +++ b/docs/intro/index.txt @@ -1,5 +1,3 @@ -.. _intro-index: - Getting started =============== diff --git a/docs/intro/install.txt b/docs/intro/install.txt index 901bde01c2..95728c75fc 100644 --- a/docs/intro/install.txt +++ b/docs/intro/install.txt @@ -1,10 +1,8 @@ -.. _intro-install: - Quick install guide =================== Before you can use Django, you'll need to get it installed. We have a -:ref:`complete installation guide <topics-install>` that covers all the +:doc:`complete installation guide </topics/install>` that covers all the possibilities; this guide will guide you to a simple, minimal installation that'll work while you walk through the introduction. @@ -12,9 +10,9 @@ Install Python -------------- Being a Python Web framework, Django requires Python. It works with any Python -version from 2.4 to 2.6 (due to backwards +version from 2.4 to 2.7 (due to backwards incompatibilities in Python 3.0, Django does not currently work with -Python 3.0; see :ref:`the Django FAQ <faq-install>` for more +Python 3.0; see :doc:`the Django FAQ </faq/install>` for more information on supported Python versions and the 3.0 transition), but we recommend installing Python 2.5 or later. If you do so, you won't need to set up a database just yet: Python 2.5 or later includes a lightweight database called SQLite_. .. _sqlite: http://sqlite.org/ @@ -25,17 +23,17 @@ probably already have it installed. .. admonition:: Django on Jython If you use Jython_ (a Python implementation for the Java platform), you'll - need to follow a few additional steps. See :ref:`howto-jython` for details. + need to follow a few additional steps. See :doc:`/howto/jython` for details. .. _jython: http://www.jython.org/ You can verify that Python's installed by typing ``python`` from your shell; you should see something like:: - Python 2.5.1 (r251:54863, Jan 17 2008, 19:35:17) + Python 2.5.1 (r251:54863, Jan 17 2008, 19:35:17) [GCC 4.0.1 (Apple Inc. build 5465)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> - + Set up a database ----------------- @@ -57,18 +55,18 @@ Install Django You've got three easy options to install Django: - * Install a version of Django :ref:`provided by your operating system - distribution <misc-distributions>`. This is the quickest option for those + * Install a version of Django :doc:`provided by your operating system + distribution </misc/distributions>`. This is the quickest option for those who have operating systems that distribute Django. * :ref:`Install an official release <installing-official-release>`. This is the best approach for users who want a stable version number and aren't concerned about running a slightly older version of Django. - + * :ref:`Install the latest development version <installing-development-version>`. This is best for users who want the latest-and-greatest features and aren't afraid of running brand-new code. - + .. warning:: If you do either of the first two steps, keep an eye out for parts of the @@ -79,7 +77,7 @@ You've got three easy options to install Django: That's it! ---------- -That's it -- you can now :ref:`move onto the tutorial <intro-tutorial01>`. +That's it -- you can now :doc:`move onto the tutorial </intro/tutorial01>`. diff --git a/docs/intro/overview.txt b/docs/intro/overview.txt index 594c9fe582..0c47e59e14 100644 --- a/docs/intro/overview.txt +++ b/docs/intro/overview.txt @@ -1,5 +1,3 @@ -.. _intro-overview: - ================== Django at a glance ================== @@ -11,8 +9,8 @@ overview of how to write a database-driven Web app with Django. The goal of this document is to give you enough technical specifics to understand how Django works, but this isn't intended to be a tutorial or reference -- but we've got both! When you're ready to start a project, you can -:ref:`start with the tutorial <intro-tutorial01>` or :ref:`dive right into more -detailed documentation <topics-index>`. +:doc:`start with the tutorial </intro/tutorial01>` or :doc:`dive right into more +detailed documentation </topics/index>`. Design your model ================= @@ -21,7 +19,7 @@ Although you can use Django without a database, it comes with an object-relational mapper in which you describe your database layout in Python code. -The :ref:`data-model syntax <topics-db-models>` offers many rich ways of +The :doc:`data-model syntax </topics/db/models>` offers many rich ways of representing your models -- so far, it's been solving two years' worth of database-schema problems. Here's a quick example:: @@ -56,7 +54,7 @@ tables in your database for whichever tables don't already exist. Enjoy the free API ================== -With that, you've got a free, and rich, :ref:`Python API <topics-db-queries>` to +With that, you've got a free, and rich, :doc:`Python API </topics/db/queries>` to access your data. The API is created on the fly, no code generation necessary:: >>> from mysite.models import Reporter, Article @@ -131,7 +129,7 @@ A dynamic admin interface: it's not just scaffolding -- it's the whole house ============================================================================ Once your models are defined, Django can automatically create a professional, -production ready :ref:`administrative interface <ref-contrib-admin>` -- a Web +production ready :doc:`administrative interface </ref/contrib/admin/index>` -- a Web site that lets authenticated users add, change and delete objects. It's as easy as registering your model in the admin site:: @@ -168,8 +166,8 @@ A clean, elegant URL scheme is an important detail in a high-quality Web application. Django encourages beautiful URL design and doesn't put any cruft in URLs, like ``.php`` or ``.asp``. -To design URLs for an app, you create a Python module called a :ref:`URLconf -<topics-http-urls>`. A table of contents for your app, it contains a simple mapping +To design URLs for an app, you create a Python module called a :doc:`URLconf +</topics/http/urls>`. A table of contents for your app, it contains a simple mapping between URL patterns and Python callback functions. URLconfs also serve to decouple URLs from Python code. @@ -216,7 +214,7 @@ and renders the template with the retrieved data. Here's an example view for a_list = Article.objects.filter(pub_date__year=year) return render_to_response('news/year_archive.html', {'year': year, 'article_list': a_list}) -This example uses Django's :ref:`template system <topics-templates>`, which has +This example uses Django's :doc:`template system </topics/templates>`, which has several powerful features but strives to stay simple enough for non-programmers to use. @@ -307,17 +305,17 @@ This is just the surface This has been only a quick overview of Django's functionality. Some more useful features: - * A :ref:`caching framework <topics-cache>` that integrates with memcached + * A :doc:`caching framework </topics/cache>` that integrates with memcached or other backends. - * A :ref:`syndication framework <ref-contrib-syndication>` that makes + * A :doc:`syndication framework </ref/contrib/syndication>` that makes creating RSS and Atom feeds as easy as writing a small Python class. * More sexy automatically-generated admin features -- this overview barely scratched the surface. -The next obvious steps are for you to `download Django`_, read :ref:`the -tutorial <intro-tutorial01>` and join `the community`_. Thanks for your +The next obvious steps are for you to `download Django`_, read :doc:`the +tutorial </intro/tutorial01>` and join `the community`_. Thanks for your interest! .. _download Django: http://www.djangoproject.com/download/ diff --git a/docs/intro/tutorial01.txt b/docs/intro/tutorial01.txt index c38fa7d7f5..6045eb111e 100644 --- a/docs/intro/tutorial01.txt +++ b/docs/intro/tutorial01.txt @@ -1,5 +1,3 @@ -.. _intro-tutorial01: - ===================================== Writing your first Django app, part 1 ===================================== @@ -14,7 +12,7 @@ It'll consist of two parts: * A public site that lets people view polls and vote in them. * An admin site that lets you add, change and delete polls. -We'll assume you have :ref:`Django installed <intro-install>` already. You can +We'll assume you have :doc:`Django installed </intro/install>` already. You can tell Django is installed by running the Python interactive interpreter and typing ``import django``. If that command runs successfully, with no errors, Django is installed. @@ -47,8 +45,8 @@ create a ``mysite`` directory in your current directory. you try to run ``django-admin.py startproject``. This is because, on Unix-based systems like OS X, a file must be marked as "executable" before it can be run as a program. To do this, open Terminal.app and navigate (using - the ``cd`` command) to the directory where :ref:`django-admin.py - <ref-django-admin>` is installed, then run the command + the ``cd`` command) to the directory where :doc:`django-admin.py + </ref/django-admin>` is installed, then run the command ``chmod +x django-admin.py``. .. note:: @@ -58,11 +56,11 @@ create a ``mysite`` directory in your current directory. ``django`` (which will conflict with Django itself) or ``test`` (which conflicts with a built-in Python package). -:ref:`django-admin.py <ref-django-admin>` should be on your system path if you +:doc:`django-admin.py </ref/django-admin>` should be on your system path if you installed Django via ``python setup.py``. If it's not on your path, you can find it in ``site-packages/django/bin``, where ```site-packages``` is a directory -within your Python installation. Consider symlinking to :ref:`django-admin.py -<ref-django-admin>` from some place on your path, such as +within your Python installation. Consider symlinking to :doc:`django-admin.py +</ref/django-admin>` from some place on your path, such as :file:`/usr/local/bin`. .. admonition:: Where should this code live? @@ -93,14 +91,14 @@ These files are: * :file:`manage.py`: A command-line utility that lets you interact with this Django project in various ways. You can read all the details about - :file:`manage.py` in :ref:`ref-django-admin`. + :file:`manage.py` in :doc:`/ref/django-admin`. * :file:`settings.py`: Settings/configuration for this Django project. - :ref:`topics-settings` will tell you all about how settings work. + :doc:`/topics/settings` will tell you all about how settings work. * :file:`urls.py`: The URL declarations for this Django project; a "table of contents" of your Django-powered site. You can read more about URLs in - :ref:`topics-http-urls`. + :doc:`/topics/http/urls`. .. _more about packages: http://docs.python.org/tutorial/modules.html#packages @@ -473,7 +471,7 @@ added to your project since the last time you ran syncdb. :djadmin:`syncdb` can be called as often as you like, and it will only ever create the tables that don't exist. -Read the :ref:`django-admin.py documentation <ref-django-admin>` for full +Read the :doc:`django-admin.py documentation </ref/django-admin>` for full information on what the ``manage.py`` utility can do. Playing with the API @@ -508,10 +506,10 @@ things: set the ``DJANGO_SETTINGS_MODULE`` environment variable to ``mysite.settings``. - For more information on all of this, see the :ref:`django-admin.py - documentation <ref-django-admin>`. + For more information on all of this, see the :doc:`django-admin.py + documentation </ref/django-admin>`. -Once you're in the shell, explore the :ref:`database API <topics-db-queries>`:: +Once you're in the shell, explore the :doc:`database API </topics/db/queries>`:: >>> from mysite.polls.models import Poll, Choice # Import the model classes we just wrote. @@ -570,8 +568,8 @@ of this object. Let's fix that by editing the polls model (in the models and don't see any change in how they're represented, you're most likely using an old version of Django. (This version of the tutorial is written for the latest development version of Django.) If you're using a - Subversion checkout of Django's development version (see :ref:`the - installation docs <topics-install>` for more information), you shouldn't have + Subversion checkout of Django's development version (see :doc:`the + installation docs </topics/install>` for more information), you shouldn't have any problems. If you want to stick with an older version of Django, you'll want to switch @@ -693,9 +691,9 @@ Save these changes and start a new Python interactive shell by running >>> c = p.choice_set.filter(choice__startswith='Just hacking') >>> c.delete() -For more information on model relations, see :ref:`Accessing related objects -<ref-models-relations>`. For full details on the database API, see our -:ref:`Database API reference <topics-db-queries>`. +For more information on model relations, see :doc:`Accessing related objects +</ref/models/relations>`. For full details on the database API, see our +:doc:`Database API reference </topics/db/queries>`. -When you're comfortable with the API, read :ref:`part 2 of this tutorial -<intro-tutorial02>` to get Django's automatic admin working. +When you're comfortable with the API, read :doc:`part 2 of this tutorial +</intro/tutorial02>` to get Django's automatic admin working. diff --git a/docs/intro/tutorial02.txt b/docs/intro/tutorial02.txt index a7ab158faa..fcdb812c81 100644 --- a/docs/intro/tutorial02.txt +++ b/docs/intro/tutorial02.txt @@ -1,10 +1,8 @@ -.. _intro-tutorial02: - ===================================== Writing your first Django app, part 2 ===================================== -This tutorial begins where :ref:`Tutorial 1 <intro-tutorial01>` left off. We're +This tutorial begins where :doc:`Tutorial 1 </intro/tutorial01>` left off. We're continuing the Web-poll application and will focus on Django's automatically-generated admin site. @@ -426,7 +424,7 @@ Then, just edit the file and replace the generic Django text with your own site's name as you see fit. This template file contains lots of text like ``{% block branding %}`` -and ``{{ title }}. The ``{%`` and ``{{`` tags are part of Django's +and ``{{ title }}``. The ``{%`` and ``{{`` tags are part of Django's template language. When Django renders ``admin/base_site.html``, this template language will be evaluated to produce the final HTML page. Don't worry if you can't make any sense of the template right now -- @@ -463,5 +461,5 @@ object-specific admin pages in whatever way you think is best. Again, don't worry if you can't understand the template language -- we'll cover that in more detail in Tutorial 3. -When you're comfortable with the admin site, read :ref:`part 3 of this tutorial -<intro-tutorial03>` to start working on public poll views. +When you're comfortable with the admin site, read :doc:`part 3 of this tutorial +</intro/tutorial03>` to start working on public poll views. diff --git a/docs/intro/tutorial03.txt b/docs/intro/tutorial03.txt index 0e09693778..1865b1bd5c 100644 --- a/docs/intro/tutorial03.txt +++ b/docs/intro/tutorial03.txt @@ -1,10 +1,8 @@ -.. _intro-tutorial03: - ===================================== Writing your first Django app, part 3 ===================================== -This tutorial begins where :ref:`Tutorial 2 <intro-tutorial02>` left off. We're +This tutorial begins where :doc:`Tutorial 2 </intro/tutorial02>` left off. We're continuing the Web-poll application and will focus on creating the public interface -- "views." @@ -68,8 +66,8 @@ arbitrary keyword arguments from the dictionary (an optional third item in the tuple). For more on :class:`~django.http.HttpRequest` objects, see the -:ref:`ref-request-response`. For more details on URLconfs, see the -:ref:`topics-http-urls`. +:doc:`/ref/request-response`. For more details on URLconfs, see the +:doc:`/topics/http/urls`. When you ran ``django-admin.py startproject mysite`` at the beginning of Tutorial 1, it created a default URLconf in ``mysite/urls.py``. It also @@ -205,7 +203,7 @@ you want, using whatever Python libraries you want. All Django wants is that :class:`~django.http.HttpResponse`. Or an exception. Because it's convenient, let's use Django's own database API, which we covered -in :ref:`Tutorial 1 <intro-tutorial01>`. Here's one stab at the ``index()`` +in :doc:`Tutorial 1 </intro/tutorial01>`. Here's one stab at the ``index()`` view, which displays the latest 5 poll questions in the system, separated by commas, according to publication date:: @@ -425,7 +423,7 @@ Method-calling happens in the ``{% for %}`` loop: ``poll.choice_set.all`` is interpreted as the Python code ``poll.choice_set.all()``, which returns an iterable of Choice objects and is suitable for use in the ``{% for %}`` tag. -See the :ref:`template guide <topics-templates>` for more about templates. +See the :doc:`template guide </topics/templates>` for more about templates. Simplifying the URLconfs ======================== @@ -514,5 +512,5 @@ under "/content/polls/", or any other URL root, and the app will still work. All the poll app cares about is its relative URLs, not its absolute URLs. -When you're comfortable with writing views, read :ref:`part 4 of this tutorial -<intro-tutorial04>` to learn about simple form processing and generic views. +When you're comfortable with writing views, read :doc:`part 4 of this tutorial +</intro/tutorial04>` to learn about simple form processing and generic views. diff --git a/docs/intro/tutorial04.txt b/docs/intro/tutorial04.txt index ee3a3b2045..a7a9aaea33 100644 --- a/docs/intro/tutorial04.txt +++ b/docs/intro/tutorial04.txt @@ -1,10 +1,8 @@ -.. _intro-tutorial04: - ===================================== Writing your first Django app, part 4 ===================================== -This tutorial begins where :ref:`Tutorial 3 <intro-tutorial03>` left off. We're +This tutorial begins where :doc:`Tutorial 3 </intro/tutorial03>` left off. We're continuing the Web-poll application and will focus on simple form processing and cutting down our code. @@ -70,7 +68,7 @@ The details of how this works are explained in the documentation for :ref:`RequestContext <subclassing-context-requestcontext>`. Now, let's create a Django view that handles the submitted data and does -something with it. Remember, in :ref:`Tutorial 3 <intro-tutorial03>`, we +something with it. Remember, in :doc:`Tutorial 3 </intro/tutorial03>`, we created a URLconf for the polls application that includes this line:: (r'^(?P<poll_id>\d+)/vote/$', 'vote'), @@ -149,7 +147,7 @@ This code includes a few things we haven't covered yet in this tutorial: As mentioned in Tutorial 3, ``request`` is a :class:`~django.http.HttpRequest` object. For more on :class:`~django.http.HttpRequest` objects, see the -:ref:`request and response documentation <ref-request-response>`. +:doc:`request and response documentation </ref/request-response>`. After somebody votes in a poll, the ``vote()`` view redirects to the results page for the poll. Let's write that view:: @@ -158,8 +156,8 @@ page for the poll. Let's write that view:: p = get_object_or_404(Poll, pk=poll_id) return render_to_response('polls/results.html', {'poll': p}) -This is almost exactly the same as the ``detail()`` view from :ref:`Tutorial 3 -<intro-tutorial03>`. The only difference is the template name. We'll fix this +This is almost exactly the same as the ``detail()`` view from :doc:`Tutorial 3 +</intro/tutorial03>`. The only difference is the template name. We'll fix this redundancy later. Now, create a ``results.html`` template: @@ -183,7 +181,7 @@ without having chosen a choice, you should see the error message. Use generic views: Less code is better ====================================== -The ``detail()`` (from :ref:`Tutorial 3 <intro-tutorial03>`) and ``results()`` +The ``detail()`` (from :doc:`Tutorial 3 </intro/tutorial03>`) and ``results()`` views are stupidly simple -- and, as mentioned above, redundant. The ``index()`` view (also from Tutorial 3), which displays a list of polls, is similar. @@ -328,8 +326,8 @@ are) used multiple times -- but we can use the name we've given:: Run the server, and use your new polling app based on generic views. -For full details on generic views, see the :ref:`generic views documentation -<topics-http-generic-views>`. +For full details on generic views, see the :doc:`generic views documentation +</topics/http/generic-views>`. Coming soon =========== @@ -344,5 +342,5 @@ will cover: * Advanced admin features: Permissions * Advanced admin features: Custom JavaScript -In the meantime, you might want to check out some pointers on :ref:`where to go -from here <intro-whatsnext>` +In the meantime, you might want to check out some pointers on :doc:`where to go +from here </intro/whatsnext>` diff --git a/docs/intro/whatsnext.txt b/docs/intro/whatsnext.txt index 0949b2299e..fe385ffd9a 100644 --- a/docs/intro/whatsnext.txt +++ b/docs/intro/whatsnext.txt @@ -1,10 +1,8 @@ -.. _intro-whatsnext: - ================= What to read next ================= -So you've read all the :ref:`introductory material <intro-index>` and have +So you've read all the :doc:`introductory material </intro/index>` and have decided you'd like to keep using Django. We've only just scratched the surface with this intro (in fact, if you've read every single word you've still read less than 10% of the overall documentation). @@ -37,15 +35,15 @@ How the documentation is organized Django's main documentation is broken up into "chunks" designed to fill different needs: - * The :ref:`introductory material <intro-index>` is designed for people new + * The :doc:`introductory material </intro/index>` is designed for people new to Django -- or to web development in general. It doesn't cover anything in depth, but instead gives a high-level overview of how developing in Django "feels". - * The :ref:`topic guides <topics-index>`, on the other hand, dive deep into + * The :doc:`topic guides </topics/index>`, on the other hand, dive deep into individual parts of Django. There are complete guides to Django's - :ref:`model system <topics-db-index>`, :ref:`template engine - <topics-templates>`, :ref:`forms framework <topics-forms-index>`, and much + :doc:`model system </topics/db/index>`, :doc:`template engine + </topics/templates>`, :doc:`forms framework </topics/forms/index>`, and much more. This is probably where you'll want to spend most of your time; if you work @@ -53,27 +51,27 @@ different needs: everything there is to know about Django. * Web development is often broad, not deep -- problems span many domains. - We've written a set of :ref:`how-to guides <howto-index>` that answer + We've written a set of :doc:`how-to guides </howto/index>` that answer common "How do I ...?" questions. Here you'll find information about - :ref:`generating PDFs with Django <howto-outputting-pdf>`, :ref:`writing - custom template tags <howto-custom-template-tags>`, and more. + :doc:`generating PDFs with Django </howto/outputting-pdf>`, :doc:`writing + custom template tags </howto/custom-template-tags>`, and more. - Answers to really common questions can also be found in the :ref:`FAQ - <faq-index>`. + Answers to really common questions can also be found in the :doc:`FAQ + </faq/index>`. * The guides and how-to's don't cover every single class, function, and method available in Django -- that would be overwhelming when you're trying to learn. Instead, details about individual classes, functions, - methods, and modules are kept in the :ref:`reference <ref-index>`. This is + methods, and modules are kept in the :doc:`reference </ref/index>`. This is where you'll turn to find the details of a particular function or whathaveyou. * Finally, there's some "specialized" documentation not usually relevant to - most developers. This includes the :ref:`release notes <releases-index>`, - :ref:`documentation of obsolete features <obsolete-index>`, - :ref:`internals documentation <internals-index>` for those who want to add - code to Django itself, and a :ref:`few other things that simply don't fit - elsewhere <misc-index>`. + most developers. This includes the :doc:`release notes </releases/index>`, + :doc:`documentation of obsolete features </obsolete/index>`, + :doc:`internals documentation </internals/index>` for those who want to add + code to Django itself, and a :doc:`few other things that simply don't fit + elsewhere </misc/index>`. How documentation is updated @@ -187,11 +185,10 @@ You can get a local copy of the HTML documentation following a few easy steps: * The HTML documentation will be placed in ``docs/_build/html``. -.. warning:: +.. note:: - At the time of this writing, Django's using a version of Sphinx not - yet released, so you'll currently need to install Sphinx from the - source. We'll fix this shortly. + Generation of the Django documentation will work with Sphinx version 0.6 + or newer, but we recommend going straight to Sphinx 1.0.2 or newer. __ http://sphinx.pocoo.org/ __ http://www.gnu.org/software/make/ diff --git a/docs/misc/api-stability.txt b/docs/misc/api-stability.txt index a648c873cc..70e5221592 100644 --- a/docs/misc/api-stability.txt +++ b/docs/misc/api-stability.txt @@ -1,10 +1,8 @@ -.. _misc-api-stability: - ============= API stability ============= -:ref:`The release of Django 1.0 <releases-1.0>` comes with a promise of API +:doc:`The release of Django 1.0 </releases/1.0>` comes with a promise of API stability and forwards-compatibility. In a nutshell, this means that code you develop against Django 1.0 will continue to work against 1.1 unchanged, and you should need to make only minor changes for any 1.X release. @@ -37,67 +35,67 @@ Stable APIs =========== In general, everything covered in the documentation -- with the exception of -anything in the :ref:`internals area <internals-index>` is considered stable as +anything in the :doc:`internals area </internals/index>` is considered stable as of 1.0. This includes these APIs: - - :ref:`Authorization <topics-auth>` + - :doc:`Authorization </topics/auth>` - - :ref:`Caching <topics-cache>`. + - :doc:`Caching </topics/cache>`. - - :ref:`Model definition, managers, querying and transactions - <topics-db-index>` + - :doc:`Model definition, managers, querying and transactions + </topics/db/index>` - - :ref:`Sending e-mail <topics-email>`. + - :doc:`Sending e-mail </topics/email>`. - - :ref:`File handling and storage <topics-files>` + - :doc:`File handling and storage </topics/files>` - - :ref:`Forms <topics-forms-index>` + - :doc:`Forms </topics/forms/index>` - - :ref:`HTTP request/response handling <topics-http-index>`, including file + - :doc:`HTTP request/response handling </topics/http/index>`, including file uploads, middleware, sessions, URL resolution, view, and shortcut APIs. - - :ref:`Generic views <topics-http-generic-views>`. + - :doc:`Generic views </topics/http/generic-views>`. - - :ref:`Internationalization <topics-i18n>`. + - :doc:`Internationalization </topics/i18n/index>`. - - :ref:`Pagination <topics-pagination>` + - :doc:`Pagination </topics/pagination>` - - :ref:`Serialization <topics-serialization>` + - :doc:`Serialization </topics/serialization>` - - :ref:`Signals <topics-signals>` + - :doc:`Signals </topics/signals>` - - :ref:`Templates <topics-templates>`, including the language, Python-level - :ref:`template APIs <ref-templates-index>`, and :ref:`custom template tags - and libraries <howto-custom-template-tags>`. We may add new template + - :doc:`Templates </topics/templates>`, including the language, Python-level + :doc:`template APIs </ref/templates/index>`, and :doc:`custom template tags + and libraries </howto/custom-template-tags>`. We may add new template tags in the future and the names may inadvertently clash with external template tags. Before adding any such tags, we'll ensure that Django raises an error if it tries to load tags with duplicate names. - - :ref:`Testing <topics-testing>` + - :doc:`Testing </topics/testing>` - - :ref:`django-admin utility <ref-django-admin>`. + - :doc:`django-admin utility </ref/django-admin>`. - - :ref:`Built-in middleware <ref-middleware>` + - :doc:`Built-in middleware </ref/middleware>` - - :ref:`Request/response objects <ref-request-response>`. + - :doc:`Request/response objects </ref/request-response>`. - - :ref:`Settings <ref-settings>`. Note, though that while the :ref:`list of - built-in settings <ref-settings>` can be considered complete we may -- and + - :doc:`Settings </ref/settings>`. Note, though that while the :doc:`list of + built-in settings </ref/settings>` can be considered complete we may -- and probably will -- add new settings in future versions. This is one of those places where "'stable' does not mean 'complete.'" - - :ref:`Built-in signals <ref-signals>`. Like settings, we'll probably add + - :doc:`Built-in signals </ref/signals>`. Like settings, we'll probably add new signals in the future, but the existing ones won't break. - - :ref:`Unicode handling <ref-unicode>`. + - :doc:`Unicode handling </ref/unicode>`. - - Everything covered by the :ref:`HOWTO guides <howto-index>`. + - Everything covered by the :doc:`HOWTO guides </howto/index>`. ``django.utils`` ---------------- Most of the modules in ``django.utils`` are designed for internal use. Only -the following parts of :ref:`django.utils <ref-utils>` can be considered stable: +the following parts of :doc:`django.utils </ref/utils>` can be considered stable: - ``django.utils.cache`` - ``django.utils.datastructures.SortedDict`` -- only this single class; the diff --git a/docs/misc/design-philosophies.txt b/docs/misc/design-philosophies.txt index 43bb8096c9..631097ae2b 100644 --- a/docs/misc/design-philosophies.txt +++ b/docs/misc/design-philosophies.txt @@ -1,5 +1,3 @@ -.. _misc-design-philosophies: - =================== Design philosophies =================== diff --git a/docs/misc/distributions.txt b/docs/misc/distributions.txt index 6a0845801d..d9281ad3da 100644 --- a/docs/misc/distributions.txt +++ b/docs/misc/distributions.txt @@ -1,5 +1,3 @@ -.. _misc-distributions: - =================================== Third-party distributions of Django =================================== diff --git a/docs/misc/index.txt b/docs/misc/index.txt index 534171b6ed..b42baeb9fd 100644 --- a/docs/misc/index.txt +++ b/docs/misc/index.txt @@ -1,5 +1,3 @@ -.. _misc-index: - Meta-documentation and miscellany ================================= diff --git a/docs/obsolete/admin-css.txt b/docs/obsolete/admin-css.txt index 4f8fb663e2..f4cca549b4 100644 --- a/docs/obsolete/admin-css.txt +++ b/docs/obsolete/admin-css.txt @@ -1,5 +1,3 @@ -.. _obsolete-admin-css: - ====================================== Customizing the Django admin interface ====================================== diff --git a/docs/obsolete/index.txt b/docs/obsolete/index.txt index 09e0826b88..ddc86237cc 100644 --- a/docs/obsolete/index.txt +++ b/docs/obsolete/index.txt @@ -1,5 +1,3 @@ -.. _obsolete-index: - Deprecated/obsolete documentation ================================= diff --git a/docs/ref/authbackends.txt b/docs/ref/authbackends.txt index 0e98c21b21..a50b414c78 100644 --- a/docs/ref/authbackends.txt +++ b/docs/ref/authbackends.txt @@ -1,5 +1,3 @@ -.. _ref-authentication-backends: - ======================= Authentication backends ======================= @@ -10,8 +8,8 @@ Authentication backends This document details the authentication backends that come with Django. For information on how to use them and how to write your own authentication backends, see the :ref:`Other authentication sources section -<authentication-backends>` of the :ref:`User authentication guide -<topics-auth>`. +<authentication-backends>` of the :doc:`User authentication guide +</topics/auth>`. Available authentication backends @@ -33,5 +31,5 @@ The following backends are available in :mod:`django.contrib.auth.backends`: Use this backend to take advantage of external-to-Django-handled authentication. It authenticates using usernames passed in :attr:`request.META['REMOTE_USER'] <django.http.HttpRequest.META>`. See - the :ref:`Authenticating against REMOTE_USER <howto-auth-remote-user>` + the :doc:`Authenticating against REMOTE_USER </howto/auth-remote-user>` documentation. diff --git a/docs/ref/contrib/admin/actions.txt b/docs/ref/contrib/admin/actions.txt index 62f944d9b6..86a5355b28 100644 --- a/docs/ref/contrib/admin/actions.txt +++ b/docs/ref/contrib/admin/actions.txt @@ -1,5 +1,3 @@ -.. _ref-contrib-admin-actions: - ============= Admin actions ============= @@ -208,7 +206,7 @@ objects. To provide an intermediary page, simply return an :class:`~django.http.HttpResponse` (or subclass) from your action. For example, you might write a simple export function that uses Django's -:ref:`serialization functions <topics-serialization>` to dump some selected +:doc:`serialization functions </topics/serialization>` to dump some selected objects as JSON:: from django.http import HttpResponse @@ -294,7 +292,7 @@ Disabling a site-wide action site-wide. If, however, you need to re-enable a globally-disabled action for one - particular model, simply list it explicitally in your ``ModelAdmin.actions`` + particular model, simply list it explicitly in your ``ModelAdmin.actions`` list:: # Globally disable delete selected diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt index 7fee903715..055057677c 100644 --- a/docs/ref/contrib/admin/index.txt +++ b/docs/ref/contrib/admin/index.txt @@ -1,5 +1,3 @@ -.. _ref-contrib-admin: - ===================== The Django admin site ===================== @@ -7,8 +5,6 @@ The Django admin site .. module:: django.contrib.admin :synopsis: Django's admin site. -.. currentmodule:: django.contrib.admin - One of the most powerful parts of Django is the automatic admin interface. It reads metadata in your model to provide a powerful and production-ready interface that content producers can immediately use to start adding content to @@ -474,17 +470,16 @@ change list page. By default, this is set to ``100``. .. attribute:: ModelAdmin.list_select_related -Set ``list_select_related`` to tell Django to use ``select_related()`` in -retrieving the list of objects on the admin change list page. This can save you -a bunch of database queries. +Set ``list_select_related`` to tell Django to use +:meth:`~django.db.models.QuerySet.select_related` in retrieving the list of +objects on the admin change list page. This can save you a bunch of database +queries. The value should be either ``True`` or ``False``. Default is ``False``. -Note that Django will use ``select_related()``, regardless of this setting, -if one of the ``list_display`` fields is a ``ForeignKey``. - -For more on ``select_related()``, see -:ref:`the select_related() docs <select-related>`. +Note that Django will use :meth:`~django.db.models.QuerySet.select_related`, +regardless of this setting, if one of the ``list_display`` fields is a +``ForeignKey``. .. attribute:: ModelAdmin.inlines @@ -595,11 +590,16 @@ This should be set to a list of field names that will be searched whenever somebody submits a search query in that text box. These fields should be some kind of text field, such as ``CharField`` or -``TextField``. You can also perform a related lookup on a ``ForeignKey`` with -the lookup API "follow" notation:: +``TextField``. You can also perform a related lookup on a ``ForeignKey`` or +``ManyToManyField`` with the lookup API "follow" notation:: search_fields = ['foreign_key__related_fieldname'] +For example, if you have a blog entry with an author, the following definition +would enable search blog entries by the email address of the author:: + + search_fields = ['user__email'] + When somebody does a search in the admin search box, Django splits the search query into words and returns all objects that contain each of the words, case insensitive, where each word must be in at least one of ``search_fields``. For @@ -674,7 +674,7 @@ do that:: Note that the key in the dictionary is the actual field class, *not* a string. The value is another dictionary; these arguments will be passed to -:meth:`~django.forms.Field.__init__`. See :ref:`ref-forms-api` for details. +:meth:`~django.forms.Field.__init__`. See :doc:`/ref/forms/api` for details. .. warning:: @@ -692,7 +692,7 @@ The value is another dictionary; these arguments will be passed to .. versionadded:: 1.1 A list of actions to make available on the change list page. See -:ref:`ref-contrib-admin-actions` for details. +:doc:`/ref/contrib/admin/actions` for details. .. attribute:: ModelAdmin.actions_on_top .. attribute:: ModelAdmin.actions_on_bottom @@ -743,8 +743,8 @@ templates used by the :class:`ModelAdmin` views: Path to a custom template, used by the :meth:`delete_selected` action method for displaying a confirmation page when deleting one - or more objects. See the :ref:`actions - documentation<ref-contrib-admin-actions>`. + or more objects. See the :doc:`actions + documentation</ref/contrib/admin/actions>`. .. attribute:: ModelAdmin.object_history_template @@ -801,7 +801,7 @@ described above in the :attr:`ModelAdmin.readonly_fields` section. The ``get_urls`` method on a ``ModelAdmin`` returns the URLs to be used for that ModelAdmin in the same way as a URLconf. Therefore you can extend them as -documented in :ref:`topics-http-urls`:: +documented in :doc:`/topics/http/urls`:: class MyModelAdmin(admin.ModelAdmin): def get_urls(self): @@ -829,7 +829,7 @@ problems: Since this is usually not what you want, Django provides a convenience wrapper to check permissions and mark the view as non-cacheable. This wrapper is -:meth:`AdminSite.admin_view` (i.e. ``self.admin_site.admin_view`` inside a +:meth:`AdminSite.admin_view` (i.e. ``self.admin_site.admin_view`` inside a ``ModelAdmin`` instance); use it like so:: class MyModelAdmin(admin.ModelAdmin): @@ -865,11 +865,26 @@ return a subset of objects for this foreign key field based on the user:: def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name == "car": kwargs["queryset"] = Car.objects.filter(owner=request.user) - return db_field.formfield(**kwargs) return super(MyModelAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs) This uses the ``HttpRequest`` instance to filter the ``Car`` foreign key field -to only the cars owned by the ``User`` instance. +to only display the cars owned by the ``User`` instance. + +.. method:: ModelAdmin.formfield_for_manytomany(self, db_field, request, **kwargs) + +.. versionadded:: 1.1 + +Like the ``formfield_for_foreignkey`` method, the ``formfield_for_manytomany`` +method can be overridden to change the default formfield for a many to many +field. For example, if an owner can own multiple cars and cars can belong +to multiple owners -- a many to many relationship -- you could filter the +``Car`` foreign key field to only display the cars owned by the ``User``:: + + class MyModelAdmin(admin.ModelAdmin): + def formfield_for_manytomany(self, db_field, request, **kwargs): + if db_field.name == "cars": + kwargs["queryset"] = Car.objects.filter(owner=request.user) + return super(MyModelAdmin, self).formfield_for_manytomany(db_field, request, **kwargs) .. method:: ModelAdmin.queryset(self, request) @@ -950,7 +965,7 @@ on your ``ModelAdmin``:: js = ("my_code.js",) Keep in mind that this will be prepended with ``MEDIA_URL``. The same rules -apply as :ref:`regular media definitions on forms <topics-forms-media>`. +apply as :doc:`regular media definitions on forms </topics/forms/media>`. Django admin Javascript makes use of the `jQuery`_ library. To avoid conflict with user scripts, Django's jQuery is namespaced as @@ -983,8 +998,8 @@ any field:: return self.cleaned_data["name"] It is important you use a ``ModelForm`` here otherwise things can break. See the -:ref:`forms <ref-forms-index>` documentation on :ref:`custom validation -<ref-forms-validation>` and, more specifically, the +:doc:`forms </ref/forms/index>` documentation on :doc:`custom validation +</ref/forms/validation>` and, more specifically, the :ref:`model form validation notes <overriding-modelform-clean-method>` for more information. @@ -993,6 +1008,8 @@ information. ``InlineModelAdmin`` objects ============================ +.. class:: InlineModelAdmin + The admin interface has the ability to edit models on the same page as a parent model. These are called inlines. Suppose you have these two models:: @@ -1027,90 +1044,88 @@ The difference between these two is merely the template used to render them. The ``InlineModelAdmin`` class is a subclass of ``ModelAdmin`` so it inherits all the same functionality as well as some of its own: -``model`` -~~~~~~~~~ +.. attribute:: InlineModelAdmin.model -The model in which the inline is using. This is required. + The model in which the inline is using. This is required. -``fk_name`` -~~~~~~~~~~~ +.. attribute:: InlineModelAdmin.fk_name -The name of the foreign key on the model. In most cases this will be dealt -with automatically, but ``fk_name`` must be specified explicitly if there are -more than one foreign key to the same parent model. + The name of the foreign key on the model. In most cases this will be dealt + with automatically, but ``fk_name`` must be specified explicitly if there + are more than one foreign key to the same parent model. -``formset`` -~~~~~~~~~~~ +.. attribute:: InlineModelAdmin.formset -This defaults to ``BaseInlineFormSet``. Using your own formset can give you -many possibilities of customization. Inlines are built around -:ref:`model formsets <model-formsets>`. + This defaults to ``BaseInlineFormSet``. Using your own formset can give you + many possibilities of customization. Inlines are built around + :ref:`model formsets <model-formsets>`. -``form`` -~~~~~~~~ +.. attribute:: InlineModelAdmin.form -The value for ``form`` defaults to ``ModelForm``. This is what is -passed through to ``inlineformset_factory`` when creating the formset for this -inline. + The value for ``form`` defaults to ``ModelForm``. This is what is passed + through to ``inlineformset_factory`` when creating the formset for this + inline. .. _ref-contrib-admin-inline-extra: -``extra`` -~~~~~~~~~ +.. attribute:: InlineModelAdmin.extra -This controls the number of extra forms the formset will display in addition -to the initial forms. See the -:ref:`formsets documentation <topics-forms-formsets>` for more information. -.. versionadded:: 1.2 + This controls the number of extra forms the formset will display in addition + to the initial forms. See the + :doc:`formsets documentation </topics/forms/formsets>` for more information. -For users with JavaScript-enabled browsers, an "Add another" link is -provided to enable any number of additional inlines to be added in -addition to those provided as a result of the ``extra`` argument. + .. versionadded:: 1.2 -The dynamic link will not appear if the number of currently displayed -forms exceeds ``max_num``, or if the user does not have JavaScript -enabled. + For users with JavaScript-enabled browsers, an "Add another" link is + provided to enable any number of additional inlines to be added in addition + to those provided as a result of the ``extra`` argument. + + The dynamic link will not appear if the number of currently displayed forms + exceeds ``max_num``, or if the user does not have JavaScript enabled. .. _ref-contrib-admin-inline-max-num: -``max_num`` -~~~~~~~~~~~ +.. attribute:: InlineModelAdmin.max_num -This controls the maximum number of forms to show in the inline. This doesn't -directly correlate to the number of objects, but can if the value is small -enough. See :ref:`model-formsets-max-num` for more information. + This controls the maximum number of forms to show in the inline. This + doesn't directly correlate to the number of objects, but can if the value + is small enough. See :ref:`model-formsets-max-num` for more information. -``raw_id_fields`` -~~~~~~~~~~~~~~~~~ +.. attribute:: InlineModelAdmin.raw_id_fields -By default, Django's admin uses a select-box interface (<select>) for -fields that are ``ForeignKey``. Sometimes you don't want to incur the -overhead of having to select all the related instances to display in the -drop-down. + By default, Django's admin uses a select-box interface (<select>) for + fields that are ``ForeignKey``. Sometimes you don't want to incur the + overhead of having to select all the related instances to display in the + drop-down. -``raw_id_fields`` is a list of fields you would like to change -into a ``Input`` widget for either a ``ForeignKey`` or ``ManyToManyField``:: + ``raw_id_fields`` is a list of fields you would like to change into a + ``Input`` widget for either a ``ForeignKey`` or ``ManyToManyField``:: - class BookInline(admin.TabularInline): - model = Book - raw_id_fields = ("pages",) + class BookInline(admin.TabularInline): + model = Book + raw_id_fields = ("pages",) -``template`` -~~~~~~~~~~~~ -The template used to render the inline on the page. +.. attribute:: InlineModelAdmin.template -``verbose_name`` -~~~~~~~~~~~~~~~~ + The template used to render the inline on the page. -An override to the ``verbose_name`` found in the model's inner ``Meta`` class. +.. attribute:: InlineModelAdmin.verbose_name -``verbose_name_plural`` -~~~~~~~~~~~~~~~~~~~~~~~ + An override to the ``verbose_name`` found in the model's inner ``Meta`` + class. + +.. attribute:: InlineModelAdmin.verbose_name_plural + + An override to the ``verbose_name_plural`` found in the model's inner + ``Meta`` class. + +.. attribute:: InlineModelAdmin.can_delete + + Specifies whether or not inline objects can be deleted in the inline. + Defaults to ``True``. -An override to the ``verbose_name_plural`` found in the model's inner ``Meta`` -class. Working with a model with two or more foreign keys to the same parent model --------------------------------------------------------------------------- @@ -1189,7 +1204,7 @@ your admin page for managing the relation. In all other respects, the ``InlineModelAdmin`` is exactly the same as any other. You can customize the appearance using any of the normal -``InlineModelAdmin`` properties. +``ModelAdmin`` properties. Working with Many-to-Many Intermediary Models ---------------------------------------------- @@ -1281,7 +1296,7 @@ example app:: ``django.contrib.contenttypes.generic`` provides both a ``GenericTabularInline`` and ``GenericStackedInline`` and behave just like any other inline. See the -:ref:`contenttypes documentation <ref-contrib-contenttypes>` for more specific +:doc:`contenttypes documentation </ref/contrib/contenttypes>` for more specific information. Overriding Admin Templates diff --git a/docs/ref/contrib/auth.txt b/docs/ref/contrib/auth.txt index 03f5ff1281..619b38e5ac 100644 --- a/docs/ref/contrib/auth.txt +++ b/docs/ref/contrib/auth.txt @@ -1,6 +1,4 @@ -.. _ref-contrib-auth: - ``django.contrib.auth`` ======================= -See :ref:`topics-auth`. +See :doc:`/topics/auth`. diff --git a/docs/ref/contrib/comments/custom.txt b/docs/ref/contrib/comments/custom.txt index 9e32fc4fed..49299d4d33 100644 --- a/docs/ref/contrib/comments/custom.txt +++ b/docs/ref/contrib/comments/custom.txt @@ -1,5 +1,3 @@ -.. _ref-contrib-comments-custom: - ================================== Customizing the comments framework ================================== diff --git a/docs/ref/contrib/comments/example.txt b/docs/ref/contrib/comments/example.txt index d4ce623bb0..424bdb13f5 100644 --- a/docs/ref/contrib/comments/example.txt +++ b/docs/ref/contrib/comments/example.txt @@ -1,5 +1,3 @@ -.. _ref-contrib-comments-example: - .. highlightlang:: html+django =========================================== @@ -7,7 +5,7 @@ Example of using the in-built comments app =========================================== Follow the first three steps of the quick start guide in the -:ref:`documentation <ref-contrib-comments-index>`. +:doc:`documentation </ref/contrib/comments/index>`. Now suppose, you have an app (``blog``) with a model (``Post``) to which you want to attach comments. Let us also suppose that @@ -85,8 +83,8 @@ It looks for the ``form.html`` under the following directories Since we customize the form in the second method, we make use of another tag called :ttag:`comment_form_target`. This tag on rendering gives the URL -where the comment form is posted. Without any :ref:`customization -<ref-contrib-comments-custom>`, :ttag:`comment_form_target` evaluates to +where the comment form is posted. Without any :doc:`customization +</ref/contrib/comments/custom>`, :ttag:`comment_form_target` evaluates to ``/comments/post/``. We use this tag in the form's ``action`` attribute. The :ttag:`get_comment_form` tag renders a ``form`` for a model instance by @@ -136,7 +134,7 @@ found under the directory structure we saw for ``form.html``. Feeds ===== -Suppose you want to export a :ref:`feed <ref-contrib-syndication>` of the +Suppose you want to export a :doc:`feed </ref/contrib/syndication>` of the latest comments, you can use the in-built :class:`LatestCommentFeed`. Just enable it in your project's ``urls.py``: @@ -163,8 +161,8 @@ Moderation Now that we have the comments framework working, we might want to have some moderation setup to administer the comments. The comments framework comes -in-built with :ref:`generic comment moderation -<ref-contrib-comments-moderation>`. The comment moderation has the following +in-built with :doc:`generic comment moderation +</ref/contrib/comments/moderation>`. The comment moderation has the following features (all of which or only certain can be enabled): * Enable comments for a particular model instance. @@ -181,18 +179,18 @@ following way: from django.contrib.comments.moderation import CommentModerator, moderator from django.db import models - + class Post(models.Model): title = models.CharField(max_length = 255) content = models.TextField() posted_date = models.DateTimeField() - + class PostModerator(CommentModerator): email_notification = True auto_close_field = 'posted_date' # Close the comments after 7 days. close_after = 7 - + moderator.register(Post, PostModerator) The generic comment moderation also has the facility to remove comments. diff --git a/docs/ref/contrib/comments/forms.txt b/docs/ref/contrib/comments/forms.txt index 3eacb5db54..c21a27bb9e 100644 --- a/docs/ref/contrib/comments/forms.txt +++ b/docs/ref/contrib/comments/forms.txt @@ -1,5 +1,3 @@ -.. _ref-contrib-comments-forms: - ==================== Comment form classes ==================== @@ -9,7 +7,7 @@ Comment form classes The ``django.contrib.comments.forms`` module contains a handful of forms you'll use when writing custom views dealing with comments, or when writing -:ref:`custom comment apps <ref-contrib-comments-custom>`. +:doc:`custom comment apps </ref/contrib/comments/custom>`. .. class:: CommentForm @@ -23,7 +21,7 @@ you'll use when writing custom views dealing with comments, or when writing Abstract comment forms for custom comment apps ---------------------------------------------- -If you're building a :ref:`custom comment app <ref-contrib-comments-custom>`, +If you're building a :doc:`custom comment app </ref/contrib/comments/custom>`, you might want to replace *some* of the form logic but still rely on parts of the existing form. diff --git a/docs/ref/contrib/comments/index.txt b/docs/ref/contrib/comments/index.txt index 9f1d3cd6e4..817871e976 100644 --- a/docs/ref/contrib/comments/index.txt +++ b/docs/ref/contrib/comments/index.txt @@ -1,5 +1,3 @@ -.. _ref-contrib-comments-index: - =========================== Django's comments framework =========================== @@ -16,7 +14,7 @@ it for comments on blog entries, photos, book chapters, or anything else. .. note:: If you used to use Django's older (undocumented) comments framework, you'll - need to upgrade. See the :ref:`upgrade guide <ref-contrib-comments-upgrade>` + need to upgrade. See the :doc:`upgrade guide </ref/contrib/comments/upgrade>` for instructions. Quick start guide @@ -42,7 +40,7 @@ To get started using the ``comments`` app, follow these steps: #. Use the `comment template tags`_ below to embed comments in your templates. -You might also want to examine :ref:`ref-contrib-comments-settings`. +You might also want to examine :doc:`/ref/contrib/comments/settings`. Comment template tags ===================== @@ -124,7 +122,7 @@ For example:: {% endfor %} This returns a list of :class:`~django.contrib.comments.models.Comment` objects; -see :ref:`the comment model documentation <ref-contrib-comments-models>` for +see :doc:`the comment model documentation </ref/contrib/comments/models>` for details. .. templatetag:: get_comment_permalink @@ -212,7 +210,7 @@ Rendering a custom comment form ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you want more control over the look and feel of the comment form, you use use -:ttag:`get_comment_form` to get a :ref:`form object <topics-forms-index>` that +:ttag:`get_comment_form` to get a :doc:`form object </topics/forms/index>` that you can use in the template:: {% get_comment_form for [object] as [varname] %} @@ -279,8 +277,8 @@ should know about: it with a warning field; if you use the comment form with a custom template you should be sure to do the same. -The comments app also depends on the more general :ref:`Cross Site Request -Forgery protection < ref-contrib-csrf>` that comes with Django. As described in +The comments app also depends on the more general :doc:`Cross Site Request +Forgery protection </ref/contrib/csrf>` that comes with Django. As described in the documentation, it is best to use ``CsrfViewMiddleware``. However, if you are not using that, you will need to use the ``csrf_protect`` decorator on any views that include the comment form, in order for those views to be able to diff --git a/docs/ref/contrib/comments/models.txt b/docs/ref/contrib/comments/models.txt index af85d68f00..e773790d65 100644 --- a/docs/ref/contrib/comments/models.txt +++ b/docs/ref/contrib/comments/models.txt @@ -1,82 +1,80 @@ -.. _ref-contrib-comments-models: - =========================== The built-in comment models =========================== .. module:: django.contrib.comments.models :synopsis: The built-in comment models - + .. class:: Comment Django's built-in comment model. Has the following fields: - + .. attribute:: content_object - + A :class:`~django.contrib.contettypes.generic.GenericForeignKey` attribute pointing to the object the comment is attached to. You can use this to get at the related object (i.e. ``my_comment.content_object``). - + Since this field is a :class:`~django.contrib.contettypes.generic.GenericForeignKey`, it's actually syntactic sugar on top of two underlying attributes, described below. - + .. attribute:: content_type - + A :class:`~django.db.models.ForeignKey` to :class:`~django.contrib.contenttypes.models.ContentType`; this is the type of the object the comment is attached to. - + .. attribute:: object_pk - + A :class:`~django.db.models.TextField` containing the primary key of the object the comment is attached to. - + .. attribute:: site - + A :class:`~django.db.models.ForeignKey` to the :class:`~django.contrib.sites.models.Site` on which the comment was posted. - + .. attribute:: user - + A :class:`~django.db.models.ForeignKey` to the :class:`~django.contrib.auth.models.User` who posted the comment. May be blank if the comment was posted by an unauthenticated user. - + .. attribute:: user_name - + The name of the user who posted the comment. - + .. attribute:: user_email - - The email of the user who posteed the comment. - + + The email of the user who posted the comment. + .. attribute:: user_url - + The URL entered by the person who posted the comment. - + .. attribute:: comment - + The actual content of the comment itself. - + .. attribute:: submit_date - + The date the comment was submitted. - + .. attribute:: ip_address - + The IP address of the user posting the comment. - + .. attribute:: is_public - + ``False`` if the comment is in moderation (see - :ref:`ref-contrib-comments-moderation`); If ``True``, the comment will + :doc:`/ref/contrib/comments/moderation`); If ``True``, the comment will be displayed on the site. - + .. attribute:: is_removed - + ``True`` if the comment was removed. Used to keep track of removed comments instead of just deleting them. - + diff --git a/docs/ref/contrib/comments/moderation.txt b/docs/ref/contrib/comments/moderation.txt index 2c4072ba5b..198f78fa89 100644 --- a/docs/ref/contrib/comments/moderation.txt +++ b/docs/ref/contrib/comments/moderation.txt @@ -1,5 +1,3 @@ -.. _ref-contrib-comments-moderation: - ========================== Generic comment moderation ========================== diff --git a/docs/ref/contrib/comments/settings.txt b/docs/ref/contrib/comments/settings.txt index ff94d2dbcc..1f1aecafd4 100644 --- a/docs/ref/contrib/comments/settings.txt +++ b/docs/ref/contrib/comments/settings.txt @@ -1,5 +1,3 @@ -.. _ref-contrib-comments-settings: - ================ Comment settings ================ @@ -29,7 +27,7 @@ this will be rejected. Defaults to 3000. COMMENTS_APP ------------ -An app which provides :ref:`customization of the comments framework -<ref-contrib-comments-custom>`. Use the same dotted-string notation +An app which provides :doc:`customization of the comments framework +</ref/contrib/comments/custom>`. Use the same dotted-string notation as in :setting:`INSTALLED_APPS`. Your custom :setting:`COMMENTS_APP` must also be listed in :setting:`INSTALLED_APPS`. diff --git a/docs/ref/contrib/comments/signals.txt b/docs/ref/contrib/comments/signals.txt index cd406b56dd..7ae34a1900 100644 --- a/docs/ref/contrib/comments/signals.txt +++ b/docs/ref/contrib/comments/signals.txt @@ -1,5 +1,3 @@ -.. _ref-contrib-comments-signals: - ================================ Signals sent by the comments app ================================ @@ -7,9 +5,9 @@ Signals sent by the comments app .. module:: django.contrib.comments.signals :synopsis: Signals sent by the comment module. -The comment app sends a series of :ref:`signals <topics-signals>` to allow for -comment moderation and similar activities. See :ref:`the introduction to signals -<topics-signals>` for information about how to register for and receive these +The comment app sends a series of :doc:`signals </topics/signals>` to allow for +comment moderation and similar activities. See :doc:`the introduction to signals +</topics/signals>` for information about how to register for and receive these signals. comment_will_be_posted diff --git a/docs/ref/contrib/comments/upgrade.txt b/docs/ref/contrib/comments/upgrade.txt index dc9bff868c..3d6b5af668 100644 --- a/docs/ref/contrib/comments/upgrade.txt +++ b/docs/ref/contrib/comments/upgrade.txt @@ -1,5 +1,3 @@ -.. _ref-contrib-comments-upgrade: - =============================================== Upgrading from Django's previous comment system =============================================== @@ -11,8 +9,8 @@ The main changes from the old system are: * This new system is documented. - * It uses modern Django features like :ref:`forms <topics-forms-index>` and - :ref:`modelforms <topics-forms-modelforms>`. + * It uses modern Django features like :doc:`forms </topics/forms/index>` and + :doc:`modelforms </topics/forms/modelforms>`. * It has a single ``Comment`` model instead of separate ``FreeComment`` and ``Comment`` models. @@ -42,7 +40,7 @@ The data models for Django's comment system have changed, as have the table names. Before you transfer your existing data into the new comments system, make sure that you have installed the new comments system as explained in the -:ref:`quick start guide <ref-contrib-comments-index>`. +:doc:`quick start guide </ref/contrib/comments/index>`. This will ensure that the new tables have been properly created. To transfer your data into the new comments system, you'll need to directly diff --git a/docs/ref/contrib/contenttypes.txt b/docs/ref/contrib/contenttypes.txt index 3085bf3ee9..b6956512ad 100644 --- a/docs/ref/contrib/contenttypes.txt +++ b/docs/ref/contrib/contenttypes.txt @@ -1,5 +1,3 @@ -.. _ref-contrib-contenttypes: - ========================== The contenttypes framework ========================== @@ -114,7 +112,7 @@ Methods on ``ContentType`` instances Takes a set of valid :ref:`lookup arguments <field-lookups-intro>` for the model the :class:`~django.contrib.contenttypes.models.ContentType` - represents, and does :ref:`a get() lookup <get-kwargs>` on that model, + represents, and does :lookup:`a get() lookup <get>` on that model, returning the corresponding object. .. method:: models.ContentType.model_class() @@ -324,15 +322,19 @@ same types of lookups manually:: ... object_id=b.id) [<TaggedItem: django>, <TaggedItem: python>] -Note that if the model with a :class:`~django.contrib.contenttypes.generic.GenericForeignKey` -that you're referring to uses a non-default value for ``ct_field`` or ``fk_field`` -(e.g. the :mod:`django.contrib.comments` app uses ``ct_field="object_pk"``), -you'll need to pass ``content_type_field`` and ``object_id_field`` to -:class:`~django.contrib.contenttypes.generic.GenericRelation`.:: +Note that if the model in a +:class:`~django.contrib.contenttypes.generic.GenericRelation` uses a +non-default value for ``ct_field`` or ``fk_field`` in its +:class:`~django.contrib.contenttypes.generic.GenericForeignKey` (e.g. the +:mod:`django.contrib.comments` app uses ``ct_field="object_pk"``), +you'll need to set ``content_type_field`` and/or ``object_id_field`` in +the :class:`~django.contrib.contenttypes.generic.GenericRelation` to +match the ``ct_field`` and ``fk_field``, respectively, in the +:class:`~django.contrib.contenttypes.generic.GenericForeignKey`:: - comments = generic.GenericRelation(Comment, content_type_field="content_type", object_id_field="object_pk") + comments = generic.GenericRelation(Comment, object_id_field="object_pk") -Note that if you delete an object that has a +Note also, that if you delete an object that has a :class:`~django.contrib.contenttypes.generic.GenericRelation`, any objects which have a :class:`~django.contrib.contenttypes.generic.GenericForeignKey` pointing at it will be deleted as well. In the example above, this means that @@ -342,7 +344,7 @@ it would be deleted at the same time. Generic relations and aggregation --------------------------------- -:ref:`Django's database aggregation API <topics-db-aggregation>` +:doc:`Django's database aggregation API </topics/db/aggregation>` doesn't work with a :class:`~django.contrib.contenttypes.generic.GenericRelation`. For example, you might be tempted to try something like:: @@ -361,14 +363,14 @@ Generic relations in forms and admin :class:`~django.contrib.contenttypes.generic.GenericInlineFormSet` and :class:`~django.contrib.contenttypes.generic.GenericInlineModelAdmin`. This enables the use of generic relations in forms and the admin. See the -:ref:`model formset <topics-forms-modelforms>` and -:ref:`admin <ref-contrib-admin>` documentation for more information. +:doc:`model formset </topics/forms/modelforms>` and +:doc:`admin </ref/contrib/admin/index>` documentation for more information. .. class:: generic.GenericInlineModelAdmin The :class:`~django.contrib.contenttypes.generic.GenericInlineModelAdmin` class inherits all properties from an - :class:`~django.contrib.admin.options.InlineModelAdmin` class. However, + :class:`~django.contrib.admin.InlineModelAdmin` class. However, it adds a couple of its own for working with the generic relation: .. attribute:: generic.GenericInlineModelAdmin.ct_field diff --git a/docs/ref/contrib/csrf.txt b/docs/ref/contrib/csrf.txt index d8a944b10a..c32dd73986 100644 --- a/docs/ref/contrib/csrf.txt +++ b/docs/ref/contrib/csrf.txt @@ -1,5 +1,3 @@ -.. _ref-contrib-csrf: - ===================================== Cross Site Request Forgery protection ===================================== @@ -400,6 +398,13 @@ set a flag on requests which relaxes the middleware and the ``csrf_protect`` decorator so that they no longer rejects requests. In every other respect (e.g. sending cookies etc.), they behave the same. +If, for some reason, you *want* the test client to perform CSRF +checks, you can create an instance of the test client that enforces +CSRF checks:: + + >>> from django.test import Client + >>> csrf_client = Client(enforce_csrf_checks=True) + Limitations =========== diff --git a/docs/ref/contrib/databrowse.txt b/docs/ref/contrib/databrowse.txt index d3536d150c..33c8228520 100644 --- a/docs/ref/contrib/databrowse.txt +++ b/docs/ref/contrib/databrowse.txt @@ -1,5 +1,3 @@ -.. _ref-contrib-databrowse: - ========== Databrowse ========== @@ -49,8 +47,8 @@ How to use Databrowse Note that you should register the model *classes*, not instances. It doesn't matter where you put this, as long as it gets executed at some - point. A good place for it is in your :ref:`URLconf file - <topics-http-urls>` (``urls.py``). + point. A good place for it is in your :doc:`URLconf file + </topics/http/urls>` (``urls.py``). 3. Change your URLconf to import the :mod:`~django.contrib.databrowse` module:: @@ -73,20 +71,20 @@ code. Simply add the following import to your URLconf:: from django.contrib.auth.decorators import login_required -Then modify the :ref:`URLconf <topics-http-urls>` so that the +Then modify the :doc:`URLconf </topics/http/urls>` so that the :func:`databrowse.site.root` view is decorated with :func:`django.contrib.auth.decorators.login_required`:: (r'^databrowse/(.*)', login_required(databrowse.site.root)), -If you haven't already added support for user logins to your :ref:`URLconf -<topics-http-urls>`, as described in the :ref:`user authentication docs -<ref-contrib-auth>`, then you will need to do so now with the following +If you haven't already added support for user logins to your :doc:`URLconf +</topics/http/urls>`, as described in the :doc:`user authentication docs +</ref/contrib/auth>`, then you will need to do so now with the following mapping:: (r'^accounts/login/$', 'django.contrib.auth.views.login'), The final step is to create the login form required by :func:`django.contrib.auth.views.login`. The -:ref:`user authentication docs <ref-contrib-auth>` provide full details and a +:doc:`user authentication docs </ref/contrib/auth>` provide full details and a sample template that can be used for this purpose. diff --git a/docs/ref/contrib/flatpages.txt b/docs/ref/contrib/flatpages.txt index 8c7f2781d0..ce6fdfcd1c 100644 --- a/docs/ref/contrib/flatpages.txt +++ b/docs/ref/contrib/flatpages.txt @@ -1,5 +1,3 @@ -.. _ref-contrib-flatpages: - ================= The flatpages app ================= @@ -21,7 +19,7 @@ template. It can be associated with one, or multiple, sites. .. versionadded:: 1.0 -The content field may optionally be left blank if you prefer to put your +The content field may optionally be left blank if you prefer to put your content in a custom template. Here are some examples of flatpages on Django-powered sites: @@ -37,20 +35,20 @@ To install the flatpages app, follow these steps: 1. Install the :mod:`sites framework <django.contrib.sites>` by adding ``'django.contrib.sites'`` to your :setting:`INSTALLED_APPS` setting, if it's not already in there. - + Also make sure you've correctly set :setting:`SITE_ID` to the ID of the site the settings file represents. This will usually be ``1`` (i.e. ``SITE_ID = 1``, but if you're using the sites framework to manage multiple sites, it could be the ID of a different site. - + 2. Add ``'django.contrib.flatpages'`` to your :setting:`INSTALLED_APPS` setting. - + 3. Add ``'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware'`` to your :setting:`MIDDLEWARE_CLASSES` setting. - + 4. Run the command :djadmin:`manage.py syncdb <syncdb>`. - + How it works ============ @@ -69,7 +67,7 @@ If it finds a match, it follows this algorithm: * If the flatpage has a custom template, it loads that template. Otherwise, it loads the template :file:`flatpages/default.html`. - + * It passes that template a single context variable, :data:`flatpage`, which is the flatpage object. It uses :class:`~django.template.context.RequestContext` in rendering the @@ -92,11 +90,11 @@ Note that the order of :setting:`MIDDLEWARE_CLASSES` matters. Generally, you can put :class:`~django.contrib.flatpages.middleware.FlatpageFallbackMiddleware` at the end of the list, because it's a last resort. -For more on middleware, read the :ref:`middleware docs -<topics-http-middleware>`. +For more on middleware, read the :doc:`middleware docs +</topics/http/middleware>`. .. admonition:: Ensure that your 404 template works - + Note that the :class:`~django.contrib.flatpages.middleware.FlatpageFallbackMiddleware` only steps in once another view has successfully produced a 404 response. @@ -124,9 +122,9 @@ Via the Python API .. class:: models.FlatPage Flatpages are represented by a standard - :ref:`Django model <topics-db-models>`, + :doc:`Django model </topics/db/models>`, which lives in `django/contrib/flatpages/models.py`_. You can access - flatpage objects via the :ref:`Django database API <topics-db-queries>`. + flatpage objects via the :doc:`Django database API </topics/db/queries>`. .. _django/contrib/flatpages/models.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/flatpages/models.py @@ -167,3 +165,64 @@ Since you're already entering raw HTML into the admin page for a flatpage, both ``flatpage.title`` and ``flatpage.content`` are marked as **not** requiring :ref:`automatic HTML escaping <automatic-html-escaping>` in the template. + +Getting a list of :class:`~django.contrib.flatpages.models.Flatpage` objects in your templates +============================================================================================== + +.. versionadded:: 1.3 + +The flatpages app provides a template tag that allows you to iterate +over all of the available flatpages on the :ref:`current site +<hooking-into-current-site-from-views>`. + +Like all custom template tags, you'll need to :ref:`load its custom +tag library <loading-custom-template-libraries>` before you can use +it. After loading the library, you can retrieve all current flatpages +via the :ttag:`get_flatpages` tag: + +.. code-block:: html+django + + {% load flatpages %} + {% get_flatpages as flatpages %} + <ul> + {% for page in flatpages %} + <li><a href="{{ page.url }}">{{ page.title }}</a></li> + {% endfor %} + </ul> + +.. templatetag:: get_flatpages + +Displaying ``registration_required`` flatpages +---------------------------------------------- + +By default, the :ttag:`get_flatpages` templatetag will only show +flatpages that are marked :attr:`registration_required`\=False. If you +want to display registration-protected flatpages, you need to specify +an authenticated user using a``for`` clause. + +For example: + +.. code-block:: html+django + + {% get_flatpages for someuser as about_pages %} + +If you provide an anonymous user, :ttag:`get_flatpages` will behave +the same as if you hadn't provided a user -- i.e., it will only show you +public flatpages. + +Limiting flatpages by base URL +------------------------------ + +An optional argument, ``starts_with``, can be applied to limit the +returned pages to those beginning with a particular base URL. This +argument may be passed as a string, or as a variable to be resolved +from the context. + +For example: + +.. code-block:: html+django + + {% get_flatpages '/about/' as about_pages %} + {% get_flatpages about_prefix as about_pages %} + {% get_flatpages '/about/' for someuser as about_pages %} + diff --git a/docs/ref/contrib/formtools/form-preview.txt b/docs/ref/contrib/formtools/form-preview.txt index ece69067ee..a2cbea704a 100644 --- a/docs/ref/contrib/formtools/form-preview.txt +++ b/docs/ref/contrib/formtools/form-preview.txt @@ -1,5 +1,3 @@ -.. _ref-contrib-formtools-form-preview: - ============ Form preview ============ diff --git a/docs/ref/contrib/formtools/form-wizard.txt b/docs/ref/contrib/formtools/form-wizard.txt index 5ef862ce3d..ab7b4829c9 100644 --- a/docs/ref/contrib/formtools/form-wizard.txt +++ b/docs/ref/contrib/formtools/form-wizard.txt @@ -1,5 +1,3 @@ -.. _ref-contrib-formtools-form-wizard: - =========== Form wizard =========== @@ -10,7 +8,7 @@ Form wizard .. versionadded:: 1.0 Django comes with an optional "form wizard" application that splits -:ref:`forms <topics-forms-index>` across multiple Web pages. It maintains +:doc:`forms </topics/forms/index>` across multiple Web pages. It maintains state in hashed HTML :samp:`<input type="hidden">` fields, and the data isn't processed server-side until the final form is submitted. @@ -65,8 +63,8 @@ Defining ``Form`` classes The first step in creating a form wizard is to create the :class:`~django.forms.Form` classes. These should be standard -:class:`django.forms.Form` classes, covered in the :ref:`forms documentation -<topics-forms-index>`. These classes can live anywhere in your codebase, but +:class:`django.forms.Form` classes, covered in the :doc:`forms documentation +</topics/forms/index>`. These classes can live anywhere in your codebase, but convention is to put them in a file called :file:`forms.py` in your application. @@ -189,8 +187,10 @@ for the wizard to work properly. Hooking the wizard into a URLconf ================================= -Finally, give your new :class:`FormWizard` object a URL in ``urls.py``. The -wizard takes a list of your :class:`~django.forms.Form` objects as arguments:: +Finally, we need to specify which forms to use in the wizard, and then +deploy the new :class:`FormWizard` object a URL in ``urls.py``. The +wizard takes a list of your :class:`~django.forms.Form` objects as +arguments when you instantiate the Wizard:: from django.conf.urls.defaults import * from mysite.testapp.forms import ContactForm1, ContactForm2, ContactWizard diff --git a/docs/ref/contrib/formtools/index.txt b/docs/ref/contrib/formtools/index.txt index 92010a25db..f36470654a 100644 --- a/docs/ref/contrib/formtools/index.txt +++ b/docs/ref/contrib/formtools/index.txt @@ -1,5 +1,3 @@ -.. _ref-contrib-formtools-index: - django.contrib.formtools ======================== diff --git a/docs/ref/contrib/gis/admin.txt b/docs/ref/contrib/gis/admin.txt index df93c58504..011bb6b6bf 100644 --- a/docs/ref/contrib/gis/admin.txt +++ b/docs/ref/contrib/gis/admin.txt @@ -54,7 +54,7 @@ GeoDjango's admin site existing geometry fields in the admin. .. note:: - + This is different from adding the geometry field to :attr:`~django.contrib.admin.ModelAdmin.readonly_fields`, which will only display the WKT of the geometry. Setting diff --git a/docs/ref/contrib/gis/commands.txt b/docs/ref/contrib/gis/commands.txt index 2cb7f69887..3dd161ce1d 100644 --- a/docs/ref/contrib/gis/commands.txt +++ b/docs/ref/contrib/gis/commands.txt @@ -78,6 +78,6 @@ of using ``ogrinspect`` :ref:`in the tutorial <ogrinspect-intro>`. all applicable fields. .. django-admin-option:: --srid - + The SRID to use for the geometry field. If not set, ``ogrinspect`` attempts to automatically determine of the SRID of the data source. diff --git a/docs/ref/contrib/gis/create_template_postgis-1.4.sh b/docs/ref/contrib/gis/create_template_postgis-1.4.sh index 74a6ef98d2..57a1373f96 100755 --- a/docs/ref/contrib/gis/create_template_postgis-1.4.sh +++ b/docs/ref/contrib/gis/create_template_postgis-1.4.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -POSTGIS_SQL_PATH=`pg_config --sharedir`/contrib/postgis-1.4 +POSTGIS_SQL_PATH=`pg_config --sharedir`/contrib createdb -E UTF8 template_postgis # Create the template spatial database. createlang -d template_postgis plpgsql # Adding PLPGSQL language support. psql -d postgres -c "UPDATE pg_database SET datistemplate='true' WHERE datname='template_postgis';" diff --git a/docs/ref/contrib/gis/db-api.txt b/docs/ref/contrib/gis/db-api.txt index 6797ce2de0..fbced8e6e1 100644 --- a/docs/ref/contrib/gis/db-api.txt +++ b/docs/ref/contrib/gis/db-api.txt @@ -14,7 +14,7 @@ Spatial Backends .. versionadded:: 1.2 -In Django 1.2, support for :ref:`multiple databases <topics-db-multi-db>` was +In Django 1.2, support for :doc:`multiple databases </topics/db/multi-db>` was introduced. In order to support multiple databases, GeoDjango has segregated its functionality into full-fledged spatial database backends: @@ -26,7 +26,7 @@ its functionality into full-fledged spatial database backends: Database Settings Backwards-Compatibility ----------------------------------------- -In :ref:`Django 1.2 <releases-1.2>`, the way +In :doc:`Django 1.2 </releases/1.2>`, the way to :ref:`specify databases <specifying-databases>` in your settings was changed. The old database settings format (e.g., the ``DATABASE_*`` settings) is backwards compatible with GeoDjango, and will automatically use the @@ -60,7 +60,7 @@ MySQL's spatial extensions only support bounding box operations [``Contains``, ``Crosses``, ``Disjoint``, ``Intersects``, ``Overlaps``, ``Touches``, ``Within``] according to the specification. Those that are implemented return - the same result as the corresponding MBR-based functions. + the same result as the corresponding MBR-based functions. In other words, while spatial lookups such as :lookup:`contains <gis-contains>` are available in GeoDjango when using MySQL, the results returned are really @@ -92,8 +92,8 @@ model):: >>> z.save() Moreover, if the ``GEOSGeometry`` is in a different coordinate system (has a -different SRID value) than that of the field, then it will be implicitly -transformed into the SRID of the model's field, using the spatial database's +different SRID value) than that of the field, then it will be implicitly +transformed into the SRID of the model's field, using the spatial database's transform procedure:: >>> poly_3084 = GEOSGeometry('POLYGON(( 10 10, 10 20, 20 20, 20 15, 10 10))', srid=3084) # SRID 3084 is 'NAD83(HARN) / Texas Centric Lambert Conformal' @@ -133,17 +133,17 @@ For example:: >>> qs = Zipcode.objects.filter(poly__contains=pnt) In this case, ``poly`` is the geographic field, :lookup:`contains <gis-contains>` -is the spatial lookup type, and ``pnt`` is the parameter (which may be a +is the spatial lookup type, and ``pnt`` is the parameter (which may be a :class:`~django.contrib.gis.geos.GEOSGeometry` object or a string of GeoJSON , WKT, or HEXEWKB). -A complete reference can be found in the :ref:`spatial lookup reference +A complete reference can be found in the :ref:`spatial lookup reference <spatial-lookups>`. .. note:: - GeoDjango constructs spatial SQL with the :class:`GeoQuerySet`, a - subclass of :class:`~django.db.models.QuerySet`. The + GeoDjango constructs spatial SQL with the :class:`GeoQuerySet`, a + subclass of :class:`~django.db.models.QuerySet`. The :class:`GeoManager` instance attached to your model is what enables use of :class:`GeoQuerySet`. @@ -156,8 +156,8 @@ Introduction ------------ Distance calculations with spatial data is tricky because, unfortunately, the Earth is not flat. Some distance queries with fields in a geographic -coordinate system may have to be expressed differently because of -limitations in PostGIS. Please see the :ref:`selecting-an-srid` section +coordinate system may have to be expressed differently because of +limitations in PostGIS. Please see the :ref:`selecting-an-srid` section in the :ref:`ref-gis-model-api` documentation for more details. .. _distance-lookups-intro: @@ -181,7 +181,7 @@ The following distance lookups are available: Distance lookups take a tuple parameter comprising: -#. A geometry to base calculations from; and +#. A geometry to base calculations from; and #. A number or :class:`~django.contrib.gis.measure.Distance` object containing the distance. If a :class:`~django.contrib.gis.measure.Distance` object is used, @@ -191,8 +191,8 @@ to be in the units of the field. .. note:: - For users of PostGIS 1.4 and below, the routine ``ST_Distance_Sphere`` - is used by default for calculating distances on geographic coordinate systems + For users of PostGIS 1.4 and below, the routine ``ST_Distance_Sphere`` + is used by default for calculating distances on geographic coordinate systems (e.g., WGS84) -- which may only be called with point geometries [#fndistsphere14]_. Thus, geographic distance lookups on traditional PostGIS geometry columns are only allowed on :class:`PointField` model fields using a point for the @@ -212,24 +212,24 @@ to be in the units of the field. You can tell GeoDjango to use a geography column by setting ``geography=True`` in your field definition. -For example, let's say we have a ``SouthTexasCity`` model (from the -`GeoDjango distance tests`__ ) on a *projected* coordinate system valid for cities +For example, let's say we have a ``SouthTexasCity`` model (from the +`GeoDjango distance tests`__ ) on a *projected* coordinate system valid for cities in southern Texas:: from django.contrib.gis.db import models - + class SouthTexasCity(models.Model): name = models.CharField(max_length=30) - # A projected coordinate system (only valid for South Texas!) + # A projected coordinate system (only valid for South Texas!) # is used, units are in meters. - point = models.PointField(srid=32140) + point = models.PointField(srid=32140) objects = models.GeoManager() Then distance queries may be performed as follows:: >>> from django.contrib.gis.geos import * >>> from django.contrib.gis.measure import D # ``D`` is a shortcut for ``Distance`` - >>> from geoapp import SouthTexasCity + >>> from geoapp import SouthTexasCity # Distances will be calculated from this point, which does not have to be projected. >>> pnt = fromstr('POINT(-96.876369 29.905320)', srid=4326) # If numeric parameter, units of field (meters in this case) are assumed. @@ -294,7 +294,7 @@ Lookup Type PostGIS Oracle MySQL [#]_ SpatiaLite ``GeoQuerySet`` Methods ----------------------- The following table provides a summary of what :class:`GeoQuerySet` methods -are available on each spatial backend. Please note that MySQL does not +are available on each spatial backend. Please note that MySQL does not support any of these methods, and is thus excluded from the table. ==================================== ======= ====== ========== @@ -330,7 +330,7 @@ Method PostGIS Oracle SpatiaLite :meth:`GeoQuerySet.translate` X X :meth:`GeoQuerySet.union` X X X :meth:`GeoQuerySet.unionagg` X X X -==================================== ======= ====== ========== +==================================== ======= ====== ========== .. rubric:: Footnotes .. [#fnwkt] *See* Open Geospatial Consortium, Inc., `OpenGIS Simple Feature Specification For SQL <http://www.opengis.org/docs/99-049.pdf>`_, Document 99-049 (May 5, 1999), at Ch. 3.2.5, p. 3-11 (SQL Textual Representation of Geometry). diff --git a/docs/ref/contrib/gis/deployment.txt b/docs/ref/contrib/gis/deployment.txt index 2cfd367fac..fa7fe69267 100644 --- a/docs/ref/contrib/gis/deployment.txt +++ b/docs/ref/contrib/gis/deployment.txt @@ -6,15 +6,15 @@ Deploying GeoDjango GeoDjango uses the GDAL geospatial library which is not thread safe at this time. Thus, it is *highly* recommended - to not use threading when deploying -- in other words, use a + to not use threading when deploying -- in other words, use a an appropriate configuration of Apache or the prefork method when using FastCGI through another web server. Apache ====== -In this section there are some example ``VirtualHost`` directives for +In this section there are some example ``VirtualHost`` directives for when deploying using either ``mod_python`` or ``mod_wsgi``. At this -time, we recommend ``mod_wsgi``, as it is now officially recommended +time, we recommend ``mod_wsgi``, as it is now officially recommended way to deploy Django applications with Apache. Moreover, if ``mod_python`` is used, then a prefork version of Apache must also be used. As long as ``mod_wsgi`` is configured correctly, it does not @@ -23,7 +23,7 @@ matter whether the version of Apache is prefork or worker. .. note:: The ``Alias`` and ``Directory`` configurations in the the examples - below use an example path to a system-wide installation folder of Django. + below use an example path to a system-wide installation folder of Django. Substitute in an appropriate location, if necessary, as it may be different than the path on your system. @@ -36,7 +36,7 @@ Example:: WSGIDaemonProcess geodjango user=geo group=geo processes=5 threads=1 WSGIProcessGroup geodjango WSGIScriptAlias / /home/geo/geodjango/world.wsgi - + Alias /media/ "/usr/lib/python2.5/site-packages/django/contrib/admin/media/" <Directory "/usr/lib/python2.5/site-packages/django/contrib/admin/media/"> Order allow,deny @@ -44,25 +44,31 @@ Example:: Allow from all IndexOptions FancyIndexing </Directory> - + </VirtualHost> .. warning:: If the ``WSGIDaemonProcess`` attribute ``threads`` is not set to ``1``, then - Apache may crash when running your GeoDjango application. Increase the + Apache may crash when running your GeoDjango application. Increase the number of ``processes`` instead. For more information, please consult Django's -:ref:`mod_wsgi documentation <howto-deployment-modwsgi>`. +:doc:`mod_wsgi documentation </howto/deployment/modwsgi>`. ``mod_python`` -------------- +.. warning:: + Support for mod_python will be deprecated in a future release of Django. If + you are configuring a new deployment, you are strongly encouraged to + consider using :doc:`mod_wsgi </howto/deployment/modwsgi>` or any of the + other :doc:`supported backends </howto/deployment/index>`. + Example:: <VirtualHost *:80> - + <Location "/"> SetHandler mod_python PythonHandler django.core.handlers.modpython @@ -70,12 +76,12 @@ Example:: PythonDebug On PythonPath "['/var/www/apps'] + sys.path" </Location> - + Alias /media/ "/usr/lib/python2.5/site-packages/django/contrib/admin/media/" <Location "/media"> SetHandler None </Location> - + </VirtualHost> .. warning:: @@ -84,7 +90,7 @@ Example:: else your GeoDjango application may crash Apache. For more information, please consult Django's -:ref:`mod_python documentation <howto-deployment-modpython>`. +:doc:`mod_python documentation </howto/deployment/modpython>`. Lighttpd ======== diff --git a/docs/ref/contrib/gis/feeds.txt b/docs/ref/contrib/gis/feeds.txt index bb9c12ae5d..7c3a2d011c 100644 --- a/docs/ref/contrib/gis/feeds.txt +++ b/docs/ref/contrib/gis/feeds.txt @@ -1,5 +1,3 @@ -.. _ref-gis-feeds: - ================ Geographic Feeds ================ @@ -8,10 +6,10 @@ Geographic Feeds :synopsis: GeoDjango's framework for generating spatial feeds. GeoDjango has its own :class:`Feed` subclass that may embed location information -in RSS/Atom feeds formatted according to either the `Simple GeoRSS`__ or +in RSS/Atom feeds formatted according to either the `Simple GeoRSS`__ or `W3C Geo`_ standards. Because GeoDjango's syndication API is a superset of -Django's, please consult `Django's syndication documentation <ref-contrib-syndication>` -for details on general usage. +Django's, please consult :doc:`Django's syndication documentation +</ref/contrib/syndication>` for details on general usage. .. _W3C Geo: http://www.w3.org/2003/01/geo/ @@ -28,7 +26,7 @@ API Reference .. class:: Feed - In addition to methods provided by + In addition to methods provided by the :class:`django.contrib.syndication.feeds.Feed` base class, GeoDjango's ``Feed`` class provides the following overrides. Note that these overrides may be done in multiple ways:: diff --git a/docs/ref/contrib/gis/geoquerysets.txt b/docs/ref/contrib/gis/geoquerysets.txt index c413ff4157..69f0c02e86 100644 --- a/docs/ref/contrib/gis/geoquerysets.txt +++ b/docs/ref/contrib/gis/geoquerysets.txt @@ -14,12 +14,12 @@ GeoQuerySet API Reference Spatial Lookups =============== -Just like when using the the :ref:`queryset-api`, interaction +Just like when using the the :ref:`queryset-api`, interaction with ``GeoQuerySet`` by :ref:`chaining filters <chaining-filters>`. Instead of the regular Django :ref:`field-lookups`, the spatial lookups in this section are available for :class:`GeometryField`. -For an introduction, see the :ref:`spatial lookups introduction +For an introduction, see the :ref:`spatial lookups introduction <spatial-lookups-intro>`. For an overview of what lookups are compatible with a particular spatial backend, refer to the :ref:`spatial lookup compatibility table <spatial-lookup-compatibility>`. @@ -31,7 +31,7 @@ bbcontains *Availability*: PostGIS, MySQL, SpatiaLite -Tests if the geometry field's bounding box completely contains the lookup +Tests if the geometry field's bounding box completely contains the lookup geometry's bounding box. Example:: @@ -53,7 +53,7 @@ bboverlaps *Availability*: PostGIS, MySQL, SpatiaLite -Tests if the geometry field's bounding box overlaps the lookup geometry's +Tests if the geometry field's bounding box overlaps the lookup geometry's bounding box. Example:: @@ -277,9 +277,9 @@ the values given in the given pattern. This lookup requires a tuple parameter, PostGIS & SpatiaLite ~~~~~~~~~~~~~~~~~~~~ -On these spatial backends the intersection pattern is a string comprising -nine characters, which define intersections between the interior, boundary, -and exterior of the geometry field and the lookup geometry. +On these spatial backends the intersection pattern is a string comprising +nine characters, which define intersections between the interior, boundary, +and exterior of the geometry field and the lookup geometry. The intersection pattern matrix may only use the following characters: ``1``, ``2``, ``T``, ``F``, or ``*``. This lookup type allows users to "fine tune" a specific geometric relationship consistent with the DE-9IM model. [#fnde9im]_ @@ -302,7 +302,7 @@ Oracle ~~~~~~ Here the relation pattern is compreised at least one of the nine relation -strings: ``TOUCH``, ``OVERLAPBDYDISJOINT``, ``OVERLAPBDYINTERSECT``, +strings: ``TOUCH``, ``OVERLAPBDYDISJOINT``, ``OVERLAPBDYINTERSECT``, ``EQUAL``, ``INSIDE``, ``COVEREDBY``, ``CONTAINS``, ``COVERS``, ``ON``, and ``ANYINTERACT``. Multiple strings may be combined with the logical Boolean operator OR, for example, ``'inside+touch'``. [#fnsdorelate]_ The relation @@ -312,7 +312,7 @@ Example:: Zipcode.objects.filter(poly__relate(geom, 'anyinteract')) -Oracle SQL equivalent:: +Oracle SQL equivalent:: SELECT ... WHERE SDO_RELATE(poly, geom, 'anyinteract') @@ -403,7 +403,7 @@ overlaps_left *Availability*: PostGIS -Tests if the geometry field's bounding box overlaps or is to the left of the lookup +Tests if the geometry field's bounding box overlaps or is to the left of the lookup geometry's bounding box. Example:: @@ -422,7 +422,7 @@ overlaps_right *Availability*: PostGIS -Tests if the geometry field's bounding box overlaps or is to the right of the lookup +Tests if the geometry field's bounding box overlaps or is to the right of the lookup geometry's bounding box. Example:: @@ -440,7 +440,7 @@ overlaps_above *Availability*: PostGIS -Tests if the geometry field's bounding box overlaps or is above the lookup +Tests if the geometry field's bounding box overlaps or is above the lookup geometry's bounding box. Example:: @@ -458,7 +458,7 @@ overlaps_below *Availability*: PostGIS -Tests if the geometry field's bounding box overlaps or is below the lookup +Tests if the geometry field's bounding box overlaps or is below the lookup geometry's bounding box. Example:: @@ -476,7 +476,7 @@ strictly_above *Availability*: PostGIS -Tests if the geometry field's bounding box is strictly above the lookup +Tests if the geometry field's bounding box is strictly above the lookup geometry's bounding box. Example:: @@ -494,7 +494,7 @@ strictly_below *Availability*: PostGIS -Tests if the geometry field's bounding box is strictly above the lookup +Tests if the geometry field's bounding box is strictly above the lookup geometry's bounding box. Example:: @@ -662,7 +662,7 @@ Keyword Argument Description ===================== ===================================================== ``field_name`` By default, ``GeoQuerySet`` methods use the first geographic field encountered in the model. This - keyword should be used to specify another + keyword should be used to specify another geographic field (e.g., ``field_name='point2'``) when there are multiple geographic fields in a model. @@ -670,7 +670,7 @@ Keyword Argument Description used on geometry fields in models that are related via a ``ForeignKey`` relation (e.g., ``field_name='related__point'``). - + ``model_att`` By default, ``GeoQuerySet`` methods typically attach their output in an attribute with the same name as the ``GeoQuerySet`` method. Setting this keyword @@ -679,12 +679,12 @@ Keyword Argument Description ``qs = Zipcode.objects.centroid(model_att='c')`` will attach the centroid of the ``Zipcode`` geometry field in a ``c`` attribute on every model rather than in a - ``centroid`` attribute. + ``centroid`` attribute. - This keyword is required if - a method name clashes with an existing - ``GeoQuerySet`` method -- if you wanted to use the - ``area()`` method on model with a ``PolygonField`` + This keyword is required if + a method name clashes with an existing + ``GeoQuerySet`` method -- if you wanted to use the + ``area()`` method on model with a ``PolygonField`` named ``area``, for example. ===================== ===================================================== @@ -705,12 +705,12 @@ each element of this GeoQuerySet. .. method:: GeoQuerySet.distance(geom, **kwargs) -This method takes a geometry as a parameter, and attaches a ``distance`` -attribute to every model in the returned queryset that contains the +This method takes a geometry as a parameter, and attaches a ``distance`` +attribute to every model in the returned queryset that contains the distance (as a :class:`~django.contrib.gis.measure.Distance` object) to the given geometry. -In the following example (taken from the `GeoDjango distance tests`__), -the distance from the `Tasmanian`__ city of Hobart to every other +In the following example (taken from the `GeoDjango distance tests`__), +the distance from the `Tasmanian`__ city of Hobart to every other :class:`PointField` in the ``AustraliaCity`` queryset is calculated:: >>> pnt = AustraliaCity.objects.get(name='Hobart').point @@ -732,7 +732,7 @@ the distance from the `Tasmanian`__ city of Hobart to every other Because the ``distance`` attribute is a :class:`~django.contrib.gis.measure.Distance` object, you can easily express the value in the units of your choice. For example, ``city.distance.mi`` is - the distance value in miles and ``city.distance.km`` is the distance value + the distance value in miles and ``city.distance.km`` is the distance value in kilometers. See the :ref:`ref-measure` for usage details and the list of :ref:`supported_units`. @@ -867,9 +867,9 @@ then 4326 (WGS84) is used by default. geometry is placed on the models. .. note:: - - What spatial reference system an integer SRID corresponds to may depend on - the spatial database used. In other words, the SRID numbers used for Oracle + + What spatial reference system an integer SRID corresponds to may depend on + the spatial database used. In other words, the SRID numbers used for Oracle are not necessarily the same as those used by PostGIS. Example:: @@ -903,7 +903,7 @@ to each element of the ``GeoQuerySet`` that is the result of the operation. .. method:: GeoQuerySet.difference(geom) Returns the spatial difference of the geographic field with the given -geometry in a ``difference`` attribute on each element of the +geometry in a ``difference`` attribute on each element of the ``GeoQuerySet``. @@ -913,7 +913,7 @@ geometry in a ``difference`` attribute on each element of the .. method:: GeoQuerySet.intersection(geom) Returns the spatial intersection of the geographic field with the -given geometry in an ``intersection`` attribute on each element of the +given geometry in an ``intersection`` attribute on each element of the ``GeoQuerySet``. ``sym_difference`` @@ -937,7 +937,7 @@ geometry in an ``union`` attribute on each element of the Geometry Output --------------- -The following ``GeoQuerySet`` methods will return an attribute that has the value +The following ``GeoQuerySet`` methods will return an attribute that has the value of the geometry field in each model converted to the requested output format. ``geohash`` @@ -967,8 +967,8 @@ Attaches a ``geojson`` attribute to every model in the queryset that contains th ===================== ===================================================== Keyword Argument Description ===================== ===================================================== -``precision`` It may be used to specify the number of significant - digits for the coordinates in the GeoJSON +``precision`` It may be used to specify the number of significant + digits for the coordinates in the GeoJSON representation -- the default value is 8. ``crs`` Set this to ``True`` if you want the coordinate @@ -988,8 +988,8 @@ __ http://geojson.org/ *Availability*: PostGIS, Oracle -Attaches a ``gml`` attribute to every model in the queryset that contains the -`Geographic Markup Language (GML)`__ representation of the geometry. +Attaches a ``gml`` attribute to every model in the queryset that contains the +`Geographic Markup Language (GML)`__ representation of the geometry. Example:: @@ -1000,9 +1000,9 @@ Example:: ===================== ===================================================== Keyword Argument Description ===================== ===================================================== -``precision`` This keyword is for PostGIS only. It may be used - to specify the number of significant digits for the - coordinates in the GML representation -- the default +``precision`` This keyword is for PostGIS only. It may be used + to specify the number of significant digits for the + coordinates in the GML representation -- the default value is 8. ``version`` This keyword is for PostGIS only. It may be used to @@ -1019,9 +1019,9 @@ __ http://en.wikipedia.org/wiki/Geography_Markup_Language *Availability*: PostGIS -Attaches a ``kml`` attribute to every model in the queryset that contains the -`Keyhole Markup Language (KML)`__ representation of the geometry fields. It -should be noted that the contents of the KML are transformed to WGS84 if +Attaches a ``kml`` attribute to every model in the queryset that contains the +`Keyhole Markup Language (KML)`__ representation of the geometry fields. It +should be noted that the contents of the KML are transformed to WGS84 if necessary. Example:: @@ -1033,8 +1033,8 @@ Example:: ===================== ===================================================== Keyword Argument Description ===================== ===================================================== -``precision`` This keyword may be used to specify the number of - significant digits for the coordinates in the KML +``precision`` This keyword may be used to specify the number of + significant digits for the coordinates in the KML representation -- the default value is 8. ===================== ===================================================== @@ -1054,11 +1054,11 @@ the `Scalable Vector Graphics (SVG)`__ path data of the geometry fields. Keyword Argument Description ===================== ===================================================== ``relative`` If set to ``True``, the path data will be implemented - in terms of relative moves. Defaults to ``False``, + in terms of relative moves. Defaults to ``False``, meaning that absolute moves are used instead. -``precision`` This keyword may be used to specify the number of - significant digits for the coordinates in the SVG +``precision`` This keyword may be used to specify the number of + significant digits for the coordinates in the SVG representation -- the default value is 8. ===================== ===================================================== @@ -1129,7 +1129,7 @@ dissolving boundaries. *Availability*: PostGIS, Oracle -Returns the extent of the ``GeoQuerySet`` as a four-tuple, comprising the +Returns the extent of the ``GeoQuerySet`` as a four-tuple, comprising the lower left coordinate and the upper right coordinate. Example:: @@ -1163,7 +1163,7 @@ Example:: *Availability*: PostGIS -Returns a ``LineString`` constructed from the point field geometries in the +Returns a ``LineString`` constructed from the point field geometries in the ``GeoQuerySet``. Currently, ordering the queryset has no effect. Example:: @@ -1184,25 +1184,25 @@ use of ``unionagg`` is processor intensive and may take a significant amount of time on large querysets. .. note:: - + If the computation time for using this method is too expensive, consider using :meth:`GeoQuerySet.collect` instead. Example:: - + >>> u = Zipcode.objects.unionagg() # This may take a long time. >>> u = Zipcode.objects.filter(poly__within=bbox).unionagg() # A more sensible approach. ===================== ===================================================== Keyword Argument Description ===================== ===================================================== -``tolerance`` This keyword is for Oracle only. It is for the +``tolerance`` This keyword is for Oracle only. It is for the tolerance value used by the ``SDOAGGRTYPE`` - procedure; the `Oracle documentation`__ has more + procedure; the `Oracle documentation`__ has more details. ===================== ===================================================== -__ http://download.oracle.com/docs/html/B14255_01/sdo_intro.htm#sthref150 +__ http://download.oracle.com/docs/html/B14255_01/sdo_intro.htm#sthref150 Aggregate Functions ------------------- diff --git a/docs/ref/contrib/gis/install.txt b/docs/ref/contrib/gis/install.txt index 00d4e62e9f..ae36e167ae 100644 --- a/docs/ref/contrib/gis/install.txt +++ b/docs/ref/contrib/gis/install.txt @@ -13,7 +13,7 @@ In general, GeoDjango installation requires: 3. :ref:`geospatial_libs` Details for each of the requirements and installation instructions -are provided in the sections below. In addition, platform-specific +are provided in the sections below. In addition, platform-specific instructions are available for: * :ref:`macosx` @@ -23,10 +23,10 @@ instructions are available for: .. admonition:: Use the Source Because GeoDjango takes advantage of the latest in the open source geospatial - software technology, recent versions of the libraries are necessary. + software technology, recent versions of the libraries are necessary. If binary packages aren't available for your platform, :ref:`installation from source <build_from_source>` - may be required. When compiling the libraries from source, please follow the + may be required. When compiling the libraries from source, please follow the directions closely, especially if you're a beginner. Requirements @@ -37,8 +37,8 @@ Requirements Python 2.4+ ----------- Because of heavy use of the decorator syntax, Python 2.4 is minimum -version supported by GeoDjango. Python 2.5+ is recommended because the -`ctypes`__ module comes included; otherwise, 2.4 users will need to +version supported by GeoDjango. Python 2.5+ is recommended because the +`ctypes`__ module comes included; otherwise, 2.4 users will need to `download and install ctypes`__. __ http://docs.python.org/lib/module-ctypes.html @@ -50,18 +50,18 @@ Django ------ Because GeoDjango is included with Django, please refer to Django's -:ref:`installation instructions <intro-install>` for details on how to install. +:doc:`installation instructions </intro/install>` for details on how to install. .. _spatial_database: Spatial Database ---------------- -PostgreSQL (with PostGIS), MySQL, Oracle, and SQLite (with SpatiaLite) are +PostgreSQL (with PostGIS), MySQL, Oracle, and SQLite (with SpatiaLite) are the spatial databases currently supported. .. note:: - PostGIS is recommended, because it is the most mature and feature-rich + PostGIS is recommended, because it is the most mature and feature-rich open source spatial database. The geospatial libraries required for a GeoDjango installation depends @@ -81,7 +81,7 @@ SQLite GEOS, GDAL, PROJ.4, SpatiaLite 3.6.+ Requires Geospatial Libraries -------------------- -GeoDjango uses and/or provides interfaces for the the following open source +GeoDjango uses and/or provides interfaces for the the following open source geospatial libraries: ======================== ==================================== ================================ ========================== @@ -117,7 +117,7 @@ Building from Source ==================== When installing from source on UNIX and GNU/Linux systems, please follow -the installation instructions carefully, and install the libraries in the +the installation instructions carefully, and install the libraries in the given order. If using MySQL or Oracle as the spatial database, only GEOS is required. @@ -147,13 +147,13 @@ internal geometry representation used by GeoDjango (it's behind the "lazy" geometries). Specifically, the C API library is called (e.g., ``libgeos_c.so``) directly from Python using ctypes. -First, download GEOS 3.2 from the refractions website and untar the source +First, download GEOS 3.2 from the refractions website and untar the source archive:: $ wget http://download.osgeo.org/geos/geos-3.2.2.tar.bz2 $ tar xjf geos-3.2.2.tar.bz2 -Next, change into the directory where GEOS was unpacked, run the configure +Next, change into the directory where GEOS was unpacked, run the configure script, compile, and install:: $ cd geos-3.2.2 @@ -172,7 +172,7 @@ When GeoDjango can't find GEOS, this error is raised:: ImportError: Could not find the GEOS library (tried "geos_c"). Try setting GEOS_LIBRARY_PATH in your settings. -The most common solution is to properly configure your :ref:`libsettings` *or* set +The most common solution is to properly configure your :ref:`libsettings` *or* set :ref:`geoslibrarypath` in your settings. If using a binary package of GEOS (e.g., on Ubuntu 8.10), you may need to :ref:`binutils`. @@ -191,7 +191,7 @@ C library. For example:: .. note:: - The setting must be the *full* path to the **C** shared library; in + The setting must be the *full* path to the **C** shared library; in other words you want to use ``libgeos_c.so``, not ``libgeos.so``. .. _proj4: @@ -199,7 +199,7 @@ C library. For example:: PROJ.4 ------ -`PROJ.4`_ is a library for converting geospatial data to different coordinate +`PROJ.4`_ is a library for converting geospatial data to different coordinate reference systems. First, download the PROJ.4 source code and datum shifting files [#]_:: @@ -228,12 +228,12 @@ PostGIS ------- `PostGIS`__ adds geographic object support to PostgreSQL, turning it -into a spatial database. :ref:`geosbuild` and :ref:`proj4` should be +into a spatial database. :ref:`geosbuild` and :ref:`proj4` should be installed prior to building PostGIS. .. note:: - The `psycopg2`_ module is required for use as the database adaptor + The `psycopg2`_ module is required for use as the database adaptor when using GeoDjango with PostGIS. .. _psycopg2: http://initd.org/projects/psycopg2 @@ -256,7 +256,7 @@ Finally, make and install:: .. note:: - GeoDjango does not automatically create a spatial database. Please + GeoDjango does not automatically create a spatial database. Please consult the section on :ref:`spatialdb_template` for more information. __ http://postgis.refractions.net/ @@ -267,7 +267,7 @@ GDAL ---- `GDAL`__ is an excellent open source geospatial library that has support for -reading most vector and raster spatial data formats. Currently, GeoDjango only +reading most vector and raster spatial data formats. Currently, GeoDjango only supports :ref:`GDAL's vector data <ref-gdal>` capabilities [#]_. :ref:`geosbuild` and :ref:`proj4` should be installed prior to building GDAL. @@ -287,11 +287,11 @@ Configure, make and install:: .. note:: Because GeoDjango has it's own Python interface, the preceding instructions - do not build GDAL's own Python bindings. The bindings may be built by + do not build GDAL's own Python bindings. The bindings may be built by adding the ``--with-python`` flag when running ``configure``. See - `GDAL/OGR In Python`__ for more information on GDAL's bindings. + `GDAL/OGR In Python`__ for more information on GDAL's bindings. -If you have any problems, please see the troubleshooting section below for +If you have any problems, please see the troubleshooting section below for suggestions and solutions. __ http://trac.osgeo.org/gdal/ @@ -312,7 +312,7 @@ will be false:: >>> gdal.HAS_GDAL False -The solution is to properly configure your :ref:`libsettings` *or* set +The solution is to properly configure your :ref:`libsettings` *or* set :ref:`gdallibrarypath` in your settings. .. _gdallibrarypath: @@ -332,22 +332,22 @@ the GDAL library. For example:: Can't find GDAL data files (``GDAL_DATA``) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -When installed from source, GDAL versions 1.5.1 and below have an autoconf bug -that places data in the wrong location. [#]_ This can lead to error messages +When installed from source, GDAL versions 1.5.1 and below have an autoconf bug +that places data in the wrong location. [#]_ This can lead to error messages like this:: ERROR 4: Unable to open EPSG support file gcs.csv. ... OGRException: OGR failure. -The solution is to set the ``GDAL_DATA`` environment variable to the location of the -GDAL data files before invoking Python (typically ``/usr/local/share``; use +The solution is to set the ``GDAL_DATA`` environment variable to the location of the +GDAL data files before invoking Python (typically ``/usr/local/share``; use ``gdal-config --datadir`` to find out). For example:: $ export GDAL_DATA=`gdal-config --datadir` $ python manage.py shell -If using Apache, you may need to add this environment variable to your configuration +If using Apache, you may need to add this environment variable to your configuration file:: SetEnv GDAL_DATA /usr/local/share @@ -363,13 +363,13 @@ SpatiaLite Mac OS X users should follow the instructions in the :ref:`kyngchaos` section, as it is much easier than building from source. -`SpatiaLite`__ adds spatial support to SQLite, turning it into a full-featured +`SpatiaLite`__ adds spatial support to SQLite, turning it into a full-featured spatial database. Because SpatiaLite has special requirements, it typically -requires SQLite and pysqlite2 (the Python SQLite DB-API adaptor) to be built from +requires SQLite and pysqlite2 (the Python SQLite DB-API adaptor) to be built from source. :ref:`geosbuild` and :ref:`proj4` should be installed prior to building SpatiaLite. -After installation is complete, don't forget to read the post-installation +After installation is complete, don't forget to read the post-installation docs on :ref:`create_spatialite_db`. __ http://www.gaia-gis.it/spatialite/index.html @@ -380,12 +380,12 @@ SQLite ^^^^^^ Typically, SQLite packages are not compiled to include the `R*Tree module`__ -- -thus it must be compiled from source. First download the latest amalgamation +thus it must be compiled from source. First download the latest amalgamation source archive from the `SQLite download page`__, and extract:: - $ wget http://www.sqlite.org/sqlite-amalgamation-3.6.22.tar.gz - $ tar xzf sqlite-amalgamation-3.6.22.tar.gz - $ cd sqlite-3.6.22 + $ wget http://sqlite.org/sqlite-amalgamation-3.6.23.1.tar.gz + $ tar xzf sqlite-amalgamation-3.6.23.1.tar.gz + $ cd sqlite-3.6.23.1 Next, run the ``configure`` script -- however the ``CFLAGS`` environment variable needs to be customized so that SQLite knows to build the R*Tree module:: @@ -398,7 +398,7 @@ needs to be customized so that SQLite knows to build the R*Tree module:: .. note:: If using Ubuntu, installing a newer SQLite from source can be very difficult - because it links to the existing ``libsqlite3.so`` in ``/usr/lib`` which + because it links to the existing ``libsqlite3.so`` in ``/usr/lib`` which many other packages depend on. Unfortunately, the best solution at this time is to overwrite the existing library by adding ``--prefix=/usr`` to the ``configure`` command. @@ -420,7 +420,7 @@ SpatiaLite library source and tools bundle from the `download page`__:: $ tar xzf spatialite-tools-2.3.1.tar.gz Prior to attempting to build, please read the important notes below to see if -customization of the ``configure`` command is necessary. If not, then run the +customization of the ``configure`` command is necessary. If not, then run the ``configure`` script, make, and install for the SpatiaLite library:: $ cd libspatialite-amalgamation-2.3.1 @@ -431,7 +431,7 @@ customization of the ``configure`` command is necessary. If not, then run the Finally, do the same for the SpatiaLite tools:: - $ cd spatialite-tools-2.3.1 + $ cd spatialite-tools-2.3.1 $ ./configure # May need to modified, see notes below. $ make $ sudo make install @@ -440,21 +440,18 @@ Finally, do the same for the SpatiaLite tools:: .. note:: If you've installed GEOS and PROJ.4 from binary packages, you will have to specify - their paths when running the ``configure`` scripts for *both* the library and the - tools (the configure scripts look, by default, in ``/usr/local``). For example, + their paths when running the ``configure`` scripts for *both* the library and the + tools (the configure scripts look, by default, in ``/usr/local``). For example, on Debian/Ubuntu distributions that have GEOS and PROJ.4 packages, the command would be:: - + $ ./configure --with-proj-include=/usr/include --with-proj-lib=/usr/lib --with-geos-include=/usr/include --with-geos-lib=/usr/lib .. note:: - For Mac OS X users building from source, the SpatiaLite library *and* tools - need to be linked into the existing ``iconv`` library. While this happens - automatically on Linux, the ``configure`` scripts need to know about the - specific location on Mac OS X (via modification of the ``CFLAGS`` and - ``LDFLAGS`` environment variables prior to configuration):: + For Mac OS X users building from source, the SpatiaLite library *and* tools + need to have their ``target`` configured:: - $ CFLAGS=-I/usr/include LDFLAGS="-L/usr/lib -liconv" ./configure + $ ./configure --target=macosx __ http://www.gaia-gis.it/spatialite/sources.html @@ -466,7 +463,7 @@ pysqlite2 Because SpatiaLite must be loaded as an external extension, it requires the ``enable_load_extension`` method, which is only available in versions 2.5+. Thus, download pysqlite2 2.6, and untar:: - + $ wget http://pysqlite.googlecode.com/files/pysqlite-2.6.0.tar.gz $ tar xzf pysqlite-2.6.0.tar.gz $ cd pysqlite-2.6.0 @@ -487,7 +484,7 @@ to look like the following:: ``define=SQLITE_OMIT_LOAD_EXTENSION`` flag and that the ``include_dirs`` and ``library_dirs`` settings are uncommented and set to the appropriate path if the SQLite header files and libraries are not in ``/usr/include`` - and ``/usr/lib``, respectively. + and ``/usr/lib``, respectively. After modifying ``setup.cfg`` appropriately, then run the ``setup.py`` script to build and install:: @@ -503,7 +500,7 @@ Creating a Spatial Database Template for PostGIS ------------------------------------------------ Creating a spatial database with PostGIS is different than normal because -additional SQL must be loaded to enable spatial functionality. Because of +additional SQL must be loaded to enable spatial functionality. Because of the steps in this process, it's better to create a database template that can be reused later. @@ -521,7 +518,7 @@ user. For example, you can use the following to become the ``postgres`` user:: version 1.5 uses ``<sharedir>/contrib/postgis-1.5/postgis.sql``. The example below assumes PostGIS 1.5, thus you may need to modify - ``POSTGIS_SQL_PATH`` and the name of the SQL file for the specific + ``POSTGIS_SQL_PATH`` and the name of the SQL file for the specific version of PostGIS you are using. Once you're a database super user, then you may execute the following commands @@ -601,7 +598,7 @@ __ http://www.gaia-gis.it/spatialite/resources.html Add ``django.contrib.gis`` to ``INSTALLED_APPS`` ------------------------------------------------ -Like other Django contrib applications, you will *only* need to add +Like other Django contrib applications, you will *only* need to add :mod:`django.contrib.gis` to :setting:`INSTALLED_APPS` in your settings. This is the so that ``gis`` templates can be located -- if not done, then features such as the geographic admin or KML sitemaps will not function properly. @@ -633,7 +630,7 @@ Invoke the Django shell from your project and execute the In Django 1.1 the name of this function is ``add_postgis_srs``. This adds an entry for the 900913 SRID to the ``spatial_ref_sys`` (or equivalent) -table, making it possible for the spatial database to transform coordinates in +table, making it possible for the spatial database to transform coordinates in this projection. You only need to execute this command *once* per spatial database. Troubleshooting @@ -643,8 +640,8 @@ If you can't find the solution to your problem here then participate in the community! You can: * Join the ``#geodjango`` IRC channel on FreeNode (may be accessed on the - web via `Mibbit`__). Please be patient and polite -- while you may not - get an immediate response, someone will attempt to answer your question + web via `Mibbit`__). Please be patient and polite -- while you may not + get an immediate response, someone will attempt to answer your question as soon as they see it. * Ask your question on the `GeoDjango`__ mailing list. * File a ticket on the `Django trac`__ if you think there's a bug. Make @@ -662,7 +659,7 @@ Library Environment Settings By far, the most common problem when installing GeoDjango is that the external shared libraries (e.g., for GEOS and GDAL) cannot be located. [#]_ -Typically, the cause of this problem is that the operating system isn't aware +Typically, the cause of this problem is that the operating system isn't aware of the directory where the libraries built from source were installed. In general, the library path may be set on a per-user basis by setting @@ -673,9 +670,9 @@ system. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ A user may set this environment variable to customize the library paths -they want to use. The typical library directory for software +they want to use. The typical library directory for software built from source is ``/usr/local/lib``. Thus, ``/usr/local/lib`` needs -to be included in the ``LD_LIBRARY_PATH`` variable. For example, the user +to be included in the ``LD_LIBRARY_PATH`` variable. For example, the user could place the following in their bash profile:: export LD_LIBRARY_PATH=/usr/local/lib @@ -685,7 +682,7 @@ Setting System Library Path On GNU/Linux systems, there is typically a file in ``/etc/ld.so.conf``, which may include additional paths from files in another directory, such as ``/etc/ld.so.conf.d``. -As the root user, add the custom library path (like ``/usr/local/lib``) on a +As the root user, add the custom library path (like ``/usr/local/lib``) on a new line in ``ld.so.conf``. This is *one* example of how to do so:: $ sudo echo /usr/local/lib >> /etc/ld.so.conf @@ -705,9 +702,9 @@ Install ``binutils`` GeoDjango uses the ``find_library`` function (from the ``ctypes.util`` Python module) to discover libraries. The ``find_library`` routine uses a program -called ``objdump`` (part of the ``binutils`` package) to verify a shared +called ``objdump`` (part of the ``binutils`` package) to verify a shared library on GNU/Linux systems. Thus, if ``binutils`` is not installed on your -Linux system then Python's ctypes may not be able to find your library even if +Linux system then Python's ctypes may not be able to find your library even if your library path is set correctly and geospatial libraries were built perfectly. The ``binutils`` package may be installed on Debian and Ubuntu systems using the @@ -738,10 +735,10 @@ several different options for installing GeoDjango. These options are: .. note:: Currently, the easiest and recommended approach for installing GeoDjango - on OS X is to use the KyngChaos packages. + on OS X is to use the KyngChaos packages. -This section also includes instructions for installing an upgraded version -of :ref:`macosx_python` from packages provided by the Python Software +This section also includes instructions for installing an upgraded version +of :ref:`macosx_python` from packages provided by the Python Software Foundation, however, this is not required. .. _macosx_python: @@ -750,8 +747,8 @@ Python ^^^^^^ Although OS X comes with Python installed, users can use framework -installers (`2.5`__ and `2.6`__ are available) provided by -the Python Software Foundation. An advantage to using the installer is +installers (`2.5`__ and `2.6`__ are available) provided by +the Python Software Foundation. An advantage to using the installer is that OS X's Python will remain "pristine" for internal operating system use. @@ -759,7 +756,7 @@ __ http://python.org/ftp/python/2.5.4/python-2.5.4-macosx.dmg __ http://python.org/ftp/python/2.6.2/python-2.6.2-macosx2009-04-16.dmg .. note:: - + You will need to modify the ``PATH`` environment variable in your ``.profile`` file so that the new version of Python is used when ``python`` is entered at the command-line:: @@ -771,15 +768,15 @@ __ http://python.org/ftp/python/2.6.2/python-2.6.2-macosx2009-04-16.dmg KyngChaos Packages ^^^^^^^^^^^^^^^^^^ -William Kyngesburye provides a number of `geospatial library binary packages`__ -that make it simple to get GeoDjango installed on OS X without compiling +William Kyngesburye provides a number of `geospatial library binary packages`__ +that make it simple to get GeoDjango installed on OS X without compiling them from source. However, the `Apple Developer Tools`_ are still necessary for compiling the Python database adapters :ref:`psycopg2_kyngchaos` (for PostGIS) -and :ref:`pysqlite2_kyngchaos` (for SpatiaLite). +and :ref:`pysqlite2_kyngchaos` (for SpatiaLite). .. note:: - SpatiaLite users should consult the :ref:`spatialite_kyngchaos` section + SpatiaLite users should consult the :ref:`spatialite_kyngchaos` section after installing the packages for additional instructions. Download the framework packages for: @@ -804,8 +801,8 @@ your ``.profile`` to be able to run the package programs from the command-line:: export PATH=/Library/Frameworks/GDAL.framework/Programs:$PATH export PATH=/usr/local/pgsql/bin:$PATH -__ http://www.kyngchaos.com/wiki/software:frameworks -__ http://www.kyngchaos.com/wiki/software:postgres +__ http://www.kyngchaos.com/software/frameworks +__ http://www.kyngchaos.com/software/postgres .. note:: @@ -837,7 +834,7 @@ described above, ``psycopg2`` may be installed using the following command:: pysqlite2 ~~~~~~~~~ -Follow the :ref:`pysqlite2` source install instructions, however, +Follow the :ref:`pysqlite2` source install instructions, however, when editing the ``setup.cfg`` use the following instead:: [build_ext] @@ -854,7 +851,7 @@ SpatiaLite When :ref:`create_spatialite_db`, the ``spatialite`` program is required. However, instead of attempting to compile the SpatiaLite tools from source, -download the `SpatiaLite Binaries`__ for OS X, and install ``spatialite`` in a +download the `SpatiaLite Binaries`__ for OS X, and install ``spatialite`` in a location available in your ``PATH``. For example:: $ curl -O http://www.gaia-gis.it/spatialite/spatialite-tools-osx-x86-2.3.1.tar.gz @@ -890,9 +887,9 @@ __ http://www.finkproject.org/ MacPorts ^^^^^^^^ -`MacPorts`__ may be used to install GeoDjango prerequisites on Macintosh +`MacPorts`__ may be used to install GeoDjango prerequisites on Macintosh computers running OS X. Because MacPorts still builds the software from source, -the `Apple Developer Tools`_ are required. +the `Apple Developer Tools`_ are required. Summary:: @@ -901,7 +898,7 @@ Summary:: $ sudo port install proj $ sudo port install postgis $ sudo port install gdal - $ sudo port install libgeoip + $ sudo port install libgeoip .. note:: @@ -932,9 +929,9 @@ Ubuntu 8.04 and lower ~~~~~~~~~~~~~~ -The 8.04 (and lower) versions of Ubuntu use GEOS v2.2.3 in their binary packages, -which is incompatible with GeoDjango. Thus, do *not* use the binary packages -for GEOS or PostGIS and build some prerequisites from source, per the instructions +The 8.04 (and lower) versions of Ubuntu use GEOS v2.2.3 in their binary packages, +which is incompatible with GeoDjango. Thus, do *not* use the binary packages +for GEOS or PostGIS and build some prerequisites from source, per the instructions in this document; however, it is okay to use the PostgreSQL binary packages. For more details, please see the Debian instructions for :ref:`etch` below. @@ -973,11 +970,11 @@ Optional packages to consider: * ``python-gdal`` for GDAL's own Python bindings -- includes interfaces for raster manipulation .. note:: - + The Ubuntu ``proj`` package does not come with the datum shifting files - installed, which will cause problems with the geographic admin because + installed, which will cause problems with the geographic admin because the ``null`` datum grid is not available for transforming geometries to the - spherical mercator projection. A solution is to download the + spherical mercator projection. A solution is to download the datum-shifting files, create the grid file, and install it yourself:: $ wget http://download.osgeo.org/proj/proj-datumgrid-1.4.tar.gz @@ -988,7 +985,7 @@ Optional packages to consider: $ sudo cp null /usr/share/proj Otherwise, the Ubuntu ``proj`` package is fine for general use as long as you - do not plan on doing any database transformation of geometries to the + do not plan on doing any database transformation of geometries to the Google projection (900913). .. note:: @@ -1035,14 +1032,14 @@ Optional packages: Source Packages ~~~~~~~~~~~~~~~ You will still have to install :ref:`geosbuild`, :ref:`proj4`, -:ref:`postgis`, and :ref:`gdalbuild` from source. Please follow the +:ref:`postgis`, and :ref:`gdalbuild` from source. Please follow the directions carefully. .. _lenny: 5.0 (Lenny) ^^^^^^^^^^^ -This version is comparable to Ubuntu :ref:`ibex`, so the command +This version is comparable to Ubuntu :ref:`ibex`, so the command is very similar:: $ sudo apt-get install binutils libgdal1-1.5.0 postgresql-8.3 postgresql-8.3-postgis postgresql-server-dev-8.3 python-psycopg2 python-setuptools @@ -1089,13 +1086,13 @@ Python ^^^^^^ First, download the `Python 2.6 installer`__ from the Python website. Next, -execute the installer and use defaults, e.g., keep 'Install for all users' +execute the installer and use defaults, e.g., keep 'Install for all users' checked and the installation path set as ``C:\Python26``. .. note:: You may already have a version of Python installed in ``C:\python`` as ESRI - products sometimes install a copy there. *You should still install a + products sometimes install a copy there. *You should still install a fresh version of Python 2.6.* __ http://python.org/ftp/python/2.6.2/python-2.6.2.msi @@ -1110,21 +1107,21 @@ the EnterpriseDB website. PostgreSQL 8.3 is required because PostGIS is not available yet for 8.4. -After downloading, simply click on the installer, follow the -on-screen directions, and keep the default options (e.g., keep the installation +After downloading, simply click on the installer, follow the +on-screen directions, and keep the default options (e.g., keep the installation path as ``C:\Program Files\PostgreSQL\8.3``). .. note:: - This PostgreSQL installation process will create both a new windows user to be the - 'postgres service account' and a special 'postgres superuser' to own the database - cluster. You will be prompted to set a password for both users (make sure to write - them down!). To see basic details on the 'service user' account right click on - 'My Computer' and select 'Manage' or go to: Control Panel -> Administrative Tools -> + This PostgreSQL installation process will create both a new windows user to be the + 'postgres service account' and a special 'postgres superuser' to own the database + cluster. You will be prompted to set a password for both users (make sure to write + them down!). To see basic details on the 'service user' account right click on + 'My Computer' and select 'Manage' or go to: Control Panel -> Administrative Tools -> Computer Management -> System Tools -> Local Users and Groups. -If installed successfully, the PostgreSQL server will run in the background each time -the system as started as a Windows service. When finished, the installer should launch +If installed successfully, the PostgreSQL server will run in the background each time +the system as started as a Windows service. When finished, the installer should launch the Application Stack Builder (ASB) -- use this to install PostGIS, see instructions below for more details. A 'PostgreSQL 8.3' start menu group should be created that contains shortcuts for the ASB and 'Command Prompt', which launches a terminal window @@ -1135,22 +1132,22 @@ __ http://www.enterprisedb.com/products/pgdownload.do#windows PostGIS ^^^^^^^ -From the Application Stack Builder (Programs -> PostgreSQL 8.3), select -'PostgreSQL Database Server 8.3 on port 5432' from the drop down menu. Next, +From the Application Stack Builder (Programs -> PostgreSQL 8.3), select +'PostgreSQL Database Server 8.3 on port 5432' from the drop down menu. Next, select 'PostGIS 1.3.6 for PostgreSQL 8.3' from the 'Spatial Extensions' tree -in the list. Select only the default options during install (do not uncheck +in the list. Select only the default options during install (do not uncheck the option to create a default PostGIS database). .. note:: - You will be prompted to enter your 'postgres superuser' password in the + You will be prompted to enter your 'postgres superuser' password in the 'Database Connection Information' dialog. psycopg2 ^^^^^^^^ The ``psycopg2`` Python module provides the interface between Python and the -PostgreSQL database. Download the `Windows installer`__ (v2.0.10) and run +PostgreSQL database. Download the `Windows installer`__ (v2.0.10) and run using the default settings. [#]_ __ http://www.stickpeople.com/projects/python/win-psycopg/psycopg2-2.0.10.win32-py2.6-pg8.3.7-release.exe @@ -1163,31 +1160,31 @@ of the process for installing GeoDjango on Windows platforms. The installer automatically installs Django 1.1, GDAL 1.6.0, PROJ 4.6.1 (including datum grid files), and configures the necessary environment variables. -Once the installer has completed, log out and log back in so that the +Once the installer has completed, log out and log back in so that the modifications to the system environment variables take effect, and you should be good to go. .. note:: The installer modifies the system ``Path`` environment variable to - include ``C:\Program Files\PostgreSQL\8.3\bin`` and + include ``C:\Program Files\PostgreSQL\8.3\bin`` and ``C:\Program Files\GeoDjango\bin``. This is required so that Python may find the GEOS DLL provided by PostGIS and the GDAL DLL provided - by the installer. The installer also sets the ``GDAL_DATA`` and + by the installer. The installer also sets the ``GDAL_DATA`` and ``PROJ_LIB`` environment variables. __ http://geodjango.org/windows/GeoDjango_Installer.exe .. rubric:: Footnotes .. [#] The datum shifting files are needed for converting data to and from certain projections. - For example, the PROJ.4 string for the `Google projection (900913) <http://spatialreference.org/ref/epsg/900913/proj4>`_ - requires the ``null`` grid file only included in the extra datum shifting files. + For example, the PROJ.4 string for the `Google projection (900913) <http://spatialreference.org/ref/epsg/900913/proj4>`_ + requires the ``null`` grid file only included in the extra datum shifting files. It is easier to install the shifting files now, then to have debug a problem caused by their absence later. .. [#] Specifically, GeoDjango provides support for the `OGR <http://gdal.org/ogr>`_ library, a component of GDAL. .. [#] See `GDAL ticket #2382 <http://trac.osgeo.org/gdal/ticket/2382>`_. .. [#] GeoDjango uses the `find_library <http://docs.python.org/library/ctypes.html#finding-shared-libraries>`_ - routine from ``ctypes.util`` to locate shared libraries. -.. [#] The ``psycopg2`` Windows installers are packaged and maintained by - `Jason Erickson <http://www.stickpeople.com/projects/python/win-psycopg/>`_. -.. [#] The source code for the installer is available in the `nsis_installer <http://geodjango.org/hg/nsis_installer/>`_ + routine from ``ctypes.util`` to locate shared libraries. +.. [#] The ``psycopg2`` Windows installers are packaged and maintained by + `Jason Erickson <http://www.stickpeople.com/projects/python/win-psycopg/>`_. +.. [#] The source code for the installer is available in the `nsis_installer <http://geodjango.org/hg/nsis_installer/>`_ GeoDjango mercurial repository. diff --git a/docs/ref/contrib/gis/layermapping.txt b/docs/ref/contrib/gis/layermapping.txt index a423259c11..0b09e176f6 100644 --- a/docs/ref/contrib/gis/layermapping.txt +++ b/docs/ref/contrib/gis/layermapping.txt @@ -14,7 +14,7 @@ vector spatial data files (e.g. shapefiles) intoto GeoDjango models. This utility grew out of the author's personal needs to eliminate the code repetition that went into pulling geometries and fields out of -a vector layer, converting to another coordinate system (e.g. WGS84), and +a vector layer, converting to another coordinate system (e.g. WGS84), and then inserting into a GeoDjango model. .. note:: @@ -27,7 +27,7 @@ then inserting into a GeoDjango model. that :class:`LayerMapping` is using too much memory, set :setting:`DEBUG` to ``False`` in your settings. When :setting:`DEBUG` is set to ``True``, Django :ref:`automatically logs <faq-see-raw-sql-queries>` - *every* SQL query -- thus, when SQL statements contain geometries, it is + *every* SQL query -- thus, when SQL statements contain geometries, it is easy to consume more memory than is typical. Example @@ -50,7 +50,7 @@ Example DATUM["WGS_1984", SPHEROID["WGS_1984",6378137,298.257223563]], PRIMEM["Greenwich",0], - UNIT["Degree",0.017453292519943295]] + UNIT["Degree",0.017453292519943295]] 2. Now we define our corresponding Django model (make sure to use ``syncdb``):: @@ -71,16 +71,16 @@ Example >>> mapping = {'name' : 'str', # The 'name' model field maps to the 'str' layer field. 'poly' : 'POLYGON', # For geometry fields use OGC name. } # The mapping is a dictionary - >>> lm = LayerMapping(TestGeo, 'test_poly.shp', mapping) - >>> lm.save(verbose=True) # Save the layermap, imports the data. + >>> lm = LayerMapping(TestGeo, 'test_poly.shp', mapping) + >>> lm.save(verbose=True) # Save the layermap, imports the data. Saved: Name: 1 Saved: Name: 2 Saved: Name: 3 Here, :class:`LayerMapping` just transformed the three geometries from the shapefile in their original spatial reference system (WGS84) to the spatial -reference system of the GeoDjango model (NAD83). If no spatial reference -system is defined for the layer, use the ``source_srs`` keyword with a +reference system of the GeoDjango model (NAD83). If no spatial reference +system is defined for the layer, use the ``source_srs`` keyword with a :class:`~django.contrib.gis.gdal.SpatialReference` object to specify one. ``LayerMapping`` API @@ -106,43 +106,43 @@ Argument Description model field is a geographic then it should correspond to the OGR geometry type, e.g., ``'POINT'``, ``'LINESTRING'``, ``'POLYGON'``. -================= ========================================================= +================= ========================================================= ===================== ===================================================== Keyword Arguments -===================== ===================================================== -``layer`` The index of the layer to use from the Data Source +===================== ===================================================== +``layer`` The index of the layer to use from the Data Source (defaults to 0) - -``source_srs`` Use this to specify the source SRS manually (for - example, some shapefiles don't come with a '.prj' - file). An integer SRID, WKT or PROJ.4 strings, and - :class:`django.contrib.gis.gdal.SpatialReference` + +``source_srs`` Use this to specify the source SRS manually (for + example, some shapefiles don't come with a '.prj' + file). An integer SRID, WKT or PROJ.4 strings, and + :class:`django.contrib.gis.gdal.SpatialReference` objects are accepted. - -``encoding`` Specifies the character set encoding of the strings - in the OGR data source. For example, ``'latin-1'``, - ``'utf-8'``, and ``'cp437'`` are all valid encoding + +``encoding`` Specifies the character set encoding of the strings + in the OGR data source. For example, ``'latin-1'``, + ``'utf-8'``, and ``'cp437'`` are all valid encoding parameters. - -``transaction_mode`` May be ``'commit_on_success'`` (default) or + +``transaction_mode`` May be ``'commit_on_success'`` (default) or ``'autocommit'``. - -``transform`` Setting this to False will disable coordinate + +``transform`` Setting this to False will disable coordinate transformations. In other words, geometries will be inserted into the database unmodified from their original state in the data source. - + ``unique`` Setting this to the name, or a tuple of names, from the given model will create models unique - only to the given name(s). Geometries will from - each feature will be added into the collection - associated with the unique model. Forces + only to the given name(s). Geometries will from + each feature will be added into the collection + associated with the unique model. Forces the transaction mode to be ``'autocommit'``. ``using`` New in version 1.2. Sets the database to use when importing spatial data. Default is ``'default'`` -===================== ===================================================== +===================== ===================================================== ``save()`` Keyword Arguments ---------------------------- @@ -156,42 +156,42 @@ specific feature ranges. =========================== ================================================= Save Keyword Arguments Description =========================== ================================================= -``fid_range`` May be set with a slice or tuple of - (begin, end) feature ID's to map from +``fid_range`` May be set with a slice or tuple of + (begin, end) feature ID's to map from the data source. In other words, this - keyword enables the user to selectively + keyword enables the user to selectively import a subset range of features in the geographic data source. ``progress`` When this keyword is set, status information - will be printed giving the number of features - processed and successfully saved. By default, + will be printed giving the number of features + processed and successfully saved. By default, progress information will be printed every 1000 - features processed, however, this default may - be overridden by setting this keyword with an + features processed, however, this default may + be overridden by setting this keyword with an integer for the desired interval. -``silent`` By default, non-fatal error notifications are - printed to ``sys.stdout``, but this keyword may +``silent`` By default, non-fatal error notifications are + printed to ``sys.stdout``, but this keyword may be set to disable these notifications. -``step`` If set with an integer, transactions will - occur at every step interval. For example, if - ``step=1000``, a commit would occur after the +``step`` If set with an integer, transactions will + occur at every step interval. For example, if + ``step=1000``, a commit would occur after the 1,000th feature, the 2,000th feature etc. -``stream`` Status information will be written to this file - handle. Defaults to using ``sys.stdout``, but +``stream`` Status information will be written to this file + handle. Defaults to using ``sys.stdout``, but any object with a ``write`` method is supported. -``strict`` Execution of the model mapping will cease upon +``strict`` Execution of the model mapping will cease upon the first error encountered. The default value (``False``) behavior is to attempt to continue. -``verbose`` If set, information will be printed - subsequent to each model save +``verbose`` If set, information will be printed + subsequent to each model save executed on the database. =========================== ================================================= @@ -213,7 +213,7 @@ If you encounter the following error when using ``LayerMapping`` and MySQL:: OperationalError: (1153, "Got a packet bigger than 'max_allowed_packet' bytes") Then the solution is to increase the value of the ``max_allowed_packet`` -setting in your MySQL configuration. For example, the default value may +setting in your MySQL configuration. For example, the default value may be something low like one megabyte -- the setting may be modified in MySQL's configuration file (``my.cnf``) in the ``[mysqld]`` section:: diff --git a/docs/ref/contrib/gis/measure.txt b/docs/ref/contrib/gis/measure.txt index 8b9629ef80..6971788b4e 100644 --- a/docs/ref/contrib/gis/measure.txt +++ b/docs/ref/contrib/gis/measure.txt @@ -7,17 +7,17 @@ Measurement Objects .. module:: django.contrib.gis.measure :synopsis: GeoDjango's distance and area measurment objects. -The :mod:`django.contrib.gis.measure` module contains objects that allow -for convenient representation of distance and area units of measure. [#]_ -Specifically, it implements two objects, :class:`Distance` and -:class:`Area` -- both of which may be accessed via the +The :mod:`django.contrib.gis.measure` module contains objects that allow +for convenient representation of distance and area units of measure. [#]_ +Specifically, it implements two objects, :class:`Distance` and +:class:`Area` -- both of which may be accessed via the :class:`D` and :class:`A` convenience aliases, respectively. Example ======= -:class:`Distance` objects may be instantiated using a keyword argument indicating the -context of the units. In the example below, two different distance objects are +:class:`Distance` objects may be instantiated using a keyword argument indicating the +context of the units. In the example below, two different distance objects are instantiated in units of kilometers (``km``) and miles (``mi``):: >>> from django.contrib.gis.measure import Distance, D @@ -40,7 +40,7 @@ Moreover, arithmetic operations may be performed between the distance objects:: >>> print d1 + d2 # Adding 5 miles to 5 kilometers - 13.04672 km + 13.04672 km >>> print d2 - d1 # Subtracting 5 kilometers from 5 miles 1.89314403881 mi @@ -67,7 +67,7 @@ Supported units ================================= ======================================== Unit Attribute Full name or alias(es) ================================= ======================================== -``km`` Kilometre, Kilometer +``km`` Kilometre, Kilometer ``mi`` Mile ``m`` Meter, Metre ``yd`` Yard @@ -163,7 +163,7 @@ Measurement API 12.949940551680001 .. classmethod:: unit_attname(unit_name) - + Returns the area unit attribute name for the given full unit name. For example:: @@ -175,6 +175,6 @@ Measurement API Alias for :class:`Area` class. .. rubric:: Footnotes -.. [#] `Robert Coup <http://koordinates.com/>`_ is the initial author of the measure objects, - and was inspired by Brian Beck's work in `geopy <http://code.google.com/p/geopy/>`_ +.. [#] `Robert Coup <http://koordinates.com/>`_ is the initial author of the measure objects, + and was inspired by Brian Beck's work in `geopy <http://code.google.com/p/geopy/>`_ and Geoff Biggs' PhD work on dimensioned units for robotics. diff --git a/docs/ref/contrib/gis/model-api.txt b/docs/ref/contrib/gis/model-api.txt index 7c83a7e267..cf73747463 100644 --- a/docs/ref/contrib/gis/model-api.txt +++ b/docs/ref/contrib/gis/model-api.txt @@ -8,11 +8,11 @@ GeoDjango Model API :synopsis: GeoDjango model and field API. This document explores the details of the GeoDjango Model API. Throughout this -section, we'll be using the following geographic model of a `ZIP code`__ as our +section, we'll be using the following geographic model of a `ZIP code`__ as our example:: from django.contrib.gis.db import models - + class Zipcode(models.Model): code = models.CharField(max_length=5) poly = models.PolygonField() @@ -23,7 +23,7 @@ __ http://en.wikipedia.org/wiki/ZIP_code Geometry Field Types ==================== -Each of the following geometry field types correspond with the +Each of the following geometry field types correspond with the OpenGIS Simple Features specification [#fnogc]_. ``GeometryField`` @@ -92,7 +92,7 @@ Selecting an SRID ^^^^^^^^^^^^^^^^^ Choosing an appropriate SRID for your model is an important decision that the -developer should consider carefully. The SRID is an integer specifier that +developer should consider carefully. The SRID is an integer specifier that corresponds to the projection system that will be used to interpret the data in the spatial database. [#fnsrid]_ Projection systems give the context to the coordinates that specify a location. Although the details of `geodesy`__ are @@ -105,7 +105,7 @@ location on the earth's surface. However, latitude and longitude are angles, not distances. [#fnharvard]_ In other words, while the shortest path between two points on a flat surface is a straight line, the shortest path between two points on a curved surface (such as the earth) is an *arc* of a `great circle`__. [#fnthematic]_ Thus, -additional computation is required to obtain distances in planar units (e.g., +additional computation is required to obtain distances in planar units (e.g., kilometers and miles). Using a geographic coordinate system may introduce complications for the developer later on. For example, PostGIS versions 1.4 and below do not have the capability to perform distance calculations between @@ -113,12 +113,12 @@ non-point geometries using geographic coordinate systems, e.g., constructing a query to find all points within 5 miles of a county boundary stored as WGS84. [#fndist]_ -Portions of the earth's surface may projected onto a two-dimensional, or +Portions of the earth's surface may projected onto a two-dimensional, or Cartesian, plane. Projected coordinate systems are especially convenient for region-specific applications, e.g., if you know that your database will -only cover geometries in `North Kansas`__, then you may consider using projection -system specific to that region. Moreover, projected coordinate systems are -defined in Cartesian units (such as meters or feet), easing distance +only cover geometries in `North Kansas`__, then you may consider using projection +system specific to that region. Moreover, projected coordinate systems are +defined in Cartesian units (such as meters or feet), easing distance calculations. .. note:: @@ -131,7 +131,7 @@ calculations. Additional Resources: -* `spatialreference.org`__: A Django-powered database of spatial reference +* `spatialreference.org`__: A Django-powered database of spatial reference systems. * `The State Plane Coordinate System`__: A website covering the various projection systems used in the United States. Much of the U.S. spatial @@ -150,7 +150,7 @@ __ http://welcome.warnercnr.colostate.edu/class_info/nr502/lg3/datums_coordinate .. attribute:: GeometryField.spatial_index Defaults to ``True``. Creates a spatial index for the given geometry -field. +field. .. note:: @@ -185,7 +185,7 @@ three-dimensonal support. .. attribute:: GeometryField.geography If set to ``True``, this option will create a database column of -type geography, rather than geometry. Please refer to the +type geography, rather than geometry. Please refer to the :ref:`geography type <geography-type>` section below for more details. @@ -212,7 +212,7 @@ to degrees if called on a geometry column in WGS84). Because geography calculations involve more mathematics, only a subset of the PostGIS spatial lookups are available for the geography type. Practically, this means that in addition to the :ref:`distance lookups <distance-lookups>` -only the following additional :ref:`spatial lookups <spatial-lookups>` are +only the following additional :ref:`spatial lookups <spatial-lookups>` are available for geography columns: * :lookup:`bboverlaps` @@ -231,13 +231,13 @@ determining `when to use geography data type over geometry data type .. currentmodule:: django.contrib.gis.db.models .. class:: GeoManager -In order to conduct geographic queries, each geographic model requires +In order to conduct geographic queries, each geographic model requires a ``GeoManager`` model manager. This manager allows for the proper SQL -construction for geographic queries; thus, without it, all geographic filters +construction for geographic queries; thus, without it, all geographic filters will fail. It should also be noted that ``GeoManager`` is required even if the -model does not have a geographic field itself, e.g., in the case of a -``ForeignKey`` relation to a model with a geographic field. For example, -if we had an ``Address`` model with a ``ForeignKey`` to our ``Zipcode`` +model does not have a geographic field itself, e.g., in the case of a +``ForeignKey`` relation to a model with a geographic field. For example, +if we had an ``Address`` model with a ``ForeignKey`` to our ``Zipcode`` model:: from django.contrib.gis.db import models @@ -251,7 +251,7 @@ model:: zipcode = models.ForeignKey(Zipcode) objects = models.GeoManager() -The geographic manager is needed to do spatial queries on related ``Zipcode`` objects, +The geographic manager is needed to do spatial queries on related ``Zipcode`` objects, for example:: qs = Address.objects.filter(zipcode__poly__contains='POINT(-104.590948 38.319914)') @@ -260,7 +260,7 @@ for example:: .. [#fnogc] OpenGIS Consortium, Inc., `Simple Feature Specification For SQL <http://www.opengis.org/docs/99-049.pdf>`_, Document 99-049 (May 5, 1999). .. [#fnogcsrid] *See id.* at Ch. 2.3.8, p. 39 (Geometry Values and Spatial Reference Systems). .. [#fnsrid] Typically, SRID integer corresponds to an EPSG (`European Petroleum Survey Group <http://www.epsg.org>`_) identifier. However, it may also be associated with custom projections defined in spatial database's spatial reference systems table. -.. [#fnharvard] Harvard Graduate School of Design, `An Overview of Geodesy and Geographic Referencing Systems <http://www.gsd.harvard.edu/gis/manual/projections/fundamentals/>`_. This is an excellent resource for an overview of principles relating to geographic and Cartesian coordinate systems. +.. [#fnharvard] Harvard Graduate School of Design, `An Overview of Geodesy and Geographic Referencing Systems <http://www.gsd.harvard.edu/gis/manual/projections/fundamentals/>`_. This is an excellent resource for an overview of principles relating to geographic and Cartesian coordinate systems. .. [#fnthematic] Terry A. Slocum, Robert B. McMaster, Fritz C. Kessler, & Hugh H. Howard, *Thematic Cartography and Geographic Visualization* (Prentice Hall, 2nd edition), at Ch. 7.1.3. .. [#fndist] This limitation does not apply to PostGIS 1.5. It should be noted that even in previous versions of PostGIS, this isn't impossible using GeoDjango; you could for example, take a known point in a projected coordinate system, buffer it to the appropriate radius, and then perform an intersection operation with the buffer transformed to the geographic coordinate system. .. [#fngeography] Please refer to the `PostGIS Geography Type <http://postgis.refractions.net/documentation/manual-1.5/ch04.html#PostGIS_Geography>`_ documentation for more details. diff --git a/docs/ref/contrib/gis/testing.txt b/docs/ref/contrib/gis/testing.txt index b2513f670b..2e81510cd9 100644 --- a/docs/ref/contrib/gis/testing.txt +++ b/docs/ref/contrib/gis/testing.txt @@ -6,7 +6,7 @@ Testing GeoDjango Apps In Django 1.2, the addition of :ref:`spatial-backends` simplified the process of testing GeoDjango applications. Specifically, testing -GeoDjango applications is now the same as :ref:`topics-testing`. +GeoDjango applications is now the same as :doc:`/topics/testing`. Included in this documentation are some additional notes and settings for :ref:`testing-postgis` and :ref:`testing-spatialite` users. @@ -133,7 +133,7 @@ You will need to download the `initialization SQL`__ script for SpatiaLite:: If ``init_spatialite-2.3.sql`` is in the same path as your project's ``manage.py``, then all you have to do is:: - $ python manage.py test + $ python manage.py test Settings -------- @@ -166,9 +166,9 @@ must be used. To use this runner, configure :setting:`TEST_RUNNER` as follows:: .. note:: - In order to create a spatial database, the :setting:`DATABASE_USER` setting - (or :setting:`TEST_DATABASE_USER`, if optionally defined on Oracle) requires - elevated privileges. When using PostGIS or MySQL, the database user + In order to create a spatial database, the :setting:`USER` setting + (or :setting:`TEST_USER`, if optionally defined on Oracle) requires + elevated privileges. When using PostGIS or MySQL, the database user must have at least the ability to create databases. When testing on Oracle, the user should be a superuser. diff --git a/docs/ref/contrib/gis/tutorial.txt b/docs/ref/contrib/gis/tutorial.txt index 5b535186f8..3a56c2e7c0 100644 --- a/docs/ref/contrib/gis/tutorial.txt +++ b/docs/ref/contrib/gis/tutorial.txt @@ -15,8 +15,8 @@ geographic web applications, like location-based services. Some features includ data formats. * Editing of geometry fields inside the admin. -This tutorial assumes a familiarity with Django; thus, if you're brand new to -Django please read through the :ref:`regular tutorial <intro-tutorial01>` to introduce +This tutorial assumes a familiarity with Django; thus, if you're brand new to +Django please read through the :doc:`regular tutorial </intro/tutorial01>` to introduce yourself with basic Django concepts. .. note:: @@ -27,12 +27,12 @@ yourself with basic Django concepts. This tutorial is going to guide you through guide the user through the creation of a geographic web application for viewing the `world borders`_. [#]_ Some of -the code used in this tutorial is taken from and/or inspired by the +the code used in this tutorial is taken from and/or inspired by the `GeoDjango basic apps`_ project. [#]_ .. note:: - Proceed through the tutorial sections sequentially for step-by-step + Proceed through the tutorial sections sequentially for step-by-step instructions. .. _OGC: http://www.opengeospatial.org/ @@ -51,11 +51,11 @@ Create a Spatial Database are already built into the database. First, a spatial database needs to be created for our project. If using -PostgreSQL and PostGIS, then the following commands will +PostgreSQL and PostGIS, then the following commands will create the database from a :ref:`spatial database template <spatialdb_template>`:: $ createdb -T template_postgis geodjango - + .. note:: This command must be issued by a database user that has permissions to @@ -65,9 +65,9 @@ create the database from a :ref:`spatial database template <spatialdb_template>` $ sudo su - postgres $ createuser --createdb geo $ exit - + Replace ``geo`` to correspond to the system login user name will be - connecting to the database. For example, ``johndoe`` if that is the + connecting to the database. For example, ``johndoe`` if that is the system user that will be running GeoDjango. Users of SQLite and SpatiaLite should consult the instructions on how @@ -92,7 +92,7 @@ Configure ``settings.py`` The ``geodjango`` project settings are stored in the ``settings.py`` file. Edit the database connection settings appropriately:: - DATABASES = { + DATABASES = { 'default': { 'ENGINE': 'django.contrib.gis.db.backends.postgis', 'NAME': 'geodjango', @@ -104,7 +104,7 @@ the database connection settings appropriately:: These database settings are for Django 1.2 and above. -In addition, modify the :setting:`INSTALLED_APPS` setting to include +In addition, modify the :setting:`INSTALLED_APPS` setting to include :mod:`django.contrib.admin`, :mod:`django.contrib.gis`, and ``world`` (our newly created application):: @@ -142,8 +142,8 @@ unzipped the world borders data set includes files with the following extensions * ``.shp``: Holds the vector data for the world borders geometries. * ``.shx``: Spatial index file for geometries stored in the ``.shp``. -* ``.dbf``: Database file for holding non-geometric attribute data - (e.g., integer and character fields). +* ``.dbf``: Database file for holding non-geometric attribute data + (e.g., integer and character fields). * ``.prj``: Contains the spatial reference information for the geographic data stored in the shapefile. @@ -153,7 +153,7 @@ __ http://en.wikipedia.org/wiki/Shapefile Use ``ogrinfo`` to examine spatial data --------------------------------------- -The GDAL ``ogrinfo`` utility is excellent for examining metadata about +The GDAL ``ogrinfo`` utility is excellent for examining metadata about shapefiles (or other vector data sources):: $ ogrinfo world/data/TM_WORLD_BORDERS-0.3.shp @@ -192,13 +192,13 @@ and use the ``-so`` option to get only important summary information:: LAT: Real (7.3) This detailed summary information tells us the number of features in the layer -(246), the geographical extent, the spatial reference system ("SRS WKT"), +(246), the geographical extent, the spatial reference system ("SRS WKT"), as well as detailed information for each attribute field. For example, ``FIPS: String (2.0)`` indicates that there's a ``FIPS`` character field with a maximum length of 2; similarly, ``LON: Real (8.3)`` is a floating-point field that holds a maximum of 8 digits up to three decimal places. Although this information may be found right on the `world borders`_ website, this shows -you how to determine this information yourself when such metadata is not +you how to determine this information yourself when such metadata is not provided. Geographic Models @@ -213,7 +213,7 @@ create a GeoDjango model to represent this data:: from django.contrib.gis.db import models class WorldBorders(models.Model): - # Regular Django fields corresponding to the attributes in the + # Regular Django fields corresponding to the attributes in the # world borders shapefile. name = models.CharField(max_length=50) area = models.IntegerField() @@ -227,7 +227,7 @@ create a GeoDjango model to represent this data:: lon = models.FloatField() lat = models.FloatField() - # GeoDjango-specific: a geometry field (MultiPolygonField), and + # GeoDjango-specific: a geometry field (MultiPolygonField), and # overriding the default manager with a GeoManager instance. mpoly = models.MultiPolygonField() objects = models.GeoManager() @@ -235,23 +235,23 @@ create a GeoDjango model to represent this data:: # So the model is pluralized correctly in the admin. class Meta: verbose_name_plural = "World Borders" - - # Returns the string representation of the model. + + # Returns the string representation of the model. def __unicode__(self): return self.name Two important things to note: 1. The ``models`` module is imported from :mod:`django.contrib.gis.db`. -2. The model overrides its default manager with +2. The model overrides its default manager with :class:`~django.contrib.gis.db.models.GeoManager`; this is *required* - to perform spatial queries. + to perform spatial queries. When declaring a geometry field on your model the default spatial reference system is WGS84 (meaning the `SRID`__ is 4326) -- in other words, the field coordinates are in longitude/latitude pairs in units of degrees. If you want the coordinate system to be different, then SRID of the geometry field may be customized by setting the ``srid`` -with an integer corresponding to the coordinate system of your choice. +with an integer corresponding to the coordinate system of your choice. __ http://en.wikipedia.org/wiki/SRID @@ -259,7 +259,7 @@ Run ``syncdb`` -------------- After you've defined your model, it needs to be synced with the spatial database. -First, let's look at the SQL that will generate the table for the ``WorldBorders`` +First, let's look at the SQL that will generate the table for the ``WorldBorders`` model:: $ python manage.py sqlall world @@ -295,7 +295,7 @@ If satisfied, you may then create this table in the database by running the Installing custom SQL for world.WorldBorders model The ``syncdb`` command may also prompt you to create an admin user; go ahead and -do so (not required now, may be done at any point in the future using the +do so (not required now, may be done at any point in the future using the ``createsuperuser`` management command). Importing Spatial Data @@ -303,11 +303,11 @@ Importing Spatial Data This section will show you how to take the data from the world borders shapefile and import it into GeoDjango models using the :ref:`ref-layermapping`. -There are many different different ways to import data in to a +There are many different different ways to import data in to a spatial database -- besides the tools included within GeoDjango, you may also use the following to populate your spatial database: -* `ogr2ogr`_: Command-line utility, included with GDAL, that +* `ogr2ogr`_: Command-line utility, included with GDAL, that supports loading a multitude of vector data formats into the PostGIS, MySQL, and Oracle spatial databases. * `shp2pgsql`_: This utility is included with PostGIS and only supports @@ -339,7 +339,7 @@ tutorial, then we can determine the path using Python's built-in >>> world_shp = os.path.abspath(os.path.join(os.path.dirname(world.__file__), ... 'data/TM_WORLD_BORDERS-0.3.shp')) -Now, the world borders shapefile may be opened using GeoDjango's +Now, the world borders shapefile may be opened using GeoDjango's :class:`~django.contrib.gis.gdal.DataSource` interface:: >>> from django.contrib.gis.gdal import * @@ -347,7 +347,7 @@ Now, the world borders shapefile may be opened using GeoDjango's >>> print ds / ... /geodjango/world/data/TM_WORLD_BORDERS-0.3.shp (ESRI Shapefile) -Data source objects can have different layers of geospatial features; however, +Data source objects can have different layers of geospatial features; however, shapefiles are only allowed to have one layer:: >>> print len(ds) @@ -367,10 +367,10 @@ contains:: .. note:: Unfortunately the shapefile data format does not allow for greater - specificity with regards to geometry types. This shapefile, like + specificity with regards to geometry types. This shapefile, like many others, actually includes ``MultiPolygon`` geometries in its features. You need to watch out for this when creating your models - as a GeoDjango ``PolygonField`` will not accept a ``MultiPolygon`` + as a GeoDjango ``PolygonField`` will not accept a ``MultiPolygon`` type geometry -- thus a ``MultiPolygonField`` is used in our model's definition instead. @@ -391,7 +391,7 @@ system associated with it -- if it does, the ``srs`` attribute will return a Here we've noticed that the shapefile is in the popular WGS84 spatial reference system -- in other words, the data uses units of degrees longitude and latitude. -In addition, shapefiles also support attribute fields that may contain +In addition, shapefiles also support attribute fields that may contain additional data. Here are the fields on the World Borders layer: >>> print lyr.fields @@ -403,8 +403,8 @@ a string) associated with each of the fields: >>> [fld.__name__ for fld in lyr.field_types] ['OFTString', 'OFTString', 'OFTString', 'OFTInteger', 'OFTString', 'OFTInteger', 'OFTInteger', 'OFTInteger', 'OFTInteger', 'OFTReal', 'OFTReal'] -You can iterate over each feature in the layer and extract information from both -the feature's geometry (accessed via the ``geom`` attribute) as well as the +You can iterate over each feature in the layer and extract information from both +the feature's geometry (accessed via the ``geom`` attribute) as well as the feature's attribute fields (whose **values** are accessed via ``get()`` method):: @@ -427,7 +427,7 @@ And individual features may be retrieved by their feature ID:: >>> print feat.get('NAME') San Marino -Here the boundary geometry for San Marino is extracted and looking +Here the boundary geometry for San Marino is extracted and looking exported to WKT and GeoJSON:: >>> geom = feat.geom @@ -465,7 +465,7 @@ We're going to dive right in -- create a file called ``load.py`` inside the world_shp = os.path.abspath(os.path.join(os.path.dirname(__file__), 'data/TM_WORLD_BORDERS-0.3.shp')) def run(verbose=True): - lm = LayerMapping(WorldBorders, world_shp, world_mapping, + lm = LayerMapping(WorldBorders, world_shp, world_mapping, transform=False, encoding='iso-8859-1') lm.save(strict=True, verbose=verbose) @@ -473,8 +473,8 @@ We're going to dive right in -- create a file called ``load.py`` inside the A few notes about what's going on: * Each key in the ``world_mapping`` dictionary corresponds to a field in the - ``WorldBorders`` model, and the value is the name of the shapefile field - that data will be loaded from. + ``WorldBorders`` model, and the value is the name of the shapefile field + that data will be loaded from. * The key ``mpoly`` for the geometry field is ``MULTIPOLYGON``, the geometry type we wish to import as. Even if simple polygons are encountered in the shapefile they will automatically be converted into collections prior @@ -503,10 +503,10 @@ do the work:: Try ``ogrinspect`` ------------------ -Now that you've seen how to define geographic models and import data with the +Now that you've seen how to define geographic models and import data with the :ref:`ref-layermapping`, it's possible to further automate this process with use of the :djadmin:`ogrinspect` management command. The :djadmin:`ogrinspect` -command introspects a GDAL-supported vector data source (e.g., a shapefile) and +command introspects a GDAL-supported vector data source (e.g., a shapefile) and generates a model definition and ``LayerMapping`` dictionary automatically. The general usage of the command goes as follows:: @@ -525,13 +525,13 @@ and mapping dictionary created above, automatically:: A few notes about the command-line options given above: * The ``--srid=4326`` option sets the SRID for the geographic field. -* The ``--mapping`` option tells ``ogrinspect`` to also generate a +* The ``--mapping`` option tells ``ogrinspect`` to also generate a mapping dictionary for use with :class:`~django.contrib.gis.utils.LayerMapping`. * The ``--multi`` option is specified so that the geographic field is a :class:`~django.contrib.gis.db.models.MultiPolygonField` instead of just a :class:`~django.contrib.gis.db.models.PolygonField`. -The command produces the following output, which may be copied +The command produces the following output, which may be copied directly into the ``models.py`` of a GeoDjango application:: # This is an auto-generated Django model module created by ogrinspect. @@ -584,7 +584,7 @@ Now, define a point of interest [#]_:: >>> pnt_wkt = 'POINT(-95.3385 29.7245)' The ``pnt_wkt`` string represents the point at -95.3385 degrees longitude, -and 29.7245 degrees latitude. The geometry is in a format known as +and 29.7245 degrees latitude. The geometry is in a format known as Well Known Text (WKT), an open standard issued by the Open Geospatial Consortium (OGC). [#]_ Import the ``WorldBorders`` model, and perform a ``contains`` lookup using the ``pnt_wkt`` as the parameter:: @@ -611,7 +611,7 @@ available -- the :ref:`ref-gis-db-api` documentation has more. Automatic Spatial Transformations --------------------------------- -When querying the spatial database GeoDjango automatically transforms +When querying the spatial database GeoDjango automatically transforms geometries if they're in a different coordinate system. In the following example, the coordinate will be expressed in terms of `EPSG SRID 32140`__, a coordinate system specific to south Texas **only** and in units of @@ -634,26 +634,26 @@ of abstraction:: ('SELECT "world_worldborders"."id", "world_worldborders"."name", "world_worldborders"."area", "world_worldborders"."pop2005", "world_worldborders"."fips", "world_worldborders"."iso2", "world_worldborders"."iso3", "world_worldborders"."un", "world_worldborders"."region", - "world_worldborders"."subregion", "world_worldborders"."lon", "world_worldborders"."lat", - "world_worldborders"."mpoly" FROM "world_worldborders" + "world_worldborders"."subregion", "world_worldborders"."lon", "world_worldborders"."lat", + "world_worldborders"."mpoly" FROM "world_worldborders" WHERE ST_Intersects("world_worldborders"."mpoly", ST_Transform(%s, 4326))', (<django.contrib.gis.db.backend.postgis.adaptor.PostGISAdaptor object at 0x25641b0>,)) >>> qs # printing evaluates the queryset - [<WorldBorders: United States>] + [<WorldBorders: United States>] __ http://spatialreference.org/ref/epsg/32140/ Lazy Geometries --------------- Geometries come to GeoDjango in a standardized textual representation. Upon -access of the geometry field, GeoDjango creates a `GEOS geometry object <ref-geos>`, -exposing powerful functionality, such as serialization properties for +access of the geometry field, GeoDjango creates a `GEOS geometry object <ref-geos>`, +exposing powerful functionality, such as serialization properties for popular geospatial formats:: >>> sm = WorldBorders.objects.get(name='San Marino') >>> sm.mpoly <MultiPolygon object at 0x24c6798> - >>> sm.mpoly.wkt # WKT + >>> sm.mpoly.wkt # WKT MULTIPOLYGON (((12.4157980000000006 43.9579540000000009, 12.4505540000000003 43.9797209999999978, ... >>> sm.mpoly.wkb # WKB (as Python binary buffer) <read-only buffer for 0x1fe2c70, size -1, offset 0 at 0x2564c40> @@ -682,16 +682,16 @@ Google Geographic Admin ---------------- -GeoDjango extends :ref:`Django's admin application <ref-contrib-admin>` to -enable support for editing geometry fields. +GeoDjango extends :doc:`Django's admin application </ref/contrib/admin/index>` +to enable support for editing geometry fields. Basics ^^^^^^ -GeoDjango also supplements the Django admin by allowing users to create +GeoDjango also supplements the Django admin by allowing users to create and modify geometries on a JavaScript slippy map (powered by `OpenLayers`_). -Let's dive in again -- create a file called ``admin.py`` inside the +Let's dive in again -- create a file called ``admin.py`` inside the ``world`` application, and insert the following:: from django.contrib.gis import admin diff --git a/docs/ref/contrib/humanize.txt b/docs/ref/contrib/humanize.txt index 07a62a7fbe..17db3c2535 100644 --- a/docs/ref/contrib/humanize.txt +++ b/docs/ref/contrib/humanize.txt @@ -1,5 +1,3 @@ -.. _ref-contrib-humanize: - ======================== django.contrib.humanize ======================== diff --git a/docs/ref/contrib/index.txt b/docs/ref/contrib/index.txt index bb470e3041..90edf72c94 100644 --- a/docs/ref/contrib/index.txt +++ b/docs/ref/contrib/index.txt @@ -1,5 +1,3 @@ -.. _ref-contrib-index: - ==================== ``contrib`` packages ==================== @@ -35,6 +33,7 @@ those packages have. gis/index humanize localflavor + markup messages redirects sitemaps @@ -46,8 +45,8 @@ admin ===== The automatic Django administrative interface. For more information, see -:ref:`Tutorial 2 <intro-tutorial02>` and the -:ref:`admin documentation <ref-contrib-admin>`. +:doc:`Tutorial 2 </intro/tutorial02>` and the +:doc:`admin documentation </ref/contrib/admin/index>`. Requires the auth_ and contenttypes_ contrib packages to be installed. @@ -56,16 +55,16 @@ auth Django's authentication framework. -See :ref:`topics-auth`. +See :doc:`/topics/auth`. comments ======== .. versionchanged:: 1.0 - The comments application has been rewriten. See :ref:`ref-contrib-comments-upgrade` + The comments application has been rewriten. See :doc:`/ref/contrib/comments/upgrade` for information on howto upgrade. -A simple yet flexible comments system. See :ref:`ref-contrib-comments-index`. +A simple yet flexible comments system. See :doc:`/ref/contrib/comments/index`. contenttypes ============ @@ -73,21 +72,21 @@ contenttypes A light framework for hooking into "types" of content, where each installed Django model is a separate content type. -See the :ref:`contenttypes documentation <ref-contrib-contenttypes>`. +See the :doc:`contenttypes documentation </ref/contrib/contenttypes>`. csrf ==== A middleware for preventing Cross Site Request Forgeries -See the :ref:`csrf documentation <ref-contrib-csrf>`. +See the :doc:`csrf documentation </ref/contrib/csrf>`. flatpages ========= A framework for managing simple "flat" HTML content in a database. -See the :ref:`flatpages documentation <ref-contrib-flatpages>`. +See the :doc:`flatpages documentation </ref/contrib/flatpages>`. Requires the sites_ contrib package to be installed as well. @@ -103,14 +102,14 @@ An abstraction of the following workflow: "Display an HTML form, force a preview, then do something with the submission." -See the :ref:`form preview documentation <ref-contrib-formtools-form-preview>`. +See the :doc:`form preview documentation </ref/contrib/formtools/form-preview>`. django.contrib.formtools.wizard -------------------------------- Splits forms across multiple Web pages. -See the :ref:`form wizard documentation <ref-contrib-formtools-form-wizard>`. +See the :doc:`form wizard documentation </ref/contrib/formtools/form-wizard>`. gis ==== @@ -118,14 +117,14 @@ gis A world-class geospatial framework built on top of Django, that enables storage, manipulation and display of spatial data. -See the :ref:`ref-contrib-gis` documentation for more. +See the :doc:`/ref/contrib/gis/index` documentation for more. humanize ======== A set of Django template filters useful for adding a "human touch" to data. -See the :ref:`humanize documentation <ref-contrib-humanize>`. +See the :doc:`humanize documentation </ref/contrib/humanize>`. localflavor =========== @@ -134,45 +133,14 @@ A collection of various Django snippets that are useful only for a particular country or culture. For example, ``django.contrib.localflavor.us.forms`` contains a ``USZipCodeField`` that you can use to validate U.S. zip codes. -See the :ref:`localflavor documentation <ref-contrib-localflavor>`. - -.. _ref-contrib-markup: +See the :doc:`localflavor documentation </ref/contrib/localflavor>`. markup ====== -A collection of template filters that implement common markup languages: - - * ``textile`` -- implements `Textile`_ -- requires `PyTextile`_ - * ``markdown`` -- implements `Markdown`_ -- requires `Python-markdown`_ - * ``restructuredtext`` -- implements `ReST (ReStructured Text)`_ - -- requires `doc-utils`_ - -In each case, the filter expects formatted markup as a string and returns a -string representing the marked-up text. For example, the ``textile`` filter -converts text that is marked-up in Textile format to HTML. - -To activate these filters, add ``'django.contrib.markup'`` to your -:setting:`INSTALLED_APPS` setting. Once you've done that, use -``{% load markup %}`` in a template, and you'll have access to these filters. -For more documentation, read the source code in -:file:`django/contrib/markup/templatetags/markup.py`. - -.. _Textile: http://en.wikipedia.org/wiki/Textile_%28markup_language%29 -.. _Markdown: http://en.wikipedia.org/wiki/Markdown -.. _ReST (ReStructured Text): http://en.wikipedia.org/wiki/ReStructuredText -.. _PyTextile: http://loopcore.com/python-textile/ -.. _Python-markdown: http://www.freewisdom.org/projects/python-markdown -.. _doc-utils: http://docutils.sf.net/ - -ReStructured Text ------------------ - -When using the `restructuredtext` markup filter you can define a :setting:`RESTRUCTUREDTEXT_FORMAT_SETTINGS` -in your django settings to override the default writer settings. See the `restructuredtext writer settings`_ for -details on what these settings are. +A collection of template filters that implement common markup languages -.. _restructuredtext writer settings: http://docutils.sourceforge.net/docs/user/config.html#html4css1-writer +See the :doc:`markup documentation </ref/contrib/markup>`. messages ======== @@ -183,21 +151,21 @@ messages A framework for storing and retrieving temporary cookie- or session-based messages -See the :ref:`messages documentation <ref-contrib-messages>`. +See the :doc:`messages documentation </ref/contrib/messages>`. redirects ========= A framework for managing redirects. -See the :ref:`redirects documentation <ref-contrib-redirects>`. +See the :doc:`redirects documentation </ref/contrib/redirects>`. sessions ======== A framework for storing data in anonymous sessions. -See the :ref:`sessions documentation <topics-http-sessions>`. +See the :doc:`sessions documentation </topics/http/sessions>`. sites ===== @@ -206,21 +174,21 @@ A light framework that lets you operate multiple Web sites off of the same database and Django installation. It gives you hooks for associating objects to one or more sites. -See the :ref:`sites documentation <ref-contrib-sites>`. +See the :doc:`sites documentation </ref/contrib/sites>`. sitemaps ======== A framework for generating Google sitemap XML files. -See the :ref:`sitemaps documentation <ref-contrib-sitemaps>`. +See the :doc:`sitemaps documentation </ref/contrib/sitemaps>`. syndication =========== A framework for generating syndication feeds, in RSS and Atom, quite easily. -See the :ref:`syndication documentation <ref-contrib-syndication>`. +See the :doc:`syndication documentation </ref/contrib/syndication>`. webdesign ========= @@ -228,7 +196,7 @@ webdesign Helpers and utilities targeted primarily at Web *designers* rather than Web *developers*. -See the :ref:`Web design helpers documentation <ref-contrib-webdesign>`. +See the :doc:`Web design helpers documentation </ref/contrib/webdesign>`. Other add-ons ============= diff --git a/docs/ref/contrib/localflavor.txt b/docs/ref/contrib/localflavor.txt index 1c58e2d5e8..48cfa6340a 100644 --- a/docs/ref/contrib/localflavor.txt +++ b/docs/ref/contrib/localflavor.txt @@ -1,5 +1,3 @@ -.. _ref-contrib-localflavor: - ========================== The "local flavor" add-ons ========================== @@ -17,7 +15,7 @@ Inside that package, country- or culture-specific code is organized into subpackages, named using `ISO 3166 country codes`_. Most of the ``localflavor`` add-ons are localized form components deriving -from the :ref:`forms <topics-forms-index>` framework -- for example, a +from the :doc:`forms </topics/forms/index>` framework -- for example, a :class:`~django.contrib.localflavor.us.forms.USStateField` that knows how to validate U.S. state abbreviations, and a :class:`~django.contrib.localflavor.fi.forms.FISocialSecurityNumber` that @@ -74,7 +72,7 @@ Countries currently supported by :mod:`~django.contrib.localflavor` are: The ``django.contrib.localflavor`` package also includes a ``generic`` subpackage, containing useful code that is not specific to one particular country or culture. Currently, it defines date, datetime and split datetime input fields based on -those from :ref:`forms <topics-forms-index>`, but with non-US default formats. +those from :doc:`forms </topics/forms/index>`, but with non-US default formats. Here's an example of how to use them:: from django import forms diff --git a/docs/ref/contrib/markup.txt b/docs/ref/contrib/markup.txt new file mode 100644 index 0000000000..f2c43fe25f --- /dev/null +++ b/docs/ref/contrib/markup.txt @@ -0,0 +1,42 @@ +===================== +django.contrib.markup +===================== + +.. module:: django.contrib.markup + :synopsis: A collection of template filters that implement common markup languages. + +Django provides template filters that implement the following markup +languages: + + * ``textile`` -- implements `Textile`_ -- requires `PyTextile`_ + * ``markdown`` -- implements `Markdown`_ -- requires `Python-markdown`_ + * ``restructuredtext`` -- implements `ReST (ReStructured Text)`_ + -- requires `doc-utils`_ + +In each case, the filter expects formatted markup as a string and +returns a string representing the marked-up text. For example, the +``textile`` filter converts text that is marked-up in Textile format +to HTML. + +To activate these filters, add ``'django.contrib.markup'`` to your +:setting:`INSTALLED_APPS` setting. Once you've done that, use +``{% load markup %}`` in a template, and you'll have access to these filters. +For more documentation, read the source code in +:file:`django/contrib/markup/templatetags/markup.py`. + +.. _Textile: http://en.wikipedia.org/wiki/Textile_%28markup_language%29 +.. _Markdown: http://en.wikipedia.org/wiki/Markdown +.. _ReST (ReStructured Text): http://en.wikipedia.org/wiki/ReStructuredText +.. _PyTextile: http://loopcore.com/python-textile/ +.. _Python-markdown: http://www.freewisdom.org/projects/python-markdown +.. _doc-utils: http://docutils.sf.net/ + +ReStructured Text +----------------- + +When using the ``restructuredtext`` markup filter you can define a +:setting:`RESTRUCTUREDTEXT_FILTER_SETTINGS` in your django settings to +override the default writer settings. See the `restructuredtext writer +settings`_ for details on what these settings are. + +.. _restructuredtext writer settings: http://docutils.sourceforge.net/docs/user/config.html#html4css1-writer diff --git a/docs/ref/contrib/messages.txt b/docs/ref/contrib/messages.txt index 554e70b1f2..3081f2718d 100644 --- a/docs/ref/contrib/messages.txt +++ b/docs/ref/contrib/messages.txt @@ -1,5 +1,3 @@ -.. _ref-contrib-messages: - ====================== The messages framework ====================== @@ -20,8 +18,8 @@ with a specific ``level`` that determines its priority (e.g., ``info``, Enabling messages ================= -Messages are implemented through a :ref:`middleware <ref-middleware>` -class and corresponding :ref:`context processor <ref-templates-api>`. +Messages are implemented through a :doc:`middleware </ref/middleware>` +class and corresponding :doc:`context processor </ref/templates/api>`. To enable message functionality, do the following: @@ -29,7 +27,7 @@ To enable message functionality, do the following: it contains ``'django.contrib.messages.middleware.MessageMiddleware'``. If you are using a :ref:`storage backend <message-storage-backends>` that - relies on :ref:`sessions <topics-http-sessions>` (the default), + relies on :doc:`sessions </topics/http/sessions>` (the default), ``'django.contrib.sessions.middleware.SessionMiddleware'`` must be enabled and appear before ``MessageMiddleware`` in your :setting:`MIDDLEWARE_CLASSES`. @@ -106,7 +104,7 @@ LegacyFallbackStorage The ``LegacyFallbackStorage`` is a temporary tool to facilitate the transition from the deprecated ``user.message_set`` API and will be removed in Django 1.4 according to Django's standard deprecation policy. For more information, see -the full :ref:`release process documentation <internals-release-process>`. +the full :doc:`release process documentation </internals/release-process>`. In addition to the functionality in the ``FallbackStorage``, it adds a custom, read-only storage class that retrieves messages from the user ``Message`` @@ -300,7 +298,7 @@ example:: messages.info(request, 'Hello world.', fail_silently=True) Internally, Django uses this functionality in the create, update, and delete -:ref:`generic views <topics-generic-views>` so that they work even if the +:doc:`generic views </topics/http/generic-views>` so that they work even if the message framework is disabled. .. note:: @@ -343,7 +341,7 @@ window/tab will have its own browsing context. Settings ======== -A few :ref:`Django settings <ref-settings>` give you control over message +A few :doc:`Django settings </ref/settings>` give you control over message behavior: MESSAGE_LEVEL diff --git a/docs/ref/contrib/redirects.txt b/docs/ref/contrib/redirects.txt index 6f9c57c09d..f1a58cb207 100644 --- a/docs/ref/contrib/redirects.txt +++ b/docs/ref/contrib/redirects.txt @@ -1,5 +1,3 @@ -.. _ref-contrib-redirects: - ================= The redirects app ================= @@ -47,8 +45,8 @@ Note that the order of :setting:`MIDDLEWARE_CLASSES` matters. Generally, you can put ``RedirectFallbackMiddleware`` at the end of the list, because it's a last resort. -For more on middleware, read the :ref:`middleware docs -<topics-http-middleware>`. +For more on middleware, read the :doc:`middleware docs +</topics/http/middleware>`. How to add, change and delete redirects ======================================= @@ -65,8 +63,8 @@ Via the Python API .. class:: models.Redirect - Redirects are represented by a standard :ref:`Django model <topics-db-models>`, + Redirects are represented by a standard :doc:`Django model </topics/db/models>`, which lives in `django/contrib/redirects/models.py`_. You can access redirect - objects via the :ref:`Django database API <topics-db-queries>`. + objects via the :doc:`Django database API </topics/db/queries>`. .. _django/contrib/redirects/models.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/redirects/models.py diff --git a/docs/ref/contrib/sitemaps.txt b/docs/ref/contrib/sitemaps.txt index a71f19d786..7a66cbe9a9 100644 --- a/docs/ref/contrib/sitemaps.txt +++ b/docs/ref/contrib/sitemaps.txt @@ -1,5 +1,3 @@ -.. _ref-contrib-sitemaps: - ===================== The sitemap framework ===================== @@ -23,10 +21,10 @@ site. The Django sitemap framework automates the creation of this XML file by letting you express this information in Python code. -It works much like Django's :ref:`syndication framework -<ref-contrib-syndication>`. To create a sitemap, just write a +It works much like Django's :doc:`syndication framework +</ref/contrib/syndication>`. To create a sitemap, just write a :class:`~django.contrib.sitemaps.Sitemap` class and point to it in your -:ref:`URLconf <topics-http-urls>`. +:doc:`URLconf </topics/http/urls>`. Installation ============ @@ -52,7 +50,7 @@ Initialization ============== To activate sitemap generation on your Django site, add this line to your -:ref:`URLconf <topics-http-urls>`:: +:doc:`URLconf </topics/http/urls>`:: (r'^sitemap\.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}) @@ -217,8 +215,8 @@ The sitemap framework provides a couple convenience classes for common cases: .. class:: FlatPageSitemap The :class:`django.contrib.sitemaps.FlatPageSitemap` class looks at all - :mod:`flatpages <django.contrib.flatpages>` defined for the current - :setting:`SITE_ID` (see the + publicly visible :mod:`flatpages <django.contrib.flatpages>` + defined for the current :setting:`SITE_ID` (see the :mod:`sites documentation <django.contrib.sites>`) and creates an entry in the sitemap. These entries include only the :attr:`~Sitemap.location` attribute -- not :attr:`~Sitemap.lastmod`, @@ -227,7 +225,7 @@ The sitemap framework provides a couple convenience classes for common cases: .. class:: GenericSitemap The :class:`django.contrib.sitemaps.GenericSitemap` class works with any - :ref:`generic views <ref-generic-views>` you already have. + :doc:`generic views </ref/generic-views>` you already have. To use it, create an instance, passing in the same :data:`info_dict` you pass to the generic views. The only requirement is that the dictionary have a :data:`queryset` entry. It may also have a :data:`date_field` entry that specifies a @@ -240,7 +238,7 @@ The sitemap framework provides a couple convenience classes for common cases: Example ------- -Here's an example of a :ref:`URLconf <topics-http-urls>` using both:: +Here's an example of a :doc:`URLconf </topics/http/urls>` using both:: from django.conf.urls.defaults import * from django.contrib.sitemaps import FlatPageSitemap, GenericSitemap diff --git a/docs/ref/contrib/sites.txt b/docs/ref/contrib/sites.txt index b7cb8d6a58..7ff05a5c82 100644 --- a/docs/ref/contrib/sites.txt +++ b/docs/ref/contrib/sites.txt @@ -1,5 +1,3 @@ -.. _ref-contrib-sites: - ===================== The "sites" framework ===================== @@ -104,6 +102,8 @@ like this:: This has the same benefits as described in the last section. +.. _hooking-into-current-site-from-views: + Hooking into the current site from views ---------------------------------------- @@ -260,7 +260,7 @@ The ``CurrentSiteManager`` If :class:`~django.contrib.sites.models.Site` plays a key role in your application, consider using the helpful :class:`~django.contrib.sites.managers.CurrentSiteManager` in your -model(s). It's a model :ref:`manager <topics-db-managers>` that +model(s). It's a model :doc:`manager </topics/db/managers>` that automatically filters its queries to include only objects associated with the current :class:`~django.contrib.sites.models.Site`. @@ -322,7 +322,7 @@ and pass a field name that doesn't exist, Django will raise a :exc:`ValueError`. Finally, note that you'll probably want to keep a normal (non-site-specific) ``Manager`` on your model, even if you use :class:`~django.contrib.sites.managers.CurrentSiteManager`. As -explained in the :ref:`manager documentation <topics-db-managers>`, if +explained in the :doc:`manager documentation </topics/db/managers>`, if you define a manager manually, then Django won't create the automatic ``objects = models.Manager()`` manager for you. Also note that certain parts of Django -- namely, the Django admin site and generic views -- @@ -387,7 +387,7 @@ Here's how Django uses the sites framework: .. versionadded:: 1.0 -Some :ref:`django.contrib <ref-contrib-index>` applications take advantage of +Some :doc:`django.contrib </ref/contrib/index>` applications take advantage of the sites framework but are architected in a way that doesn't *require* the sites framework to be installed in your database. (Some people don't want to, or just aren't *able* to install the extra database table that the sites framework diff --git a/docs/ref/contrib/syndication.txt b/docs/ref/contrib/syndication.txt index 7b47ef8a4e..a12d646a08 100644 --- a/docs/ref/contrib/syndication.txt +++ b/docs/ref/contrib/syndication.txt @@ -1,5 +1,3 @@ -.. _ref-contrib-syndication: - ============================== The syndication feed framework ============================== @@ -38,8 +36,8 @@ Overview The high-level feed-generating framework is supplied by the :class:`~django.contrib.syndication.views.Feed` class. To create a feed, write a :class:`~django.contrib.syndication.views.Feed` class -and point to an instance of it in your :ref:`URLconf -<topics-http-urls>`. +and point to an instance of it in your :doc:`URLconf +</topics/http/urls>`. Feed classes ------------ @@ -54,7 +52,7 @@ Feed classes subclass :class:`django.contrib.syndication.views.Feed`. They can live anywhere in your codebase. Instances of :class:`~django.contrib.syndication.views.Feed` classes -are views which can be used in your :ref:`URLconf <topics-http-urls>`. +are views which can be used in your :doc:`URLconf </topics/http/urls>`. A simple example ---------------- @@ -80,7 +78,7 @@ latest five news items:: return item.description To connect a URL to this feed, put an instance of the Feed object in -your :ref:`URLconf <topics-http-urls>`. For example:: +your :doc:`URLconf </topics/http/urls>`. For example:: from django.conf.urls.defaults import * from myproject.feeds import LatestEntriesFeed @@ -102,7 +100,7 @@ Note: * :meth:`items()` is, simply, a method that returns a list of objects that should be included in the feed as ``<item>`` elements. Although this example returns ``NewsItem`` objects using Django's - :ref:`object-relational mapper <ref-models-querysets>`, :meth:`items()` + :doc:`object-relational mapper </ref/models/querysets>`, :meth:`items()` doesn't have to return model instances. Although you get a few bits of functionality "for free" by using Django models, :meth:`items()` can return any type of object you want. @@ -123,7 +121,7 @@ into those elements. both. If you want to do any special formatting for either the title or - description, :ref:`Django templates <topics-templates>` can be used + description, :doc:`Django templates </topics/templates>` can be used instead. Their paths can be specified with the ``title_template`` and ``description_template`` attributes on the :class:`~django.contrib.syndication.views.Feed` class. The templates are @@ -167,7 +165,7 @@ police beat in Chicago. It'd be silly to create a separate :class:`~django.contrib.syndication.views.Feed` class for each police beat; that would violate the :ref:`DRY principle <dry>` and would couple data to programming logic. Instead, the syndication framework lets you access the -arguments passed from your :ref:`URLconf <topics-http-urls>` so feeds can output +arguments passed from your :doc:`URLconf </topics/http/urls>` so feeds can output items based on information in the feed's URL. On chicagocrime.org, the police-beat feeds are accessible via URLs like this: @@ -175,7 +173,7 @@ On chicagocrime.org, the police-beat feeds are accessible via URLs like this: * :file:`/beats/613/rss/` -- Returns recent crimes for beat 613. * :file:`/beats/1424/rss/` -- Returns recent crimes for beat 1424. -These can be matched with a :ref:`URLconf <topics-http-urls>` line such as:: +These can be matched with a :doc:`URLconf </topics/http/urls>` line such as:: (r'^beats/(?P<beat_id>\d+)/rss/$', BeatFeed()), diff --git a/docs/ref/contrib/webdesign.txt b/docs/ref/contrib/webdesign.txt index e69ad49232..d355d03565 100644 --- a/docs/ref/contrib/webdesign.txt +++ b/docs/ref/contrib/webdesign.txt @@ -1,5 +1,3 @@ -.. _ref-contrib-webdesign: - ======================== django.contrib.webdesign ======================== @@ -9,13 +7,13 @@ django.contrib.webdesign rather than Web *developers*. The ``django.contrib.webdesign`` package, part of the -:ref:`"django.contrib" add-ons <ref-contrib-index>`, provides various Django +:doc:`"django.contrib" add-ons </ref/contrib/index>`, provides various Django helpers that are particularly useful to Web *designers* (as opposed to developers). At present, the package contains only a single template tag. If you have ideas for Web-designer-friendly functionality in Django, please -:ref:`suggest them <internals-contributing>`. +:doc:`suggest them </internals/contributing>`. Template tags ============= diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt index 3a7c3eaaca..edb2c026dd 100644 --- a/docs/ref/databases.txt +++ b/docs/ref/databases.txt @@ -1,5 +1,3 @@ -.. _ref-databases: - ========= Databases ========= @@ -18,6 +16,22 @@ documentation or reference manuals. PostgreSQL notes ================ +.. versionchanged:: 1.3 + +Django supports PostgreSQL 8.0 and higher. If you want to use +:ref:`database-level autocommit <postgresql-autocommit-mode>`, a +minimum version of PostgreSQL 8.2 is required. + +.. admonition:: Improvements in recent PostgreSQL versions + + PostgreSQL 8.0 and 8.1 `will soon reach end-of-life`_; there have + also been a number of significant performance improvements added + in recent PostgreSQL versions. Although PostgreSQL 8.0 is the minimum + supported version, you would be well advised to use a more recent + version if at all possible. + +.. _will soon reach end-of-life: http://wiki.postgresql.org/wiki/PostgreSQL_Release_Support_Policy + PostgreSQL 8.2 to 8.2.4 ----------------------- @@ -34,11 +48,13 @@ aggregate with a database backend that falls within the affected release range. Transaction handling --------------------- -:ref:`By default <topics-db-transactions>`, Django starts a transaction when a +:doc:`By default </topics/db/transactions>`, Django starts a transaction when a database connection is first used and commits the result at the end of the request/response handling. The PostgreSQL backends normally operate the same as any other Django backend in this respect. +.. _postgresql-autocommit-mode: + Autocommit mode ~~~~~~~~~~~~~~~ @@ -84,6 +100,7 @@ protection for multi-call operations. Indexes for ``varchar`` and ``text`` columns ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + .. versionadded:: 1.1.2 When specifying ``db_index=True`` on your model fields, Django typically @@ -247,7 +264,7 @@ table (usually called ``django_session``) and the Connecting to the database -------------------------- -Refer to the :ref:`settings documentation <ref-settings>`. +Refer to the :doc:`settings documentation </ref/settings>`. Connection settings are used in this order: diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt index 0918f5c1f4..6d0993c7f0 100644 --- a/docs/ref/django-admin.txt +++ b/docs/ref/django-admin.txt @@ -1,5 +1,3 @@ -.. _ref-django-admin: - ============================= django-admin.py and manage.py ============================= @@ -104,9 +102,9 @@ compilemessages Before 1.0 this was the "bin/compile-messages.py" command. Compiles .po files created with ``makemessages`` to .mo files for use with -the builtin gettext support. See :ref:`topics-i18n`. +the builtin gettext support. See :doc:`/topics/i18n/index`. -Use the :djadminopt:`--locale`` option to specify the locale to process. +Use the :djadminopt:`--locale` option to specify the locale to process. If not provided, all locales are processed. Example usage:: @@ -119,7 +117,7 @@ createcachetable .. django-admin:: createcachetable Creates a cache table named ``tablename`` for use with the database cache -backend. See :ref:`topics-cache` for more information. +backend. See :doc:`/topics/cache` for more information. .. versionadded:: 1.2 @@ -151,8 +149,8 @@ using the ``--username`` and ``--email`` arguments on the command line. If either of those is not supplied, ``createsuperuser`` will prompt for it when running interactively. -This command is only available if Django's :ref:`authentication system -<topics-auth>` (``django.contrib.auth``) is installed. +This command is only available if Django's :doc:`authentication system +</topics/auth>` (``django.contrib.auth``) is installed. dbshell ------- @@ -210,6 +208,12 @@ records to dump. If you're using a :ref:`custom manager <custom-managers>` as the default manager and it filters some of the available records, not all of the objects will be dumped. +.. versionadded:: 1.3 + +The :djadminopt:`--all` option may be provided to specify that +``dumpdata`` should use Django's base manager, dumping records which +might otherwise be filtered or modified by a custom manager. + .. django-admin-option:: --format <fmt> By default, ``dumpdata`` will format its output in JSON, but you can use the @@ -227,6 +231,11 @@ pretty-print the output with a number of indentation spaces. The :djadminopt:`--exclude` option may be provided to prevent specific applications from being dumped. +.. versionadded:: 1.3 + +The :djadminopt:`--exclude` option may also be provided to prevent specific +models (specified as in the form of ``appname.ModelName``) from being dumped. + .. versionadded:: 1.1 In addition to specifying application names, you can provide a list of @@ -268,7 +277,6 @@ prompts. The :djadminopt:`--database` option may be used to specify the database to flush. - inspectdb --------- @@ -524,8 +532,8 @@ runfcgi [options] .. django-admin:: runfcgi Starts a set of FastCGI processes suitable for use with any Web server that -supports the FastCGI protocol. See the :ref:`FastCGI deployment documentation -<howto-deployment-fastcgi>` for details. Requires the Python FastCGI module from +supports the FastCGI protocol. See the :doc:`FastCGI deployment documentation +</howto/deployment/fastcgi>` for details. Requires the Python FastCGI module from `flup`_. .. _flup: http://www.saddi.com/software/flup/ @@ -611,7 +619,7 @@ Serving static files with the development server By default, the development server doesn't serve any static files for your site (such as CSS files, images, things under ``MEDIA_URL`` and so forth). If -you want to configure Django to serve static media, read :ref:`howto-static-files`. +you want to configure Django to serve static media, read :doc:`/howto/static-files`. shell ----- @@ -804,8 +812,6 @@ with an appropriate extension (e.g. ``json`` or ``xml``). See the documentation for ``loaddata`` for details on the specification of fixture data files. ---noinput -~~~~~~~~~ The :djadminopt:`--noinput` option may be provided to suppress all user prompts. @@ -819,7 +825,7 @@ test <app or test identifier> .. django-admin:: test -Runs tests for all installed models. See :ref:`topics-testing` for more +Runs tests for all installed models. See :doc:`/topics/testing` for more information. .. versionadded:: 1.2 @@ -844,7 +850,7 @@ For example, this command:: ...would perform the following steps: - 1. Create a test database, as described in :ref:`topics-testing`. + 1. Create a test database, as described in :doc:`/topics/testing`. 2. Populate the test database with fixture data from the given fixtures. (For more on fixtures, see the documentation for ``loaddata`` above.) 3. Runs the Django development server (as in ``runserver``), pointed at @@ -852,7 +858,7 @@ For example, this command:: This is useful in a number of ways: - * When you're writing :ref:`unit tests <topics-testing>` of how your views + * When you're writing :doc:`unit tests </topics/testing>` of how your views act with certain fixture data, you can use ``testserver`` to interact with the views in a Web browser, manually. @@ -889,6 +895,11 @@ To run on 1.2.3.4:7000 with a ``test`` fixture:: django-admin.py testserver --addrport 1.2.3.4:7000 test +.. versionadded:: 1.3 + +The :djadminopt:`--noinput` option may be provided to suppress all user +prompts. + validate -------- @@ -954,6 +965,7 @@ that ``django-admin.py`` should print to the console. * ``0`` means no output. * ``1`` means normal output (default). * ``2`` means verbose output. + * ``3`` means *very* verbose output. Common options ============== @@ -1107,4 +1119,4 @@ distribution. It enables tab-completion of ``django-admin.py`` and with ``sql``. -See :ref:`howto-custom-management-commands` for how to add customized actions. +See :doc:`/howto/custom-management-commands` for how to add customized actions. diff --git a/docs/ref/exceptions.txt b/docs/ref/exceptions.txt index 1fc9b177d4..4a4384376b 100644 --- a/docs/ref/exceptions.txt +++ b/docs/ref/exceptions.txt @@ -1,5 +1,3 @@ -.. _ref-exceptions: - ================= Django Exceptions ================= diff --git a/docs/ref/files/file.txt b/docs/ref/files/file.txt index 1ea5865ea7..f4ae59f241 100644 --- a/docs/ref/files/file.txt +++ b/docs/ref/files/file.txt @@ -1,5 +1,3 @@ -.. _ref-files-file: - The ``File`` object =================== @@ -20,14 +18,14 @@ Django's ``File`` has the following attributes and methods: The absolute path to the file's location on a local filesystem. - :ref:`Custom file storage systems <howto-custom-file-storage>` may not store + :doc:`Custom file storage systems </howto/custom-file-storage>` may not store files locally; files stored on these systems will have a ``path`` of ``None``. .. attribute:: File.url The URL where the file can be retrieved. This is often useful in - :ref:`templates <topics-templates>`; for example, a bit of a template for + :doc:`templates </topics/templates>`; for example, a bit of a template for displaying a ``Car`` (see above) might look like: .. code-block:: html+django diff --git a/docs/ref/files/index.txt b/docs/ref/files/index.txt index 1d59d5fa23..171fcc6391 100644 --- a/docs/ref/files/index.txt +++ b/docs/ref/files/index.txt @@ -1,5 +1,3 @@ -.. _ref-files-index: - ============= File handling ============= diff --git a/docs/ref/files/storage.txt b/docs/ref/files/storage.txt index c8aafa8626..2b055bb60b 100644 --- a/docs/ref/files/storage.txt +++ b/docs/ref/files/storage.txt @@ -1,5 +1,3 @@ -.. _ref-files-storage: - File storage API ================ diff --git a/docs/ref/forms/api.txt b/docs/ref/forms/api.txt index 0d174ea4af..613d7544a9 100644 --- a/docs/ref/forms/api.txt +++ b/docs/ref/forms/api.txt @@ -1,5 +1,3 @@ -.. _ref-forms-api: - ============= The Forms API ============= @@ -11,7 +9,7 @@ The Forms API .. admonition:: About this document This document covers the gritty details of Django's forms API. You should - read the :ref:`introduction to working with forms <topics-forms-index>` + read the :doc:`introduction to working with forms </topics/forms/index>` first. .. _ref-forms-api-bound-unbound: @@ -262,7 +260,7 @@ for each field in the "Built-in ``Field`` classes" section below. You can write code to perform validation for particular form fields (based on their name) or for the form as a whole (considering combinations of various -fields). More information about this is in :ref:`ref-forms-validation`. +fields). More information about this is in :doc:`/ref/forms/validation`. Outputting forms as HTML ------------------------ diff --git a/docs/ref/forms/fields.txt b/docs/ref/forms/fields.txt index 396e51046e..0dd9095a77 100644 --- a/docs/ref/forms/fields.txt +++ b/docs/ref/forms/fields.txt @@ -1,5 +1,3 @@ -.. _ref-forms-fields: - =========== Form fields =========== @@ -192,7 +190,7 @@ The callable will be evaluated only when the unbound form is displayed, not when .. attribute:: Field.widget The ``widget`` argument lets you specify a ``Widget`` class to use when -rendering this ``Field``. See :ref:`ref-forms-widgets` for more information. +rendering this ``Field``. See :doc:`/ref/forms/widgets` for more information. ``help_text`` ~~~~~~~~~~~~~ @@ -267,7 +265,7 @@ error message keys it uses. The ``validators`` argument lets you provide a list of validation functions for this field. -See the :ref:`validators documentation <ref-validators>` for more information. +See the :doc:`validators documentation </ref/validators>` for more information. ``localize`` ~~~~~~~~~~~~ @@ -516,8 +514,8 @@ given length. * Validates that non-empty file data has been bound to the form. * Error message keys: ``required``, ``invalid``, ``missing``, ``empty`` -To learn more about the ``UploadedFile`` object, see the :ref:`file uploads -documentation <topics-http-file-uploads>`. +To learn more about the ``UploadedFile`` object, see the :doc:`file uploads +documentation </topics/http/file-uploads>`. When you use a ``FileField`` in a form, you must also remember to :ref:`bind the file data to the form <binding-uploaded-files>`. diff --git a/docs/ref/forms/index.txt b/docs/ref/forms/index.txt index e310863c7a..610416a363 100644 --- a/docs/ref/forms/index.txt +++ b/docs/ref/forms/index.txt @@ -1,10 +1,8 @@ -.. _ref-forms-index: - ===== Forms ===== -Detailed form API reference. For introductory material, see :ref:`topics-forms-index`. +Detailed form API reference. For introductory material, see :doc:`/topics/forms/index`. .. toctree:: :maxdepth: 1 diff --git a/docs/ref/forms/validation.txt b/docs/ref/forms/validation.txt index 911496c9ae..1c047f246f 100644 --- a/docs/ref/forms/validation.txt +++ b/docs/ref/forms/validation.txt @@ -1,5 +1,3 @@ -.. _ref-forms-validation: - Form and field validation ========================= @@ -180,7 +178,7 @@ Using validators .. versionadded:: 1.2 Django's form (and model) fields support use of simple utility functions and -classes known as validators. These can passed to a field's constructor, via +classes known as validators. These can be passed to a field's constructor, via the field's ``validators`` argument, or defined on the Field class itself with the ``default_validators`` attribute. diff --git a/docs/ref/forms/widgets.txt b/docs/ref/forms/widgets.txt index 1fc2bfa85d..05215d4d8e 100644 --- a/docs/ref/forms/widgets.txt +++ b/docs/ref/forms/widgets.txt @@ -1,12 +1,10 @@ -.. _ref-forms-widgets: - ======= Widgets ======= .. module:: django.forms.widgets :synopsis: Django's built-in form widgets. - + .. currentmodule:: django.forms A widget is Django's representation of a HTML input element. The widget @@ -29,7 +27,12 @@ commonly used groups of widgets: .. attribute:: PasswordInput.render_value Determines whether the widget will have a value filled in when the - form is re-displayed after a validation error (default is ``True``). + form is re-displayed after a validation error (default is ``False``). + +.. versionchanged:: 1.3 + The default value for + :attr:`~PasswordInput.render_value` was + changed from ``True`` to ``False`` .. class:: HiddenInput @@ -50,7 +53,7 @@ commonly used groups of widgets: Date input as a simple text box: ``<input type='text' ...>`` Takes one optional argument: - + .. attribute:: DateInput.format The format in which this field's initial value will be displayed. @@ -64,11 +67,11 @@ commonly used groups of widgets: Date/time input as a simple text box: ``<input type='text' ...>`` Takes one optional argument: - + .. attribute:: DateTimeInput.format - + The format in which this field's initial value will be displayed. - + If no ``format`` argument is provided, the default format is ``'%Y-%m-%d %H:%M:%S'``. @@ -77,11 +80,11 @@ commonly used groups of widgets: Time input as a simple text box: ``<input type='text' ...>`` Takes one optional argument: - + .. attribute:: TimeInput.format - + The format in which this field's initial value will be displayed. - + If no ``format`` argument is provided, the default format is ``'%H:%M:%S'``. .. versionchanged:: 1.1 @@ -98,15 +101,15 @@ commonly used groups of widgets: Takes one optional argument: .. attribute:: CheckboxInput.check_test - - A callable that takes the value of the CheckBoxInput + + A callable that takes the value of the CheckBoxInput and returns ``True`` if the checkbox should be checked for - that value. + that value. .. class:: Select Select widget: ``<select><option ...>...</select>`` - + Requires that your field provides :attr:`~Field.choices`. .. class:: NullBooleanSelect @@ -123,22 +126,22 @@ commonly used groups of widgets: .. class:: RadioSelect A list of radio buttons: - + .. code-block:: html - + <ul> <li><input type='radio' ...></li> ... </ul> - + Requires that your field provides :attr:`~Field.choices`. .. class:: CheckboxSelectMultiple A list of checkboxes: - + .. code-block:: html - + <ul> <li><input type='checkbox' ...></li> ... @@ -155,7 +158,7 @@ commonly used groups of widgets: Takes two optional arguments, ``date_format`` and ``time_format``, which work just like the ``format`` argument for ``DateInput`` and ``TimeInput``. - + .. versionchanged:: 1.1 The ``date_format`` and ``time_format`` arguments were not supported in Django 1.0. diff --git a/docs/ref/generic-views.txt b/docs/ref/generic-views.txt index 574db88a60..a7d67c74e3 100644 --- a/docs/ref/generic-views.txt +++ b/docs/ref/generic-views.txt @@ -1,5 +1,3 @@ -.. _ref-generic-views: - ============= Generic views ============= @@ -9,8 +7,8 @@ again and again. In Django, the most common of these patterns have been abstracted into "generic views" that let you quickly provide common views of an object without actually needing to write any Python code. -A general introduction to generic views can be found in the :ref:`topic guide -<topics-generic-views>`. +A general introduction to generic views can be found in the :doc:`topic guide +</topics/http/generic-views>`. This reference contains details of Django's built-in generic views, along with a list of all keyword arguments that a generic view expects. Remember that @@ -18,7 +16,7 @@ arguments may either come from the URL pattern or from the ``extra_context`` additional-information dictionary. Most generic views require the ``queryset`` key, which is a ``QuerySet`` -instance; see :ref:`topics-db-queries` for more information about ``QuerySet`` +instance; see :doc:`/topics/db/queries` for more information about ``QuerySet`` objects. "Simple" generic views @@ -90,9 +88,15 @@ If the given URL is ``None``, Django will return an ``HttpResponseGone`` (410). redirect will use status code 301. If ``False``, then the redirect will use status code 302. By default, ``permanent`` is ``True``. + * ``query_string``: Whether to pass along the GET query string to + the new location. If ``True``, then the query string is appended + to the URL. If ``False``, then the query string is discarded. By + default, ``query_string`` is ``False``. + .. versionadded:: 1.1 The ``permanent`` keyword argument is new in Django 1.1. + **Example:** This example issues a permanent redirect (HTTP status code 301) from @@ -766,8 +770,8 @@ specify the page number in the URL in one of two ways: These values and lists are 1-based, not 0-based, so the first page would be represented as page ``1``. -For more on pagination, read the :ref:`pagination documentation -<topics-pagination>`. +For more on pagination, read the :doc:`pagination documentation +</topics/pagination>`. .. versionadded:: 1.0 @@ -858,8 +862,8 @@ for creating, editing and deleting objects. .. versionchanged:: 1.0 ``django.views.generic.create_update.create_object`` and -``django.views.generic.create_update.update_object`` now use the new :ref:`forms -library <topics-forms-index>` to build and display the form. +``django.views.generic.create_update.update_object`` now use the new :doc:`forms +library </topics/forms/index>` to build and display the form. ``django.views.generic.create_update.create_object`` ---------------------------------------------------- @@ -875,7 +879,7 @@ validation errors (if there are any) and saving the object. If you provide ``form_class``, it should be a ``django.forms.ModelForm`` subclass. Use this argument when you need to customize the model's form. - See the :ref:`ModelForm docs <topics-forms-modelforms>` for more + See the :doc:`ModelForm docs </topics/forms/modelforms>` for more information. Otherwise, ``model`` should be a Django model class and the form used @@ -892,7 +896,7 @@ validation errors (if there are any) and saving the object. * ``login_required``: A boolean that designates whether a user must be logged in, in order to see the page and save changes. This hooks into the - Django :ref:`authentication system <topics-auth>`. By default, this is + Django :doc:`authentication system </topics/auth>`. By default, this is ``False``. If this is ``True``, and a non-logged-in user attempts to visit this page @@ -932,7 +936,7 @@ In addition to ``extra_context``, the template's context will be: <p>{{ form.address.label_tag }} {{ form.address }}</p> </form> - See the :ref:`forms documentation <topics-forms-index>` for more + See the :doc:`forms documentation </topics/forms/index>` for more information about using ``Form`` objects in templates. ``django.views.generic.create_update.update_object`` @@ -951,7 +955,7 @@ model class. If you provide ``form_class``, it should be a ``django.forms.ModelForm`` subclass. Use this argument when you need to customize the model's form. - See the :ref:`ModelForm docs <topics-forms-modelforms>` for more + See the :doc:`ModelForm docs </topics/forms/modelforms>` for more information. Otherwise, ``model`` should be a Django model class and the form used @@ -977,7 +981,7 @@ model class. * ``login_required``: A boolean that designates whether a user must be logged in, in order to see the page and save changes. This hooks into the - Django :ref:`authentication system <topics-auth>`. By default, this is + Django :doc:`authentication system </topics/auth>`. By default, this is ``False``. If this is ``True``, and a non-logged-in user attempts to visit this page @@ -1020,7 +1024,7 @@ In addition to ``extra_context``, the template's context will be: <p>{{ form.address.label_tag }} {{ form.address }}</p> </form> - See the :ref:`forms documentation <topics-forms-index>` for more + See the :doc:`forms documentation </topics/forms/index>` for more information about using ``Form`` objects in templates. * ``object``: The original object being edited. This variable's name @@ -1059,7 +1063,7 @@ contain a form that POSTs to the same URL. * ``login_required``: A boolean that designates whether a user must be logged in, in order to see the page and save changes. This hooks into the - Django :ref:`authentication system <topics-auth>`. By default, this is + Django :doc:`authentication system </topics/auth>`. By default, this is ``False``. If this is ``True``, and a non-logged-in user attempts to visit this page diff --git a/docs/ref/index.txt b/docs/ref/index.txt index a2088ed6ce..09194178af 100644 --- a/docs/ref/index.txt +++ b/docs/ref/index.txt @@ -1,5 +1,3 @@ -.. _ref-index: - ============= API Reference ============= diff --git a/docs/ref/middleware.txt b/docs/ref/middleware.txt index 2afd79038f..290ea2736d 100644 --- a/docs/ref/middleware.txt +++ b/docs/ref/middleware.txt @@ -1,5 +1,3 @@ -.. _ref-middleware: - ========== Middleware ========== @@ -9,7 +7,7 @@ Middleware This document explains all middleware components that come with Django. For information on how how to use them and how to write your own middleware, see -the :ref:`middleware usage guide <topics-http-middleware>`. +the :doc:`middleware usage guide </topics/http/middleware>`. Available middleware ==================== @@ -26,7 +24,7 @@ Cache middleware Enable the site-wide cache. If these are enabled, each Django-powered page will be cached for as long as the :setting:`CACHE_MIDDLEWARE_SECONDS` setting -defines. See the :ref:`cache documentation <topics-cache>`. +defines. See the :doc:`cache documentation </topics/cache>`. "Common" middleware ------------------- @@ -136,8 +134,8 @@ Locale middleware .. class:: django.middleware.locale.LocaleMiddleware Enables language selection based on data from the request. It customizes -content for each user. See the :ref:`internationalization documentation -<topics-i18n>`. +content for each user. See the :doc:`internationalization documentation +</topics/i18n/index>`. Message middleware ------------------ @@ -151,7 +149,7 @@ Message middleware ``MessageMiddleware`` was added. Enables cookie- and session-based message support. See the -:ref:`messages documentation <ref-contrib-messages>`. +:doc:`messages documentation </ref/contrib/messages>`. Session middleware ------------------ @@ -161,8 +159,8 @@ Session middleware .. class:: django.contrib.sessions.middleware.SessionMiddleware -Enables session support. See the :ref:`session documentation -<topics-http-sessions>`. +Enables session support. See the :doc:`session documentation +</topics/http/sessions>`. Authentication middleware ------------------------- @@ -173,8 +171,8 @@ Authentication middleware .. class:: django.contrib.auth.middleware.AuthenticationMiddleware Adds the ``user`` attribute, representing the currently-logged-in user, to -every incoming ``HttpRequest`` object. See :ref:`Authentication in Web requests -<topics-auth>`. +every incoming ``HttpRequest`` object. See :doc:`Authentication in Web requests +</topics/auth>`. CSRF protection middleware -------------------------- @@ -189,7 +187,7 @@ CSRF protection middleware Adds protection against Cross Site Request Forgeries by adding hidden form fields to POST forms and checking requests for the correct value. See the -:ref:`Cross Site Request Forgery protection documentation <ref-contrib-csrf>`. +:doc:`Cross Site Request Forgery protection documentation </ref/contrib/csrf>`. Transaction middleware ---------------------- @@ -208,4 +206,4 @@ running outside of it run with commit-on-save - the default Django behavior. Middleware modules running inside it (coming later in the stack) will be under the same transaction control as the view functions. -See the :ref:`transaction management documentation <topics-db-transactions>`. +See the :doc:`transaction management documentation </topics/db/transactions>`. diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt index 3a0066987f..68208b3bfe 100644 --- a/docs/ref/models/fields.txt +++ b/docs/ref/models/fields.txt @@ -1,5 +1,3 @@ -.. _ref-models-fields: - ===================== Model field reference ===================== @@ -14,8 +12,8 @@ This document contains all the gory details about all the `field options`_ and .. seealso:: - If the built-in fields don't do the trick, you can easily :ref:`write your - own custom model fields <howto-custom-model-fields>`. + If the built-in fields don't do the trick, you can easily :doc:`write your + own custom model fields </howto/custom-model-fields>`. .. note:: @@ -293,8 +291,6 @@ A human-readable name for the field. If the verbose name isn't given, Django will automatically create it using the field's attribute name, converting underscores to spaces. See :ref:`Verbose field names <verbose-field-names>`. -.. _model-field-types: - ``validators`` ------------------- @@ -302,9 +298,10 @@ underscores to spaces. See :ref:`Verbose field names <verbose-field-names>`. .. attribute:: Field.validators -A list of validators to run for this field.See the :ref:`validators -documentation <ref-validators>` for more information. +A list of validators to run for this field.See the :doc:`validators +documentation </ref/validators>` for more information. +.. _model-field-types: Field types =========== @@ -370,8 +367,8 @@ The admin represents this as an ``<input type="text">`` (a single-line input). If you are writing an application that must be portable to multiple database backends, you should be aware that there are restrictions on - ``max_length`` for some backends. Refer to the :ref:`database backend - notes <ref-databases>` for details. + ``max_length`` for some backends. Refer to the :doc:`database backend + notes </ref/databases>` for details. .. admonition:: MySQL users @@ -518,7 +515,7 @@ Also has one optional argument: .. versionadded:: 1.0 Optional. A storage object, which handles the storage and retrieval of your - files. See :ref:`topics-files` for details on how to provide this object. + files. See :doc:`/topics/files` for details on how to provide this object. The admin represents this field as an ``<input type="file">`` (a file-upload widget). @@ -553,7 +550,7 @@ day. If you upload a file on Jan. 15, 2007, it will be saved in the directory If you want to retrieve the upload file's on-disk filename, or a URL that refers to that file, or the file's size, you can use the :attr:`~django.core.files.File.name`, :attr:`~django.core.files.File.url` -and :attr:`~django.core.files.File.size` attributes; see :ref:`topics-files`. +and :attr:`~django.core.files.File.size` attributes; see :doc:`/topics/files`. Note that whenever you deal with uploaded files, you should pay close attention to where you're uploading them and what type of files they are, to avoid @@ -903,7 +900,7 @@ define the details of how the relation works. .. attribute:: ForeignKey.limit_choices_to - A dictionary of lookup arguments and values (see :ref:`topics-db-queries`) + A dictionary of lookup arguments and values (see :doc:`/topics/db/queries`) that limit the available admin choices for this object. Use this with functions from the Python ``datetime`` module to limit choices of objects by date. For example:: diff --git a/docs/ref/models/index.txt b/docs/ref/models/index.txt index 64b47b26cc..b5896c35ed 100644 --- a/docs/ref/models/index.txt +++ b/docs/ref/models/index.txt @@ -1,10 +1,8 @@ -.. _ref-models-index: - ====== Models ====== -Model API reference. For introductory material, see :ref:`topics-db-models`. +Model API reference. For introductory material, see :doc:`/topics/db/models`. .. toctree:: :maxdepth: 1 diff --git a/docs/ref/models/instances.txt b/docs/ref/models/instances.txt index 7e6cdeb5c7..dfe4c747e8 100644 --- a/docs/ref/models/instances.txt +++ b/docs/ref/models/instances.txt @@ -1,5 +1,3 @@ -.. _ref-models-instances: - ======================== Model instance reference ======================== @@ -7,13 +5,13 @@ Model instance reference .. currentmodule:: django.db.models This document describes the details of the ``Model`` API. It builds on the -material presented in the :ref:`model <topics-db-models>` and :ref:`database -query <topics-db-queries>` guides, so you'll probably want to read and +material presented in the :doc:`model </topics/db/models>` and :doc:`database +query </topics/db/queries>` guides, so you'll probably want to read and understand those documents before reading this one. Throughout this reference we'll use the :ref:`example weblog models -<queryset-model-example>` presented in the :ref:`database query guide -<topics-db-queries>`. +<queryset-model-example>` presented in the :doc:`database query guide +</topics/db/queries>`. Creating objects ================ @@ -45,8 +43,8 @@ All three steps are performed when you call by a model's When you use a ``ModelForm``, the call to ``is_valid()`` will perform these validation steps for all the fields that are included on the -form. (See the :ref:`ModelForm documentation -<topics-forms-modelforms>` for more information.) You should only need +form. (See the :doc:`ModelForm documentation +</topics/forms/modelforms>` for more information.) You should only need to call a model's ``full_clean()`` method if you plan to handle validation errors yourself, or if you have excluded fields from the ModelForm that require validation. @@ -107,9 +105,9 @@ special key that is used for errors that are tied to the entire model instead of to a specific field. You can access these errors with ``NON_FIELD_ERRORS``:: - from django.core.validators import ValidationError, NON_FIELD_ERRORS + from django.core.exceptions import ValidationError, NON_FIELD_ERRORS try: - article.full_clean(): + article.full_clean() except ValidationError, e: non_field_errors = e.message_dict[NON_FIELD_ERRORS] @@ -215,7 +213,7 @@ What happens when you save? When you save an object, Django performs the following steps: - 1. **Emit a pre-save signal.** The :ref:`signal <ref-signals>` + 1. **Emit a pre-save signal.** The :doc:`signal </ref/signals>` :attr:`django.db.models.signals.pre_save` is sent, allowing any functions listening for that signal to take some customized action. @@ -426,8 +424,8 @@ Django uses this in its admin interface. If an object defines link that will jump you directly to the object's public view, according to ``get_absolute_url()``. -Also, a couple of other bits of Django, such as the :ref:`syndication feed -framework <ref-contrib-syndication>`, use ``get_absolute_url()`` as a +Also, a couple of other bits of Django, such as the :doc:`syndication feed +framework </ref/contrib/syndication>`, use ``get_absolute_url()`` as a convenience to reward people who've defined the method. It's good practice to use ``get_absolute_url()`` in templates, instead of @@ -517,14 +515,14 @@ the ``url`` function):: ...and then using that name to perform the reverse URL resolution instead of the view name:: - from django.db.models import permalink + from django.db import models @models.permalink def get_absolute_url(self): return ('people_view', [str(self.id)]) -More details on named URL patterns are in the :ref:`URL dispatch documentation -<topics-http-urls>`. +More details on named URL patterns are in the :doc:`URL dispatch documentation +</topics/http/urls>`. Extra instance methods ====================== @@ -570,3 +568,5 @@ described in :ref:`Field lookups <field-lookups>`. Note that in the case of identical date values, these methods will use the ID as a fallback check. This guarantees that no records are skipped or duplicated. + +That also means you cannot use those methods on unsaved objects. diff --git a/docs/ref/models/options.txt b/docs/ref/models/options.txt index f3e7363e36..3dfcdfffbc 100644 --- a/docs/ref/models/options.txt +++ b/docs/ref/models/options.txt @@ -1,5 +1,3 @@ -.. _ref-models-options: - ====================== Model ``Meta`` options ====================== diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt index 56ff6444e4..87680d3e4d 100644 --- a/docs/ref/models/querysets.txt +++ b/docs/ref/models/querysets.txt @@ -1,5 +1,3 @@ -.. _ref-models-querysets: - ====================== QuerySet API reference ====================== @@ -7,13 +5,13 @@ QuerySet API reference .. currentmodule:: django.db.models.QuerySet This document describes the details of the ``QuerySet`` API. It builds on the -material presented in the :ref:`model <topics-db-models>` and :ref:`database -query <topics-db-queries>` guides, so you'll probably want to read and +material presented in the :doc:`model </topics/db/models>` and :doc:`database +query </topics/db/queries>` guides, so you'll probably want to read and understand those documents before reading this one. Throughout this reference we'll use the :ref:`example weblog models -<queryset-model-example>` presented in the :ref:`database query guide -<topics-db-queries>`. +<queryset-model-example>` presented in the :doc:`database query guide +</topics/db/queries>`. .. _when-querysets-are-evaluated: @@ -223,8 +221,8 @@ control the name of the annotation:: >>> q[0].number_of_entries 42 -For an in-depth discussion of aggregation, see :ref:`the topic guide on -Aggregation <topics-db-aggregation>`. +For an in-depth discussion of aggregation, see :doc:`the topic guide on +Aggregation </topics/db/aggregation>`. ``order_by(*fields)`` ~~~~~~~~~~~~~~~~~~~~~ @@ -332,8 +330,6 @@ a model which defines a default ordering, or when using ordering was undefined prior to calling ``reverse()``, and will remain undefined afterward). -.. _queryset-distinct: - ``distinct()`` ~~~~~~~~~~~~~~ @@ -367,9 +363,6 @@ query spans multiple tables, it's possible to get duplicate results when a ``values()`` together, be careful when ordering by fields not in the ``values()`` call. - -.. _queryset-values: - ``values(*fields)`` ~~~~~~~~~~~~~~~~~~~ @@ -679,8 +672,6 @@ related object. ``OneToOneFields`` will not be traversed in the reverse direction if you are performing a depth-based ``select_related``. -.. _queryset-extra: - ``extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -712,9 +703,10 @@ of the arguments is required, but you should use at least one of them. greater than Jan. 1, 2006. Django inserts the given SQL snippet directly into the ``SELECT`` - statement, so the resulting SQL of the above example would be:: + statement, so the resulting SQL of the above example would be something + like:: - SELECT blog_entry.*, (pub_date > '2006-01-01') + SELECT blog_entry.*, (pub_date > '2006-01-01') AS is_recent FROM blog_entry; @@ -843,8 +835,6 @@ of the arguments is required, but you should use at least one of them. Entry.objects.extra(where=['headline=%s'], params=['Lennon']) -.. _queryset-defer: - ``defer(*fields)`` ~~~~~~~~~~~~~~~~~~ @@ -870,7 +860,7 @@ deferred field will be retrieved from the database if you access that field You can make multiple calls to ``defer()``. Each call adds new fields to the deferred set:: - # Defers both the body and lede fields. + # Defers both the body and headline fields. Entry.objects.defer("body").filter(rating=5).defer("headline") The order in which fields are added to the deferred set does not matter. Calling ``defer()`` with a field name that has already been deferred is harmless (the field will still be deferred). @@ -930,7 +920,7 @@ immediately; the remainder are deferred. Thus, successive calls to ``only()`` result in only the final fields being considered:: # This will defer all fields except the headline. - Entry.objects.only("body", "lede").only("headline") + Entry.objects.only("body", "rating").only("headline") Since ``defer()`` acts incrementally (adding fields to the deferred list), you can combine calls to ``only()`` and ``defer()`` and things will behave @@ -973,8 +963,6 @@ something *other than* a ``QuerySet``. These methods do not use a cache (see :ref:`caching-and-querysets`). Rather, they query the database each time they're called. -.. _get-kwargs: - ``get(**kwargs)`` ~~~~~~~~~~~~~~~~~ @@ -1143,8 +1131,6 @@ Example:: If you pass ``in_bulk()`` an empty list, you'll get an empty dictionary. -.. _queryset-iterator: - ``iterator()`` ~~~~~~~~~~~~~~ @@ -1216,8 +1202,8 @@ control the name of the aggregation value that is returned:: >>> q = Blog.objects.aggregate(number_of_entries=Count('entry')) {'number_of_entries': 16} -For an in-depth discussion of aggregation, see :ref:`the topic guide on -Aggregation <topics-db-aggregation>`. +For an in-depth discussion of aggregation, see :doc:`the topic guide on +Aggregation </topics/db/aggregation>`. ``exists()`` ~~~~~~~~~~~~ @@ -1277,7 +1263,7 @@ SQL equivalents:: a Django setting. It's possible to configure your MySQL tables to use case-sensitive comparisons, but some trade-offs are involved. For more information about this, see the :ref:`collation section <mysql-collation>` - in the :ref:`databases <ref-databases>` documentation. + in the :doc:`databases </ref/databases>` documentation. .. fieldlookup:: iexact @@ -1570,7 +1556,7 @@ Example:: SQL equivalent:: - SELECT ... WHERE EXTRACT('year' FROM pub_date) = '2005'; + SELECT ... WHERE pub_date BETWEEN '2005-01-01' AND '2005-12-31 23:59:59.999999'; (The exact SQL syntax varies for each database engine.) @@ -1736,7 +1722,7 @@ Aggregation Functions Django provides the following aggregation functions in the ``django.db.models`` module. For details on how to use these aggregate functions, see -:ref:`the topic guide on aggregation <topics-db-aggregation>`. +:doc:`the topic guide on aggregation </topics/db/aggregation>`. ``Avg`` ~~~~~~~ diff --git a/docs/ref/models/relations.txt b/docs/ref/models/relations.txt index f58cfe7301..0481644d7a 100644 --- a/docs/ref/models/relations.txt +++ b/docs/ref/models/relations.txt @@ -1,5 +1,3 @@ -.. _ref-models-relations: - ========================= Related objects reference ========================= diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt index fa8baf0783..b8b08829e9 100644 --- a/docs/ref/request-response.txt +++ b/docs/ref/request-response.txt @@ -1,5 +1,3 @@ -.. _ref-request-response: - ============================ Request and response objects ============================ @@ -32,10 +30,25 @@ All attributes except ``session`` should be considered read-only. .. attribute:: HttpRequest.path - A string representing the full path to the requested page, not including - the domain. + A string representing the full path to the requested page, not including + the domain. + + Example: ``"/music/bands/the_beatles/"`` + +.. attribute:: HttpRequest.path_info + + Under some web server configurations, the portion of the URL after the host + name is split up into a script prefix portion and a path info portion + (this happens, for example, when using the ``django.root`` option + with the :ref:`modpython handler from Apache <howto-deployment-modpython>`). + The ``path_info`` attribute always contains the path info portion of the + path, no matter what web server is being used. Using this instead of + attr:`~HttpRequest.path` can make your code much easier to move between test + and deployment servers. - Example: ``"/music/bands/the_beatles/"`` + For example, if the ``django.root`` for your application is set to + ``"/minfo"``, then ``path`` might be ``"/minfo/music/bands/the_beatles/"`` + and ``path_info`` would be ``"/music/bands/the_beatles/"``. .. attribute:: HttpRequest.method @@ -106,7 +119,7 @@ All attributes except ``session`` should be considered read-only. * ``chunks(chunk_size=None)`` -- A generator that yields sequential chunks of data. - See :ref:`topics-files` for more information. + See :doc:`/topics/files` for more information. Note that ``FILES`` will only contain data if the request method was POST and the ``<form>`` that posted to the request had @@ -165,14 +178,14 @@ All attributes except ``session`` should be considered read-only. ``user`` is only available if your Django installation has the ``AuthenticationMiddleware`` activated. For more, see - :ref:`topics-auth`. + :doc:`/topics/auth`. .. attribute:: HttpRequest.session A readable-and-writable, dictionary-like object that represents the current session. This is only available if your Django installation has session - support activated. See the :ref:`session documentation - <topics-http-sessions>` for full details. + support activated. See the :doc:`session documentation + </topics/http/sessions>` for full details. .. attribute:: HttpRequest.raw_post_data @@ -286,7 +299,7 @@ a subclass of dictionary. Exceptions are outlined here: .. method:: QueryDict.setdefault(key, default) Just like the standard dictionary ``setdefault()`` method, except it uses - ``__setitem__`` internally. + ``__setitem__()`` internally. .. method:: QueryDict.update(other_dict) @@ -305,7 +318,7 @@ a subclass of dictionary. Exceptions are outlined here: .. method:: QueryDict.items() Just like the standard dictionary ``items()`` method, except this uses the - same last-value logic as ``__getitem()__``. For example:: + same last-value logic as ``__getitem__()``. For example:: >>> q = QueryDict('a=1&a=2&a=3') >>> q.items() @@ -315,7 +328,7 @@ a subclass of dictionary. Exceptions are outlined here: Just like the standard dictionary ``iteritems()`` method. Like :meth:`QueryDict.items()` this uses the same last-value logic as - :meth:`QueryDict.__getitem()__`. + :meth:`QueryDict.__getitem__()`. .. method:: QueryDict.iterlists() @@ -325,7 +338,7 @@ a subclass of dictionary. Exceptions are outlined here: .. method:: QueryDict.values() Just like the standard dictionary ``values()`` method, except this uses the - same last-value logic as ``__getitem()__``. For example:: + same last-value logic as ``__getitem__()``. For example:: >>> q = QueryDict('a=1&a=2&a=3') >>> q.values() @@ -498,11 +511,11 @@ Methods .. method:: HttpResponse.__delitem__(header) Deletes the header with the given name. Fails silently if the header - doesn't exist. Case-sensitive. + doesn't exist. Case-insensitive. .. method:: HttpResponse.__getitem__(header) - Returns the value for the given header name. Case-sensitive. + Returns the value for the given header name. Case-insensitive. .. method:: HttpResponse.has_header(header) @@ -516,8 +529,11 @@ Methods * ``max_age`` should be a number of seconds, or ``None`` (default) if the cookie should last only as long as the client's browser session. - * ``expires`` should be a string in the format - ``"Wdy, DD-Mon-YY HH:MM:SS GMT"``. + If ``expires`` is not specified, it will be calculated. + * ``expires`` should either be a string in the format + ``"Wdy, DD-Mon-YY HH:MM:SS GMT"`` or a ``datetime.datetime`` object + in UTC. If ``expires`` is a ``datetime`` object, the ``max_age`` + will be calculated. * Use ``domain`` if you want to set a cross-domain cookie. For example, ``domain=".lawrence.com"`` will set a cookie that is readable by the domains www.lawrence.com, blogs.lawrence.com and diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index 58f87b9cf4..b5556deac8 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -1,5 +1,3 @@ -.. _ref-settings: - ======== Settings ======== @@ -74,7 +72,7 @@ of (Full name, e-mail address). Example:: (('John', 'john@example.com'), ('Mary', 'mary@example.com')) Note that Django will e-mail *all* of these people whenever an error happens. -See :ref:`howto-error-reporting` for more information. +See :doc:`/howto/error-reporting` for more information. .. setting:: ALLOWED_INCLUDE_ROOTS @@ -99,7 +97,7 @@ APPEND_SLASH Default: ``True`` Whether to append trailing slashes to URLs. This is only used if -``CommonMiddleware`` is installed (see :ref:`topics-http-middleware`). See also +``CommonMiddleware`` is installed (see :doc:`/topics/http/middleware`). See also ``PREPEND_WWW``. .. setting:: AUTHENTICATION_BACKENDS @@ -110,8 +108,8 @@ AUTHENTICATION_BACKENDS Default: ``('django.contrib.auth.backends.ModelBackend',)`` A tuple of authentication backend classes (as strings) to use when attempting to -authenticate a user. See the :ref:`authentication backends documentation -<authentication-backends>` for details. +authenticate a user. See the :doc:`authentication backends documentation +</ref/authbackends>` for details. .. setting:: AUTH_PROFILE_MODULE @@ -130,7 +128,23 @@ CACHE_BACKEND Default: ``'locmem://'`` -The cache backend to use. See :ref:`topics-cache`. +The cache backend to use. See :doc:`/topics/cache`. + +.. setting:: CACHE_MIDDLEWARE_ANONYMOUS_ONLY + +CACHE_MIDDLEWARE_ANONYMOUS_ONLY +------------------------------- + +Default: ``False`` + +If the value of this setting is ``True``, only anonymous requests (i.e., not +those made by a logged-in user) will be cached. Otherwise, the middleware +caches every page that doesn't have GET or POST parameters. + +If you set the value of this setting to ``True``, you should make sure you've +activated ``AuthenticationMiddleware``. + +See the :doc:`cache documentation </topics/cache>` for more information. .. setting:: CACHE_MIDDLEWARE_KEY_PREFIX @@ -140,7 +154,7 @@ CACHE_MIDDLEWARE_KEY_PREFIX Default: ``''`` (Empty string) The cache key prefix that the cache middleware should use. See -:ref:`topics-cache`. +:doc:`/topics/cache`. .. setting:: CACHE_MIDDLEWARE_SECONDS @@ -152,18 +166,6 @@ Default: ``600`` The default number of seconds to cache a page when the caching middleware or ``cache_page()`` decorator is used. -.. setting:: CSRF_COOKIE_NAME - -CSRF_COOKIE_NAME ----------------- - -.. versionadded:: 1.2 - -Default: ``'csrftoken'`` - -The name of the cookie to use for the CSRF authentication token. This can be whatever you -want. See :ref:`ref-contrib-csrf`. - .. setting:: CSRF_COOKIE_DOMAIN CSRF_COOKIE_DOMAIN @@ -179,6 +181,18 @@ request forgery protection. It should be set to a string such as ``".lawrence.com"`` to allow a POST request from a form on one subdomain to be accepted by accepted by a view served from another subdomain. +.. setting:: CSRF_COOKIE_NAME + +CSRF_COOKIE_NAME +---------------- + +.. versionadded:: 1.2 + +Default: ``'csrftoken'`` + +The name of the cookie to use for the CSRF authentication token. This can be whatever you +want. See :doc:`/ref/contrib/csrf`. + .. setting:: CSRF_FAILURE_VIEW CSRF_FAILURE_VIEW @@ -195,7 +209,7 @@ is rejected by the CSRF protection. The function should have this signature:: where ``reason`` is a short message (intended for developers or logging, not for end users) indicating the reason the request was rejected. See -:ref:`ref-contrib-csrf`. +:doc:`/ref/contrib/csrf`. .. setting:: DATABASES @@ -208,7 +222,7 @@ DATABASES Default: ``{}`` (Empty dictionary) A dictionary containing the settings for all databases to be used with -Django. It is a nested dictionary who's contents maps database aliases +Django. It is a nested dictionary whose contents maps database aliases to a dictionary containing the options for an individual database. The :setting:`DATABASES` setting must configure a ``default`` database; @@ -385,8 +399,19 @@ If the default value (``None``) is used with the SQLite database engine, the tests will use a memory resident database. For all other database engines the test database will use the name ``'test_' + DATABASE_NAME``. -See :ref:`topics-testing`. +See :doc:`/topics/testing`. +.. setting:: TEST_USER + +TEST_USER +~~~~~~~~~ + +Default: ``None`` + +This is an Oracle-specific setting. + +The username to use when connecting to the Oracle database that will be used +when running tests. .. setting:: DATABASE_ROUTERS @@ -441,7 +466,7 @@ to be displayed. See also ``DATETIME_INPUT_FORMATS`` and ``TIME_INPUT_FORMATS``. -.. _datetime: http://docs.python.org/library/datetime.html#strftime-behavior +.. _datetime: http://docs.python.org/library/datetime.html#strftime-strptime-behavior .. setting:: DATETIME_FORMAT @@ -481,7 +506,7 @@ to be displayed. See also ``DATE_INPUT_FORMATS`` and ``TIME_INPUT_FORMATS``. -.. _datetime: http://docs.python.org/library/datetime.html#strftime-behavior +.. _datetime: http://docs.python.org/library/datetime.html#strftime-strptime-behavior .. setting:: DEBUG @@ -494,8 +519,9 @@ A boolean that turns on/off debug mode. If you define custom settings, `django/views/debug.py`_ has a ``HIDDEN_SETTINGS`` regular expression which will hide from the DEBUG view anything that contains -``'SECRET'``, ``'PASSWORD'``, or ``'PROFANITIES'``. This allows untrusted users to -be able to give backtraces without seeing sensitive (or offensive) settings. +``'SECRET'``, ``'PASSWORD'``, ``'PROFANITIES'``, or ``'SIGNATURE'``. This allows +untrusted users to be able to give backtraces without seeing sensitive (or +offensive) settings. Still, note that there are always going to be sections of your debug output that are inappropriate for public consumption. File paths, configuration options, and @@ -554,7 +580,7 @@ Default content type to use for all ``HttpResponse`` objects, if a MIME type isn't manually specified. Used with ``DEFAULT_CHARSET`` to construct the ``Content-Type`` header. -.. setting:: DEFAULT_FROM_EMAIL +.. setting:: DEFAULT_FILE_STORAGE DEFAULT_FILE_STORAGE -------------------- @@ -562,7 +588,9 @@ DEFAULT_FILE_STORAGE Default: ``'django.core.files.storage.FileSystemStorage'`` Default file storage class to be used for any file-related operations that don't -specify a particular storage system. See :ref:`topics-files`. +specify a particular storage system. See :doc:`/topics/files`. + +.. setting:: DEFAULT_FROM_EMAIL DEFAULT_FROM_EMAIL ------------------ @@ -572,29 +600,29 @@ Default: ``'webmaster@localhost'`` Default e-mail address to use for various automated correspondence from the site manager(s). -.. setting:: DEFAULT_TABLESPACE +.. setting:: DEFAULT_INDEX_TABLESPACE -DEFAULT_TABLESPACE ------------------- +DEFAULT_INDEX_TABLESPACE +------------------------ .. versionadded:: 1.0 Default: ``''`` (Empty string) -Default tablespace to use for models that don't specify one, if the -backend supports it. +Default tablespace to use for indexes on fields that don't specify +one, if the backend supports it. -.. setting:: DEFAULT_INDEX_TABLESPACE +.. setting:: DEFAULT_TABLESPACE -DEFAULT_INDEX_TABLESPACE ------------------------- +DEFAULT_TABLESPACE +------------------ .. versionadded:: 1.0 Default: ``''`` (Empty string) -Default tablespace to use for indexes on fields that don't specify -one, if the backend supports it. +Default tablespace to use for models that don't specify one, if the +backend supports it. .. setting:: DISALLOWED_USER_AGENTS @@ -606,7 +634,7 @@ Default: ``()`` (Empty tuple) List of compiled regular expression objects representing User-Agent strings that are not allowed to visit any page, systemwide. Use this for bad robots/crawlers. This is only used if ``CommonMiddleware`` is installed (see -:ref:`topics-http-middleware`). +:doc:`/topics/http/middleware`). .. setting:: EMAIL_BACKEND @@ -615,10 +643,10 @@ EMAIL_BACKEND .. versionadded:: 1.2 -Default: ``'django.core.mail.backends.smtp'`` +Default: ``'django.core.mail.backends.smtp.EmailBackend'`` The backend to use for sending emails. For the list of available backends see -:ref:`topics-email`. +:doc:`/topics/email`. .. setting:: EMAIL_FILE_PATH @@ -723,7 +751,7 @@ Default:: ("django.core.files.uploadhandler.MemoryFileUploadHandler", "django.core.files.uploadhandler.TemporaryFileUploadHandler",) -A tuple of handlers to use for uploading. See :ref:`topics-files` for details. +A tuple of handlers to use for uploading. See :doc:`/topics/files` for details. .. setting:: FILE_UPLOAD_MAX_MEMORY_SIZE @@ -735,22 +763,7 @@ FILE_UPLOAD_MAX_MEMORY_SIZE Default: ``2621440`` (i.e. 2.5 MB). The maximum size (in bytes) that an upload will be before it gets streamed to -the file system. See :ref:`topics-files` for details. - -.. setting:: FILE_UPLOAD_TEMP_DIR - -FILE_UPLOAD_TEMP_DIR --------------------- - -.. versionadded:: 1.0 - -Default: ``None`` - -The directory to store data temporarily while uploading files. If ``None``, -Django will use the standard temporary directory for the operating system. For -example, this will default to '/tmp' on \*nix-style operating systems. - -See :ref:`topics-files` for details. +the file system. See :doc:`/topics/files` for details. .. setting:: FILE_UPLOAD_PERMISSIONS @@ -780,6 +793,21 @@ system's standard umask. .. _documentation for os.chmod: http://docs.python.org/library/os.html#os.chmod +.. setting:: FILE_UPLOAD_TEMP_DIR + +FILE_UPLOAD_TEMP_DIR +-------------------- + +.. versionadded:: 1.0 + +Default: ``None`` + +The directory to store data temporarily while uploading files. If ``None``, +Django will use the standard temporary directory for the operating system. For +example, this will default to '/tmp' on \*nix-style operating systems. + +See :doc:`/topics/files` for details. + .. setting:: FIRST_DAY_OF_WEEK FIRST_DAY_OF_WEEK @@ -806,7 +834,7 @@ Default: ``()`` (Empty tuple) List of locations of the fixture data files, in search order. Note that these paths should use Unix-style forward slashes, even on Windows. See -:ref:`topics-testing`. +:doc:`/topics/testing`. FORCE_SCRIPT_NAME ------------------ @@ -866,7 +894,7 @@ Default: ``('/cgi-bin/', '/_vti_bin', '/_vti_inf')`` A tuple of strings that specify beginnings of URLs that should be ignored by the 404 e-mailer. See ``SEND_BROKEN_LINK_EMAILS``, ``IGNORABLE_404_ENDS`` and -the :ref:`howto-error-reporting`. +the :doc:`/howto/error-reporting`. .. setting:: INSTALLED_APPS @@ -899,7 +927,7 @@ A tuple of IP addresses, as strings, that: * See debug comments, when ``DEBUG`` is ``True`` * Receive X headers if the ``XViewMiddleware`` is installed (see - :ref:`topics-http-middleware`) + :doc:`/topics/http/middleware`) .. setting:: LANGUAGE_CODE @@ -910,7 +938,7 @@ Default: ``'en-us'`` A string representing the language code for this installation. This should be in standard :term:`language format<language code>`. For example, U.S. English is -``"en-us"``. See :ref:`topics-i18n`. +``"en-us"``. See :doc:`/topics/i18n/index`. .. setting:: LANGUAGE_COOKIE_NAME @@ -923,7 +951,7 @@ Default: ``'django_language'`` The name of the cookie to use for the language cookie. This can be whatever you want (but should be different from ``SESSION_COOKIE_NAME``). See -:ref:`topics-i18n`. +:doc:`/topics/i18n/index`. .. setting:: LANGUAGES @@ -941,7 +969,7 @@ The list is a tuple of two-tuples in the format ``(language code, language name)``, the ``language code`` part should be a :term:`language name<language code>` -- for example, ``('ja', 'Japanese')``. This specifies which languages are available for language selection. See -:ref:`topics-i18n`. +:doc:`/topics/i18n/index`. Generally, the default value should suffice. Only set this setting if you want to restrict language selection to a subset of the Django-provided languages. @@ -1061,7 +1089,7 @@ MESSAGE_LEVEL Default: `messages.INFO` Sets the minimum message level that will be recorded by the messages -framework. See the :ref:`messages documentation <ref-contrib-messages>` for +framework. See the :doc:`messages documentation </ref/contrib/messages>` for more details. MESSAGE_STORAGE @@ -1072,7 +1100,7 @@ MESSAGE_STORAGE Default: ``'django.contrib.messages.storage.user_messages.LegacyFallbackStorage'`` Controls where Django stores message data. See the -:ref:`messages documentation <ref-contrib-messages>` for more details. +:doc:`messages documentation </ref/contrib/messages>` for more details. MESSAGE_TAGS ------------ @@ -1088,7 +1116,7 @@ Default:: messages.ERROR: 'error',} Sets the mapping of message levels to message tags. See the -:ref:`messages documentation <ref-contrib-messages>` for more details. +:doc:`messages documentation </ref/contrib/messages>` for more details. MIDDLEWARE_CLASSES ------------------ @@ -1101,12 +1129,12 @@ Default:: 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',) -A tuple of middleware classes to use. See :ref:`topics-http-middleware`. +A tuple of middleware classes to use. See :doc:`/topics/http/middleware`. .. versionchanged:: 1.2 ``'django.contrib.messages.middleware.MessageMiddleware'`` was added to the - default. For more information, see the :ref:`messages documentation - <ref-contrib-messages>`. + default. For more information, see the :doc:`messages documentation + </ref/contrib/messages>`. .. setting:: MONTH_DAY_FORMAT @@ -1152,7 +1180,7 @@ PREPEND_WWW Default: ``False`` Whether to prepend the "www." subdomain to URLs that don't have it. This is only -used if ``CommonMiddleware`` is installed (see :ref:`topics-http-middleware`). +used if ``CommonMiddleware`` is installed (see :doc:`/topics/http/middleware`). See also ``APPEND_SLASH``. .. setting:: PROFANITIES_LIST @@ -1167,6 +1195,21 @@ We don't list the default values here, because that would be profane. To see the default values, see the file `django/conf/global_settings.py`_. .. _django/conf/global_settings.py: http://code.djangoproject.com/browser/django/trunk/django/conf/global_settings.py + +.. setting:: RESTRUCTUREDTEXT_FILTER_SETTINGS + +RESTRUCTUREDTEXT_FILTER_SETTINGS +-------------------------------- + +Default: ``{}`` + +A dictionary containing settings for the ``restructuredtext`` markup filter from +the :doc:`django.contrib.markup application </ref/contrib/markup>`. They override +the default writer settings. See the Docutils restructuredtext `writer settings +docs`_ for details. + +.. _writer settings docs: http://docutils.sourceforge.net/docs/user/config.html#html4css1-writer + .. setting:: ROOT_URLCONF ROOT_URLCONF @@ -1200,8 +1243,8 @@ Default: ``False`` Whether to send an e-mail to the ``MANAGERS`` each time somebody visits a Django-powered page that is 404ed with a non-empty referer (i.e., a broken link). This is only used if ``CommonMiddleware`` is installed (see -:ref:`topics-http-middleware`. See also ``IGNORABLE_404_STARTS``, -``IGNORABLE_404_ENDS`` and :ref:`howto-error-reporting`. +:doc:`/topics/http/middleware`. See also ``IGNORABLE_404_STARTS``, +``IGNORABLE_404_ENDS`` and :doc:`/howto/error-reporting`. .. setting:: SERIALIZATION_MODULES @@ -1226,27 +1269,6 @@ Default: ``'root@localhost'`` The e-mail address that error messages come from, such as those sent to ``ADMINS`` and ``MANAGERS``. -.. setting:: SESSION_ENGINE - -SESSION_ENGINE --------------- - -.. versionadded:: 1.0 - -.. versionchanged:: 1.1 - The ``cached_db`` backend was added - -Default: ``django.contrib.sessions.backends.db`` - -Controls where Django stores session data. Valid values are: - - * ``'django.contrib.sessions.backends.db'`` - * ``'django.contrib.sessions.backends.file'`` - * ``'django.contrib.sessions.backends.cache'`` - * ``'django.contrib.sessions.backends.cached_db'`` - -See :ref:`topics-http-sessions`. - .. setting:: SESSION_COOKIE_AGE SESSION_COOKIE_AGE @@ -1254,7 +1276,7 @@ SESSION_COOKIE_AGE Default: ``1209600`` (2 weeks, in seconds) -The age of session cookies, in seconds. See :ref:`topics-http-sessions`. +The age of session cookies, in seconds. See :doc:`/topics/http/sessions`. .. setting:: SESSION_COOKIE_DOMAIN @@ -1265,7 +1287,7 @@ Default: ``None`` The domain to use for session cookies. Set this to a string such as ``".lawrence.com"`` for cross-domain cookies, or use ``None`` for a standard -domain cookie. See the :ref:`topics-http-sessions`. +domain cookie. See the :doc:`/topics/http/sessions`. .. setting:: SESSION_COOKIE_NAME @@ -1275,7 +1297,7 @@ SESSION_COOKIE_NAME Default: ``'sessionid'`` The name of the cookie to use for sessions. This can be whatever you want (but -should be different from ``LANGUAGE_COOKIE_NAME``). See the :ref:`topics-http-sessions`. +should be different from ``LANGUAGE_COOKIE_NAME``). See the :doc:`/topics/http/sessions`. .. setting:: SESSION_COOKIE_PATH @@ -1303,7 +1325,28 @@ Default: ``False`` Whether to use a secure cookie for the session cookie. If this is set to ``True``, the cookie will be marked as "secure," which means browsers may ensure that the cookie is only sent under an HTTPS connection. -See the :ref:`topics-http-sessions`. +See the :doc:`/topics/http/sessions`. + +.. setting:: SESSION_ENGINE + +SESSION_ENGINE +-------------- + +.. versionadded:: 1.0 + +.. versionchanged:: 1.1 + The ``cached_db`` backend was added + +Default: ``django.contrib.sessions.backends.db`` + +Controls where Django stores session data. Valid values are: + + * ``'django.contrib.sessions.backends.db'`` + * ``'django.contrib.sessions.backends.file'`` + * ``'django.contrib.sessions.backends.cache'`` + * ``'django.contrib.sessions.backends.cached_db'`` + +See :doc:`/topics/http/sessions`. .. setting:: SESSION_EXPIRE_AT_BROWSER_CLOSE @@ -1313,7 +1356,7 @@ SESSION_EXPIRE_AT_BROWSER_CLOSE Default: ``False`` Whether to expire the session when the user closes his or her browser. -See the :ref:`topics-http-sessions`. +See the :doc:`/topics/http/sessions`. .. setting:: SESSION_FILE_PATH @@ -1325,7 +1368,7 @@ SESSION_FILE_PATH Default: ``None`` If you're using file-based session storage, this sets the directory in -which Django will store session data. See :ref:`topics-http-sessions`. When +which Django will store session data. See :doc:`/topics/http/sessions`. When the default value (``None``) is used, Django will use the standard temporary directory for the system. @@ -1337,7 +1380,7 @@ SESSION_SAVE_EVERY_REQUEST Default: ``False`` Whether to save the session data on every request. See -:ref:`topics-http-sessions`. +:doc:`/topics/http/sessions`. .. setting:: SHORT_DATE_FORMAT @@ -1382,7 +1425,7 @@ The ID, as an integer, of the current site in the ``django_site`` database table. This is used so that application data can hook into specific site(s) and a single database can manage content for multiple sites. -See :ref:`ref-contrib-sites`. +See :doc:`/ref/contrib/sites`. .. _site framework docs: ../sites/ @@ -1405,8 +1448,8 @@ of items to be merged into the context. .. versionchanged:: 1.2 ``"django.contrib.messages.context_processors.messages"`` was added to the - default. For more information, see the :ref:`messages documentation - <ref-contrib-messages>`. + default. For more information, see the :doc:`messages documentation + </ref/contrib/messages>`. .. versionchanged:: 1.2 The auth context processor was moved in this release from its old location @@ -1440,7 +1483,7 @@ Default: ``()`` (Empty tuple) List of locations of the template source files, in search order. Note that these paths should use Unix-style forward slashes, even on Windows. -See :ref:`topics-templates`.. +See :doc:`/topics/templates`. .. setting:: TEMPLATE_LOADERS @@ -1456,7 +1499,7 @@ A tuple of template loader classes, specified as strings. Each ``Loader`` class knows how to import templates from a particular source. Optionally, a tuple can be used instead of a string. The first item in the tuple should be the ``Loader``'s module, subsequent items are passed to the ``Loader`` during initialization. See -:ref:`ref-templates-api`. +:doc:`/ref/templates/api`. .. setting:: TEMPLATE_STRING_IF_INVALID @@ -1479,7 +1522,7 @@ Default: ``'django.test.simple.DjangoTestSuiteRunner'`` Prior to 1.2, test runners were a function, not a class. The name of the class to use for starting the test suite. See -:ref:`topics-testing`. +:doc:`/topics/testing`. .. _Testing Django Applications: ../testing/ @@ -1531,7 +1574,7 @@ to be displayed. See also ``DATE_INPUT_FORMATS`` and ``DATETIME_INPUT_FORMATS``. -.. _datetime: http://docs.python.org/library/datetime.html#strftime-behavior +.. _datetime: http://docs.python.org/library/datetime.html#strftime-strptime-behavior .. setting:: TIME_ZONE @@ -1598,7 +1641,21 @@ Default: ``False`` A boolean that specifies whether to output the "Etag" header. This saves bandwidth but slows down performance. This is only used if ``CommonMiddleware`` -is installed (see :ref:`topics-http-middleware`). +is installed (see :doc:`/topics/http/middleware`). + +.. setting:: USE_I18N + +USE_I18N +-------- + +Default: ``True`` + +A boolean that specifies whether Django's internationalization system should be +enabled. This provides an easy way to turn it off, for performance. If this is +set to ``False``, Django will make some optimizations so as not to load the +internationalization machinery. + +See also ``USE_L10N`` .. setting:: USE_L10N @@ -1615,20 +1672,6 @@ format of the current locale. See also ``USE_I18N`` and ``LANGUAGE_CODE`` -.. setting:: USE_I18N - -USE_I18N --------- - -Default: ``True`` - -A boolean that specifies whether Django's internationalization system should be -enabled. This provides an easy way to turn it off, for performance. If this is -set to ``False``, Django will make some optimizations so as not to load the -internationalization machinery. - -See also ``USE_L10N`` - .. setting:: USE_THOUSAND_SEPARATOR USE_THOUSAND_SEPARATOR diff --git a/docs/ref/signals.txt b/docs/ref/signals.txt index 12372dd0f1..04243a87a4 100644 --- a/docs/ref/signals.txt +++ b/docs/ref/signals.txt @@ -1,5 +1,3 @@ -.. _ref-signals: - ======= Signals ======= @@ -8,11 +6,11 @@ A list of all the signals that Django sends. .. seealso:: - See the documentation on the :ref:`signal dispatcher <topics-signals>` for + See the documentation on the :doc:`signal dispatcher </topics/signals>` for information regarding how to register for and receive signals. - The :ref:`comment framework <ref-contrib-comments-index>` sends a :ref:`set - of comment-related signals <ref-contrib-comments-signals>`. + The :doc:`comment framework </ref/contrib/comments/index>` sends a :doc:`set + of comment-related signals </ref/contrib/comments/signals>`. Model signals ============= @@ -33,9 +31,9 @@ module system. If you override these methods on your model, you must call the parent class' methods for this signals to be sent. - Note also that Django stores signal handlers as weak references by default, - so if your handler is a local function, it may be garbage collected. To - prevent this, pass ``weak=False`` when you call the signal's :meth:`~django.dispatch.Signal.connect`. + Note also that Django stores signal handlers as weak references by default, + so if your handler is a local function, it may be garbage collected. To + prevent this, pass ``weak=False`` when you call the signal's :meth:`~django.dispatch.Signal.connect`. pre_init -------- @@ -61,7 +59,7 @@ Arguments sent with this signal: A dictionary of keyword arguments passed to :meth:`~django.db.models.Model.__init__`:. -For example, the :ref:`tutorial <intro-tutorial01>` has this line: +For example, the :doc:`tutorial </intro/tutorial01>` has this line: .. code-block:: python @@ -113,6 +111,9 @@ Arguments sent with this signal: ``instance`` The actual instance being saved. + ``using`` + The database alias being used. + post_save --------- @@ -133,6 +134,9 @@ Arguments sent with this signal: ``created`` A boolean; ``True`` if a new record was created. + ``using`` + The database alias being used. + pre_delete ---------- @@ -150,6 +154,9 @@ Arguments sent with this signal: ``instance`` The actual instance being deleted. + ``using`` + The database alias being used. + post_delete ----------- @@ -170,6 +177,9 @@ Arguments sent with this signal: Note that the object will no longer be in the database, so be very careful what you do with this instance. + ``using`` + The database alias being used. + m2m_changed ----------- @@ -215,8 +225,8 @@ Arguments sent with this signal: Sent *after* the relation is cleared ``reverse`` - Indicates which side of the relation is updated (i.e., if it is the - forward or reverse relation that is being modified). + Indicates which side of the relation is updated (i.e., if it is the + forward or reverse relation that is being modified). ``model`` The class of the objects that are added to, removed from or cleared @@ -228,6 +238,9 @@ Arguments sent with this signal: For the ``"clear"`` action, this is ``None``. + ``using`` + The database alias being used. + For example, if a ``Pizza`` can have multiple ``Topping`` objects, modeled like this: @@ -266,6 +279,8 @@ the arguments sent to a :data:`m2m_changed` handler would be: ``Pizza``) ``pk_set`` ``[t.id]`` (since only ``Topping t`` was added to the relation) + + ``using`` ``"default"`` (since the default router sends writes here) ============== ============================================================ And if we would then do something like this: @@ -293,6 +308,8 @@ the arguments sent to a :data:`m2m_changed` handler would be: ``pk_set`` ``[p.id]`` (since only ``Pizza p`` was removed from the relation) + + ``using`` ``"default"`` (since the default router sends writes here) ============== ============================================================ class_prepared @@ -313,7 +330,7 @@ Arguments that are sent with this signal: Management signals ================== -Signals sent by :ref:`django-admin <ref-django-admin>`. +Signals sent by :doc:`django-admin </ref/django-admin>`. post_syncdb ----------- @@ -376,8 +393,7 @@ Sent when Django begins processing an HTTP request. Arguments sent with this signal: ``sender`` - The handler class -- i.e. - :class:`django.core.handlers.modpython.ModPythonHandler` or + The handler class -- e.g. :class:`django.core.handlers.wsgi.WsgiHandler` -- that handled the request. @@ -416,7 +432,7 @@ Test signals .. module:: django.test.signals :synopsis: Signals sent during testing. -Signals only sent when :ref:`running tests <topics-testing>`. +Signals only sent when :doc:`running tests </topics/testing>`. template_rendered ----------------- @@ -438,3 +454,39 @@ Arguments sent with this signal: context The :class:`~django.template.Context` with which the template was rendered. + +Database Wrappers +================= + +.. module:: django.db.backends + :synopsis: Core signals sent by the database wrapper. + +Signals sent by the database wrapper when a database connection is +initiated. + +connection_created +------------------ + +.. data:: django.db.backends.signals.connection_created + :module: + +.. versionadded:: 1.1 + +.. versionchanged:: 1.2 + The connection argument was added + +Sent when the database wrapper makes the initial connection to the +database. This is particularly useful if you'd like to send any post +connection commands to the SQL backend. + +Arguments sent with this signal: + + sender + The database wrapper class -- i.e. + :class: `django.db.backends.postgresql_psycopg2.DatabaseWrapper` or + :class: `django.db.backends.mysql.DatabaseWrapper`, etc. + + connection + The database connection that was opened. This can be used in a + multiple-database configuration to differentiate connection signals + from different databases. diff --git a/docs/ref/templates/api.txt b/docs/ref/templates/api.txt index 3e267531de..2ac4e653c4 100644 --- a/docs/ref/templates/api.txt +++ b/docs/ref/templates/api.txt @@ -1,12 +1,10 @@ -.. _ref-templates-api: - ==================================================== The Django template language: For Python programmers ==================================================== This document explains the Django template system from a technical perspective -- how it works and how to extend it. If you're just looking for -reference on the language syntax, see :ref:`topics-templates`. +reference on the language syntax, see :doc:`/topics/templates`. If you're looking to use the Django template system as part of another application -- i.e., without the rest of the framework -- make sure to read @@ -323,7 +321,7 @@ and return a dictionary of items to be merged into the context. By default, .. versionadded:: 1.2 The ``'messages'`` context processor was added. For more information, see - the :ref:`messages documentation <ref-contrib-messages>`. + the :doc:`messages documentation </ref/contrib/messages>`. .. versionchanged:: 1.2 The auth context processor was moved in this release from its old location @@ -384,7 +382,7 @@ If :setting:`TEMPLATE_CONTEXT_PROCESSORS` contains this processor, every logged in). * ``messages`` -- A list of messages (as strings) that have been set - via the :ref:`messages framework <ref-contrib-messages>`. + via the :doc:`messages framework </ref/contrib/messages>`. * ``perms`` -- An instance of ``django.core.context_processors.PermWrapper``, representing the @@ -397,7 +395,7 @@ If :setting:`TEMPLATE_CONTEXT_PROCESSORS` contains this processor, every .. versionchanged:: 1.2 Prior to version 1.2, the ``messages`` variable was a lazy accessor for ``user.get_and_delete_messages()``. It has been changed to include any - messages added via the :ref:`messages framework <ref-contrib-messages>`. + messages added via the :doc:`messages framework </ref/contrib/messages>`. django.core.context_processors.debug ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -423,7 +421,7 @@ If :setting:`TEMPLATE_CONTEXT_PROCESSORS` contains this processor, every * ``LANGUAGE_CODE`` -- ``request.LANGUAGE_CODE``, if it exists. Otherwise, the value of the :setting:`LANGUAGE_CODE` setting. -See :ref:`topics-i18n` for more. +See :doc:`/topics/i18n/index` for more. django.core.context_processors.media ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -440,7 +438,7 @@ django.core.context_processors.csrf .. versionadded:: 1.2 This processor adds a token that is needed by the ``csrf_token`` template tag -for protection against :ref:`Cross Site Request Forgeries <ref-contrib-csrf>`. +for protection against :doc:`Cross Site Request Forgeries </ref/contrib/csrf>`. django.core.context_processors.request ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -458,7 +456,7 @@ If :setting:`TEMPLATE_CONTEXT_PROCESSORS` contains this processor, every * ``messages`` -- A list of messages (as strings) that have been set via the user model (using ``user.message_set.create``) or through - the :ref:`messages framework <ref-contrib-messages>`. + the :doc:`messages framework </ref/contrib/messages>`. .. versionadded:: 1.2 This template context variable was previously supplied by the ``'auth'`` @@ -702,7 +700,7 @@ Configuring the template system in standalone mode Normally, Django will load all the configuration information it needs from its own default configuration file, combined with the settings in the module given -in the :setting:`DJANGO_SETTINGS_MODULE` environment variable. But if you're +in the :envvar:`DJANGO_SETTINGS_MODULE` environment variable. But if you're using the template system independently of the rest of Django, the environment variable approach isn't very convenient, because you probably want to configure the template system in line with the rest of your application rather than @@ -716,7 +714,7 @@ settings you wish to specify. You might want to consider setting at least :setting:`TEMPLATE_DIRS` (if you're going to use template loaders), :setting:`DEFAULT_CHARSET` (although the default of ``utf-8`` is probably fine) and :setting:`TEMPLATE_DEBUG`. All available settings are described in the -:ref:`settings documentation <ref-settings>`, and any setting starting with +:doc:`settings documentation </ref/settings>`, and any setting starting with ``TEMPLATE_`` is of obvious interest. .. _topic-template-alternate-language: diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt index 002aa3f416..4f33bd212c 100644 --- a/docs/ref/templates/builtins.txt +++ b/docs/ref/templates/builtins.txt @@ -1,5 +1,3 @@ -.. _ref-templates-builtins: - ================================== Built-in template tags and filters ================================== @@ -25,7 +23,7 @@ autoescape Control the current auto-escaping behavior. This tag takes either ``on`` or ``off`` as an argument and that determines whether auto-escaping is in effect -inside the block. +inside the block. The block is closed with an ``endautoescape`` ending tag. When auto-escaping is in effect, all variable content has HTML escaping applied to it before placing the result into the output (but after any filters have @@ -36,6 +34,12 @@ The only exceptions are variables that are already marked as "safe" from escaping, either by the code that populated the variable, or because it has had the ``safe`` or ``escape`` filters applied. +Sample usage:: + + {% autoescape on %} + {{ body }} + {% endautoescape %} + .. templatetag:: block block @@ -60,8 +64,8 @@ csrf_token In the Django 1.1.X series, this is a no-op tag that returns an empty string for future compatibility purposes. In Django 1.2 and later, it is used for CSRF -protection, as described in the documentation for :ref:`Cross Site Request -Forgeries <ref-contrib-csrf>`. +protection, as described in the documentation for :doc:`Cross Site Request +Forgeries </ref/contrib/csrf>`. .. templatetag:: cycle @@ -633,7 +637,7 @@ load Load a custom template tag set. -See :ref:`Custom tag and filter libraries <howto-custom-template-tags>` for more information. +See :doc:`Custom tag and filter libraries </howto/custom-template-tags>` for more information. .. templatetag:: now @@ -1883,6 +1887,8 @@ For example:: If ``value`` is ``"Joel is a slug"``, the output will be ``"Joel is ..."``. +Newlines within the string will be removed. + .. templatefilter:: truncatewords_html truncatewords_html @@ -1902,6 +1908,8 @@ For example:: If ``value`` is ``"<p>Joel is a slug</p>"``, the output will be ``"<p>Joel is ...</p>"``. +Newlines in the HTML content will be preserved. + .. templatefilter:: unordered_list unordered_list @@ -2058,7 +2066,7 @@ django.contrib.humanize ~~~~~~~~~~~~~~~~~~~~~~~ A set of Django template filters useful for adding a "human touch" to data. See -:ref:`ref-contrib-humanize`. +:doc:`/ref/contrib/humanize`. django.contrib.markup ~~~~~~~~~~~~~~~~~~~~~ @@ -2069,13 +2077,13 @@ A collection of template filters that implement these common markup languages: * Markdown * ReST (ReStructured Text) -See :ref:`ref-contrib-markup`. +See the :doc:`markup documentation </ref/contrib/markup>`. django.contrib.webdesign ~~~~~~~~~~~~~~~~~~~~~~~~ A collection of template tags that can be useful while designing a website, -such as a generator of Lorem Ipsum text. See :ref:`ref-contrib-webdesign`. +such as a generator of Lorem Ipsum text. See :doc:`/ref/contrib/webdesign`. i18n ~~~~ diff --git a/docs/ref/templates/index.txt b/docs/ref/templates/index.txt index 6655b3da18..0aa4798a94 100644 --- a/docs/ref/templates/index.txt +++ b/docs/ref/templates/index.txt @@ -1,5 +1,3 @@ -.. _ref-templates-index: - ========= Templates ========= @@ -18,4 +16,4 @@ an understanding of HTML; no knowledge of Python is required. .. seealso:: For information on writing your own custom tags and filters, see - :ref:`howto-custom-template-tags`. + :doc:`/howto/custom-template-tags`. diff --git a/docs/ref/unicode.txt b/docs/ref/unicode.txt index a6149119bf..8e110af5d5 100644 --- a/docs/ref/unicode.txt +++ b/docs/ref/unicode.txt @@ -1,5 +1,3 @@ -.. _ref-unicode: - ============ Unicode data ============ @@ -95,7 +93,7 @@ Calling ``unicode()`` with the lazy translation as the argument will generate a Unicode string in the current locale. For more details about lazy translation objects, refer to the -:ref:`internationalization <topics-i18n>` documentation. +:doc:`internationalization </topics/i18n/index>` documentation. Useful utility functions ------------------------ diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt index e64b3868b9..39b01df0be 100644 --- a/docs/ref/utils.txt +++ b/docs/ref/utils.txt @@ -1,5 +1,3 @@ -.. _ref-utils: - ============ Django Utils ============ @@ -32,7 +30,7 @@ into account when building its cache key. Requests with the same path but different header content for headers named in ``Vary`` need to get different cache keys to prevent delivery of wrong content. -For example, :ref:`internationalization <topics-i18n>` middleware would need +For example, :doc:`internationalization </topics/i18n/index>` middleware would need to distinguish caches by the ``Accept-language`` header. .. function:: patch_cache_control(response, **kwargs) @@ -395,7 +393,7 @@ applied once). :synopsis: Internationalization support. For a complete discussion on the usage of the following see the -:ref:`Internationalization documentation <topics-i18n-internationalization>`. +:doc:`Internationalization documentation </topics/i18n/internationalization>`. .. function:: gettext(message) diff --git a/docs/ref/validators.txt b/docs/ref/validators.txt index bbba84c7f9..4937512e46 100644 --- a/docs/ref/validators.txt +++ b/docs/ref/validators.txt @@ -1,10 +1,10 @@ -.. _ref-validators: - ========== Validators ========== .. versionadded:: 1.2 +.. module:: django.core.validators + :synopsis: Validation utilities and base classes Writing validators ================== @@ -40,12 +40,12 @@ use the same validator with forms:: How validators are run ====================== -See the :ref:`form validation <ref-forms-validation>` for more information on +See the :doc:`form validation </ref/forms/validation>` for more information on how validators are run in forms, and :ref:`Validating objects <validating-objects>` for how they're run in models. Note that validators will not be run automatically when you save a model, but if you are using a ``ModelForm``, it will run your validators on any fields that are included in -your form. See the :ref:`ModelForm documentation <topics-forms-modelforms>` +your form. See the :doc:`ModelForm documentation </topics/forms/modelforms>` for information on how model validation interacts with forms. Built-in validators diff --git a/docs/releases/0.95.txt b/docs/releases/0.95.txt index b74160128b..7409bff1c0 100644 --- a/docs/releases/0.95.txt +++ b/docs/releases/0.95.txt @@ -1,5 +1,3 @@ -.. _releases-0.95: - ================================= Django version 0.95 release notes ================================= @@ -100,7 +98,7 @@ Problem reports and getting help ================================ Need help resolving a problem with Django? The documentation in the distribution -is also available online_ at the `Django Web site`_. The :ref:`FAQ <faq-index>` +is also available online_ at the `Django Web site`_. The :doc:`FAQ </faq/index>` document is especially recommended, as it contains a number of issues that come up time and again. diff --git a/docs/releases/0.96.txt b/docs/releases/0.96.txt index 8d795acebd..1224360e3f 100644 --- a/docs/releases/0.96.txt +++ b/docs/releases/0.96.txt @@ -1,5 +1,3 @@ -.. _releases-0.96: - ================================= Django version 0.96 release notes ================================= diff --git a/docs/releases/1.0-alpha-1.txt b/docs/releases/1.0-alpha-1.txt index caee575cb2..82846be44a 100644 --- a/docs/releases/1.0-alpha-1.txt +++ b/docs/releases/1.0-alpha-1.txt @@ -1,5 +1,3 @@ -.. _releases-1.0-alpha-1: - ================================ Django 1.0 alpha release notes ================================ @@ -34,7 +32,7 @@ Refactored admin application (newforms-admin) documentation for the admin application is available online in the official Django documentation: - :ref:`admin reference <ref-contrib-admin>` + :doc:`admin reference </ref/contrib/admin/index>` Improved Unicode handling Django's internals have been refactored to use Unicode throughout; @@ -45,7 +43,7 @@ Improved Unicode handling Unicode gracefully. Details are available in Django's Unicode-handling documentation: - :ref:`unicode reference <ref-unicode>` + :doc:`unicode reference </ref/unicode>` An improved Django ORM Django's object-relational mapper -- the component which provides @@ -156,7 +154,7 @@ to join the discussions there. Django's online documentation also includes pointers on how to contribute to Django: - :ref:`contributing to Django <internals-contributing>` + :doc:`contributing to Django </internals/contributing>` Contributions on any level -- developing code, writing documentation or simply triaging tickets and helping to test proposed diff --git a/docs/releases/1.0-alpha-2.txt b/docs/releases/1.0-alpha-2.txt index 5cbd777204..83e2e2e14a 100644 --- a/docs/releases/1.0-alpha-2.txt +++ b/docs/releases/1.0-alpha-2.txt @@ -1,5 +1,3 @@ -.. _releases-1.0-alpha-2: - ================================ Django 1.0 alpha 2 release notes ================================ @@ -21,8 +19,8 @@ What's new in Django 1.0 alpha 2 Django's development trunk has been the site of nearly constant activity over the past year, with several major new features landing since the 0.96 release. -For features which were new as of Django 1.0 alpha 1, see :ref:`the 1.0 alpha 1 -release notes <releases-1.0-alpha-1`. Since the 1.0 alpha 1 release several new +For features which were new as of Django 1.0 alpha 1, see :doc:`the 1.0 alpha 1 +release notes </releases/1.0-alpha-1>`. Since the 1.0 alpha 1 release several new features have landed, including: ``django.contrib.gis`` (`GeoDjango`_) @@ -37,8 +35,8 @@ features have landed, including: Pluggable file storage Django's built-in ``FileField`` and ``ImageField`` now can take advantage of pluggable file-storage backends, allowing extensive customization of where - and how uploaded files get stored by Django. For details, see :ref:`the - files documentation <topics-files>`; big thanks go to Marty Alchin for + and how uploaded files get stored by Django. For details, see :doc:`the + files documentation </topics/files>`; big thanks go to Marty Alchin for putting in the hard work to get this completed. Jython compatibility @@ -51,11 +49,11 @@ Jython compatibility There are many other new features and improvements in this release, including two major performance boosts: strings marked for translation using -:ref:`Django's internationalization system <topics-i18n>` now consume far less +:doc:`Django's internationalization system </topics/i18n/index>` now consume far less memory, and Django's internal dispatcher -- which is invoked frequently during request/response processing and when working with Django's object-relational mapper -- is now significantly faster. - + .. _GeoDjango: http://geodjango.org/ .. _Geographic Information Systems: http://en.wikipedia.org/wiki/Geographic_information_system .. _Its documentation: http://geodjango.org/docs/ @@ -131,7 +129,7 @@ to join the discussions there. Django's online documentation also includes pointers on how to contribute to Django: - :ref:`contributing to Django <internals-contributing>` + :doc:`contributing to Django </internals/contributing>` Contributions on any level -- developing code, writing documentation or simply triaging tickets and helping to test proposed diff --git a/docs/releases/1.0-beta-2.txt b/docs/releases/1.0-beta-2.txt index 89b9233576..eabd6b744b 100644 --- a/docs/releases/1.0-beta-2.txt +++ b/docs/releases/1.0-beta-2.txt @@ -1,5 +1,3 @@ -.. _releases-1.0-beta-2: - =============================== Django 1.0 beta 2 release notes =============================== @@ -21,11 +19,11 @@ What's new in Django 1.0 beta 2 Django's development trunk has been the site of nearly constant activity over the past year, with several major new features landing since the 0.96 release. For features which were new as of Django 1.0 -alpha 1, see :ref:`the 1.0 alpha 1 release notes -<releases-1.0-alpha-1>`. For features which were new as of Django 1.0 -alpha 2, see :ref:`the 1.0 alpha 2 release notes -<releases-1.0-alpha-2>`. For features which were new as of Django 1.0 -beta 1, see :ref:`the 1.0 beta 1 release notes <releases-1.0-beta>`. +alpha 1, see :doc:`the 1.0 alpha 1 release notes +</releases/1.0-alpha-1>`. For features which were new as of Django 1.0 +alpha 2, see :doc:`the 1.0 alpha 2 release notes +</releases/1.0-alpha-2>`. For features which were new as of Django 1.0 +beta 1, see :doc:`the 1.0 beta 1 release notes </releases/1.0-beta>`. This beta release includes two major features: @@ -33,9 +31,9 @@ Refactored ``django.contrib.comments`` As part of a Google Summer of Code project, Thejaswi Puthraya carried out a major rewrite and refactoring of Django's bundled comment system, greatly increasing its flexibility and - customizability. :ref:`Full documentation - <ref-contrib-comments-index>` is available, as well as :ref:`an - upgrade guide <ref-contrib-comments-upgrade>` if you were using + customizability. :doc:`Full documentation + </ref/contrib/comments/index>` is available, as well as :doc:`an + upgrade guide </ref/contrib/comments/upgrade>` if you were using the previous incarnation of the comments application.. Refactored documentation @@ -59,8 +57,8 @@ Also, as part of its ongoing deprecation process, Django's old form-handling system has been removed; this means ``django.oldforms`` no longer exists, and its various API hooks (such as automatic manipulators) are no longer present in Django. This system has been -completely replaced by :ref:`the new form-handling system -<topics-forms-index>` in ``django.forms``. +completely replaced by :doc:`the new form-handling system +</topics/forms/index>` in ``django.forms``. The Django 1.0 roadmap @@ -114,7 +112,7 @@ to join the discussions there. Django's online documentation also includes pointers on how to contribute to Django: - :ref:`contributing to Django <internals-contributing>` + :doc:`contributing to Django </internals/contributing>` Contributions on any level -- developing code, writing documentation or simply triaging tickets and helping to test proposed diff --git a/docs/releases/1.0-beta.txt b/docs/releases/1.0-beta.txt index c1957a75a4..9e07e6c03f 100644 --- a/docs/releases/1.0-beta.txt +++ b/docs/releases/1.0-beta.txt @@ -1,5 +1,3 @@ -.. _releases-1.0-beta: - =============================== Django 1.0 beta 1 release notes =============================== @@ -20,9 +18,9 @@ What's new in Django 1.0 beta 1 Django's development trunk has been the site of nearly constant activity over the past year, with several major new features landing since the 0.96 release. -For features which were new as of Django 1.0 alpha 1, see :ref:`the 1.0 alpha 1 -release notes <releases-1.0-alpha-1>`. For features which were new as of Django -1.0 alpha 2, see :ref:`the 1.0 alpha 2 release notes <releases-1.0-alpha-2>`. +For features which were new as of Django 1.0 alpha 1, see :doc:`the 1.0 alpha 1 +release notes </releases/1.0-alpha-1>`. For features which were new as of Django +1.0 alpha 2, see :doc:`the 1.0 alpha 2 release notes </releases/1.0-alpha-2>`. This beta release does not contain any major new features, but does include several smaller updates and improvements to Django: @@ -38,7 +36,7 @@ Improved flexibility in the admin interface (``django.contrib.admin``), introduced in Django 1.0 alpha 1, two new hooks have been added to allow customized pre- and post-save handling of model instances in the admin. Full - details are in :ref:`the admin documentation <ref-contrib-admin>`. + details are in :doc:`the admin documentation </ref/contrib/admin/index>`. ``INSERT``/``UPDATE`` distinction Although Django's default behavior of having a model's ``save()`` @@ -59,7 +57,7 @@ Split ``CacheMiddleware`` flexibility for situations where combining these functions into a single middleware posed problems. Full details, including updated notes on appropriate use, are in - :ref:`the caching documentation <topics-cache>`. + :doc:`the caching documentation </topics/cache>`. Removal of deprecated features A number of features and methods which had previously been marked @@ -148,7 +146,7 @@ to join the discussions there. Django's online documentation also includes pointers on how to contribute to Django: - :ref:`contributing to Django <internals-contributing>` + :doc:`contributing to Django </internals/contributing>` Contributions on any level -- developing code, writing documentation or simply triaging tickets and helping to test proposed diff --git a/docs/releases/1.0-porting-guide.txt b/docs/releases/1.0-porting-guide.txt index f87da1c8d0..95b9efe255 100644 --- a/docs/releases/1.0-porting-guide.txt +++ b/docs/releases/1.0-porting-guide.txt @@ -13,7 +13,7 @@ Changes`_ for a list of a bunch of less-common compatibility issues. .. seealso:: - The :ref:`1.0 release notes <releases-1.0>`. That document explains the new + The :doc:`1.0 release notes </releases/1.0>`. That document explains the new features in 1.0 more deeply; the porting guide is more concerned with helping you quickly update your code. @@ -31,7 +31,7 @@ now uses Unicode strings throughout. In most places, raw strings will continue to work, but updating to use Unicode literals will prevent some obscure problems. -See :ref:`ref-unicode` for full details. +See :doc:`/ref/unicode` for full details. Models ------ @@ -211,7 +211,7 @@ New (1.0):: can be found on the `NewformsAdminBranch wiki page`__ * The new admin comes with a ton of new features; you can read about them in - the :ref:`admin documentation <ref-contrib-admin>`. + the :doc:`admin documentation </ref/contrib/admin/index>`. __ http://code.djangoproject.com/wiki/NewformsAdminBranch @@ -271,7 +271,7 @@ New:: If you're using the old forms system (formerly known as ``django.forms`` and ``django.oldforms``), you'll have to rewrite your forms. A good place to start -is the :ref:`forms documentation <topics-forms-index>` +is the :doc:`forms documentation </topics/forms/index>` Handle uploaded files using the new API ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -318,7 +318,7 @@ Old (0.96) New (1.0) Note that the ``width`` and ``height`` attributes only make sense for :class:`~django.db.models.ImageField` fields. More details can be found in the -:ref:`model API <ref-models-fields>` documentation. +:doc:`model API </ref/models/fields>` documentation. Use ``Paginator`` instead of ``ObjectPaginator`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -392,7 +392,7 @@ Comments If you were using Django 0.96's ``django.contrib.comments`` app, you'll need to upgrade to the new comments app introduced in 1.0. See -:ref:`ref-contrib-comments-upgrade` for details. +:doc:`/ref/contrib/comments/upgrade` for details. Template tags ------------- diff --git a/docs/releases/1.0.1.txt b/docs/releases/1.0.1.txt index ce101bed03..780dc53c1f 100644 --- a/docs/releases/1.0.1.txt +++ b/docs/releases/1.0.1.txt @@ -1,5 +1,3 @@ -.. _releases-1.0.1: - ========================== Django 1.0.1 release notes ========================== diff --git a/docs/releases/1.0.2.txt b/docs/releases/1.0.2.txt index 136a833f05..b34522a08d 100644 --- a/docs/releases/1.0.2.txt +++ b/docs/releases/1.0.2.txt @@ -1,5 +1,3 @@ -.. _releases-1.0.2: - ========================== Django 1.0.2 release notes ========================== @@ -9,7 +7,7 @@ Welcome to Django 1.0.2! This is the second "bugfix" release in the Django 1.0 series, improving the stability and performance of the Django 1.0 codebase. As such, Django 1.0.2 contains no new features (and, pursuant to -:ref:`our compatibility policy <misc-api-stability>`, maintains backwards compatibility with Django +:doc:`our compatibility policy </misc/api-stability>`, maintains backwards compatibility with Django 1.0.0), but does contain a number of fixes and other improvements. Django 1.0.2 is a recommended upgrade for any development or deployment currently using or targeting Django 1.0. @@ -27,7 +25,7 @@ Django's unit-test suite. Django 1.0.2 contains updated packaging scripts, and the release package contains the directories omitted from Django 1.0.1. As such, this release contains all of the fixes and improvements from Django -1.0.1; see :ref:`the Django 1.0.1 release notes <releases-1.0.1>` for +1.0.1; see :doc:`the Django 1.0.1 release notes </releases/1.0.1>` for details. Additionally, in the period since Django 1.0.1 was released: diff --git a/docs/releases/1.0.txt b/docs/releases/1.0.txt index 6827a62cc8..359490aad3 100644 --- a/docs/releases/1.0.txt +++ b/docs/releases/1.0.txt @@ -1,5 +1,3 @@ -.. _releases-1.0: - ======================== Django 1.0 release notes ======================== @@ -24,12 +22,12 @@ contributions overtake those made privately. Stability and forwards-compatibility ==================================== -:ref:`The release of Django 1.0 <releases-1.0>` comes with a promise of API +:doc:`The release of Django 1.0 </releases/1.0>` comes with a promise of API stability and forwards-compatibility. In a nutshell, this means that code you develop against Django 1.0 will continue to work against 1.1 unchanged, and you should need to make only minor changes for any 1.X release. -See the :ref:`API stability guide <misc-api-stability>` for full details. +See the :doc:`API stability guide </misc/api-stability>` for full details. Backwards-incompatible changes ============================== @@ -42,7 +40,7 @@ detailed porting guide: :maxdepth: 1 1.0-porting-guide - + A complete list of backwards-incompatible changes can be found at http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges. @@ -60,9 +58,9 @@ In fact, new documentation is one of our favorite features of Django 1.0, so we might as well start there. First, there's a new documentation site: http://docs.djangoproject.com/ - + The documentation has been greatly improved, cleaned up, and generally made -awesome. There's now dedicated search, indexes, and more. +awesome. There's now dedicated search, indexes, and more. We can't possibly document everything that's new in 1.0, but the documentation will be your definitive guide. Anywhere you see something like: @@ -85,7 +83,7 @@ Django's new form-handling library (introduced in the 0.96 release as redesigned with extensibility and customization in mind. Full documentation for the admin application is available online in the official Django documentation: -See the :ref:`admin reference <ref-contrib-admin>` for details +See the :doc:`admin reference </ref/contrib/admin/index>` for details Improved Unicode handling ------------------------- @@ -97,7 +95,7 @@ interoperability with third-party libraries and systems which may or may not handle Unicode gracefully. Details are available in Django's Unicode-handling documentation. -See :ref:`ref-unicode`. +See :doc:`/ref/unicode`. An improved ORM --------------- @@ -142,8 +140,8 @@ Pluggable file storage Django's built-in ``FileField`` and ``ImageField`` now can take advantage of pluggable file-storage backends, allowing extensive customization of where and -how uploaded files get stored by Django. For details, see :ref:`the files -documentation <topics-files>`; big thanks go to Marty Alchin for putting in the +how uploaded files get stored by Django. For details, see :doc:`the files +documentation </topics/files>`; big thanks go to Marty Alchin for putting in the hard work to get this completed. Jython compatibility @@ -155,7 +153,7 @@ Django's codebase has been refactored to remove incompatibilities with on the Java Virtual Machine. Django is now compatible with the forthcoming Jython 2.5 release. -See :ref:`howto-jython`. +See :doc:`/howto/jython`. .. _Jython: http://www.jython.org/ @@ -187,17 +185,17 @@ handle the two parts of caching (inserting into and reading from the cache) separately, offering additional flexibility for situations where combining these functions into a single middleware posed problems. -Full details, including updated notes on appropriate use, are in :ref:`the -caching documentation <topics-cache>`. +Full details, including updated notes on appropriate use, are in :doc:`the +caching documentation </topics/cache>`. Refactored ``django.contrib.comments`` -------------------------------------- As part of a Google Summer of Code project, Thejaswi Puthraya carried out a major rewrite and refactoring of Django's bundled comment system, greatly -increasing its flexibility and customizability. :ref:`Full documentation -<ref-contrib-comments-index>` is available, as well as :ref:`an upgrade guide -<ref-contrib-comments-upgrade>` if you were using the previous incarnation of +increasing its flexibility and customizability. :doc:`Full documentation +</ref/contrib/comments/index>` is available, as well as :doc:`an upgrade guide +</ref/contrib/comments/upgrade>` if you were using the previous incarnation of the comments application. Removal of deprecated features @@ -240,7 +238,7 @@ Caveats with support of certain databases ----------------------------------------- Django attempts to support as many features as possible on all database -backends. However, not all database backends are alike, and in particular many of the supported database differ greatly from version to version. It's a good idea to checkout our :ref:`notes on supported database <ref-databases>`: +backends. However, not all database backends are alike, and in particular many of the supported database differ greatly from version to version. It's a good idea to checkout our :doc:`notes on supported database </ref/databases>`: - :ref:`mysql-notes` - :ref:`sqlite-notes` diff --git a/docs/releases/1.1-alpha-1.txt b/docs/releases/1.1-alpha-1.txt index 664c354561..b15a2a423c 100644 --- a/docs/releases/1.1-alpha-1.txt +++ b/docs/releases/1.1-alpha-1.txt @@ -1,5 +1,3 @@ -.. _releases-1.1-alpha-1: - ================================ Django 1.1 alpha 1 release notes ================================ @@ -37,8 +35,8 @@ results of the aggregate directly, or else annotate the objects in a :class:`QuerySet` with the results of the aggregate query. This feature is available as new :meth:`QuerySet.aggregate()`` and -:meth:`QuerySet.annotate()`` methods, and is covered in detail in :ref:`the ORM -aggregation documentation <topics-db-aggregation>` +:meth:`QuerySet.annotate()`` methods, and is covered in detail in :doc:`the ORM +aggregation documentation </topics/db/aggregation>` Query expressions ~~~~~~~~~~~~~~~~~ @@ -53,7 +51,7 @@ Performance improvements .. currentmodule:: django.test -Tests written using Django's :ref:`testing framework <topics-testing>` now run +Tests written using Django's :doc:`testing framework </topics/testing>` now run dramatically faster (as much as 10 times faster in many cases). This was accomplished through the introduction of transaction-based tests: when @@ -68,7 +66,7 @@ Other improvements Other new features and changes introduced since Django 1.0 include: -* The :ref:`CSRF protection middleware <ref-contrib-csrf>` has been split into +* The :doc:`CSRF protection middleware </ref/contrib/csrf>` has been split into two classes -- ``CsrfViewMiddleware`` checks incoming requests, and ``CsrfResponseMiddleware`` processes outgoing responses. The combined ``CsrfMiddleware`` class (which does both) remains for @@ -85,13 +83,13 @@ Other new features and changes introduced since Django 1.0 include: * The ``include()`` function in Django URLconf modules can now accept sequences of URL patterns (generated by ``patterns()``) in addition to module names. -* Instances of Django forms (see :ref:`the forms overview <topics-forms-index>`) +* Instances of Django forms (see :doc:`the forms overview </topics/forms/index>`) now have two additional methods, ``hidden_fields()`` and ``visible_fields()``, which return the list of hidden -- i.e., ``<input type="hidden">`` -- and visible fields on the form, respectively. -* The ``redirect_to`` generic view (see :ref:`the generic views documentation - <ref-generic-views>`) now accepts an additional keyword argument +* The ``redirect_to`` generic view (see :doc:`the generic views documentation + </ref/generic-views>`) now accepts an additional keyword argument ``permanent``. If ``permanent`` is ``True``, the view will emit an HTTP permanent redirect (status code 301). If ``False``, the view will emit an HTTP temporary redirect (status code 302). @@ -104,8 +102,8 @@ Other new features and changes introduced since Django 1.0 include: * The ``{% for %}`` tag in Django's template language now accepts an optional ``{% empty %}`` clause, to be displayed when ``{% for %}`` is asked to loop - over an empty sequence. See :ref:`the list of built-in template tags - <ref-templates-builtins>` for examples of this. + over an empty sequence. See :doc:`the list of built-in template tags + </ref/templates/builtins>` for examples of this. The Django 1.1 roadmap ====================== @@ -153,7 +151,7 @@ discussions there. Django's online documentation also includes pointers on how to contribute to Django: - * :ref:`How to contribute to Django <internals-contributing>` + * :doc:`How to contribute to Django </internals/contributing>` Contributions on any level -- developing code, writing documentation or simply triaging tickets and helping to test proposed bugfixes -- are always welcome and diff --git a/docs/releases/1.1-beta-1.txt b/docs/releases/1.1-beta-1.txt index a433efc33c..83423962b3 100644 --- a/docs/releases/1.1-beta-1.txt +++ b/docs/releases/1.1-beta-1.txt @@ -1,5 +1,3 @@ -.. _releases-1.1-beta-1: - =============================== Django 1.1 beta 1 release notes =============================== @@ -22,7 +20,7 @@ What's new in Django 1.1 beta 1 .. seealso:: - The :ref:`1.1 alpha release notes <releases-1.1-alpha-1>`, which has a + The :doc:`1.1 alpha release notes </releases/1.1-alpha-1>`, which has a list of everything new between Django 1.0 and Django 1.1 alpha. Model improvements @@ -71,8 +69,9 @@ processing to convert them to Python objects. If you know you don't need those particular fields, you can now tell Django not to retrieve them from the database. -You'll do this with the :ref:`new queryset methods <queryset-defer>` -``defer()`` and ``only()``. +You'll do this with the new queryset methods +:meth:`~django.db.models.QuerySet.defer` and +:meth:`~django.db.models.QuerySet.only`. New admin features ------------------ @@ -90,7 +89,7 @@ up as form widgets on the list pages, and can be edited and saved in bulk. Admin "actions" ~~~~~~~~~~~~~~~ -You can now define :ref:`admin actions <ref-contrib-admin-actions>` that can perform +You can now define :doc:`admin actions </ref/contrib/admin/actions>` that can perform some action to a group of models in bulk. Users will be able to select objects on the change list page and then apply these bulk actions to all selected objects. @@ -103,23 +102,23 @@ Testing improvements .. currentmodule:: django.test.client A couple of small but very useful improvements have been made to the -:ref:`testing framework <topics-testing>`: +:doc:`testing framework </topics/testing>`: * The test :class:`Client` now can automatically follow redirects with the ``follow`` argument to :meth:`Client.get` and :meth:`Client.post`. This makes testing views that issue redirects simpler. - + * It's now easier to get at the template context in the response returned the test client: you'll simply access the context as ``request.context[key]``. The old way, which treats ``request.context`` as a list of contexts, one for each rendered template, is still available if you need it. - + Conditional view processing --------------------------- -Django now has much better support for :ref:`conditional view processing -<topics-conditional-processing>` using the standard ``ETag`` and +Django now has much better support for :doc:`conditional view processing +</topics/conditional-view-processing>` using the standard ``ETag`` and ``Last-Modified`` HTTP headers. This means you can now easily short-circuit view processing by testing less-expensive conditions. For many views this can lead to a serious improvement in speed and reduction in bandwidth. @@ -133,23 +132,23 @@ release, including: * The :djadmin:`dumpdata` management command now accepts individual model names as arguments, allowing you to export the data just from particular models. - + * There's a new :tfilter:`safeseq` template filter which works just like :tfilter:`safe` for lists, marking each item in the list as safe. - - * :ref:`Cache backends <topics-cache>` now support ``incr()`` and + + * :doc:`Cache backends </topics/cache>` now support ``incr()`` and ``decr()`` commands to increment and decrement the value of a cache key. On cache backends that support atomic increment/decrement -- most notably, the memcached backend -- these operations will be atomic, and quite fast. - - * Django now can :ref:`easily delegate authentication to the web server - <howto-auth-remote-user>` via a new authentication backend that supports + + * Django now can :doc:`easily delegate authentication to the web server + </howto/auth-remote-user>` via a new authentication backend that supports the standard ``REMOTE_USER`` environment variable used for this purpose. - + * There's a new :func:`django.shortcuts.redirect` function that makes it easier to issue redirects given an object, a view name, or a URL. - + * The ``postgresql_psycopg2`` backend now supports :ref:`native PostgreSQL autocommit <postgresql-notes>`. This is an advanced, PostgreSQL-specific feature, that can make certain read-heavy applications a good deal @@ -183,7 +182,7 @@ central place to search for open issues: * http://code.djangoproject.com/timeline Please open new tickets if no existing ticket corresponds to a problem you're -running into. +running into. Additionally, discussion of Django development, including progress toward the 1.1 release, takes place daily on the django-developers mailing list: @@ -195,9 +194,9 @@ interested in helping out with Django's development, feel free to join the discussions there. Django's online documentation also includes pointers on how to contribute to -Django: +Django: - * :ref:`How to contribute to Django <internals-contributing>` + * :doc:`How to contribute to Django </internals/contributing>` Contributions on any level -- developing code, writing documentation or simply triaging tickets and helping to test proposed bugfixes -- are always welcome and diff --git a/docs/releases/1.1-rc-1.txt b/docs/releases/1.1-rc-1.txt index bda424800e..f74444f1f9 100644 --- a/docs/releases/1.1-rc-1.txt +++ b/docs/releases/1.1-rc-1.txt @@ -1,5 +1,3 @@ -.. _releases-1.1-rc-1: - ============================= Django 1.1 RC 1 release notes ============================= @@ -30,8 +28,8 @@ contains only one new feature (see below); work leading up to this release candidate has instead been focused on bugfixing, particularly on the new features introduced prior to the 1.1 beta. -For an overview of those features, consult :ref:`the Django 1.1 beta -release notes <releases-1.1-beta-1>`. +For an overview of those features, consult :doc:`the Django 1.1 beta +release notes </releases/1.1-beta-1>`. URL namespaces @@ -104,7 +102,7 @@ discussions there. Django's online documentation also includes pointers on how to contribute to Django: - * :ref:`How to contribute to Django <internals-contributing>` + * :doc:`How to contribute to Django </internals/contributing>` Contributions on any level -- developing code, writing documentation or simply triaging tickets and helping to test proposed bugfixes -- are always welcome and diff --git a/docs/releases/1.1.2.txt b/docs/releases/1.1.2.txt index 3e5355f231..90a69759bf 100644 --- a/docs/releases/1.1.2.txt +++ b/docs/releases/1.1.2.txt @@ -1,5 +1,3 @@ -.. _releases-1.1.2: - ========================== Django 1.1.2 release notes ========================== @@ -15,7 +13,7 @@ improvements. Django 1.1.2 is a recommended upgrade for any development or deployment currently using or targeting Django 1.1. For full details on the new features, backwards incompatibilities, and -deprecated features in the 1.1 branch, see the :ref:`releases-1.1`. +deprecated features in the 1.1 branch, see the :doc:`/releases/1.1`. Backwards-incompatible changes in 1.1.2 ======================================= diff --git a/docs/releases/1.1.txt b/docs/releases/1.1.txt index edb7cf1af2..39cb0ab2b0 100644 --- a/docs/releases/1.1.txt +++ b/docs/releases/1.1.txt @@ -1,5 +1,3 @@ -.. _releases-1.1: - ======================== Django 1.1 release notes ======================== @@ -19,7 +17,7 @@ fixes, and an easy upgrade path from Django 1.0. Backwards-incompatible changes in 1.1 ===================================== -Django has a policy of :ref:`API stability <misc-api-stability>`. This means +Django has a policy of :doc:`API stability </misc/api-stability>`. This means that, in general, code you develop against Django 1.0 should continue to work against 1.1 unchanged. However, we do sometimes make backwards-incompatible changes if they're necessary to resolve bugs, and there are a handful of such @@ -176,7 +174,7 @@ be upgraded to a ``DeprecationWarning``, which will be displayed loudly. Django 1.3 will remove ``AdminSite.root()`` entirely. For more details on our deprecation policies and strategy, see -:ref:`internals-release-process`. +:doc:`/internals/release-process`. What's new in Django 1.1 ======================== @@ -203,8 +201,8 @@ results of the aggregate directly, or else annotate the objects in a :class:`QuerySet` with the results of the aggregate query. This feature is available as new :meth:`QuerySet.aggregate()`` and -:meth:`QuerySet.annotate()`` methods, and is covered in detail in :ref:`the ORM -aggregation documentation <topics-db-aggregation>`. +:meth:`QuerySet.annotate()`` methods, and is covered in detail in :doc:`the ORM +aggregation documentation </topics/db/aggregation>`. Query expressions ~~~~~~~~~~~~~~~~~ @@ -258,21 +256,22 @@ processing to convert them to Python objects. If you know you don't need those particular fields, you can now tell Django not to retrieve them from the database. -You'll do this with the :ref:`new queryset methods <queryset-defer>` -``defer()`` and ``only()``. +You'll do this with the new queryset methods +:meth:`~django.db.models.QuerySet.defer` and +:meth:`~django.db.models.QuerySet.only`. Testing improvements -------------------- -A few notable improvements have been made to the :ref:`testing framework -<topics-testing>`. +A few notable improvements have been made to the :doc:`testing framework +</topics/testing>`. Test performance improvements ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. currentmodule:: django.test -Tests written using Django's :ref:`testing framework <topics-testing>` now run +Tests written using Django's :doc:`testing framework </topics/testing>` now run dramatically faster (as much as 10 times faster in many cases). This was accomplished through the introduction of transaction-based tests: when @@ -315,7 +314,7 @@ up as form widgets on the list pages, and can be edited and saved in bulk. Admin "actions" ~~~~~~~~~~~~~~~ -You can now define :ref:`admin actions <ref-contrib-admin-actions>` that can +You can now define :doc:`admin actions </ref/contrib/admin/actions>` that can perform some action to a group of models in bulk. Users will be able to select objects on the change list page and then apply these bulk actions to all selected objects. @@ -326,8 +325,8 @@ one fell swoop. Conditional view processing --------------------------- -Django now has much better support for :ref:`conditional view processing -<topics-conditional-processing>` using the standard ``ETag`` and +Django now has much better support for :doc:`conditional view processing +</topics/conditional-view-processing>` using the standard ``ETag`` and ``Last-Modified`` HTTP headers. This means you can now easily short-circuit view processing by testing less-expensive conditions. For many views this can lead to a serious improvement in speed and reduction in bandwidth. @@ -375,7 +374,7 @@ Other improvements Other new features and changes introduced since Django 1.0 include: -* The :ref:`CSRF protection middleware <ref-contrib-csrf>` has been split into +* The :doc:`CSRF protection middleware </ref/contrib/csrf>` has been split into two classes -- ``CsrfViewMiddleware`` checks incoming requests, and ``CsrfResponseMiddleware`` processes outgoing responses. The combined ``CsrfMiddleware`` class (which does both) remains for @@ -392,13 +391,13 @@ Other new features and changes introduced since Django 1.0 include: * The ``include()`` function in Django URLconf modules can now accept sequences of URL patterns (generated by ``patterns()``) in addition to module names. -* Instances of Django forms (see :ref:`the forms overview <topics-forms-index>`) +* Instances of Django forms (see :doc:`the forms overview </topics/forms/index>`) now have two additional methods, ``hidden_fields()`` and ``visible_fields()``, which return the list of hidden -- i.e., ``<input type="hidden">`` -- and visible fields on the form, respectively. -* The ``redirect_to`` generic view (see :ref:`the generic views documentation - <ref-generic-views>`) now accepts an additional keyword argument +* The ``redirect_to`` generic view (see :doc:`the generic views documentation + </ref/generic-views>`) now accepts an additional keyword argument ``permanent``. If ``permanent`` is ``True``, the view will emit an HTTP permanent redirect (status code 301). If ``False``, the view will emit an HTTP temporary redirect (status code 302). @@ -411,8 +410,8 @@ Other new features and changes introduced since Django 1.0 include: * The ``{% for %}`` tag in Django's template language now accepts an optional ``{% empty %}`` clause, to be displayed when ``{% for %}`` is asked to loop - over an empty sequence. See :ref:`the list of built-in template tags - <ref-templates-builtins>` for examples of this. + over an empty sequence. See :doc:`the list of built-in template tags + </ref/templates/builtins>` for examples of this. * The :djadmin:`dumpdata` management command now accepts individual model names as arguments, allowing you to export the data just from @@ -421,14 +420,14 @@ Other new features and changes introduced since Django 1.0 include: * There's a new :tfilter:`safeseq` template filter which works just like :tfilter:`safe` for lists, marking each item in the list as safe. -* :ref:`Cache backends <topics-cache>` now support ``incr()`` and +* :doc:`Cache backends </topics/cache>` now support ``incr()`` and ``decr()`` commands to increment and decrement the value of a cache key. On cache backends that support atomic increment/decrement -- most notably, the memcached backend -- these operations will be atomic, and quite fast. -* Django now can :ref:`easily delegate authentication to the web server - <howto-auth-remote-user>` via a new authentication backend that supports +* Django now can :doc:`easily delegate authentication to the web server + </howto/auth-remote-user>` via a new authentication backend that supports the standard ``REMOTE_USER`` environment variable used for this purpose. * There's a new :func:`django.shortcuts.redirect` function that makes it @@ -455,7 +454,7 @@ join the discussions! Django's online documentation also includes pointers on how to contribute to Django: - * :ref:`How to contribute to Django <internals-contributing>` + * :doc:`How to contribute to Django </internals/contributing>` Contributions on any level -- developing code, writing documentation or simply triaging tickets and helping to test proposed bugfixes -- are always welcome and diff --git a/docs/releases/1.2-alpha-1.txt b/docs/releases/1.2-alpha-1.txt index 1e9d4422ce..4144a9a3a9 100644 --- a/docs/releases/1.2-alpha-1.txt +++ b/docs/releases/1.2-alpha-1.txt @@ -1,5 +1,3 @@ -.. _releases-1.2-alpha-1: - ================================ Django 1.2 alpha 1 release notes ================================ @@ -25,7 +23,7 @@ CSRF Protection --------------- There have been large changes to the way that CSRF protection works, detailed in -:ref:`the CSRF documentaton <ref-contrib-csrf>`. The following are the major +:doc:`the CSRF documentaton </ref/contrib/csrf>`. The following are the major changes that developers must be aware of: * ``CsrfResponseMiddleware`` and ``CsrfMiddleware`` have been deprecated, and @@ -303,7 +301,7 @@ User Messages API The API for storing messages in the user ``Message`` model (via ``user.message_set.create``) is now deprecated and will be removed in Django -1.4 according to the standard :ref:`release process <internals-release-process>`. +1.4 according to the standard :doc:`release process </internals/release-process>`. To upgrade your code, you need to replace any instances of:: @@ -327,7 +325,7 @@ with:: ... For more information, see the full -:ref:`messages documentation <ref-contrib-messages>`. You should begin to +:doc:`messages documentation </ref/contrib/messages>`. You should begin to update your code to use the new API immediately. Date format helper functions @@ -378,8 +376,8 @@ release cycle. Some minor features will continue development until the CSRF support ------------ -Django now has much improved protection against :ref:`Cross-Site -Request Forgery (CSRF) attacks<ref-contrib-csrf>`. This type of attack +Django now has much improved protection against :doc:`Cross-Site +Request Forgery (CSRF) attacks</ref/contrib/csrf>`. This type of attack occurs when a malicious Web site contains a link, a form button or some javascript that is intended to perform some action on your Web site, using the credentials of a logged-in user who visits the @@ -395,7 +393,7 @@ You can now :ref:`configure the way that Django sends e-mail can now choose a configurable e-mail backend to send messages. If your hosting provider uses a sandbox or some other non-SMTP technique for sending mail, you can now construct an e-mail backend that will allow -Django's standard :ref:`mail sending methods<topics-email>` to use +Django's standard :doc:`mail sending methods</topics/email>` to use those facilities. This also makes it easier to debug mail sending - Django ships with @@ -408,8 +406,8 @@ e-mail to be :ref:`thrown away<topic-email-dummy-backend>`. Messages Framework ------------------ -Django now includes a robust and configurable :ref:`messages framework -<ref-contrib-messages>` with built-in support for cookie- and session-based +Django now includes a robust and configurable :doc:`messages framework +</ref/contrib/messages>` with built-in support for cookie- and session-based messaging, for both anonymous and authenticated clients. The messages framework replaces the deprecated user message API and allows you to temporarily store messages in one request and retrieve them for display in a subsequent request @@ -418,8 +416,8 @@ messages in one request and retrieve them for display in a subsequent request Support for multiple databases ------------------------------ -Django 1.2 adds the ability to use :ref:`more than one database -<topics-db-multi-db>` in your Django project. Queries can be +Django 1.2 adds the ability to use :doc:`more than one database +</topics/db/multi-db>` in your Django project. Queries can be issued at a specific database with the `using()` method on querysets; individual objects can be saved to a specific database by providing a ``using`` argument when you save the instance. @@ -500,7 +498,7 @@ from the test run that reports details of the tests run before the interruption. Improved localization --------------------- -Django's :ref:`internationalization framework <topics-i18n>` has been +Django's :doc:`internationalization framework </topics/i18n/index>` has been expanded by locale aware formatting and form processing. That means, if enabled, dates and numbers on templates will be displayed using the format specified for the current locale. Django will also use localized formats @@ -568,7 +566,7 @@ discussions there. Django's online documentation also includes pointers on how to contribute to Django: - * :ref:`How to contribute to Django <internals-contributing>` + * :doc:`How to contribute to Django </internals/contributing>` Contributions on any level -- developing code, writing documentation or simply triaging tickets and helping to test proposed bugfixes -- are always welcome and diff --git a/docs/releases/1.2-beta-1.txt b/docs/releases/1.2-beta-1.txt index 650971de2b..2a12ef33bb 100644 --- a/docs/releases/1.2-beta-1.txt +++ b/docs/releases/1.2-beta-1.txt @@ -1,5 +1,3 @@ -.. _releases-1.2-beta-1: - =============================== Django 1.2 beta 1 release notes =============================== @@ -19,7 +17,7 @@ As such, this release is *not* intended for production use, and any such use is discouraged. This document covers changes since the Django 1.2 alpha release; the -:ref:`1.2 alpha release notes <releases-1.2-alpha-1>` cover new and +:doc:`1.2 alpha release notes </releases/1.2-alpha-1>` cover new and updated features in Django between 1.1 and 1.2 alpha. @@ -28,7 +26,7 @@ Deprecations and other changes in 1.2 beta This beta release deprecates two portions of public API, and introduces a potentially backwards-incompatible change to -another. Under :ref:`our API stability policy <misc-api-stability>`, +another. Under :doc:`our API stability policy </misc/api-stability>`, deprecation proceeds over multiple release cycles: initially, the deprecated API will raise ``PendingDeprecationWarning``, followed by raising ``DeprecationWarning`` in the next release, and finally @@ -58,8 +56,8 @@ Also, in accordance with `RSS best practices`_, RSS feeds will now include an ``atom:link`` element. You may need to update your tests to take this into account. -For more information, see the full :ref:`syndication framework -documentation <ref-contrib-syndication>`. +For more information, see the full :doc:`syndication framework +documentation </ref/contrib/syndication>`. .. _RSS best practices: http://www.rssboard.org/rss-profile @@ -93,7 +91,7 @@ added in Django 1.2 alpha but not documented with the alpha release. The default authentication backends shipped with Django do not currently make use of this, but third-party authentication backends -are free to do so. See the :ref:`authentication docs <topics-auth>` +are free to do so. See the :doc:`authentication docs </topics/auth>` for more information. @@ -106,7 +104,7 @@ class will check the backend for permissions, just as the normal ``User`` does. This is intended to help centralize permission handling; apps can always delegate the question of whether something is allowed or not to the authorization/authentication system. See the -:ref:`authentication docs <topics-auth>` for more details. +:doc:`authentication docs </topics/auth>` for more details. ``select_related()`` improvements @@ -163,7 +161,7 @@ discussions there. Django's online documentation also includes pointers on how to contribute to Django: - * :ref:`How to contribute to Django <internals-contributing>` + * :doc:`How to contribute to Django </internals/contributing>` Contributions on any level -- developing code, writing documentation or simply triaging tickets and helping to test proposed bugfixes -- diff --git a/docs/releases/1.2-rc-1.txt b/docs/releases/1.2-rc-1.txt index c627612a6e..b599dcca1e 100644 --- a/docs/releases/1.2-rc-1.txt +++ b/docs/releases/1.2-rc-1.txt @@ -1,5 +1,3 @@ -.. _releases-1.2-rc-1: - ============================= Django 1.2 RC 1 release notes ============================= @@ -21,8 +19,8 @@ such use is discouraged. Django has been feature frozen since the 1.2 beta release, so this release candidate contains no new features, only bugfixes; for a -summary of features new to Django 1.2, consult the :ref:`1.2 alpha -<releases-1.2-alpha-1>` and :ref:`1.2 beta <releases-1.2-beta-1>` +summary of features new to Django 1.2, consult the :doc:`1.2 alpha +</releases/1.2-alpha-1>` and :doc:`1.2 beta </releases/1.2-beta-1>` release notes. @@ -42,7 +40,7 @@ This change should affect only a small number of Django users, as most operating-system vendors today are shipping Python 2.4 or newer as their default version. If you're still using Python 2.3, however, you'll need to stick to Django 1.1 until you can upgrade; per -:ref:`our support policy <internals-release-process>`, Django 1.1 will +:doc:`our support policy </internals/release-process>`, Django 1.1 will continue to receive security support until the release of Django 1.3. A roadmap for Django's overall 2.x Python support, and eventual @@ -96,7 +94,7 @@ discussions there. Django's online documentation also includes pointers on how to contribute to Django: - * :ref:`How to contribute to Django <internals-contributing>` + * :doc:`How to contribute to Django </internals/contributing>` Contributions on any level -- developing code, writing documentation or simply triaging tickets and helping to test proposed bugfixes -- are always welcome and diff --git a/docs/releases/1.2.txt b/docs/releases/1.2.txt index a54675514f..f46c19b8e8 100644 --- a/docs/releases/1.2.txt +++ b/docs/releases/1.2.txt @@ -1,5 +1,3 @@ -.. _releases-1.2: - ======================== Django 1.2 release notes ======================== @@ -21,11 +19,11 @@ Overview Django 1.2 introduces several large, important new features, including: * Support for `multiple database connections`_ in a single Django instance. - + * `Model validation`_ inspired by Django's form validation. - + * Vastly `improved protection against Cross-Site Request Forgery`_ (CSRF). - + * A new `user "messages" framework`_ with support for cookie- and session-based message for both anonymous and authenticated users. @@ -49,13 +47,13 @@ be found below`_. .. seealso:: - `Django Advent`_ covered the release of Django 1.2 with a series of + `Django Advent`_ covered the release of Django 1.2 with a series of articles and tutorials that cover some of the new features in depth. - + .. _django advent: http://djangoadvent.com/ Wherever possible these features have been introduced in a backwards-compatible -manner per :ref:`our API stability policy <misc-api-stability>` policy. +manner per :doc:`our API stability policy </misc/api-stability>` policy. However, a handful of features *have* changed in ways that, for some users, will be backwards-incompatible. The big changes are: @@ -66,7 +64,7 @@ backwards-incompatible. The big changes are: * The new CSRF protection framework is not backwards-compatible with the old system. Users of the old system will not be affected until the old system is removed in Django 1.4. - + However, upgrading to the new CSRF protection framework requires a few important backwards-incompatible changes, detailed in `CSRF Protection`_, below. @@ -74,12 +72,12 @@ backwards-incompatible. The big changes are: * Authors of custom :class:`~django.db.models.Field` subclasses should be aware that a number of methods have had a change in prototype, detailed under `get_db_prep_*() methods on Field`_, below. - + * The internals of template tags have changed somewhat; authors of custom template tags that need to store state (e.g. custom control flow tags) should ensure that their code follows the new rules for `stateful template tags`_ - + * The :func:`~django.contrib.auth.decorators.user_passes_test`, :func:`~django.contrib.auth.decorators.login_required`, and :func:`~django.contrib.auth.decorators.permission_required`, decorators @@ -110,7 +108,7 @@ This change should affect only a small number of Django users, as most operating-system vendors today are shipping Python 2.4 or newer as their default version. If you're still using Python 2.3, however, you'll need to stick to Django 1.1 until you can upgrade; per -:ref:`our support policy <internals-release-process>`, Django 1.1 will +:doc:`our support policy </internals/release-process>`, Django 1.1 will continue to receive security support until the release of Django 1.3. A roadmap for Django's overall 2.x Python support, and eventual @@ -123,8 +121,8 @@ What's new in Django 1.2 Support for multiple databases ------------------------------ -Django 1.2 adds the ability to use :ref:`more than one database -<topics-db-multi-db>` in your Django project. Queries can be issued at a +Django 1.2 adds the ability to use :doc:`more than one database +</topics/db/multi-db>` in your Django project. Queries can be issued at a specific database with the `using()` method on ``QuerySet`` objects. Individual objects can be saved to a specific database by providing a ``using`` argument when you call ``save()``. @@ -134,7 +132,7 @@ Model validation Model instances now have support for :ref:`validating their own data <validating-objects>`, and both model and form fields now accept configurable -lists of :ref:`validators <ref-validators>` specifying reusable, encapsulated +lists of :doc:`validators </ref/validators>` specifying reusable, encapsulated validation behavior. Note, however, that validation must still be performed explicitly. Simply invoking a model instance's ``save()`` method will not perform any validation of the instance's data. @@ -142,8 +140,8 @@ perform any validation of the instance's data. Improved CSRF protection ------------------------ -Django now has much improved protection against :ref:`Cross-Site Request Forgery -(CSRF) attacks<ref-contrib-csrf>`. This type of attack occurs when a malicious +Django now has much improved protection against :doc:`Cross-Site Request Forgery +(CSRF) attacks</ref/contrib/csrf>`. This type of attack occurs when a malicious Web site contains a link, a form button or some JavaScript that is intended to perform some action on your Web site, using the credentials of a logged-in user who visits the malicious site in their browser. A related type of attack, "login @@ -153,8 +151,8 @@ with someone else's credentials, is also covered. Messages framework ------------------ -Django now includes a robust and configurable :ref:`messages framework -<ref-contrib-messages>` with built-in support for cookie- and session-based +Django now includes a robust and configurable :doc:`messages framework +</ref/contrib/messages>` with built-in support for cookie- and session-based messaging, for both anonymous and authenticated clients. The messages framework replaces the deprecated user message API and allows you to temporarily store messages in one request and retrieve them for display in a subsequent request @@ -166,8 +164,8 @@ Object-level permissions A foundation for specifying permissions at the per-object level has been added. Although there is no implementation of this in core, a custom authentication backend can provide this implementation and it will be used by -:class:`django.contrib.auth.models.User`. See the :ref:`authentication docs -<topics-auth>` for more information. +:class:`django.contrib.auth.models.User`. See the :doc:`authentication docs +</topics/auth>` for more information. Permissions for anonymous users ------------------------------- @@ -176,8 +174,8 @@ If you provide a custom auth backend with ``supports_anonymous_user`` set to ``True``, AnonymousUser will check the backend for permissions, just like User already did. This is useful for centralizing permission handling - apps can always delegate the question of whether something is allowed or not to -the authorization/authentication backend. See the :ref:`authentication -docs <topics-auth>` for more details. +the authorization/authentication backend. See the :doc:`authentication +docs </topics/auth>` for more details. Relaxed requirements for usernames ---------------------------------- @@ -194,7 +192,7 @@ You can now :ref:`configure the way that Django sends e-mail can now choose a configurable e-mail backend to send messages. If your hosting provider uses a sandbox or some other non-SMTP technique for sending mail, you can now construct an e-mail backend that will allow -Django's standard :ref:`mail sending methods<topics-email>` to use +Django's standard :doc:`mail sending methods</topics/email>` to use those facilities. This also makes it easier to debug mail sending. Django ships with @@ -286,7 +284,7 @@ Models can now use a 64-bit :class:`~django.db.models.BigIntegerField` type. Improved localization --------------------- -Django's :ref:`internationalization framework <topics-i18n>` has been expanded +Django's :doc:`internationalization framework </topics/i18n/index>` has been expanded with locale-aware formatting and form processing. That means, if enabled, dates and numbers on templates will be displayed using the format specified for the current locale. Django will also use localized formats when parsing data in @@ -309,8 +307,8 @@ the colors used by ``django-admin.py`` to provide :ref:`syntax highlighting Syndication feeds as views -------------------------- -:ref:`Syndication feeds <ref-contrib-syndication>` can now be used directly as -views in your :ref:`URLconf <topics-http-urls>`. This means that you can +:doc:`Syndication feeds </ref/contrib/syndication>` can now be used directly as +views in your :doc:`URLconf </topics/http/urls>`. This means that you can maintain complete control over the URL structure of your feeds. Like any other view, feeds views are passed a ``request`` object, so you can do anything you would normally do with a view, like user based access control, or making a feed @@ -319,7 +317,7 @@ a named URL. GeoDjango --------- -The most significant new feature for :ref:`GeoDjango <ref-contrib-gis>` +The most significant new feature for :doc:`GeoDjango </ref/contrib/gis/index>` in 1.2 is support for multiple spatial databases. As a result, the following :ref:`spatial database backends <spatial-backends>` are now included: @@ -357,7 +355,7 @@ set a :attr:`~django.contrib.gis.gdal.Layer.spatial_filter` on the features returned when iterating over a :class:`~django.contrib.gis.gdal.Layer`. -Finally, :ref:`GeoDjango's documentation <ref-contrib-gis>` is now +Finally, :doc:`GeoDjango's documentation </ref/contrib/gis/index>` is now included with Django's and is no longer hosted separately at `geodjango.org <http://geodjango.org/>`_. @@ -391,8 +389,8 @@ Backwards-incompatible changes in 1.2 ===================================== Wherever possible the new features above have been introduced in a -backwards-compatible manner per :ref:`our API stability policy -<misc-api-stability>` policy. This means that practically all existing +backwards-compatible manner per :doc:`our API stability policy +</misc/api-stability>` policy. This means that practically all existing code which worked with Django 1.1 will continue to work with Django 1.2; such code will, however, begin issuing warnings (see below for details). @@ -405,7 +403,7 @@ CSRF Protection --------------- We've made large changes to the way CSRF protection works, detailed in -:ref:`the CSRF documentaton <ref-contrib-csrf>`. Here are the major changes you +:doc:`the CSRF documentaton </ref/contrib/csrf>`. Here are the major changes you should be aware of: * ``CsrfResponseMiddleware`` and ``CsrfMiddleware`` have been deprecated and @@ -435,6 +433,8 @@ database-compatible values. A custom field might look something like:: class CustomModelField(models.Field): # ... + def db_type(self): + # ... def get_db_prep_save(self, value): # ... @@ -451,6 +451,9 @@ two extra methods have been introduced:: class CustomModelField(models.Field): # ... + def db_type(self, connection): + # ... + def get_prep_value(self, value): # ... @@ -467,10 +470,10 @@ two extra methods have been introduced:: # ... These changes are required to support multiple databases -- -``get_db_prep_*`` can no longer make any assumptions regarding the -database for which it is preparing. The ``connection`` argument now -provides the preparation methods with the specific connection for -which the value is being prepared. +``db_type`` and ``get_db_prep_*`` can no longer make any assumptions +regarding the database for which it is preparing. The ``connection`` +argument now provides the preparation methods with the specific +connection for which the value is being prepared. The two new methods exist to differentiate general data-preparation requirements from requirements that are database-specific. The @@ -603,13 +606,13 @@ new keyword and so is not a valid variable name in this tag. -------------- ``LazyObject`` is an undocumented-but-often-used utility class used for lazily -wrapping other objects of unknown type. +wrapping other objects of unknown type. In Django 1.1 and earlier, it handled introspection in a non-standard way, depending on wrapped objects implementing a public method named ``get_all_members()``. Since this could easily lead to name clashes, it has been changed to use the standard Python introspection method, involving -``__members__`` and ``__dir__()``. +``__members__`` and ``__dir__()``. If you used ``LazyObject`` in your own code and implemented the ``get_all_members()`` method for wrapped objects, you'll need @@ -737,9 +740,9 @@ be removed entirely. .. seealso:: - For more details, see the documentation :ref:`Django's release process - <internals-release-process>` and our :ref:`deprecation timeline - <internals-deprecation>`.` + For more details, see the documentation :doc:`Django's release process + </internals/release-process>` and our :doc:`deprecation timeline + </internals/deprecation>`.` .. _specifying-databases: @@ -872,7 +875,7 @@ User Messages API The API for storing messages in the user ``Message`` model (via ``user.message_set.create``) is now deprecated and will be removed in Django -1.4 according to the standard :ref:`release process <internals-release-process>`. +1.4 according to the standard :doc:`release process </internals/release-process>`. To upgrade your code, you need to replace any instances of this:: @@ -896,7 +899,7 @@ following:: ... For more information, see the full -:ref:`messages documentation <ref-contrib-messages>`. You should begin to +:doc:`messages documentation </ref/contrib/messages>`. You should begin to update your code to use the new API immediately. Date format helper functions @@ -960,7 +963,7 @@ Django 1.4. The new class has an almost identical API, but allows instances to be used as views. For example, consider the use of the old framework in -the following :ref:`URLconf <topics-http-urls>`:: +the following :doc:`URLconf </topics/http/urls>`:: from django.conf.urls.defaults import * from myproject.feeds import LatestEntries, LatestEntriesByCategory @@ -1029,8 +1032,8 @@ In accordance with `RSS best practices`_, RSS feeds will now include an ``atom:link`` element. You may need to update your tests to take this into account. -For more information, see the full :ref:`syndication framework -documentation <ref-contrib-syndication>`. +For more information, see the full :doc:`syndication framework +documentation </ref/contrib/syndication>`. .. _RSS best practices: http://www.rssboard.org/rss-profile diff --git a/docs/releases/1.3.txt b/docs/releases/1.3.txt new file mode 100644 index 0000000000..51de6fdd92 --- /dev/null +++ b/docs/releases/1.3.txt @@ -0,0 +1,86 @@ +============================================ +Django 1.3 release notes - UNDER DEVELOPMENT +============================================ + +This page documents release notes for the as-yet-unreleased Django +1.3. As such, it's tentative and subject to change. It provides +up-to-date information for those who are following trunk. + +Django 1.3 includes a number of nifty `new features`_, lots of bug +fixes and an easy upgrade path from Django 1.2. + +.. _new features: `What's new in Django 1.3`_ + +.. _backwards-incompatible-changes-1.3: + +Backwards-incompatible changes in 1.3 +===================================== + +PasswordInput default rendering behavior +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Prior to Django 1.3, a :class:`~django.forms.PasswordInput` would render +data values like any other form. If a form submission raised an error, +the password that was submitted would be reflected to the client as form +data populating the form for resubmission. + +This had the potential to leak passwords, as any failed password +attempt would cause the password that was typed to be sent back to the +client. + +In Django 1.3, the default behavior of +:class:`~django.forms.PasswordInput` is to suppress the display of +password values. This change doesn't alter the way form data is +validated or handled. It only affects the user experience with +passwords on a form when they make an error submitting form data (such +as on unsuccessful logins, or when completing a registration form). + +If you want restore the pre-Django 1.3 behavior, you need to pass in a +custom widget to your form that sets the ``render_value`` argument:: + + class LoginForm(forms.Form): + username = forms.CharField(max_length=100) + password = forms.PasswordField(widget=forms.PasswordInput(render_value=True)) + +.. _deprecated-features-1.3: + +Features deprecated in 1.3 +========================== + +Django 1.3 deprecates some features from earlier releases. +These features are still supported, but will be gradually phased out +over the next few release cycles. + +Code taking advantage of any of the features below will raise a +``PendingDeprecationWarning`` in Django 1.3. This warning will be +silent by default, but may be turned on using Python's `warnings +module`_, or by running Python with a ``-Wd`` or `-Wall` flag. + +.. _warnings module: http://docs.python.org/library/warnings.html + +In Django 1.4, these warnings will become a ``DeprecationWarning``, +which is *not* silent. In Django 1.5 support for these features will +be removed entirely. + +.. seealso:: + + For more details, see the documentation :doc:`Django's release process + </internals/release-process>` and our :doc:`deprecation timeline + </internals/deprecation>`.` + +``mod_python`` support +~~~~~~~~~~~~~~~~~~~~~~ + +The ``mod_python`` library has not had a release since 2007 or a commit since +2008. The Apache Foundation board voted to remove ``mod_python`` from the set +of active projects in its version control repositories, and its lead developer +has shifted all of his efforts toward the lighter, slimmer, more stable, and +more flexible ``mod_wsgi`` backend. + +If you are currently using the ``mod_python`` request handler, it is strongly +encouraged you redeploy your Django instances using :doc:`mod_wsgi +</howto/deployment/modwsgi>`. + +What's new in Django 1.3 +======================== + diff --git a/docs/releases/index.txt b/docs/releases/index.txt index 676ee67519..aa014f3cf2 100644 --- a/docs/releases/index.txt +++ b/docs/releases/index.txt @@ -1,5 +1,3 @@ -.. _releases-index: - ============= Release notes ============= @@ -16,6 +14,13 @@ up to and including the new version. Final releases ============== +1.3 release +----------- +.. toctree:: + :maxdepth: 1 + + 1.3 + 1.2 release ----------- .. toctree:: diff --git a/docs/topics/auth.txt b/docs/topics/auth.txt index 4e07f73190..8c9b0f1cff 100644 --- a/docs/topics/auth.txt +++ b/docs/topics/auth.txt @@ -1,5 +1,3 @@ -.. _topics-auth: - ============================= User authentication in Django ============================= @@ -138,8 +136,8 @@ Methods :class:`~django.contrib.auth.models.User` objects have two many-to-many fields: models.User. ``groups`` and ``user_permissions``. :class:`~django.contrib.auth.models.User` objects can access their related - objects in the same way as any other :ref:`Django model - <topics-db-models>`: + objects in the same way as any other :doc:`Django model + </topics/db/models>`: .. code-block:: python @@ -435,8 +433,6 @@ Anonymous users instead of ``False``. * :meth:`~django.contrib.auth.models.User.is_authenticated()` returns ``False`` instead of ``True``. - * :meth:`~django.contrib.auth.models.User.has_perm()` always returns - ``False``. * :meth:`~django.contrib.auth.models.User.set_password()`, :meth:`~django.contrib.auth.models.User.check_password()`, :meth:`~django.contrib.auth.models.User.save()`, @@ -539,7 +535,7 @@ First, install the :class:`~django.contrib.sessions.middleware.SessionMiddleware` and :class:`~django.contrib.auth.middleware.AuthenticationMiddleware` middlewares by adding them to your :setting:`MIDDLEWARE_CLASSES` setting. See -the :ref:`session documentation <topics-http-sessions>` for more information. +the :doc:`session documentation </topics/http/sessions>` for more information. Once you have those middlewares installed, you'll be able to access :attr:`request.user <django.http.HttpRequest.user>` in views. @@ -556,7 +552,7 @@ section). You can tell them apart with else: # Do something for anonymous users. -.. _howtologauserin: +.. _how-to-log-a-user-in: How to log a user in -------------------- @@ -717,6 +713,17 @@ The login_required decorator def my_view(request): ... + .. versionadded:: 1.3 + + :func:`~django.contrib.auth.decorators.login_required` also takes an + optional ``login_url`` parameter. Example:: + + from django.contrib.auth.decorators import login_required + + @login_required(login_url='/accounts/login/') + def my_view(request): + ... + :func:`~django.contrib.auth.decorators.login_required` does the following: * If the user isn't logged in, redirect to @@ -730,9 +737,9 @@ The login_required decorator * If the user is logged in, execute the view normally. The view code is free to assume the user is logged in. -Note that you'll need to map the appropriate Django view to -:setting:`settings.LOGIN_URL <LOGIN_URL>`. For example, using the defaults, add -the following line to your URLconf:: +Note that if you don't specify the ``login_url`` parameter, you'll need to map +the appropriate Django view to :setting:`settings.LOGIN_URL <LOGIN_URL>`. For +example, using the defaults, add the following line to your URLconf:: (r'^accounts/login/$', 'django.contrib.auth.views.login'), @@ -755,7 +762,7 @@ the following line to your URLconf:: template context variables: * ``form``: A :class:`~django.forms.Form` object representing the login - form. See the :ref:`forms documentation <topics-forms-index>` for + form. See the :doc:`forms documentation </topics/forms/index>` for more on ``Form`` objects. * ``next``: The URL to redirect to after successful login. This may @@ -771,7 +778,7 @@ the following line to your URLconf:: * ``site_name``: An alias for ``site.name``. If you don't have the site framework installed, this will be set to the value of :attr:`request.META['SERVER_NAME'] <django.http.HttpRequest.META>`. - For more on sites, see :ref:`ref-contrib-sites`. + For more on sites, see :doc:`/ref/contrib/sites`. If you'd prefer not to call the template :file:`registration/login.html`, you can pass the ``template_name`` parameter via the extra arguments to @@ -798,7 +805,8 @@ the following line to your URLconf:: <p>Your username and password didn't match. Please try again.</p> {% endif %} - <form method="post" action="{% url django.contrib.auth.views.login %}">{% csrf_token %} + <form method="post" action="{% url django.contrib.auth.views.login %}"> + {% csrf_token %} <table> <tr> <td>{{ form.username.label_tag }}</td> @@ -898,10 +906,11 @@ includes a few other useful built-in views located in default to :file:`registration/password_change_done.html` if not supplied. -.. function:: views.password_reset(request[, is_admin_site, template_name, email_template_name, password_reset_form, token_generator, post_reset_redirect]) +.. function:: views.password_reset(request[, is_admin_site, template_name, email_template_name, password_reset_form, token_generator, post_reset_redirect, from_email]) - Allows a user to reset their password, and sends them the new password - in an e-mail. + Allows a user to reset their password by generating a one-time use link + that can be used to reset the password, and sending that link to the + user's registered e-mail address. **Optional arguments:** @@ -923,6 +932,11 @@ includes a few other useful built-in views located in * ``post_reset_redirect``: The URL to redirect to after a successful password change. + .. versionchanged:: 1.3 + + * ``from_email``: A valid e-mail address. By default Django uses + the :setting:`DEFAULT_FROM_EMAIL`. + **Template context:** * ``form``: The form for resetting the user's password. @@ -1007,8 +1021,8 @@ provides several built-in forms located in :mod:`django.contrib.auth.forms`: .. class:: PasswordResetForm - A form for resetting a user's password and e-mailing the new password to - them. + A form for generating and e-mailing a one-time use link to reset a + user's password. .. class:: SetPasswordForm @@ -1112,7 +1126,7 @@ The permission_required decorator Limiting access to generic views -------------------------------- -To limit access to a :ref:`generic view <ref-generic-views>`, write a thin +To limit access to a :doc:`generic view </ref/generic-views>`, write a thin wrapper around the view, and point your URLconf to your wrapper instead of the generic view itself. For example:: @@ -1229,13 +1243,13 @@ Methods ~~~~~~~ :class:`~django.contrib.auth.models.Permission` objects have the standard -data-access methods like any other :ref:`Django model <ref-models-instances>`. +data-access methods like any other :doc:`Django model </ref/models/instances>`. Authentication data in templates ================================ The currently logged-in user and his/her permissions are made available in the -:ref:`template context <ref-templates-api>` when you use +:doc:`template context </ref/templates/api>` when you use :class:`~django.template.context.RequestContext`. .. admonition:: Technicality @@ -1324,7 +1338,7 @@ Messages .. deprecated:: 1.2 This functionality will be removed in Django 1.4. You should use the - :ref:`messages framework <ref-contrib-messages>` for all new projects and + :doc:`messages framework </ref/contrib/messages>` for all new projects and begin to update your existing code immediately. The message system is a lightweight way to queue messages for given users. @@ -1359,7 +1373,7 @@ a playlist:: When you use :class:`~django.template.context.RequestContext`, the currently logged-in user and his/her messages are made available in the -:ref:`template context <ref-templates-api>` as the template variable +:doc:`template context </ref/templates/api>` as the template variable ``{{ messages }}``. Here's an example of template code that displays messages: .. code-block:: html+django @@ -1374,14 +1388,14 @@ logged-in user and his/her messages are made available in the .. versionchanged:: 1.2 The ``messages`` template variable uses a backwards compatible method in the - :ref:`messages framework <ref-contrib-messages>` to retrieve messages from + :doc:`messages framework </ref/contrib/messages>` to retrieve messages from both the user ``Message`` model and from the new framework. Unlike in previous revisions, the messages will not be erased unless they are actually displayed. Finally, note that this messages framework only works with users in the user database. To send messages to anonymous users, use the -:ref:`messages framework <ref-contrib-messages>`. +:doc:`messages framework </ref/contrib/messages>`. .. _authentication-backends: @@ -1402,7 +1416,7 @@ plug in other authentication sources. You can override Django's default database-based scheme, or you can use the default system in tandem with other systems. -See the :ref:`authentication backend reference <ref-authentication-backends>` +See the :doc:`authentication backend reference </ref/authbackends>` for information on the authentication backends included with Django. Specifying authentication backends @@ -1411,9 +1425,9 @@ Specifying authentication backends Behind the scenes, Django maintains a list of "authentication backends" that it checks for authentication. When somebody calls :func:`django.contrib.auth.authenticate()` -- as described in :ref:`How to log -a user in` above -- Django tries authenticating across all of its -authentication backends. If the first authentication method fails, Django tries -the second one, and so on, until all backends have been attempted. +a user in <how-to-log-a-user-in>` above -- Django tries authenticating across +all of its authentication backends. If the first authentication method fails, +Django tries the second one, and so on, until all backends have been attempted. The list of authentication backends to use is specified in the :setting:`AUTHENTICATION_BACKENDS` setting. This should be a tuple of Python @@ -1593,7 +1607,7 @@ object permissions will always return ``False`` or an empty list (depending on the check performed). To enable object permissions in your own -:ref:`authentication backend <ref-authentication-backends>` you'll just have +:doc:`authentication backend </ref/authbackends>` you'll just have to allow passing an ``obj`` parameter to the permission methods and set the ``supports_object_permissions`` class attribute to ``True``. diff --git a/docs/topics/cache.txt b/docs/topics/cache.txt index 9dedbcf3b9..5797199411 100644 --- a/docs/topics/cache.txt +++ b/docs/topics/cache.txt @@ -1,5 +1,3 @@ -.. _topics-cache: - ======================== Django's cache framework ======================== @@ -136,6 +134,49 @@ settings file. You can't use a different database backend for your cache table. Database caching works best if you've got a fast, well-indexed database server. +Database caching and multiple databases +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you use database caching with multiple databases, you'll also need +to set up routing instructions for your database cache table. For the +purposes of routing, the database cache table appears as a model named +``CacheEntry``, in an application named ``django_cache``. This model +won't appear in the models cache, but the model details can be used +for routing purposes. + +For example, the following router would direct all cache read +operations to ``cache_slave``, and all write operations to +``cache_master``. The cache table will only be synchronized onto +``cache_master``:: + + class CacheRouter(object): + """A router to control all database cache operations""" + + def db_for_read(self, model, **hints): + "All cache read operations go to the slave" + if model._meta.app_label in ('django_cache',): + return 'cache_slave' + return None + + def db_for_write(self, model, **hints): + "All cache write operations go to master" + if model._meta.app_label in ('django_cache',): + return 'cache_master' + return None + + def allow_syncdb(self, db, model): + "Only synchronize the cache model on master" + if model._meta.app_label in ('django_cache',): + return db == 'cache_master' + return None + +If you don't specify routing directions for the database cache model, +the cache backend will use the ``default`` database. + +Of course, if you don't use the database cache backend, you don't need +to worry about providing routing instructions for the database cache +model. + Filesystem caching ------------------ @@ -301,7 +342,7 @@ Additionally, the cache middleware automatically sets a few headers in each * Sets the ``Cache-Control`` header to give a max age for the page -- again, from the ``CACHE_MIDDLEWARE_SECONDS`` setting. -See :ref:`topics-http-middleware` for more on middleware. +See :doc:`/topics/http/middleware` for more on middleware. .. versionadded:: 1.0 @@ -322,7 +363,7 @@ include the name of the active :term:`language<language code>`. This allows you to easily cache multilingual sites without having to create the cache key yourself. -See :ref:`topics-i18n-deployment` for more on how Django discovers the active +See :doc:`/topics/i18n/deployment` for more on how Django discovers the active language. __ `Controlling cache: Using other headers`_ @@ -600,6 +641,45 @@ nonexistent cache key.:: However, if the backend doesn't natively provide an increment/decrement operation, it will be implemented using a two-step retrieve/update. +Cache key warnings +------------------ + +.. versionadded:: 1.3 + +Memcached, the most commonly-used production cache backend, does not allow +cache keys longer than 250 characters or containing whitespace or control +characters, and using such keys will cause an exception. To encourage +cache-portable code and minimize unpleasant surprises, the other built-in cache +backends issue a warning (``django.core.cache.backends.base.CacheKeyWarning``) +if a key is used that would cause an error on memcached. + +If you are using a production backend that can accept a wider range of keys (a +custom backend, or one of the non-memcached built-in backends), and want to use +this wider range without warnings, you can silence ``CacheKeyWarning`` with +this code in the ``management`` module of one of your +:setting:`INSTALLED_APPS`:: + + import warnings + + from django.core.cache import CacheKeyWarning + + warnings.simplefilter("ignore", CacheKeyWarning) + +If you want to instead provide custom key validation logic for one of the +built-in backends, you can subclass it, override just the ``validate_key`` +method, and follow the instructions for `using a custom cache backend`_. For +instance, to do this for the ``locmem`` backend, put this code in a module:: + + from django.core.cache.backends.locmem import CacheClass as LocMemCacheClass + + class CacheClass(LocMemCacheClass): + def validate_key(self, key): + """Custom validation, raising exceptions or warnings as needed.""" + # ... + +...and use the dotted Python path to this module as the scheme portion of your +:setting:`CACHE_BACKEND`. + Upstream caches =============== diff --git a/docs/topics/conditional-view-processing.txt b/docs/topics/conditional-view-processing.txt index 1ce3c3bb92..b5da140827 100644 --- a/docs/topics/conditional-view-processing.txt +++ b/docs/topics/conditional-view-processing.txt @@ -1,5 +1,3 @@ -.. _topics-conditional-processing: - =========================== Conditional View Processing =========================== diff --git a/docs/topics/db/aggregation.txt b/docs/topics/db/aggregation.txt index 087c1bf239..eb21021038 100644 --- a/docs/topics/db/aggregation.txt +++ b/docs/topics/db/aggregation.txt @@ -1,5 +1,3 @@ -.. _topics-db-aggregation: - =========== Aggregation =========== @@ -8,7 +6,7 @@ Aggregation .. currentmodule:: django.db.models -The topic guide on :ref:`Django's database-abstraction API <topics-db-queries>` +The topic guide on :doc:`Django's database-abstraction API </topics/db/queries>` described the way that you can use Django queries that create, retrieve, update and delete individual objects. However, sometimes you will need to retrieve values that are derived by summarizing or *aggregating* a @@ -353,7 +351,7 @@ without any harmful effects, since that is already playing a role in the query. This behavior is the same as that noted in the queryset documentation for -:ref:`distinct() <queryset-distinct>` and the general rule is the same: +:meth:`~django.db.models.QuerySet.distinct` and the general rule is the same: normally you won't want extra columns playing a part in the result, so clear out the ordering, or at least make sure it's restricted only to those fields you also select in a ``values()`` call. @@ -363,7 +361,7 @@ you also select in a ``values()`` call. for you. The main reason is consistency with ``distinct()`` and other places: Django **never** removes ordering constraints that you have specified (and we can't change those other methods' behavior, as that - would violate our :ref:`misc-api-stability` policy). + would violate our :doc:`/misc/api-stability` policy). Aggregating annotations ----------------------- diff --git a/docs/topics/db/index.txt b/docs/topics/db/index.txt index 3eb62b70ca..c49f158e64 100644 --- a/docs/topics/db/index.txt +++ b/docs/topics/db/index.txt @@ -1,5 +1,3 @@ -.. _topics-db-index: - Models and databases ==================== diff --git a/docs/topics/db/managers.txt b/docs/topics/db/managers.txt index 123408b2bb..5ebe0b1b94 100644 --- a/docs/topics/db/managers.txt +++ b/docs/topics/db/managers.txt @@ -1,5 +1,3 @@ -.. _topics-db-managers: - ======== Managers ======== @@ -12,7 +10,7 @@ A ``Manager`` is the interface through which database query operations are provided to Django models. At least one ``Manager`` exists for every model in a Django application. -The way ``Manager`` classes work is documented in :ref:`topics-db-queries`; +The way ``Manager`` classes work is documented in :doc:`/topics/db/queries`; this document specifically touches on model options that customize ``Manager`` behavior. @@ -170,7 +168,8 @@ and ``Person.people.all()``, yielding predictable results. If you use custom ``Manager`` objects, take note that the first ``Manager`` Django encounters (in the order in which they're defined in the model) has a special status. Django interprets the first ``Manager`` defined in a class as -the "default" ``Manager``, and several parts of Django will use that ``Manager`` +the "default" ``Manager``, and several parts of Django +(including :djadmin:`dumpdata`) will use that ``Manager`` exclusively for that model. As a result, it's a good idea to be careful in your choice of default manager in order to avoid a situation where overriding ``get_query_set()`` results in an inability to retrieve objects you'd like to @@ -324,7 +323,7 @@ it will use :class:`django.db.models.Manager`. attribute only controlled the type of manager used for related field access, which is where the name came from. As it became clear the concept was more broadly useful, the name hasn't been changed. This is primarily - so that existing code will :ref:`continue to work <misc-api-stability>` in + so that existing code will :doc:`continue to work </misc/api-stability>` in future Django versions. Writing Correct Managers For Use In Automatic Manager Instances diff --git a/docs/topics/db/models.txt b/docs/topics/db/models.txt index 6d7a7a4374..0ff34ea0e1 100644 --- a/docs/topics/db/models.txt +++ b/docs/topics/db/models.txt @@ -1,5 +1,3 @@ -.. _topics-db-models: - ====== Models ====== @@ -18,7 +16,7 @@ The basics: * Each attribute of the model represents a database field. * With all of this, Django gives you an automatically-generated - database-access API; see :ref:`topics-db-queries`. + database-access API; see :doc:`/topics/db/queries`. .. seealso:: @@ -64,7 +62,7 @@ Some technical notes: * The ``CREATE TABLE`` SQL in this example is formatted using PostgreSQL syntax, but it's worth noting Django uses SQL tailored to the database - backend specified in your :ref:`settings file <topics-settings>`. + backend specified in your :doc:`settings file </topics/settings>`. Using models ============ @@ -126,7 +124,7 @@ determine a few things: Django ships with dozens of built-in field types; you can find the complete list in the :ref:`model field reference <model-field-types>`. You can easily write your own fields if Django's built-in ones don't do the trick; see -:ref:`howto-custom-model-fields`. +:doc:`/howto/custom-model-fields`. Field options ------------- @@ -353,7 +351,7 @@ For example, if a ``Pizza`` has multiple ``Topping`` objects -- that is, a As with :class:`~django.db.models.ForeignKey`, you can also create :ref:`recursive relationships <recursive-relationships>` (an object with a -many-to-one relationship to itself) and :ref:`relationships to models not yet +many-to-many relationship to itself) and :ref:`relationships to models not yet defined <lazy-relationships>`; see :ref:`the model field reference <ref-manytomany>` for details. @@ -612,7 +610,7 @@ Custom field types If one of the existing model fields cannot be used to fit your purposes, or if you wish to take advantage of some less common database column types, you can create your own field class. Full coverage of creating your own fields is -provided in :ref:`howto-custom-model-fields`. +provided in :doc:`/howto/custom-model-fields`. .. _meta-options: @@ -634,8 +632,8 @@ human-readable singular and plural names (:attr:`~Options.verbose_name` and :attr:`~Options.verbose_name_plural`). None are required, and adding ``class Meta`` to a model is completely optional. -A complete list of all possible ``Meta`` options can be found in the :ref:`model -option reference <ref-models-options>`. +A complete list of all possible ``Meta`` options can be found in the :doc:`model +option reference </ref/models/options>`. .. _model-methods: @@ -684,7 +682,7 @@ properties`_. .. _Read more about properties: http://www.python.org/download/releases/2.2/descrintro/#property -The :ref:`model instance reference <ref-models-instances>` has a complete list +The :doc:`model instance reference </ref/models/instances>` has a complete list of :ref:`methods automatically given to each model <model-instance-methods>`. You can override most of these -- see `overriding predefined model methods`_, below -- but there are a couple that you'll almost always want to define: @@ -763,7 +761,7 @@ Executing custom SQL Another common pattern is writing custom SQL statements in model methods and module-level methods. For more details on using raw SQL, see the documentation -on :ref:`using raw SQL<topics-db-sql>`. +on :doc:`using raw SQL</topics/db/sql>`. .. _model-inheritance: diff --git a/docs/topics/db/multi-db.txt b/docs/topics/db/multi-db.txt index e3f62ea71b..1a939b0e3a 100644 --- a/docs/topics/db/multi-db.txt +++ b/docs/topics/db/multi-db.txt @@ -1,5 +1,3 @@ -.. _topics-db-multi-db: - ================== Multiple databases ================== @@ -240,7 +238,7 @@ master/slave relationship between the databases ``master``, ``slave1`` and return False return None - class MasterSlaveRouter(object): + class MasterSlaveRouter(object): """A router that sets up a simple master/slave configuration""" def db_for_read(self, model, **hints): diff --git a/docs/topics/db/optimization.txt b/docs/topics/db/optimization.txt index 6063bc6c2a..baf8cfa268 100644 --- a/docs/topics/db/optimization.txt +++ b/docs/topics/db/optimization.txt @@ -1,11 +1,9 @@ -.. _topics-db-optimization: - ============================ Database access optimization ============================ Django's database layer provides various ways to help developers get the most -out of their databases. This documents gathers together links to the relevant +out of their databases. This document gathers together links to the relevant documentation, and adds various tips, organized under an number of headings that outline the steps to take when attempting to optimize your database usage. @@ -45,13 +43,13 @@ Use standard DB optimization techniques We will assume you have done the obvious things above. The rest of this document focuses on how to use Django in such a way that you are not doing unnecessary work. This document also does not address other optimization techniques that -apply to all expensive operations, such as :ref:`general purpose caching -<topics-cache>`. +apply to all expensive operations, such as :doc:`general purpose caching +</topics/cache>`. Understand QuerySets ==================== -Understanding :ref:`QuerySets <ref-models-querysets>` is vital to getting good +Understanding :doc:`QuerySets </ref/models/querysets>` is vital to getting good performance with simple code. In particular: Understand QuerySet evaluation @@ -101,36 +99,35 @@ Use ``iterator()`` When you have a lot of objects, the caching behaviour of the ``QuerySet`` can cause a large amount of memory to be used. In this case, -:ref:`QuerySet.iterator() <queryset-iterator>` may help. +:meth:`~django.db.models.QuerySet.iterator()` may help. Do database work in the database rather than in Python ====================================================== For instance: -* At the most basic level, use :ref:`filter and exclude <queryset-api>` to - filtering in the database to avoid loading data into your Python process, only - to throw much of it away. +* At the most basic level, use :ref:`filter and exclude <queryset-api>` to do + filtering in the database. * Use :ref:`F() object query expressions <query-expressions>` to do filtering against other fields within the same model. -* Use :ref:`annotate to do aggregation in the database <topics-db-aggregation>`. +* Use :doc:`annotate to do aggregation in the database </topics/db/aggregation>`. If these aren't enough to generate the SQL you need: Use ``QuerySet.extra()`` ------------------------ -A less portable but more powerful method is :ref:`QuerySet.extra() -<queryset-extra>`, which allows some SQL to be explicitly added to the query. -If that still isn't powerful enough: +A less portable but more powerful method is +:meth:`~django.db.models.QuerySet.extra()`, which allows some SQL to be +explicitly added to the query. If that still isn't powerful enough: Use raw SQL ----------- -Write your own :ref:`custom SQL to retrieve data or populate models -<topics-db-sql>`. Use ``django.db.connection.queries`` to find out what Django +Write your own :doc:`custom SQL to retrieve data or populate models +</topics/db/sql>`. Use ``django.db.connection.queries`` to find out what Django is writing for you and start from there. Retrieve everything at once if you know you will need it @@ -149,7 +146,7 @@ Understand :ref:`QuerySet.select_related() <select-related>` thoroughly, and use * in view code, -* and in :ref:`managers and default managers <topics-db-managers>` where +* and in :doc:`managers and default managers </topics/db/managers>` where appropriate. Be aware when your manager is and is not used; sometimes this is tricky so don't make assumptions. @@ -160,7 +157,7 @@ Use ``QuerySet.values()`` and ``values_list()`` ----------------------------------------------- When you just want a dict/list of values, and don't need ORM model objects, make -appropriate usage of :ref:`QuerySet.values() <queryset-values>`. +appropriate usage of :meth:`~django.db.models.QuerySet.values()`. These can be useful for replacing model objects in template code - as long as the dicts you supply have the same attributes as those used in the template, you are fine. @@ -168,7 +165,8 @@ are fine. Use ``QuerySet.defer()`` and ``only()`` --------------------------------------- -Use :ref:`defer() and only() <queryset-defer>` if there are database columns you +Use :meth:`~django.db.models.QuerySet.defer()` and +:meth:`~django.db.models.QuerySet.only()` if there are database columns you know that you won't need (or won't need in most cases) to avoid loading them. Note that if you *do* use them, the ORM will have to go and get them in a separate query, making this a pessimization if you use it inappropriately. @@ -243,10 +241,7 @@ individual, use a bulk SQL UPDATE statement, via :ref:`QuerySet.update() Note, however, that these bulk update methods cannot call the ``save()`` or ``delete()`` methods of individual instances, which means that any custom behaviour you have added for these methods will not be executed, including anything driven from the -normal database object :ref:`signals <ref-signals>`. - -Don't retrieve things you already have -====================================== +normal database object :doc:`signals </ref/signals>`. Use foreign key values directly ------------------------------- diff --git a/docs/topics/db/queries.txt b/docs/topics/db/queries.txt index 981d727f4f..b0d053c2e9 100644 --- a/docs/topics/db/queries.txt +++ b/docs/topics/db/queries.txt @@ -1,15 +1,13 @@ -.. _topics-db-queries: - ============== Making queries ============== .. currentmodule:: django.db.models -Once you've created your :ref:`data models <topics-db-models>`, Django +Once you've created your :doc:`data models </topics/db/models>`, Django automatically gives you a database-abstraction API that lets you create, retrieve, update and delete objects. This document explains how to use this -API. Refer to the :ref:`data model reference <ref-models-index>` for full +API. Refer to the :doc:`data model reference </ref/models/index>` for full details of all the various model lookup options. Throughout this guide (and in the reference), we'll refer to the following @@ -825,6 +823,8 @@ a join with an ``F()`` object, a ``FieldError`` will be raised:: # THIS WILL RAISE A FieldError >>> Entry.objects.update(headline=F('blog__name')) +.. _topics-db-queries-related: + Related objects =============== @@ -937,7 +937,7 @@ be accessed from an instance:: In addition to the ``QuerySet`` methods defined in "Retrieving objects" above, the ``ForeignKey`` ``Manager`` has additional methods used to handle the set of related objects. A synopsis of each is below, and complete details can be found -in the :ref:`related objects reference <ref-models-relations>`. +in the :doc:`related objects reference </ref/models/relations>`. ``add(obj1, obj2, ...)`` Adds the specified model objects to the related object set. @@ -1067,7 +1067,7 @@ Falling back to raw SQL If you find yourself needing to write an SQL query that is too complex for Django's database-mapper to handle, you can fall back on writing SQL by hand. Django has a couple of options for writing raw SQL queries; see -:ref:`topics-db-sql`. +:doc:`/topics/db/sql`. Finally, it's important to note that the Django database layer is merely an interface to your database. You can access your database via other tools, diff --git a/docs/topics/db/sql.txt b/docs/topics/db/sql.txt index f55a164373..9f2be7a2ef 100644 --- a/docs/topics/db/sql.txt +++ b/docs/topics/db/sql.txt @@ -1,12 +1,10 @@ -.. _topics-db-sql: - ========================== Performing raw SQL queries ========================== .. currentmodule:: django.db.models -When the :ref:`model query APIs <topics-db-queries>` don't go far enough, you +When the :doc:`model query APIs </topics/db/queries>` don't go far enough, you can fall back to writing raw SQL. Django gives you two ways of performing raw SQL queries: you can use :meth:`Manager.raw()` to `perform raw queries and return model instances`__, or you can avoid the model layer entirely and @@ -116,9 +114,9 @@ Fields may also be left out:: >>> people = Person.objects.raw('SELECT id, first_name FROM myapp_person') -The ``Person`` objects returned by this query will be :ref:`deferred -<queryset-defer>` model instances. This means that the fields that are omitted -from the query will be loaded on demand. For example:: +The ``Person`` objects returned by this query will be deferred model instances +(see :meth:`~django.db.models.QuerySet.defer()`). This means that the fields +that are omitted from the query will be loaded on demand. For example:: >>> for p in Person.objects.raw('SELECT id, first_name FROM myapp_person'): ... print p.first_name, # This will be retrieved by the original query @@ -269,7 +267,7 @@ Connections and cursors ----------------------- ``connection`` and ``cursor`` mostly implement the standard `Python DB-API`_ -(except when it comes to :ref:`transaction handling <topics-db-transactions>`). +(except when it comes to :doc:`transaction handling </topics/db/transactions>`). If you're not familiar with the Python DB-API, note that the SQL statement in ``cursor.execute()`` uses placeholders, ``"%s"``, rather than adding parameters directly within the SQL. If you use this technique, the underlying database diff --git a/docs/topics/db/transactions.txt b/docs/topics/db/transactions.txt index fb6e3f8646..2d99c17a32 100644 --- a/docs/topics/db/transactions.txt +++ b/docs/topics/db/transactions.txt @@ -1,5 +1,3 @@ -.. _topics-db-transactions: - ============================== Managing database transactions ============================== @@ -306,7 +304,7 @@ Database-level autocommit .. versionadded:: 1.1 With PostgreSQL 8.2 or later, there is an advanced option to run PostgreSQL -with :ref:`database-level autocommit <ref-databases>`. If you use this option, +with :doc:`database-level autocommit </ref/databases>`. If you use this option, there is no constantly open transaction, so it is always possible to continue after catching an exception. For example:: diff --git a/docs/topics/email.txt b/docs/topics/email.txt index 74e153de61..31092b0aaa 100644 --- a/docs/topics/email.txt +++ b/docs/topics/email.txt @@ -1,5 +1,3 @@ -.. _topics-email: - ============== Sending e-mail ============== @@ -501,7 +499,7 @@ convenience that can be used during development. Defining a custom e-mail backend -------------------------------- -If you need to change how e-mails are send you can write your own e-mail +If you need to change how e-mails are sent you can write your own e-mail backend. The ``EMAIL_BACKEND`` setting in your settings file is then the Python import path for your backend class. diff --git a/docs/topics/files.txt b/docs/topics/files.txt index 45aca5488a..26114cb50b 100644 --- a/docs/topics/files.txt +++ b/docs/topics/files.txt @@ -1,5 +1,3 @@ -.. _topics-files: - ============== Managing files ============== @@ -70,7 +68,7 @@ using a Python built-in ``file`` object:: >>> myfile = File(f) Now you can use any of the ``File`` attributes and methods documented in -:ref:`ref-files-file`. +:doc:`/ref/files/file`. File storage ============ @@ -84,7 +82,7 @@ setting; if you don't explicitly provide a storage system, this is the one that will be used. See below for details of the built-in default file storage system, and see -:ref:`howto-custom-file-storage` for information on writing your own file +:doc:`/howto/custom-file-storage` for information on writing your own file storage system. Storage objects @@ -111,7 +109,7 @@ useful -- you can use the global default storage system:: >>> default_storage.exists(path) False -See :ref:`ref-files-storage` for the file storage API. +See :doc:`/ref/files/storage` for the file storage API. The built-in filesystem storage class ------------------------------------- @@ -143,5 +141,5 @@ For example, the following code will store uploaded files under ... photo = models.ImageField(storage=fs) -:ref:`Custom storage systems <howto-custom-file-storage>` work the same way: you +:doc:`Custom storage systems </howto/custom-file-storage>` work the same way: you can pass them in as the ``storage`` argument to a ``FileField``. diff --git a/docs/topics/forms/formsets.txt b/docs/topics/forms/formsets.txt index 732fd93de1..e7b09dc409 100644 --- a/docs/topics/forms/formsets.txt +++ b/docs/topics/forms/formsets.txt @@ -1,4 +1,3 @@ -.. _topics-forms-formsets: .. _formsets: Formsets diff --git a/docs/topics/forms/index.txt b/docs/topics/forms/index.txt index 119e943889..cef322a02f 100644 --- a/docs/topics/forms/index.txt +++ b/docs/topics/forms/index.txt @@ -1,5 +1,3 @@ -.. _topics-forms-index: - ================== Working with forms ================== @@ -7,8 +5,8 @@ Working with forms .. admonition:: About this document This document provides an introduction to Django's form handling features. - For a more detailed look at the forms API, see :ref:`ref-forms-api`. For - documentation of the available field types, see :ref:`ref-forms-fields`. + For a more detailed look at the forms API, see :doc:`/ref/forms/api`. For + documentation of the available field types, see :doc:`/ref/forms/fields`. .. highlightlang:: html+django @@ -77,10 +75,10 @@ personal Web site: A form is composed of ``Field`` objects. In this case, our form has four fields: ``subject``, ``message``, ``sender`` and ``cc_myself``. ``CharField``, ``EmailField`` and ``BooleanField`` are just three of the available field types; -a full list can be found in :ref:`ref-forms-fields`. +a full list can be found in :doc:`/ref/forms/fields`. If your form is going to be used to directly add or edit a Django model, you can -use a :ref:`ModelForm <topics-forms-modelforms>` to avoid duplicating your model +use a :doc:`ModelForm </topics/forms/modelforms>` to avoid duplicating your model description. Using a form in a view @@ -163,7 +161,7 @@ Extending the above example, here's how the form data could be processed: send_mail(subject, message, sender, recipients) return HttpResponseRedirect('/thanks/') # Redirect after POST -For more on sending e-mail from Django, see :ref:`topics-email`. +For more on sending e-mail from Django, see :doc:`/topics/email`. Displaying a form using a template ---------------------------------- @@ -397,4 +395,4 @@ This covers the basics, but forms can do a whole lot more: .. seealso:: - The :ref:`form API reference <ref-forms-index>`. + The :doc:`form API reference </ref/forms/index>`. diff --git a/docs/topics/forms/media.txt b/docs/topics/forms/media.txt index 663b0d3113..64cf50743d 100644 --- a/docs/topics/forms/media.txt +++ b/docs/topics/forms/media.txt @@ -1,5 +1,3 @@ -.. _topics-forms-media: - Form Media ========== diff --git a/docs/topics/forms/modelforms.txt b/docs/topics/forms/modelforms.txt index fd3edf5104..2cdd2bfa74 100644 --- a/docs/topics/forms/modelforms.txt +++ b/docs/topics/forms/modelforms.txt @@ -1,5 +1,3 @@ -.. _topics-forms-modelforms: - ========================== Creating forms from models ========================== @@ -446,7 +444,7 @@ parameter when declaring the form field:: class Meta: model = Article - See the :ref:`form field documentation <ref-forms-fields>` for more information + See the :doc:`form field documentation </ref/forms/fields>` for more information on fields and their arguments. Changing the order of fields @@ -540,7 +538,7 @@ Interaction with model validation As part of its validation process, ``ModelForm`` will call the ``clean()`` method of each field on your model that has a corresponding field on your form. If you have excluded any model fields, validation will not be run on those -fields. See the :ref:`form validation <ref-forms-validation>` documentation +fields. See the :doc:`form validation </ref/forms/validation>` documentation for more on how field cleaning and validation work. Also, your model's ``clean()`` method will be called before any uniqueness checks are made. See :ref:`Validating objects <validating-objects>` for more information on the @@ -551,7 +549,7 @@ model's ``clean()`` hook. Model formsets ============== -Like :ref:`regular formsets <topics-forms-formsets>`, Django provides a couple +Like :doc:`regular formsets </topics/forms/formsets>`, Django provides a couple of enhanced formset classes that make it easy to work with Django models. Let's reuse the ``Author`` model from above:: @@ -595,8 +593,8 @@ Alternatively, you can create a subclass that sets ``self.queryset`` in class BaseAuthorFormSet(BaseModelFormSet): def __init__(self, *args, **kwargs): - self.queryset = Author.objects.filter(name__startswith='O') super(BaseAuthorFormSet, self).__init__(*args, **kwargs) + self.queryset = Author.objects.filter(name__startswith='O') Then, pass your ``BaseAuthorFormSet`` class to the factory function:: diff --git a/docs/topics/generic-views.txt b/docs/topics/generic-views.txt index f70bb43f38..f90745d451 100644 --- a/docs/topics/generic-views.txt +++ b/docs/topics/generic-views.txt @@ -1,5 +1,3 @@ -.. _topics-generic-views: - ============= Generic views ============= @@ -192,7 +190,7 @@ might look like the following:: That's really all there is to it. All the cool features of generic views come from changing the "info" dictionary passed to the generic view. The -:ref:`generic views reference<ref-generic-views>` documents all the generic +:doc:`generic views reference</ref/generic-views>` documents all the generic views and all their options in detail; the rest of this document will consider some of the common ways you might customize and extend generic views. @@ -315,9 +313,9 @@ Viewing subsets of objects Now let's take a closer look at this ``queryset`` key we've been using all along. Most generic views take one of these ``queryset`` arguments -- it's how -the view knows which set of objects to display (see :ref:`topics-db-queries` for +the view knows which set of objects to display (see :doc:`/topics/db/queries` for more information about ``QuerySet`` objects, and see the -:ref:`generic views reference<ref-generic-views>` for the complete details). +:doc:`generic views reference</ref/generic-views>` for the complete details). To pick a simple example, we might want to order a list of books by publication date, with the most recent first: @@ -365,7 +363,7 @@ We'll deal with this problem in the next section. If you get a 404 when requesting ``/books/acme/``, check to ensure you actually have a Publisher with the name 'ACME Publishing'. Generic views have an ``allow_empty`` parameter for this case. See the - :ref:`generic views reference<ref-generic-views>` for more details. + :doc:`generic views reference</ref/generic-views>` for more details. Complex filtering with wrapper functions ---------------------------------------- diff --git a/docs/topics/http/file-uploads.txt b/docs/topics/http/file-uploads.txt index ab8277599c..6b0a4d5722 100644 --- a/docs/topics/http/file-uploads.txt +++ b/docs/topics/http/file-uploads.txt @@ -1,5 +1,3 @@ -.. _topics-http-file-uploads: - ============ File Uploads ============ @@ -10,8 +8,8 @@ File Uploads When Django handles a file upload, the file data ends up placed in :attr:`request.FILES <django.http.HttpRequest.FILES>` (for more on the -``request`` object see the documentation for :ref:`request and response objects -<ref-request-response>`). This document explains how files are stored on disk +``request`` object see the documentation for :doc:`request and response objects +</ref/request-response>`). This document explains how files are stored on disk and in memory, and how to customize the default behavior. Basic file uploads diff --git a/docs/topics/http/generic-views.txt b/docs/topics/http/generic-views.txt index 5aa2c48ea5..15f895ea78 100644 --- a/docs/topics/http/generic-views.txt +++ b/docs/topics/http/generic-views.txt @@ -1,7 +1,5 @@ -.. _topics-http-generic-views: - ============= Generic views ============= -See :ref:`ref-generic-views`.
\ No newline at end of file +See :doc:`/ref/generic-views`. diff --git a/docs/topics/http/index.txt b/docs/topics/http/index.txt index ae73c2c29b..5ef776dd7b 100644 --- a/docs/topics/http/index.txt +++ b/docs/topics/http/index.txt @@ -1,5 +1,3 @@ -.. _topics-http-index: - Handling HTTP requests ====================== diff --git a/docs/topics/http/middleware.txt b/docs/topics/http/middleware.txt index 215a4ec12c..eee398a3dc 100644 --- a/docs/topics/http/middleware.txt +++ b/docs/topics/http/middleware.txt @@ -1,5 +1,3 @@ -.. _topics-http-middleware: - ========== Middleware ========== @@ -14,8 +12,8 @@ an ``"X-View"`` HTTP header to every response to a ``HEAD`` request. This document explains how middleware works, how you activate middleware, and how to write your own middleware. Django ships with some built-in middleware -you can use right out of the box; they're documented in the :ref:`built-in -middleware reference <ref-middleware>`. +you can use right out of the box; they're documented in the :doc:`built-in +middleware reference </ref/middleware>`. Activating middleware ===================== @@ -173,9 +171,9 @@ Guidelines cares about is that the :setting:`MIDDLEWARE_CLASSES` setting includes the path to it. - * Feel free to look at :ref:`Django's available middleware - <ref-middleware>` for examples. + * Feel free to look at :doc:`Django's available middleware + </ref/middleware>` for examples. * If you write a middleware component that you think would be useful to - other people, contribute to the community! :ref:`Let us know - <internals-contributing>`, and we'll consider adding it to Django. + other people, contribute to the community! :doc:`Let us know + </internals/contributing>`, and we'll consider adding it to Django. diff --git a/docs/topics/http/sessions.txt b/docs/topics/http/sessions.txt index 68ead03b65..8a0f0d4b72 100644 --- a/docs/topics/http/sessions.txt +++ b/docs/topics/http/sessions.txt @@ -1,5 +1,3 @@ -.. _topics-http-sessions: - =================== How to use sessions =================== @@ -15,7 +13,7 @@ Cookies contain a session ID -- not the data itself. Enabling sessions ================= -Sessions are implemented via a piece of :ref:`middleware <ref-middleware>`. +Sessions are implemented via a piece of :doc:`middleware </ref/middleware>`. To enable session functionality, do the following: @@ -56,8 +54,8 @@ For better performance, you may want to use a cache-based session backend. Django 1.0 did not include the ``cached_db`` session backend. To store session data using Django's cache system, you'll first need to make -sure you've configured your cache; see the :ref:`cache documentation -<topics-cache>` for details. +sure you've configured your cache; see the :doc:`cache documentation +</topics/cache>` for details. .. warning:: @@ -412,7 +410,7 @@ in the past -- but your application may have different requirements. Settings ======== -A few :ref:`Django settings <ref-settings>` give you control over session behavior: +A few :doc:`Django settings </ref/settings>` give you control over session behavior: SESSION_ENGINE -------------- diff --git a/docs/topics/http/shortcuts.txt b/docs/topics/http/shortcuts.txt index 5510613355..6bd3058941 100644 --- a/docs/topics/http/shortcuts.txt +++ b/docs/topics/http/shortcuts.txt @@ -1,5 +1,3 @@ -.. _topics-http-shortcuts: - ========================= Django shortcut functions ========================= diff --git a/docs/topics/http/urls.txt b/docs/topics/http/urls.txt index 5a2980f9d2..1f499909ee 100644 --- a/docs/topics/http/urls.txt +++ b/docs/topics/http/urls.txt @@ -1,5 +1,3 @@ -.. _topics-http-urls: - ============== URL dispatcher ============== @@ -335,7 +333,7 @@ The view prefix You can specify a common prefix in your ``patterns()`` call, to cut down on code duplication. -Here's the example URLconf from the :ref:`Django overview <intro-overview>`:: +Here's the example URLconf from the :doc:`Django overview </intro/overview>`:: from django.conf.urls.defaults import * @@ -537,8 +535,8 @@ In this example, for a request to ``/blog/2005/``, Django will call the year='2005', foo='bar' -This technique is used in :ref:`generic views <ref-generic-views>` and in the -:ref:`syndication framework <ref-contrib-syndication>` to pass metadata and +This technique is used in :doc:`generic views </ref/generic-views>` and in the +:doc:`syndication framework </ref/contrib/syndication>` to pass metadata and options to views. .. admonition:: Dealing with conflicts @@ -827,17 +825,80 @@ namespaces into URLs on specific application instances, according to the resolve() --------- -The :func:`django.core.urlresolvers.resolve` function can be used for resolving -URL paths to the corresponding view functions. It has the following signature: +The :func:`django.core.urlresolvers.resolve` function can be used for +resolving URL paths to the corresponding view functions. It has the +following signature: .. function:: resolve(path, urlconf=None) -``path`` is the URL path you want to resolve. As with ``reverse()`` above, you -don't need to worry about the ``urlconf`` parameter. The function returns the -triple (view function, arguments, keyword arguments). +``path`` is the URL path you want to resolve. As with +:func:`~django.core.urlresolvers.reverse`, you don't need to +worry about the ``urlconf`` parameter. The function returns a +:class:`django.core.urlresolvers.ResolverMatch` object that allows you +to access various meta-data about the resolved URL. + +.. class:: ResolverMatch() + + .. attribute:: ResolverMatch.func + + The view function that would be used to serve the URL + + .. attribute:: ResolverMatch.args + + The arguments that would be passed to the view function, as + parsed from the URL. + + .. attribute:: ResolverMatch.kwargs + + The keyword arguments that would be passed to the view + function, as parsed from the URL. + + .. attribute:: ResolverMatch.url_name + + The name of the URL pattern that matches the URL. + + .. attribute:: ResolverMatch.app_name + + The application namespace for the URL pattern that matches the + URL. + + .. attribute:: ResolverMatch.namespace + + The instance namespace for the URL pattern that matches the + URL. -For example, it can be used for testing if a view would raise a ``Http404`` -error before redirecting to it:: + .. attribute:: ResolverMatch.namespaces + + The list of individual namespace components in the full + instance namespace for the URL pattern that matches the URL. + i.e., if the namespace is ``foo:bar``, then namespaces will be + ``[`foo`, `bar`]``. + +A :class:`~django.core.urlresolvers.ResolverMatch` object can then be +interrogated to provide information about the URL pattern that matches +a URL:: + + # Resolve a URL + match = resolve('/some/path/') + # Print the URL pattern that matches the URL + print match.url_name + +A :class:`~django.core.urlresolvers.ResolverMatch` object can also be +assigned to a triple:: + + func, args, kwargs = resolve('/some/path/') + +.. versionchanged:: 1.3 + Triple-assignment exists for backwards-compatibility. Prior to + Django 1.3, :func:`~django.core.urlresolvers.resolve` returned a + triple containing (view function, arguments, keyword arguments); + the :class:`~django.core.urlresolvers.ResolverMatch` object (as + well as the namespace and pattern information it provides) is not + available in earlier Django releases. + +One possible use of :func:`~django.core.urlresolvers.resolve` would be +to testing if a view would raise a ``Http404`` error before +redirecting to it:: from urlparse import urlparse from django.core.urlresolvers import resolve @@ -858,9 +919,29 @@ error before redirecting to it:: return HttpResponseRedirect('/') return response + permalink() ----------- The :func:`django.db.models.permalink` decorator is useful for writing short methods that return a full URL path. For example, a model's ``get_absolute_url()`` method. See :func:`django.db.models.permalink` for more. + +get_script_prefix() +------------------- + +.. function:: get_script_prefix() + +.. versionadded:: 1.0 + +Normally, you should always use :func:`~django.core.urlresolvers.reverse` or +:func:`~django.db.models.permalink` to define URLs within your application. +However, if your application constructs part of the URL hierarchy itself, you +may occasionally need to generate URLs. In that case, you need to be able to +find the base URL of the Django project within its web server +(normally, :func:`~django.core.urlresolvers.reverse` takes care of this for +you). In that case, you can call ``get_script_prefix()``, which will return the +script prefix portion of the URL for your Django project. If your Django +project is at the root of its webserver, this is always ``"/"``, but it can be +changed, for instance by using ``django.root`` (see :ref:`How to use +Django with Apache and mod_python <howto-deployment-modpython>`). diff --git a/docs/topics/http/views.txt b/docs/topics/http/views.txt index 7d8743fe06..399e6b6ad1 100644 --- a/docs/topics/http/views.txt +++ b/docs/topics/http/views.txt @@ -1,5 +1,3 @@ -.. _topics-http-views: - ============= Writing Views ============= @@ -59,7 +57,7 @@ Mapping URLs to Views So, to recap, this view function returns an HTML page that includes the current date and time. To display this view at a particular URL, you'll need to create a -*URLconf*; see :ref:`topics-http-urls` for instructions. +*URLconf*; see :doc:`/topics/http/urls` for instructions. Returning errors ================ diff --git a/docs/topics/i18n/deployment.txt b/docs/topics/i18n/deployment.txt index 1a4f5fa4d5..f06227e0f6 100644 --- a/docs/topics/i18n/deployment.txt +++ b/docs/topics/i18n/deployment.txt @@ -1,5 +1,3 @@ -.. _topics-i18n-deployment: - ========================== Deployment of translations ========================== @@ -72,8 +70,8 @@ For example, your ``MIDDLEWARE_CLASSES`` might look like this:: 'django.middleware.common.CommonMiddleware', ) -(For more on middleware, see the :ref:`middleware documentation -<topics-http-middleware>`.) +(For more on middleware, see the :doc:`middleware documentation +</topics/http/middleware>`.) ``LocaleMiddleware`` tries to determine the user's language preference by following this algorithm: diff --git a/docs/topics/i18n/index.txt b/docs/topics/i18n/index.txt index e39a75067a..9c25192504 100644 --- a/docs/topics/i18n/index.txt +++ b/docs/topics/i18n/index.txt @@ -1,5 +1,3 @@ -.. _topics-i18n: - ===================================== Internationalization and localization ===================================== @@ -23,10 +21,10 @@ associated with each of these tasks (although it's perfectly normal if you find yourself performing more than one of these roles): * For application authors wishing to make sure their Django apps can be - used in different locales: :ref:`topics-i18n-internationalization`. - * For translators wanting to translate Django apps: :ref:`topics-i18n-localization`. + used in different locales: :doc:`/topics/i18n/internationalization`. + * For translators wanting to translate Django apps: :doc:`/topics/i18n/localization`. * For system administrators/final users setting up internationalized apps or - developers integrating third party apps: :ref:`topics-i18n-deployment`. + developers integrating third party apps: :doc:`/topics/i18n/deployment`. .. toctree:: :hidden: diff --git a/docs/topics/i18n/internationalization.txt b/docs/topics/i18n/internationalization.txt index 7ae8d18791..879c7739fb 100644 --- a/docs/topics/i18n/internationalization.txt +++ b/docs/topics/i18n/internationalization.txt @@ -1,5 +1,3 @@ -.. _topics-i18n-internationalization: - ==================== Internationalization ==================== @@ -242,7 +240,7 @@ If you don't like the verbose name ``ugettext_lazy``, you can just alias it as class MyThing(models.Model): name = models.CharField(help_text=_('This is the help text')) -Always use lazy translations in :ref:`Django models <topics-db-models>`. +Always use lazy translations in :doc:`Django models </topics/db/models>`. Field names and table names should be marked for translation (otherwise, they won't be translated in the admin interface). This means writing explicit ``verbose_name`` and ``verbose_name_plural`` options in the ``Meta`` class, @@ -332,7 +330,7 @@ Specifying translation strings: In template code .. highlightlang:: html+django -Translations in :ref:`Django templates <topics-templates>` uses two template +Translations in :doc:`Django templates </topics/templates>` uses two template tags and a slightly different syntax than in Python code. To give your template access to these tags, put ``{% load i18n %}`` toward the top of your template. @@ -569,7 +567,7 @@ function supports both positional and named interpolation: object or associative array. For example:: d = { - count: 10 + count: 10, total: 50 }; diff --git a/docs/topics/i18n/localization.txt b/docs/topics/i18n/localization.txt index ff8715571a..8ba1e1ecdc 100644 --- a/docs/topics/i18n/localization.txt +++ b/docs/topics/i18n/localization.txt @@ -1,5 +1,3 @@ -.. _topics-i18n-localization: - ============ Localization ============ @@ -12,7 +10,7 @@ files`_ and `locale aware date, time and numbers input/output in forms`_ .. seealso:: - The :ref:`howto-i18n` document included with the Django HOW-TO documents collection. + The :doc:`/howto/i18n` document included with the Django HOW-TO documents collection. .. _how-to-create-language-files: diff --git a/docs/topics/index.txt b/docs/topics/index.txt index 33f425a03e..4c6b7fc685 100644 --- a/docs/topics/index.txt +++ b/docs/topics/index.txt @@ -1,5 +1,3 @@ -.. _topics-index: - Using Django ============ diff --git a/docs/topics/install.txt b/docs/topics/install.txt index d53f49de46..2965c32ab6 100644 --- a/docs/topics/install.txt +++ b/docs/topics/install.txt @@ -1,5 +1,3 @@ -.. _topics-install: - ===================== How to install Django ===================== @@ -11,9 +9,9 @@ Install Python Being a Python Web framework, Django requires Python. -It works with any Python version from 2.4 to 2.6 (due to backwards +It works with any Python version from 2.4 to 2.7 (due to backwards incompatibilities in Python 3.0, Django does not currently work with -Python 3.0; see :ref:`the Django FAQ <faq-install>` for more +Python 3.0; see :doc:`the Django FAQ </faq/install>` for more information on supported Python versions and the 3.0 transition). Get Python at http://www.python.org. If you're running Linux or Mac OS X, you @@ -22,34 +20,45 @@ probably already have it installed. .. admonition:: Django on Jython If you use Jython_ (a Python implementation for the Java platform), you'll - need to follow a few additional steps. See :ref:`howto-jython` for details. + need to follow a few additional steps. See :doc:`/howto/jython` for details. .. _jython: http://jython.org/ Install Apache and mod_wsgi ============================= -If you just want to experiment with Django, skip ahead to the next section; -Django includes a lightweight web server you can use for testing, so you won't -need to set up Apache until you're ready to deploy Django in production. - -If you want to use Django on a production site, use Apache with `mod_wsgi`_. -mod_wsgi is similar to mod_perl -- it embeds Python within Apache and loads -Python code into memory when the server starts. Code stays in memory throughout -the life of an Apache process, which leads to significant performance gains over -other server arrangements. Make sure you have Apache installed, with the -mod_wsgi module activated. Django will work with any version of Apache that -supports mod_wsgi. - -See :ref:`How to use Django with mod_wsgi <howto-deployment-modwsgi>` for -information on how to configure mod_wsgi once you have it installed. - -If you can't use mod_wsgi for some reason, fear not: Django supports many other -deployment options. A great second choice is :ref:`mod_python -<howto-deployment-modpython>`, the predecessor to mod_wsgi. Additionally, Django -follows the WSGI_ spec, which allows it to run on a variety of server platforms. -See the `server-arrangements wiki page`_ for specific installation instructions -for each platform. +If you just want to experiment with Django, skip ahead to the next +section; Django includes a lightweight web server you can use for +testing, so you won't need to set up Apache until you're ready to +deploy Django in production. + +If you want to use Django on a production site, use Apache with +`mod_wsgi`_. mod_wsgi can operate in one of two modes: an embedded +mode and a daemon mode. In embedded mode, mod_wsgi is similar to +mod_perl -- it embeds Python within Apache and loads Python code into +memory when the server starts. Code stays in memory throughout the +life of an Apache process, which leads to significant performance +gains over other server arrangements. In daemon mode, mod_wsgi spawns +an independent daemon process that handles requests. The daemon +process can run as a different user than the webserver, possibly +leading to improved security, and the daemon process can be restarted +without restarting the entire Apache webserver, possibly making +refreshing your codebase more seamless. Consult the mod_wsgi +documentation to determine which mode is right for your setup. Make +sure you have Apache installed, with the mod_wsgi module activated. +Django will work with any version of Apache that supports mod_wsgi. + +See :doc:`How to use Django with mod_wsgi </howto/deployment/modwsgi>` +for information on how to configure mod_wsgi once you have it +installed. + +If you can't use mod_wsgi for some reason, fear not: Django supports +many other deployment options. Another option is :doc:`FastCGI +</howto/deployment/fastcgi>`, perfect for using Django with servers +other than Apache. Additionally, Django follows the WSGI_ spec, which +allows it to run on a variety of server platforms. See the +`server-arrangements wiki page`_ for specific installation +instructions for each platform. .. _Apache: http://httpd.apache.org/ .. _mod_wsgi: http://code.google.com/p/modwsgi/ @@ -90,8 +99,8 @@ database bindings are installed. If you're on Windows, check out the unofficial `compiled Windows version`_. * If you're using MySQL, you'll need MySQLdb_, version 1.2.1p2 or higher. You - will also want to read the database-specific notes for the :ref:`MySQL - backend <ref-databases>`. + will also want to read the database-specific notes for the :doc:`MySQL + backend </ref/databases>`. * If you're using SQLite and Python 2.4, you'll need pysqlite_. Use version 2.0.3 or higher. Python 2.5 ships with an SQLite wrapper in the standard @@ -115,7 +124,7 @@ can simply grant Django ``SELECT``, ``INSERT``, ``UPDATE`` and ``ALTER TABLE`` privileges during ``syncdb`` but won't issue ``ALTER TABLE`` statements on a table once ``syncdb`` has created it. -If you're using Django's :ref:`testing framework<topics-testing>` to test database queries, +If you're using Django's :doc:`testing framework</topics/testing>` to test database queries, Django will need permission to create a test database. .. _PostgreSQL: http://www.postgresql.org/ @@ -177,7 +186,7 @@ It's easy, no matter which way you choose. Installing a distribution-specific package ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Check the :ref:`distribution specific notes <misc-distributions>` to see if your +Check the :doc:`distribution specific notes </misc/distributions>` to see if your platform/distribution provides official Django packages/installers. Distribution-provided packages will typically allow for automatic installation of dependencies and easy upgrade paths. @@ -257,15 +266,15 @@ latest bug fixes and improvements, follow these instructions: links. (Environment variables can be defined on Windows systems `from the Control Panel`_.) - .. admonition:: What about Apache and mod_python? + .. admonition:: What about Apache and mod_wsgi? - If you take the approach of setting ``PYTHONPATH``, you'll need to - remember to do the same thing in your Apache configuration once you - deploy your production site. Do this by setting ``PythonPath`` in your - Apache configuration file. + If you take the approach of setting ``PYTHONPATH``, you'll need + to remember to do the same thing in your WSGI application once + you deploy your production site. Do this by appending to + ``sys.path`` in your WSGI application. More information about deployment is available, of course, in our - :ref:`How to use Django with mod_python <howto-deployment-modpython>` + :doc:`How to use Django with mod_wsgi </howto/deployment/modwsgi>` documentation. 4. On Unix-like systems, create a symbolic link to the file diff --git a/docs/topics/pagination.txt b/docs/topics/pagination.txt index 70f087bd84..ee8a43379a 100644 --- a/docs/topics/pagination.txt +++ b/docs/topics/pagination.txt @@ -1,5 +1,3 @@ -.. _topics-pagination: - ========== Pagination ========== diff --git a/docs/topics/serialization.txt b/docs/topics/serialization.txt index 1cf8e86462..c8acc8539e 100644 --- a/docs/topics/serialization.txt +++ b/docs/topics/serialization.txt @@ -1,5 +1,3 @@ -.. _topics-serialization: - ========================== Serializing Django objects ========================== @@ -141,10 +139,6 @@ to install third-party Python modules: ``json`` Serializes to and from JSON_ (using a version of simplejson_ bundled with Django). - ``python`` Translates to and from "simple" Python objects (lists, dicts, - strings, etc.). Not really all that useful on its own, but - used as a base for other serializers. - ``yaml`` Serializes to YAML (YAML Ain't a Markup Language). This serializer is only available if PyYAML_ is installed. ========== ============================================================== @@ -169,7 +163,7 @@ For example:: json_serializer.serialize(queryset, ensure_ascii=False, stream=response) The Django source code includes the simplejson_ module. However, if you're -using Python 2.6 (which includes a builtin version of the module), Django will +using Python 2.6 or later (which includes a builtin version of the module), Django will use the builtin ``json`` module automatically. If you have a system installed version that includes the C-based speedup extension, or your system version is more recent than the version shipped with Django (currently, 2.0.7), the @@ -338,7 +332,7 @@ example, ``(first name, last name)``. Then, when you call ``serializers.serialize()``, you provide a ``use_natural_keys=True`` argument:: - >>> serializers.serialize([book1, book2], format='json', indent=2, use_natural_keys=True) + >>> serializers.serialize('json', [book1, book2], indent=2, use_natural_keys=True) When ``use_natural_keys=True`` is specified, Django will use the ``natural_key()`` method to serialize any reference to objects of the diff --git a/docs/topics/settings.txt b/docs/topics/settings.txt index 9e6a689588..6d96190a90 100644 --- a/docs/topics/settings.txt +++ b/docs/topics/settings.txt @@ -1,5 +1,3 @@ -.. _topics-settings: - =============== Django settings =============== @@ -46,7 +44,7 @@ Python `import search path`_. The django-admin.py utility --------------------------- -When using :ref:`django-admin.py <ref-django-admin>`, you can either set the +When using :doc:`django-admin.py </ref/django-admin>`, you can either set the environment variable once, or explicitly pass in the settings module each time you run the utility. @@ -66,20 +64,19 @@ Use the ``--settings`` command-line argument to specify the settings manually:: .. _django-admin.py: ../django-admin/ -On the server (mod_python) +On the server (mod_wsgi) -------------------------- -In your live server environment, you'll need to tell Apache/mod_python which -settings file to use. Do that with ``SetEnv``:: +In your live server environment, you'll need to tell your WSGI +application what settings file to use. Do that with ``os.environ``:: + + import os - <Location "/mysite/"> - SetHandler python-program - PythonHandler django.core.handlers.modpython - SetEnv DJANGO_SETTINGS_MODULE mysite.settings - </Location> + os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings' -Read the :ref:`Django mod_python documentation <howto-deployment-modpython>` for -more information. +Read the :doc:`Django mod_wsgi documentation +</howto/deployment/modwsgi>` for more information and other common +elements to a Django WSGI application. Default settings ================ @@ -151,7 +148,7 @@ read it. This is especially important in a shared-hosting environment. Available settings ================== -For a full list of available settings, see the :ref:`settings reference <ref-settings>`. +For a full list of available settings, see the :doc:`settings reference </ref/settings>`. Creating your own settings ========================== diff --git a/docs/topics/signals.txt b/docs/topics/signals.txt index eb1a5fd825..78790db071 100644 --- a/docs/topics/signals.txt +++ b/docs/topics/signals.txt @@ -1,5 +1,3 @@ -.. _topics-signals: - ======= Signals ======= @@ -13,7 +11,7 @@ signals allow certain *senders* to notify a set of *receivers* that some action has taken place. They're especially useful when many pieces of code may be interested in the same events. -Django provides a :ref:`set of built-in signals <ref-signals>` that let user +Django provides a :doc:`set of built-in signals </ref/signals>` that let user code get notified by Django itself of certain actions. These include some useful notifications: @@ -38,7 +36,7 @@ notifications: Sent when Django starts or finishes an HTTP request. -See the :ref:`built-in signal documentation <ref-signals>` for a complete list, +See the :doc:`built-in signal documentation </ref/signals>` for a complete list, and a complete explanation of each signal. You can also `define and send your own custom signals`_; see below. @@ -82,7 +80,8 @@ must be able to handle those new arguments. Connecting receiver functions ----------------------------- -Next, we'll need to connect our receiver to the signal: +There are two ways you can connect a receiever to a signal. You can take the +manual connect route: .. code-block:: python @@ -90,6 +89,17 @@ Next, we'll need to connect our receiver to the signal: request_finished.connect(my_callback) +Alternatively, you can use a decorator used when you define your receiver: + +.. code-block:: python + + from django.core.signals import request_finished + from django.dispatch import receiver + + @receiver(request_finished) + def my_callback(sender, **kwargs): + print "Request finished!" + Now, our ``my_callback`` function will be called each time a request finishes. .. admonition:: Where should this code live? @@ -117,18 +127,18 @@ signals sent by some model: .. code-block:: python from django.db.models.signals import pre_save + from django.dispatch import receiver from myapp.models import MyModel + @receiver(pre_save, sender=MyModel) def my_handler(sender, **kwargs): ... - pre_save.connect(my_handler, sender=MyModel) - The ``my_handler`` function will only be called when an instance of ``MyModel`` is saved. Different signals use different objects as their senders; you'll need to consult -the :ref:`built-in signal documentation <ref-signals>` for details of each +the :doc:`built-in signal documentation </ref/signals>` for details of each particular signal. Defining and sending signals diff --git a/docs/topics/templates.txt b/docs/topics/templates.txt index 9fa6c44dc9..492d23d716 100644 --- a/docs/topics/templates.txt +++ b/docs/topics/templates.txt @@ -1,5 +1,3 @@ -.. _topics-templates: - ============================ The Django template language ============================ @@ -8,7 +6,7 @@ The Django template language This document explains the language syntax of the Django template system. If you're looking for a more technical perspective on how it works and how to - extend it, see :ref:`ref-templates-api`. + extend it, see :doc:`/ref/templates/api`. Django's template language is designed to strike a balance between power and ease. It's designed to feel comfortable to those used to working with HTML. If @@ -28,8 +26,8 @@ or CheetahTemplate_, you should feel right at home with Django's templates. tag for looping, etc. -- but these are not simply executed as the corresponding Python code, and the template system will not execute arbitrary Python expressions. Only the tags, filters and syntax listed below - are supported by default (although you can add :ref:`your own extensions - <howto-custom-template-tags>` to the template language as needed). + are supported by default (although you can add :doc:`your own extensions + </howto/custom-template-tags>` to the template language as needed). .. _`The Django template language: For Python programmers`: ../templates_python/ .. _Smarty: http://smarty.php.net/ @@ -140,7 +138,7 @@ template filters: If ``value`` isn't provided or is empty, the above will display "``nothing``". - + :tfilter:`length` Returns the length of the value. This works for both strings and lists; for example:: @@ -148,7 +146,7 @@ template filters: {{ value|length }} If ``value`` is ``['a', 'b', 'c', 'd']``, the output will be ``4``. - + :tfilter:`striptags` Strips all [X]HTML tags. For example:: @@ -161,7 +159,7 @@ Again, these are just a few examples; see the :ref:`built-in filter reference <ref-templates-builtins-filters>` for the complete list. You can also create your own custom template filters; see -:ref:`howto-custom-template-tags`. +:doc:`/howto/custom-template-tags`. Tags ==== @@ -217,7 +215,7 @@ Again, the above is only a selection of the whole list; see the :ref:`built-in tag reference <ref-templates-builtins-tags>` for the complete list. You can also create your own custom template tags; see -:ref:`howto-custom-template-tags`. +:doc:`/howto/custom-template-tags`. Comments ======== @@ -571,6 +569,45 @@ This doesn't affect what happens to data coming from the variable itself. The variable's contents are still automatically escaped, if necessary, because they're beyond the control of the template author. +.. _template-accessing-methods: + +Accessing method calls +====================== + +Most method calls attached to objects are also available from within templates. +This means that templates have access to much more than just class attributes +(like field names) and variables passed in from views. For example, the Django +ORM provides the :ref:`"entry_set"<topics-db-queries-related>` syntax for +finding a collection of objects related on a foreign key. Therefore, given +a model called "comment" with a foreign key relationship to a model called +"task" you can loop through all comments attached to a given task like this:: + + {% for comment in task.comment_set.all %} + {{ comment }} + {% endfor %} + +Similarly, :doc:`QuerySets<ref/models/querysets>` provide a ``count()`` method +to count the number of objects they contain. Therefore, you can obtain a count +of all comments related to the current task with:: + + {{ task.comment_set.all.count }} + +And of course you can easily access methods you've explicitly defined on your +own models:: + + # In model + class Task(models.Model): + def foo(self): + return "bar" + + # In template + {{ task.foo }} + +Because Django intentionally limits the amount of logic processing available +in the template language, it is not possible to pass arguments to method calls +accessed from within templates. Data should be calculated in views, then passed +to templates for display. + .. _template-built-in-reference: Using the built-in reference @@ -634,8 +671,8 @@ The ``{% load %}`` tag can take multiple library names, separated by spaces. Example:: {% load comments i18n %} - -See :ref:`howto-custom-template-tags` for information on writing your own custom + +See :doc:`/howto/custom-template-tags` for information on writing your own custom template libraries. Custom libraries and template inheritance diff --git a/docs/topics/testing.txt b/docs/topics/testing.txt index bd727ee136..5c1933c946 100644 --- a/docs/topics/testing.txt +++ b/docs/topics/testing.txt @@ -1,5 +1,3 @@ -.. _topics-testing: - =========================== Testing Django applications =========================== @@ -311,6 +309,24 @@ can press ``Ctrl-C`` a second time and the test run will halt immediately, but not gracefully. No details of the tests run before the interruption will be reported, and any test databases created by the run will not be destroyed. +Running tests outside the test runner +------------------------------------- + +If you want to run tests outside of ``./manage.py test`` -- for example, +from a shell prompt -- you will need to set up the test +environment first. Django provides a convenience method to do this:: + + >>> from django.test.utils import setup_test_environment + >>> setup_test_environment() + +This convenience method sets up the test database, and puts other +Django features into modes that allow for repeatable testing. + +The call to :meth:`~django.test.utils.setup_test_environment` is made +automatically as part of the setup of `./manage.py test`. You only +need to manually invoke this method if you're not using running your +tests via Django's test runner. + The test database ----------------- @@ -342,7 +358,7 @@ For fine-grained control over the character encoding of your test database, use the :setting:`TEST_CHARSET` option. If you're using MySQL, you can also use the :setting:`TEST_COLLATION` option to control the particular collation used by the test database. See the -:ref:`settings documentation <ref-settings>` for details of these +:doc:`settings documentation </ref/settings>` for details of these advanced settings. .. _topics-testing-masterslave: @@ -401,7 +417,7 @@ Other test conditions --------------------- Regardless of the value of the :setting:`DEBUG` setting in your configuration -file, all Django tests run with :setting:`DEBUG=False`. This is to ensure that +file, all Django tests run with :setting:`DEBUG`\=False. This is to ensure that the observed output of your code matches what will be seen in a production setting. @@ -556,6 +572,19 @@ Note a few important things about how the test client works: This black magic (essentially a patching of Django's template system in memory) only happens during test running. + * By default, the test client will disable any CSRF checks + performed by your site. + + If, for some reason, you *want* the test client to perform CSRF + checks, you can create an instance of the test client that + enforces CSRF checks. To do this, pass in the + ``enforce_csrf_checks`` argument when you construct your + client:: + + >>> from django.test import Client + >>> csrf_client = Client(enforce_csrf_checks=True) + + .. _urllib: http://docs.python.org/library/urllib.html .. _urllib2: http://docs.python.org/library/urllib2.html @@ -751,7 +780,7 @@ arguments at time of construction: .. versionadded:: 1.0 - If your site uses Django's :ref:`authentication system<topics-auth>` + If your site uses Django's :doc:`authentication system</topics/auth>` and you deal with logging in users, you can use the test client's ``login()`` method to simulate the effect of a user logging into the site. @@ -797,7 +826,7 @@ arguments at time of construction: .. versionadded:: 1.0 - If your site uses Django's :ref:`authentication system<topics-auth>`, + If your site uses Django's :doc:`authentication system</topics/auth>`, the ``logout()`` method can be used to simulate the effect of a user logging out of your site. @@ -904,7 +933,16 @@ can access these properties as part of a test condition. .. attribute:: Client.session A dictionary-like object containing session information. See the - :ref:`session documentation<topics-http-sessions>` for full details. + :doc:`session documentation</topics/http/sessions>` for full details. + + To modify the session and then save it, it must be stored in a variable + first (because a new ``SessionStore`` is created every time this property + is accessed):: + + def test_something(self): + session = self.client.session + session['somekey'] = 'test' + session.save() .. _Cookie module documentation: http://docs.python.org/library/cookie.html @@ -1052,23 +1090,25 @@ A fixture is a collection of data that Django knows how to import into a database. For example, if your site has user accounts, you might set up a fixture of fake user accounts in order to populate your database during tests. -The most straightforward way of creating a fixture is to use the ``manage.py -dumpdata`` command. This assumes you already have some data in your database. -See the :djadmin:`dumpdata documentation<dumpdata>` for more details. +The most straightforward way of creating a fixture is to use the +:djadmin:`manage.py dumpdata <dumpdata>` command. This assumes you +already have some data in your database. See the :djadmin:`dumpdata +documentation<dumpdata>` for more details. .. note:: - If you've ever run ``manage.py syncdb``, you've already used a fixture - without even knowing it! When you call ``syncdb`` in the database for - the first time, Django installs a fixture called ``initial_data``. - This gives you a way of populating a new database with any initial data, - such as a default set of categories. + If you've ever run :djadmin:`manage.py syncdb<syncdb>`, you've + already used a fixture without even knowing it! When you call + :djadmin:`syncdb` in the database for the first time, Django + installs a fixture called ``initial_data``. This gives you a way + of populating a new database with any initial data, such as a + default set of categories. - Fixtures with other names can always be installed manually using the - ``manage.py loaddata`` command. + Fixtures with other names can always be installed manually using + the :djadmin:`manage.py loaddata<loaddata>` command. Once you've created a fixture and placed it in a ``fixtures`` directory in one of your :setting:`INSTALLED_APPS`, you can use it in your unit tests by -specifying a ``fixtures`` class attribute on your ``django.test.TestCase`` +specifying a ``fixtures`` class attribute on your :class:`django.test.TestCase` subclass:: from django.test import TestCase @@ -1248,6 +1288,21 @@ cause of an failure in your test suite. ``target_status_code`` will be the url and status code for the final point of the redirect chain. +.. method:: TestCase.assertQuerysetEqual(qs, values, transform=repr) + + .. versionadded:: 1.3 + + Asserts that a queryset ``qs`` returns a particular list of values ``values``. + + The comparison of the contents of ``qs`` and ``values`` is performed using + the function ``transform``; by default, this means that the ``repr()`` of + each value is compared. Any other callable can be used if ``repr()`` doesn't + provide a unique or helpful comparison. + + The comparison is also ordering dependent. If ``qs`` doesn't provide an + implicit ordering, you will need to apply a ``order_by()`` clause to your + queryset to ensure that the test will pass reliably. + .. _topics-testing-email: E-mail services @@ -1255,8 +1310,8 @@ E-mail services .. versionadded:: 1.0 -If any of your Django views send e-mail using :ref:`Django's e-mail -functionality <topics-email>`, you probably don't want to send e-mail each time +If any of your Django views send e-mail using :doc:`Django's e-mail +functionality </topics/email>`, you probably don't want to send e-mail each time you run a test using that view. For this reason, Django's test runner automatically redirects all Django-sent e-mail to a dummy outbox. This lets you test every aspect of sending e-mail -- from the number of messages sent to the @@ -1,4 +1,4 @@ [bdist_rpm] -doc_files = docs examples extras AUTHORS INSTALL LICENSE README +doc_files = docs extras AUTHORS INSTALL LICENSE README install-script = scripts/rpm-install.sh diff --git a/tests/modeltests/aggregation/models.py b/tests/modeltests/aggregation/models.py index f50abe651b..ccc12898b7 100644 --- a/tests/modeltests/aggregation/models.py +++ b/tests/modeltests/aggregation/models.py @@ -1,6 +1,7 @@ # coding: utf-8 from django.db import models + class Author(models.Model): name = models.CharField(max_length=100) age = models.IntegerField() @@ -39,323 +40,3 @@ class Store(models.Model): def __unicode__(self): return self.name -# Tests on 'aggregate' -# Different backends and numbers. -__test__ = {'API_TESTS': """ ->>> from django.core import management ->>> from decimal import Decimal ->>> from datetime import date - -# Reset the database representation of this app. -# This will return the database to a clean initial state. ->>> management.call_command('flush', verbosity=0, interactive=False) - -# Empty Call - request nothing, get nothing. ->>> Author.objects.all().aggregate() -{} - ->>> from django.db.models import Avg, Sum, Count, Max, Min - -# Single model aggregation -# - -# Single aggregate -# Average age of Authors ->>> Author.objects.all().aggregate(Avg('age')) -{'age__avg': 37.4...} - -# Multiple aggregates -# Average and Sum of Author ages ->>> Author.objects.all().aggregate(Sum('age'), Avg('age')) -{'age__sum': 337, 'age__avg': 37.4...} - -# Aggreates interact with filters, and only -# generate aggregate values for the filtered values -# Sum of the age of those older than 29 years old ->>> Author.objects.all().filter(age__gt=29).aggregate(Sum('age')) -{'age__sum': 254} - -# Depth-1 Joins -# - -# On Relationships with self -# Average age of the friends of each author ->>> Author.objects.all().aggregate(Avg('friends__age')) -{'friends__age__avg': 34.07...} - -# On ManyToMany Relationships -# - -# Forward -# Average age of the Authors of Books with a rating of less than 4.5 ->>> Book.objects.all().filter(rating__lt=4.5).aggregate(Avg('authors__age')) -{'authors__age__avg': 38.2...} - -# Backward -# Average rating of the Books whose Author's name contains the letter 'a' ->>> Author.objects.all().filter(name__contains='a').aggregate(Avg('book__rating')) -{'book__rating__avg': 4.0} - -# On OneToMany Relationships -# - -# Forward -# Sum of the number of awards of each Book's Publisher ->>> Book.objects.all().aggregate(Sum('publisher__num_awards')) -{'publisher__num_awards__sum': 30} - -# Backward -# Sum of the price of every Book that has a Publisher ->>> Publisher.objects.all().aggregate(Sum('book__price')) -{'book__price__sum': Decimal("270.27")} - -# Multiple Joins -# - -# Forward ->>> Store.objects.all().aggregate(Max('books__authors__age')) -{'books__authors__age__max': 57} - -# Backward -# Note that the very long default alias may be truncated ->>> Author.objects.all().aggregate(Min('book__publisher__num_awards')) -{'book__publisher__num_award...': 1} - -# Aggregate outputs can also be aliased. - -# Average amazon.com Book rating ->>> Store.objects.filter(name='Amazon.com').aggregate(amazon_mean=Avg('books__rating')) -{'amazon_mean': 4.08...} - -# Tests on annotate() - -# An empty annotate call does nothing but return the same QuerySet ->>> Book.objects.all().annotate().order_by('pk') -[<Book: The Definitive Guide to Django: Web Development Done Right>, <Book: Sams Teach Yourself Django in 24 Hours>, <Book: Practical Django Projects>, <Book: Python Web Development with Django>, <Book: Artificial Intelligence: A Modern Approach>, <Book: Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp>] - -# Annotate inserts the alias into the model object with the aggregated result ->>> books = Book.objects.all().annotate(mean_age=Avg('authors__age')) ->>> books.get(pk=1).name -u'The Definitive Guide to Django: Web Development Done Right' - ->>> books.get(pk=1).mean_age -34.5 - -# On ManyToMany Relationships - -# Forward -# Average age of the Authors of each book with a rating less than 4.5 ->>> books = Book.objects.all().filter(rating__lt=4.5).annotate(Avg('authors__age')) ->>> sorted([(b.name, b.authors__age__avg) for b in books]) -[(u'Artificial Intelligence: A Modern Approach', 51.5), (u'Practical Django Projects', 29.0), (u'Python Web Development with Django', 30.3...), (u'Sams Teach Yourself Django in 24 Hours', 45.0)] - -# Count the number of authors of each book ->>> books = Book.objects.annotate(num_authors=Count('authors')) ->>> sorted([(b.name, b.num_authors) for b in books]) -[(u'Artificial Intelligence: A Modern Approach', 2), (u'Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp', 1), (u'Practical Django Projects', 1), (u'Python Web Development with Django', 3), (u'Sams Teach Yourself Django in 24 Hours', 1), (u'The Definitive Guide to Django: Web Development Done Right', 2)] - -# Backward -# Average rating of the Books whose Author's names contains the letter 'a' ->>> authors = Author.objects.all().filter(name__contains='a').annotate(Avg('book__rating')) ->>> sorted([(a.name, a.book__rating__avg) for a in authors]) -[(u'Adrian Holovaty', 4.5), (u'Brad Dayley', 3.0), (u'Jacob Kaplan-Moss', 4.5), (u'James Bennett', 4.0), (u'Paul Bissex', 4.0), (u'Stuart Russell', 4.0)] - -# Count the number of books written by each author ->>> authors = Author.objects.annotate(num_books=Count('book')) ->>> sorted([(a.name, a.num_books) for a in authors]) -[(u'Adrian Holovaty', 1), (u'Brad Dayley', 1), (u'Jacob Kaplan-Moss', 1), (u'James Bennett', 1), (u'Jeffrey Forcier', 1), (u'Paul Bissex', 1), (u'Peter Norvig', 2), (u'Stuart Russell', 1), (u'Wesley J. Chun', 1)] - -# On OneToMany Relationships - -# Forward -# Annotate each book with the number of awards of each Book's Publisher ->>> books = Book.objects.all().annotate(Sum('publisher__num_awards')) ->>> sorted([(b.name, b.publisher__num_awards__sum) for b in books]) -[(u'Artificial Intelligence: A Modern Approach', 7), (u'Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp', 9), (u'Practical Django Projects', 3), (u'Python Web Development with Django', 7), (u'Sams Teach Yourself Django in 24 Hours', 1), (u'The Definitive Guide to Django: Web Development Done Right', 3)] - -# Backward -# Annotate each publisher with the sum of the price of all books sold ->>> publishers = Publisher.objects.all().annotate(Sum('book__price')) ->>> sorted([(p.name, p.book__price__sum) for p in publishers]) -[(u'Apress', Decimal("59.69")), (u"Jonno's House of Books", None), (u'Morgan Kaufmann', Decimal("75.00")), (u'Prentice Hall', Decimal("112.49")), (u'Sams', Decimal("23.09"))] - -# Calls to values() are not commutative over annotate(). - -# Calling values on a queryset that has annotations returns the output -# as a dictionary ->>> [sorted(o.iteritems()) for o in Book.objects.filter(pk=1).annotate(mean_age=Avg('authors__age')).values()] -[[('contact_id', 1), ('id', 1), ('isbn', u'159059725'), ('mean_age', 34.5), ('name', u'The Definitive Guide to Django: Web Development Done Right'), ('pages', 447), ('price', Decimal("30...")), ('pubdate', datetime.date(2007, 12, 6)), ('publisher_id', 1), ('rating', 4.5)]] - ->>> Book.objects.filter(pk=1).annotate(mean_age=Avg('authors__age')).values('pk', 'isbn', 'mean_age') -[{'pk': 1, 'isbn': u'159059725', 'mean_age': 34.5}] - -# Calling values() with parameters reduces the output ->>> Book.objects.filter(pk=1).annotate(mean_age=Avg('authors__age')).values('name') -[{'name': u'The Definitive Guide to Django: Web Development Done Right'}] - -# An empty values() call before annotating has the same effect as an -# empty values() call after annotating ->>> [sorted(o.iteritems()) for o in Book.objects.filter(pk=1).values().annotate(mean_age=Avg('authors__age'))] -[[('contact_id', 1), ('id', 1), ('isbn', u'159059725'), ('mean_age', 34.5), ('name', u'The Definitive Guide to Django: Web Development Done Right'), ('pages', 447), ('price', Decimal("30...")), ('pubdate', datetime.date(2007, 12, 6)), ('publisher_id', 1), ('rating', 4.5)]] - -# Calling annotate() on a ValuesQuerySet annotates over the groups of -# fields to be selected by the ValuesQuerySet. - -# Note that an extra parameter is added to each dictionary. This -# parameter is a queryset representing the objects that have been -# grouped to generate the annotation - ->>> Book.objects.all().values('rating').annotate(n_authors=Count('authors__id'), mean_age=Avg('authors__age')).order_by('rating') -[{'rating': 3.0, 'n_authors': 1, 'mean_age': 45.0}, {'rating': 4.0, 'n_authors': 6, 'mean_age': 37.1...}, {'rating': 4.5, 'n_authors': 2, 'mean_age': 34.5}, {'rating': 5.0, 'n_authors': 1, 'mean_age': 57.0}] - -# If a join doesn't match any objects, an aggregate returns None ->>> authors = Author.objects.all().annotate(Avg('friends__age')).order_by('id') ->>> len(authors) -9 ->>> sorted([(a.name, a.friends__age__avg) for a in authors]) -[(u'Adrian Holovaty', 32.0), (u'Brad Dayley', None), (u'Jacob Kaplan-Moss', 29.5), (u'James Bennett', 34.0), (u'Jeffrey Forcier', 27.0), (u'Paul Bissex', 31.0), (u'Peter Norvig', 46.0), (u'Stuart Russell', 57.0), (u'Wesley J. Chun', 33.6...)] - - -# The Count aggregation function allows an extra parameter: distinct. -# This restricts the count results to unique items ->>> Book.objects.all().aggregate(Count('rating')) -{'rating__count': 6} - ->>> Book.objects.all().aggregate(Count('rating', distinct=True)) -{'rating__count': 4} - -# Retreiving the grouped objects - -# When using Count you can also omit the primary key and refer only to -# the related field name if you want to count all the related objects -# and not a specific column ->>> explicit = list(Author.objects.annotate(Count('book__id'))) ->>> implicit = list(Author.objects.annotate(Count('book'))) ->>> explicit == implicit -True - -# Ordering is allowed on aggregates ->>> Book.objects.values('rating').annotate(oldest=Max('authors__age')).order_by('oldest', 'rating') -[{'rating': 4.5, 'oldest': 35}, {'rating': 3.0, 'oldest': 45}, {'rating': 4.0, 'oldest': 57}, {'rating': 5.0, 'oldest': 57}] - ->>> Book.objects.values('rating').annotate(oldest=Max('authors__age')).order_by('-oldest', '-rating') -[{'rating': 5.0, 'oldest': 57}, {'rating': 4.0, 'oldest': 57}, {'rating': 3.0, 'oldest': 45}, {'rating': 4.5, 'oldest': 35}] - -# It is possible to aggregate over anotated values ->>> Book.objects.all().annotate(num_authors=Count('authors__id')).aggregate(Avg('num_authors')) -{'num_authors__avg': 1.66...} - -# You can filter the results based on the aggregation alias. - -# Lets add a publisher to test the different possibilities for filtering ->>> p = Publisher(name='Expensive Publisher', num_awards=0) ->>> p.save() ->>> Book(name='ExpensiveBook1', pages=1, isbn='111', rating=3.5, price=Decimal("1000"), publisher=p, contact_id=1, pubdate=date(2008,12,1)).save() ->>> Book(name='ExpensiveBook2', pages=1, isbn='222', rating=4.0, price=Decimal("1000"), publisher=p, contact_id=1, pubdate=date(2008,12,2)).save() ->>> Book(name='ExpensiveBook3', pages=1, isbn='333', rating=4.5, price=Decimal("35"), publisher=p, contact_id=1, pubdate=date(2008,12,3)).save() - -# Publishers that have: - -# (i) more than one book ->>> Publisher.objects.annotate(num_books=Count('book__id')).filter(num_books__gt=1).order_by('pk') -[<Publisher: Apress>, <Publisher: Prentice Hall>, <Publisher: Expensive Publisher>] - -# (ii) a book that cost less than 40 ->>> Publisher.objects.filter(book__price__lt=Decimal("40.0")).order_by('pk') -[<Publisher: Apress>, <Publisher: Apress>, <Publisher: Sams>, <Publisher: Prentice Hall>, <Publisher: Expensive Publisher>] - -# (iii) more than one book and (at least) a book that cost less than 40 ->>> Publisher.objects.annotate(num_books=Count('book__id')).filter(num_books__gt=1, book__price__lt=Decimal("40.0")).order_by('pk') -[<Publisher: Apress>, <Publisher: Prentice Hall>, <Publisher: Expensive Publisher>] - -# (iv) more than one book that costs less than $40 ->>> Publisher.objects.filter(book__price__lt=Decimal("40.0")).annotate(num_books=Count('book__id')).filter(num_books__gt=1).order_by('pk') -[<Publisher: Apress>] - -# Now a bit of testing on the different lookup types -# - ->>> Publisher.objects.annotate(num_books=Count('book')).filter(num_books__range=[1, 3]).order_by('pk') -[<Publisher: Apress>, <Publisher: Sams>, <Publisher: Prentice Hall>, <Publisher: Morgan Kaufmann>, <Publisher: Expensive Publisher>] - ->>> Publisher.objects.annotate(num_books=Count('book')).filter(num_books__range=[1, 2]).order_by('pk') -[<Publisher: Apress>, <Publisher: Sams>, <Publisher: Prentice Hall>, <Publisher: Morgan Kaufmann>] - ->>> Publisher.objects.annotate(num_books=Count('book')).filter(num_books__in=[1, 3]).order_by('pk') -[<Publisher: Sams>, <Publisher: Morgan Kaufmann>, <Publisher: Expensive Publisher>] - ->>> Publisher.objects.annotate(num_books=Count('book')).filter(num_books__isnull=True) -[] - ->>> p.delete() - -# Does Author X have any friends? (or better, how many friends does author X have) ->> Author.objects.filter(pk=1).aggregate(Count('friends__id')) -{'friends__id__count': 2.0} - -# Give me a list of all Books with more than 1 authors ->>> Book.objects.all().annotate(num_authors=Count('authors__name')).filter(num_authors__ge=2).order_by('pk') -[<Book: The Definitive Guide to Django: Web Development Done Right>, <Book: Artificial Intelligence: A Modern Approach>] - -# Give me a list of all Authors that have no friends ->>> Author.objects.all().annotate(num_friends=Count('friends__id', distinct=True)).filter(num_friends=0).order_by('pk') -[<Author: Brad Dayley>] - -# Give me a list of all publishers that have published more than 1 books ->>> Publisher.objects.all().annotate(num_books=Count('book__id')).filter(num_books__gt=1).order_by('pk') -[<Publisher: Apress>, <Publisher: Prentice Hall>] - -# Give me a list of all publishers that have published more than 1 books that cost less than 40 ->>> Publisher.objects.all().filter(book__price__lt=Decimal("40.0")).annotate(num_books=Count('book__id')).filter(num_books__gt=1) -[<Publisher: Apress>] - -# Give me a list of all Books that were written by X and one other author. ->>> Book.objects.all().annotate(num_authors=Count('authors__id')).filter(authors__name__contains='Norvig', num_authors__gt=1) -[<Book: Artificial Intelligence: A Modern Approach>] - -# Give me the average rating of all Books that were written by X and one other author. -#(Aggregate over objects discovered using membership of the m2m set) - -# Adding an existing author to another book to test it the right way ->>> a = Author.objects.get(name__contains='Norvig') ->>> b = Book.objects.get(name__contains='Done Right') ->>> b.authors.add(a) ->>> b.save() - -# This should do it ->>> Book.objects.all().annotate(num_authors=Count('authors__id')).filter(authors__name__contains='Norvig', num_authors__gt=1).aggregate(Avg('rating')) -{'rating__avg': 4.25} ->>> b.authors.remove(a) - -# Give me a list of all Authors that have published a book with at least one other person -# (Filters over a count generated on a related object) -# -# Cheating: [a for a in Author.objects.all().annotate(num_coleagues=Count('book__authors__id'), num_books=Count('book__id', distinct=True)) if a.num_coleagues - a.num_books > 0] -# F-Syntax is required. Will be fixed after F objects are available - -# Aggregates also work on dates, times and datetimes ->>> Publisher.objects.annotate(earliest_book=Min('book__pubdate')).exclude(earliest_book=None).order_by('earliest_book').values() -[{'earliest_book': datetime.date(1991, 10, 15), 'num_awards': 9, 'id': 4, 'name': u'Morgan Kaufmann'}, {'earliest_book': datetime.date(1995, 1, 15), 'num_awards': 7, 'id': 3, 'name': u'Prentice Hall'}, {'earliest_book': datetime.date(2007, 12, 6), 'num_awards': 3, 'id': 1, 'name': u'Apress'}, {'earliest_book': datetime.date(2008, 3, 3), 'num_awards': 1, 'id': 2, 'name': u'Sams'}] - ->>> Store.objects.aggregate(Max('friday_night_closing'), Min("original_opening")) -{'friday_night_closing__max': datetime.time(23, 59, 59), 'original_opening__min': datetime.datetime(1945, 4, 25, 16, 24, 14)} - -# values_list() can also be used - ->>> Book.objects.filter(pk=1).annotate(mean_age=Avg('authors__age')).values_list('pk', 'isbn', 'mean_age') -[(1, u'159059725', 34.5)] - ->>> Book.objects.filter(pk=1).annotate(mean_age=Avg('authors__age')).values_list('isbn') -[(u'159059725',)] - ->>> Book.objects.filter(pk=1).annotate(mean_age=Avg('authors__age')).values_list('mean_age') -[(34.5,)] - ->>> Book.objects.filter(pk=1).annotate(mean_age=Avg('authors__age')).values_list('mean_age', flat=True) -[34.5] - ->>> qs = Book.objects.values_list('price').annotate(count=Count('price')).order_by('-count', 'price') ->>> list(qs) == [(Decimal('29.69'), 2), (Decimal('23.09'), 1), (Decimal('30'), 1), (Decimal('75'), 1), (Decimal('82.8'), 1)] -True - -"""} diff --git a/tests/modeltests/aggregation/tests.py b/tests/modeltests/aggregation/tests.py new file mode 100644 index 0000000000..c830368b9d --- /dev/null +++ b/tests/modeltests/aggregation/tests.py @@ -0,0 +1,565 @@ +import datetime +from decimal import Decimal + +from django.db.models import Avg, Sum, Count, Max, Min +from django.test import TestCase, Approximate + +from models import Author, Publisher, Book, Store + + +class BaseAggregateTestCase(TestCase): + fixtures = ["initial_data.json"] + + def test_empty_aggregate(self): + self.assertEqual(Author.objects.all().aggregate(), {}) + + def test_single_aggregate(self): + vals = Author.objects.aggregate(Avg("age")) + self.assertEqual(vals, {"age__avg": Approximate(37.4, places=1)}) + + def test_multiple_aggregates(self): + vals = Author.objects.aggregate(Sum("age"), Avg("age")) + self.assertEqual(vals, {"age__sum": 337, "age__avg": Approximate(37.4, places=1)}) + + def test_filter_aggregate(self): + vals = Author.objects.filter(age__gt=29).aggregate(Sum("age")) + self.assertEqual(len(vals), 1) + self.assertEqual(vals["age__sum"], 254) + + def test_related_aggregate(self): + vals = Author.objects.aggregate(Avg("friends__age")) + self.assertEqual(len(vals), 1) + self.assertAlmostEqual(vals["friends__age__avg"], 34.07, places=2) + + vals = Book.objects.filter(rating__lt=4.5).aggregate(Avg("authors__age")) + self.assertEqual(len(vals), 1) + self.assertAlmostEqual(vals["authors__age__avg"], 38.2857, places=2) + + vals = Author.objects.all().filter(name__contains="a").aggregate(Avg("book__rating")) + self.assertEqual(len(vals), 1) + self.assertEqual(vals["book__rating__avg"], 4.0) + + vals = Book.objects.aggregate(Sum("publisher__num_awards")) + self.assertEqual(len(vals), 1) + self.assertEquals(vals["publisher__num_awards__sum"], 30) + + vals = Publisher.objects.aggregate(Sum("book__price")) + self.assertEqual(len(vals), 1) + self.assertEqual(vals["book__price__sum"], Decimal("270.27")) + + def test_aggregate_multi_join(self): + vals = Store.objects.aggregate(Max("books__authors__age")) + self.assertEqual(len(vals), 1) + self.assertEqual(vals["books__authors__age__max"], 57) + + vals = Author.objects.aggregate(Min("book__publisher__num_awards")) + self.assertEqual(len(vals), 1) + self.assertEqual(vals["book__publisher__num_awards__min"], 1) + + def test_aggregate_alias(self): + vals = Store.objects.filter(name="Amazon.com").aggregate(amazon_mean=Avg("books__rating")) + self.assertEqual(len(vals), 1) + self.assertAlmostEqual(vals["amazon_mean"], 4.08, places=2) + + def test_annotate_basic(self): + self.assertQuerysetEqual( + Book.objects.annotate().order_by('pk'), [ + "The Definitive Guide to Django: Web Development Done Right", + "Sams Teach Yourself Django in 24 Hours", + "Practical Django Projects", + "Python Web Development with Django", + "Artificial Intelligence: A Modern Approach", + "Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp" + ], + lambda b: b.name + ) + + books = Book.objects.annotate(mean_age=Avg("authors__age")) + b = books.get(pk=1) + self.assertEqual( + b.name, + u'The Definitive Guide to Django: Web Development Done Right' + ) + self.assertEqual(b.mean_age, 34.5) + + def test_annotate_m2m(self): + books = Book.objects.filter(rating__lt=4.5).annotate(Avg("authors__age")).order_by("name") + self.assertQuerysetEqual( + books, [ + (u'Artificial Intelligence: A Modern Approach', 51.5), + (u'Practical Django Projects', 29.0), + (u'Python Web Development with Django', Approximate(30.3, places=1)), + (u'Sams Teach Yourself Django in 24 Hours', 45.0) + ], + lambda b: (b.name, b.authors__age__avg), + ) + + books = Book.objects.annotate(num_authors=Count("authors")).order_by("name") + self.assertQuerysetEqual( + books, [ + (u'Artificial Intelligence: A Modern Approach', 2), + (u'Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp', 1), + (u'Practical Django Projects', 1), + (u'Python Web Development with Django', 3), + (u'Sams Teach Yourself Django in 24 Hours', 1), + (u'The Definitive Guide to Django: Web Development Done Right', 2) + ], + lambda b: (b.name, b.num_authors) + ) + + def test_backwards_m2m_annotate(self): + authors = Author.objects.filter(name__contains="a").annotate(Avg("book__rating")).order_by("name") + self.assertQuerysetEqual( + authors, [ + (u'Adrian Holovaty', 4.5), + (u'Brad Dayley', 3.0), + (u'Jacob Kaplan-Moss', 4.5), + (u'James Bennett', 4.0), + (u'Paul Bissex', 4.0), + (u'Stuart Russell', 4.0) + ], + lambda a: (a.name, a.book__rating__avg) + ) + + authors = Author.objects.annotate(num_books=Count("book")).order_by("name") + self.assertQuerysetEqual( + authors, [ + (u'Adrian Holovaty', 1), + (u'Brad Dayley', 1), + (u'Jacob Kaplan-Moss', 1), + (u'James Bennett', 1), + (u'Jeffrey Forcier', 1), + (u'Paul Bissex', 1), + (u'Peter Norvig', 2), + (u'Stuart Russell', 1), + (u'Wesley J. Chun', 1) + ], + lambda a: (a.name, a.num_books) + ) + + def test_reverse_fkey_annotate(self): + books = Book.objects.annotate(Sum("publisher__num_awards")).order_by("name") + self.assertQuerysetEqual( + books, [ + (u'Artificial Intelligence: A Modern Approach', 7), + (u'Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp', 9), + (u'Practical Django Projects', 3), + (u'Python Web Development with Django', 7), + (u'Sams Teach Yourself Django in 24 Hours', 1), + (u'The Definitive Guide to Django: Web Development Done Right', 3) + ], + lambda b: (b.name, b.publisher__num_awards__sum) + ) + + publishers = Publisher.objects.annotate(Sum("book__price")).order_by("name") + self.assertQuerysetEqual( + publishers, [ + (u'Apress', Decimal("59.69")), + (u"Jonno's House of Books", None), + (u'Morgan Kaufmann', Decimal("75.00")), + (u'Prentice Hall', Decimal("112.49")), + (u'Sams', Decimal("23.09")) + ], + lambda p: (p.name, p.book__price__sum) + ) + + def test_annotate_values(self): + books = list(Book.objects.filter(pk=1).annotate(mean_age=Avg("authors__age")).values()) + self.assertEqual( + books, [ + { + "contact_id": 1, + "id": 1, + "isbn": "159059725", + "mean_age": 34.5, + "name": "The Definitive Guide to Django: Web Development Done Right", + "pages": 447, + "price": Approximate(Decimal("30")), + "pubdate": datetime.date(2007, 12, 6), + "publisher_id": 1, + "rating": 4.5, + } + ] + ) + + books = Book.objects.filter(pk=1).annotate(mean_age=Avg('authors__age')).values('pk', 'isbn', 'mean_age') + self.assertEqual( + list(books), [ + { + "pk": 1, + "isbn": "159059725", + "mean_age": 34.5, + } + ] + ) + + books = Book.objects.filter(pk=1).annotate(mean_age=Avg("authors__age")).values("name") + self.assertEqual( + list(books), [ + { + "name": "The Definitive Guide to Django: Web Development Done Right" + } + ] + ) + + books = Book.objects.filter(pk=1).values().annotate(mean_age=Avg('authors__age')) + self.assertEqual( + list(books), [ + { + "contact_id": 1, + "id": 1, + "isbn": "159059725", + "mean_age": 34.5, + "name": "The Definitive Guide to Django: Web Development Done Right", + "pages": 447, + "price": Approximate(Decimal("30")), + "pubdate": datetime.date(2007, 12, 6), + "publisher_id": 1, + "rating": 4.5, + } + ] + ) + + books = Book.objects.values("rating").annotate(n_authors=Count("authors__id"), mean_age=Avg("authors__age")).order_by("rating") + self.assertEqual( + list(books), [ + { + "rating": 3.0, + "n_authors": 1, + "mean_age": 45.0, + }, + { + "rating": 4.0, + "n_authors": 6, + "mean_age": Approximate(37.16, places=1) + }, + { + "rating": 4.5, + "n_authors": 2, + "mean_age": 34.5, + }, + { + "rating": 5.0, + "n_authors": 1, + "mean_age": 57.0, + } + ] + ) + + authors = Author.objects.annotate(Avg("friends__age")).order_by("name") + self.assertEqual(len(authors), 9) + self.assertQuerysetEqual( + authors, [ + (u'Adrian Holovaty', 32.0), + (u'Brad Dayley', None), + (u'Jacob Kaplan-Moss', 29.5), + (u'James Bennett', 34.0), + (u'Jeffrey Forcier', 27.0), + (u'Paul Bissex', 31.0), + (u'Peter Norvig', 46.0), + (u'Stuart Russell', 57.0), + (u'Wesley J. Chun', Approximate(33.66, places=1)) + ], + lambda a: (a.name, a.friends__age__avg) + ) + + def test_count(self): + vals = Book.objects.aggregate(Count("rating")) + self.assertEqual(vals, {"rating__count": 6}) + + vals = Book.objects.aggregate(Count("rating", distinct=True)) + self.assertEqual(vals, {"rating__count": 4}) + + def test_fkey_aggregate(self): + explicit = list(Author.objects.annotate(Count('book__id'))) + implicit = list(Author.objects.annotate(Count('book'))) + self.assertEqual(explicit, implicit) + + def test_annotate_ordering(self): + books = Book.objects.values('rating').annotate(oldest=Max('authors__age')).order_by('oldest', 'rating') + self.assertEqual( + list(books), [ + { + "rating": 4.5, + "oldest": 35, + }, + { + "rating": 3.0, + "oldest": 45 + }, + { + "rating": 4.0, + "oldest": 57, + }, + { + "rating": 5.0, + "oldest": 57, + } + ] + ) + + books = Book.objects.values("rating").annotate(oldest=Max("authors__age")).order_by("-oldest", "-rating") + self.assertEqual( + list(books), [ + { + "rating": 5.0, + "oldest": 57, + }, + { + "rating": 4.0, + "oldest": 57, + }, + { + "rating": 3.0, + "oldest": 45, + }, + { + "rating": 4.5, + "oldest": 35, + } + ] + ) + + def test_aggregate_annotation(self): + vals = Book.objects.annotate(num_authors=Count("authors__id")).aggregate(Avg("num_authors")) + self.assertEqual(vals, {"num_authors__avg": Approximate(1.66, places=1)}) + + def test_filtering(self): + p = Publisher.objects.create(name='Expensive Publisher', num_awards=0) + Book.objects.create( + name='ExpensiveBook1', + pages=1, + isbn='111', + rating=3.5, + price=Decimal("1000"), + publisher=p, + contact_id=1, + pubdate=datetime.date(2008,12,1) + ) + Book.objects.create( + name='ExpensiveBook2', + pages=1, + isbn='222', + rating=4.0, + price=Decimal("1000"), + publisher=p, + contact_id=1, + pubdate=datetime.date(2008,12,2) + ) + Book.objects.create( + name='ExpensiveBook3', + pages=1, + isbn='333', + rating=4.5, + price=Decimal("35"), + publisher=p, + contact_id=1, + pubdate=datetime.date(2008,12,3) + ) + + publishers = Publisher.objects.annotate(num_books=Count("book__id")).filter(num_books__gt=1).order_by("pk") + self.assertQuerysetEqual( + publishers, [ + "Apress", + "Prentice Hall", + "Expensive Publisher", + ], + lambda p: p.name, + ) + + publishers = Publisher.objects.filter(book__price__lt=Decimal("40.0")).order_by("pk") + self.assertQuerysetEqual( + publishers, [ + "Apress", + "Apress", + "Sams", + "Prentice Hall", + "Expensive Publisher", + ], + lambda p: p.name + ) + + publishers = Publisher.objects.annotate(num_books=Count("book__id")).filter(num_books__gt=1, book__price__lt=Decimal("40.0")).order_by("pk") + self.assertQuerysetEqual( + publishers, [ + "Apress", + "Prentice Hall", + "Expensive Publisher", + ], + lambda p: p.name, + ) + + publishers = Publisher.objects.filter(book__price__lt=Decimal("40.0")).annotate(num_books=Count("book__id")).filter(num_books__gt=1).order_by("pk") + self.assertQuerysetEqual( + publishers, [ + "Apress", + ], + lambda p: p.name + ) + + publishers = Publisher.objects.annotate(num_books=Count("book")).filter(num_books__range=[1, 3]).order_by("pk") + self.assertQuerysetEqual( + publishers, [ + "Apress", + "Sams", + "Prentice Hall", + "Morgan Kaufmann", + "Expensive Publisher", + ], + lambda p: p.name + ) + + publishers = Publisher.objects.annotate(num_books=Count("book")).filter(num_books__range=[1, 2]).order_by("pk") + self.assertQuerysetEqual( + publishers, [ + "Apress", + "Sams", + "Prentice Hall", + "Morgan Kaufmann", + ], + lambda p: p.name + ) + + publishers = Publisher.objects.annotate(num_books=Count("book")).filter(num_books__in=[1, 3]).order_by("pk") + self.assertQuerysetEqual( + publishers, [ + "Sams", + "Morgan Kaufmann", + "Expensive Publisher", + ], + lambda p: p.name, + ) + + publishers = Publisher.objects.annotate(num_books=Count("book")).filter(num_books__isnull=True) + self.assertEqual(len(publishers), 0) + + def test_annotation(self): + vals = Author.objects.filter(pk=1).aggregate(Count("friends__id")) + self.assertEqual(vals, {"friends__id__count": 2}) + + books = Book.objects.annotate(num_authors=Count("authors__name")).filter(num_authors__ge=2).order_by("pk") + self.assertQuerysetEqual( + books, [ + "The Definitive Guide to Django: Web Development Done Right", + "Artificial Intelligence: A Modern Approach", + ], + lambda b: b.name + ) + + authors = Author.objects.annotate(num_friends=Count("friends__id", distinct=True)).filter(num_friends=0).order_by("pk") + self.assertQuerysetEqual( + authors, [ + "Brad Dayley", + ], + lambda a: a.name + ) + + publishers = Publisher.objects.annotate(num_books=Count("book__id")).filter(num_books__gt=1).order_by("pk") + self.assertQuerysetEqual( + publishers, [ + "Apress", + "Prentice Hall", + ], + lambda p: p.name + ) + + publishers = Publisher.objects.filter(book__price__lt=Decimal("40.0")).annotate(num_books=Count("book__id")).filter(num_books__gt=1) + self.assertQuerysetEqual( + publishers, [ + "Apress", + ], + lambda p: p.name + ) + + books = Book.objects.annotate(num_authors=Count("authors__id")).filter(authors__name__contains="Norvig", num_authors__gt=1) + self.assertQuerysetEqual( + books, [ + "Artificial Intelligence: A Modern Approach", + ], + lambda b: b.name + ) + + def test_more_aggregation(self): + a = Author.objects.get(name__contains='Norvig') + b = Book.objects.get(name__contains='Done Right') + b.authors.add(a) + b.save() + + vals = Book.objects.annotate(num_authors=Count("authors__id")).filter(authors__name__contains="Norvig", num_authors__gt=1).aggregate(Avg("rating")) + self.assertEqual(vals, {"rating__avg": 4.25}) + + def test_even_more_aggregate(self): + publishers = Publisher.objects.annotate(earliest_book=Min("book__pubdate")).exclude(earliest_book=None).order_by("earliest_book").values() + self.assertEqual( + list(publishers), [ + { + 'earliest_book': datetime.date(1991, 10, 15), + 'num_awards': 9, + 'id': 4, + 'name': u'Morgan Kaufmann' + }, + { + 'earliest_book': datetime.date(1995, 1, 15), + 'num_awards': 7, + 'id': 3, + 'name': u'Prentice Hall' + }, + { + 'earliest_book': datetime.date(2007, 12, 6), + 'num_awards': 3, + 'id': 1, + 'name': u'Apress' + }, + { + 'earliest_book': datetime.date(2008, 3, 3), + 'num_awards': 1, + 'id': 2, + 'name': u'Sams' + } + ] + ) + + vals = Store.objects.aggregate(Max("friday_night_closing"), Min("original_opening")) + self.assertEqual( + vals, + { + "friday_night_closing__max": datetime.time(23, 59, 59), + "original_opening__min": datetime.datetime(1945, 4, 25, 16, 24, 14), + } + ) + + def test_annotate_values_list(self): + books = Book.objects.filter(pk=1).annotate(mean_age=Avg("authors__age")).values_list("pk", "isbn", "mean_age") + self.assertEqual( + list(books), [ + (1, "159059725", 34.5), + ] + ) + + books = Book.objects.filter(pk=1).annotate(mean_age=Avg("authors__age")).values_list("isbn") + self.assertEqual( + list(books), [ + ('159059725',) + ] + ) + + books = Book.objects.filter(pk=1).annotate(mean_age=Avg("authors__age")).values_list("mean_age") + self.assertEqual( + list(books), [ + (34.5,) + ] + ) + + books = Book.objects.filter(pk=1).annotate(mean_age=Avg("authors__age")).values_list("mean_age", flat=True) + self.assertEqual(list(books), [34.5]) + + books = Book.objects.values_list("price").annotate(count=Count("price")).order_by("-count", "price") + self.assertEqual( + list(books), [ + (Decimal("29.69"), 2), + (Decimal('23.09'), 1), + (Decimal('30'), 1), + (Decimal('75'), 1), + (Decimal('82.8'), 1), + ] + ) diff --git a/tests/modeltests/choices/models.py b/tests/modeltests/choices/models.py index e378260598..27316f5dea 100644 --- a/tests/modeltests/choices/models.py +++ b/tests/modeltests/choices/models.py @@ -22,29 +22,3 @@ class Person(models.Model): def __unicode__(self): return self.name - -__test__ = {'API_TESTS':""" ->>> a = Person(name='Adrian', gender='M') ->>> a.save() ->>> s = Person(name='Sara', gender='F') ->>> s.save() ->>> a.gender -'M' ->>> s.gender -'F' ->>> a.get_gender_display() -u'Male' ->>> s.get_gender_display() -u'Female' - -# If the value for the field doesn't correspond to a valid choice, -# the value itself is provided as a display value. ->>> a.gender = '' ->>> a.get_gender_display() -u'' - ->>> a.gender = 'U' ->>> a.get_gender_display() -u'U' - -"""} diff --git a/tests/modeltests/choices/tests.py b/tests/modeltests/choices/tests.py new file mode 100644 index 0000000000..09023d8113 --- /dev/null +++ b/tests/modeltests/choices/tests.py @@ -0,0 +1,23 @@ +from django.test import TestCase + +from models import Person + + +class ChoicesTests(TestCase): + def test_display(self): + a = Person.objects.create(name='Adrian', gender='M') + s = Person.objects.create(name='Sara', gender='F') + self.assertEqual(a.gender, 'M') + self.assertEqual(s.gender, 'F') + + self.assertEqual(a.get_gender_display(), 'Male') + self.assertEqual(s.get_gender_display(), 'Female') + + # If the value for the field doesn't correspond to a valid choice, + # the value itself is provided as a display value. + a.gender = '' + self.assertEqual(a.get_gender_display(), '') + + a.gender = 'U' + self.assertEqual(a.get_gender_display(), 'U') + diff --git a/tests/modeltests/custom_columns/models.py b/tests/modeltests/custom_columns/models.py index 74691cd1bc..651f8a61b2 100644 --- a/tests/modeltests/custom_columns/models.py +++ b/tests/modeltests/custom_columns/models.py @@ -38,68 +38,3 @@ class Article(models.Model): class Meta: ordering = ('headline',) -__test__ = {'API_TESTS':""" -# Create a Author. ->>> a = Author(first_name='John', last_name='Smith') ->>> a.save() - ->>> a.id -1 - -# Create another author ->>> a2 = Author(first_name='Peter', last_name='Jones') ->>> a2.save() - -# Create an article ->>> art = Article(headline='Django lets you build web apps easily') ->>> art.save() ->>> art.authors = [a, a2] - -# Although the table and column names on Author have been set to custom values, -# nothing about using the Author model has changed... - -# Query the available authors ->>> Author.objects.all() -[<Author: Peter Jones>, <Author: John Smith>] - ->>> Author.objects.filter(first_name__exact='John') -[<Author: John Smith>] - ->>> Author.objects.get(first_name__exact='John') -<Author: John Smith> - ->>> Author.objects.filter(firstname__exact='John') -Traceback (most recent call last): - ... -FieldError: Cannot resolve keyword 'firstname' into field. Choices are: article, first_name, id, last_name - ->>> a = Author.objects.get(last_name__exact='Smith') ->>> a.first_name -u'John' ->>> a.last_name -u'Smith' ->>> a.firstname -Traceback (most recent call last): - ... -AttributeError: 'Author' object has no attribute 'firstname' ->>> a.last -Traceback (most recent call last): - ... -AttributeError: 'Author' object has no attribute 'last' - -# Although the Article table uses a custom m2m table, -# nothing about using the m2m relationship has changed... - -# Get all the authors for an article ->>> art.authors.all() -[<Author: Peter Jones>, <Author: John Smith>] - -# Get the articles for an author ->>> a.article_set.all() -[<Article: Django lets you build web apps easily>] - -# Query the authors across the m2m relation ->>> art.authors.filter(last_name='Jones') -[<Author: Peter Jones>] - -"""} diff --git a/tests/modeltests/custom_columns/tests.py b/tests/modeltests/custom_columns/tests.py new file mode 100644 index 0000000000..e8ec21f06f --- /dev/null +++ b/tests/modeltests/custom_columns/tests.py @@ -0,0 +1,71 @@ +from django.core.exceptions import FieldError +from django.test import TestCase + +from models import Author, Article + + +class CustomColumnsTests(TestCase): + def test_db_column(self): + a1 = Author.objects.create(first_name="John", last_name="Smith") + a2 = Author.objects.create(first_name="Peter", last_name="Jones") + + art = Article.objects.create(headline="Django lets you build web apps easily") + art.authors = [a1, a2] + + # Although the table and column names on Author have been set to custom + # values, nothing about using the Author model has changed... + + # Query the available authors + self.assertQuerysetEqual( + Author.objects.all(), [ + "Peter Jones", "John Smith", + ], + unicode + ) + self.assertQuerysetEqual( + Author.objects.filter(first_name__exact="John"), [ + "John Smith", + ], + unicode + ) + self.assertEqual( + Author.objects.get(first_name__exact="John"), + a1, + ) + + self.assertRaises(FieldError, + lambda: Author.objects.filter(firstname__exact="John") + ) + + a = Author.objects.get(last_name__exact="Smith") + a.first_name = "John" + a.last_name = "Smith" + + self.assertRaises(AttributeError, lambda: a.firstname) + self.assertRaises(AttributeError, lambda: a.last) + + # Although the Article table uses a custom m2m table, + # nothing about using the m2m relationship has changed... + + # Get all the authors for an article + self.assertQuerysetEqual( + art.authors.all(), [ + "Peter Jones", + "John Smith", + ], + unicode + ) + # Get the articles for an author + self.assertQuerysetEqual( + a.article_set.all(), [ + "Django lets you build web apps easily", + ], + lambda a: a.headline + ) + # Query the authors across the m2m relation + self.assertQuerysetEqual( + art.authors.filter(last_name='Jones'), [ + "Peter Jones" + ], + unicode + ) diff --git a/tests/modeltests/custom_managers/models.py b/tests/modeltests/custom_managers/models.py index 40bf77e273..1052552bb3 100644 --- a/tests/modeltests/custom_managers/models.py +++ b/tests/modeltests/custom_managers/models.py @@ -57,51 +57,3 @@ class Car(models.Model): def __unicode__(self): return self.name - -__test__ = {'API_TESTS':""" ->>> p1 = Person(first_name='Bugs', last_name='Bunny', fun=True) ->>> p1.save() ->>> p2 = Person(first_name='Droopy', last_name='Dog', fun=False) ->>> p2.save() ->>> Person.objects.get_fun_people() -[<Person: Bugs Bunny>] - -# The RelatedManager used on the 'books' descriptor extends the default manager ->>> from modeltests.custom_managers.models import PublishedBookManager ->>> isinstance(p2.books, PublishedBookManager) -True - ->>> b1 = Book(title='How to program', author='Rodney Dangerfield', is_published=True) ->>> b1.save() ->>> b2 = Book(title='How to be smart', author='Albert Einstein', is_published=False) ->>> b2.save() - -# The default manager, "objects", doesn't exist, -# because a custom one was provided. ->>> Book.objects -Traceback (most recent call last): - ... -AttributeError: type object 'Book' has no attribute 'objects' - -# The RelatedManager used on the 'authors' descriptor extends the default manager ->>> from modeltests.custom_managers.models import PersonManager ->>> isinstance(b2.authors, PersonManager) -True - ->>> Book.published_objects.all() -[<Book: How to program>] - ->>> c1 = Car(name='Corvette', mileage=21, top_speed=180) ->>> c1.save() ->>> c2 = Car(name='Neon', mileage=31, top_speed=100) ->>> c2.save() ->>> Car.cars.order_by('name') -[<Car: Corvette>, <Car: Neon>] ->>> Car.fast_cars.all() -[<Car: Corvette>] - -# Each model class gets a "_default_manager" attribute, which is a reference -# to the first manager defined in the class. In this case, it's "cars". ->>> Car._default_manager.order_by('name') -[<Car: Corvette>, <Car: Neon>] -"""} diff --git a/tests/modeltests/custom_managers/tests.py b/tests/modeltests/custom_managers/tests.py new file mode 100644 index 0000000000..8721e9ac52 --- /dev/null +++ b/tests/modeltests/custom_managers/tests.py @@ -0,0 +1,71 @@ +from django.test import TestCase + +from models import Person, Book, Car, PersonManager, PublishedBookManager + + +class CustomManagerTests(TestCase): + def test_manager(self): + p1 = Person.objects.create(first_name="Bugs", last_name="Bunny", fun=True) + p2 = Person.objects.create(first_name="Droopy", last_name="Dog", fun=False) + + self.assertQuerysetEqual( + Person.objects.get_fun_people(), [ + "Bugs Bunny" + ], + unicode + ) + # The RelatedManager used on the 'books' descriptor extends the default + # manager + self.assertTrue(isinstance(p2.books, PublishedBookManager)) + + b1 = Book.published_objects.create( + title="How to program", author="Rodney Dangerfield", is_published=True + ) + b2 = Book.published_objects.create( + title="How to be smart", author="Albert Einstein", is_published=False + ) + + # The default manager, "objects", doesn't exist, because a custom one + # was provided. + self.assertRaises(AttributeError, lambda: Book.objects) + + # The RelatedManager used on the 'authors' descriptor extends the + # default manager + self.assertTrue(isinstance(b2.authors, PersonManager)) + + self.assertQuerysetEqual( + Book.published_objects.all(), [ + "How to program", + ], + lambda b: b.title + ) + + c1 = Car.cars.create(name="Corvette", mileage=21, top_speed=180) + c2 = Car.cars.create(name="Neon", mileage=31, top_speed=100) + + self.assertQuerysetEqual( + Car.cars.order_by("name"), [ + "Corvette", + "Neon", + ], + lambda c: c.name + ) + + self.assertQuerysetEqual( + Car.fast_cars.all(), [ + "Corvette", + ], + lambda c: c.name + ) + + # Each model class gets a "_default_manager" attribute, which is a + # reference to the first manager defined in the class. In this case, + # it's "cars". + + self.assertQuerysetEqual( + Car._default_manager.order_by("name"), [ + "Corvette", + "Neon", + ], + lambda c: c.name + ) diff --git a/tests/modeltests/custom_methods/models.py b/tests/modeltests/custom_methods/models.py index d420871373..15150a6c3f 100644 --- a/tests/modeltests/custom_methods/models.py +++ b/tests/modeltests/custom_methods/models.py @@ -33,27 +33,4 @@ class Article(models.Model): WHERE pub_date = %s AND id != %s""", [connection.ops.value_to_db_date(self.pub_date), self.id]) - # The asterisk in "(*row)" tells Python to expand the list into - # positional arguments to Article(). return [self.__class__(*row) for row in cursor.fetchall()] - -__test__ = {'API_TESTS':""" -# Create a couple of Articles. ->>> from datetime import date ->>> a = Article(id=None, headline='Area man programs in Python', pub_date=date(2005, 7, 27)) ->>> a.save() ->>> b = Article(id=None, headline='Beatles reunite', pub_date=date(2005, 7, 27)) ->>> b.save() - -# Test the custom methods. ->>> a.was_published_today() -False ->>> a.articles_from_same_day_1() -[<Article: Beatles reunite>] ->>> a.articles_from_same_day_2() -[<Article: Beatles reunite>] ->>> b.articles_from_same_day_1() -[<Article: Area man programs in Python>] ->>> b.articles_from_same_day_2() -[<Article: Area man programs in Python>] -"""} diff --git a/tests/modeltests/custom_methods/tests.py b/tests/modeltests/custom_methods/tests.py new file mode 100644 index 0000000000..90a7f0da29 --- /dev/null +++ b/tests/modeltests/custom_methods/tests.py @@ -0,0 +1,42 @@ +from datetime import date + +from django.test import TestCase + +from models import Article + + +class MethodsTests(TestCase): + def test_custom_methods(self): + a = Article.objects.create( + headline="Area man programs in Python", pub_date=date(2005, 7, 27) + ) + b = Article.objects.create( + headline="Beatles reunite", pub_date=date(2005, 7, 27) + ) + + self.assertFalse(a.was_published_today()) + self.assertQuerysetEqual( + a.articles_from_same_day_1(), [ + "Beatles reunite", + ], + lambda a: a.headline, + ) + self.assertQuerysetEqual( + a.articles_from_same_day_2(), [ + "Beatles reunite", + ], + lambda a: a.headline + ) + + self.assertQuerysetEqual( + b.articles_from_same_day_1(), [ + "Area man programs in Python", + ], + lambda a: a.headline, + ) + self.assertQuerysetEqual( + b.articles_from_same_day_2(), [ + "Area man programs in Python", + ], + lambda a: a.headline + ) diff --git a/tests/modeltests/custom_pk/fields.py b/tests/modeltests/custom_pk/fields.py index 319e42f974..2eeb80e6ac 100644 --- a/tests/modeltests/custom_pk/fields.py +++ b/tests/modeltests/custom_pk/fields.py @@ -3,6 +3,7 @@ import string from django.db import models + class MyWrapper(object): def __init__(self, value): self.value = value diff --git a/tests/modeltests/custom_pk/models.py b/tests/modeltests/custom_pk/models.py index 84e6480af9..ff2f2bac1b 100644 --- a/tests/modeltests/custom_pk/models.py +++ b/tests/modeltests/custom_pk/models.py @@ -40,138 +40,3 @@ class Bar(models.Model): class Foo(models.Model): bar = models.ForeignKey(Bar) -__test__ = {'API_TESTS':""" ->>> dan = Employee(employee_code=123, first_name='Dan', last_name='Jones') ->>> dan.save() ->>> Employee.objects.all() -[<Employee: Dan Jones>] - ->>> fran = Employee(employee_code=456, first_name='Fran', last_name='Bones') ->>> fran.save() ->>> Employee.objects.all() -[<Employee: Fran Bones>, <Employee: Dan Jones>] - ->>> Employee.objects.get(pk=123) -<Employee: Dan Jones> ->>> Employee.objects.get(pk=456) -<Employee: Fran Bones> ->>> Employee.objects.get(pk=42) -Traceback (most recent call last): - ... -DoesNotExist: Employee matching query does not exist. - -# Use the name of the primary key, rather than pk. ->>> Employee.objects.get(employee_code__exact=123) -<Employee: Dan Jones> - -# pk can be used as a substitute for the primary key. ->>> Employee.objects.filter(pk__in=[123, 456]) -[<Employee: Fran Bones>, <Employee: Dan Jones>] - -# The primary key can be accessed via the pk property on the model. ->>> e = Employee.objects.get(pk=123) ->>> e.pk -123 - -# Or we can use the real attribute name for the primary key: ->>> e.employee_code -123 - -# Fran got married and changed her last name. ->>> fran = Employee.objects.get(pk=456) ->>> fran.last_name = 'Jones' ->>> fran.save() ->>> Employee.objects.filter(last_name__exact='Jones') -[<Employee: Dan Jones>, <Employee: Fran Jones>] ->>> emps = Employee.objects.in_bulk([123, 456]) ->>> emps[123] -<Employee: Dan Jones> - ->>> b = Business(name='Sears') ->>> b.save() ->>> b.employees.add(dan, fran) ->>> b.employees.all() -[<Employee: Dan Jones>, <Employee: Fran Jones>] ->>> fran.business_set.all() -[<Business: Sears>] ->>> Business.objects.in_bulk(['Sears']) -{u'Sears': <Business: Sears>} - ->>> Business.objects.filter(name__exact='Sears') -[<Business: Sears>] ->>> Business.objects.filter(pk='Sears') -[<Business: Sears>] - -# Queries across tables, involving primary key ->>> Employee.objects.filter(business__name__exact='Sears') -[<Employee: Dan Jones>, <Employee: Fran Jones>] ->>> Employee.objects.filter(business__pk='Sears') -[<Employee: Dan Jones>, <Employee: Fran Jones>] - ->>> Business.objects.filter(employees__employee_code__exact=123) -[<Business: Sears>] ->>> Business.objects.filter(employees__pk=123) -[<Business: Sears>] ->>> Business.objects.filter(employees__first_name__startswith='Fran') -[<Business: Sears>] - -# Primary key may be unicode string ->>> bus = Business(name=u'jaźń') ->>> bus.save() - -# The primary key must also obviously be unique, so trying to create a new -# object with the same primary key will fail. ->>> try: -... sid = transaction.savepoint() -... Employee.objects.create(employee_code=123, first_name='Fred', last_name='Jones') -... transaction.savepoint_commit(sid) -... except Exception, e: -... if isinstance(e, IntegrityError): -... transaction.savepoint_rollback(sid) -... print "Pass" -... else: -... print "Fail with %s" % type(e) -Pass - -# Regression for #10785 -- Custom fields can be used for primary keys. ->>> new_bar = Bar.objects.create() ->>> new_foo = Foo.objects.create(bar=new_bar) - -# FIXME: This still doesn't work, but will require some changes in -# get_db_prep_lookup to fix it. -# >>> f = Foo.objects.get(bar=new_bar.pk) -# >>> f == new_foo -# True -# >>> f.bar == new_bar -# True - ->>> f = Foo.objects.get(bar=new_bar) ->>> f == new_foo -True ->>> f.bar == new_bar -True - -"""} - -# SQLite lets objects be saved with an empty primary key, even though an -# integer is expected. So we can't check for an error being raised in that case -# for SQLite. Remove it from the suite for this next bit. -if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.sqlite3': - __test__["API_TESTS"] += """ -# The primary key must be specified, so an error is raised if you try to create -# an object without it. ->>> try: -... sid = transaction.savepoint() -... Employee.objects.create(first_name='Tom', last_name='Smith') -... print 'hello' -... transaction.savepoint_commit(sid) -... print 'hello2' -... except Exception, e: -... if isinstance(e, IntegrityError): -... transaction.savepoint_rollback(sid) -... print "Pass" -... else: -... print "Fail with %s" % type(e) -Pass - -""" diff --git a/tests/modeltests/custom_pk/tests.py b/tests/modeltests/custom_pk/tests.py new file mode 100644 index 0000000000..6ef4bdd433 --- /dev/null +++ b/tests/modeltests/custom_pk/tests.py @@ -0,0 +1,183 @@ +# -*- coding: utf-8 -*- +from django.conf import settings +from django.db import DEFAULT_DB_ALIAS, transaction, IntegrityError +from django.test import TestCase + +from models import Employee, Business, Bar, Foo + + +class CustomPKTests(TestCase): + def test_custom_pk(self): + dan = Employee.objects.create( + employee_code=123, first_name="Dan", last_name="Jones" + ) + self.assertQuerysetEqual( + Employee.objects.all(), [ + "Dan Jones", + ], + unicode + ) + + fran = Employee.objects.create( + employee_code=456, first_name="Fran", last_name="Bones" + ) + self.assertQuerysetEqual( + Employee.objects.all(), [ + "Fran Bones", + "Dan Jones", + ], + unicode + ) + + self.assertEqual(Employee.objects.get(pk=123), dan) + self.assertEqual(Employee.objects.get(pk=456), fran) + + self.assertRaises(Employee.DoesNotExist, + lambda: Employee.objects.get(pk=42) + ) + + # Use the name of the primary key, rather than pk. + self.assertEqual(Employee.objects.get(employee_code=123), dan) + # pk can be used as a substitute for the primary key. + self.assertQuerysetEqual( + Employee.objects.filter(pk__in=[123, 456]), [ + "Fran Bones", + "Dan Jones", + ], + unicode + ) + # The primary key can be accessed via the pk property on the model. + e = Employee.objects.get(pk=123) + self.assertEqual(e.pk, 123) + # Or we can use the real attribute name for the primary key: + self.assertEqual(e.employee_code, 123) + + # Fran got married and changed her last name. + fran = Employee.objects.get(pk=456) + fran.last_name = "Jones" + fran.save() + + self.assertQuerysetEqual( + Employee.objects.filter(last_name="Jones"), [ + "Dan Jones", + "Fran Jones", + ], + unicode + ) + + emps = Employee.objects.in_bulk([123, 456]) + self.assertEqual(emps[123], dan) + + b = Business.objects.create(name="Sears") + b.employees.add(dan, fran) + self.assertQuerysetEqual( + b.employees.all(), [ + "Dan Jones", + "Fran Jones", + ], + unicode + ) + self.assertQuerysetEqual( + fran.business_set.all(), [ + "Sears", + ], + lambda b: b.name + ) + + self.assertEqual(Business.objects.in_bulk(["Sears"]), { + "Sears": b, + }) + + self.assertQuerysetEqual( + Business.objects.filter(name="Sears"), [ + "Sears" + ], + lambda b: b.name + ) + self.assertQuerysetEqual( + Business.objects.filter(pk="Sears"), [ + "Sears", + ], + lambda b: b.name + ) + + # Queries across tables, involving primary key + self.assertQuerysetEqual( + Employee.objects.filter(business__name="Sears"), [ + "Dan Jones", + "Fran Jones", + ], + unicode, + ) + self.assertQuerysetEqual( + Employee.objects.filter(business__pk="Sears"), [ + "Dan Jones", + "Fran Jones", + ], + unicode, + ) + + self.assertQuerysetEqual( + Business.objects.filter(employees__employee_code=123), [ + "Sears", + ], + lambda b: b.name + ) + self.assertQuerysetEqual( + Business.objects.filter(employees__pk=123), [ + "Sears", + ], + lambda b: b.name, + ) + + self.assertQuerysetEqual( + Business.objects.filter(employees__first_name__startswith="Fran"), [ + "Sears", + ], + lambda b: b.name + ) + + def test_unicode_pk(self): + # Primary key may be unicode string + bus = Business.objects.create(name=u'jaźń') + + def test_unique_pk(self): + # The primary key must also obviously be unique, so trying to create a + # new object with the same primary key will fail. + e = Employee.objects.create( + employee_code=123, first_name="Frank", last_name="Jones" + ) + sid = transaction.savepoint() + self.assertRaises(IntegrityError, + Employee.objects.create, employee_code=123, first_name="Fred", last_name="Jones" + ) + transaction.savepoint_rollback(sid) + + def test_custom_field_pk(self): + # Regression for #10785 -- Custom fields can be used for primary keys. + new_bar = Bar.objects.create() + new_foo = Foo.objects.create(bar=new_bar) + + # FIXME: This still doesn't work, but will require some changes in + # get_db_prep_lookup to fix it. + # f = Foo.objects.get(bar=new_bar.pk) + # self.assertEqual(f, new_foo) + # self.assertEqual(f.bar, new_bar) + + f = Foo.objects.get(bar=new_bar) + self.assertEqual(f, new_foo), + self.assertEqual(f.bar, new_bar) + + + # SQLite lets objects be saved with an empty primary key, even though an + # integer is expected. So we can't check for an error being raised in that + # case for SQLite. Remove it from the suite for this next bit. + if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.sqlite3': + def test_required_pk(self): + # The primary key must be specified, so an error is raised if you + # try to create an object without it. + sid = transaction.savepoint() + self.assertRaises(IntegrityError, + Employee.objects.create, first_name="Tom", last_name="Smith" + ) + transaction.savepoint_rollback(sid) diff --git a/tests/modeltests/defer/models.py b/tests/modeltests/defer/models.py index ac3c876a57..4fddd39d26 100644 --- a/tests/modeltests/defer/models.py +++ b/tests/modeltests/defer/models.py @@ -3,7 +3,7 @@ Tests for defer() and only(). """ from django.db import models -from django.db.models.query_utils import DeferredAttribute + class Secondary(models.Model): first = models.CharField(max_length=50) @@ -22,165 +22,3 @@ class Child(Primary): class BigChild(Primary): other = models.CharField(max_length=50) - -def count_delayed_fields(obj, debug=False): - """ - Returns the number of delayed attributes on the given model instance. - """ - count = 0 - for field in obj._meta.fields: - if isinstance(obj.__class__.__dict__.get(field.attname), - DeferredAttribute): - if debug: - print field.name, field.attname - count += 1 - return count - - -__test__ = {"API_TEST": """ -To all outward appearances, instances with deferred fields look the same as -normal instances when we examine attribute values. Therefore we test for the -number of deferred fields on returned instances (by poking at the internals), -as a way to observe what is going on. - ->>> s1 = Secondary.objects.create(first="x1", second="y1") ->>> p1 = Primary.objects.create(name="p1", value="xx", related=s1) - ->>> qs = Primary.objects.all() - ->>> count_delayed_fields(qs.defer('name')[0]) -1 ->>> count_delayed_fields(qs.only('name')[0]) -2 ->>> count_delayed_fields(qs.defer('related__first')[0]) -0 ->>> obj = qs.select_related().only('related__first')[0] ->>> count_delayed_fields(obj) -2 ->>> obj.related_id == s1.pk -True ->>> count_delayed_fields(qs.defer('name').extra(select={'a': 1})[0]) -1 ->>> count_delayed_fields(qs.extra(select={'a': 1}).defer('name')[0]) -1 ->>> count_delayed_fields(qs.defer('name').defer('value')[0]) -2 ->>> count_delayed_fields(qs.only('name').only('value')[0]) -2 ->>> count_delayed_fields(qs.only('name').defer('value')[0]) -2 ->>> count_delayed_fields(qs.only('name', 'value').defer('value')[0]) -2 ->>> count_delayed_fields(qs.defer('name').only('value')[0]) -2 ->>> obj = qs.only()[0] ->>> count_delayed_fields(qs.defer(None)[0]) -0 ->>> count_delayed_fields(qs.only('name').defer(None)[0]) -0 - -User values() won't defer anything (you get the full list of dictionaries -back), but it still works. ->>> qs.defer('name').values()[0] == {'id': p1.id, 'name': u'p1', 'value': 'xx', 'related_id': s1.id} -True ->>> qs.only('name').values()[0] == {'id': p1.id, 'name': u'p1', 'value': 'xx', 'related_id': s1.id} -True - -Using defer() and only() with get() is also valid. ->>> count_delayed_fields(qs.defer('name').get(pk=p1.pk)) -1 ->>> count_delayed_fields(qs.only('name').get(pk=p1.pk)) -2 - -# KNOWN NOT TO WORK: >>> count_delayed_fields(qs.only('name').select_related('related')[0]) -# KNOWN NOT TO WORK >>> count_delayed_fields(qs.defer('related').select_related('related')[0]) - -# Saving models with deferred fields is possible (but inefficient, since every -# field has to be retrieved first). - ->>> obj = Primary.objects.defer("value").get(name="p1") ->>> obj.name = "a new name" ->>> obj.save() ->>> Primary.objects.all() -[<Primary: a new name>] - -# Regression for #10572 - A subclass with no extra fields can defer fields from the base class ->>> _ = Child.objects.create(name="c1", value="foo", related=s1) - -# You can defer a field on a baseclass when the subclass has no fields ->>> obj = Child.objects.defer("value").get(name="c1") ->>> count_delayed_fields(obj) -1 ->>> obj.name -u"c1" ->>> obj.value -u"foo" ->>> obj.name = "c2" ->>> obj.save() - -# You can retrive a single column on a base class with no fields ->>> obj = Child.objects.only("name").get(name="c2") ->>> count_delayed_fields(obj) -3 ->>> obj.name -u"c2" ->>> obj.value -u"foo" ->>> obj.name = "cc" ->>> obj.save() - ->>> _ = BigChild.objects.create(name="b1", value="foo", related=s1, other="bar") - -# You can defer a field on a baseclass ->>> obj = BigChild.objects.defer("value").get(name="b1") ->>> count_delayed_fields(obj) -1 ->>> obj.name -u"b1" ->>> obj.value -u"foo" ->>> obj.other -u"bar" ->>> obj.name = "b2" ->>> obj.save() - -# You can defer a field on a subclass ->>> obj = BigChild.objects.defer("other").get(name="b2") ->>> count_delayed_fields(obj) -1 ->>> obj.name -u"b2" ->>> obj.value -u"foo" ->>> obj.other -u"bar" ->>> obj.name = "b3" ->>> obj.save() - -# You can retrieve a single field on a baseclass ->>> obj = BigChild.objects.only("name").get(name="b3") ->>> count_delayed_fields(obj) -4 ->>> obj.name -u"b3" ->>> obj.value -u"foo" ->>> obj.other -u"bar" ->>> obj.name = "b4" ->>> obj.save() - -# You can retrieve a single field on a baseclass ->>> obj = BigChild.objects.only("other").get(name="b4") ->>> count_delayed_fields(obj) -4 ->>> obj.name -u"b4" ->>> obj.value -u"foo" ->>> obj.other -u"bar" ->>> obj.name = "bb" ->>> obj.save() - -"""} diff --git a/tests/modeltests/defer/tests.py b/tests/modeltests/defer/tests.py new file mode 100644 index 0000000000..5f6c53dee2 --- /dev/null +++ b/tests/modeltests/defer/tests.py @@ -0,0 +1,137 @@ +from django.db.models.query_utils import DeferredAttribute +from django.test import TestCase + +from models import Secondary, Primary, Child, BigChild + + +class DeferTests(TestCase): + def assert_delayed(self, obj, num): + count = 0 + for field in obj._meta.fields: + if isinstance(obj.__class__.__dict__.get(field.attname), + DeferredAttribute): + count += 1 + self.assertEqual(count, num) + + def test_defer(self): + # To all outward appearances, instances with deferred fields look the + # same as normal instances when we examine attribute values. Therefore + # we test for the number of deferred fields on returned instances (by + # poking at the internals), as a way to observe what is going on. + + s1 = Secondary.objects.create(first="x1", second="y1") + p1 = Primary.objects.create(name="p1", value="xx", related=s1) + + qs = Primary.objects.all() + + self.assert_delayed(qs.defer("name")[0], 1) + self.assert_delayed(qs.only("name")[0], 2) + self.assert_delayed(qs.defer("related__first")[0], 0) + + obj = qs.select_related().only("related__first")[0] + self.assert_delayed(obj, 2) + + self.assertEqual(obj.related_id, s1.pk) + + self.assert_delayed(qs.defer("name").extra(select={"a": 1})[0], 1) + self.assert_delayed(qs.extra(select={"a": 1}).defer("name")[0], 1) + self.assert_delayed(qs.defer("name").defer("value")[0], 2) + self.assert_delayed(qs.only("name").only("value")[0], 2) + self.assert_delayed(qs.only("name").defer("value")[0], 2) + self.assert_delayed(qs.only("name", "value").defer("value")[0], 2) + self.assert_delayed(qs.defer("name").only("value")[0], 2) + + obj = qs.only()[0] + self.assert_delayed(qs.defer(None)[0], 0) + self.assert_delayed(qs.only("name").defer(None)[0], 0) + + # User values() won't defer anything (you get the full list of + # dictionaries back), but it still works. + self.assertEqual(qs.defer("name").values()[0], { + "id": p1.id, + "name": "p1", + "value": "xx", + "related_id": s1.id, + }) + self.assertEqual(qs.only("name").values()[0], { + "id": p1.id, + "name": "p1", + "value": "xx", + "related_id": s1.id, + }) + + # Using defer() and only() with get() is also valid. + self.assert_delayed(qs.defer("name").get(pk=p1.pk), 1) + self.assert_delayed(qs.only("name").get(pk=p1.pk), 2) + + # DOES THIS WORK? + self.assert_delayed(qs.only("name").select_related("related")[0], 1) + self.assert_delayed(qs.defer("related").select_related("related")[0], 0) + + # Saving models with deferred fields is possible (but inefficient, + # since every field has to be retrieved first). + obj = Primary.objects.defer("value").get(name="p1") + obj.name = "a new name" + obj.save() + self.assertQuerysetEqual( + Primary.objects.all(), [ + "a new name", + ], + lambda p: p.name + ) + + # Regression for #10572 - A subclass with no extra fields can defer + # fields from the base class + Child.objects.create(name="c1", value="foo", related=s1) + # You can defer a field on a baseclass when the subclass has no fields + obj = Child.objects.defer("value").get(name="c1") + self.assert_delayed(obj, 1) + self.assertEqual(obj.name, "c1") + self.assertEqual(obj.value, "foo") + obj.name = "c2" + obj.save() + + # You can retrive a single column on a base class with no fields + obj = Child.objects.only("name").get(name="c2") + self.assert_delayed(obj, 3) + self.assertEqual(obj.name, "c2") + self.assertEqual(obj.value, "foo") + obj.name = "cc" + obj.save() + + BigChild.objects.create(name="b1", value="foo", related=s1, other="bar") + # You can defer a field on a baseclass + obj = BigChild.objects.defer("value").get(name="b1") + self.assert_delayed(obj, 1) + self.assertEqual(obj.name, "b1") + self.assertEqual(obj.value, "foo") + self.assertEqual(obj.other, "bar") + obj.name = "b2" + obj.save() + + # You can defer a field on a subclass + obj = BigChild.objects.defer("other").get(name="b2") + self.assert_delayed(obj, 1) + self.assertEqual(obj.name, "b2") + self.assertEqual(obj.value, "foo") + self.assertEqual(obj.other, "bar") + obj.name = "b3" + obj.save() + + # You can retrieve a single field on a baseclass + obj = BigChild.objects.only("name").get(name="b3") + self.assert_delayed(obj, 4) + self.assertEqual(obj.name, "b3") + self.assertEqual(obj.value, "foo") + self.assertEqual(obj.other, "bar") + obj.name = "b4" + obj.save() + + # You can retrieve a single field on a baseclass + obj = BigChild.objects.only("other").get(name="b4") + self.assert_delayed(obj, 4) + self.assertEqual(obj.name, "b4") + self.assertEqual(obj.value, "foo") + self.assertEqual(obj.other, "bar") + obj.name = "bb" + obj.save() diff --git a/tests/modeltests/delete/models.py b/tests/modeltests/delete/models.py index 0e063fd8f0..9c81f6b8f8 100644 --- a/tests/modeltests/delete/models.py +++ b/tests/modeltests/delete/models.py @@ -40,168 +40,3 @@ class E(DefaultRepr, models.Model): class F(DefaultRepr, models.Model): e = models.ForeignKey(E, related_name='f_rel') - -__test__ = {'API_TESTS': """ -### Tests for models A,B,C,D ### - -## First, test the CollectedObjects data structure directly - ->>> from django.db.models.query import CollectedObjects - ->>> g = CollectedObjects() ->>> g.add("key1", 1, "item1", None) -False ->>> g["key1"] -{1: 'item1'} ->>> g.add("key2", 1, "item1", "key1") -False ->>> g.add("key2", 2, "item2", "key1") -False ->>> g["key2"] -{1: 'item1', 2: 'item2'} ->>> g.add("key3", 1, "item1", "key1") -False ->>> g.add("key3", 1, "item1", "key2") -True ->>> g.ordered_keys() -['key3', 'key2', 'key1'] - ->>> g.add("key2", 1, "item1", "key3") -True ->>> g.ordered_keys() -Traceback (most recent call last): - ... -CyclicDependency: There is a cyclic dependency of items to be processed. - - -## Second, test the usage of CollectedObjects by Model.delete() - -# Due to the way that transactions work in the test harness, -# doing m.delete() here can work but fail in a real situation, -# since it may delete all objects, but not in the right order. -# So we manually check that the order of deletion is correct. - -# Also, it is possible that the order is correct 'accidentally', due -# solely to order of imports etc. To check this, we set the order -# that 'get_models()' will retrieve to a known 'nice' order, and -# then try again with a known 'tricky' order. Slightly naughty -# access to internals here :-) - -# If implementation changes, then the tests may need to be simplified: -# - remove the lines that set the .keyOrder and clear the related -# object caches -# - remove the second set of tests (with a2, b2 etc) - ->>> from django.db.models.loading import cache - ->>> def clear_rel_obj_caches(models): -... for m in models: -... if hasattr(m._meta, '_related_objects_cache'): -... del m._meta._related_objects_cache - -# Nice order ->>> cache.app_models['delete'].keyOrder = ['a', 'b', 'c', 'd'] ->>> clear_rel_obj_caches([A, B, C, D]) - ->>> a1 = A() ->>> a1.save() ->>> b1 = B(a=a1) ->>> b1.save() ->>> c1 = C(b=b1) ->>> c1.save() ->>> d1 = D(c=c1, a=a1) ->>> d1.save() - ->>> o = CollectedObjects() ->>> a1._collect_sub_objects(o) ->>> o.keys() -[<class 'modeltests.delete.models.D'>, <class 'modeltests.delete.models.C'>, <class 'modeltests.delete.models.B'>, <class 'modeltests.delete.models.A'>] ->>> a1.delete() - -# Same again with a known bad order ->>> cache.app_models['delete'].keyOrder = ['d', 'c', 'b', 'a'] ->>> clear_rel_obj_caches([A, B, C, D]) - ->>> a2 = A() ->>> a2.save() ->>> b2 = B(a=a2) ->>> b2.save() ->>> c2 = C(b=b2) ->>> c2.save() ->>> d2 = D(c=c2, a=a2) ->>> d2.save() - ->>> o = CollectedObjects() ->>> a2._collect_sub_objects(o) ->>> o.keys() -[<class 'modeltests.delete.models.D'>, <class 'modeltests.delete.models.C'>, <class 'modeltests.delete.models.B'>, <class 'modeltests.delete.models.A'>] ->>> a2.delete() - -### Tests for models E,F - nullable related fields ### - -## First, test the CollectedObjects data structure directly - ->>> g = CollectedObjects() ->>> g.add("key1", 1, "item1", None) -False ->>> g.add("key2", 1, "item1", "key1", nullable=True) -False ->>> g.add("key1", 1, "item1", "key2") -True ->>> g.ordered_keys() -['key1', 'key2'] - -## Second, test the usage of CollectedObjects by Model.delete() - ->>> e1 = E() ->>> e1.save() ->>> f1 = F(e=e1) ->>> f1.save() ->>> e1.f = f1 ->>> e1.save() - -# Since E.f is nullable, we should delete F first (after nulling out -# the E.f field), then E. - ->>> o = CollectedObjects() ->>> e1._collect_sub_objects(o) ->>> o.keys() -[<class 'modeltests.delete.models.F'>, <class 'modeltests.delete.models.E'>] - -# temporarily replace the UpdateQuery class to verify that E.f is actually nulled out first ->>> import django.db.models.sql ->>> class LoggingUpdateQuery(django.db.models.sql.UpdateQuery): -... def clear_related(self, related_field, pk_list, using): -... print "CLEARING FIELD",related_field.name -... return super(LoggingUpdateQuery, self).clear_related(related_field, pk_list, using) ->>> original_class = django.db.models.sql.UpdateQuery ->>> django.db.models.sql.UpdateQuery = LoggingUpdateQuery ->>> e1.delete() -CLEARING FIELD f - ->>> e2 = E() ->>> e2.save() ->>> f2 = F(e=e2) ->>> f2.save() ->>> e2.f = f2 ->>> e2.save() - -# Same deal as before, though we are starting from the other object. - ->>> o = CollectedObjects() ->>> f2._collect_sub_objects(o) ->>> o.keys() -[<class 'modeltests.delete.models.F'>, <class 'modeltests.delete.models.E'>] - ->>> f2.delete() -CLEARING FIELD f - -# Put this back to normal ->>> django.db.models.sql.UpdateQuery = original_class - -# Restore the app cache to previous condition so that all models are accounted for. ->>> cache.app_models['delete'].keyOrder = ['a', 'b', 'c', 'd', 'e', 'f'] ->>> clear_rel_obj_caches([A, B, C, D, E, F]) - -""" -} diff --git a/tests/modeltests/delete/tests.py b/tests/modeltests/delete/tests.py new file mode 100644 index 0000000000..7927cce1c1 --- /dev/null +++ b/tests/modeltests/delete/tests.py @@ -0,0 +1,135 @@ +from django.db.models import sql +from django.db.models.loading import cache +from django.db.models.query import CollectedObjects +from django.db.models.query_utils import CyclicDependency +from django.test import TestCase + +from models import A, B, C, D, E, F + + +class DeleteTests(TestCase): + def clear_rel_obj_caches(self, *models): + for m in models: + if hasattr(m._meta, '_related_objects_cache'): + del m._meta._related_objects_cache + + def order_models(self, *models): + cache.app_models["delete"].keyOrder = models + + def setUp(self): + self.order_models("a", "b", "c", "d", "e", "f") + self.clear_rel_obj_caches(A, B, C, D, E, F) + + def tearDown(self): + self.order_models("a", "b", "c", "d", "e", "f") + self.clear_rel_obj_caches(A, B, C, D, E, F) + + def test_collected_objects(self): + g = CollectedObjects() + self.assertFalse(g.add("key1", 1, "item1", None)) + self.assertEqual(g["key1"], {1: "item1"}) + + self.assertFalse(g.add("key2", 1, "item1", "key1")) + self.assertFalse(g.add("key2", 2, "item2", "key1")) + + self.assertEqual(g["key2"], {1: "item1", 2: "item2"}) + + self.assertFalse(g.add("key3", 1, "item1", "key1")) + self.assertTrue(g.add("key3", 1, "item1", "key2")) + self.assertEqual(g.ordered_keys(), ["key3", "key2", "key1"]) + + self.assertTrue(g.add("key2", 1, "item1", "key3")) + self.assertRaises(CyclicDependency, g.ordered_keys) + + def test_delete(self): + ## Second, test the usage of CollectedObjects by Model.delete() + + # Due to the way that transactions work in the test harness, doing + # m.delete() here can work but fail in a real situation, since it may + # delete all objects, but not in the right order. So we manually check + # that the order of deletion is correct. + + # Also, it is possible that the order is correct 'accidentally', due + # solely to order of imports etc. To check this, we set the order that + # 'get_models()' will retrieve to a known 'nice' order, and then try + # again with a known 'tricky' order. Slightly naughty access to + # internals here :-) + + # If implementation changes, then the tests may need to be simplified: + # - remove the lines that set the .keyOrder and clear the related + # object caches + # - remove the second set of tests (with a2, b2 etc) + + a1 = A.objects.create() + b1 = B.objects.create(a=a1) + c1 = C.objects.create(b=b1) + d1 = D.objects.create(c=c1, a=a1) + + o = CollectedObjects() + a1._collect_sub_objects(o) + self.assertEqual(o.keys(), [D, C, B, A]) + a1.delete() + + # Same again with a known bad order + self.order_models("d", "c", "b", "a") + self.clear_rel_obj_caches(A, B, C, D) + + a2 = A.objects.create() + b2 = B.objects.create(a=a2) + c2 = C.objects.create(b=b2) + d2 = D.objects.create(c=c2, a=a2) + + o = CollectedObjects() + a2._collect_sub_objects(o) + self.assertEqual(o.keys(), [D, C, B, A]) + a2.delete() + + def test_collected_objects_null(self): + g = CollectedObjects() + self.assertFalse(g.add("key1", 1, "item1", None)) + self.assertFalse(g.add("key2", 1, "item1", "key1", nullable=True)) + self.assertTrue(g.add("key1", 1, "item1", "key2")) + self.assertEqual(g.ordered_keys(), ["key1", "key2"]) + + def test_delete_nullable(self): + e1 = E.objects.create() + f1 = F.objects.create(e=e1) + e1.f = f1 + e1.save() + + # Since E.f is nullable, we should delete F first (after nulling out + # the E.f field), then E. + + o = CollectedObjects() + e1._collect_sub_objects(o) + self.assertEqual(o.keys(), [F, E]) + + # temporarily replace the UpdateQuery class to verify that E.f is + # actually nulled out first + + logged = [] + class LoggingUpdateQuery(sql.UpdateQuery): + def clear_related(self, related_field, pk_list, using): + logged.append(related_field.name) + return super(LoggingUpdateQuery, self).clear_related(related_field, pk_list, using) + original = sql.UpdateQuery + sql.UpdateQuery = LoggingUpdateQuery + + e1.delete() + self.assertEqual(logged, ["f"]) + logged = [] + + e2 = E.objects.create() + f2 = F.objects.create(e=e2) + e2.f = f2 + e2.save() + + # Same deal as before, though we are starting from the other object. + o = CollectedObjects() + f2._collect_sub_objects(o) + self.assertEqual(o.keys(), [F, E]) + f2.delete() + self.assertEqual(logged, ["f"]) + logged = [] + + sql.UpdateQuery = original diff --git a/tests/modeltests/empty/models.py b/tests/modeltests/empty/models.py index d57087134e..a6cdb0aa22 100644 --- a/tests/modeltests/empty/models.py +++ b/tests/modeltests/empty/models.py @@ -7,20 +7,6 @@ no fields. from django.db import models + class Empty(models.Model): pass - -__test__ = {'API_TESTS':""" ->>> m = Empty() ->>> m.id ->>> m.save() ->>> m2 = Empty() ->>> m2.save() ->>> len(Empty.objects.all()) -2 ->>> m.id is not None -True ->>> existing = Empty(m.id) ->>> existing.save() - -"""} diff --git a/tests/modeltests/empty/tests.py b/tests/modeltests/empty/tests.py new file mode 100644 index 0000000000..01fa1c58cd --- /dev/null +++ b/tests/modeltests/empty/tests.py @@ -0,0 +1,15 @@ +from django.test import TestCase + +from models import Empty + + +class EmptyModelTests(TestCase): + def test_empty(self): + m = Empty() + self.assertEqual(m.id, None) + m.save() + m2 = Empty.objects.create() + self.assertEqual(len(Empty.objects.all()), 2) + self.assertTrue(m.id is not None) + existing = Empty(m.id) + existing.save() diff --git a/tests/modeltests/expressions/models.py b/tests/modeltests/expressions/models.py index f6292f5d9b..b004408536 100644 --- a/tests/modeltests/expressions/models.py +++ b/tests/modeltests/expressions/models.py @@ -25,108 +25,3 @@ class Company(models.Model): def __unicode__(self): return self.name - - -__test__ = {'API_TESTS': """ ->>> from django.db.models import F - ->>> Company(name='Example Inc.', num_employees=2300, num_chairs=5, -... ceo=Employee.objects.create(firstname='Joe', lastname='Smith')).save() ->>> Company(name='Foobar Ltd.', num_employees=3, num_chairs=3, -... ceo=Employee.objects.create(firstname='Frank', lastname='Meyer')).save() ->>> Company(name='Test GmbH', num_employees=32, num_chairs=1, -... ceo=Employee.objects.create(firstname='Max', lastname='Mustermann')).save() - ->>> company_query = Company.objects.values('name','num_employees','num_chairs').order_by('name','num_employees','num_chairs') - -# We can filter for companies where the number of employees is greater than the -# number of chairs. ->>> company_query.filter(num_employees__gt=F('num_chairs')) -[{'num_chairs': 5, 'name': u'Example Inc.', 'num_employees': 2300}, {'num_chairs': 1, 'name': u'Test GmbH', 'num_employees': 32}] - -# We can set one field to have the value of another field -# Make sure we have enough chairs ->>> _ = company_query.update(num_chairs=F('num_employees')) ->>> company_query -[{'num_chairs': 2300, 'name': u'Example Inc.', 'num_employees': 2300}, {'num_chairs': 3, 'name': u'Foobar Ltd.', 'num_employees': 3}, {'num_chairs': 32, 'name': u'Test GmbH', 'num_employees': 32}] - -# We can perform arithmetic operations in expressions -# Make sure we have 2 spare chairs ->>> _ =company_query.update(num_chairs=F('num_employees')+2) ->>> company_query -[{'num_chairs': 2302, 'name': u'Example Inc.', 'num_employees': 2300}, {'num_chairs': 5, 'name': u'Foobar Ltd.', 'num_employees': 3}, {'num_chairs': 34, 'name': u'Test GmbH', 'num_employees': 32}] - -# Law of order of operations is followed ->>> _ =company_query.update(num_chairs=F('num_employees') + 2 * F('num_employees')) ->>> company_query -[{'num_chairs': 6900, 'name': u'Example Inc.', 'num_employees': 2300}, {'num_chairs': 9, 'name': u'Foobar Ltd.', 'num_employees': 3}, {'num_chairs': 96, 'name': u'Test GmbH', 'num_employees': 32}] - -# Law of order of operations can be overridden by parentheses ->>> _ =company_query.update(num_chairs=((F('num_employees') + 2) * F('num_employees'))) ->>> company_query -[{'num_chairs': 5294600, 'name': u'Example Inc.', 'num_employees': 2300}, {'num_chairs': 15, 'name': u'Foobar Ltd.', 'num_employees': 3}, {'num_chairs': 1088, 'name': u'Test GmbH', 'num_employees': 32}] - -# The relation of a foreign key can become copied over to an other foreign key. ->>> Company.objects.update(point_of_contact=F('ceo')) -3 - ->>> [c.point_of_contact for c in Company.objects.all()] -[<Employee: Joe Smith>, <Employee: Frank Meyer>, <Employee: Max Mustermann>] - ->>> c = Company.objects.all()[0] ->>> c.point_of_contact = Employee.objects.create(firstname="Guido", lastname="van Rossum") ->>> c.save() - -# F Expressions can also span joins ->>> Company.objects.filter(ceo__firstname=F('point_of_contact__firstname')).distinct().order_by('name') -[<Company: Foobar Ltd.>, <Company: Test GmbH>] - ->>> _ = Company.objects.exclude(ceo__firstname=F('point_of_contact__firstname')).update(name='foo') ->>> Company.objects.exclude(ceo__firstname=F('point_of_contact__firstname')).get().name -u'foo' - ->>> _ = Company.objects.exclude(ceo__firstname=F('point_of_contact__firstname')).update(name=F('point_of_contact__lastname')) -Traceback (most recent call last): -... -FieldError: Joined field references are not permitted in this query - -# F expressions can be used to update attributes on single objects ->>> test_gmbh = Company.objects.get(name='Test GmbH') ->>> test_gmbh.num_employees -32 ->>> test_gmbh.num_employees = F('num_employees') + 4 ->>> test_gmbh.save() ->>> test_gmbh = Company.objects.get(pk=test_gmbh.pk) ->>> test_gmbh.num_employees -36 - -# F expressions cannot be used to update attributes which are foreign keys, or -# attributes which involve joins. ->>> test_gmbh.point_of_contact = None ->>> test_gmbh.save() ->>> test_gmbh.point_of_contact is None -True ->>> test_gmbh.point_of_contact = F('ceo') -Traceback (most recent call last): -... -ValueError: Cannot assign "<django.db.models.expressions.F object at ...>": "Company.point_of_contact" must be a "Employee" instance. - ->>> test_gmbh.point_of_contact = test_gmbh.ceo ->>> test_gmbh.save() ->>> test_gmbh.name = F('ceo__last_name') ->>> test_gmbh.save() -Traceback (most recent call last): -... -FieldError: Joined field references are not permitted in this query - -# F expressions cannot be used to update attributes on objects which do not yet -# exist in the database ->>> acme = Company(name='The Acme Widget Co.', num_employees=12, num_chairs=5, -... ceo=test_gmbh.ceo) ->>> acme.num_employees = F('num_employees') + 16 ->>> acme.save() -Traceback (most recent call last): -... -TypeError: ... - -"""} diff --git a/tests/modeltests/expressions/tests.py b/tests/modeltests/expressions/tests.py new file mode 100644 index 0000000000..0a136ae5d8 --- /dev/null +++ b/tests/modeltests/expressions/tests.py @@ -0,0 +1,218 @@ +from django.core.exceptions import FieldError +from django.db.models import F +from django.test import TestCase + +from models import Company, Employee + + +class ExpressionsTests(TestCase): + def test_filter(self): + Company.objects.create( + name="Example Inc.", num_employees=2300, num_chairs=5, + ceo=Employee.objects.create(firstname="Joe", lastname="Smith") + ) + Company.objects.create( + name="Foobar Ltd.", num_employees=3, num_chairs=4, + ceo=Employee.objects.create(firstname="Frank", lastname="Meyer") + ) + Company.objects.create( + name="Test GmbH", num_employees=32, num_chairs=1, + ceo=Employee.objects.create(firstname="Max", lastname="Mustermann") + ) + + company_query = Company.objects.values( + "name", "num_employees", "num_chairs" + ).order_by( + "name", "num_employees", "num_chairs" + ) + + # We can filter for companies where the number of employees is greater + # than the number of chairs. + self.assertQuerysetEqual( + company_query.filter(num_employees__gt=F("num_chairs")), [ + { + "num_chairs": 5, + "name": "Example Inc.", + "num_employees": 2300, + }, + { + "num_chairs": 1, + "name": "Test GmbH", + "num_employees": 32 + }, + ], + lambda o: o + ) + + # We can set one field to have the value of another field + # Make sure we have enough chairs + company_query.update(num_chairs=F("num_employees")) + self.assertQuerysetEqual( + company_query, [ + { + "num_chairs": 2300, + "name": "Example Inc.", + "num_employees": 2300 + }, + { + "num_chairs": 3, + "name": "Foobar Ltd.", + "num_employees": 3 + }, + { + "num_chairs": 32, + "name": "Test GmbH", + "num_employees": 32 + } + ], + lambda o: o + ) + + # We can perform arithmetic operations in expressions + # Make sure we have 2 spare chairs + company_query.update(num_chairs=F("num_employees")+2) + self.assertQuerysetEqual( + company_query, [ + { + 'num_chairs': 2302, + 'name': u'Example Inc.', + 'num_employees': 2300 + }, + { + 'num_chairs': 5, + 'name': u'Foobar Ltd.', + 'num_employees': 3 + }, + { + 'num_chairs': 34, + 'name': u'Test GmbH', + 'num_employees': 32 + } + ], + lambda o: o, + ) + + # Law of order of operations is followed + company_query.update( + num_chairs=F('num_employees') + 2 * F('num_employees') + ) + self.assertQuerysetEqual( + company_query, [ + { + 'num_chairs': 6900, + 'name': u'Example Inc.', + 'num_employees': 2300 + }, + { + 'num_chairs': 9, + 'name': u'Foobar Ltd.', + 'num_employees': 3 + }, + { + 'num_chairs': 96, + 'name': u'Test GmbH', + 'num_employees': 32 + } + ], + lambda o: o, + ) + + # Law of order of operations can be overridden by parentheses + company_query.update( + num_chairs=((F('num_employees') + 2) * F('num_employees')) + ) + self.assertQuerysetEqual( + company_query, [ + { + 'num_chairs': 5294600, + 'name': u'Example Inc.', + 'num_employees': 2300 + }, + { + 'num_chairs': 15, + 'name': u'Foobar Ltd.', + 'num_employees': 3 + }, + { + 'num_chairs': 1088, + 'name': u'Test GmbH', + 'num_employees': 32 + } + ], + lambda o: o, + ) + + # The relation of a foreign key can become copied over to an other + # foreign key. + self.assertEqual( + Company.objects.update(point_of_contact=F('ceo')), + 3 + ) + self.assertQuerysetEqual( + Company.objects.all(), [ + "Joe Smith", + "Frank Meyer", + "Max Mustermann", + ], + lambda c: unicode(c.point_of_contact), + ) + + c = Company.objects.all()[0] + c.point_of_contact = Employee.objects.create(firstname="Guido", lastname="van Rossum") + c.save() + + # F Expressions can also span joins + self.assertQuerysetEqual( + Company.objects.filter(ceo__firstname=F("point_of_contact__firstname")), [ + "Foobar Ltd.", + "Test GmbH", + ], + lambda c: c.name + ) + + Company.objects.exclude( + ceo__firstname=F("point_of_contact__firstname") + ).update(name="foo") + self.assertEqual( + Company.objects.exclude( + ceo__firstname=F('point_of_contact__firstname') + ).get().name, + "foo", + ) + + self.assertRaises(FieldError, + lambda: Company.objects.exclude( + ceo__firstname=F('point_of_contact__firstname') + ).update(name=F('point_of_contact__lastname')) + ) + + # F expressions can be used to update attributes on single objects + test_gmbh = Company.objects.get(name="Test GmbH") + self.assertEqual(test_gmbh.num_employees, 32) + test_gmbh.num_employees = F("num_employees") + 4 + test_gmbh.save() + test_gmbh = Company.objects.get(pk=test_gmbh.pk) + self.assertEqual(test_gmbh.num_employees, 36) + + # F expressions cannot be used to update attributes which are foreign + # keys, or attributes which involve joins. + test_gmbh.point_of_contact = None + test_gmbh.save() + self.assertTrue(test_gmbh.point_of_contact is None) + def test(): + test_gmbh.point_of_contact = F("ceo") + self.assertRaises(ValueError, test) + + test_gmbh.point_of_contact = test_gmbh.ceo + test_gmbh.save() + test_gmbh.name = F("ceo__last_name") + self.assertRaises(FieldError, test_gmbh.save) + + # F expressions cannot be used to update attributes on objects which do + # not yet exist in the database + acme = Company( + name="The Acme Widget Co.", num_employees=12, num_chairs=5, + ceo=test_gmbh.ceo + ) + acme.num_employees = F("num_employees") + 16 + self.assertRaises(TypeError, acme.save) diff --git a/tests/modeltests/field_defaults/models.py b/tests/modeltests/field_defaults/models.py index f258134147..0dd1f72934 100644 --- a/tests/modeltests/field_defaults/models.py +++ b/tests/modeltests/field_defaults/models.py @@ -19,41 +19,3 @@ class Article(models.Model): def __unicode__(self): return self.headline - -__test__ = {'API_TESTS': u""" ->>> from datetime import datetime - -# No articles are in the system yet. ->>> Article.objects.all() -[] - -# Create an Article. ->>> a = Article(id=None) - -# Grab the current datetime it should be very close to the default that just -# got saved as a.pub_date ->>> now = datetime.now() - -# Save it into the database. You have to call save() explicitly. ->>> a.save() - -# Now it has an ID. Note it's a long integer, as designated by the trailing "L". ->>> a.id -1L - -# Access database columns via Python attributes. ->>> a.headline -u'Default headline' - -# make sure the two dates are sufficiently close ->>> d = now - a.pub_date ->>> d.seconds < 5 -True - -# make sure that SafeString/SafeUnicode fields work ->>> from django.utils.safestring import SafeUnicode, SafeString ->>> a.headline = SafeUnicode(u'Iñtërnâtiônà lizætiøn1') ->>> a.save() ->>> a.headline = SafeString(u'Iñtërnâtiônà lizætiøn1'.encode('utf-8')) ->>> a.save() -"""} diff --git a/tests/modeltests/field_defaults/tests.py b/tests/modeltests/field_defaults/tests.py new file mode 100644 index 0000000000..a23f64404a --- /dev/null +++ b/tests/modeltests/field_defaults/tests.py @@ -0,0 +1,16 @@ +from datetime import datetime + +from django.test import TestCase + +from models import Article + + +class DefaultTests(TestCase): + def test_field_defaults(self): + a = Article() + now = datetime.now() + a.save() + + self.assertTrue(isinstance(a.id, (int, long))) + self.assertEqual(a.headline, "Default headline") + self.assertTrue((now - a.pub_date).seconds < 5) diff --git a/tests/modeltests/field_subclassing/fields.py b/tests/modeltests/field_subclassing/fields.py index a43714dcf5..1f9bdf5e0a 100644 --- a/tests/modeltests/field_subclassing/fields.py +++ b/tests/modeltests/field_subclassing/fields.py @@ -53,18 +53,18 @@ class SmallField(models.Field): class JSONField(models.TextField): __metaclass__ = models.SubfieldBase - + description = ("JSONField automatically serializes and desializes values to " "and from JSON.") - + def to_python(self, value): if not value: return None - + if isinstance(value, basestring): value = json.loads(value) return value - + def get_db_prep_save(self, value): if value is None: return None diff --git a/tests/modeltests/field_subclassing/models.py b/tests/modeltests/field_subclassing/models.py index a9fe88fe77..4a55b72961 100644 --- a/tests/modeltests/field_subclassing/models.py +++ b/tests/modeltests/field_subclassing/models.py @@ -2,7 +2,6 @@ Tests for field subclassing. """ -from django.core import serializers from django.db import models from django.utils.encoding import force_unicode @@ -18,56 +17,3 @@ class MyModel(models.Model): class DataModel(models.Model): data = JSONField() - -__test__ = {'API_TESTS': ur""" -# Creating a model with custom fields is done as per normal. ->>> s = Small(1, 2) ->>> print s -12 ->>> m = MyModel(name='m', data=s) ->>> m.save() - -# Custom fields still have normal field's attributes. ->>> m._meta.get_field('data').verbose_name -'small field' - -# The m.data attribute has been initialised correctly. It's a Small object. ->>> m.data.first, m.data.second -(1, 2) - -# The data loads back from the database correctly and 'data' has the right type. ->>> m1 = MyModel.objects.get(pk=m.pk) ->>> isinstance(m1.data, Small) -True ->>> print m1.data -12 - -# We can do normal filtering on the custom field (and will get an error when we -# use a lookup type that does not make sense). ->>> s1 = Small(1, 3) ->>> s2 = Small('a', 'b') ->>> MyModel.objects.filter(data__in=[s, s1, s2]) -[<MyModel: m>] ->>> MyModel.objects.filter(data__lt=s) -Traceback (most recent call last): -... -TypeError: Invalid lookup type: 'lt' - -# Serialization works, too. ->>> stream = serializers.serialize("json", MyModel.objects.all()) ->>> stream -'[{"pk": 1, "model": "field_subclassing.mymodel", "fields": {"data": "12", "name": "m"}}]' ->>> obj = list(serializers.deserialize("json", stream))[0] ->>> obj.object == m -True - -# Test retrieving custom field data ->>> m.delete() ->>> m1 = MyModel(name="1", data=Small(1, 2)) ->>> m1.save() ->>> m2 = MyModel(name="2", data=Small(2, 3)) ->>> m2.save() ->>> for m in MyModel.objects.all(): print unicode(m.data) -12 -23 -"""} diff --git a/tests/modeltests/field_subclassing/tests.py b/tests/modeltests/field_subclassing/tests.py index 731ab51d24..ba7148a654 100644 --- a/tests/modeltests/field_subclassing/tests.py +++ b/tests/modeltests/field_subclassing/tests.py @@ -1,21 +1,75 @@ +from django.core import serializers from django.test import TestCase -from models import DataModel +from fields import Small +from models import DataModel, MyModel class CustomField(TestCase): def test_defer(self): d = DataModel.objects.create(data=[1, 2, 3]) - + self.assertTrue(isinstance(d.data, list)) - + d = DataModel.objects.get(pk=d.pk) self.assertTrue(isinstance(d.data, list)) self.assertEqual(d.data, [1, 2, 3]) - + d = DataModel.objects.defer("data").get(pk=d.pk) d.save() - + d = DataModel.objects.get(pk=d.pk) self.assertTrue(isinstance(d.data, list)) self.assertEqual(d.data, [1, 2, 3]) + + def test_custom_field(self): + # Creating a model with custom fields is done as per normal. + s = Small(1, 2) + self.assertEqual(str(s), "12") + + m = MyModel.objects.create(name="m", data=s) + # Custom fields still have normal field's attributes. + self.assertEqual(m._meta.get_field("data").verbose_name, "small field") + + # The m.data attribute has been initialised correctly. It's a Small + # object. + self.assertEqual((m.data.first, m.data.second), (1, 2)) + + # The data loads back from the database correctly and 'data' has the + # right type. + m1 = MyModel.objects.get(pk=m.pk) + self.assertTrue(isinstance(m1.data, Small)) + self.assertEqual(str(m1.data), "12") + + # We can do normal filtering on the custom field (and will get an error + # when we use a lookup type that does not make sense). + s1 = Small(1, 3) + s2 = Small("a", "b") + self.assertQuerysetEqual( + MyModel.objects.filter(data__in=[s, s1, s2]), [ + "m", + ], + lambda m: m.name, + ) + self.assertRaises(TypeError, lambda: MyModel.objects.filter(data__lt=s)) + + # Serialization works, too. + stream = serializers.serialize("json", MyModel.objects.all()) + self.assertEqual(stream, '[{"pk": 1, "model": "field_subclassing.mymodel", "fields": {"data": "12", "name": "m"}}]') + + obj = list(serializers.deserialize("json", stream))[0] + self.assertEqual(obj.object, m) + + # Test retrieving custom field data + m.delete() + + m1 = MyModel.objects.create(name="1", data=Small(1, 2)) + m2 = MyModel.objects.create(name="2", data=Small(2, 3)) + + self.assertQuerysetEqual( + MyModel.objects.all(), [ + "12", + "23", + ], + lambda m: str(m.data) + ) diff --git a/tests/modeltests/files/models.py b/tests/modeltests/files/models.py index 67c27b54b5..f798f74df9 100644 --- a/tests/modeltests/files/models.py +++ b/tests/modeltests/files/models.py @@ -7,10 +7,12 @@ and where files should be stored. import random import tempfile + from django.db import models from django.core.files.base import ContentFile from django.core.files.storage import FileSystemStorage + temp_storage_location = tempfile.mkdtemp() temp_storage = FileSystemStorage(location=temp_storage_location) @@ -30,125 +32,3 @@ class Storage(models.Model): custom = models.FileField(storage=temp_storage, upload_to=custom_upload_to) random = models.FileField(storage=temp_storage, upload_to=random_upload_to) default = models.FileField(storage=temp_storage, upload_to='tests', default='tests/default.txt') - -__test__ = {'API_TESTS':""" -# Attempting to access a FileField from the class raises a descriptive error ->>> Storage.normal -Traceback (most recent call last): -... -AttributeError: The 'normal' attribute can only be accessed from Storage instances. - -# An object without a file has limited functionality. - ->>> obj1 = Storage() ->>> obj1.normal -<FieldFile: None> ->>> obj1.normal.size -Traceback (most recent call last): -... -ValueError: The 'normal' attribute has no file associated with it. - -# Saving a file enables full functionality. - ->>> obj1.normal.save('django_test.txt', ContentFile('content')) ->>> obj1.normal -<FieldFile: tests/django_test.txt> ->>> obj1.normal.size -7 ->>> obj1.normal.read() -'content' - -# File objects can be assigned to FileField attributes, but shouldn't get -# committed until the model it's attached to is saved. - ->>> from django.core.files.uploadedfile import SimpleUploadedFile ->>> obj1.normal = SimpleUploadedFile('assignment.txt', 'content') ->>> dirs, files = temp_storage.listdir('tests') ->>> dirs -[] ->>> files.sort() ->>> files == ['default.txt', 'django_test.txt'] -True - ->>> obj1.save() ->>> dirs, files = temp_storage.listdir('tests') ->>> files.sort() ->>> files == ['assignment.txt', 'default.txt', 'django_test.txt'] -True - -# Files can be read in a little at a time, if necessary. - ->>> obj1.normal.open() ->>> obj1.normal.read(3) -'con' ->>> obj1.normal.read() -'tent' ->>> '-'.join(obj1.normal.chunks(chunk_size=2)) -'co-nt-en-t' - -# Save another file with the same name. - ->>> obj2 = Storage() ->>> obj2.normal.save('django_test.txt', ContentFile('more content')) ->>> obj2.normal -<FieldFile: tests/django_test_1.txt> ->>> obj2.normal.size -12 - -# Push the objects into the cache to make sure they pickle properly - ->>> from django.core.cache import cache ->>> cache.set('obj1', obj1) ->>> cache.set('obj2', obj2) ->>> cache.get('obj2').normal -<FieldFile: tests/django_test_1.txt> - -# Deleting an object deletes the file it uses, if there are no other objects -# still using that file. - ->>> obj2.delete() ->>> obj2.normal.save('django_test.txt', ContentFile('more content')) ->>> obj2.normal -<FieldFile: tests/django_test_1.txt> - -# Multiple files with the same name get _N appended to them. - ->>> objs = [Storage() for i in range(3)] ->>> for o in objs: -... o.normal.save('multiple_files.txt', ContentFile('Same Content')) ->>> [o.normal for o in objs] -[<FieldFile: tests/multiple_files.txt>, <FieldFile: tests/multiple_files_1.txt>, <FieldFile: tests/multiple_files_2.txt>] ->>> for o in objs: -... o.delete() - -# Default values allow an object to access a single file. - ->>> obj3 = Storage.objects.create() ->>> obj3.default -<FieldFile: tests/default.txt> ->>> obj3.default.read() -'default content' - -# But it shouldn't be deleted, even if there are no more objects using it. - ->>> obj3.delete() ->>> obj3 = Storage() ->>> obj3.default.read() -'default content' - -# Verify the fix for #5655, making sure the directory is only determined once. - ->>> obj4 = Storage() ->>> obj4.random.save('random_file', ContentFile('random content')) ->>> obj4.random -<FieldFile: .../random_file> - -# Clean up the temporary files and dir. ->>> obj1.normal.delete() ->>> obj2.normal.delete() ->>> obj3.default.delete() ->>> obj4.random.delete() - ->>> import shutil ->>> shutil.rmtree(temp_storage_location) -"""} diff --git a/tests/modeltests/files/tests.py b/tests/modeltests/files/tests.py new file mode 100644 index 0000000000..025fcc574a --- /dev/null +++ b/tests/modeltests/files/tests.py @@ -0,0 +1,100 @@ +import shutil + +from django.core.cache import cache +from django.core.files.base import ContentFile +from django.core.files.uploadedfile import SimpleUploadedFile +from django.test import TestCase + +from models import Storage, temp_storage, temp_storage_location + + +class FileTests(TestCase): + def tearDown(self): + shutil.rmtree(temp_storage_location) + + def test_files(self): + # Attempting to access a FileField from the class raises a descriptive + # error + self.assertRaises(AttributeError, lambda: Storage.normal) + + # An object without a file has limited functionality. + obj1 = Storage() + self.assertEqual(obj1.normal.name, "") + self.assertRaises(ValueError, lambda: obj1.normal.size) + + # Saving a file enables full functionality. + obj1.normal.save("django_test.txt", ContentFile("content")) + self.assertEqual(obj1.normal.name, "tests/django_test.txt") + self.assertEqual(obj1.normal.size, 7) + self.assertEqual(obj1.normal.read(), "content") + + # File objects can be assigned to FileField attributes, but shouldn't + # get committed until the model it's attached to is saved. + obj1.normal = SimpleUploadedFile("assignment.txt", "content") + dirs, files = temp_storage.listdir("tests") + self.assertEqual(dirs, []) + self.assertEqual(sorted(files), ["default.txt", "django_test.txt"]) + + obj1.save() + dirs, files = temp_storage.listdir("tests") + self.assertEqual( + sorted(files), ["assignment.txt", "default.txt", "django_test.txt"] + ) + + # Files can be read in a little at a time, if necessary. + obj1.normal.open() + self.assertEqual(obj1.normal.read(3), "con") + self.assertEqual(obj1.normal.read(), "tent") + self.assertEqual(list(obj1.normal.chunks(chunk_size=2)), ["co", "nt", "en", "t"]) + + # Save another file with the same name. + obj2 = Storage() + obj2.normal.save("django_test.txt", ContentFile("more content")) + self.assertEqual(obj2.normal.name, "tests/django_test_1.txt") + self.assertEqual(obj2.normal.size, 12) + + # Push the objects into the cache to make sure they pickle properly + cache.set("obj1", obj1) + cache.set("obj2", obj2) + self.assertEqual(cache.get("obj2").normal.name, "tests/django_test_1.txt") + + # Deleting an object deletes the file it uses, if there are no other + # objects still using that file. + obj2.delete() + obj2.normal.save("django_test.txt", ContentFile("more content")) + self.assertEqual(obj2.normal.name, "tests/django_test_1.txt") + + # Multiple files with the same name get _N appended to them. + objs = [Storage() for i in range(3)] + for o in objs: + o.normal.save("multiple_files.txt", ContentFile("Same Content")) + self.assertEqual( + [o.normal.name for o in objs], + ["tests/multiple_files.txt", "tests/multiple_files_1.txt", "tests/multiple_files_2.txt"] + ) + for o in objs: + o.delete() + + # Default values allow an object to access a single file. + obj3 = Storage.objects.create() + self.assertEqual(obj3.default.name, "tests/default.txt") + self.assertEqual(obj3.default.read(), "default content") + + # But it shouldn't be deleted, even if there are no more objects using + # it. + obj3.delete() + obj3 = Storage() + self.assertEqual(obj3.default.read(), "default content") + + # Verify the fix for #5655, making sure the directory is only + # determined once. + obj4 = Storage() + obj4.random.save("random_file", ContentFile("random content")) + self.assertTrue(obj4.random.name.endswith("/random_file")) + + # Clean up the temporary files and dir. + obj1.normal.delete() + obj2.normal.delete() + obj3.default.delete() + obj4.random.delete() + diff --git a/tests/modeltests/fixtures/models.py b/tests/modeltests/fixtures/models.py index 46e07a5e6b..0035bcc7e0 100644 --- a/tests/modeltests/fixtures/models.py +++ b/tests/modeltests/fixtures/models.py @@ -72,6 +72,14 @@ class Person(models.Model): def natural_key(self): return (self.name,) +class SpyManager(PersonManager): + def get_query_set(self): + return super(SpyManager, self).get_query_set().filter(cover_blown=False) + +class Spy(Person): + objects = SpyManager() + cover_blown = models.BooleanField(default=False) + class Visa(models.Model): person = models.ForeignKey(Person) permissions = models.ManyToManyField(Permission, blank=True) @@ -90,230 +98,3 @@ class Book(models.Model): class Meta: ordering = ('name',) - -__test__ = {'API_TESTS': """ ->>> from django.core import management ->>> from django.db.models import get_app - -# Reset the database representation of this app. -# This will return the database to a clean initial state. ->>> management.call_command('flush', verbosity=0, interactive=False) - -# Syncdb introduces 1 initial data object from initial_data.json. ->>> Article.objects.all() -[<Article: Python program becomes self aware>] - -# Load fixture 1. Single JSON file, with two objects. ->>> management.call_command('loaddata', 'fixture1.json', verbosity=0) ->>> Article.objects.all() -[<Article: Time to reform copyright>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>] - -# Dump the current contents of the database as a JSON fixture ->>> management.call_command('dumpdata', 'fixtures', format='json') -[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}] - -# Try just dumping the contents of fixtures.Category ->>> management.call_command('dumpdata', 'fixtures.Category', format='json') -[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}] - -# ...and just fixtures.Article ->>> management.call_command('dumpdata', 'fixtures.Article', format='json') -[{"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}] - -# ...and both ->>> management.call_command('dumpdata', 'fixtures.Category', 'fixtures.Article', format='json') -[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}] - -# Specify a specific model twice ->>> management.call_command('dumpdata', 'fixtures.Article', 'fixtures.Article', format='json') -[{"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}] - -# Specify a dump that specifies Article both explicitly and implicitly ->>> management.call_command('dumpdata', 'fixtures.Article', 'fixtures', format='json') -[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}] - -# Same again, but specify in the reverse order ->>> management.call_command('dumpdata', 'fixtures', 'fixtures.Article', format='json') -[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}] - -# Specify one model from one application, and an entire other application. ->>> management.call_command('dumpdata', 'fixtures.Category', 'sites', format='json') -[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 1, "model": "sites.site", "fields": {"domain": "example.com", "name": "example.com"}}] - -# Load fixture 2. JSON file imported by default. Overwrites some existing objects ->>> management.call_command('loaddata', 'fixture2.json', verbosity=0) ->>> Article.objects.all() -[<Article: Django conquers world!>, <Article: Copyright is fine the way it is>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>] - -# Load fixture 3, XML format. ->>> management.call_command('loaddata', 'fixture3.xml', verbosity=0) ->>> Article.objects.all() -[<Article: XML identified as leading cause of cancer>, <Article: Django conquers world!>, <Article: Copyright is fine the way it is>, <Article: Poker on TV is great!>, <Article: Python program becomes self aware>] - -# Load fixture 6, JSON file with dynamic ContentType fields. Testing ManyToOne. ->>> management.call_command('loaddata', 'fixture6.json', verbosity=0) ->>> Tag.objects.all() -[<Tag: <Article: Copyright is fine the way it is> tagged "copyright">, <Tag: <Article: Copyright is fine the way it is> tagged "law">] - -# Load fixture 7, XML file with dynamic ContentType fields. Testing ManyToOne. ->>> management.call_command('loaddata', 'fixture7.xml', verbosity=0) ->>> Tag.objects.all() -[<Tag: <Article: Copyright is fine the way it is> tagged "copyright">, <Tag: <Article: Copyright is fine the way it is> tagged "legal">, <Tag: <Article: Django conquers world!> tagged "django">, <Tag: <Article: Django conquers world!> tagged "world domination">] - -# Load fixture 8, JSON file with dynamic Permission fields. Testing ManyToMany. ->>> management.call_command('loaddata', 'fixture8.json', verbosity=0) ->>> Visa.objects.all() -[<Visa: Django Reinhardt Can add user, Can change user, Can delete user>, <Visa: Stephane Grappelli Can add user>, <Visa: Prince >] - -# Load fixture 9, XML file with dynamic Permission fields. Testing ManyToMany. ->>> management.call_command('loaddata', 'fixture9.xml', verbosity=0) ->>> Visa.objects.all() -[<Visa: Django Reinhardt Can add user, Can change user, Can delete user>, <Visa: Stephane Grappelli Can add user, Can delete user>, <Visa: Artist formerly known as "Prince" Can change user>] - ->>> Book.objects.all() -[<Book: Music for all ages by Artist formerly known as "Prince" and Django Reinhardt>] - -# Load a fixture that doesn't exist ->>> management.call_command('loaddata', 'unknown.json', verbosity=0) - -# object list is unaffected ->>> Article.objects.all() -[<Article: XML identified as leading cause of cancer>, <Article: Django conquers world!>, <Article: Copyright is fine the way it is>, <Article: Poker on TV is great!>, <Article: Python program becomes self aware>] - -# By default, you get raw keys on dumpdata ->>> management.call_command('dumpdata', 'fixtures.book', format='json') -[{"pk": 1, "model": "fixtures.book", "fields": {"name": "Music for all ages", "authors": [3, 1]}}] - -# But you can get natural keys if you ask for them and they are available ->>> management.call_command('dumpdata', 'fixtures.book', format='json', use_natural_keys=True) -[{"pk": 1, "model": "fixtures.book", "fields": {"name": "Music for all ages", "authors": [["Artist formerly known as \\"Prince\\""], ["Django Reinhardt"]]}}] - -# Dump the current contents of the database as a JSON fixture ->>> management.call_command('dumpdata', 'fixtures', format='json', use_natural_keys=True) -[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 5, "model": "fixtures.article", "fields": {"headline": "XML identified as leading cause of cancer", "pub_date": "2006-06-16 16:00:00"}}, {"pk": 4, "model": "fixtures.article", "fields": {"headline": "Django conquers world!", "pub_date": "2006-06-16 15:00:00"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Copyright is fine the way it is", "pub_date": "2006-06-16 14:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker on TV is great!", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 1, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "copyright", "tagged_id": 3}}, {"pk": 2, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "legal", "tagged_id": 3}}, {"pk": 3, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "django", "tagged_id": 4}}, {"pk": 4, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "world domination", "tagged_id": 4}}, {"pk": 3, "model": "fixtures.person", "fields": {"name": "Artist formerly known as \\"Prince\\""}}, {"pk": 1, "model": "fixtures.person", "fields": {"name": "Django Reinhardt"}}, {"pk": 2, "model": "fixtures.person", "fields": {"name": "Stephane Grappelli"}}, {"pk": 1, "model": "fixtures.visa", "fields": {"person": ["Django Reinhardt"], "permissions": [["add_user", "auth", "user"], ["change_user", "auth", "user"], ["delete_user", "auth", "user"]]}}, {"pk": 2, "model": "fixtures.visa", "fields": {"person": ["Stephane Grappelli"], "permissions": [["add_user", "auth", "user"], ["delete_user", "auth", "user"]]}}, {"pk": 3, "model": "fixtures.visa", "fields": {"person": ["Artist formerly known as \\"Prince\\""], "permissions": [["change_user", "auth", "user"]]}}, {"pk": 1, "model": "fixtures.book", "fields": {"name": "Music for all ages", "authors": [["Artist formerly known as \\"Prince\\""], ["Django Reinhardt"]]}}] - -# Dump the current contents of the database as an XML fixture ->>> management.call_command('dumpdata', 'fixtures', format='xml', use_natural_keys=True) -<?xml version="1.0" encoding="utf-8"?> -<django-objects version="1.0"><object pk="1" model="fixtures.category"><field type="CharField" name="title">News Stories</field><field type="TextField" name="description">Latest news stories</field></object><object pk="5" model="fixtures.article"><field type="CharField" name="headline">XML identified as leading cause of cancer</field><field type="DateTimeField" name="pub_date">2006-06-16 16:00:00</field></object><object pk="4" model="fixtures.article"><field type="CharField" name="headline">Django conquers world!</field><field type="DateTimeField" name="pub_date">2006-06-16 15:00:00</field></object><object pk="3" model="fixtures.article"><field type="CharField" name="headline">Copyright is fine the way it is</field><field type="DateTimeField" name="pub_date">2006-06-16 14:00:00</field></object><object pk="2" model="fixtures.article"><field type="CharField" name="headline">Poker on TV is great!</field><field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field></object><object pk="1" model="fixtures.article"><field type="CharField" name="headline">Python program becomes self aware</field><field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field></object><object pk="1" model="fixtures.tag"><field type="CharField" name="name">copyright</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="2" model="fixtures.tag"><field type="CharField" name="name">legal</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="3" model="fixtures.tag"><field type="CharField" name="name">django</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">4</field></object><object pk="4" model="fixtures.tag"><field type="CharField" name="name">world domination</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">4</field></object><object pk="3" model="fixtures.person"><field type="CharField" name="name">Artist formerly known as "Prince"</field></object><object pk="1" model="fixtures.person"><field type="CharField" name="name">Django Reinhardt</field></object><object pk="2" model="fixtures.person"><field type="CharField" name="name">Stephane Grappelli</field></object><object pk="1" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Django Reinhardt</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>add_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>change_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>delete_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="2" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Stephane Grappelli</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>add_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>delete_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="3" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Artist formerly known as "Prince"</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>change_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="1" model="fixtures.book"><field type="CharField" name="name">Music for all ages</field><field to="fixtures.person" name="authors" rel="ManyToManyRel"><object><natural>Artist formerly known as "Prince"</natural></object><object><natural>Django Reinhardt</natural></object></field></object></django-objects> - -"""} - -# Database flushing does not work on MySQL with the default storage engine -# because it requires transaction support. -if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.mysql': - __test__['API_TESTS'] += \ -""" -# Reset the database representation of this app. This will delete all data. ->>> management.call_command('flush', verbosity=0, interactive=False) ->>> Article.objects.all() -[<Article: Python program becomes self aware>] - -# Load fixture 1 again, using format discovery ->>> management.call_command('loaddata', 'fixture1', verbosity=0) ->>> Article.objects.all() -[<Article: Time to reform copyright>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>] - -# Try to load fixture 2 using format discovery; this will fail -# because there are two fixture2's in the fixtures directory ->>> management.call_command('loaddata', 'fixture2', verbosity=0) # doctest: +ELLIPSIS -Multiple fixtures named 'fixture2' in '...fixtures'. Aborting. - -# object list is unaffected ->>> Article.objects.all() -[<Article: Time to reform copyright>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>] - -# Dump the current contents of the database as a JSON fixture ->>> management.call_command('dumpdata', 'fixtures', format='json') -[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}] - -# Load fixture 4 (compressed), using format discovery ->>> management.call_command('loaddata', 'fixture4', verbosity=0) ->>> Article.objects.all() -[<Article: Django pets kitten>, <Article: Time to reform copyright>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>] - ->>> management.call_command('flush', verbosity=0, interactive=False) - -# Load fixture 4 (compressed), using format specification ->>> management.call_command('loaddata', 'fixture4.json', verbosity=0) ->>> Article.objects.all() -[<Article: Django pets kitten>, <Article: Python program becomes self aware>] - ->>> management.call_command('flush', verbosity=0, interactive=False) - -# Load fixture 5 (compressed), using format *and* compression specification ->>> management.call_command('loaddata', 'fixture5.json.zip', verbosity=0) ->>> Article.objects.all() -[<Article: WoW subscribers now outnumber readers>, <Article: Python program becomes self aware>] - ->>> management.call_command('flush', verbosity=0, interactive=False) - -# Load fixture 5 (compressed), only compression specification ->>> management.call_command('loaddata', 'fixture5.zip', verbosity=0) ->>> Article.objects.all() -[<Article: WoW subscribers now outnumber readers>, <Article: Python program becomes self aware>] - ->>> management.call_command('flush', verbosity=0, interactive=False) - -# Try to load fixture 5 using format and compression discovery; this will fail -# because there are two fixture5's in the fixtures directory ->>> management.call_command('loaddata', 'fixture5', verbosity=0) # doctest: +ELLIPSIS -Multiple fixtures named 'fixture5' in '...fixtures'. Aborting. - ->>> management.call_command('flush', verbosity=0, interactive=False) - -# Load db fixtures 1 and 2. These will load using the 'default' database identifier implicitly ->>> management.call_command('loaddata', 'db_fixture_1', verbosity=0) ->>> management.call_command('loaddata', 'db_fixture_2', verbosity=0) ->>> Article.objects.all() -[<Article: Who needs more than one database?>, <Article: Who needs to use compressed data?>, <Article: Python program becomes self aware>] - ->>> management.call_command('flush', verbosity=0, interactive=False) - -# Load db fixtures 1 and 2. These will load using the 'default' database identifier explicitly ->>> management.call_command('loaddata', 'db_fixture_1', verbosity=0, using='default') ->>> management.call_command('loaddata', 'db_fixture_2', verbosity=0, using='default') ->>> Article.objects.all() -[<Article: Who needs more than one database?>, <Article: Who needs to use compressed data?>, <Article: Python program becomes self aware>] - ->>> management.call_command('flush', verbosity=0, interactive=False) - -# Try to load db fixture 3. This won't load because the database identifier doesn't match ->>> management.call_command('loaddata', 'db_fixture_3', verbosity=0) ->>> Article.objects.all() -[<Article: Python program becomes self aware>] - ->>> management.call_command('loaddata', 'db_fixture_3', verbosity=0, using='default') ->>> Article.objects.all() -[<Article: Python program becomes self aware>] - ->>> management.call_command('flush', verbosity=0, interactive=False) - -# Load back in fixture 1, we need the articles from it ->>> management.call_command('loaddata', 'fixture1', verbosity=0) - -# Try to load fixture 6 using format discovery ->>> management.call_command('loaddata', 'fixture6', verbosity=0) ->>> Tag.objects.all() -[<Tag: <Article: Time to reform copyright> tagged "copyright">, <Tag: <Article: Time to reform copyright> tagged "law">] - -# Dump the current contents of the database as a JSON fixture ->>> management.call_command('dumpdata', 'fixtures', format='json', use_natural_keys=True) -[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 1, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "copyright", "tagged_id": 3}}, {"pk": 2, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "law", "tagged_id": 3}}, {"pk": 1, "model": "fixtures.person", "fields": {"name": "Django Reinhardt"}}, {"pk": 3, "model": "fixtures.person", "fields": {"name": "Prince"}}, {"pk": 2, "model": "fixtures.person", "fields": {"name": "Stephane Grappelli"}}] - -# Dump the current contents of the database as an XML fixture ->>> management.call_command('dumpdata', 'fixtures', format='xml', use_natural_keys=True) -<?xml version="1.0" encoding="utf-8"?> -<django-objects version="1.0"><object pk="1" model="fixtures.category"><field type="CharField" name="title">News Stories</field><field type="TextField" name="description">Latest news stories</field></object><object pk="3" model="fixtures.article"><field type="CharField" name="headline">Time to reform copyright</field><field type="DateTimeField" name="pub_date">2006-06-16 13:00:00</field></object><object pk="2" model="fixtures.article"><field type="CharField" name="headline">Poker has no place on ESPN</field><field type="DateTimeField" name="pub_date">2006-06-16 12:00:00</field></object><object pk="1" model="fixtures.article"><field type="CharField" name="headline">Python program becomes self aware</field><field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field></object><object pk="1" model="fixtures.tag"><field type="CharField" name="name">copyright</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="2" model="fixtures.tag"><field type="CharField" name="name">law</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="1" model="fixtures.person"><field type="CharField" name="name">Django Reinhardt</field></object><object pk="3" model="fixtures.person"><field type="CharField" name="name">Prince</field></object><object pk="2" model="fixtures.person"><field type="CharField" name="name">Stephane Grappelli</field></object></django-objects> - -""" - -from django.test import TestCase - -class SampleTestCase(TestCase): - fixtures = ['fixture1.json', 'fixture2.json'] - - def testClassFixtures(self): - "Check that test case has installed 4 fixture objects" - self.assertEqual(Article.objects.count(), 4) - self.assertEquals(str(Article.objects.all()), "[<Article: Django conquers world!>, <Article: Copyright is fine the way it is>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>]") diff --git a/tests/modeltests/fixtures/tests.py b/tests/modeltests/fixtures/tests.py new file mode 100644 index 0000000000..799a7328da --- /dev/null +++ b/tests/modeltests/fixtures/tests.py @@ -0,0 +1,336 @@ +import StringIO +import sys + +from django.test import TestCase, TransactionTestCase +from django.conf import settings +from django.core import management +from django.db import DEFAULT_DB_ALIAS + +from models import Article, Blog, Book, Category, Person, Spy, Tag, Visa + +class TestCaseFixtureLoadingTests(TestCase): + fixtures = ['fixture1.json', 'fixture2.json'] + + def testClassFixtures(self): + "Check that test case has installed 4 fixture objects" + self.assertEqual(Article.objects.count(), 4) + self.assertQuerysetEqual(Article.objects.all(), [ + '<Article: Django conquers world!>', + '<Article: Copyright is fine the way it is>', + '<Article: Poker has no place on ESPN>', + '<Article: Python program becomes self aware>' + ]) + +class FixtureLoadingTests(TestCase): + + def _dumpdata_assert(self, args, output, format='json', natural_keys=False, + use_base_manager=False, exclude_list=[]): + new_io = StringIO.StringIO() + management.call_command('dumpdata', *args, **{'format':format, + 'stdout':new_io, + 'stderr':new_io, + 'use_natural_keys':natural_keys, + 'use_base_manager':use_base_manager, + 'exclude': exclude_list}) + command_output = new_io.getvalue().strip() + self.assertEqual(command_output, output) + + def test_initial_data(self): + # Syncdb introduces 1 initial data object from initial_data.json. + self.assertQuerysetEqual(Article.objects.all(), [ + '<Article: Python program becomes self aware>' + ]) + + def test_loading_and_dumping(self): + new_io = StringIO.StringIO() + + # Load fixture 1. Single JSON file, with two objects. + management.call_command('loaddata', 'fixture1.json', verbosity=0, commit=False) + self.assertQuerysetEqual(Article.objects.all(), [ + '<Article: Time to reform copyright>', + '<Article: Poker has no place on ESPN>', + '<Article: Python program becomes self aware>' + ]) + + # Dump the current contents of the database as a JSON fixture + self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]') + + # Try just dumping the contents of fixtures.Category + self._dumpdata_assert(['fixtures.Category'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}]') + + # ...and just fixtures.Article + self._dumpdata_assert(['fixtures.Article'], '[{"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]') + + # ...and both + self._dumpdata_assert(['fixtures.Category', 'fixtures.Article'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]') + + # Specify a specific model twice + self._dumpdata_assert(['fixtures.Article', 'fixtures.Article'], '[{"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]') + + # Specify a dump that specifies Article both explicitly and implicitly + self._dumpdata_assert(['fixtures.Article', 'fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]') + + # Same again, but specify in the reverse order + self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]') + + # Specify one model from one application, and an entire other application. + self._dumpdata_assert(['fixtures.Category', 'sites'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 1, "model": "sites.site", "fields": {"domain": "example.com", "name": "example.com"}}]') + + # Load fixture 2. JSON file imported by default. Overwrites some existing objects + management.call_command('loaddata', 'fixture2.json', verbosity=0, commit=False) + self.assertQuerysetEqual(Article.objects.all(), [ + '<Article: Django conquers world!>', + '<Article: Copyright is fine the way it is>', + '<Article: Poker has no place on ESPN>', + '<Article: Python program becomes self aware>' + ]) + + # Load fixture 3, XML format. + management.call_command('loaddata', 'fixture3.xml', verbosity=0, commit=False) + self.assertQuerysetEqual(Article.objects.all(), [ + '<Article: XML identified as leading cause of cancer>', + '<Article: Django conquers world!>', + '<Article: Copyright is fine the way it is>', + '<Article: Poker on TV is great!>', + '<Article: Python program becomes self aware>' + ]) + + # Load fixture 6, JSON file with dynamic ContentType fields. Testing ManyToOne. + management.call_command('loaddata', 'fixture6.json', verbosity=0, commit=False) + self.assertQuerysetEqual(Tag.objects.all(), [ + '<Tag: <Article: Copyright is fine the way it is> tagged "copyright">', + '<Tag: <Article: Copyright is fine the way it is> tagged "law">' + ]) + + # Load fixture 7, XML file with dynamic ContentType fields. Testing ManyToOne. + management.call_command('loaddata', 'fixture7.xml', verbosity=0, commit=False) + self.assertQuerysetEqual(Tag.objects.all(), [ + '<Tag: <Article: Copyright is fine the way it is> tagged "copyright">', + '<Tag: <Article: Copyright is fine the way it is> tagged "legal">', + '<Tag: <Article: Django conquers world!> tagged "django">', + '<Tag: <Article: Django conquers world!> tagged "world domination">' + ]) + + # Load fixture 8, JSON file with dynamic Permission fields. Testing ManyToMany. + management.call_command('loaddata', 'fixture8.json', verbosity=0, commit=False) + self.assertQuerysetEqual(Visa.objects.all(), [ + '<Visa: Django Reinhardt Can add user, Can change user, Can delete user>', + '<Visa: Stephane Grappelli Can add user>', + '<Visa: Prince >' + ]) + + # Load fixture 9, XML file with dynamic Permission fields. Testing ManyToMany. + management.call_command('loaddata', 'fixture9.xml', verbosity=0, commit=False) + self.assertQuerysetEqual(Visa.objects.all(), [ + '<Visa: Django Reinhardt Can add user, Can change user, Can delete user>', + '<Visa: Stephane Grappelli Can add user, Can delete user>', + '<Visa: Artist formerly known as "Prince" Can change user>' + ]) + + self.assertQuerysetEqual(Book.objects.all(), [ + '<Book: Music for all ages by Artist formerly known as "Prince" and Django Reinhardt>' + ]) + + # Load a fixture that doesn't exist + management.call_command('loaddata', 'unknown.json', verbosity=0, commit=False) + + # object list is unaffected + self.assertQuerysetEqual(Article.objects.all(), [ + '<Article: XML identified as leading cause of cancer>', + '<Article: Django conquers world!>', + '<Article: Copyright is fine the way it is>', + '<Article: Poker on TV is great!>', + '<Article: Python program becomes self aware>' + ]) + + # By default, you get raw keys on dumpdata + self._dumpdata_assert(['fixtures.book'], '[{"pk": 1, "model": "fixtures.book", "fields": {"name": "Music for all ages", "authors": [3, 1]}}]') + + # But you can get natural keys if you ask for them and they are available + self._dumpdata_assert(['fixtures.book'], '[{"pk": 1, "model": "fixtures.book", "fields": {"name": "Music for all ages", "authors": [["Artist formerly known as \\"Prince\\""], ["Django Reinhardt"]]}}]', natural_keys=True) + + # Dump the current contents of the database as a JSON fixture + self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 5, "model": "fixtures.article", "fields": {"headline": "XML identified as leading cause of cancer", "pub_date": "2006-06-16 16:00:00"}}, {"pk": 4, "model": "fixtures.article", "fields": {"headline": "Django conquers world!", "pub_date": "2006-06-16 15:00:00"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Copyright is fine the way it is", "pub_date": "2006-06-16 14:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker on TV is great!", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 1, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "copyright", "tagged_id": 3}}, {"pk": 2, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "legal", "tagged_id": 3}}, {"pk": 3, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "django", "tagged_id": 4}}, {"pk": 4, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "world domination", "tagged_id": 4}}, {"pk": 3, "model": "fixtures.person", "fields": {"name": "Artist formerly known as \\"Prince\\""}}, {"pk": 1, "model": "fixtures.person", "fields": {"name": "Django Reinhardt"}}, {"pk": 2, "model": "fixtures.person", "fields": {"name": "Stephane Grappelli"}}, {"pk": 1, "model": "fixtures.visa", "fields": {"person": ["Django Reinhardt"], "permissions": [["add_user", "auth", "user"], ["change_user", "auth", "user"], ["delete_user", "auth", "user"]]}}, {"pk": 2, "model": "fixtures.visa", "fields": {"person": ["Stephane Grappelli"], "permissions": [["add_user", "auth", "user"], ["delete_user", "auth", "user"]]}}, {"pk": 3, "model": "fixtures.visa", "fields": {"person": ["Artist formerly known as \\"Prince\\""], "permissions": [["change_user", "auth", "user"]]}}, {"pk": 1, "model": "fixtures.book", "fields": {"name": "Music for all ages", "authors": [["Artist formerly known as \\"Prince\\""], ["Django Reinhardt"]]}}]', natural_keys=True) + + # Dump the current contents of the database as an XML fixture + self._dumpdata_assert(['fixtures'], """<?xml version="1.0" encoding="utf-8"?> +<django-objects version="1.0"><object pk="1" model="fixtures.category"><field type="CharField" name="title">News Stories</field><field type="TextField" name="description">Latest news stories</field></object><object pk="5" model="fixtures.article"><field type="CharField" name="headline">XML identified as leading cause of cancer</field><field type="DateTimeField" name="pub_date">2006-06-16 16:00:00</field></object><object pk="4" model="fixtures.article"><field type="CharField" name="headline">Django conquers world!</field><field type="DateTimeField" name="pub_date">2006-06-16 15:00:00</field></object><object pk="3" model="fixtures.article"><field type="CharField" name="headline">Copyright is fine the way it is</field><field type="DateTimeField" name="pub_date">2006-06-16 14:00:00</field></object><object pk="2" model="fixtures.article"><field type="CharField" name="headline">Poker on TV is great!</field><field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field></object><object pk="1" model="fixtures.article"><field type="CharField" name="headline">Python program becomes self aware</field><field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field></object><object pk="1" model="fixtures.tag"><field type="CharField" name="name">copyright</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="2" model="fixtures.tag"><field type="CharField" name="name">legal</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="3" model="fixtures.tag"><field type="CharField" name="name">django</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">4</field></object><object pk="4" model="fixtures.tag"><field type="CharField" name="name">world domination</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">4</field></object><object pk="3" model="fixtures.person"><field type="CharField" name="name">Artist formerly known as "Prince"</field></object><object pk="1" model="fixtures.person"><field type="CharField" name="name">Django Reinhardt</field></object><object pk="2" model="fixtures.person"><field type="CharField" name="name">Stephane Grappelli</field></object><object pk="1" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Django Reinhardt</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>add_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>change_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>delete_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="2" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Stephane Grappelli</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>add_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>delete_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="3" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Artist formerly known as "Prince"</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>change_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="1" model="fixtures.book"><field type="CharField" name="name">Music for all ages</field><field to="fixtures.person" name="authors" rel="ManyToManyRel"><object><natural>Artist formerly known as "Prince"</natural></object><object><natural>Django Reinhardt</natural></object></field></object></django-objects>""", format='xml', natural_keys=True) + + def test_dumpdata_with_excludes(self): + # Load fixture1 which has a site, two articles, and a category + management.call_command('loaddata', 'fixture1.json', verbosity=0, commit=False) + + # Excluding fixtures app should only leave sites + self._dumpdata_assert( + ['sites', 'fixtures'], + '[{"pk": 1, "model": "sites.site", "fields": {"domain": "example.com", "name": "example.com"}}]', + exclude_list=['fixtures']) + + # Excluding fixtures.Article should leave fixtures.Category + self._dumpdata_assert( + ['sites', 'fixtures'], + '[{"pk": 1, "model": "sites.site", "fields": {"domain": "example.com", "name": "example.com"}}, {"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}]', + exclude_list=['fixtures.Article']) + + # Excluding fixtures and fixtures.Article should be a no-op + self._dumpdata_assert( + ['sites', 'fixtures'], + '[{"pk": 1, "model": "sites.site", "fields": {"domain": "example.com", "name": "example.com"}}, {"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}]', + exclude_list=['fixtures.Article']) + + # Excluding sites and fixtures.Article should only leave fixtures.Category + self._dumpdata_assert( + ['sites', 'fixtures'], + '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}]', + exclude_list=['fixtures.Article', 'sites']) + + # Excluding a bogus app should throw an error + self.assertRaises(SystemExit, + self._dumpdata_assert, + ['fixtures', 'sites'], + '', + exclude_list=['foo_app']) + + # Excluding a bogus model should throw an error + self.assertRaises(SystemExit, + self._dumpdata_assert, + ['fixtures', 'sites'], + '', + exclude_list=['fixtures.FooModel']) + + def test_dumpdata_with_filtering_manager(self): + Spy(name='Paul').save() + Spy(name='Alex', cover_blown=True).save() + self.assertQuerysetEqual(Spy.objects.all(), + ['<Spy: Paul>']) + # Use the default manager + self._dumpdata_assert(['fixtures.Spy'],'[{"pk": 1, "model": "fixtures.spy", "fields": {"cover_blown": false}}]') + # Dump using Django's base manager. Should return all objects, + # even those normally filtered by the manager + self._dumpdata_assert(['fixtures.Spy'], '[{"pk": 2, "model": "fixtures.spy", "fields": {"cover_blown": true}}, {"pk": 1, "model": "fixtures.spy", "fields": {"cover_blown": false}}]', use_base_manager=True) + + def test_compress_format_loading(self): + # Load fixture 4 (compressed), using format specification + management.call_command('loaddata', 'fixture4.json', verbosity=0, commit=False) + self.assertQuerysetEqual(Article.objects.all(), [ + '<Article: Django pets kitten>', + '<Article: Python program becomes self aware>' + ]) + + def test_compressed_specified_loading(self): + # Load fixture 5 (compressed), using format *and* compression specification + management.call_command('loaddata', 'fixture5.json.zip', verbosity=0, commit=False) + self.assertQuerysetEqual(Article.objects.all(), [ + '<Article: WoW subscribers now outnumber readers>', + '<Article: Python program becomes self aware>' + ]) + + def test_compressed_loading(self): + # Load fixture 5 (compressed), only compression specification + management.call_command('loaddata', 'fixture5.zip', verbosity=0, commit=False) + self.assertQuerysetEqual(Article.objects.all(), [ + '<Article: WoW subscribers now outnumber readers>', + '<Article: Python program becomes self aware>' + ]) + + def test_ambiguous_compressed_fixture(self): + # The name "fixture5" is ambigous, so loading it will raise an error + new_io = StringIO.StringIO() + management.call_command('loaddata', 'fixture5', verbosity=0, stderr=new_io, commit=False) + output = new_io.getvalue().strip().split('\n') + self.assertEqual(len(output), 1) + self.assertTrue(output[0].startswith("Multiple fixtures named 'fixture5'")) + + def test_db_loading(self): + # Load db fixtures 1 and 2. These will load using the 'default' database identifier implicitly + management.call_command('loaddata', 'db_fixture_1', verbosity=0, commit=False) + management.call_command('loaddata', 'db_fixture_2', verbosity=0, commit=False) + self.assertQuerysetEqual(Article.objects.all(), [ + '<Article: Who needs more than one database?>', + '<Article: Who needs to use compressed data?>', + '<Article: Python program becomes self aware>' + ]) + + def test_loading_using(self): + # Load db fixtures 1 and 2. These will load using the 'default' database identifier explicitly + management.call_command('loaddata', 'db_fixture_1', verbosity=0, using='default', commit=False) + management.call_command('loaddata', 'db_fixture_2', verbosity=0, using='default', commit=False) + self.assertQuerysetEqual(Article.objects.all(), [ + '<Article: Who needs more than one database?>', + '<Article: Who needs to use compressed data?>', + '<Article: Python program becomes self aware>' + ]) + + def test_unmatched_identifier_loading(self): + # Try to load db fixture 3. This won't load because the database identifier doesn't match + management.call_command('loaddata', 'db_fixture_3', verbosity=0, commit=False) + self.assertQuerysetEqual(Article.objects.all(), [ + '<Article: Python program becomes self aware>' + ]) + + management.call_command('loaddata', 'db_fixture_3', verbosity=0, using='default', commit=False) + self.assertQuerysetEqual(Article.objects.all(), [ + '<Article: Python program becomes self aware>' + ]) + + def test_output_formats(self): + # Load back in fixture 1, we need the articles from it + management.call_command('loaddata', 'fixture1', verbosity=0, commit=False) + + # Try to load fixture 6 using format discovery + management.call_command('loaddata', 'fixture6', verbosity=0, commit=False) + self.assertQuerysetEqual(Tag.objects.all(), [ + '<Tag: <Article: Time to reform copyright> tagged "copyright">', + '<Tag: <Article: Time to reform copyright> tagged "law">' + ]) + + # Dump the current contents of the database as a JSON fixture + self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 1, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "copyright", "tagged_id": 3}}, {"pk": 2, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "law", "tagged_id": 3}}, {"pk": 1, "model": "fixtures.person", "fields": {"name": "Django Reinhardt"}}, {"pk": 3, "model": "fixtures.person", "fields": {"name": "Prince"}}, {"pk": 2, "model": "fixtures.person", "fields": {"name": "Stephane Grappelli"}}]', natural_keys=True) + + # Dump the current contents of the database as an XML fixture + self._dumpdata_assert(['fixtures'], """<?xml version="1.0" encoding="utf-8"?> +<django-objects version="1.0"><object pk="1" model="fixtures.category"><field type="CharField" name="title">News Stories</field><field type="TextField" name="description">Latest news stories</field></object><object pk="3" model="fixtures.article"><field type="CharField" name="headline">Time to reform copyright</field><field type="DateTimeField" name="pub_date">2006-06-16 13:00:00</field></object><object pk="2" model="fixtures.article"><field type="CharField" name="headline">Poker has no place on ESPN</field><field type="DateTimeField" name="pub_date">2006-06-16 12:00:00</field></object><object pk="1" model="fixtures.article"><field type="CharField" name="headline">Python program becomes self aware</field><field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field></object><object pk="1" model="fixtures.tag"><field type="CharField" name="name">copyright</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="2" model="fixtures.tag"><field type="CharField" name="name">law</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="1" model="fixtures.person"><field type="CharField" name="name">Django Reinhardt</field></object><object pk="3" model="fixtures.person"><field type="CharField" name="name">Prince</field></object><object pk="2" model="fixtures.person"><field type="CharField" name="name">Stephane Grappelli</field></object></django-objects>""", format='xml', natural_keys=True) + +if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.mysql': + class FixtureTransactionTests(TransactionTestCase): + def _dumpdata_assert(self, args, output, format='json'): + new_io = StringIO.StringIO() + management.call_command('dumpdata', *args, **{'format':format, 'stdout':new_io}) + command_output = new_io.getvalue().strip() + self.assertEqual(command_output, output) + + def test_format_discovery(self): + # Load fixture 1 again, using format discovery + management.call_command('loaddata', 'fixture1', verbosity=0, commit=False) + self.assertQuerysetEqual(Article.objects.all(), [ + '<Article: Time to reform copyright>', + '<Article: Poker has no place on ESPN>', + '<Article: Python program becomes self aware>' + ]) + + # Try to load fixture 2 using format discovery; this will fail + # because there are two fixture2's in the fixtures directory + new_io = StringIO.StringIO() + management.call_command('loaddata', 'fixture2', verbosity=0, stderr=new_io) + output = new_io.getvalue().strip().split('\n') + self.assertEqual(len(output), 1) + self.assertTrue(output[0].startswith("Multiple fixtures named 'fixture2'")) + + # object list is unaffected + self.assertQuerysetEqual(Article.objects.all(), [ + '<Article: Time to reform copyright>', + '<Article: Poker has no place on ESPN>', + '<Article: Python program becomes self aware>' + ]) + + # Dump the current contents of the database as a JSON fixture + self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]') + + # Load fixture 4 (compressed), using format discovery + management.call_command('loaddata', 'fixture4', verbosity=0, commit=False) + self.assertQuerysetEqual(Article.objects.all(), [ + '<Article: Django pets kitten>', + '<Article: Time to reform copyright>', + '<Article: Poker has no place on ESPN>', + '<Article: Python program becomes self aware>' + ]) diff --git a/tests/modeltests/fixtures_model_package/models/__init__.py b/tests/modeltests/fixtures_model_package/models/__init__.py index 1581102b88..c0450b27bf 100644 --- a/tests/modeltests/fixtures_model_package/models/__init__.py +++ b/tests/modeltests/fixtures_model_package/models/__init__.py @@ -12,43 +12,3 @@ class Article(models.Model): app_label = 'fixtures_model_package' ordering = ('-pub_date', 'headline') -__test__ = {'API_TESTS': """ ->>> from django.core import management ->>> from django.db.models import get_app - -# Reset the database representation of this app. -# This will return the database to a clean initial state. ->>> management.call_command('flush', verbosity=0, interactive=False) - -# Syncdb introduces 1 initial data object from initial_data.json. ->>> Article.objects.all() -[<Article: Python program becomes self aware>] - -# Load fixture 1. Single JSON file, with two objects. ->>> management.call_command('loaddata', 'fixture1.json', verbosity=0) ->>> Article.objects.all() -[<Article: Time to reform copyright>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>] - -# Load fixture 2. JSON file imported by default. Overwrites some existing objects ->>> management.call_command('loaddata', 'fixture2.json', verbosity=0) ->>> Article.objects.all() -[<Article: Django conquers world!>, <Article: Copyright is fine the way it is>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>] - -# Load a fixture that doesn't exist ->>> management.call_command('loaddata', 'unknown.json', verbosity=0) - -# object list is unaffected ->>> Article.objects.all() -[<Article: Django conquers world!>, <Article: Copyright is fine the way it is>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>] -"""} - - -from django.test import TestCase - -class SampleTestCase(TestCase): - fixtures = ['fixture1.json', 'fixture2.json'] - - def testClassFixtures(self): - "Check that test case has installed 4 fixture objects" - self.assertEqual(Article.objects.count(), 4) - self.assertEquals(str(Article.objects.all()), "[<Article: Django conquers world!>, <Article: Copyright is fine the way it is>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>]") diff --git a/tests/modeltests/fixtures_model_package/tests.py b/tests/modeltests/fixtures_model_package/tests.py new file mode 100644 index 0000000000..1fae5ee807 --- /dev/null +++ b/tests/modeltests/fixtures_model_package/tests.py @@ -0,0 +1,71 @@ +from django.core import management +from django.test import TestCase + +from models import Article + + +class SampleTestCase(TestCase): + fixtures = ['fixture1.json', 'fixture2.json'] + + def testClassFixtures(self): + "Test cases can load fixture objects into models defined in packages" + self.assertEqual(Article.objects.count(), 4) + self.assertQuerysetEqual( + Article.objects.all(),[ + "Django conquers world!", + "Copyright is fine the way it is", + "Poker has no place on ESPN", + "Python program becomes self aware" + ], + lambda a: a.headline + ) + + +class FixtureTestCase(TestCase): + def test_initial_data(self): + "Fixtures can load initial data into models defined in packages" + #Syncdb introduces 1 initial data object from initial_data.json + self.assertQuerysetEqual( + Article.objects.all(), [ + "Python program becomes self aware" + ], + lambda a: a.headline + ) + + def test_loaddata(self): + "Fixtures can load data into models defined in packages" + # Load fixture 1. Single JSON file, with two objects + management.call_command("loaddata", "fixture1.json", verbosity=0, commit=False) + self.assertQuerysetEqual( + Article.objects.all(), [ + "Time to reform copyright", + "Poker has no place on ESPN", + "Python program becomes self aware", + ], + lambda a: a.headline, + ) + + # Load fixture 2. JSON file imported by default. Overwrites some + # existing objects + management.call_command("loaddata", "fixture2.json", verbosity=0, commit=False) + self.assertQuerysetEqual( + Article.objects.all(), [ + "Django conquers world!", + "Copyright is fine the way it is", + "Poker has no place on ESPN", + "Python program becomes self aware", + ], + lambda a: a.headline, + ) + + # Load a fixture that doesn't exist + management.call_command("loaddata", "unknown.json", verbosity=0, commit=False) + self.assertQuerysetEqual( + Article.objects.all(), [ + "Django conquers world!", + "Copyright is fine the way it is", + "Poker has no place on ESPN", + "Python program becomes self aware", + ], + lambda a: a.headline, + ) diff --git a/tests/modeltests/force_insert_update/models.py b/tests/modeltests/force_insert_update/models.py index 2489740e98..9516be7718 100644 --- a/tests/modeltests/force_insert_update/models.py +++ b/tests/modeltests/force_insert_update/models.py @@ -11,54 +11,3 @@ class Counter(models.Model): class WithCustomPK(models.Model): name = models.IntegerField(primary_key=True) value = models.IntegerField() - -__test__ = {"API_TESTS": """ ->>> c = Counter.objects.create(name="one", value=1) - -# The normal case ->>> c.value = 2 ->>> c.save() - -# Same thing, via an update ->>> c.value = 3 ->>> c.save(force_update=True) - -# Won't work because force_update and force_insert are mutually exclusive ->>> c.value = 4 ->>> c.save(force_insert=True, force_update=True) -Traceback (most recent call last): -... -ValueError: Cannot force both insert and updating in model saving. - -# Try to update something that doesn't have a primary key in the first place. ->>> c1 = Counter(name="two", value=2) ->>> c1.save(force_update=True) -Traceback (most recent call last): -... -ValueError: Cannot force an update in save() with no primary key. - ->>> c1.save(force_insert=True) - -# Won't work because we can't insert a pk of the same value. ->>> sid = transaction.savepoint() ->>> c.value = 5 ->>> try: -... c.save(force_insert=True) -... except Exception, e: -... if isinstance(e, IntegrityError): -... print "Pass" -... else: -... print "Fail with %s" % type(e) -Pass ->>> transaction.savepoint_rollback(sid) - -# Trying to update should still fail, even with manual primary keys, if the -# data isn't in the database already. ->>> obj = WithCustomPK(name=1, value=1) ->>> obj.save(force_update=True) -Traceback (most recent call last): -... -DatabaseError: ... - -""" -} diff --git a/tests/modeltests/force_insert_update/tests.py b/tests/modeltests/force_insert_update/tests.py new file mode 100644 index 0000000000..bd3eb7dcf6 --- /dev/null +++ b/tests/modeltests/force_insert_update/tests.py @@ -0,0 +1,38 @@ +from django.db import transaction, IntegrityError, DatabaseError +from django.test import TestCase + +from models import Counter, WithCustomPK + + +class ForceTests(TestCase): + def test_force_update(self): + c = Counter.objects.create(name="one", value=1) + # The normal case + + c.value = 2 + c.save() + # Same thing, via an update + c.value = 3 + c.save(force_update=True) + + # Won't work because force_update and force_insert are mutually + # exclusive + c.value = 4 + self.assertRaises(ValueError, c.save, force_insert=True, force_update=True) + + # Try to update something that doesn't have a primary key in the first + # place. + c1 = Counter(name="two", value=2) + self.assertRaises(ValueError, c1.save, force_update=True) + c1.save(force_insert=True) + + # Won't work because we can't insert a pk of the same value. + sid = transaction.savepoint() + c.value = 5 + self.assertRaises(IntegrityError, c.save, force_insert=True) + transaction.savepoint_rollback(sid) + + # Trying to update should still fail, even with manual primary keys, if + # the data isn't in the database already. + obj = WithCustomPK(name=1, value=1) + self.assertRaises(DatabaseError, obj.save, force_update=True) diff --git a/tests/modeltests/get_latest/models.py b/tests/modeltests/get_latest/models.py index 624f3a879a..1eeb299267 100644 --- a/tests/modeltests/get_latest/models.py +++ b/tests/modeltests/get_latest/models.py @@ -28,52 +28,3 @@ class Person(models.Model): def __unicode__(self): return self.name - -__test__ = {'API_TESTS':""" -# Because no Articles exist yet, latest() raises ArticleDoesNotExist. ->>> Article.objects.latest() -Traceback (most recent call last): - ... -DoesNotExist: Article matching query does not exist. - -# Create a couple of Articles. ->>> from datetime import datetime ->>> a1 = Article(headline='Article 1', pub_date=datetime(2005, 7, 26), expire_date=datetime(2005, 9, 1)) ->>> a1.save() ->>> a2 = Article(headline='Article 2', pub_date=datetime(2005, 7, 27), expire_date=datetime(2005, 7, 28)) ->>> a2.save() ->>> a3 = Article(headline='Article 3', pub_date=datetime(2005, 7, 27), expire_date=datetime(2005, 8, 27)) ->>> a3.save() ->>> a4 = Article(headline='Article 4', pub_date=datetime(2005, 7, 28), expire_date=datetime(2005, 7, 30)) ->>> a4.save() - -# Get the latest Article. ->>> Article.objects.latest() -<Article: Article 4> - -# Get the latest Article that matches certain filters. ->>> Article.objects.filter(pub_date__lt=datetime(2005, 7, 27)).latest() -<Article: Article 1> - -# Pass a custom field name to latest() to change the field that's used to -# determine the latest object. ->>> Article.objects.latest('expire_date') -<Article: Article 1> - ->>> Article.objects.filter(pub_date__gt=datetime(2005, 7, 26)).latest('expire_date') -<Article: Article 3> - -# You can still use latest() with a model that doesn't have "get_latest_by" -# set -- just pass in the field name manually. ->>> p1 = Person(name='Ralph', birthday=datetime(1950, 1, 1)) ->>> p1.save() ->>> p2 = Person(name='Stephanie', birthday=datetime(1960, 2, 3)) ->>> p2.save() ->>> Person.objects.latest() -Traceback (most recent call last): - ... -AssertionError: latest() requires either a field_name parameter or 'get_latest_by' in the model - ->>> Person.objects.latest('birthday') -<Person: Stephanie> -"""} diff --git a/tests/modeltests/get_latest/tests.py b/tests/modeltests/get_latest/tests.py new file mode 100644 index 0000000000..3c3588bba0 --- /dev/null +++ b/tests/modeltests/get_latest/tests.py @@ -0,0 +1,53 @@ +from datetime import datetime + +from django.test import TestCase + +from models import Article, Person + + +class LatestTests(TestCase): + def test_latest(self): + # Because no Articles exist yet, latest() raises ArticleDoesNotExist. + self.assertRaises(Article.DoesNotExist, Article.objects.latest) + + a1 = Article.objects.create( + headline="Article 1", pub_date=datetime(2005, 7, 26), + expire_date=datetime(2005, 9, 1) + ) + a2 = Article.objects.create( + headline="Article 2", pub_date=datetime(2005, 7, 27), + expire_date=datetime(2005, 7, 28) + ) + a3 = Article.objects.create( + headline="Article 3", pub_date=datetime(2005, 7, 27), + expire_date=datetime(2005, 8, 27) + ) + a4 = Article.objects.create( + headline="Article 4", pub_date=datetime(2005, 7, 28), + expire_date=datetime(2005, 7, 30) + ) + + # Get the latest Article. + self.assertEqual(Article.objects.latest(), a4) + # Get the latest Article that matches certain filters. + self.assertEqual( + Article.objects.filter(pub_date__lt=datetime(2005, 7, 27)).latest(), + a1 + ) + + # Pass a custom field name to latest() to change the field that's used + # to determine the latest object. + self.assertEqual(Article.objects.latest('expire_date'), a1) + self.assertEqual( + Article.objects.filter(pub_date__gt=datetime(2005, 7, 26)).latest('expire_date'), + a3, + ) + + def test_latest_manual(self): + # You can still use latest() with a model that doesn't have + # "get_latest_by" set -- just pass in the field name manually. + p1 = Person.objects.create(name="Ralph", birthday=datetime(1950, 1, 1)) + p2 = Person.objects.create(name="Stephanie", birthday=datetime(1960, 2, 3)) + self.assertRaises(AssertionError, Person.objects.latest) + + self.assertEqual(Person.objects.latest("birthday"), p2) diff --git a/tests/modeltests/get_object_or_404/models.py b/tests/modeltests/get_object_or_404/models.py index b2812e61e7..eb3cd8254d 100644 --- a/tests/modeltests/get_object_or_404/models.py +++ b/tests/modeltests/get_object_or_404/models.py @@ -32,76 +32,3 @@ class Article(models.Model): def __unicode__(self): return self.title - -__test__ = {'API_TESTS':""" -# Create some Authors. ->>> a = Author.objects.create(name="Brave Sir Robin") ->>> a.save() ->>> a2 = Author.objects.create(name="Patsy") ->>> a2.save() - -# No Articles yet, so we should get a Http404 error. ->>> get_object_or_404(Article, title="Foo") -Traceback (most recent call last): -... -Http404: No Article matches the given query. - -# Create an Article. ->>> article = Article.objects.create(title="Run away!") ->>> article.authors = [a, a2] ->>> article.save() - -# get_object_or_404 can be passed a Model to query. ->>> get_object_or_404(Article, title__contains="Run") -<Article: Run away!> - -# We can also use the Article manager through an Author object. ->>> get_object_or_404(a.article_set, title__contains="Run") -<Article: Run away!> - -# No articles containing "Camelot". This should raise a Http404 error. ->>> get_object_or_404(a.article_set, title__contains="Camelot") -Traceback (most recent call last): -... -Http404: No Article matches the given query. - -# Custom managers can be used too. ->>> get_object_or_404(Article.by_a_sir, title="Run away!") -<Article: Run away!> - -# QuerySets can be used too. ->>> get_object_or_404(Article.objects.all(), title__contains="Run") -<Article: Run away!> - -# Just as when using a get() lookup, you will get an error if more than one -# object is returned. ->>> get_object_or_404(Author.objects.all()) -Traceback (most recent call last): -... -MultipleObjectsReturned: get() returned more than one Author -- it returned ...! Lookup parameters were {} - -# Using an EmptyQuerySet raises a Http404 error. ->>> get_object_or_404(Article.objects.none(), title__contains="Run") -Traceback (most recent call last): -... -Http404: No Article matches the given query. - -# get_list_or_404 can be used to get lists of objects ->>> get_list_or_404(a.article_set, title__icontains='Run') -[<Article: Run away!>] - -# Http404 is returned if the list is empty. ->>> get_list_or_404(a.article_set, title__icontains='Shrubbery') -Traceback (most recent call last): -... -Http404: No Article matches the given query. - -# Custom managers can be used too. ->>> get_list_or_404(Article.by_a_sir, title__icontains="Run") -[<Article: Run away!>] - -# QuerySets can be used too. ->>> get_list_or_404(Article.objects.all(), title__icontains="Run") -[<Article: Run away!>] - -"""} diff --git a/tests/modeltests/get_object_or_404/tests.py b/tests/modeltests/get_object_or_404/tests.py new file mode 100644 index 0000000000..b8c4f7510b --- /dev/null +++ b/tests/modeltests/get_object_or_404/tests.py @@ -0,0 +1,80 @@ +from django.http import Http404 +from django.shortcuts import get_object_or_404, get_list_or_404 +from django.test import TestCase + +from models import Author, Article + + +class GetObjectOr404Tests(TestCase): + def test_get_object_or_404(self): + a1 = Author.objects.create(name="Brave Sir Robin") + a2 = Author.objects.create(name="Patsy") + + # No Articles yet, so we should get a Http404 error. + self.assertRaises(Http404, get_object_or_404, Article, title="Foo") + + article = Article.objects.create(title="Run away!") + article.authors = [a1, a2] + # get_object_or_404 can be passed a Model to query. + self.assertEqual( + get_object_or_404(Article, title__contains="Run"), + article + ) + + # We can also use the Article manager through an Author object. + self.assertEqual( + get_object_or_404(a1.article_set, title__contains="Run"), + article + ) + + # No articles containing "Camelot". This should raise a Http404 error. + self.assertRaises(Http404, + get_object_or_404, a1.article_set, title__contains="Camelot" + ) + + # Custom managers can be used too. + self.assertEqual( + get_object_or_404(Article.by_a_sir, title="Run away!"), + article + ) + + # QuerySets can be used too. + self.assertEqual( + get_object_or_404(Article.objects.all(), title__contains="Run"), + article + ) + + # Just as when using a get() lookup, you will get an error if more than + # one object is returned. + + self.assertRaises(Author.MultipleObjectsReturned, + get_object_or_404, Author.objects.all() + ) + + # Using an EmptyQuerySet raises a Http404 error. + self.assertRaises(Http404, + get_object_or_404, Article.objects.none(), title__contains="Run" + ) + + # get_list_or_404 can be used to get lists of objects + self.assertEqual( + get_list_or_404(a1.article_set, title__icontains="Run"), + [article] + ) + + # Http404 is returned if the list is empty. + self.assertRaises(Http404, + get_list_or_404, a1.article_set, title__icontains="Shrubbery" + ) + + # Custom managers can be used too. + self.assertEqual( + get_list_or_404(Article.by_a_sir, title__icontains="Run"), + [article] + ) + + # QuerySets can be used too. + self.assertEqual( + get_list_or_404(Article.objects.all(), title__icontains="Run"), + [article] + ) diff --git a/tests/modeltests/get_or_create/models.py b/tests/modeltests/get_or_create/models.py index 56baa5c1ed..db5719b79e 100644 --- a/tests/modeltests/get_or_create/models.py +++ b/tests/modeltests/get_or_create/models.py @@ -19,65 +19,3 @@ class Person(models.Model): class ManualPrimaryKeyTest(models.Model): id = models.IntegerField(primary_key=True) data = models.CharField(max_length=100) - -__test__ = {'API_TESTS':""" -# Acting as a divine being, create an Person. ->>> from datetime import date ->>> p = Person(first_name='John', last_name='Lennon', birthday=date(1940, 10, 9)) ->>> p.save() - -# Only one Person is in the database at this point. ->>> Person.objects.count() -1 - -# get_or_create() a person with similar first names. ->>> p, created = Person.objects.get_or_create(first_name='John', last_name='Lennon', defaults={'birthday': date(1940, 10, 9)}) - -# get_or_create() didn't have to create an object. ->>> created -False - -# There's still only one Person in the database. ->>> Person.objects.count() -1 - -# get_or_create() a Person with a different name. ->>> p, created = Person.objects.get_or_create(first_name='George', last_name='Harrison', defaults={'birthday': date(1943, 2, 25)}) ->>> created -True ->>> Person.objects.count() -2 - -# If we execute the exact same statement, it won't create a Person. ->>> p, created = Person.objects.get_or_create(first_name='George', last_name='Harrison', defaults={'birthday': date(1943, 2, 25)}) ->>> created -False ->>> Person.objects.count() -2 - -# If you don't specify a value or default value for all required fields, you -# will get an error. ->>> try: -... p, created = Person.objects.get_or_create(first_name='Tom', last_name='Smith') -... except Exception, e: -... if isinstance(e, IntegrityError): -... print "Pass" -... else: -... print "Fail with %s" % type(e) -Pass - -# If you specify an existing primary key, but different other fields, then you -# will get an error and data will not be updated. ->>> m = ManualPrimaryKeyTest(id=1, data='Original') ->>> m.save() ->>> try: -... m, created = ManualPrimaryKeyTest.objects.get_or_create(id=1, data='Different') -... except Exception, e: -... if isinstance(e, IntegrityError): -... print "Pass" -... else: -... print "Fail with %s" % type(e) -Pass ->>> ManualPrimaryKeyTest.objects.get(id=1).data == 'Original' -True -"""} diff --git a/tests/modeltests/get_or_create/tests.py b/tests/modeltests/get_or_create/tests.py new file mode 100644 index 0000000000..1999b20c76 --- /dev/null +++ b/tests/modeltests/get_or_create/tests.py @@ -0,0 +1,52 @@ +from datetime import date + +from django.db import IntegrityError +from django.test import TransactionTestCase + +from models import Person, ManualPrimaryKeyTest + + +class GetOrCreateTests(TransactionTestCase): + def test_get_or_create(self): + p = Person.objects.create( + first_name='John', last_name='Lennon', birthday=date(1940, 10, 9) + ) + + p, created = Person.objects.get_or_create( + first_name="John", last_name="Lennon", defaults={ + "birthday": date(1940, 10, 9) + } + ) + self.assertFalse(created) + self.assertEqual(Person.objects.count(), 1) + + p, created = Person.objects.get_or_create( + first_name='George', last_name='Harrison', defaults={ + 'birthday': date(1943, 2, 25) + } + ) + self.assertTrue(created) + self.assertEqual(Person.objects.count(), 2) + + # If we execute the exact same statement, it won't create a Person. + p, created = Person.objects.get_or_create( + first_name='George', last_name='Harrison', defaults={ + 'birthday': date(1943, 2, 25) + } + ) + self.assertFalse(created) + self.assertEqual(Person.objects.count(), 2) + + # If you don't specify a value or default value for all required + # fields, you will get an error. + self.assertRaises(IntegrityError, + Person.objects.get_or_create, first_name="Tom", last_name="Smith" + ) + + # If you specify an existing primary key, but different other fields, + # then you will get an error and data will not be updated. + m = ManualPrimaryKeyTest.objects.create(id=1, data="Original") + self.assertRaises(IntegrityError, + ManualPrimaryKeyTest.objects.get_or_create, id=1, data="Different" + ) + self.assertEqual(ManualPrimaryKeyTest.objects.get(id=1).data, "Original") diff --git a/tests/modeltests/m2m_and_m2o/models.py b/tests/modeltests/m2m_and_m2o/models.py index 0ab7a72d57..0fea1a2e7b 100644 --- a/tests/modeltests/m2m_and_m2o/models.py +++ b/tests/modeltests/m2m_and_m2o/models.py @@ -19,47 +19,3 @@ class Issue(models.Model): class Meta: ordering = ('num',) - - -__test__ = {'API_TESTS':""" ->>> Issue.objects.all() -[] ->>> r = User(username='russell') ->>> r.save() ->>> g = User(username='gustav') ->>> g.save() - ->>> i = Issue(num=1) ->>> i.client = r ->>> i.save() - ->>> i2 = Issue(num=2) ->>> i2.client = r ->>> i2.save() ->>> i2.cc.add(r) - ->>> i3 = Issue(num=3) ->>> i3.client = g ->>> i3.save() ->>> i3.cc.add(r) - ->>> from django.db.models.query import Q - ->>> Issue.objects.filter(client=r.id) -[<Issue: 1>, <Issue: 2>] ->>> Issue.objects.filter(client=g.id) -[<Issue: 3>] ->>> Issue.objects.filter(cc__id__exact=g.id) -[] ->>> Issue.objects.filter(cc__id__exact=r.id) -[<Issue: 2>, <Issue: 3>] - -# These queries combine results from the m2m and the m2o relationships. -# They're three ways of saying the same thing. ->>> Issue.objects.filter(Q(cc__id__exact=r.id) | Q(client=r.id)) -[<Issue: 1>, <Issue: 2>, <Issue: 3>] ->>> Issue.objects.filter(cc__id__exact=r.id) | Issue.objects.filter(client=r.id) -[<Issue: 1>, <Issue: 2>, <Issue: 3>] ->>> Issue.objects.filter(Q(client=r.id) | Q(cc__id__exact=r.id)) -[<Issue: 1>, <Issue: 2>, <Issue: 3>] -"""} diff --git a/tests/modeltests/m2m_and_m2o/tests.py b/tests/modeltests/m2m_and_m2o/tests.py new file mode 100644 index 0000000000..dedf9cdf26 --- /dev/null +++ b/tests/modeltests/m2m_and_m2o/tests.py @@ -0,0 +1,75 @@ +from django.db.models import Q +from django.test import TestCase + +from models import Issue, User + + +class RelatedObjectTests(TestCase): + def test_m2m_and_m2o(self): + r = User.objects.create(username="russell") + g = User.objects.create(username="gustav") + + i1 = Issue(num=1) + i1.client = r + i1.save() + + i2 = Issue(num=2) + i2.client = r + i2.save() + i2.cc.add(r) + + i3 = Issue(num=3) + i3.client = g + i3.save() + i3.cc.add(r) + + self.assertQuerysetEqual( + Issue.objects.filter(client=r.id), [ + 1, + 2, + ], + lambda i: i.num + ) + self.assertQuerysetEqual( + Issue.objects.filter(client=g.id), [ + 3, + ], + lambda i: i.num + ) + self.assertQuerysetEqual( + Issue.objects.filter(cc__id__exact=g.id), [] + ) + self.assertQuerysetEqual( + Issue.objects.filter(cc__id__exact=r.id), [ + 2, + 3, + ], + lambda i: i.num + ) + + # These queries combine results from the m2m and the m2o relationships. + # They're three ways of saying the same thing. + self.assertQuerysetEqual( + Issue.objects.filter(Q(cc__id__exact = r.id) | Q(client=r.id)), [ + 1, + 2, + 3, + ], + lambda i: i.num + ) + self.assertQuerysetEqual( + Issue.objects.filter(cc__id__exact=r.id) | Issue.objects.filter(client=r.id), [ + 1, + 2, + 3, + ], + lambda i: i.num + ) + self.assertQuerysetEqual( + Issue.objects.filter(Q(client=r.id) | Q(cc__id__exact=r.id)), [ + 1, + 2, + 3, + ], + lambda i: i.num + ) diff --git a/tests/modeltests/m2m_intermediary/models.py b/tests/modeltests/m2m_intermediary/models.py index e9f964aa4e..8042a52b38 100644 --- a/tests/modeltests/m2m_intermediary/models.py +++ b/tests/modeltests/m2m_intermediary/models.py @@ -34,35 +34,3 @@ class Writer(models.Model): def __unicode__(self): return u'%s (%s)' % (self.reporter, self.position) -__test__ = {'API_TESTS':""" -# Create a few Reporters. ->>> r1 = Reporter(first_name='John', last_name='Smith') ->>> r1.save() ->>> r2 = Reporter(first_name='Jane', last_name='Doe') ->>> r2.save() - -# Create an Article. ->>> from datetime import datetime ->>> a = Article(headline='This is a test', pub_date=datetime(2005, 7, 27)) ->>> a.save() - -# Create a few Writers. ->>> w1 = Writer(reporter=r1, article=a, position='Main writer') ->>> w1.save() ->>> w2 = Writer(reporter=r2, article=a, position='Contributor') ->>> w2.save() - -# Play around with the API. ->>> a.writer_set.select_related().order_by('-position') -[<Writer: John Smith (Main writer)>, <Writer: Jane Doe (Contributor)>] ->>> w1.reporter -<Reporter: John Smith> ->>> w2.reporter -<Reporter: Jane Doe> ->>> w1.article -<Article: This is a test> ->>> w2.article -<Article: This is a test> ->>> r1.writer_set.all() -[<Writer: John Smith (Main writer)>] -"""} diff --git a/tests/modeltests/m2m_intermediary/tests.py b/tests/modeltests/m2m_intermediary/tests.py new file mode 100644 index 0000000000..5f357412a5 --- /dev/null +++ b/tests/modeltests/m2m_intermediary/tests.py @@ -0,0 +1,38 @@ +from datetime import datetime + +from django.test import TestCase + +from models import Reporter, Article, Writer + + +class M2MIntermediaryTests(TestCase): + def test_intermeiary(self): + r1 = Reporter.objects.create(first_name="John", last_name="Smith") + r2 = Reporter.objects.create(first_name="Jane", last_name="Doe") + + a = Article.objects.create( + headline="This is a test", pub_date=datetime(2005, 7, 27) + ) + + w1 = Writer.objects.create(reporter=r1, article=a, position="Main writer") + w2 = Writer.objects.create(reporter=r2, article=a, position="Contributor") + + self.assertQuerysetEqual( + a.writer_set.select_related().order_by("-position"), [ + ("John Smith", "Main writer"), + ("Jane Doe", "Contributor"), + ], + lambda w: (unicode(w.reporter), w.position) + ) + self.assertEqual(w1.reporter, r1) + self.assertEqual(w2.reporter, r2) + + self.assertEqual(w1.article, a) + self.assertEqual(w2.article, a) + + self.assertQuerysetEqual( + r1.writer_set.all(), [ + ("John Smith", "Main writer") + ], + lambda w: (unicode(w.reporter), w.position) + ) diff --git a/tests/modeltests/m2m_multiple/models.py b/tests/modeltests/m2m_multiple/models.py index 42e74553d9..e53f840653 100644 --- a/tests/modeltests/m2m_multiple/models.py +++ b/tests/modeltests/m2m_multiple/models.py @@ -28,52 +28,3 @@ class Article(models.Model): def __unicode__(self): return self.headline -__test__ = {'API_TESTS':""" ->>> from datetime import datetime - ->>> c1 = Category(name='Sports') ->>> c1.save() ->>> c2 = Category(name='News') ->>> c2.save() ->>> c3 = Category(name='Crime') ->>> c3.save() ->>> c4 = Category(name='Life') ->>> c4.save() - ->>> a1 = Article(headline='Area man steals', pub_date=datetime(2005, 11, 27)) ->>> a1.save() ->>> a1.primary_categories.add(c2, c3) ->>> a1.secondary_categories.add(c4) - ->>> a2 = Article(headline='Area man runs', pub_date=datetime(2005, 11, 28)) ->>> a2.save() ->>> a2.primary_categories.add(c1, c2) ->>> a2.secondary_categories.add(c4) - ->>> a1.primary_categories.all() -[<Category: Crime>, <Category: News>] - ->>> a2.primary_categories.all() -[<Category: News>, <Category: Sports>] - ->>> a1.secondary_categories.all() -[<Category: Life>] - - ->>> c1.primary_article_set.all() -[<Article: Area man runs>] ->>> c1.secondary_article_set.all() -[] ->>> c2.primary_article_set.all() -[<Article: Area man steals>, <Article: Area man runs>] ->>> c2.secondary_article_set.all() -[] ->>> c3.primary_article_set.all() -[<Article: Area man steals>] ->>> c3.secondary_article_set.all() -[] ->>> c4.primary_article_set.all() -[] ->>> c4.secondary_article_set.all() -[<Article: Area man steals>, <Article: Area man runs>] -"""} diff --git a/tests/modeltests/m2m_multiple/tests.py b/tests/modeltests/m2m_multiple/tests.py new file mode 100644 index 0000000000..1f4503a483 --- /dev/null +++ b/tests/modeltests/m2m_multiple/tests.py @@ -0,0 +1,84 @@ +from datetime import datetime + +from django.test import TestCase + +from models import Article, Category + + +class M2MMultipleTests(TestCase): + def test_multiple(self): + c1, c2, c3, c4 = [ + Category.objects.create(name=name) + for name in ["Sports", "News", "Crime", "Life"] + ] + + a1 = Article.objects.create( + headline="Area man steals", pub_date=datetime(2005, 11, 27) + ) + a1.primary_categories.add(c2, c3) + a1.secondary_categories.add(c4) + + a2 = Article.objects.create( + headline="Area man runs", pub_date=datetime(2005, 11, 28) + ) + a2.primary_categories.add(c1, c2) + a2.secondary_categories.add(c4) + + self.assertQuerysetEqual( + a1.primary_categories.all(), [ + "Crime", + "News", + ], + lambda c: c.name + ) + self.assertQuerysetEqual( + a2.primary_categories.all(), [ + "News", + "Sports", + ], + lambda c: c.name + ) + self.assertQuerysetEqual( + a1.secondary_categories.all(), [ + "Life", + ], + lambda c: c.name + ) + self.assertQuerysetEqual( + c1.primary_article_set.all(), [ + "Area man runs", + ], + lambda a: a.headline + ) + self.assertQuerysetEqual( + c1.secondary_article_set.all(), [] + ) + self.assertQuerysetEqual( + c2.primary_article_set.all(), [ + "Area man steals", + "Area man runs", + ], + lambda a: a.headline + ) + self.assertQuerysetEqual( + c2.secondary_article_set.all(), [] + ) + self.assertQuerysetEqual( + c3.primary_article_set.all(), [ + "Area man steals", + ], + lambda a: a.headline + ) + self.assertQuerysetEqual( + c3.secondary_article_set.all(), [] + ) + self.assertQuerysetEqual( + c4.primary_article_set.all(), [] + ) + self.assertQuerysetEqual( + c4.secondary_article_set.all(), [ + "Area man steals", + "Area man runs", + ], + lambda a: a.headline + ) diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py index 1087cf8795..7ded82bb7c 100644 --- a/tests/modeltests/model_forms/models.py +++ b/tests/modeltests/model_forms/models.py @@ -554,7 +554,7 @@ fields with the 'choices' attribute are represented by a ChoiceField. <option value="1">Entertainment</option> <option value="2">It's a test</option> <option value="3">Third test</option> -</select><br /> Hold down "Control", or "Command" on a Mac, to select more than one.</td></tr> +</select><br /><span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></td></tr> You can restrict a form to a subset of the complete list of fields by providing a 'fields' argument. If you try to save a @@ -579,7 +579,7 @@ inserted as 'initial' data in each Field. ... model = Writer >>> f = RoykoForm(auto_id=False, instance=w) >>> print f -<tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="50" /><br />Use both first and last names.</td></tr> +<tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="50" /><br /><span class="helptext">Use both first and last names.</span></td></tr> >>> art = Article(headline='Test article', slug='test-article', pub_date=datetime.date(1988, 1, 4), writer=w, article='Hello.') >>> art.save() @@ -609,7 +609,7 @@ inserted as 'initial' data in each Field. <option value="1">Entertainment</option> <option value="2">It's a test</option> <option value="3">Third test</option> -</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li> +</select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></li> >>> f = TestArticleForm({'headline': u'Test headline', 'slug': 'test-headline', 'pub_date': u'1984-02-06', 'writer': unicode(w_royko.pk), 'article': 'Hello.'}, instance=art) >>> f.errors {} @@ -672,7 +672,7 @@ Add some categories and test the many-to-many form output. <option value="1" selected="selected">Entertainment</option> <option value="2">It's a test</option> <option value="3">Third test</option> -</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li> +</select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></li> Initial values can be provided for model forms >>> f = TestArticleForm(auto_id=False, initial={'headline': 'Your headline here', 'categories': ['1','2']}) @@ -696,7 +696,7 @@ Initial values can be provided for model forms <option value="1" selected="selected">Entertainment</option> <option value="2" selected="selected">It's a test</option> <option value="3">Third test</option> -</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li> +</select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></li> >>> f = TestArticleForm({'headline': u'New headline', 'slug': u'new-headline', 'pub_date': u'1988-01-04', ... 'writer': unicode(w_royko.pk), 'article': u'Hello.', 'categories': [u'1', u'2']}, instance=new_art) @@ -812,7 +812,7 @@ the data in the database when the form is instantiated. <option value="1">Entertainment</option> <option value="2">It's a test</option> <option value="3">Third</option> -</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li> +</select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></li> >>> Category.objects.create(name='Fourth', url='4th') <Category: Fourth> >>> Writer.objects.create(name='Carl Bernstein') @@ -839,7 +839,7 @@ the data in the database when the form is instantiated. <option value="2">It's a test</option> <option value="3">Third</option> <option value="4">Fourth</option> -</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li> +</select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></li> # ModelChoiceField ############################################################ diff --git a/tests/modeltests/model_forms/tests.py b/tests/modeltests/model_forms/tests.py index 6a5f9395cc..c5647c714f 100644 --- a/tests/modeltests/model_forms/tests.py +++ b/tests/modeltests/model_forms/tests.py @@ -156,6 +156,10 @@ class UniqueTest(TestCase): form = PostForm({'subtitle': "Finally", "title": "Django 1.0 is released", "slug": "Django 1.0", 'posted': '2008-09-03'}, instance=p) self.assertTrue(form.is_valid()) + form = PostForm({'title': "Django 1.0 is released"}) + self.assertFalse(form.is_valid()) + self.assertEqual(len(form.errors), 1) + self.assertEqual(form.errors['posted'], [u'This field is required.']) def test_inherited_unique_for_date(self): p = Post.objects.create(title="Django 1.0 is released", diff --git a/tests/modeltests/proxy_model_inheritance/tests.py b/tests/modeltests/proxy_model_inheritance/tests.py index a07958a13b..d10d6a4ac1 100644 --- a/tests/modeltests/proxy_model_inheritance/tests.py +++ b/tests/modeltests/proxy_model_inheritance/tests.py @@ -23,9 +23,9 @@ class ProxyModelInheritanceTests(TransactionTestCase): settings.INSTALLED_APPS = ('app1', 'app2') map(load_app, settings.INSTALLED_APPS) call_command('syncdb', verbosity=0) + global ProxyModel, NiceModel from app1.models import ProxyModel from app2.models import NiceModel - global ProxyModel, NiceModel def tearDown(self): settings.INSTALLED_APPS = self.old_installed_apps diff --git a/tests/modeltests/signals/models.py b/tests/modeltests/signals/models.py index ea8137f657..8a500752be 100644 --- a/tests/modeltests/signals/models.py +++ b/tests/modeltests/signals/models.py @@ -3,6 +3,7 @@ Testing signals before/after saving and deleting. """ from django.db import models +from django.dispatch import receiver class Person(models.Model): first_name = models.CharField(max_length=20) @@ -11,6 +12,13 @@ class Person(models.Model): def __unicode__(self): return u"%s %s" % (self.first_name, self.last_name) +class Car(models.Model): + make = models.CharField(max_length=20) + model = models.CharField(max_length=20) + + def __unicode__(self): + return u"%s %s" % (self.make, self.model) + def pre_save_test(signal, sender, instance, **kwargs): print 'pre_save signal,', instance if kwargs.get('raw'): @@ -52,22 +60,44 @@ __test__ = {'API_TESTS':""" >>> models.signals.pre_delete.connect(pre_delete_test) >>> models.signals.post_delete.connect(post_delete_test) +# throw a decorator syntax receiver into the mix +>>> @receiver(models.signals.pre_save) +... def pre_save_decorator_test(signal, sender, instance, **kwargs): +... print "pre_save signal decorator,", instance + +# throw a decorator syntax receiver into the mix +>>> @receiver(models.signals.pre_save, sender=Car) +... def pre_save_decorator_sender_test(signal, sender, instance, **kwargs): +... print "pre_save signal decorator sender,", instance + >>> p1 = Person(first_name='John', last_name='Smith') >>> p1.save() pre_save signal, John Smith +pre_save signal decorator, John Smith post_save signal, John Smith Is created >>> p1.first_name = 'Tom' >>> p1.save() pre_save signal, Tom Smith +pre_save signal decorator, Tom Smith post_save signal, Tom Smith Is updated +# Car signal (sender defined) +>>> c1 = Car(make="Volkswagon", model="Passat") +>>> c1.save() +pre_save signal, Volkswagon Passat +pre_save signal decorator, Volkswagon Passat +pre_save signal decorator sender, Volkswagon Passat +post_save signal, Volkswagon Passat +Is created + # Calling an internal method purely so that we can trigger a "raw" save. >>> p1.save_base(raw=True) pre_save signal, Tom Smith Is raw +pre_save signal decorator, Tom Smith post_save signal, Tom Smith Is updated Is raw @@ -82,12 +112,14 @@ instance.id is None: False >>> p2.id = 99999 >>> p2.save() pre_save signal, James Jones +pre_save signal decorator, James Jones post_save signal, James Jones Is created >>> p2.id = 99998 >>> p2.save() pre_save signal, James Jones +pre_save signal decorator, James Jones post_save signal, James Jones Is created @@ -104,6 +136,8 @@ instance.id is None: False >>> models.signals.pre_delete.disconnect(pre_delete_test) >>> models.signals.post_save.disconnect(post_save_test) >>> models.signals.pre_save.disconnect(pre_save_test) +>>> models.signals.pre_save.disconnect(pre_save_decorator_test) +>>> models.signals.pre_save.disconnect(pre_save_decorator_sender_test, sender=Car) # Check that all our signals got disconnected properly. >>> post_signals = (len(models.signals.pre_save.receivers), diff --git a/tests/modeltests/test_client/models.py b/tests/modeltests/test_client/models.py index c51323d843..30520082da 100644 --- a/tests/modeltests/test_client/models.py +++ b/tests/modeltests/test_client/models.py @@ -21,6 +21,7 @@ rather than the HTML rendered to the end-user. """ from django.test import Client, TestCase +from django.conf import settings from django.core import mail class ClientTest(TestCase): @@ -433,3 +434,26 @@ class ClientTest(TestCase): self.assertEqual(mail.outbox[1].from_email, 'from@example.com') self.assertEqual(mail.outbox[1].to[0], 'second@example.com') self.assertEqual(mail.outbox[1].to[1], 'third@example.com') + +class CSRFEnabledClientTests(TestCase): + def setUp(self): + # Enable the CSRF middleware for this test + self.old_MIDDLEWARE_CLASSES = settings.MIDDLEWARE_CLASSES + csrf_middleware_class = 'django.middleware.csrf.CsrfViewMiddleware' + if csrf_middleware_class not in settings.MIDDLEWARE_CLASSES: + settings.MIDDLEWARE_CLASSES += (csrf_middleware_class,) + + def tearDown(self): + settings.MIDDLEWARE_CLASSES = self.old_MIDDLEWARE_CLASSES + + def test_csrf_enabled_client(self): + "A client can be instantiated with CSRF checks enabled" + csrf_client = Client(enforce_csrf_checks=True) + + # The normal client allows the post + response = self.client.post('/test_client/post_view/', {}) + self.assertEqual(response.status_code, 200) + + # The CSRF-enabled client rejects it + response = csrf_client.post('/test_client/post_view/', {}) + self.assertEqual(response.status_code, 403) diff --git a/tests/modeltests/validation/test_unique.py b/tests/modeltests/validation/test_unique.py index 1b966390c4..fb77c4d28c 100644 --- a/tests/modeltests/validation/test_unique.py +++ b/tests/modeltests/validation/test_unique.py @@ -40,6 +40,15 @@ class GetUniqueCheckTests(unittest.TestCase): ), m._get_unique_checks() ) + def test_unique_for_date_exclusion(self): + m = UniqueForDateModel() + self.assertEqual(( + [(UniqueForDateModel, ('id',))], + [(UniqueForDateModel, 'year', 'count', 'end_date'), + (UniqueForDateModel, 'month', 'order', 'end_date')] + ), m._get_unique_checks(exclude='start_date') + ) + class PerformUniqueChecksTest(unittest.TestCase): def setUp(self): # Set debug to True to gain access to connection.queries. diff --git a/tests/regressiontests/admin_changelist/tests.py b/tests/regressiontests/admin_changelist/tests.py index b70d7c51f4..c8ad1ce8f6 100644 --- a/tests/regressiontests/admin_changelist/tests.py +++ b/tests/regressiontests/admin_changelist/tests.py @@ -1,10 +1,10 @@ -import unittest from django.contrib import admin from django.contrib.admin.views.main import ChangeList from django.template import Context, Template +from django.test import TransactionTestCase from regressiontests.admin_changelist.models import Child, Parent -class ChangeListTests(unittest.TestCase): +class ChangeListTests(TransactionTestCase): def test_select_related_preserved(self): """ Regression test for #10348: ChangeList.get_query_set() shouldn't @@ -18,9 +18,8 @@ class ChangeListTests(unittest.TestCase): def test_result_list_html(self): """ - Regression test for #11791: Inclusion tag result_list generates a - table and this checks that the items are nested within the table - element tags. + Verifies that inclusion tag result_list generates a table when with + default ModelAdmin settings. """ new_parent = Parent.objects.create(name='parent') new_child = Child.objects.create(name='name', parent=new_parent) @@ -29,16 +28,27 @@ class ChangeListTests(unittest.TestCase): cl = ChangeList(request, Child, m.list_display, m.list_display_links, m.list_filter, m.date_hierarchy, m.search_fields, m.list_select_related, m.list_per_page, m.list_editable, m) - FormSet = m.get_changelist_formset(request) - cl.formset = FormSet(queryset=cl.result_list) + cl.formset = None template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}') context = Context({'cl': cl}) table_output = template.render(context) - hidden_input_elem = '<input type="hidden" name="form-0-id" value="1" id="id_form-0-id" />' - self.failIf(table_output.find(hidden_input_elem) == -1, - 'Failed to find expected hidden input element in: %s' % table_output) - self.failIf(table_output.find('<td>%s</td>' % hidden_input_elem) == -1, - 'Hidden input element is not enclosed in <td> element.') + row_html = '<tbody><tr class="row1"><td><input type="checkbox" class="action-select" value="1" name="_selected_action" /></td><th><a href="1/">name</a></th><td>Parent object</td></tr></tbody>' + self.failIf(table_output.find(row_html) == -1, + 'Failed to find expected row element: %s' % table_output) + + def test_result_list_editable_html(self): + """ + Regression tests for #11791: Inclusion tag result_list generates a + table and this checks that the items are nested within the table + element tags. + Also a regression test for #13599, verifies that hidden fields + when list_editable is enabled are rendered in a div outside the + table. + """ + new_parent = Parent.objects.create(name='parent') + new_child = Child.objects.create(name='name', parent=new_parent) + request = MockRequest() + m = ChildAdmin(Child, admin.site) # Test with list_editable fields m.list_display = ['id', 'name', 'parent'] @@ -52,10 +62,14 @@ class ChangeListTests(unittest.TestCase): template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}') context = Context({'cl': cl}) table_output = template.render(context) - self.failIf(table_output.find(hidden_input_elem) == -1, - 'Failed to find expected hidden input element in: %s' % table_output) - self.failIf(table_output.find('<td>%s</td>' % hidden_input_elem) == -1, - 'Hidden input element is not enclosed in <td> element.') + # make sure that hidden fields are in the correct place + hiddenfields_div = '<div class="hiddenfields"><input type="hidden" name="form-0-id" value="1" id="id_form-0-id" /></div>' + self.failIf(table_output.find(hiddenfields_div) == -1, + 'Failed to find hidden fields in: %s' % table_output) + # make sure that list editable fields are rendered in divs correctly + editable_name_field = '<input name="form-0-name" value="name" class="vTextField" maxlength="30" type="text" id="id_form-0-name" />' + self.failIf('<td>%s</td>' % editable_name_field == -1, + 'Failed to find "name" list_editable field in: %s' % table_output) class ChildAdmin(admin.ModelAdmin): list_display = ['name', 'parent'] diff --git a/tests/regressiontests/admin_scripts/tests.py b/tests/regressiontests/admin_scripts/tests.py index 7ec2454561..3dd8ad5d13 100644 --- a/tests/regressiontests/admin_scripts/tests.py +++ b/tests/regressiontests/admin_scripts/tests.py @@ -133,7 +133,7 @@ class AdminScriptTestCase(unittest.TestCase): return out, err def run_django_admin(self, args, settings_file=None): - bin_dir = os.path.dirname(bin.__file__) + bin_dir = os.path.abspath(os.path.dirname(bin.__file__)) return self.run_test(os.path.join(bin_dir,'django-admin.py'), args, settings_file) def run_manage(self, args, settings_file=None): diff --git a/tests/regressiontests/admin_views/models.py b/tests/regressiontests/admin_views/models.py index a2700ba747..b25a9b9a96 100644 --- a/tests/regressiontests/admin_views/models.py +++ b/tests/regressiontests/admin_views/models.py @@ -10,6 +10,7 @@ from django.core.mail import EmailMessage from django.db import models from django import forms from django.forms.models import BaseModelFormSet +from django.contrib.auth.models import User from django.contrib.contenttypes import generic from django.contrib.contenttypes.models import ContentType @@ -579,6 +580,10 @@ class Pizza(models.Model): class PizzaAdmin(admin.ModelAdmin): readonly_fields = ('toppings',) +class Album(models.Model): + owner = models.ForeignKey(User) + title = models.CharField(max_length=30) + admin.site.register(Article, ArticleAdmin) admin.site.register(CustomArticle, CustomArticleAdmin) admin.site.register(Section, save_as=True, inlines=[ArticleInline]) @@ -625,3 +630,4 @@ admin.site.register(Promo) admin.site.register(ChapterXtra1) admin.site.register(Pizza, PizzaAdmin) admin.site.register(Topping) +admin.site.register(Album) diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py index 1385e5e0aa..725369a5b1 100644 --- a/tests/regressiontests/admin_views/tests.py +++ b/tests/regressiontests/admin_views/tests.py @@ -604,6 +604,28 @@ class AdminViewPermissionsTest(TestCase): 'Plural error message not found in response to post with multiple errors.') self.client.get('/test_admin/admin/logout/') + def testConditionallyShowAddSectionLink(self): + """ + The foreign key widget should only show the "add related" button if the + user has permission to add that related item. + """ + # Set up and log in user. + url = '/test_admin/admin/admin_views/article/add/' + add_link_text = ' class="add-another"' + self.client.get('/test_admin/admin/') + self.client.post('/test_admin/admin/', self.adduser_login) + # The add user can't add sections yet, so they shouldn't see the "add + # section" link. + response = self.client.get(url) + self.assertNotContains(response, add_link_text) + # Allow the add user to add sections too. Now they can see the "add + # section" link. + add_user = User.objects.get(username='adduser') + perm = get_perm(Section, Section._meta.get_add_permission()) + add_user.user_permissions.add(perm) + response = self.client.get(url) + self.assertContains(response, add_link_text) + def testCustomModelAdminTemplates(self): self.client.get('/test_admin/admin/') self.client.post('/test_admin/admin/', self.super_login) @@ -1477,6 +1499,21 @@ class AdminActionsTest(TestCase): response = self.client.post('/test_admin/admin/admin_views/externalsubscriber/', action_data) self.failUnlessEqual(response.status_code, 302) + def test_default_redirect(self): + """ + Test that actions which don't return an HttpResponse are redirected to + the same page, retaining the querystring (which may contain changelist + information). + """ + action_data = { + ACTION_CHECKBOX_NAME: [1], + 'action' : 'external_mail', + 'index': 0, + } + url = '/test_admin/admin/admin_views/externalsubscriber/?ot=asc&o=1' + response = self.client.post(url, action_data) + self.assertRedirects(response, url) + def test_model_without_action(self): "Tests a ModelAdmin without any action" response = self.client.get('/test_admin/admin/admin_views/oldsubscriber/') @@ -2113,11 +2150,9 @@ class ReadonlyTest(TestCase): response = self.client.get('/test_admin/admin/admin_views/pizza/add/') self.assertEqual(response.status_code, 200) -class IncompleteFormTest(TestCase): +class UserAdminTest(TestCase): """ - Tests validation of a ModelForm that doesn't explicitly have all data - corresponding to model fields. Model validation shouldn't fail - such a forms. + Tests user CRUD functionality. """ fixtures = ['admin-views-users.xml'] @@ -2128,6 +2163,7 @@ class IncompleteFormTest(TestCase): self.client.logout() def test_user_creation(self): + user_count = User.objects.count() response = self.client.post('/test_admin/admin/auth/user/add/', { 'username': 'newuser', 'password1': 'newpassword', @@ -2136,6 +2172,7 @@ class IncompleteFormTest(TestCase): }) new_user = User.objects.order_by('-id')[0] self.assertRedirects(response, '/test_admin/admin/auth/user/%s/' % new_user.pk) + self.assertEquals(User.objects.count(), user_count + 1) self.assertNotEquals(new_user.password, UNUSABLE_PASSWORD) def test_password_mismatch(self): @@ -2149,3 +2186,71 @@ class IncompleteFormTest(TestCase): self.assert_('password' not in adminform.form.errors) self.assertEquals(adminform.form.errors['password2'], [u"The two password fields didn't match."]) + + def test_user_fk_popup(self): + response = self.client.get('/test_admin/admin/admin_views/album/add/') + self.failUnlessEqual(response.status_code, 200) + self.assertContains(response, '/test_admin/admin/auth/user/add') + self.assertContains(response, 'class="add-another" id="add_id_owner" onclick="return showAddAnotherPopup(this);"') + response = self.client.get('/test_admin/admin/auth/user/add/?_popup=1') + self.assertNotContains(response, 'name="_continue"') + + def test_user_add_another(self): + user_count = User.objects.count() + response = self.client.post('/test_admin/admin/auth/user/add/', { + 'username': 'newuser', + 'password1': 'newpassword', + 'password2': 'newpassword', + '_addanother': '1', + }) + new_user = User.objects.order_by('-id')[0] + self.assertRedirects(response, '/test_admin/admin/auth/user/add/') + self.assertEquals(User.objects.count(), user_count + 1) + self.assertNotEquals(new_user.password, UNUSABLE_PASSWORD) + +try: + # If docutils isn't installed, skip the AdminDocs tests. + import docutils + + class AdminDocsTest(TestCase): + fixtures = ['admin-views-users.xml'] + + def setUp(self): + self.client.login(username='super', password='secret') + + def tearDown(self): + self.client.logout() + + def test_tags(self): + response = self.client.get('/test_admin/admin/doc/tags/') + + # The builtin tag group exists + self.assertContains(response, "<h2>Built-in tags</h2>", count=2) + + # A builtin tag exists in both the index and detail + self.assertContains(response, '<h3 id="built_in-autoescape">autoescape</h3>') + self.assertContains(response, '<li><a href="#built_in-autoescape">autoescape</a></li>') + + # An app tag exists in both the index and detail + self.assertContains(response, '<h3 id="flatpages-get_flatpages">get_flatpages</h3>') + self.assertContains(response, '<li><a href="#flatpages-get_flatpages">get_flatpages</a></li>') + + # The admin list tag group exists + self.assertContains(response, "<h2>admin_list</h2>", count=2) + + # An admin list tag exists in both the index and detail + self.assertContains(response, '<h3 id="admin_list-admin_actions">admin_actions</h3>') + self.assertContains(response, '<li><a href="#admin_list-admin_actions">admin_actions</a></li>') + + def test_filters(self): + response = self.client.get('/test_admin/admin/doc/filters/') + + # The builtin filter group exists + self.assertContains(response, "<h2>Built-in filters</h2>", count=2) + + # A builtin filter exists in both the index and detail + self.assertContains(response, '<h3 id="built_in-add">add</h3>') + self.assertContains(response, '<li><a href="#built_in-add">add</a></li>') + +except ImportError: + pass diff --git a/tests/regressiontests/admin_widgets/tests.py b/tests/regressiontests/admin_widgets/tests.py index fd0c25ca1a..c445644335 100644 --- a/tests/regressiontests/admin_widgets/tests.py +++ b/tests/regressiontests/admin_widgets/tests.py @@ -1,3 +1,5 @@ +# encoding: utf-8 + from django import forms from django.contrib import admin from django.contrib.admin import widgets @@ -151,3 +153,13 @@ class AdminForeignKeyRawIdWidget(DjangoTestCase): post_data) self.assertContains(response, 'Select a valid choice. That choice is not one of the available choices.') + + def test_invalid_target_id(self): + + for test_str in ('Iñtërnâtiônà lizætiøn', "1234'", -1234): + # This should result in an error message, not a server exception. + response = self.client.post('%s/admin_widgets/event/add/' % self.admin_root, + {"band": test_str}) + + self.assertContains(response, + 'Select a valid choice. That choice is not one of the available choices.') diff --git a/tests/regressiontests/aggregation_regress/models.py b/tests/regressiontests/aggregation_regress/models.py index ba74357534..783c21956a 100644 --- a/tests/regressiontests/aggregation_regress/models.py +++ b/tests/regressiontests/aggregation_regress/models.py @@ -4,6 +4,7 @@ import pickle from django.db import connection, models, DEFAULT_DB_ALIAS from django.conf import settings + class Author(models.Model): name = models.CharField(max_length=100) age = models.IntegerField() @@ -12,6 +13,7 @@ class Author(models.Model): def __unicode__(self): return self.name + class Publisher(models.Model): name = models.CharField(max_length=255) num_awards = models.IntegerField() @@ -19,6 +21,7 @@ class Publisher(models.Model): def __unicode__(self): return self.name + class Book(models.Model): isbn = models.CharField(max_length=9) name = models.CharField(max_length=255) @@ -36,6 +39,7 @@ class Book(models.Model): def __unicode__(self): return self.name + class Store(models.Model): name = models.CharField(max_length=255) books = models.ManyToManyField(Book) @@ -45,334 +49,21 @@ class Store(models.Model): def __unicode__(self): return self.name + class Entries(models.Model): EntryID = models.AutoField(primary_key=True, db_column='Entry ID') Entry = models.CharField(unique=True, max_length=50) Exclude = models.BooleanField() + class Clues(models.Model): ID = models.AutoField(primary_key=True) EntryID = models.ForeignKey(Entries, verbose_name='Entry', db_column = 'Entry ID') Clue = models.CharField(max_length=150) + class HardbackBook(Book): weight = models.FloatField() def __unicode__(self): return "%s (hardback): %s" % (self.name, self.weight) - -__test__ = {'API_TESTS': """ ->>> from django.core import management ->>> from django.db.models import get_app, F - -# Reset the database representation of this app. -# This will return the database to a clean initial state. ->>> management.call_command('flush', verbosity=0, interactive=False) - ->>> from django.db.models import Avg, Sum, Count, Max, Min, StdDev, Variance - -# Ordering requests are ignored ->>> Author.objects.all().order_by('name').aggregate(Avg('age')) -{'age__avg': 37.4...} - -# Implicit ordering is also ignored ->>> Book.objects.all().aggregate(Sum('pages')) -{'pages__sum': 3703} - -# Baseline results ->>> Book.objects.all().aggregate(Sum('pages'), Avg('pages')) -{'pages__sum': 3703, 'pages__avg': 617.1...} - -# Empty values query doesn't affect grouping or results ->>> Book.objects.all().values().aggregate(Sum('pages'), Avg('pages')) -{'pages__sum': 3703, 'pages__avg': 617.1...} - -# Aggregate overrides extra selected column ->>> Book.objects.all().extra(select={'price_per_page' : 'price / pages'}).aggregate(Sum('pages')) -{'pages__sum': 3703} - -# Annotations get combined with extra select clauses ->>> sorted((k,v) for k,v in Book.objects.all().annotate(mean_auth_age=Avg('authors__age')).extra(select={'manufacture_cost' : 'price * .5'}).get(pk=2).__dict__.items() if k != '_state') -[('contact_id', 3), ('id', 2), ('isbn', u'067232959'), ('manufacture_cost', ...11.545...), ('mean_auth_age', 45.0), ('name', u'Sams Teach Yourself Django in 24 Hours'), ('pages', 528), ('price', Decimal("23.09")), ('pubdate', datetime.date(2008, 3, 3)), ('publisher_id', 2), ('rating', 3.0)] - -# Order of the annotate/extra in the query doesn't matter ->>> sorted((k,v) for k,v in Book.objects.all().extra(select={'manufacture_cost' : 'price * .5'}).annotate(mean_auth_age=Avg('authors__age')).get(pk=2).__dict__.items()if k != '_state') -[('contact_id', 3), ('id', 2), ('isbn', u'067232959'), ('manufacture_cost', ...11.545...), ('mean_auth_age', 45.0), ('name', u'Sams Teach Yourself Django in 24 Hours'), ('pages', 528), ('price', Decimal("23.09")), ('pubdate', datetime.date(2008, 3, 3)), ('publisher_id', 2), ('rating', 3.0)] - -# Values queries can be combined with annotate and extra ->>> sorted((k,v) for k,v in Book.objects.all().annotate(mean_auth_age=Avg('authors__age')).extra(select={'manufacture_cost' : 'price * .5'}).values().get(pk=2).items()if k != '_state') -[('contact_id', 3), ('id', 2), ('isbn', u'067232959'), ('manufacture_cost', ...11.545...), ('mean_auth_age', 45.0), ('name', u'Sams Teach Yourself Django in 24 Hours'), ('pages', 528), ('price', Decimal("23.09")), ('pubdate', datetime.date(2008, 3, 3)), ('publisher_id', 2), ('rating', 3.0)] - -# The order of the (empty) values, annotate and extra clauses doesn't matter ->>> sorted((k,v) for k,v in Book.objects.all().values().annotate(mean_auth_age=Avg('authors__age')).extra(select={'manufacture_cost' : 'price * .5'}).get(pk=2).items()if k != '_state') -[('contact_id', 3), ('id', 2), ('isbn', u'067232959'), ('manufacture_cost', ...11.545...), ('mean_auth_age', 45.0), ('name', u'Sams Teach Yourself Django in 24 Hours'), ('pages', 528), ('price', Decimal("23.09")), ('pubdate', datetime.date(2008, 3, 3)), ('publisher_id', 2), ('rating', 3.0)] - -# If the annotation precedes the values clause, it won't be included -# unless it is explicitly named ->>> sorted(Book.objects.all().annotate(mean_auth_age=Avg('authors__age')).extra(select={'price_per_page' : 'price / pages'}).values('name').get(pk=1).items()) -[('name', u'The Definitive Guide to Django: Web Development Done Right')] - ->>> sorted(Book.objects.all().annotate(mean_auth_age=Avg('authors__age')).extra(select={'price_per_page' : 'price / pages'}).values('name','mean_auth_age').get(pk=1).items()) -[('mean_auth_age', 34.5), ('name', u'The Definitive Guide to Django: Web Development Done Right')] - -# If an annotation isn't included in the values, it can still be used in a filter ->>> Book.objects.annotate(n_authors=Count('authors')).values('name').filter(n_authors__gt=2) -[{'name': u'Python Web Development with Django'}] - -# The annotations are added to values output if values() precedes annotate() ->>> sorted(Book.objects.all().values('name').annotate(mean_auth_age=Avg('authors__age')).extra(select={'price_per_page' : 'price / pages'}).get(pk=1).items()) -[('mean_auth_age', 34.5), ('name', u'The Definitive Guide to Django: Web Development Done Right')] - -# Check that all of the objects are getting counted (allow_nulls) and that values respects the amount of objects ->>> len(Author.objects.all().annotate(Avg('friends__age')).values()) -9 - -# Check that consecutive calls to annotate accumulate in the query ->>> Book.objects.values('price').annotate(oldest=Max('authors__age')).order_by('oldest', 'price').annotate(Max('publisher__num_awards')) -[{'price': Decimal("30..."), 'oldest': 35, 'publisher__num_awards__max': 3}, {'price': Decimal("29.69"), 'oldest': 37, 'publisher__num_awards__max': 7}, {'price': Decimal("23.09"), 'oldest': 45, 'publisher__num_awards__max': 1}, {'price': Decimal("75..."), 'oldest': 57, 'publisher__num_awards__max': 9}, {'price': Decimal("82.8..."), 'oldest': 57, 'publisher__num_awards__max': 7}] - -# Aggregates can be composed over annotations. -# The return type is derived from the composed aggregate ->>> Book.objects.all().annotate(num_authors=Count('authors__id')).aggregate(Max('pages'), Max('price'), Sum('num_authors'), Avg('num_authors')) -{'num_authors__sum': 10, 'num_authors__avg': 1.66..., 'pages__max': 1132, 'price__max': Decimal("82.80")} - -# Bad field requests in aggregates are caught and reported ->>> Book.objects.all().aggregate(num_authors=Count('foo')) -Traceback (most recent call last): -... -FieldError: Cannot resolve keyword 'foo' into field. Choices are: authors, contact, hardbackbook, id, isbn, name, pages, price, pubdate, publisher, rating, store - ->>> Book.objects.all().annotate(num_authors=Count('foo')) -Traceback (most recent call last): -... -FieldError: Cannot resolve keyword 'foo' into field. Choices are: authors, contact, hardbackbook, id, isbn, name, pages, price, pubdate, publisher, rating, store - ->>> Book.objects.all().annotate(num_authors=Count('authors__id')).aggregate(Max('foo')) -Traceback (most recent call last): -... -FieldError: Cannot resolve keyword 'foo' into field. Choices are: authors, contact, hardbackbook, id, isbn, name, pages, price, pubdate, publisher, rating, store, num_authors - -# Old-style count aggregations can be mixed with new-style ->>> Book.objects.annotate(num_authors=Count('authors')).count() -6 - -# Non-ordinal, non-computed Aggregates over annotations correctly inherit -# the annotation's internal type if the annotation is ordinal or computed ->>> Book.objects.annotate(num_authors=Count('authors')).aggregate(Max('num_authors')) -{'num_authors__max': 3} - ->>> Publisher.objects.annotate(avg_price=Avg('book__price')).aggregate(Max('avg_price')) -{'avg_price__max': 75.0...} - -# Aliases are quoted to protected aliases that might be reserved names ->>> Book.objects.aggregate(number=Max('pages'), select=Max('pages')) -{'number': 1132, 'select': 1132} - -# Regression for #10064: select_related() plays nice with aggregates ->>> sorted(Book.objects.select_related('publisher').annotate(num_authors=Count('authors')).values()[0].iteritems()) -[('contact_id', 8), ('id', 5), ('isbn', u'013790395'), ('name', u'Artificial Intelligence: A Modern Approach'), ('num_authors', 2), ('pages', 1132), ('price', Decimal("82.8...")), ('pubdate', datetime.date(1995, 1, 15)), ('publisher_id', 3), ('rating', 4.0)] - -# Regression for #10010: exclude on an aggregate field is correctly negated ->>> len(Book.objects.annotate(num_authors=Count('authors'))) -6 ->>> len(Book.objects.annotate(num_authors=Count('authors')).filter(num_authors__gt=2)) -1 ->>> len(Book.objects.annotate(num_authors=Count('authors')).exclude(num_authors__gt=2)) -5 - ->>> len(Book.objects.annotate(num_authors=Count('authors')).filter(num_authors__lt=3).exclude(num_authors__lt=2)) -2 ->>> len(Book.objects.annotate(num_authors=Count('authors')).exclude(num_authors__lt=2).filter(num_authors__lt=3)) -2 - -# Aggregates can be used with F() expressions -# ... where the F() is pushed into the HAVING clause ->>> Publisher.objects.annotate(num_books=Count('book')).filter(num_books__lt=F('num_awards')/2).order_by('name').values('name','num_books','num_awards') -[{'num_books': 1, 'name': u'Morgan Kaufmann', 'num_awards': 9}, {'num_books': 2, 'name': u'Prentice Hall', 'num_awards': 7}] - ->>> Publisher.objects.annotate(num_books=Count('book')).exclude(num_books__lt=F('num_awards')/2).order_by('name').values('name','num_books','num_awards') -[{'num_books': 2, 'name': u'Apress', 'num_awards': 3}, {'num_books': 0, 'name': u"Jonno's House of Books", 'num_awards': 0}, {'num_books': 1, 'name': u'Sams', 'num_awards': 1}] - -# ... and where the F() references an aggregate ->>> Publisher.objects.annotate(num_books=Count('book')).filter(num_awards__gt=2*F('num_books')).order_by('name').values('name','num_books','num_awards') -[{'num_books': 1, 'name': u'Morgan Kaufmann', 'num_awards': 9}, {'num_books': 2, 'name': u'Prentice Hall', 'num_awards': 7}] - ->>> Publisher.objects.annotate(num_books=Count('book')).exclude(num_books__lt=F('num_awards')/2).order_by('name').values('name','num_books','num_awards') -[{'num_books': 2, 'name': u'Apress', 'num_awards': 3}, {'num_books': 0, 'name': u"Jonno's House of Books", 'num_awards': 0}, {'num_books': 1, 'name': u'Sams', 'num_awards': 1}] - -# Tests on fields with non-default table and column names. ->>> Clues.objects.values('EntryID__Entry').annotate(Appearances=Count('EntryID'), Distinct_Clues=Count('Clue', distinct=True)) -[] - ->>> Entries.objects.annotate(clue_count=Count('clues__ID')) -[] - -# Regression for #10089: Check handling of empty result sets with aggregates ->>> Book.objects.filter(id__in=[]).count() -0 - ->>> Book.objects.filter(id__in=[]).aggregate(num_authors=Count('authors'), avg_authors=Avg('authors'), max_authors=Max('authors'), max_price=Max('price'), max_rating=Max('rating')) -{'max_authors': None, 'max_rating': None, 'num_authors': 0, 'avg_authors': None, 'max_price': None} - ->>> list(Publisher.objects.filter(pk=5).annotate(num_authors=Count('book__authors'), avg_authors=Avg('book__authors'), max_authors=Max('book__authors'), max_price=Max('book__price'), max_rating=Max('book__rating')).values()) == [{'max_authors': None, 'name': u"Jonno's House of Books", 'num_awards': 0, 'max_price': None, 'num_authors': 0, 'max_rating': None, 'id': 5, 'avg_authors': None}] -True - -# Regression for #10113 - Fields mentioned in order_by() must be included in the GROUP BY. -# This only becomes a problem when the order_by introduces a new join. ->>> Book.objects.annotate(num_authors=Count('authors')).order_by('publisher__name', 'name') -[<Book: Practical Django Projects>, <Book: The Definitive Guide to Django: Web Development Done Right>, <Book: Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp>, <Book: Artificial Intelligence: A Modern Approach>, <Book: Python Web Development with Django>, <Book: Sams Teach Yourself Django in 24 Hours>] - -# Regression for #10127 - Empty select_related() works with annotate ->>> books = Book.objects.all().filter(rating__lt=4.5).select_related().annotate(Avg('authors__age')) ->>> sorted([(b.name, b.authors__age__avg, b.publisher.name, b.contact.name) for b in books]) -[(u'Artificial Intelligence: A Modern Approach', 51.5, u'Prentice Hall', u'Peter Norvig'), (u'Practical Django Projects', 29.0, u'Apress', u'James Bennett'), (u'Python Web Development with Django', 30.3..., u'Prentice Hall', u'Jeffrey Forcier'), (u'Sams Teach Yourself Django in 24 Hours', 45.0, u'Sams', u'Brad Dayley')] - -# Regression for #10132 - If the values() clause only mentioned extra(select=) columns, those columns are used for grouping ->>> Book.objects.extra(select={'pub':'publisher_id'}).values('pub').annotate(Count('id')).order_by('pub') -[{'pub': 1, 'id__count': 2}, {'pub': 2, 'id__count': 1}, {'pub': 3, 'id__count': 2}, {'pub': 4, 'id__count': 1}] - ->>> Book.objects.extra(select={'pub':'publisher_id','foo':'pages'}).values('pub').annotate(Count('id')).order_by('pub') -[{'pub': 1, 'id__count': 2}, {'pub': 2, 'id__count': 1}, {'pub': 3, 'id__count': 2}, {'pub': 4, 'id__count': 1}] - -# Regression for #10182 - Queries with aggregate calls are correctly realiased when used in a subquery ->>> ids = Book.objects.filter(pages__gt=100).annotate(n_authors=Count('authors')).filter(n_authors__gt=2).order_by('n_authors') ->>> Book.objects.filter(id__in=ids) -[<Book: Python Web Development with Django>] - -# Regression for #10197 -- Queries with aggregates can be pickled. -# First check that pickling is possible at all. No crash = success ->>> qs = Book.objects.annotate(num_authors=Count('authors')) ->>> out = pickle.dumps(qs) - -# Then check that the round trip works. ->>> query = qs.query.get_compiler(qs.db).as_sql()[0] ->>> select_fields = qs.query.select_fields ->>> query2 = pickle.loads(pickle.dumps(qs)) ->>> query2.query.get_compiler(query2.db).as_sql()[0] == query -True ->>> query2.query.select_fields = select_fields - -# Regression for #10199 - Aggregate calls clone the original query so the original query can still be used ->>> books = Book.objects.all() ->>> _ = books.aggregate(Avg('authors__age')) ->>> books.all() -[<Book: Artificial Intelligence: A Modern Approach>, <Book: Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp>, <Book: Practical Django Projects>, <Book: Python Web Development with Django>, <Book: Sams Teach Yourself Django in 24 Hours>, <Book: The Definitive Guide to Django: Web Development Done Right>] - -# Regression for #10248 - Annotations work with DateQuerySets ->>> Book.objects.annotate(num_authors=Count('authors')).filter(num_authors=2).dates('pubdate', 'day') -[datetime.datetime(1995, 1, 15, 0, 0), datetime.datetime(2007, 12, 6, 0, 0)] - -# Regression for #10290 - extra selects with parameters can be used for -# grouping. ->>> qs = Book.objects.all().annotate(mean_auth_age=Avg('authors__age')).extra(select={'sheets' : '(pages + %s) / %s'}, select_params=[1, 2]).order_by('sheets').values('sheets') ->>> [int(x['sheets']) for x in qs] -[150, 175, 224, 264, 473, 566] - -# Regression for 10425 - annotations don't get in the way of a count() clause ->>> Book.objects.values('publisher').annotate(Count('publisher')).count() -4 - ->>> Book.objects.annotate(Count('publisher')).values('publisher').count() -6 - ->>> publishers = Publisher.objects.filter(id__in=(1,2)) ->>> publishers -[<Publisher: Apress>, <Publisher: Sams>] - ->>> publishers = publishers.annotate(n_books=models.Count('book')) ->>> publishers[0].n_books -2 - ->>> publishers -[<Publisher: Apress>, <Publisher: Sams>] - ->>> books = Book.objects.filter(publisher__in=publishers) ->>> books -[<Book: Practical Django Projects>, <Book: Sams Teach Yourself Django in 24 Hours>, <Book: The Definitive Guide to Django: Web Development Done Right>] - ->>> publishers -[<Publisher: Apress>, <Publisher: Sams>] - - -# Regression for 10666 - inherited fields work with annotations and aggregations ->>> HardbackBook.objects.aggregate(n_pages=Sum('book_ptr__pages')) -{'n_pages': 2078} - ->>> HardbackBook.objects.aggregate(n_pages=Sum('pages')) -{'n_pages': 2078} - ->>> HardbackBook.objects.annotate(n_authors=Count('book_ptr__authors')).values('name','n_authors') -[{'n_authors': 2, 'name': u'Artificial Intelligence: A Modern Approach'}, {'n_authors': 1, 'name': u'Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp'}] - ->>> HardbackBook.objects.annotate(n_authors=Count('authors')).values('name','n_authors') -[{'n_authors': 2, 'name': u'Artificial Intelligence: A Modern Approach'}, {'n_authors': 1, 'name': u'Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp'}] - -# Regression for #10766 - Shouldn't be able to reference an aggregate fields in an an aggregate() call. ->>> Book.objects.all().annotate(mean_age=Avg('authors__age')).annotate(Avg('mean_age')) -Traceback (most recent call last): -... -FieldError: Cannot compute Avg('mean_age'): 'mean_age' is an aggregate - -""" -} - -def run_stddev_tests(): - """Check to see if StdDev/Variance tests should be run. - - Stddev and Variance are not guaranteed to be available for SQLite, and - are not available for PostgreSQL before 8.2. - """ - if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] == 'django.db.backends.sqlite3': - return False - - class StdDevPop(object): - sql_function = 'STDDEV_POP' - - try: - connection.ops.check_aggregate_support(StdDevPop()) - except: - return False - return True - -if run_stddev_tests(): - __test__['API_TESTS'] += """ ->>> Book.objects.aggregate(StdDev('pages')) -{'pages__stddev': 311.46...} - ->>> Book.objects.aggregate(StdDev('rating')) -{'rating__stddev': 0.60...} - ->>> Book.objects.aggregate(StdDev('price')) -{'price__stddev': 24.16...} - - ->>> Book.objects.aggregate(StdDev('pages', sample=True)) -{'pages__stddev': 341.19...} - ->>> Book.objects.aggregate(StdDev('rating', sample=True)) -{'rating__stddev': 0.66...} - ->>> Book.objects.aggregate(StdDev('price', sample=True)) -{'price__stddev': 26.46...} - - ->>> Book.objects.aggregate(Variance('pages')) -{'pages__variance': 97010.80...} - ->>> Book.objects.aggregate(Variance('rating')) -{'rating__variance': 0.36...} - ->>> Book.objects.aggregate(Variance('price')) -{'price__variance': 583.77...} - - ->>> Book.objects.aggregate(Variance('pages', sample=True)) -{'pages__variance': 116412.96...} - ->>> Book.objects.aggregate(Variance('rating', sample=True)) -{'rating__variance': 0.44...} - ->>> Book.objects.aggregate(Variance('price', sample=True)) -{'price__variance': 700.53...} - -""" diff --git a/tests/regressiontests/aggregation_regress/tests.py b/tests/regressiontests/aggregation_regress/tests.py index 3c4bdfa47d..51f439c5a1 100644 --- a/tests/regressiontests/aggregation_regress/tests.py +++ b/tests/regressiontests/aggregation_regress/tests.py @@ -1,12 +1,38 @@ +import datetime +from decimal import Decimal + +from django.core.exceptions import FieldError from django.conf import settings -from django.test import TestCase +from django.test import TestCase, Approximate from django.db import DEFAULT_DB_ALIAS -from django.db.models import Count, Max +from django.db.models import Count, Max, Avg, Sum, StdDev, Variance, F from regressiontests.aggregation_regress.models import * +def run_stddev_tests(): + """Check to see if StdDev/Variance tests should be run. + + Stddev and Variance are not guaranteed to be available for SQLite, and + are not available for PostgreSQL before 8.2. + """ + if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] == 'django.db.backends.sqlite3': + return False + + class StdDevPop(object): + sql_function = 'STDDEV_POP' + + try: + connection.ops.check_aggregate_support(StdDevPop()) + except: + return False + return True + + class AggregationTests(TestCase): + def assertObjectAttrs(self, obj, **kwargs): + for attr, value in kwargs.iteritems(): + self.assertEqual(getattr(obj, attr), value) def test_aggregates_in_where_clause(self): """ @@ -70,3 +96,593 @@ class AggregationTests(TestCase): }).annotate(total_books=Count('book')) # force execution of the query list(qs) + + def test_aggregate(self): + # Ordering requests are ignored + self.assertEqual( + Author.objects.order_by("name").aggregate(Avg("age")), + {"age__avg": Approximate(37.444, places=1)} + ) + + # Implicit ordering is also ignored + self.assertEqual( + Book.objects.aggregate(Sum("pages")), + {"pages__sum": 3703}, + ) + + # Baseline results + self.assertEqual( + Book.objects.aggregate(Sum('pages'), Avg('pages')), + {'pages__sum': 3703, 'pages__avg': Approximate(617.166, places=2)} + ) + + # Empty values query doesn't affect grouping or results + self.assertEqual( + Book.objects.values().aggregate(Sum('pages'), Avg('pages')), + {'pages__sum': 3703, 'pages__avg': Approximate(617.166, places=2)} + ) + + # Aggregate overrides extra selected column + self.assertEqual( + Book.objects.extra(select={'price_per_page' : 'price / pages'}).aggregate(Sum('pages')), + {'pages__sum': 3703} + ) + + def test_annotation(self): + # Annotations get combined with extra select clauses + obj = Book.objects.annotate(mean_auth_age=Avg("authors__age")).extra(select={"manufacture_cost": "price * .5"}).get(pk=2) + self.assertObjectAttrs(obj, + contact_id=3, + id=2, + isbn=u'067232959', + mean_auth_age=45.0, + name='Sams Teach Yourself Django in 24 Hours', + pages=528, + price=Decimal("23.09"), + pubdate=datetime.date(2008, 3, 3), + publisher_id=2, + rating=3.0 + ) + # Different DB backends return different types for the extra select computation + self.assertTrue(obj.manufacture_cost == 11.545 or obj.manufacture_cost == Decimal('11.545')) + + # Order of the annotate/extra in the query doesn't matter + obj = Book.objects.extra(select={'manufacture_cost' : 'price * .5'}).annotate(mean_auth_age=Avg('authors__age')).get(pk=2) + self.assertObjectAttrs(obj, + contact_id=3, + id=2, + isbn=u'067232959', + mean_auth_age=45.0, + name=u'Sams Teach Yourself Django in 24 Hours', + pages=528, + price=Decimal("23.09"), + pubdate=datetime.date(2008, 3, 3), + publisher_id=2, + rating=3.0 + ) + # Different DB backends return different types for the extra select computation + self.assertTrue(obj.manufacture_cost == 11.545 or obj.manufacture_cost == Decimal('11.545')) + + # Values queries can be combined with annotate and extra + obj = Book.objects.annotate(mean_auth_age=Avg('authors__age')).extra(select={'manufacture_cost' : 'price * .5'}).values().get(pk=2) + manufacture_cost = obj['manufacture_cost'] + self.assertTrue(manufacture_cost == 11.545 or manufacture_cost == Decimal('11.545')) + del obj['manufacture_cost'] + self.assertEqual(obj, { + "contact_id": 3, + "id": 2, + "isbn": u"067232959", + "mean_auth_age": 45.0, + "name": u"Sams Teach Yourself Django in 24 Hours", + "pages": 528, + "price": Decimal("23.09"), + "pubdate": datetime.date(2008, 3, 3), + "publisher_id": 2, + "rating": 3.0, + }) + + # The order of the (empty) values, annotate and extra clauses doesn't + # matter + obj = Book.objects.values().annotate(mean_auth_age=Avg('authors__age')).extra(select={'manufacture_cost' : 'price * .5'}).get(pk=2) + manufacture_cost = obj['manufacture_cost'] + self.assertTrue(manufacture_cost == 11.545 or manufacture_cost == Decimal('11.545')) + del obj['manufacture_cost'] + self.assertEqual(obj, { + 'contact_id': 3, + 'id': 2, + 'isbn': u'067232959', + 'mean_auth_age': 45.0, + 'name': u'Sams Teach Yourself Django in 24 Hours', + 'pages': 528, + 'price': Decimal("23.09"), + 'pubdate': datetime.date(2008, 3, 3), + 'publisher_id': 2, + 'rating': 3.0 + }) + + # If the annotation precedes the values clause, it won't be included + # unless it is explicitly named + obj = Book.objects.annotate(mean_auth_age=Avg('authors__age')).extra(select={'price_per_page' : 'price / pages'}).values('name').get(pk=1) + self.assertEqual(obj, { + "name": u'The Definitive Guide to Django: Web Development Done Right', + }) + + obj = Book.objects.annotate(mean_auth_age=Avg('authors__age')).extra(select={'price_per_page' : 'price / pages'}).values('name','mean_auth_age').get(pk=1) + self.assertEqual(obj, { + 'mean_auth_age': 34.5, + 'name': u'The Definitive Guide to Django: Web Development Done Right', + }) + + # If an annotation isn't included in the values, it can still be used + # in a filter + qs = Book.objects.annotate(n_authors=Count('authors')).values('name').filter(n_authors__gt=2) + self.assertQuerysetEqual( + qs, [ + {"name": u'Python Web Development with Django'} + ], + lambda b: b, + ) + + # The annotations are added to values output if values() precedes + # annotate() + obj = Book.objects.values('name').annotate(mean_auth_age=Avg('authors__age')).extra(select={'price_per_page' : 'price / pages'}).get(pk=1) + self.assertEqual(obj, { + 'mean_auth_age': 34.5, + 'name': u'The Definitive Guide to Django: Web Development Done Right', + }) + + # Check that all of the objects are getting counted (allow_nulls) and + # that values respects the amount of objects + self.assertEqual( + len(Author.objects.annotate(Avg('friends__age')).values()), + 9 + ) + + # Check that consecutive calls to annotate accumulate in the query + qs = Book.objects.values('price').annotate(oldest=Max('authors__age')).order_by('oldest', 'price').annotate(Max('publisher__num_awards')) + self.assertQuerysetEqual( + qs, [ + {'price': Decimal("30"), 'oldest': 35, 'publisher__num_awards__max': 3}, + {'price': Decimal("29.69"), 'oldest': 37, 'publisher__num_awards__max': 7}, + {'price': Decimal("23.09"), 'oldest': 45, 'publisher__num_awards__max': 1}, + {'price': Decimal("75"), 'oldest': 57, 'publisher__num_awards__max': 9}, + {'price': Decimal("82.8"), 'oldest': 57, 'publisher__num_awards__max': 7} + ], + lambda b: b, + ) + + def test_aggrate_annotation(self): + # Aggregates can be composed over annotations. + # The return type is derived from the composed aggregate + vals = Book.objects.all().annotate(num_authors=Count('authors__id')).aggregate(Max('pages'), Max('price'), Sum('num_authors'), Avg('num_authors')) + self.assertEqual(vals, { + 'num_authors__sum': 10, + 'num_authors__avg': Approximate(1.666, places=2), + 'pages__max': 1132, + 'price__max': Decimal("82.80") + }) + + def test_field_error(self): + # Bad field requests in aggregates are caught and reported + self.assertRaises( + FieldError, + lambda: Book.objects.all().aggregate(num_authors=Count('foo')) + ) + + self.assertRaises( + FieldError, + lambda: Book.objects.all().annotate(num_authors=Count('foo')) + ) + + self.assertRaises( + FieldError, + lambda: Book.objects.all().annotate(num_authors=Count('authors__id')).aggregate(Max('foo')) + ) + + def test_more(self): + # Old-style count aggregations can be mixed with new-style + self.assertEqual( + Book.objects.annotate(num_authors=Count('authors')).count(), + 6 + ) + + # Non-ordinal, non-computed Aggregates over annotations correctly + # inherit the annotation's internal type if the annotation is ordinal + # or computed + vals = Book.objects.annotate(num_authors=Count('authors')).aggregate(Max('num_authors')) + self.assertEqual( + vals, + {'num_authors__max': 3} + ) + + vals = Publisher.objects.annotate(avg_price=Avg('book__price')).aggregate(Max('avg_price')) + self.assertEqual( + vals, + {'avg_price__max': 75.0} + ) + + # Aliases are quoted to protected aliases that might be reserved names + vals = Book.objects.aggregate(number=Max('pages'), select=Max('pages')) + self.assertEqual( + vals, + {'number': 1132, 'select': 1132} + ) + + # Regression for #10064: select_related() plays nice with aggregates + obj = Book.objects.select_related('publisher').annotate(num_authors=Count('authors')).values()[0] + self.assertEqual(obj, { + 'contact_id': 8, + 'id': 5, + 'isbn': u'013790395', + 'name': u'Artificial Intelligence: A Modern Approach', + 'num_authors': 2, + 'pages': 1132, + 'price': Decimal("82.8"), + 'pubdate': datetime.date(1995, 1, 15), + 'publisher_id': 3, + 'rating': 4.0, + }) + + # Regression for #10010: exclude on an aggregate field is correctly + # negated + self.assertEqual( + len(Book.objects.annotate(num_authors=Count('authors'))), + 6 + ) + self.assertEqual( + len(Book.objects.annotate(num_authors=Count('authors')).filter(num_authors__gt=2)), + 1 + ) + self.assertEqual( + len(Book.objects.annotate(num_authors=Count('authors')).exclude(num_authors__gt=2)), + 5 + ) + + self.assertEqual( + len(Book.objects.annotate(num_authors=Count('authors')).filter(num_authors__lt=3).exclude(num_authors__lt=2)), + 2 + ) + self.assertEqual( + len(Book.objects.annotate(num_authors=Count('authors')).exclude(num_authors__lt=2).filter(num_authors__lt=3)), + 2 + ) + + def test_aggregate_fexpr(self): + # Aggregates can be used with F() expressions + # ... where the F() is pushed into the HAVING clause + qs = Publisher.objects.annotate(num_books=Count('book')).filter(num_books__lt=F('num_awards')/2).order_by('name').values('name','num_books','num_awards') + self.assertQuerysetEqual( + qs, [ + {'num_books': 1, 'name': u'Morgan Kaufmann', 'num_awards': 9}, + {'num_books': 2, 'name': u'Prentice Hall', 'num_awards': 7} + ], + lambda p: p, + ) + + qs = Publisher.objects.annotate(num_books=Count('book')).exclude(num_books__lt=F('num_awards')/2).order_by('name').values('name','num_books','num_awards') + self.assertQuerysetEqual( + qs, [ + {'num_books': 2, 'name': u'Apress', 'num_awards': 3}, + {'num_books': 0, 'name': u"Jonno's House of Books", 'num_awards': 0}, + {'num_books': 1, 'name': u'Sams', 'num_awards': 1} + ], + lambda p: p, + ) + + # ... and where the F() references an aggregate + qs = Publisher.objects.annotate(num_books=Count('book')).filter(num_awards__gt=2*F('num_books')).order_by('name').values('name','num_books','num_awards') + self.assertQuerysetEqual( + qs, [ + {'num_books': 1, 'name': u'Morgan Kaufmann', 'num_awards': 9}, + {'num_books': 2, 'name': u'Prentice Hall', 'num_awards': 7} + ], + lambda p: p, + ) + + qs = Publisher.objects.annotate(num_books=Count('book')).exclude(num_books__lt=F('num_awards')/2).order_by('name').values('name','num_books','num_awards') + self.assertQuerysetEqual( + qs, [ + {'num_books': 2, 'name': u'Apress', 'num_awards': 3}, + {'num_books': 0, 'name': u"Jonno's House of Books", 'num_awards': 0}, + {'num_books': 1, 'name': u'Sams', 'num_awards': 1} + ], + lambda p: p, + ) + + def test_db_col_table(self): + # Tests on fields with non-default table and column names. + qs = Clues.objects.values('EntryID__Entry').annotate(Appearances=Count('EntryID'), Distinct_Clues=Count('Clue', distinct=True)) + self.assertQuerysetEqual(qs, []) + + qs = Entries.objects.annotate(clue_count=Count('clues__ID')) + self.assertQuerysetEqual(qs, []) + + def test_empty(self): + # Regression for #10089: Check handling of empty result sets with + # aggregates + self.assertEqual( + Book.objects.filter(id__in=[]).count(), + 0 + ) + + vals = Book.objects.filter(id__in=[]).aggregate(num_authors=Count('authors'), avg_authors=Avg('authors'), max_authors=Max('authors'), max_price=Max('price'), max_rating=Max('rating')) + self.assertEqual( + vals, + {'max_authors': None, 'max_rating': None, 'num_authors': 0, 'avg_authors': None, 'max_price': None} + ) + + qs = Publisher.objects.filter(pk=5).annotate(num_authors=Count('book__authors'), avg_authors=Avg('book__authors'), max_authors=Max('book__authors'), max_price=Max('book__price'), max_rating=Max('book__rating')).values() + self.assertQuerysetEqual( + qs, [ + {'max_authors': None, 'name': u"Jonno's House of Books", 'num_awards': 0, 'max_price': None, 'num_authors': 0, 'max_rating': None, 'id': 5, 'avg_authors': None} + ], + lambda p: p + ) + + def test_more_more(self): + # Regression for #10113 - Fields mentioned in order_by() must be + # included in the GROUP BY. This only becomes a problem when the + # order_by introduces a new join. + self.assertQuerysetEqual( + Book.objects.annotate(num_authors=Count('authors')).order_by('publisher__name', 'name'), [ + "Practical Django Projects", + "The Definitive Guide to Django: Web Development Done Right", + "Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp", + "Artificial Intelligence: A Modern Approach", + "Python Web Development with Django", + "Sams Teach Yourself Django in 24 Hours", + ], + lambda b: b.name + ) + + # Regression for #10127 - Empty select_related() works with annotate + qs = Book.objects.filter(rating__lt=4.5).select_related().annotate(Avg('authors__age')) + self.assertQuerysetEqual( + qs, [ + (u'Artificial Intelligence: A Modern Approach', 51.5, u'Prentice Hall', u'Peter Norvig'), + (u'Practical Django Projects', 29.0, u'Apress', u'James Bennett'), + (u'Python Web Development with Django', Approximate(30.333, places=2), u'Prentice Hall', u'Jeffrey Forcier'), + (u'Sams Teach Yourself Django in 24 Hours', 45.0, u'Sams', u'Brad Dayley') + ], + lambda b: (b.name, b.authors__age__avg, b.publisher.name, b.contact.name) + ) + + # Regression for #10132 - If the values() clause only mentioned extra + # (select=) columns, those columns are used for grouping + qs = Book.objects.extra(select={'pub':'publisher_id'}).values('pub').annotate(Count('id')).order_by('pub') + self.assertQuerysetEqual( + qs, [ + {'pub': 1, 'id__count': 2}, + {'pub': 2, 'id__count': 1}, + {'pub': 3, 'id__count': 2}, + {'pub': 4, 'id__count': 1} + ], + lambda b: b + ) + + qs = Book.objects.extra(select={'pub':'publisher_id', 'foo':'pages'}).values('pub').annotate(Count('id')).order_by('pub') + self.assertQuerysetEqual( + qs, [ + {'pub': 1, 'id__count': 2}, + {'pub': 2, 'id__count': 1}, + {'pub': 3, 'id__count': 2}, + {'pub': 4, 'id__count': 1} + ], + lambda b: b + ) + + # Regression for #10182 - Queries with aggregate calls are correctly + # realiased when used in a subquery + ids = Book.objects.filter(pages__gt=100).annotate(n_authors=Count('authors')).filter(n_authors__gt=2).order_by('n_authors') + self.assertQuerysetEqual( + Book.objects.filter(id__in=ids), [ + "Python Web Development with Django", + ], + lambda b: b.name + ) + + def test_pickle(self): + # Regression for #10197 -- Queries with aggregates can be pickled. + # First check that pickling is possible at all. No crash = success + qs = Book.objects.annotate(num_authors=Count('authors')) + out = pickle.dumps(qs) + + # Then check that the round trip works. + query = qs.query.get_compiler(qs.db).as_sql()[0] + qs2 = pickle.loads(pickle.dumps(qs)) + self.assertEqual( + qs2.query.get_compiler(qs2.db).as_sql()[0], + query, + ) + + def test_more_more_more(self): + # Regression for #10199 - Aggregate calls clone the original query so + # the original query can still be used + books = Book.objects.all() + books.aggregate(Avg("authors__age")) + self.assertQuerysetEqual( + books.all(), [ + u'Artificial Intelligence: A Modern Approach', + u'Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp', + u'Practical Django Projects', + u'Python Web Development with Django', + u'Sams Teach Yourself Django in 24 Hours', + u'The Definitive Guide to Django: Web Development Done Right' + ], + lambda b: b.name + ) + + # Regression for #10248 - Annotations work with DateQuerySets + qs = Book.objects.annotate(num_authors=Count('authors')).filter(num_authors=2).dates('pubdate', 'day') + self.assertQuerysetEqual( + qs, [ + datetime.datetime(1995, 1, 15, 0, 0), + datetime.datetime(2007, 12, 6, 0, 0) + ], + lambda b: b + ) + + # Regression for #10290 - extra selects with parameters can be used for + # grouping. + qs = Book.objects.annotate(mean_auth_age=Avg('authors__age')).extra(select={'sheets' : '(pages + %s) / %s'}, select_params=[1, 2]).order_by('sheets').values('sheets') + self.assertQuerysetEqual( + qs, [ + 150, + 175, + 224, + 264, + 473, + 566 + ], + lambda b: int(b["sheets"]) + ) + + # Regression for 10425 - annotations don't get in the way of a count() + # clause + self.assertEqual( + Book.objects.values('publisher').annotate(Count('publisher')).count(), + 4 + ) + self.assertEqual( + Book.objects.annotate(Count('publisher')).values('publisher').count(), + 6 + ) + + publishers = Publisher.objects.filter(id__in=[1, 2]) + self.assertQuerysetEqual( + publishers, [ + "Apress", + "Sams" + ], + lambda p: p.name + ) + + publishers = publishers.annotate(n_books=Count("book")) + self.assertEqual( + publishers[0].n_books, + 2 + ) + + self.assertQuerysetEqual( + publishers, [ + "Apress", + "Sams", + ], + lambda p: p.name + ) + + books = Book.objects.filter(publisher__in=publishers) + self.assertQuerysetEqual( + books, [ + "Practical Django Projects", + "Sams Teach Yourself Django in 24 Hours", + "The Definitive Guide to Django: Web Development Done Right", + ], + lambda b: b.name + ) + self.assertQuerysetEqual( + publishers, [ + "Apress", + "Sams", + ], + lambda p: p.name + ) + + # Regression for 10666 - inherited fields work with annotations and + # aggregations + self.assertEqual( + HardbackBook.objects.aggregate(n_pages=Sum('book_ptr__pages')), + {'n_pages': 2078} + ) + + self.assertEqual( + HardbackBook.objects.aggregate(n_pages=Sum('pages')), + {'n_pages': 2078}, + ) + + qs = HardbackBook.objects.annotate(n_authors=Count('book_ptr__authors')).values('name', 'n_authors') + self.assertQuerysetEqual( + qs, [ + {'n_authors': 2, 'name': u'Artificial Intelligence: A Modern Approach'}, + {'n_authors': 1, 'name': u'Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp'} + ], + lambda h: h + ) + + qs = HardbackBook.objects.annotate(n_authors=Count('authors')).values('name', 'n_authors') + self.assertQuerysetEqual( + qs, [ + {'n_authors': 2, 'name': u'Artificial Intelligence: A Modern Approach'}, + {'n_authors': 1, 'name': u'Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp'} + ], + lambda h: h, + ) + + # Regression for #10766 - Shouldn't be able to reference an aggregate + # fields in an an aggregate() call. + self.assertRaises( + FieldError, + lambda: Book.objects.annotate(mean_age=Avg('authors__age')).annotate(Avg('mean_age')) + ) + + if run_stddev_tests(): + def test_stddev(self): + self.assertEqual( + Book.objects.aggregate(StdDev('pages')), + {'pages__stddev': Approximate(311.46, 1)} + ) + + self.assertEqual( + Book.objects.aggregate(StdDev('rating')), + {'rating__stddev': Approximate(0.60, 1)} + ) + + self.assertEqual( + Book.objects.aggregate(StdDev('price')), + {'price__stddev': Approximate(24.16, 2)} + ) + + self.assertEqual( + Book.objects.aggregate(StdDev('pages', sample=True)), + {'pages__stddev': Approximate(341.19, 2)} + ) + + self.assertEqual( + Book.objects.aggregate(StdDev('rating', sample=True)), + {'rating__stddev': Approximate(0.66, 2)} + ) + + self.assertEqual( + Book.objects.aggregate(StdDev('price', sample=True)), + {'price__stddev': Approximate(26.46, 1)} + ) + + self.assertEqual( + Book.objects.aggregate(Variance('pages')), + {'pages__variance': Approximate(97010.80, 1)} + ) + + self.assertEqual( + Book.objects.aggregate(Variance('rating')), + {'rating__variance': Approximate(0.36, 1)} + ) + + self.assertEqual( + Book.objects.aggregate(Variance('price')), + {'price__variance': Approximate(583.77, 1)} + ) + + self.assertEqual( + Book.objects.aggregate(Variance('pages', sample=True)), + {'pages__variance': Approximate(116412.96, 1)} + ) + + self.assertEqual( + Book.objects.aggregate(Variance('rating', sample=True)), + {'rating__variance': Approximate(0.44, 2)} + ) + + self.assertEqual( + Book.objects.aggregate(Variance('price', sample=True)), + {'price__variance': Approximate(700.53, 2)} + ) diff --git a/tests/regressiontests/backends/models.py b/tests/regressiontests/backends/models.py index 423bead1ad..7315408d49 100644 --- a/tests/regressiontests/backends/models.py +++ b/tests/regressiontests/backends/models.py @@ -1,5 +1,9 @@ +from django.contrib.contenttypes import generic +from django.contrib.contenttypes.models import ContentType +from django.conf import settings from django.db import models -from django.db import connection +from django.db import connection, DEFAULT_DB_ALIAS + class Square(models.Model): root = models.IntegerField() @@ -8,6 +12,7 @@ class Square(models.Model): def __unicode__(self): return "%s ** 2 == %s" % (self.root, self.square) + class Person(models.Model): first_name = models.CharField(max_length=20) last_name = models.CharField(max_length=20) @@ -15,11 +20,40 @@ class Person(models.Model): def __unicode__(self): return u'%s %s' % (self.first_name, self.last_name) + class SchoolClass(models.Model): year = models.PositiveIntegerField() day = models.CharField(max_length=9, blank=True) last_updated = models.DateTimeField() +# Unfortunately, the following model breaks MySQL hard. +# Until #13711 is fixed, this test can't be run under MySQL. +if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.mysql': + class VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ(models.Model): + class Meta: + # We need to use a short actual table name or + # we hit issue #8548 which we're not testing! + verbose_name = 'model_with_long_table_name' + primary_key_is_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz = models.AutoField(primary_key=True) + charfield_is_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz = models.CharField(max_length=100) + m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz = models.ManyToManyField(Person,blank=True) + + +class Tag(models.Model): + name = models.CharField(max_length=30) + content_type = models.ForeignKey(ContentType, related_name='backend_tags') + object_id = models.PositiveIntegerField() + content_object = generic.GenericForeignKey('content_type', 'object_id') + + +class Post(models.Model): + name = models.CharField(max_length=30) + text = models.TextField() + tags = generic.GenericRelation('Tag') + + class Meta: + db_table = 'CaseSensitive_Post' + qn = connection.ops.quote_name __test__ = {'API_TESTS': """ diff --git a/tests/regressiontests/backends/tests.py b/tests/regressiontests/backends/tests.py index 6a26a608eb..01764135fa 100644 --- a/tests/regressiontests/backends/tests.py +++ b/tests/regressiontests/backends/tests.py @@ -1,13 +1,18 @@ # -*- coding: utf-8 -*- # Unit and doctests for specific database backends. import datetime -import models import unittest -from django.db import backend, connection, DEFAULT_DB_ALIAS -from django.db.backends.signals import connection_created + from django.conf import settings +from django.core import management +from django.core.management.color import no_style +from django.db import backend, connection, connections, DEFAULT_DB_ALIAS +from django.db.backends.signals import connection_created +from django.db.backends.postgresql import version as pg_version from django.test import TestCase +from regressiontests.backends import models + class Callproc(unittest.TestCase): def test_dbms_session(self): @@ -76,6 +81,7 @@ class DateQuotingTest(TestCase): classes = models.SchoolClass.objects.filter(last_updated__day=20) self.assertEqual(len(classes), 1) + class ParameterHandlingTest(TestCase): def test_bad_parameter_count(self): "An executemany call with too many/not enough parameters will raise an exception (Refs #12612)" @@ -88,46 +94,95 @@ class ParameterHandlingTest(TestCase): self.assertRaises(Exception, cursor.executemany, query, [(1,2,3),]) self.assertRaises(Exception, cursor.executemany, query, [(1,),]) +# Unfortunately, the following tests would be a good test to run on all +# backends, but it breaks MySQL hard. Until #13711 is fixed, it can't be run +# everywhere (although it would be an effective test of #13711). +if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.mysql': + class LongNameTest(TestCase): + """Long primary keys and model names can result in a sequence name + that exceeds the database limits, which will result in truncation + on certain databases (e.g., Postgres). The backend needs to use + the correct sequence name in last_insert_id and other places, so + check it is. Refs #8901. + """ -def connection_created_test(sender, **kwargs): - print 'connection_created signal' - -__test__ = {'API_TESTS': """ -# Check Postgres version parsing ->>> from django.db.backends.postgresql import version as pg_version - ->>> pg_version._parse_version("PostgreSQL 8.3.1 on i386-apple-darwin9.2.2, compiled by GCC i686-apple-darwin9-gcc-4.0.1 (GCC) 4.0.1 (Apple Inc. build 5478)") -(8, 3, 1) - ->>> pg_version._parse_version("PostgreSQL 8.3.6") -(8, 3, 6) - ->>> pg_version._parse_version("PostgreSQL 8.3") -(8, 3, None) - ->>> pg_version._parse_version("EnterpriseDB 8.3") -(8, 3, None) - ->>> pg_version._parse_version("PostgreSQL 8.3 beta4") -(8, 3, None) + def test_sequence_name_length_limits_create(self): + """Test creation of model with long name and long pk name doesn't error. Ref #8901""" + models.VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ.objects.create() + + def test_sequence_name_length_limits_m2m(self): + """Test an m2m save of a model with a long name and a long m2m field name doesn't error as on Django >=1.2 this now uses object saves. Ref #8901""" + obj = models.VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ.objects.create() + rel_obj = models.Person.objects.create(first_name='Django', last_name='Reinhardt') + obj.m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.add(rel_obj) + + def test_sequence_name_length_limits_flush(self): + """Test that sequence resetting as part of a flush with model with long name and long pk name doesn't error. Ref #8901""" + # A full flush is expensive to the full test, so we dig into the + # internals to generate the likely offending SQL and run it manually + + # Some convenience aliases + VLM = models.VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ + VLM_m2m = VLM.m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.through + tables = [ + VLM._meta.db_table, + VLM_m2m._meta.db_table, + ] + sequences = [ + { + 'column': VLM._meta.pk.column, + 'table': VLM._meta.db_table + }, + ] + cursor = connection.cursor() + for statement in connection.ops.sql_flush(no_style(), tables, sequences): + cursor.execute(statement) ->>> pg_version._parse_version("PostgreSQL 8.4beta1") -(8, 4, None) +class SequenceResetTest(TestCase): + def test_generic_relation(self): + "Sequence names are correct when resetting generic relations (Ref #13941)" + # Create an object with a manually specified PK + models.Post.objects.create(id=10, name='1st post', text='hello world') -"""} + # Reset the sequences for the database + cursor = connection.cursor() + commands = connections[DEFAULT_DB_ALIAS].ops.sequence_reset_sql(no_style(), [models.Post]) + for sql in commands: + cursor.execute(sql) + + # If we create a new object now, it should have a PK greater + # than the PK we specified manually. + obj = models.Post.objects.create(name='New post', text='goodbye world') + self.assertTrue(obj.pk > 10) + +class PostgresVersionTest(TestCase): + def assert_parses(self, version_string, version): + self.assertEqual(pg_version._parse_version(version_string), version) + + def test_parsing(self): + self.assert_parses("PostgreSQL 8.3 beta4", (8, 3, None)) + self.assert_parses("PostgreSQL 8.3", (8, 3, None)) + self.assert_parses("EnterpriseDB 8.3", (8, 3, None)) + self.assert_parses("PostgreSQL 8.3.6", (8, 3, 6)) + self.assert_parses("PostgreSQL 8.4beta1", (8, 4, None)) + self.assert_parses("PostgreSQL 8.3.1 on i386-apple-darwin9.2.2, compiled by GCC i686-apple-darwin9-gcc-4.0.1 (GCC) 4.0.1 (Apple Inc. build 5478)", (8, 3, 1)) # Unfortunately with sqlite3 the in-memory test database cannot be # closed, and so it cannot be re-opened during testing, and so we # sadly disable this test for now. -if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.sqlite3': - __test__['API_TESTS'] += """ ->>> connection_created.connect(connection_created_test) ->>> connection.close() # Ensure the connection is closed ->>> cursor = connection.cursor() -connection_created signal ->>> connection_created.disconnect(connection_created_test) ->>> cursor = connection.cursor() -""" - -if __name__ == '__main__': - unittest.main() +if settings.DATABASES[DEFAULT_DB_ALIAS]["ENGINE"] != "django.db.backends.sqlite3": + class ConnectionCreatedSignalTest(TestCase): + def test_signal(self): + data = {} + def receiver(sender, connection, **kwargs): + data["connection"] = connection + + connection_created.connect(receiver) + connection.close() + cursor = connection.cursor() + self.assertTrue(data["connection"] is connection) + + connection_created.disconnect(receiver) + data.clear() + cursor = connection.cursor() + self.assertTrue(data == {}) diff --git a/tests/regressiontests/cache/liberal_backend.py b/tests/regressiontests/cache/liberal_backend.py new file mode 100644 index 0000000000..5c7e312690 --- /dev/null +++ b/tests/regressiontests/cache/liberal_backend.py @@ -0,0 +1,9 @@ +from django.core.cache.backends.locmem import CacheClass as LocMemCacheClass + +class LiberalKeyValidationMixin(object): + def validate_key(self, key): + pass + +class CacheClass(LiberalKeyValidationMixin, LocMemCacheClass): + pass + diff --git a/tests/regressiontests/cache/tests.py b/tests/regressiontests/cache/tests.py index 109374c46c..1e0a4046bb 100644 --- a/tests/regressiontests/cache/tests.py +++ b/tests/regressiontests/cache/tests.py @@ -8,11 +8,12 @@ import shutil import tempfile import time import unittest +import warnings from django.conf import settings from django.core import management from django.core.cache import get_cache -from django.core.cache.backends.base import InvalidCacheBackendError +from django.core.cache.backends.base import InvalidCacheBackendError, CacheKeyWarning from django.http import HttpResponse, HttpRequest from django.middleware.cache import FetchFromCacheMiddleware, UpdateCacheMiddleware from django.utils import translation @@ -352,21 +353,68 @@ class BaseCacheTests(object): self.assertEqual(self.cache.get('key3'), 'sausage') self.assertEqual(self.cache.get('key4'), 'lobster bisque') + def perform_cull_test(self, initial_count, final_count): + """This is implemented as a utility method, because only some of the backends + implement culling. The culling algorithm also varies slightly, so the final + number of entries will vary between backends""" + # Create initial cache key entries. This will overflow the cache, causing a cull + for i in range(1, initial_count): + self.cache.set('cull%d' % i, 'value', 1000) + count = 0 + # Count how many keys are left in the cache. + for i in range(1, initial_count): + if self.cache.has_key('cull%d' % i): + count = count + 1 + self.assertEqual(count, final_count) + + def test_invalid_keys(self): + """ + All the builtin backends (except memcached, see below) should warn on + keys that would be refused by memcached. This encourages portable + caching code without making it too difficult to use production backends + with more liberal key rules. Refs #6447. + + """ + # On Python 2.6+ we could use the catch_warnings context + # manager to test this warning nicely. Since we can't do that + # yet, the cleanest option is to temporarily ask for + # CacheKeyWarning to be raised as an exception. + warnings.simplefilter("error", CacheKeyWarning) + + # memcached does not allow whitespace or control characters in keys + self.assertRaises(CacheKeyWarning, self.cache.set, 'key with spaces', 'value') + # memcached limits key length to 250 + self.assertRaises(CacheKeyWarning, self.cache.set, 'a' * 251, 'value') + + # The warnings module has no public API for getting the + # current list of warning filters, so we can't save that off + # and reset to the previous value, we have to globally reset + # it. The effect will be the same, as long as the Django test + # runner doesn't add any global warning filters (it currently + # does not). + warnings.resetwarnings() + class DBCacheTests(unittest.TestCase, BaseCacheTests): def setUp(self): # Spaces are used in the table name to ensure quoting/escaping is working self._table_name = 'test cache table' management.call_command('createcachetable', self._table_name, verbosity=0, interactive=False) - self.cache = get_cache('db://%s' % self._table_name) + self.cache = get_cache('db://%s?max_entries=30' % self._table_name) def tearDown(self): from django.db import connection cursor = connection.cursor() cursor.execute('DROP TABLE %s' % connection.ops.quote_name(self._table_name)) + def test_cull(self): + self.perform_cull_test(50, 29) + class LocMemCacheTests(unittest.TestCase, BaseCacheTests): def setUp(self): - self.cache = get_cache('locmem://') + self.cache = get_cache('locmem://?max_entries=30') + + def test_cull(self): + self.perform_cull_test(50, 29) # memcached backend isn't guaranteed to be available. # To check the memcached backend, the test settings file will @@ -377,13 +425,29 @@ if settings.CACHE_BACKEND.startswith('memcached://'): def setUp(self): self.cache = get_cache(settings.CACHE_BACKEND) + def test_invalid_keys(self): + """ + On memcached, we don't introduce a duplicate key validation + step (for speed reasons), we just let the memcached API + library raise its own exception on bad keys. Refs #6447. + + In order to be memcached-API-library agnostic, we only assert + that a generic exception of some kind is raised. + + """ + # memcached does not allow whitespace or control characters in keys + self.assertRaises(Exception, self.cache.set, 'key with spaces', 'value') + # memcached limits key length to 250 + self.assertRaises(Exception, self.cache.set, 'a' * 251, 'value') + + class FileBasedCacheTests(unittest.TestCase, BaseCacheTests): """ Specific test cases for the file-based cache. """ def setUp(self): self.dirname = tempfile.mkdtemp() - self.cache = get_cache('file://%s' % self.dirname) + self.cache = get_cache('file://%s?max_entries=30' % self.dirname) def test_hashing(self): """Test that keys are hashed into subdirectories correctly""" @@ -406,6 +470,25 @@ class FileBasedCacheTests(unittest.TestCase, BaseCacheTests): self.assert_(not os.path.exists(os.path.dirname(keypath))) self.assert_(not os.path.exists(os.path.dirname(os.path.dirname(keypath)))) + def test_cull(self): + self.perform_cull_test(50, 28) + +class CustomCacheKeyValidationTests(unittest.TestCase): + """ + Tests for the ability to mixin a custom ``validate_key`` method to + a custom cache backend that otherwise inherits from a builtin + backend, and override the default key validation. Refs #6447. + + """ + def test_custom_key_validation(self): + cache = get_cache('regressiontests.cache.liberal_backend://') + + # this key is both longer than 250 characters, and has spaces + key = 'some key with spaces' * 15 + val = 'a value' + cache.set(key, val) + self.assertEqual(cache.get(key), val) + class CacheUtils(unittest.TestCase): """TestCase for django.utils.cache functions.""" diff --git a/tests/regressiontests/csrf_tests/tests.py b/tests/regressiontests/csrf_tests/tests.py index 0a24522d9c..9030d397ab 100644 --- a/tests/regressiontests/csrf_tests/tests.py +++ b/tests/regressiontests/csrf_tests/tests.py @@ -3,7 +3,7 @@ from django.test import TestCase from django.http import HttpRequest, HttpResponse from django.middleware.csrf import CsrfMiddleware, CsrfViewMiddleware -from django.views.decorators.csrf import csrf_exempt +from django.views.decorators.csrf import csrf_exempt, csrf_view_exempt from django.core.context_processors import csrf from django.contrib.sessions.middleware import SessionMiddleware from django.utils.importlib import import_module @@ -12,8 +12,8 @@ from django.template import RequestContext, Template # Response/views used for CsrfResponseMiddleware and CsrfViewMiddleware tests def post_form_response(): - resp = HttpResponse(content=""" -<html><body><form method="post"><input type="text" /></form></body></html> + resp = HttpResponse(content=u""" +<html><body><h1>\u00a1Unicode!<form method="post"><input type="text" /></form></body></html> """, mimetype="text/html") return resp @@ -56,6 +56,9 @@ class TestingHttpRequest(HttpRequest): return getattr(self, '_is_secure', False) class CsrfMiddlewareTest(TestCase): + # The csrf token is potentially from an untrusted source, so could have + # characters that need dealing with. + _csrf_id_cookie = "<1>\xc2\xa1" _csrf_id = "1" # This is a valid session token for this ID and secret key. This was generated using @@ -71,7 +74,7 @@ class CsrfMiddlewareTest(TestCase): def _get_GET_csrf_cookie_request(self): req = TestingHttpRequest() - req.COOKIES[settings.CSRF_COOKIE_NAME] = self._csrf_id + req.COOKIES[settings.CSRF_COOKIE_NAME] = self._csrf_id_cookie return req def _get_POST_csrf_cookie_request(self): @@ -123,6 +126,23 @@ class CsrfMiddlewareTest(TestCase): # Check the Vary header got patched correctly self.assert_('Cookie' in resp2.get('Vary','')) + def test_process_response_for_exempt_view(self): + """ + Check that a view decorated with 'csrf_view_exempt' is still + post-processed to add the CSRF token. + """ + req = self._get_GET_no_csrf_cookie_request() + CsrfMiddleware().process_view(req, csrf_view_exempt(post_form_view), (), {}) + + resp = post_form_response() + resp_content = resp.content # needed because process_response modifies resp + resp2 = CsrfMiddleware().process_response(req, resp) + + csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, False) + self.assertNotEqual(csrf_cookie, False) + self.assertNotEqual(resp_content, resp2.content) + self._check_token_present(resp2, csrf_cookie.value) + def test_process_response_no_csrf_cookie_view_only_get_token_used(self): """ When no prior CSRF cookie exists, check that the cookie is created, even @@ -187,8 +207,11 @@ class CsrfMiddlewareTest(TestCase): """ Check that no post processing is done for an exempt view """ - req = self._get_POST_csrf_cookie_request() - resp = csrf_exempt(post_form_view)(req) + req = self._get_GET_csrf_cookie_request() + view = csrf_exempt(post_form_view) + CsrfMiddleware().process_view(req, view, (), {}) + + resp = view(req) resp_content = resp.content resp2 = CsrfMiddleware().process_response(req, resp) self.assertEquals(resp_content, resp2.content) @@ -270,6 +293,17 @@ class CsrfMiddlewareTest(TestCase): resp = token_view(req) self.assertEquals(u"", resp.content) + def test_token_node_empty_csrf_cookie(self): + """ + Check that we get a new token if the csrf_cookie is the empty string + """ + req = self._get_GET_no_csrf_cookie_request() + req.COOKIES[settings.CSRF_COOKIE_NAME] = "" + CsrfViewMiddleware().process_view(req, token_view, (), {}) + resp = token_view(req) + + self.assertNotEqual(u"", resp.content) + def test_token_node_with_csrf_cookie(self): """ Check that CsrfTokenNode works when a CSRF cookie is set @@ -279,6 +313,15 @@ class CsrfMiddlewareTest(TestCase): resp = token_view(req) self._check_token_present(resp) + def test_get_token_for_exempt_view(self): + """ + Check that get_token still works for a view decorated with 'csrf_view_exempt'. + """ + req = self._get_GET_csrf_cookie_request() + CsrfViewMiddleware().process_view(req, csrf_view_exempt(token_view), (), {}) + resp = token_view(req) + self._check_token_present(resp) + def test_token_node_with_new_csrf_cookie(self): """ Check that CsrfTokenNode works when a CSRF cookie is created by diff --git a/tests/regressiontests/defaultfilters/tests.py b/tests/regressiontests/defaultfilters/tests.py index 25555081e4..8341f83e36 100644 --- a/tests/regressiontests/defaultfilters/tests.py +++ b/tests/regressiontests/defaultfilters/tests.py @@ -476,6 +476,15 @@ u'1024.0 MB' >>> filesizeformat(1024*1024*1024) u'1.0 GB' +>>> filesizeformat(1024*1024*1024*1024) +u'1.0 TB' + +>>> filesizeformat(1024*1024*1024*1024*1024) +u'1.0 PB' + +>>> filesizeformat(1024*1024*1024*1024*1024*2000) +u'2000.0 PB' + >>> filesizeformat(complex(1,-1)) u'0 bytes' diff --git a/tests/regressiontests/file_storage/tests.py b/tests/regressiontests/file_storage/tests.py index 2c5f0f4551..a1470f9f05 100644 --- a/tests/regressiontests/file_storage/tests.py +++ b/tests/regressiontests/file_storage/tests.py @@ -230,3 +230,19 @@ if Image is not None: finally: del images.open self.assert_(FileWrapper._closed) + + class InconsistentGetImageDimensionsBug(TestCase): + """ + Test that get_image_dimensions() works properly after various calls using a file handler (#11158) + """ + def test_multiple_calls(self): + """ + Multiple calls of get_image_dimensions() should return the same size. + """ + from django.core.files.images import ImageFile + img_path = os.path.join(os.path.dirname(__file__), "test.png") + image = ImageFile(open(img_path)) + image_pil = Image.open(img_path) + size_1, size_2 = get_image_dimensions(image), get_image_dimensions(image) + self.assertEqual(image_pil.size, size_1) + self.assertEqual(size_1, size_2) diff --git a/tests/regressiontests/forms/fields.py b/tests/regressiontests/forms/fields.py index 990a9f74cf..e4f2c261c9 100644 --- a/tests/regressiontests/forms/fields.py +++ b/tests/regressiontests/forms/fields.py @@ -766,13 +766,13 @@ class FieldsTests(TestCase): # FilePathField ############################################################### def test_filepathfield_65(self): - path = forms.__file__ + path = os.path.abspath(forms.__file__) path = os.path.dirname(path) + '/' - assert fix_os_paths(path).endswith('/django/forms/') + self.assertTrue(fix_os_paths(path).endswith('/django/forms/')) def test_filepathfield_66(self): path = forms.__file__ - path = os.path.dirname(path) + '/' + path = os.path.dirname(os.path.abspath(path)) + '/' f = FilePathField(path=path) f.choices = [p for p in f.choices if p[0].endswith('.py')] f.choices.sort() @@ -787,13 +787,13 @@ class FieldsTests(TestCase): ] for exp, got in zip(expected, fix_os_paths(f.choices)): self.assertEqual(exp[1], got[1]) - assert got[0].endswith(exp[0]) + self.assertTrue(got[0].endswith(exp[0])) self.assertRaisesErrorWithMessage(ValidationError, "[u'Select a valid choice. fields.py is not one of the available choices.']", f.clean, 'fields.py') assert fix_os_paths(f.clean(path + 'fields.py')).endswith('/django/forms/fields.py') def test_filepathfield_67(self): path = forms.__file__ - path = os.path.dirname(path) + '/' + path = os.path.dirname(os.path.abspath(path)) + '/' f = FilePathField(path=path, match='^.*?\.py$') f.choices.sort() expected = [ @@ -807,10 +807,10 @@ class FieldsTests(TestCase): ] for exp, got in zip(expected, fix_os_paths(f.choices)): self.assertEqual(exp[1], got[1]) - assert got[0].endswith(exp[0]) + self.assertTrue(got[0].endswith(exp[0])) def test_filepathfield_68(self): - path = forms.__file__ + path = os.path.abspath(forms.__file__) path = os.path.dirname(path) + '/' f = FilePathField(path=path, recursive=True, match='^.*?\.py$') f.choices.sort() @@ -827,7 +827,7 @@ class FieldsTests(TestCase): ] for exp, got in zip(expected, fix_os_paths(f.choices)): self.assertEqual(exp[1], got[1]) - assert got[0].endswith(exp[0]) + self.assertTrue(got[0].endswith(exp[0])) # SplitDateTimeField ########################################################## diff --git a/tests/regressiontests/forms/forms.py b/tests/regressiontests/forms/forms.py index 58051fd133..91594139f2 100644 --- a/tests/regressiontests/forms/forms.py +++ b/tests/regressiontests/forms/forms.py @@ -522,6 +522,18 @@ tags. <input type="hidden" name="composers" value="P" /> <input type="hidden" name="composers" value="J" /> +DateTimeField rendered as_hidden() is special too + +>>> class MessageForm(Form): +... when = SplitDateTimeField() +>>> f = MessageForm({'when_0': '1992-01-01', 'when_1': '01:01'}) +>>> print f.is_valid() +True +>>> print f['when'] +<input type="text" name="when_0" value="1992-01-01" id="id_when_0" /><input type="text" name="when_1" value="01:01" id="id_when_1" /> +>>> print f['when'].as_hidden() +<input type="hidden" name="when_0" value="1992-01-01" id="id_when_0" /><input type="hidden" name="when_1" value="01:01" id="id_when_1" /> + MultipleChoiceField can also be used with the CheckboxSelectMultiple widget. >>> class SongForm(Form): ... name = CharField() @@ -705,13 +717,13 @@ Form.clean() is required to return a dictionary of all clean data. >>> print f.as_table() <tr><td colspan="2"><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></td></tr> <tr><th>Username:</th><td><input type="text" name="username" value="adrian" maxlength="10" /></td></tr> -<tr><th>Password1:</th><td><input type="password" name="password1" value="foo" /></td></tr> -<tr><th>Password2:</th><td><input type="password" name="password2" value="bar" /></td></tr> +<tr><th>Password1:</th><td><input type="password" name="password1" /></td></tr> +<tr><th>Password2:</th><td><input type="password" name="password2" /></td></tr> >>> print f.as_ul() <li><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></li> <li>Username: <input type="text" name="username" value="adrian" maxlength="10" /></li> -<li>Password1: <input type="password" name="password1" value="foo" /></li> -<li>Password2: <input type="password" name="password2" value="bar" /></li> +<li>Password1: <input type="password" name="password1" /></li> +<li>Password2: <input type="password" name="password2" /></li> >>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False) >>> f.errors {} @@ -1258,20 +1270,20 @@ to a Field class. This help text is displayed when a Form is rendered. ... password = CharField(widget=PasswordInput, help_text='Choose wisely.') >>> p = UserRegistration(auto_id=False) >>> print p.as_ul() -<li>Username: <input type="text" name="username" maxlength="10" /> e.g., user@example.com</li> -<li>Password: <input type="password" name="password" /> Choose wisely.</li> +<li>Username: <input type="text" name="username" maxlength="10" /> <span class="helptext">e.g., user@example.com</span></li> +<li>Password: <input type="password" name="password" /> <span class="helptext">Choose wisely.</span></li> >>> print p.as_p() -<p>Username: <input type="text" name="username" maxlength="10" /> e.g., user@example.com</p> -<p>Password: <input type="password" name="password" /> Choose wisely.</p> +<p>Username: <input type="text" name="username" maxlength="10" /> <span class="helptext">e.g., user@example.com</span></p> +<p>Password: <input type="password" name="password" /> <span class="helptext">Choose wisely.</span></p> >>> print p.as_table() -<tr><th>Username:</th><td><input type="text" name="username" maxlength="10" /><br />e.g., user@example.com</td></tr> -<tr><th>Password:</th><td><input type="password" name="password" /><br />Choose wisely.</td></tr> +<tr><th>Username:</th><td><input type="text" name="username" maxlength="10" /><br /><span class="helptext">e.g., user@example.com</span></td></tr> +<tr><th>Password:</th><td><input type="password" name="password" /><br /><span class="helptext">Choose wisely.</span></td></tr> The help text is displayed whether or not data is provided for the form. >>> p = UserRegistration({'username': u'foo'}, auto_id=False) >>> print p.as_ul() -<li>Username: <input type="text" name="username" value="foo" maxlength="10" /> e.g., user@example.com</li> -<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /> Choose wisely.</li> +<li>Username: <input type="text" name="username" value="foo" maxlength="10" /> <span class="helptext">e.g., user@example.com</span></li> +<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /> <span class="helptext">Choose wisely.</span></li> help_text is not displayed for hidden fields. It can be used for documentation purposes, though. @@ -1281,7 +1293,7 @@ purposes, though. ... next = CharField(widget=HiddenInput, initial='/', help_text='Redirect destination') >>> p = UserRegistration(auto_id=False) >>> print p.as_ul() -<li>Username: <input type="text" name="username" maxlength="10" /> e.g., user@example.com</li> +<li>Username: <input type="text" name="username" maxlength="10" /> <span class="helptext">e.g., user@example.com</span></li> <li>Password: <input type="password" name="password" /><input type="hidden" name="next" value="/" /></li> Help text can include arbitrary Unicode characters. @@ -1289,7 +1301,7 @@ Help text can include arbitrary Unicode characters. ... username = CharField(max_length=10, help_text='Å ÄĆŽćžšđ') >>> p = UserRegistration(auto_id=False) >>> p.as_ul() -u'<li>Username: <input type="text" name="username" maxlength="10" /> \u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111</li>' +u'<li>Username: <input type="text" name="username" maxlength="10" /> <span class="helptext">\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111</span></li>' # Subclassing forms ########################################################### @@ -1589,8 +1601,8 @@ Case 2: POST with erroneous data (a redisplayed form, with errors). <table> <tr><td colspan="2"><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></td></tr> <tr><th>Username:</th><td><ul class="errorlist"><li>Ensure this value has at most 10 characters (it has 23).</li></ul><input type="text" name="username" value="this-is-a-long-username" maxlength="10" /></td></tr> -<tr><th>Password1:</th><td><input type="password" name="password1" value="foo" /></td></tr> -<tr><th>Password2:</th><td><input type="password" name="password2" value="bar" /></td></tr> +<tr><th>Password1:</th><td><input type="password" name="password1" /></td></tr> +<tr><th>Password2:</th><td><input type="password" name="password2" /></td></tr> </table> <input type="submit" /> </form> @@ -1719,8 +1731,8 @@ the list of errors is empty). You can also use it in {% if %} statements. >>> print t.render(Context({'form': UserRegistration({'username': 'django', 'password1': 'foo', 'password2': 'bar'}, auto_id=False)})) <form action=""> <p><label>Your username: <input type="text" name="username" value="django" maxlength="10" /></label></p> -<p><label>Password: <input type="password" name="password1" value="foo" /></label></p> -<p><label>Password (again): <input type="password" name="password2" value="bar" /></label></p> +<p><label>Password: <input type="password" name="password1" /></label></p> +<p><label>Password (again): <input type="password" name="password2" /></label></p> <input type="submit" /> </form> >>> t = Template('''<form action=""> @@ -1734,8 +1746,8 @@ the list of errors is empty). You can also use it in {% if %} statements. <form action=""> <ul class="errorlist"><li>Please make sure your passwords match.</li></ul> <p><label>Your username: <input type="text" name="username" value="django" maxlength="10" /></label></p> -<p><label>Password: <input type="password" name="password1" value="foo" /></label></p> -<p><label>Password (again): <input type="password" name="password2" value="bar" /></label></p> +<p><label>Password: <input type="password" name="password1" /></label></p> +<p><label>Password (again): <input type="password" name="password2" /></label></p> <input type="submit" /> </form> diff --git a/tests/regressiontests/forms/input_formats.py b/tests/regressiontests/forms/input_formats.py new file mode 100644 index 0000000000..498c6de9fb --- /dev/null +++ b/tests/regressiontests/forms/input_formats.py @@ -0,0 +1,894 @@ +from datetime import time, date, datetime +from unittest import TestCase + +from django import forms +from django.conf import settings +from django.utils.translation import activate, deactivate + + +class LocalizedTimeTests(TestCase): + def setUp(self): + self.old_TIME_INPUT_FORMATS = settings.TIME_INPUT_FORMATS + self.old_USE_L10N = settings.USE_L10N + + settings.TIME_INPUT_FORMATS = ["%I:%M:%S %p", "%I:%M %p"] + settings.USE_L10N = True + + activate('de') + + def tearDown(self): + settings.TIME_INPUT_FORMATS = self.old_TIME_INPUT_FORMATS + settings.USE_L10N = self.old_USE_L10N + + deactivate() + + def test_timeField(self): + "TimeFields can parse dates in the default format" + f = forms.TimeField() + # Parse a time in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM') + + # Parse a time in a valid format, get a parsed result + result = f.clean('13:30:05') + self.assertEqual(result, time(13,30,5)) + + # Check that the parsed result does a round trip + text = f.widget._format_value(result) + self.assertEqual(text, '13:30:05') + + # Parse a time in a valid, but non-default format, get a parsed result + result = f.clean('13:30') + self.assertEqual(result, time(13,30,0)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "13:30:00") + + def test_localized_timeField(self): + "Localized TimeFields act as unlocalized widgets" + f = forms.TimeField(localize=True) + # Parse a time in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM') + + # Parse a time in a valid format, get a parsed result + result = f.clean('13:30:05') + self.assertEqual(result, time(13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, '13:30:05') + + # Parse a time in a valid format, get a parsed result + result = f.clean('13:30') + self.assertEqual(result, time(13,30,0)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "13:30:00") + + def test_timeField_with_inputformat(self): + "TimeFields with manually specified input formats can accept those formats" + f = forms.TimeField(input_formats=["%H.%M.%S", "%H.%M"]) + # Parse a time in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM') + self.assertRaises(forms.ValidationError, f.clean, '13:30:05') + + # Parse a time in a valid format, get a parsed result + result = f.clean('13.30.05') + self.assertEqual(result, time(13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "13:30:05") + + # Parse a time in a valid format, get a parsed result + result = f.clean('13.30') + self.assertEqual(result, time(13,30,0)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "13:30:00") + + def test_localized_timeField_with_inputformat(self): + "Localized TimeFields with manually specified input formats can accept those formats" + f = forms.TimeField(input_formats=["%H.%M.%S", "%H.%M"], localize=True) + # Parse a time in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM') + self.assertRaises(forms.ValidationError, f.clean, '13:30:05') + + # Parse a time in a valid format, get a parsed result + result = f.clean('13.30.05') + self.assertEqual(result, time(13,30,5)) + + # # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "13:30:05") + + # Parse a time in a valid format, get a parsed result + result = f.clean('13.30') + self.assertEqual(result, time(13,30,0)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "13:30:00") + + +class CustomTimeInputFormatsTests(TestCase): + def setUp(self): + self.old_TIME_INPUT_FORMATS = settings.TIME_INPUT_FORMATS + settings.TIME_INPUT_FORMATS = ["%I:%M:%S %p", "%I:%M %p"] + + def tearDown(self): + settings.TIME_INPUT_FORMATS = self.old_TIME_INPUT_FORMATS + + def test_timeField(self): + "TimeFields can parse dates in the default format" + f = forms.TimeField() + # Parse a time in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '13:30:05') + + # Parse a time in a valid format, get a parsed result + result = f.clean('1:30:05 PM') + self.assertEqual(result, time(13,30,5)) + + # Check that the parsed result does a round trip + text = f.widget._format_value(result) + self.assertEqual(text, '01:30:05 PM') + + # Parse a time in a valid, but non-default format, get a parsed result + result = f.clean('1:30 PM') + self.assertEqual(result, time(13,30,0)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "01:30:00 PM") + + def test_localized_timeField(self): + "Localized TimeFields act as unlocalized widgets" + f = forms.TimeField(localize=True) + # Parse a time in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '13:30:05') + + # Parse a time in a valid format, get a parsed result + result = f.clean('1:30:05 PM') + self.assertEqual(result, time(13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, '01:30:05 PM') + + # Parse a time in a valid format, get a parsed result + result = f.clean('01:30 PM') + self.assertEqual(result, time(13,30,0)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "01:30:00 PM") + + def test_timeField_with_inputformat(self): + "TimeFields with manually specified input formats can accept those formats" + f = forms.TimeField(input_formats=["%H.%M.%S", "%H.%M"]) + # Parse a time in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM') + self.assertRaises(forms.ValidationError, f.clean, '13:30:05') + + # Parse a time in a valid format, get a parsed result + result = f.clean('13.30.05') + self.assertEqual(result, time(13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "01:30:05 PM") + + # Parse a time in a valid format, get a parsed result + result = f.clean('13.30') + self.assertEqual(result, time(13,30,0)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "01:30:00 PM") + + def test_localized_timeField_with_inputformat(self): + "Localized TimeFields with manually specified input formats can accept those formats" + f = forms.TimeField(input_formats=["%H.%M.%S", "%H.%M"], localize=True) + # Parse a time in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM') + self.assertRaises(forms.ValidationError, f.clean, '13:30:05') + + # Parse a time in a valid format, get a parsed result + result = f.clean('13.30.05') + self.assertEqual(result, time(13,30,5)) + + # # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "01:30:05 PM") + + # Parse a time in a valid format, get a parsed result + result = f.clean('13.30') + self.assertEqual(result, time(13,30,0)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "01:30:00 PM") + + +class SimpleTimeFormatTests(TestCase): + def test_timeField(self): + "TimeFields can parse dates in the default format" + f = forms.TimeField() + # Parse a time in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM') + + # Parse a time in a valid format, get a parsed result + result = f.clean('13:30:05') + self.assertEqual(result, time(13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "13:30:05") + + # Parse a time in a valid, but non-default format, get a parsed result + result = f.clean('13:30') + self.assertEqual(result, time(13,30,0)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "13:30:00") + + def test_localized_timeField(self): + "Localized TimeFields in a non-localized environment act as unlocalized widgets" + f = forms.TimeField() + # Parse a time in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM') + + # Parse a time in a valid format, get a parsed result + result = f.clean('13:30:05') + self.assertEqual(result, time(13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "13:30:05") + + # Parse a time in a valid format, get a parsed result + result = f.clean('13:30') + self.assertEqual(result, time(13,30,0)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "13:30:00") + + def test_timeField_with_inputformat(self): + "TimeFields with manually specified input formats can accept those formats" + f = forms.TimeField(input_formats=["%I:%M:%S %p", "%I:%M %p"]) + # Parse a time in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '13:30:05') + + # Parse a time in a valid format, get a parsed result + result = f.clean('1:30:05 PM') + self.assertEqual(result, time(13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "13:30:05") + + # Parse a time in a valid format, get a parsed result + result = f.clean('1:30 PM') + self.assertEqual(result, time(13,30,0)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "13:30:00") + + def test_localized_timeField_with_inputformat(self): + "Localized TimeFields with manually specified input formats can accept those formats" + f = forms.TimeField(input_formats=["%I:%M:%S %p", "%I:%M %p"], localize=True) + # Parse a time in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '13:30:05') + + # Parse a time in a valid format, get a parsed result + result = f.clean('1:30:05 PM') + self.assertEqual(result, time(13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "13:30:05") + + # Parse a time in a valid format, get a parsed result + result = f.clean('1:30 PM') + self.assertEqual(result, time(13,30,0)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "13:30:00") + + +class LocalizedDateTests(TestCase): + def setUp(self): + self.old_DATE_INPUT_FORMATS = settings.DATE_INPUT_FORMATS + self.old_USE_L10N = settings.USE_L10N + + settings.DATE_INPUT_FORMATS = ["%d/%m/%Y", "%d-%m-%Y"] + settings.USE_L10N = True + + activate('de') + + def tearDown(self): + settings.DATE_INPUT_FORMATS = self.old_DATE_INPUT_FORMATS + settings.USE_L10N = self.old_USE_L10N + + deactivate() + + def test_dateField(self): + "DateFields can parse dates in the default format" + f = forms.DateField() + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '21/12/2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('21.12.2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip + text = f.widget._format_value(result) + self.assertEqual(text, '21.12.2010') + + # Parse a date in a valid, but non-default format, get a parsed result + result = f.clean('21.12.10') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010") + + def test_localized_dateField(self): + "Localized DateFields act as unlocalized widgets" + f = forms.DateField(localize=True) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '21/12/2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('21.12.2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, '21.12.2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('21.12.10') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010") + + def test_dateField_with_inputformat(self): + "DateFields with manually specified input formats can accept those formats" + f = forms.DateField(input_formats=["%m.%d.%Y", "%m-%d-%Y"]) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21') + self.assertRaises(forms.ValidationError, f.clean, '21/12/2010') + self.assertRaises(forms.ValidationError, f.clean, '21.12.2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('12.21.2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010") + + # Parse a date in a valid format, get a parsed result + result = f.clean('12-21-2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010") + + def test_localized_dateField_with_inputformat(self): + "Localized DateFields with manually specified input formats can accept those formats" + f = forms.DateField(input_formats=["%m.%d.%Y", "%m-%d-%Y"], localize=True) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21') + self.assertRaises(forms.ValidationError, f.clean, '21/12/2010') + self.assertRaises(forms.ValidationError, f.clean, '21.12.2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('12.21.2010') + self.assertEqual(result, date(2010,12,21)) + + # # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010") + + # Parse a date in a valid format, get a parsed result + result = f.clean('12-21-2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010") + +class CustomDateInputFormatsTests(TestCase): + def setUp(self): + self.old_DATE_INPUT_FORMATS = settings.DATE_INPUT_FORMATS + settings.DATE_INPUT_FORMATS = ["%d.%m.%Y", "%d-%m-%Y"] + + def tearDown(self): + settings.DATE_INPUT_FORMATS = self.old_DATE_INPUT_FORMATS + + def test_dateField(self): + "DateFields can parse dates in the default format" + f = forms.DateField() + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21') + + # Parse a date in a valid format, get a parsed result + result = f.clean('21.12.2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip + text = f.widget._format_value(result) + self.assertEqual(text, '21.12.2010') + + # Parse a date in a valid, but non-default format, get a parsed result + result = f.clean('21-12-2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010") + + def test_localized_dateField(self): + "Localized DateFields act as unlocalized widgets" + f = forms.DateField(localize=True) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21') + + # Parse a date in a valid format, get a parsed result + result = f.clean('21.12.2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, '21.12.2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('21-12-2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010") + + def test_dateField_with_inputformat(self): + "DateFields with manually specified input formats can accept those formats" + f = forms.DateField(input_formats=["%m.%d.%Y", "%m-%d-%Y"]) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '21.12.2010') + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21') + + # Parse a date in a valid format, get a parsed result + result = f.clean('12.21.2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010") + + # Parse a date in a valid format, get a parsed result + result = f.clean('12-21-2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010") + + def test_localized_dateField_with_inputformat(self): + "Localized DateFields with manually specified input formats can accept those formats" + f = forms.DateField(input_formats=["%m.%d.%Y", "%m-%d-%Y"], localize=True) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '21.12.2010') + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21') + + # Parse a date in a valid format, get a parsed result + result = f.clean('12.21.2010') + self.assertEqual(result, date(2010,12,21)) + + # # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010") + + # Parse a date in a valid format, get a parsed result + result = f.clean('12-21-2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010") + +class SimpleDateFormatTests(TestCase): + def test_dateField(self): + "DateFields can parse dates in the default format" + f = forms.DateField() + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '21.12.2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('2010-12-21') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21") + + # Parse a date in a valid, but non-default format, get a parsed result + result = f.clean('12/21/2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21") + + def test_localized_dateField(self): + "Localized DateFields in a non-localized environment act as unlocalized widgets" + f = forms.DateField() + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '21.12.2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('2010-12-21') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21") + + # Parse a date in a valid format, get a parsed result + result = f.clean('12/21/2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21") + + def test_dateField_with_inputformat(self): + "DateFields with manually specified input formats can accept those formats" + f = forms.DateField(input_formats=["%d.%m.%Y", "%d-%m-%Y"]) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21') + + # Parse a date in a valid format, get a parsed result + result = f.clean('21.12.2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21") + + # Parse a date in a valid format, get a parsed result + result = f.clean('21-12-2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21") + + def test_localized_dateField_with_inputformat(self): + "Localized DateFields with manually specified input formats can accept those formats" + f = forms.DateField(input_formats=["%d.%m.%Y", "%d-%m-%Y"], localize=True) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21') + + # Parse a date in a valid format, get a parsed result + result = f.clean('21.12.2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21") + + # Parse a date in a valid format, get a parsed result + result = f.clean('21-12-2010') + self.assertEqual(result, date(2010,12,21)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21") + +class LocalizedDateTimeTests(TestCase): + def setUp(self): + self.old_DATETIME_INPUT_FORMATS = settings.DATETIME_INPUT_FORMATS + self.old_USE_L10N = settings.USE_L10N + + settings.DATETIME_INPUT_FORMATS = ["%I:%M:%S %p %d/%m/%Y", "%I:%M %p %d-%m-%Y"] + settings.USE_L10N = True + + activate('de') + + def tearDown(self): + settings.DATETIME_INPUT_FORMATS = self.old_DATETIME_INPUT_FORMATS + settings.USE_L10N = self.old_USE_L10N + + deactivate() + + def test_dateTimeField(self): + "DateTimeFields can parse dates in the default format" + f = forms.DateTimeField() + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM 21/12/2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('21.12.2010 13:30:05') + self.assertEqual(result, datetime(2010,12,21,13,30,5)) + + # Check that the parsed result does a round trip + text = f.widget._format_value(result) + self.assertEqual(text, '21.12.2010 13:30:05') + + # Parse a date in a valid, but non-default format, get a parsed result + result = f.clean('21.12.2010 13:30') + self.assertEqual(result, datetime(2010,12,21,13,30)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010 13:30:00") + + def test_localized_dateTimeField(self): + "Localized DateTimeFields act as unlocalized widgets" + f = forms.DateTimeField(localize=True) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM 21/12/2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('21.12.2010 13:30:05') + self.assertEqual(result, datetime(2010,12,21,13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, '21.12.2010 13:30:05') + + # Parse a date in a valid format, get a parsed result + result = f.clean('21.12.2010 13:30') + self.assertEqual(result, datetime(2010,12,21,13,30)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010 13:30:00") + + def test_dateTimeField_with_inputformat(self): + "DateTimeFields with manually specified input formats can accept those formats" + f = forms.DateTimeField(input_formats=["%H.%M.%S %m.%d.%Y", "%H.%M %m-%d-%Y"]) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05 13:30:05') + self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM 21/12/2010') + self.assertRaises(forms.ValidationError, f.clean, '13:30:05 21.12.2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('13.30.05 12.21.2010') + self.assertEqual(result, datetime(2010,12,21,13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010 13:30:05") + + # Parse a date in a valid format, get a parsed result + result = f.clean('13.30 12-21-2010') + self.assertEqual(result, datetime(2010,12,21,13,30)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010 13:30:00") + + def test_localized_dateTimeField_with_inputformat(self): + "Localized DateTimeFields with manually specified input formats can accept those formats" + f = forms.DateTimeField(input_formats=["%H.%M.%S %m.%d.%Y", "%H.%M %m-%d-%Y"], localize=True) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05') + self.assertRaises(forms.ValidationError, f.clean, '1:30:05 PM 21/12/2010') + self.assertRaises(forms.ValidationError, f.clean, '13:30:05 21.12.2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('13.30.05 12.21.2010') + self.assertEqual(result, datetime(2010,12,21,13,30,5)) + + # # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010 13:30:05") + + # Parse a date in a valid format, get a parsed result + result = f.clean('13.30 12-21-2010') + self.assertEqual(result, datetime(2010,12,21,13,30)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "21.12.2010 13:30:00") + + +class CustomDateTimeInputFormatsTests(TestCase): + def setUp(self): + self.old_DATETIME_INPUT_FORMATS = settings.DATETIME_INPUT_FORMATS + settings.DATETIME_INPUT_FORMATS = ["%I:%M:%S %p %d/%m/%Y", "%I:%M %p %d-%m-%Y"] + + def tearDown(self): + settings.DATETIME_INPUT_FORMATS = self.old_DATETIME_INPUT_FORMATS + + def test_dateTimeField(self): + "DateTimeFields can parse dates in the default format" + f = forms.DateTimeField() + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05') + + # Parse a date in a valid format, get a parsed result + result = f.clean('1:30:05 PM 21/12/2010') + self.assertEqual(result, datetime(2010,12,21,13,30,5)) + + # Check that the parsed result does a round trip + text = f.widget._format_value(result) + self.assertEqual(text, '01:30:05 PM 21/12/2010') + + # Parse a date in a valid, but non-default format, get a parsed result + result = f.clean('1:30 PM 21-12-2010') + self.assertEqual(result, datetime(2010,12,21,13,30)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "01:30:00 PM 21/12/2010") + + def test_localized_dateTimeField(self): + "Localized DateTimeFields act as unlocalized widgets" + f = forms.DateTimeField(localize=True) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05') + + # Parse a date in a valid format, get a parsed result + result = f.clean('1:30:05 PM 21/12/2010') + self.assertEqual(result, datetime(2010,12,21,13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, '01:30:05 PM 21/12/2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('1:30 PM 21-12-2010') + self.assertEqual(result, datetime(2010,12,21,13,30)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "01:30:00 PM 21/12/2010") + + def test_dateTimeField_with_inputformat(self): + "DateTimeFields with manually specified input formats can accept those formats" + f = forms.DateTimeField(input_formats=["%m.%d.%Y %H:%M:%S", "%m-%d-%Y %H:%M"]) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '13:30:05 21.12.2010') + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05') + + # Parse a date in a valid format, get a parsed result + result = f.clean('12.21.2010 13:30:05') + self.assertEqual(result, datetime(2010,12,21,13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "01:30:05 PM 21/12/2010") + + # Parse a date in a valid format, get a parsed result + result = f.clean('12-21-2010 13:30') + self.assertEqual(result, datetime(2010,12,21,13,30)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "01:30:00 PM 21/12/2010") + + def test_localized_dateTimeField_with_inputformat(self): + "Localized DateTimeFields with manually specified input formats can accept those formats" + f = forms.DateTimeField(input_formats=["%m.%d.%Y %H:%M:%S", "%m-%d-%Y %H:%M"], localize=True) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '13:30:05 21.12.2010') + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05') + + # Parse a date in a valid format, get a parsed result + result = f.clean('12.21.2010 13:30:05') + self.assertEqual(result, datetime(2010,12,21,13,30,5)) + + # # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "01:30:05 PM 21/12/2010") + + # Parse a date in a valid format, get a parsed result + result = f.clean('12-21-2010 13:30') + self.assertEqual(result, datetime(2010,12,21,13,30)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "01:30:00 PM 21/12/2010") + +class SimpleDateTimeFormatTests(TestCase): + def test_dateTimeField(self): + "DateTimeFields can parse dates in the default format" + f = forms.DateTimeField() + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '13:30:05 21.12.2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('2010-12-21 13:30:05') + self.assertEqual(result, datetime(2010,12,21,13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21 13:30:05") + + # Parse a date in a valid, but non-default format, get a parsed result + result = f.clean('12/21/2010 13:30:05') + self.assertEqual(result, datetime(2010,12,21,13,30,5)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21 13:30:05") + + def test_localized_dateTimeField(self): + "Localized DateTimeFields in a non-localized environment act as unlocalized widgets" + f = forms.DateTimeField() + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '13:30:05 21.12.2010') + + # Parse a date in a valid format, get a parsed result + result = f.clean('2010-12-21 13:30:05') + self.assertEqual(result, datetime(2010,12,21,13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21 13:30:05") + + # Parse a date in a valid format, get a parsed result + result = f.clean('12/21/2010 13:30:05') + self.assertEqual(result, datetime(2010,12,21,13,30,5)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21 13:30:05") + + def test_dateTimeField_with_inputformat(self): + "DateTimeFields with manually specified input formats can accept those formats" + f = forms.DateTimeField(input_formats=["%I:%M:%S %p %d.%m.%Y", "%I:%M %p %d-%m-%Y"]) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05') + + # Parse a date in a valid format, get a parsed result + result = f.clean('1:30:05 PM 21.12.2010') + self.assertEqual(result, datetime(2010,12,21,13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21 13:30:05") + + # Parse a date in a valid format, get a parsed result + result = f.clean('1:30 PM 21-12-2010') + self.assertEqual(result, datetime(2010,12,21,13,30)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21 13:30:00") + + def test_localized_dateTimeField_with_inputformat(self): + "Localized DateTimeFields with manually specified input formats can accept those formats" + f = forms.DateTimeField(input_formats=["%I:%M:%S %p %d.%m.%Y", "%I:%M %p %d-%m-%Y"], localize=True) + # Parse a date in an unaccepted format; get an error + self.assertRaises(forms.ValidationError, f.clean, '2010-12-21 13:30:05') + + # Parse a date in a valid format, get a parsed result + result = f.clean('1:30:05 PM 21.12.2010') + self.assertEqual(result, datetime(2010,12,21,13,30,5)) + + # Check that the parsed result does a round trip to the same format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21 13:30:05") + + # Parse a date in a valid format, get a parsed result + result = f.clean('1:30 PM 21-12-2010') + self.assertEqual(result, datetime(2010,12,21,13,30)) + + # Check that the parsed result does a round trip to default format + text = f.widget._format_value(result) + self.assertEqual(text, "2010-12-21 13:30:00") diff --git a/tests/regressiontests/forms/localflavor/au.py b/tests/regressiontests/forms/localflavor/au.py index fd4c0d6980..cda782094a 100644 --- a/tests/regressiontests/forms/localflavor/au.py +++ b/tests/regressiontests/forms/localflavor/au.py @@ -50,7 +50,7 @@ u'' ## AUPhoneNumberField ######################################################## A field that accepts a 10 digit Australian phone number. -llows spaces and parentheses around area code. +Allows spaces and parentheses around area code. >>> from django.contrib.localflavor.au.forms import AUPhoneNumberField >>> f = AUPhoneNumberField() diff --git a/tests/regressiontests/forms/models.py b/tests/regressiontests/forms/models.py index 229c50556e..028ff9bad2 100644 --- a/tests/regressiontests/forms/models.py +++ b/tests/regressiontests/forms/models.py @@ -38,11 +38,28 @@ class ChoiceOptionModel(models.Model): Can't reuse ChoiceModel because error_message tests require that it have no instances.""" name = models.CharField(max_length=10) + class Meta: + ordering = ('name',) + + def __unicode__(self): + return u'ChoiceOption %d' % self.pk + class ChoiceFieldModel(models.Model): """Model with ForeignKey to another model, for testing ModelForm generation with ModelChoiceField.""" choice = models.ForeignKey(ChoiceOptionModel, blank=False, - default=lambda: ChoiceOptionModel.objects.all()[0]) + default=lambda: ChoiceOptionModel.objects.get(name='default')) + choice_int = models.ForeignKey(ChoiceOptionModel, blank=False, related_name='choice_int', + default=lambda: 1) + + multi_choice = models.ManyToManyField(ChoiceOptionModel, blank=False, related_name='multi_choice', + default=lambda: ChoiceOptionModel.objects.filter(name='default')) + multi_choice_int = models.ManyToManyField(ChoiceOptionModel, blank=False, related_name='multi_choice_int', + default=lambda: [1]) + +class ChoiceFieldForm(django_forms.ModelForm): + class Meta: + model = ChoiceFieldModel class FileModel(models.Model): file = models.FileField(storage=temp_storage, upload_to='tests') @@ -74,6 +91,74 @@ class TestTicket12510(TestCase): # only one query is required to pull the model from DB self.assertEqual(initial_queries+1, len(connection.queries)) +class ModelFormCallableModelDefault(TestCase): + def test_no_empty_option(self): + "If a model's ForeignKey has blank=False and a default, no empty option is created (Refs #10792)." + option = ChoiceOptionModel.objects.create(name='default') + + choices = list(ChoiceFieldForm().fields['choice'].choices) + self.assertEquals(len(choices), 1) + self.assertEquals(choices[0], (option.pk, unicode(option))) + + def test_callable_initial_value(self): + "The initial value for a callable default returning a queryset is the pk (refs #13769)" + obj1 = ChoiceOptionModel.objects.create(id=1, name='default') + obj2 = ChoiceOptionModel.objects.create(id=2, name='option 2') + obj3 = ChoiceOptionModel.objects.create(id=3, name='option 3') + self.assertEquals(ChoiceFieldForm().as_p(), """<p><label for="id_choice">Choice:</label> <select name="choice" id="id_choice"> +<option value="1" selected="selected">ChoiceOption 1</option> +<option value="2">ChoiceOption 2</option> +<option value="3">ChoiceOption 3</option> +</select><input type="hidden" name="initial-choice" value="1" id="initial-id_choice" /></p> +<p><label for="id_choice_int">Choice int:</label> <select name="choice_int" id="id_choice_int"> +<option value="1" selected="selected">ChoiceOption 1</option> +<option value="2">ChoiceOption 2</option> +<option value="3">ChoiceOption 3</option> +</select><input type="hidden" name="initial-choice_int" value="1" id="initial-id_choice_int" /></p> +<p><label for="id_multi_choice">Multi choice:</label> <select multiple="multiple" name="multi_choice" id="id_multi_choice"> +<option value="1" selected="selected">ChoiceOption 1</option> +<option value="2">ChoiceOption 2</option> +<option value="3">ChoiceOption 3</option> +</select><input type="hidden" name="initial-multi_choice" value="1" id="initial-id_multi_choice_0" /> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></p> +<p><label for="id_multi_choice_int">Multi choice int:</label> <select multiple="multiple" name="multi_choice_int" id="id_multi_choice_int"> +<option value="1" selected="selected">ChoiceOption 1</option> +<option value="2">ChoiceOption 2</option> +<option value="3">ChoiceOption 3</option> +</select><input type="hidden" name="initial-multi_choice_int" value="1" id="initial-id_multi_choice_int_0" /> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></p>""") + + def test_initial_instance_value(self): + "Initial instances for model fields may also be instances (refs #7287)" + obj1 = ChoiceOptionModel.objects.create(id=1, name='default') + obj2 = ChoiceOptionModel.objects.create(id=2, name='option 2') + obj3 = ChoiceOptionModel.objects.create(id=3, name='option 3') + self.assertEquals(ChoiceFieldForm(initial={ + 'choice': obj2, + 'choice_int': obj2, + 'multi_choice': [obj2,obj3], + 'multi_choice_int': ChoiceOptionModel.objects.exclude(name="default"), + }).as_p(), """<p><label for="id_choice">Choice:</label> <select name="choice" id="id_choice"> +<option value="1">ChoiceOption 1</option> +<option value="2" selected="selected">ChoiceOption 2</option> +<option value="3">ChoiceOption 3</option> +</select><input type="hidden" name="initial-choice" value="2" id="initial-id_choice" /></p> +<p><label for="id_choice_int">Choice int:</label> <select name="choice_int" id="id_choice_int"> +<option value="1">ChoiceOption 1</option> +<option value="2" selected="selected">ChoiceOption 2</option> +<option value="3">ChoiceOption 3</option> +</select><input type="hidden" name="initial-choice_int" value="2" id="initial-id_choice_int" /></p> +<p><label for="id_multi_choice">Multi choice:</label> <select multiple="multiple" name="multi_choice" id="id_multi_choice"> +<option value="1">ChoiceOption 1</option> +<option value="2" selected="selected">ChoiceOption 2</option> +<option value="3" selected="selected">ChoiceOption 3</option> +</select><input type="hidden" name="initial-multi_choice" value="2" id="initial-id_multi_choice_0" /> +<input type="hidden" name="initial-multi_choice" value="3" id="initial-id_multi_choice_1" /> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></p> +<p><label for="id_multi_choice_int">Multi choice int:</label> <select multiple="multiple" name="multi_choice_int" id="id_multi_choice_int"> +<option value="1">ChoiceOption 1</option> +<option value="2" selected="selected">ChoiceOption 2</option> +<option value="3" selected="selected">ChoiceOption 3</option> +</select><input type="hidden" name="initial-multi_choice_int" value="2" id="initial-id_multi_choice_int_0" /> +<input type="hidden" name="initial-multi_choice_int" value="3" id="initial-id_multi_choice_int_1" /> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></p>""") + __test__ = {'API_TESTS': """ >>> from django.forms.models import ModelForm @@ -155,18 +240,5 @@ u'class default value' datetime.date(1999, 3, 2) >>> shutil.rmtree(temp_storage_location) -In a ModelForm with a ModelChoiceField, if the model's ForeignKey has blank=False and a default, -no empty option is created (regression test for #10792). - -First we need at least one instance of ChoiceOptionModel: - ->>> ChoiceOptionModel.objects.create(name='default') -<ChoiceOptionModel: ChoiceOptionModel object> - ->>> class ChoiceFieldForm(ModelForm): -... class Meta: -... model = ChoiceFieldModel ->>> list(ChoiceFieldForm().fields['choice'].choices) -[(1, u'ChoiceOptionModel object')] """} diff --git a/tests/regressiontests/forms/tests.py b/tests/regressiontests/forms/tests.py index 8757e799a9..7a91cb701e 100644 --- a/tests/regressiontests/forms/tests.py +++ b/tests/regressiontests/forms/tests.py @@ -41,6 +41,8 @@ from fields import FieldsTests from validators import TestFieldWithValidators from widgets import WidgetTests +from input_formats import * + __test__ = { 'extra_tests': extra_tests, 'form_tests': form_tests, diff --git a/tests/regressiontests/forms/widgets.py b/tests/regressiontests/forms/widgets.py index 39d7d569a3..59b33d5210 100644 --- a/tests/regressiontests/forms/widgets.py +++ b/tests/regressiontests/forms/widgets.py @@ -62,6 +62,17 @@ u'<input type="text" class="special" name="email" />' u'<input type="password" name="email" />' >>> w.render('email', None) u'<input type="password" name="email" />' +>>> w.render('email', 'secret') +u'<input type="password" name="email" />' + +The render_value argument lets you specify whether the widget should render +its value. For security reasons, this is off by default. + +>>> w = PasswordInput(render_value=True) +>>> w.render('email', '') +u'<input type="password" name="email" />' +>>> w.render('email', None) +u'<input type="password" name="email" />' >>> w.render('email', 'test@example.com') u'<input type="password" name="email" value="test@example.com" />' >>> w.render('email', 'some "quoted" & ampersanded value') @@ -70,36 +81,20 @@ u'<input type="password" name="email" value="some "quoted" & amper u'<input type="password" name="email" value="test@example.com" class="fun" />' You can also pass 'attrs' to the constructor: ->>> w = PasswordInput(attrs={'class': 'fun'}) +>>> w = PasswordInput(attrs={'class': 'fun'}, render_value=True) >>> w.render('email', '') u'<input type="password" class="fun" name="email" />' >>> w.render('email', 'foo@example.com') u'<input type="password" class="fun" value="foo@example.com" name="email" />' 'attrs' passed to render() get precedence over those passed to the constructor: ->>> w = PasswordInput(attrs={'class': 'pretty'}) +>>> w = PasswordInput(attrs={'class': 'pretty'}, render_value=True) >>> w.render('email', '', attrs={'class': 'special'}) u'<input type="password" class="special" name="email" />' >>> w.render('email', 'Å ÄĆŽćžšđ', attrs={'class': 'fun'}) u'<input type="password" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />' -The render_value argument lets you specify whether the widget should render -its value. You may want to do this for security reasons. ->>> w = PasswordInput(render_value=True) ->>> w.render('email', 'secret') -u'<input type="password" name="email" value="secret" />' ->>> w = PasswordInput(render_value=False) ->>> w.render('email', '') -u'<input type="password" name="email" />' ->>> w.render('email', None) -u'<input type="password" name="email" />' ->>> w.render('email', 'secret') -u'<input type="password" name="email" />' ->>> w = PasswordInput(attrs={'class': 'fun'}, render_value=False) ->>> w.render('email', 'secret') -u'<input type="password" class="fun" name="email" />' - # HiddenInput Widget ############################################################ >>> w = HiddenInput() @@ -1286,7 +1281,7 @@ class SelectAndTextWidget(forms.MultiWidget): forms.TextInput ] super(SelectAndTextWidget, self).__init__(widgets) - + def _set_choices(self, choices): """ When choices are set for this widget, we want to pass those along to the Select widget @@ -1310,3 +1305,21 @@ class WidgetTests(TestCase): # w2 ought to be independent of w1, since MultiWidget ought # to make a copy of its sub-widgets when it is copied. self.assertEqual(w1.choices, [1,2,3]) + + def test_13390(self): + # See ticket #13390 + class SplitDateForm(forms.Form): + field = forms.DateTimeField(widget=forms.SplitDateTimeWidget, required=False) + + form = SplitDateForm({'field': ''}) + self.assertTrue(form.is_valid()) + form = SplitDateForm({'field': ['', '']}) + self.assertTrue(form.is_valid()) + + class SplitDateRequiredForm(forms.Form): + field = forms.DateTimeField(widget=forms.SplitDateTimeWidget, required=True) + + form = SplitDateRequiredForm({'field': ''}) + self.assertFalse(form.is_valid()) + form = SplitDateRequiredForm({'field': ['', '']}) + self.assertFalse(form.is_valid()) diff --git a/tests/regressiontests/httpwrappers/tests.py b/tests/regressiontests/httpwrappers/tests.py index 132472ca37..d3991aad86 100644 --- a/tests/regressiontests/httpwrappers/tests.py +++ b/tests/regressiontests/httpwrappers/tests.py @@ -1,476 +1,237 @@ -""" -################### -# Empty QueryDict # -################### - ->>> q = QueryDict('') - ->>> q['foo'] -Traceback (most recent call last): -... -MultiValueDictKeyError: "Key 'foo' not found in <QueryDict: {}>" - ->>> q['something'] = 'bar' -Traceback (most recent call last): -... -AttributeError: This QueryDict instance is immutable - ->>> q.get('foo', 'default') -'default' - ->>> q.getlist('foo') -[] - ->>> q.setlist('foo', ['bar', 'baz']) -Traceback (most recent call last): -... -AttributeError: This QueryDict instance is immutable - ->>> q.appendlist('foo', ['bar']) -Traceback (most recent call last): -... -AttributeError: This QueryDict instance is immutable - ->>> q.has_key('foo') -False - ->>> 'foo' in q -False - ->>> q.items() -[] - ->>> q.lists() -[] - ->>> q.keys() -[] - ->>> q.values() -[] - ->>> len(q) -0 - ->>> q.update({'foo': 'bar'}) -Traceback (most recent call last): -... -AttributeError: This QueryDict instance is immutable - ->>> q.pop('foo') -Traceback (most recent call last): -... -AttributeError: This QueryDict instance is immutable - ->>> q.popitem() -Traceback (most recent call last): -... -AttributeError: This QueryDict instance is immutable - ->>> q.clear() -Traceback (most recent call last): -... -AttributeError: This QueryDict instance is immutable - ->>> q.setdefault('foo', 'bar') -Traceback (most recent call last): -... -AttributeError: This QueryDict instance is immutable - ->>> q.urlencode() -'' - -################################### -# Mutable copy of empty QueryDict # -################################### - ->>> q = q.copy() - ->>> q['foo'] -Traceback (most recent call last): -... -MultiValueDictKeyError: "Key 'foo' not found in <QueryDict: {}>" - ->>> q['name'] = 'john' - ->>> q['name'] -u'john' - ->>> del q['name'] ->>> 'name' in q -False - ->>> q['name'] = 'john' - ->>> q.get('foo', 'default') -'default' - ->>> q.get('name', 'default') -u'john' - ->>> q.getlist('name') -[u'john'] - ->>> q.getlist('foo') -[] - ->>> q.setlist('foo', ['bar', 'baz']) - ->>> q.get('foo', 'default') -u'baz' - ->>> q.getlist('foo') -[u'bar', u'baz'] - ->>> q.appendlist('foo', 'another') - ->>> q.getlist('foo') -[u'bar', u'baz', u'another'] - ->>> q['foo'] -u'another' - ->>> q.has_key('foo') -True - ->>> 'foo' in q -True - ->>> q.items() -[(u'foo', u'another'), (u'name', u'john')] - ->>> q.lists() -[(u'foo', [u'bar', u'baz', u'another']), (u'name', [u'john'])] - ->>> q.keys() -[u'foo', u'name'] - ->>> q.values() -[u'another', u'john'] - ->>> len(q) -2 - ->>> q.update({'foo': 'hello'}) - -# Displays last value ->>> q['foo'] -u'hello' - ->>> q.get('foo', 'not available') -u'hello' - ->>> q.getlist('foo') -[u'bar', u'baz', u'another', u'hello'] - ->>> q.pop('foo') -[u'bar', u'baz', u'another', u'hello'] - ->>> q.pop('foo', 'not there') -'not there' - ->>> q.get('foo', 'not there') -'not there' - ->>> q.setdefault('foo', 'bar') -u'bar' - ->>> q['foo'] -u'bar' - ->>> q.getlist('foo') -[u'bar'] - ->>> q.urlencode() -'foo=bar&name=john' - ->>> q.clear() - ->>> len(q) -0 - -##################################### -# QueryDict with one key/value pair # -##################################### - ->>> q = QueryDict('foo=bar') - ->>> q['foo'] -u'bar' - ->>> q['bar'] -Traceback (most recent call last): -... -MultiValueDictKeyError: "Key 'bar' not found in <QueryDict: {u'foo': [u'bar']}>" - ->>> q['something'] = 'bar' -Traceback (most recent call last): -... -AttributeError: This QueryDict instance is immutable - ->>> q.get('foo', 'default') -u'bar' - ->>> q.get('bar', 'default') -'default' - ->>> q.getlist('foo') -[u'bar'] - ->>> q.getlist('bar') -[] - ->>> q.setlist('foo', ['bar', 'baz']) -Traceback (most recent call last): -... -AttributeError: This QueryDict instance is immutable - ->>> q.appendlist('foo', ['bar']) -Traceback (most recent call last): -... -AttributeError: This QueryDict instance is immutable - ->>> q.has_key('foo') -True - ->>> 'foo' in q -True - ->>> q.has_key('bar') -False - ->>> 'bar' in q -False - ->>> q.items() -[(u'foo', u'bar')] - ->>> q.lists() -[(u'foo', [u'bar'])] - ->>> q.keys() -[u'foo'] - ->>> q.values() -[u'bar'] - ->>> len(q) -1 - ->>> q.update({'foo': 'bar'}) -Traceback (most recent call last): -... -AttributeError: This QueryDict instance is immutable - ->>> q.pop('foo') -Traceback (most recent call last): -... -AttributeError: This QueryDict instance is immutable - ->>> q.popitem() -Traceback (most recent call last): -... -AttributeError: This QueryDict instance is immutable - ->>> q.clear() -Traceback (most recent call last): -... -AttributeError: This QueryDict instance is immutable - ->>> q.setdefault('foo', 'bar') -Traceback (most recent call last): -... -AttributeError: This QueryDict instance is immutable - ->>> q.urlencode() -'foo=bar' - -##################################################### -# QueryDict with two key/value pairs with same keys # -##################################################### - ->>> q = QueryDict('vote=yes&vote=no') - ->>> q['vote'] -u'no' - ->>> q['something'] = 'bar' -Traceback (most recent call last): -... -AttributeError: This QueryDict instance is immutable - ->>> q.get('vote', 'default') -u'no' - ->>> q.get('foo', 'default') -'default' - ->>> q.getlist('vote') -[u'yes', u'no'] - ->>> q.getlist('foo') -[] - ->>> q.setlist('foo', ['bar', 'baz']) -Traceback (most recent call last): -... -AttributeError: This QueryDict instance is immutable - ->>> q.appendlist('foo', ['bar']) -Traceback (most recent call last): -... -AttributeError: This QueryDict instance is immutable - ->>> q.has_key('vote') -True - ->>> 'vote' in q -True - ->>> q.has_key('foo') -False - ->>> 'foo' in q -False - ->>> q.items() -[(u'vote', u'no')] - ->>> q.lists() -[(u'vote', [u'yes', u'no'])] - ->>> q.keys() -[u'vote'] - ->>> q.values() -[u'no'] - ->>> len(q) -1 - ->>> q.update({'foo': 'bar'}) -Traceback (most recent call last): -... -AttributeError: This QueryDict instance is immutable - ->>> q.pop('foo') -Traceback (most recent call last): -... -AttributeError: This QueryDict instance is immutable - ->>> q.popitem() -Traceback (most recent call last): -... -AttributeError: This QueryDict instance is immutable - ->>> q.clear() -Traceback (most recent call last): -... -AttributeError: This QueryDict instance is immutable - ->>> q.setdefault('foo', 'bar') -Traceback (most recent call last): -... -AttributeError: This QueryDict instance is immutable - ->>> q.urlencode() -'vote=yes&vote=no' - ->>> del q['vote'] -Traceback (most recent call last): -... -AttributeError: This QueryDict instance is immutable - -# QueryDicts must be able to handle invalid input encoding (in this case, bad -# UTF-8 encoding). ->>> q = QueryDict('foo=bar&foo=\xff') - ->>> q['foo'] -u'\ufffd' - ->>> q.getlist('foo') -[u'bar', u'\ufffd'] - - -######################## -# Pickling a QueryDict # -######################## ->>> import pickle ->>> q = QueryDict('') ->>> q1 = pickle.loads(pickle.dumps(q, 2)) ->>> q == q1 -True ->>> q = QueryDict('a=b&c=d') ->>> q1 = pickle.loads(pickle.dumps(q, 2)) ->>> q == q1 -True ->>> q = QueryDict('a=b&c=d&a=1') ->>> q1 = pickle.loads(pickle.dumps(q, 2)) ->>> q == q1 -True - -###################################### -# HttpResponse with Unicode headers # -###################################### - ->>> r = HttpResponse() - -If we insert a unicode value it will be converted to an ascii -string. This makes sure we comply with the HTTP specifications. - ->>> r['value'] = u'test value' ->>> isinstance(r['value'], str) -True - -An error is raised When a unicode object with non-ascii is assigned. - ->>> r['value'] = u't\xebst value' # doctest:+ELLIPSIS -Traceback (most recent call last): -... -UnicodeEncodeError: ..., HTTP response headers must be in US-ASCII format - -The response also converts unicode keys to strings. - ->>> r[u'test'] = 'testing key' ->>> l = list(r.items()) ->>> l.sort() ->>> l[1] -('test', 'testing key') - -It will also raise errors for keys with non-ascii data. - ->>> r[u't\xebst'] = 'testing key' # doctest:+ELLIPSIS -Traceback (most recent call last): -... -UnicodeEncodeError: ..., HTTP response headers must be in US-ASCII format - -# Bug #10188: Do not allow newlines in headers (CR or LF) ->>> r['test\\rstr'] = 'test' -Traceback (most recent call last): -... -BadHeaderError: Header values can't contain newlines (got 'test\\rstr') - ->>> r['test\\nstr'] = 'test' -Traceback (most recent call last): -... -BadHeaderError: Header values can't contain newlines (got 'test\\nstr') - -# -# Regression test for #8278: QueryDict.update(QueryDict) -# ->>> x = QueryDict("a=1&a=2", mutable=True) ->>> y = QueryDict("a=3&a=4") ->>> x.update(y) ->>> x.getlist('a') -[u'1', u'2', u'3', u'4'] -""" - -from django.http import QueryDict, HttpResponse, CompatCookie -from django.test import TestCase - - -class Cookies(TestCase): - +import copy +import pickle +import unittest +from django.http import QueryDict, HttpResponse, CompatCookie, BadHeaderError + +class QueryDictTests(unittest.TestCase): + def test_missing_key(self): + q = QueryDict('') + self.assertRaises(KeyError, q.__getitem__, 'foo') + + def test_immutability(self): + q = QueryDict('') + self.assertRaises(AttributeError, q.__setitem__, 'something', 'bar') + self.assertRaises(AttributeError, q.setlist, 'foo', ['bar']) + self.assertRaises(AttributeError, q.appendlist, 'foo', ['bar']) + self.assertRaises(AttributeError, q.update, {'foo': 'bar'}) + self.assertRaises(AttributeError, q.pop, 'foo') + self.assertRaises(AttributeError, q.popitem) + self.assertRaises(AttributeError, q.clear) + + def test_immutable_get_with_default(self): + q = QueryDict('') + self.assertEqual(q.get('foo', 'default'), 'default') + + def test_immutable_basic_operations(self): + q = QueryDict('') + self.assertEqual(q.getlist('foo'), []) + self.assertEqual(q.has_key('foo'), False) + self.assertEqual('foo' in q, False) + self.assertEqual(q.items(), []) + self.assertEqual(q.lists(), []) + self.assertEqual(q.items(), []) + self.assertEqual(q.keys(), []) + self.assertEqual(q.values(), []) + self.assertEqual(len(q), 0) + self.assertEqual(q.urlencode(), '') + + def test_single_key_value(self): + """Test QueryDict with one key/value pair""" + + q = QueryDict('foo=bar') + self.assertEqual(q['foo'], 'bar') + self.assertRaises(KeyError, q.__getitem__, 'bar') + self.assertRaises(AttributeError, q.__setitem__, 'something', 'bar') + + self.assertEqual(q.get('foo', 'default'), 'bar') + self.assertEqual(q.get('bar', 'default'), 'default') + self.assertEqual(q.getlist('foo'), ['bar']) + self.assertEqual(q.getlist('bar'), []) + + self.assertRaises(AttributeError, q.setlist, 'foo', ['bar']) + self.assertRaises(AttributeError, q.appendlist, 'foo', ['bar']) + + self.failUnless(q.has_key('foo')) + self.failUnless('foo' in q) + self.failIf(q.has_key('bar')) + self.failIf('bar' in q) + + self.assertEqual(q.items(), [(u'foo', u'bar')]) + self.assertEqual(q.lists(), [(u'foo', [u'bar'])]) + self.assertEqual(q.keys(), ['foo']) + self.assertEqual(q.values(), ['bar']) + self.assertEqual(len(q), 1) + + self.assertRaises(AttributeError, q.update, {'foo': 'bar'}) + self.assertRaises(AttributeError, q.pop, 'foo') + self.assertRaises(AttributeError, q.popitem) + self.assertRaises(AttributeError, q.clear) + self.assertRaises(AttributeError, q.setdefault, 'foo', 'bar') + + self.assertEqual(q.urlencode(), 'foo=bar') + + def test_mutable_copy(self): + """A copy of a QueryDict is mutable.""" + q = QueryDict('').copy() + self.assertRaises(KeyError, q.__getitem__, "foo") + q['name'] = 'john' + self.assertEqual(q['name'], 'john') + + def test_mutable_delete(self): + q = QueryDict('').copy() + q['name'] = 'john' + del q['name'] + self.failIf('name' in q) + + def test_basic_mutable_operations(self): + q = QueryDict('').copy() + q['name'] = 'john' + self.assertEqual(q.get('foo', 'default'), 'default') + self.assertEqual(q.get('name', 'default'), 'john') + self.assertEqual(q.getlist('name'), ['john']) + self.assertEqual(q.getlist('foo'), []) + + q.setlist('foo', ['bar', 'baz']) + self.assertEqual(q.get('foo', 'default'), 'baz') + self.assertEqual(q.getlist('foo'), ['bar', 'baz']) + + q.appendlist('foo', 'another') + self.assertEqual(q.getlist('foo'), ['bar', 'baz', 'another']) + self.assertEqual(q['foo'], 'another') + self.failUnless(q.has_key('foo')) + self.failUnless('foo' in q) + + self.assertEqual(q.items(), [(u'foo', u'another'), (u'name', u'john')]) + self.assertEqual(q.lists(), [(u'foo', [u'bar', u'baz', u'another']), (u'name', [u'john'])]) + self.assertEqual(q.keys(), [u'foo', u'name']) + self.assertEqual(q.values(), [u'another', u'john']) + self.assertEqual(len(q), 2) + + q.update({'foo': 'hello'}) + self.assertEqual(q['foo'], 'hello') + self.assertEqual(q.get('foo', 'not available'), 'hello') + self.assertEqual(q.getlist('foo'), [u'bar', u'baz', u'another', u'hello']) + self.assertEqual(q.pop('foo'), [u'bar', u'baz', u'another', u'hello']) + self.assertEqual(q.pop('foo', 'not there'), 'not there') + self.assertEqual(q.get('foo', 'not there'), 'not there') + self.assertEqual(q.setdefault('foo', 'bar'), 'bar') + self.assertEqual(q['foo'], 'bar') + self.assertEqual(q.getlist('foo'), ['bar']) + self.assertEqual(q.urlencode(), 'foo=bar&name=john') + + q.clear() + self.assertEqual(len(q), 0) + + def test_multiple_keys(self): + """Test QueryDict with two key/value pairs with same keys.""" + + q = QueryDict('vote=yes&vote=no') + + self.assertEqual(q['vote'], u'no') + self.assertRaises(AttributeError, q.__setitem__, 'something', 'bar') + + self.assertEqual(q.get('vote', 'default'), u'no') + self.assertEqual(q.get('foo', 'default'), 'default') + self.assertEqual(q.getlist('vote'), [u'yes', u'no']) + self.assertEqual(q.getlist('foo'), []) + + self.assertRaises(AttributeError, q.setlist, 'foo', ['bar', 'baz']) + self.assertRaises(AttributeError, q.setlist, 'foo', ['bar', 'baz']) + self.assertRaises(AttributeError, q.appendlist, 'foo', ['bar']) + + self.assertEqual(q.has_key('vote'), True) + self.assertEqual('vote' in q, True) + self.assertEqual(q.has_key('foo'), False) + self.assertEqual('foo' in q, False) + self.assertEqual(q.items(), [(u'vote', u'no')]) + self.assertEqual(q.lists(), [(u'vote', [u'yes', u'no'])]) + self.assertEqual(q.keys(), [u'vote']) + self.assertEqual(q.values(), [u'no']) + self.assertEqual(len(q), 1) + + self.assertRaises(AttributeError, q.update, {'foo': 'bar'}) + self.assertRaises(AttributeError, q.pop, 'foo') + self.assertRaises(AttributeError, q.popitem) + self.assertRaises(AttributeError, q.clear) + self.assertRaises(AttributeError, q.setdefault, 'foo', 'bar') + self.assertRaises(AttributeError, q.__delitem__, 'vote') + + def test_invalid_input_encoding(self): + """ + QueryDicts must be able to handle invalid input encoding (in this + case, bad UTF-8 encoding). + """ + q = QueryDict('foo=bar&foo=\xff') + self.assertEqual(q['foo'], u'\ufffd') + self.assertEqual(q.getlist('foo'), [u'bar', u'\ufffd']) + + def test_pickle(self): + q = QueryDict('') + q1 = pickle.loads(pickle.dumps(q, 2)) + self.assertEqual(q == q1, True) + q = QueryDict('a=b&c=d') + q1 = pickle.loads(pickle.dumps(q, 2)) + self.assertEqual(q == q1, True) + q = QueryDict('a=b&c=d&a=1') + q1 = pickle.loads(pickle.dumps(q, 2)) + self.assertEqual(q == q1 , True) + + def test_update_from_querydict(self): + """Regression test for #8278: QueryDict.update(QueryDict)""" + x = QueryDict("a=1&a=2", mutable=True) + y = QueryDict("a=3&a=4") + x.update(y) + self.assertEqual(x.getlist('a'), [u'1', u'2', u'3', u'4']) + + def test_non_default_encoding(self): + """#13572 - QueryDict with a non-default encoding""" + q = QueryDict('sbb=one', encoding='rot_13') + self.assertEqual(q.encoding , 'rot_13' ) + self.assertEqual(q.items() , [(u'foo', u'bar')] ) + self.assertEqual(q.urlencode() , 'sbb=one' ) + q = q.copy() + self.assertEqual(q.encoding , 'rot_13' ) + self.assertEqual(q.items() , [(u'foo', u'bar')] ) + self.assertEqual(q.urlencode() , 'sbb=one' ) + self.assertEqual(copy.copy(q).encoding , 'rot_13' ) + self.assertEqual(copy.deepcopy(q).encoding , 'rot_13') + +class HttpResponseTests(unittest.TestCase): + def test_unicode_headers(self): + r = HttpResponse() + + # If we insert a unicode value it will be converted to an ascii + r['value'] = u'test value' + self.failUnless(isinstance(r['value'], str)) + + # An error is raised ~hen a unicode object with non-ascii is assigned. + self.assertRaises(UnicodeEncodeError, r.__setitem__, 'value', u't\xebst value') + + # An error is raised when a unicode object with non-ASCII format is + # passed as initial mimetype or content_type. + self.assertRaises(UnicodeEncodeError, HttpResponse, + mimetype=u't\xebst value') + + # HttpResponse headers must be convertible to ASCII. + self.assertRaises(UnicodeEncodeError, HttpResponse, + content_type=u't\xebst value') + + # The response also converts unicode keys to strings.) + r[u'test'] = 'testing key' + l = list(r.items()) + l.sort() + self.assertEqual(l[1], ('test', 'testing key')) + + # It will also raise errors for keys with non-ascii data. + self.assertRaises(UnicodeEncodeError, r.__setitem__, u't\xebst key', 'value') + + def test_newlines_in_headers(self): + # Bug #10188: Do not allow newlines in headers (CR or LF) + r = HttpResponse() + self.assertRaises(BadHeaderError, r.__setitem__, 'test\rstr', 'test') + self.assertRaises(BadHeaderError, r.__setitem__, 'test\nstr', 'test') + +class CookieTests(unittest.TestCase): def test_encode(self): """ Test that we don't output tricky characters in encoded value @@ -502,7 +263,3 @@ class Cookies(TestCase): c2 = CompatCookie() c2.load(c.output()) self.assertEqual(c['test'].value, c2['test'].value) - -if __name__ == "__main__": - import doctest - doctest.testmod() diff --git a/tests/regressiontests/i18n/models.py b/tests/regressiontests/i18n/models.py index 8b142b36bc..75cd996f83 100644 --- a/tests/regressiontests/i18n/models.py +++ b/tests/regressiontests/i18n/models.py @@ -10,9 +10,3 @@ class Company(models.Model): date_added = models.DateTimeField(default=datetime(1799,1,31,23,59,59,0)) cents_payed = models.DecimalField(max_digits=4, decimal_places=2) products_delivered = models.IntegerField() - -__test__ = {'API_TESTS': ''' ->>> tm = TestModel() ->>> tm.save() -''' -} diff --git a/tests/regressiontests/i18n/tests.py b/tests/regressiontests/i18n/tests.py index 32c991a436..7e2e9f8228 100644 --- a/tests/regressiontests/i18n/tests.py +++ b/tests/regressiontests/i18n/tests.py @@ -5,14 +5,17 @@ import os import sys import pickle -from django.template import Template, Context from django.conf import settings +from django.template import Template, Context +from django.test import TestCase from django.utils.formats import get_format, date_format, time_format, localize, localize_input from django.utils.numberformat import format as nformat -from django.test import TestCase +from django.utils.safestring import mark_safe, SafeString, SafeUnicode from django.utils.translation import ugettext, ugettext_lazy, activate, deactivate, gettext_lazy, to_locale + from forms import I18nForm, SelectDateForm, SelectDateWidget, CompanyForm +from models import Company, TestModel class TranslationTests(TestCase): @@ -59,7 +62,6 @@ class TranslationTests(TestCase): """ Translating a string requiring no auto-escaping shouldn't change the "safe" status. """ - from django.utils.safestring import mark_safe, SafeString, SafeUnicode s = mark_safe('Password') self.assertEqual(SafeString, type(s)) activate('de') @@ -170,14 +172,14 @@ class FormattingTests(TestCase): self.assertEqual(u'desembre 2009', date_format(self.d, 'YEAR_MONTH_FORMAT')) self.assertEqual(u'12/31/2009 8:50 p.m.', date_format(self.dt, 'SHORT_DATETIME_FORMAT')) self.assertEqual('No localizable', localize('No localizable')) - self.assertEqual(decimal.Decimal('66666.666'), localize(self.n)) - self.assertEqual(99999.999, localize(self.f)) - self.assertEqual(datetime.date(2009, 12, 31), localize(self.d)) - self.assertEqual(datetime.datetime(2009, 12, 31, 20, 50), localize(self.dt)) + self.assertEqual('66666.666', localize(self.n)) + self.assertEqual('99999.999', localize(self.f)) + self.assertEqual(u'des. 31, 2009', localize(self.d)) + self.assertEqual(u'des. 31, 2009, 8:50 p.m.', localize(self.dt)) self.assertEqual(u'66666.666', Template('{{ n }}').render(self.ctxt)) self.assertEqual(u'99999.999', Template('{{ f }}').render(self.ctxt)) - self.assertEqual(u'2009-12-31', Template('{{ d }}').render(self.ctxt)) - self.assertEqual(u'2009-12-31 20:50:00', Template('{{ dt }}').render(self.ctxt)) + self.assertEqual(u'des. 31, 2009', Template('{{ d }}').render(self.ctxt)) + self.assertEqual(u'des. 31, 2009, 8:50 p.m.', Template('{{ dt }}').render(self.ctxt)) self.assertEqual(u'66666.67', Template('{{ n|floatformat:2 }}').render(self.ctxt)) self.assertEqual(u'100000.0', Template('{{ f|floatformat }}').render(self.ctxt)) self.assertEqual(u'10:15 a.m.', Template('{{ t|time:"TIME_FORMAT" }}').render(self.ctxt)) @@ -620,3 +622,16 @@ class DjangoFallbackResolutionOrderI18NTests(ResolutionOrderI18NTests): def test_django_fallback(self): self.assertUgettext('Date/time', 'Datum/Zeit') + + +class TestModels(TestCase): + def test_lazy(self): + tm = TestModel() + tm.save() + + def test_safestr(self): + c = Company(cents_payed=12, products_delivered=1) + c.name = SafeUnicode(u'Iñtërnâtiônà lizætiøn1') + c.save() + c.name = SafeString(u'Iñtërnâtiônà lizætiøn1'.encode('utf-8')) + c.save() diff --git a/tests/regressiontests/localflavor/models.py b/tests/regressiontests/localflavor/models.py index f74a5051d4..e69de29bb2 100644 --- a/tests/regressiontests/localflavor/models.py +++ b/tests/regressiontests/localflavor/models.py @@ -1,8 +0,0 @@ -from django.db import models -from django.contrib.localflavor.us.models import USStateField - -class Place(models.Model): - state = USStateField(blank=True) - state_req = USStateField() - state_default = USStateField(default="CA", blank=True) - name = models.CharField(max_length=20) diff --git a/tests/regressiontests/localflavor/tests.py b/tests/regressiontests/localflavor/tests.py index 0ea3c52568..69682360d3 100644 --- a/tests/regressiontests/localflavor/tests.py +++ b/tests/regressiontests/localflavor/tests.py @@ -1,83 +1,5 @@ +import unittest from django.test import TestCase -from models import Place -from forms import PlaceForm -class USLocalflavorTests(TestCase): - def setUp(self): - self.form = PlaceForm({'state':'GA', 'state_req':'NC', 'name':'impossible'}) - - def test_get_display_methods(self): - """Test that the get_*_display() methods are added to the model instances.""" - place = self.form.save() - self.assertEqual(place.get_state_display(), 'Georgia') - self.assertEqual(place.get_state_req_display(), 'North Carolina') - - def test_required(self): - """Test that required USStateFields throw appropriate errors.""" - form = PlaceForm({'state':'GA', 'name':'Place in GA'}) - self.assertFalse(form.is_valid()) - self.assertEqual(form.errors['state_req'], [u'This field is required.']) - - def test_field_blank_option(self): - """Test that the empty option is there.""" - state_select_html = """\ -<select name="state" id="id_state"> -<option value="">---------</option> -<option value="AL">Alabama</option> -<option value="AK">Alaska</option> -<option value="AS">American Samoa</option> -<option value="AZ">Arizona</option> -<option value="AR">Arkansas</option> -<option value="CA">California</option> -<option value="CO">Colorado</option> -<option value="CT">Connecticut</option> -<option value="DE">Delaware</option> -<option value="DC">District of Columbia</option> -<option value="FL">Florida</option> -<option value="GA" selected="selected">Georgia</option> -<option value="GU">Guam</option> -<option value="HI">Hawaii</option> -<option value="ID">Idaho</option> -<option value="IL">Illinois</option> -<option value="IN">Indiana</option> -<option value="IA">Iowa</option> -<option value="KS">Kansas</option> -<option value="KY">Kentucky</option> -<option value="LA">Louisiana</option> -<option value="ME">Maine</option> -<option value="MD">Maryland</option> -<option value="MA">Massachusetts</option> -<option value="MI">Michigan</option> -<option value="MN">Minnesota</option> -<option value="MS">Mississippi</option> -<option value="MO">Missouri</option> -<option value="MT">Montana</option> -<option value="NE">Nebraska</option> -<option value="NV">Nevada</option> -<option value="NH">New Hampshire</option> -<option value="NJ">New Jersey</option> -<option value="NM">New Mexico</option> -<option value="NY">New York</option> -<option value="NC">North Carolina</option> -<option value="ND">North Dakota</option> -<option value="MP">Northern Mariana Islands</option> -<option value="OH">Ohio</option> -<option value="OK">Oklahoma</option> -<option value="OR">Oregon</option> -<option value="PA">Pennsylvania</option> -<option value="PR">Puerto Rico</option> -<option value="RI">Rhode Island</option> -<option value="SC">South Carolina</option> -<option value="SD">South Dakota</option> -<option value="TN">Tennessee</option> -<option value="TX">Texas</option> -<option value="UT">Utah</option> -<option value="VT">Vermont</option> -<option value="VI">Virgin Islands</option> -<option value="VA">Virginia</option> -<option value="WA">Washington</option> -<option value="WV">West Virginia</option> -<option value="WI">Wisconsin</option> -<option value="WY">Wyoming</option> -</select>""" - self.assertEqual(str(self.form['state']), state_select_html) +# just import your tests here +from us.tests import * diff --git a/tests/regressiontests/localflavor/us/__init__.py b/tests/regressiontests/localflavor/us/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/regressiontests/localflavor/us/__init__.py diff --git a/tests/regressiontests/localflavor/forms.py b/tests/regressiontests/localflavor/us/forms.py index 2dd1da6dd0..9b77e1078b 100644 --- a/tests/regressiontests/localflavor/forms.py +++ b/tests/regressiontests/localflavor/us/forms.py @@ -1,7 +1,7 @@ from django.forms import ModelForm -from models import Place +from models import USPlace -class PlaceForm(ModelForm): +class USPlaceForm(ModelForm): """docstring for PlaceForm""" class Meta: - model = Place + model = USPlace diff --git a/tests/regressiontests/localflavor/us/models.py b/tests/regressiontests/localflavor/us/models.py new file mode 100644 index 0000000000..a8a4cf0f50 --- /dev/null +++ b/tests/regressiontests/localflavor/us/models.py @@ -0,0 +1,13 @@ +from django.db import models +from django.contrib.localflavor.us.models import USStateField + +# When creating models you need to remember to add a app_label as +# 'localflavor', so your model can be found + +class USPlace(models.Model): + state = USStateField(blank=True) + state_req = USStateField() + state_default = USStateField(default="CA", blank=True) + name = models.CharField(max_length=20) + class Meta: + app_label = 'localflavor' diff --git a/tests/regressiontests/localflavor/us/tests.py b/tests/regressiontests/localflavor/us/tests.py new file mode 100644 index 0000000000..07fe057833 --- /dev/null +++ b/tests/regressiontests/localflavor/us/tests.py @@ -0,0 +1,82 @@ +from django.test import TestCase +from forms import USPlaceForm + +class USLocalflavorTests(TestCase): + def setUp(self): + self.form = USPlaceForm({'state':'GA', 'state_req':'NC', 'name':'impossible'}) + + def test_get_display_methods(self): + """Test that the get_*_display() methods are added to the model instances.""" + place = self.form.save() + self.assertEqual(place.get_state_display(), 'Georgia') + self.assertEqual(place.get_state_req_display(), 'North Carolina') + + def test_required(self): + """Test that required USStateFields throw appropriate errors.""" + form = USPlaceForm({'state':'GA', 'name':'Place in GA'}) + self.assertFalse(form.is_valid()) + self.assertEqual(form.errors['state_req'], [u'This field is required.']) + + def test_field_blank_option(self): + """Test that the empty option is there.""" + state_select_html = """\ +<select name="state" id="id_state"> +<option value="">---------</option> +<option value="AL">Alabama</option> +<option value="AK">Alaska</option> +<option value="AS">American Samoa</option> +<option value="AZ">Arizona</option> +<option value="AR">Arkansas</option> +<option value="CA">California</option> +<option value="CO">Colorado</option> +<option value="CT">Connecticut</option> +<option value="DE">Delaware</option> +<option value="DC">District of Columbia</option> +<option value="FL">Florida</option> +<option value="GA" selected="selected">Georgia</option> +<option value="GU">Guam</option> +<option value="HI">Hawaii</option> +<option value="ID">Idaho</option> +<option value="IL">Illinois</option> +<option value="IN">Indiana</option> +<option value="IA">Iowa</option> +<option value="KS">Kansas</option> +<option value="KY">Kentucky</option> +<option value="LA">Louisiana</option> +<option value="ME">Maine</option> +<option value="MD">Maryland</option> +<option value="MA">Massachusetts</option> +<option value="MI">Michigan</option> +<option value="MN">Minnesota</option> +<option value="MS">Mississippi</option> +<option value="MO">Missouri</option> +<option value="MT">Montana</option> +<option value="NE">Nebraska</option> +<option value="NV">Nevada</option> +<option value="NH">New Hampshire</option> +<option value="NJ">New Jersey</option> +<option value="NM">New Mexico</option> +<option value="NY">New York</option> +<option value="NC">North Carolina</option> +<option value="ND">North Dakota</option> +<option value="MP">Northern Mariana Islands</option> +<option value="OH">Ohio</option> +<option value="OK">Oklahoma</option> +<option value="OR">Oregon</option> +<option value="PA">Pennsylvania</option> +<option value="PR">Puerto Rico</option> +<option value="RI">Rhode Island</option> +<option value="SC">South Carolina</option> +<option value="SD">South Dakota</option> +<option value="TN">Tennessee</option> +<option value="TX">Texas</option> +<option value="UT">Utah</option> +<option value="VT">Vermont</option> +<option value="VI">Virgin Islands</option> +<option value="VA">Virginia</option> +<option value="WA">Washington</option> +<option value="WV">West Virginia</option> +<option value="WI">Wisconsin</option> +<option value="WY">Wyoming</option> +</select>""" + self.assertEqual(str(self.form['state']), state_select_html) diff --git a/tests/regressiontests/m2m_through_regress/models.py b/tests/regressiontests/m2m_through_regress/models.py index 56aecd6975..ec87985765 100644 --- a/tests/regressiontests/m2m_through_regress/models.py +++ b/tests/regressiontests/m2m_through_regress/models.py @@ -1,8 +1,10 @@ from datetime import datetime + from django.contrib.auth.models import User from django.core import management from django.db import models + # Forward declared intermediate model class Membership(models.Model): person = models.ForeignKey('Person') @@ -51,159 +53,3 @@ class Through(ThroughBase): class B(models.Model): b_text = models.CharField(max_length=20) a_list = models.ManyToManyField(A, through=Through) - - -__test__ = {'API_TESTS':""" -# Create some dummy data ->>> bob = Person.objects.create(name='Bob') ->>> jim = Person.objects.create(name='Jim') - ->>> rock = Group.objects.create(name='Rock') ->>> roll = Group.objects.create(name='Roll') - ->>> frank = User.objects.create_user('frank','frank@example.com','password') ->>> jane = User.objects.create_user('jane','jane@example.com','password') - -# Now test that the forward declared Membership works ->>> Membership.objects.create(person=bob, group=rock) -<Membership: Bob is a member of Rock> - ->>> Membership.objects.create(person=bob, group=roll) -<Membership: Bob is a member of Roll> - ->>> Membership.objects.create(person=jim, group=rock) -<Membership: Jim is a member of Rock> - ->>> bob.group_set.all() -[<Group: Rock>, <Group: Roll>] - ->>> roll.members.all() -[<Person: Bob>] - -# Error messages use the model name, not repr of the class name ->>> bob.group_set = [] -Traceback (most recent call last): -... -AttributeError: Cannot set values on a ManyToManyField which specifies an intermediary model. Use m2m_through_regress.Membership's Manager instead. - ->>> roll.members = [] -Traceback (most recent call last): -... -AttributeError: Cannot set values on a ManyToManyField which specifies an intermediary model. Use m2m_through_regress.Membership's Manager instead. - ->>> rock.members.create(name='Anne') -Traceback (most recent call last): -... -AttributeError: Cannot use create() on a ManyToManyField which specifies an intermediary model. Use m2m_through_regress.Membership's Manager instead. - ->>> bob.group_set.create(name='Funk') -Traceback (most recent call last): -... -AttributeError: Cannot use create() on a ManyToManyField which specifies an intermediary model. Use m2m_through_regress.Membership's Manager instead. - -# Now test that the intermediate with a relationship outside -# the current app (i.e., UserMembership) workds ->>> UserMembership.objects.create(user=frank, group=rock) -<UserMembership: frank is a user and member of Rock> - ->>> UserMembership.objects.create(user=frank, group=roll) -<UserMembership: frank is a user and member of Roll> - ->>> UserMembership.objects.create(user=jane, group=rock) -<UserMembership: jane is a user and member of Rock> - ->>> frank.group_set.all() -[<Group: Rock>, <Group: Roll>] - ->>> roll.user_members.all() -[<User: frank>] - -# Regression test for #8134 -- -# m2m-through models shouldn't be serialized as m2m fields on the model. - -# First, clean up a lot of objects we don't need. -# The serialization test only requires three objects to work - -# one for each end of the m2m, plus the through model. - ->>> User.objects.all().delete() ->>> UserMembership.objects.all().delete() ->>> frank.delete() ->>> rock.delete() ->>> jim.delete() - -# Dump the current contents of the database as a JSON fixture ->>> management.call_command('dumpdata', 'm2m_through_regress', format='json', indent=2) -[ - { - "pk": 2, - "model": "m2m_through_regress.membership", - "fields": { - "person": 1, - "price": 100, - "group": 2 - } - }, - { - "pk": 1, - "model": "m2m_through_regress.person", - "fields": { - "name": "Bob" - } - }, - { - "pk": 2, - "model": "m2m_through_regress.group", - "fields": { - "name": "Roll" - } - } -] - -# Check the XML serializer too, since it doesn't use the common implementation ->>> management.call_command('dumpdata', 'm2m_through_regress', format='xml', indent=2) -<?xml version="1.0" encoding="utf-8"?> -<django-objects version="1.0"> - <object pk="2" model="m2m_through_regress.membership"> - <field to="m2m_through_regress.person" name="person" rel="ManyToOneRel">1</field> - <field to="m2m_through_regress.group" name="group" rel="ManyToOneRel">2</field> - <field type="IntegerField" name="price">100</field> - </object> - <object pk="1" model="m2m_through_regress.person"> - <field type="CharField" name="name">Bob</field> - </object> - <object pk="2" model="m2m_through_regress.group"> - <field type="CharField" name="name">Roll</field> - </object> -</django-objects> - -## Regression test for #8046: -Check that we don't involve too many copies of the intermediate table when -doing a join. - ->>> bob = Person.objects.create(name='Bob') ->>> jim = Person.objects.create(name='Jim') ->>> rock = Group.objects.create(name='Rock') ->>> roll = Group.objects.create(name='Roll') ->>> _ = Membership.objects.create(person=bob, group=rock) ->>> _ = Membership.objects.create(person=jim, group=rock, price=50) ->>> _ = Membership.objects.create(person=bob, group=roll, price=50) ->>> rock.members.filter(membership__price=50) -[<Person: Jim>] - -## Regression test for #8254 ->>> bob.group_set.filter(membership__price=50) -[<Group: Roll>] - -## Regression test for #9804 -# Flush the database, just to make sure we can. ->>> management.call_command('flush', verbosity=0, interactive=False) - -## Regression test for #11107 -Ensure that sequences on m2m_through tables are being created for the through -model, not for a phantom auto-generated m2m table. - ->>> management.call_command('loaddata', 'm2m_through', verbosity=0) ->>> management.call_command('dumpdata', 'm2m_through_regress', format='json') -[{"pk": 1, "model": "m2m_through_regress.usermembership", "fields": {"price": 100, "group": 1, "user": 1}}, {"pk": 1, "model": "m2m_through_regress.person", "fields": {"name": "Guido"}}, {"pk": 1, "model": "m2m_through_regress.group", "fields": {"name": "Python Core Group"}}] - -"""} diff --git a/tests/regressiontests/m2m_through_regress/tests.py b/tests/regressiontests/m2m_through_regress/tests.py new file mode 100644 index 0000000000..406851acfd --- /dev/null +++ b/tests/regressiontests/m2m_through_regress/tests.py @@ -0,0 +1,128 @@ +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + +from django.core import management +from django.contrib.auth.models import User +from django.test import TestCase + +from models import Person, Group, Membership, UserMembership + + +class M2MThroughTestCase(TestCase): + def test_everything(self): + bob = Person.objects.create(name="Bob") + jim = Person.objects.create(name="Jim") + + rock = Group.objects.create(name="Rock") + roll = Group.objects.create(name="Roll") + + frank = User.objects.create_user("frank", "frank@example.com", "password") + jane = User.objects.create_user("jane", "jane@example.com", "password") + + Membership.objects.create(person=bob, group=rock) + Membership.objects.create(person=bob, group=roll) + Membership.objects.create(person=jim, group=rock) + + self.assertQuerysetEqual( + bob.group_set.all(), [ + "<Group: Rock>", + "<Group: Roll>", + ] + ) + + self.assertQuerysetEqual( + roll.members.all(), [ + "<Person: Bob>", + ] + ) + + self.assertRaises(AttributeError, setattr, bob, "group_set", []) + self.assertRaises(AttributeError, setattr, roll, "members", []) + + self.assertRaises(AttributeError, rock.members.create, name="Anne") + self.assertRaises(AttributeError, bob.group_set.create, name="Funk") + + UserMembership.objects.create(user=frank, group=rock) + UserMembership.objects.create(user=frank, group=roll) + UserMembership.objects.create(user=jane, group=rock) + + self.assertQuerysetEqual( + frank.group_set.all(), [ + "<Group: Rock>", + "<Group: Roll>", + ] + ) + + self.assertQuerysetEqual( + roll.user_members.all(), [ + "<User: frank>", + ] + ) + + def test_serialization(self): + "m2m-through models aren't serialized as m2m fields. Refs #8134" + + p = Person.objects.create(name="Bob") + g = Group.objects.create(name="Roll") + m =Membership.objects.create(person=p, group=g) + + pks = {"p_pk": p.pk, "g_pk": g.pk, "m_pk": m.pk} + + out = StringIO() + management.call_command("dumpdata", "m2m_through_regress", format="json", stdout=out) + self.assertEqual(out.getvalue().strip(), """[{"pk": %(m_pk)s, "model": "m2m_through_regress.membership", "fields": {"person": %(p_pk)s, "price": 100, "group": %(g_pk)s}}, {"pk": %(p_pk)s, "model": "m2m_through_regress.person", "fields": {"name": "Bob"}}, {"pk": %(g_pk)s, "model": "m2m_through_regress.group", "fields": {"name": "Roll"}}]""" % pks) + + out = StringIO() + management.call_command("dumpdata", "m2m_through_regress", format="xml", + indent=2, stdout=out) + self.assertEqual(out.getvalue().strip(), """ +<?xml version="1.0" encoding="utf-8"?> +<django-objects version="1.0"> + <object pk="%(m_pk)s" model="m2m_through_regress.membership"> + <field to="m2m_through_regress.person" name="person" rel="ManyToOneRel">%(p_pk)s</field> + <field to="m2m_through_regress.group" name="group" rel="ManyToOneRel">%(g_pk)s</field> + <field type="IntegerField" name="price">100</field> + </object> + <object pk="%(p_pk)s" model="m2m_through_regress.person"> + <field type="CharField" name="name">Bob</field> + </object> + <object pk="%(g_pk)s" model="m2m_through_regress.group"> + <field type="CharField" name="name">Roll</field> + </object> +</django-objects> + """.strip() % pks) + + def test_join_trimming(self): + "Check that we don't involve too many copies of the intermediate table when doing a join. Refs #8046, #8254" + bob = Person.objects.create(name="Bob") + jim = Person.objects.create(name="Jim") + + rock = Group.objects.create(name="Rock") + roll = Group.objects.create(name="Roll") + + Membership.objects.create(person=bob, group=rock) + Membership.objects.create(person=jim, group=rock, price=50) + Membership.objects.create(person=bob, group=roll, price=50) + + self.assertQuerysetEqual( + rock.members.filter(membership__price=50), [ + "<Person: Jim>", + ] + ) + + self.assertQuerysetEqual( + bob.group_set.filter(membership__price=50), [ + "<Group: Roll>", + ] + ) + +class ThroughLoadDataTestCase(TestCase): + fixtures = ["m2m_through"] + + def test_sequence_creation(self): + "Check that sequences on an m2m_through are created for the through model, not a phantom auto-generated m2m table. Refs #11107" + out = StringIO() + management.call_command("dumpdata", "m2m_through_regress", format="json", stdout=out) + self.assertEqual(out.getvalue().strip(), """[{"pk": 1, "model": "m2m_through_regress.usermembership", "fields": {"price": 100, "group": 1, "user": 1}}, {"pk": 1, "model": "m2m_through_regress.person", "fields": {"name": "Guido"}}, {"pk": 1, "model": "m2m_through_regress.group", "fields": {"name": "Python Core Group"}}]""") diff --git a/tests/regressiontests/model_forms_regress/models.py b/tests/regressiontests/model_forms_regress/models.py index 871bb6f815..4f9811a963 100644 --- a/tests/regressiontests/model_forms_regress/models.py +++ b/tests/regressiontests/model_forms_regress/models.py @@ -54,3 +54,6 @@ class Author(models.Model): class Author1(models.Model): publication = models.OneToOneField(Publication, null=False) full_name = models.CharField(max_length=255) + +class Homepage(models.Model): + url = models.URLField(verify_exists=False) diff --git a/tests/regressiontests/model_forms_regress/tests.py b/tests/regressiontests/model_forms_regress/tests.py index 5a7a83bc0e..baf769c02a 100644 --- a/tests/regressiontests/model_forms_regress/tests.py +++ b/tests/regressiontests/model_forms_regress/tests.py @@ -5,8 +5,10 @@ from django import forms from django.forms.models import modelform_factory, ModelChoiceField from django.conf import settings from django.test import TestCase +from django.core.exceptions import FieldError -from models import Person, RealPerson, Triple, FilePathModel, Article, Publication, CustomFF, Author, Author1 +from models import Person, RealPerson, Triple, FilePathModel, Article, \ + Publication, CustomFF, Author, Author1, Homepage class ModelMultipleChoiceFieldTests(TestCase): @@ -128,7 +130,7 @@ class ManyToManyCallableInitialTests(TestCase): <option value="1" selected="selected">First Book</option> <option value="2" selected="selected">Second Book</option> <option value="3">Third Book</option> -</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>""") +</select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></li>""") class CFFForm(forms.ModelForm): class Meta: @@ -212,7 +214,122 @@ class TestTicket11183(TestCase): def test_11183(self): form1 = ModelChoiceForm() field1 = form1.fields['person'] - # To allow the widget to change the queryset of field1.widget.choices correctly, + # To allow the widget to change the queryset of field1.widget.choices correctly, # without affecting other forms, the following must hold: self.assert_(field1 is not ModelChoiceForm.base_fields['person']) self.assert_(field1.widget.choices.field is field1) + +class HomepageForm(forms.ModelForm): + class Meta: + model = Homepage + +class URLFieldTests(TestCase): + def test_url_on_modelform(self): + "Check basic URL field validation on model forms" + self.assertFalse(HomepageForm({'url': 'foo'}).is_valid()) + self.assertFalse(HomepageForm({'url': 'http://'}).is_valid()) + self.assertFalse(HomepageForm({'url': 'http://example'}).is_valid()) + self.assertFalse(HomepageForm({'url': 'http://example.'}).is_valid()) + self.assertFalse(HomepageForm({'url': 'http://com.'}).is_valid()) + + self.assertTrue(HomepageForm({'url': 'http://localhost'}).is_valid()) + self.assertTrue(HomepageForm({'url': 'http://example.com'}).is_valid()) + self.assertTrue(HomepageForm({'url': 'http://www.example.com'}).is_valid()) + self.assertTrue(HomepageForm({'url': 'http://www.example.com:8000'}).is_valid()) + self.assertTrue(HomepageForm({'url': 'http://www.example.com/test'}).is_valid()) + self.assertTrue(HomepageForm({'url': 'http://www.example.com:8000/test'}).is_valid()) + self.assertTrue(HomepageForm({'url': 'http://example.com/foo/bar'}).is_valid()) + + def test_http_prefixing(self): + "If the http:// prefix is omitted on form input, the field adds it again. (Refs #13613)" + form = HomepageForm({'url': 'example.com'}) + form.is_valid() + # self.assertTrue(form.is_valid()) + # self.assertEquals(form.cleaned_data['url'], 'http://example.com/') + + form = HomepageForm({'url': 'example.com/test'}) + form.is_valid() + # self.assertTrue(form.is_valid()) + # self.assertEquals(form.cleaned_data['url'], 'http://example.com/test') + + +class FormFieldCallbackTests(TestCase): + + def test_baseform_with_widgets_in_meta(self): + """Regression for #13095: Using base forms with widgets defined in Meta should not raise errors.""" + widget = forms.Textarea() + + class BaseForm(forms.ModelForm): + class Meta: + model = Person + widgets = {'name': widget} + + Form = modelform_factory(Person, form=BaseForm) + self.assertTrue(Form.base_fields['name'].widget is widget) + + def test_custom_callback(self): + """Test that a custom formfield_callback is used if provided""" + + callback_args = [] + + def callback(db_field, **kwargs): + callback_args.append((db_field, kwargs)) + return db_field.formfield(**kwargs) + + widget = forms.Textarea() + + class BaseForm(forms.ModelForm): + class Meta: + model = Person + widgets = {'name': widget} + + _ = modelform_factory(Person, form=BaseForm, + formfield_callback=callback) + id_field, name_field = Person._meta.fields + + self.assertEqual(callback_args, + [(id_field, {}), (name_field, {'widget': widget})]) + + def test_bad_callback(self): + # A bad callback provided by user still gives an error + self.assertRaises(TypeError, modelform_factory, Person, + formfield_callback='not a function or callable') + + +class InvalidFieldAndFactory(TestCase): + """ Tests for #11905 """ + + def test_extra_field_model_form(self): + try: + class ExtraPersonForm(forms.ModelForm): + """ ModelForm with an extra field """ + + age = forms.IntegerField() + + class Meta: + model = Person + fields = ('name', 'no-field') + except FieldError, e: + # Make sure the exception contains some reference to the + # field responsible for the problem. + self.assertTrue('no-field' in e.args[0]) + else: + self.fail('Invalid "no-field" field not caught') + + def test_extra_declared_field_model_form(self): + try: + class ExtraPersonForm(forms.ModelForm): + """ ModelForm with an extra field """ + + age = forms.IntegerField() + + class Meta: + model = Person + fields = ('name', 'age') + except FieldError: + self.fail('Declarative field raised FieldError incorrectly') + + def test_extra_field_modelform_factory(self): + self.assertRaises(FieldError, modelform_factory, + Person, fields=['no-field', 'name']) + diff --git a/tests/regressiontests/model_formsets_regress/tests.py b/tests/regressiontests/model_formsets_regress/tests.py index 61bc514324..ee2a26f6c2 100644 --- a/tests/regressiontests/model_formsets_regress/tests.py +++ b/tests/regressiontests/model_formsets_regress/tests.py @@ -1,8 +1,10 @@ -from django.forms.models import modelform_factory, inlineformset_factory +from django import forms +from django.forms.models import modelform_factory, inlineformset_factory, modelformset_factory from django.test import TestCase from models import User, UserSite, Restaurant, Manager + class InlineFormsetTests(TestCase): def test_formset_over_to_field(self): "A formset over a ForeignKey with a to_field can be saved. Regression for #10243" @@ -156,3 +158,61 @@ class InlineFormsetTests(TestCase): # you can create a formset with an instance of None form = Form(instance=None) formset = FormSet(instance=None) + + +class CustomWidget(forms.CharField): + pass + + +class UserSiteForm(forms.ModelForm): + class Meta: + model = UserSite + widgets = {'data': CustomWidget} + + +class Callback(object): + + def __init__(self): + self.log = [] + + def __call__(self, db_field, **kwargs): + self.log.append((db_field, kwargs)) + return db_field.formfield(**kwargs) + + +class FormfieldCallbackTests(TestCase): + """ + Regression for #13095: Using base forms with widgets + defined in Meta should not raise errors. + """ + + def test_inlineformset_factory_default(self): + Formset = inlineformset_factory(User, UserSite, form=UserSiteForm) + form = Formset({}).forms[0] + self.assertTrue(isinstance(form['data'].field.widget, CustomWidget)) + + def test_modelformset_factory_default(self): + Formset = modelformset_factory(UserSite, form=UserSiteForm) + form = Formset({}).forms[0] + self.assertTrue(isinstance(form['data'].field.widget, CustomWidget)) + + def assertCallbackCalled(self, callback): + id_field, user_field, data_field = UserSite._meta.fields + expected_log = [ + (id_field, {}), + (user_field, {}), + (data_field, {'widget': CustomWidget}), + ] + self.assertEqual(callback.log, expected_log) + + def test_inlineformset_custom_callback(self): + callback = Callback() + inlineformset_factory(User, UserSite, form=UserSiteForm, + formfield_callback=callback) + self.assertCallbackCalled(callback) + + def test_modelformset_custom_callback(self): + callback = Callback() + modelformset_factory(UserSite, form=UserSiteForm, + formfield_callback=callback) + self.assertCallbackCalled(callback) diff --git a/tests/regressiontests/multiple_database/fixtures/pets.json b/tests/regressiontests/multiple_database/fixtures/pets.json new file mode 100644 index 0000000000..89756a3e5b --- /dev/null +++ b/tests/regressiontests/multiple_database/fixtures/pets.json @@ -0,0 +1,18 @@ +[ + { + "pk": 1, + "model": "multiple_database.pet", + "fields": { + "name": "Mr Bigglesworth", + "owner": 1 + } + }, + { + "pk": 2, + "model": "multiple_database.pet", + "fields": { + "name": "Spot", + "owner": 2 + } + } +]
\ No newline at end of file diff --git a/tests/regressiontests/multiple_database/tests.py b/tests/regressiontests/multiple_database/tests.py index 7bde8bf037..7f66ea3bcf 100644 --- a/tests/regressiontests/multiple_database/tests.py +++ b/tests/regressiontests/multiple_database/tests.py @@ -7,6 +7,7 @@ from django.conf import settings from django.contrib.auth.models import User from django.core import management from django.db import connections, router, DEFAULT_DB_ALIAS +from django.db.models import signals from django.db.utils import ConnectionRouter from django.test import TestCase @@ -883,7 +884,13 @@ class QueryTestCase(TestCase): self.assertRaises(ValueError, str, qs.query) # Evaluating the query shouldn't work, either - self.assertRaises(ValueError, list, qs) + try: + for obj in qs: + pass + self.fail('Iterating over query should raise ValueError') + except ValueError: + pass + class TestRouter(object): # A test router. The behaviour is vaguely master/slave, but the @@ -1491,19 +1498,10 @@ class AuthTestCase(TestCase): self.old_routers = router.routers router.routers = [AuthRouter()] - # Redirect stdout to a buffer so we can test - # the output of a management command - self.old_stdout = sys.stdout - self.stdout = StringIO() - sys.stdout = self.stdout - def tearDown(self): # Restore the 'other' database as an independent database router.routers = self.old_routers - # Restore stdout - sys.stdout = self.old_stdout - def test_auth_manager(self): "The methods on the auth manager obey database hints" # Create one user using default allocation policy @@ -1539,14 +1537,16 @@ class AuthTestCase(TestCase): # Check that dumping the default database doesn't try to include auth # because allow_syncdb prohibits auth on default - self.stdout.flush() - management.call_command('dumpdata', 'auth', format='json', database='default') - self.assertEquals(self.stdout.getvalue(), '[]\n') + new_io = StringIO() + management.call_command('dumpdata', 'auth', format='json', database='default', stdout=new_io) + command_output = new_io.getvalue().strip() + self.assertEqual(command_output, '[]') # Check that dumping the other database does include auth - self.stdout.flush() - management.call_command('dumpdata', 'auth', format='json', database='other') - self.assertTrue('alice@example.com' in self.stdout.getvalue()) + new_io = StringIO() + management.call_command('dumpdata', 'auth', format='json', database='other', stdout=new_io) + command_output = new_io.getvalue().strip() + self.assertTrue('"email": "alice@example.com",' in command_output) class UserProfileTestCase(TestCase): def setUp(self): @@ -1570,11 +1570,31 @@ class UserProfileTestCase(TestCase): self.assertEquals(alice.get_profile().flavor, 'chocolate') self.assertEquals(bob.get_profile().flavor, 'crunchy frog') +class AntiPetRouter(object): + # A router that only expresses an opinion on syncdb, + # passing pets to the 'other' database + + def allow_syncdb(self, db, model): + "Make sure the auth app only appears on the 'other' db" + if db == 'other': + return model._meta.object_name == 'Pet' + else: + return model._meta.object_name != 'Pet' + return None class FixtureTestCase(TestCase): multi_db = True fixtures = ['multidb-common', 'multidb'] + def setUp(self): + # Install the anti-pet router + self.old_routers = router.routers + router.routers = [AntiPetRouter()] + + def tearDown(self): + # Restore the 'other' database as an independent database + router.routers = self.old_routers + def test_fixture_loading(self): "Multi-db fixtures are loaded correctly" # Check that "Pro Django" exists on the default database, but not on other database @@ -1612,6 +1632,14 @@ class FixtureTestCase(TestCase): except Book.DoesNotExist: self.fail('"The Definitive Guide to Django" should exist on both databases') + def test_pseudo_empty_fixtures(self): + "A fixture can contain entries, but lead to nothing in the database; this shouldn't raise an error (ref #14068)" + new_io = StringIO() + management.call_command('loaddata', 'pets', stdout=new_io, stderr=new_io) + command_output = new_io.getvalue().strip() + # No objects will actually be loaded + self.assertEqual(command_output, "Installed 0 object(s) (of 2) from 1 fixture(s)") + class PickleQuerySetTestCase(TestCase): multi_db = True @@ -1620,3 +1648,114 @@ class PickleQuerySetTestCase(TestCase): Book.objects.using(db).create(title='Dive into Python', published=datetime.date(2009, 5, 4)) qs = Book.objects.all() self.assertEqual(qs.db, pickle.loads(pickle.dumps(qs)).db) + + +class DatabaseReceiver(object): + """ + Used in the tests for the database argument in signals (#13552) + """ + def __call__(self, signal, sender, **kwargs): + self._database = kwargs['using'] + +class WriteToOtherRouter(object): + """ + A router that sends all writes to the other database. + """ + def db_for_write(self, model, **hints): + return "other" + +class SignalTests(TestCase): + multi_db = True + + def setUp(self): + self.old_routers = router.routers + + def tearDown(self): + router.routser = self.old_routers + + def _write_to_other(self): + "Sends all writes to 'other'." + router.routers = [WriteToOtherRouter()] + + def _write_to_default(self): + "Sends all writes to the default DB" + router.routers = self.old_routers + + def test_database_arg_save_and_delete(self): + """ + Tests that the pre/post_save signal contains the correct database. + (#13552) + """ + # Make some signal receivers + pre_save_receiver = DatabaseReceiver() + post_save_receiver = DatabaseReceiver() + pre_delete_receiver = DatabaseReceiver() + post_delete_receiver = DatabaseReceiver() + # Make model and connect receivers + signals.pre_save.connect(sender=Person, receiver=pre_save_receiver) + signals.post_save.connect(sender=Person, receiver=post_save_receiver) + signals.pre_delete.connect(sender=Person, receiver=pre_delete_receiver) + signals.post_delete.connect(sender=Person, receiver=post_delete_receiver) + p = Person.objects.create(name='Darth Vader') + # Save and test receivers got calls + p.save() + self.assertEqual(pre_save_receiver._database, DEFAULT_DB_ALIAS) + self.assertEqual(post_save_receiver._database, DEFAULT_DB_ALIAS) + # Delete, and test + p.delete() + self.assertEqual(pre_delete_receiver._database, DEFAULT_DB_ALIAS) + self.assertEqual(post_delete_receiver._database, DEFAULT_DB_ALIAS) + # Save again to a different database + p.save(using="other") + self.assertEqual(pre_save_receiver._database, "other") + self.assertEqual(post_save_receiver._database, "other") + # Delete, and test + p.delete(using="other") + self.assertEqual(pre_delete_receiver._database, "other") + self.assertEqual(post_delete_receiver._database, "other") + + def test_database_arg_m2m(self): + """ + Test that the m2m_changed signal has a correct database arg (#13552) + """ + # Make a receiver + receiver = DatabaseReceiver() + # Connect it, and make the models + signals.m2m_changed.connect(receiver=receiver) + + b = Book.objects.create(title="Pro Django", + published=datetime.date(2008, 12, 16)) + + p = Person.objects.create(name="Marty Alchin") + + # Test addition + b.authors.add(p) + self.assertEqual(receiver._database, DEFAULT_DB_ALIAS) + self._write_to_other() + b.authors.add(p) + self._write_to_default() + self.assertEqual(receiver._database, "other") + + # Test removal + b.authors.remove(p) + self.assertEqual(receiver._database, DEFAULT_DB_ALIAS) + self._write_to_other() + b.authors.remove(p) + self._write_to_default() + self.assertEqual(receiver._database, "other") + + # Test addition in reverse + p.book_set.add(b) + self.assertEqual(receiver._database, DEFAULT_DB_ALIAS) + self._write_to_other() + p.book_set.add(b) + self._write_to_default() + self.assertEqual(receiver._database, "other") + + # Test clearing + b.authors.clear() + self.assertEqual(receiver._database, DEFAULT_DB_ALIAS) + self._write_to_other() + b.authors.clear() + self._write_to_default() + self.assertEqual(receiver._database, "other") diff --git a/tests/regressiontests/requests/tests.py b/tests/regressiontests/requests/tests.py index 1615a73406..22bc88c172 100644 --- a/tests/regressiontests/requests/tests.py +++ b/tests/regressiontests/requests/tests.py @@ -1,5 +1,5 @@ """ ->>> from django.http import HttpRequest +>>> from django.http import HttpRequest, HttpResponse >>> print repr(HttpRequest()) <HttpRequest GET:{}, @@ -44,4 +44,27 @@ https://www.example.com/asdf >>> request.path = '' >>> print request.build_absolute_uri(location="/path/with:colons") http://www.example.com/path/with:colons + + +# Test cookie datetime expiration logic +>>> from datetime import datetime, timedelta +>>> delta = timedelta(seconds=10) +>>> response = HttpResponse() +>>> response.set_cookie('datetime', expires=datetime.utcnow()+delta) +>>> datetime_cookie = response.cookies['datetime'] +>>> datetime_cookie['max-age'] +10 +>>> response.set_cookie('datetime', expires=datetime(2028, 1, 1, 4, 5, 6)) +>>> response.cookies['datetime']['expires'] +'Sat, 01-Jan-2028 04:05:06 GMT' + +# Test automatically setting cookie expires if only max_age is provided +>>> response.set_cookie('max_age', max_age=10) +>>> max_age_cookie = response.cookies['max_age'] +>>> max_age_cookie['max-age'] +10 +>>> from django.utils.http import cookie_date +>>> import time +>>> max_age_cookie['expires'] == cookie_date(time.time()+10) +True """ diff --git a/tests/regressiontests/serializers_regress/tests.py b/tests/regressiontests/serializers_regress/tests.py index 84e90ff7e1..be920c6920 100644 --- a/tests/regressiontests/serializers_regress/tests.py +++ b/tests/regressiontests/serializers_regress/tests.py @@ -10,14 +10,16 @@ forward, backwards and self references. import datetime import decimal -import unittest -from cStringIO import StringIO +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO -from django.utils.functional import curry -from django.core import serializers -from django.db import transaction, DEFAULT_DB_ALIAS -from django.core import management from django.conf import settings +from django.core import serializers, management +from django.db import transaction, DEFAULT_DB_ALIAS +from django.test import TestCase +from django.utils.functional import curry from models import * @@ -59,10 +61,10 @@ def im2m_create(pk, klass, data): def im_create(pk, klass, data): instance = klass(id=pk) - setattr(instance, 'right_id', data['right']) - setattr(instance, 'left_id', data['left']) + instance.right_id = data['right'] + instance.left_id = data['left'] if 'extra' in data: - setattr(instance, 'extra', data['extra']) + instance.extra = data['extra'] models.Model.save_base(instance, raw=True) return [instance] @@ -96,7 +98,9 @@ def inherited_create(pk, klass, data): def data_compare(testcase, pk, klass, data): instance = klass.objects.get(id=pk) testcase.assertEqual(data, instance.data, - "Objects with PK=%d not equal; expected '%s' (%s), got '%s' (%s)" % (pk,data, type(data), instance.data, type(instance.data))) + "Objects with PK=%d not equal; expected '%s' (%s), got '%s' (%s)" % ( + pk, data, type(data), instance.data, type(instance.data)) + ) def generic_compare(testcase, pk, klass, data): instance = klass.objects.get(id=pk) @@ -348,28 +352,16 @@ if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.mysql': # Dynamically create serializer tests to ensure that all # registered serializers are automatically tested. -class SerializerTests(unittest.TestCase): +class SerializerTests(TestCase): pass def serializerTest(format, self): - # Clear the database first - management.call_command('flush', verbosity=0, interactive=False) # Create all the objects defined in the test data objects = [] instance_count = {} - transaction.enter_transaction_management() - try: - transaction.managed(True) - for (func, pk, klass, datum) in test_data: - objects.extend(func[0](pk, klass, datum)) - instance_count[klass] = 0 - transaction.commit() - except: - transaction.rollback() - transaction.leave_transaction_management() - raise - transaction.leave_transaction_management() + for (func, pk, klass, datum) in test_data: + objects.extend(func[0](pk, klass, datum)) # Get a count of the number of objects created for each class for klass in instance_count: @@ -381,19 +373,8 @@ def serializerTest(format, self): # Serialize the test database serialized_data = serializers.serialize(format, objects, indent=2) - # Flush the database and recreate from the serialized data - management.call_command('flush', verbosity=0, interactive=False) - transaction.enter_transaction_management() - try: - transaction.managed(True) - for obj in serializers.deserialize(format, serialized_data): - obj.save() - transaction.commit() - except: - transaction.rollback() - transaction.leave_transaction_management() - raise - transaction.leave_transaction_management() + for obj in serializers.deserialize(format, serialized_data): + obj.save() # Assert that the deserialized data is the same # as the original source @@ -406,10 +387,7 @@ def serializerTest(format, self): self.assertEquals(count, klass.objects.count()) def fieldsTest(format, self): - # Clear the database first - management.call_command('flush', verbosity=0, interactive=False) - - obj = ComplexModel(field1='first',field2='second',field3='third') + obj = ComplexModel(field1='first', field2='second', field3='third') obj.save_base(raw=True) # Serialize then deserialize the test database @@ -422,9 +400,6 @@ def fieldsTest(format, self): self.assertEqual(result.object.field3, 'third') def streamTest(format, self): - # Clear the database first - management.call_command('flush', verbosity=0, interactive=False) - obj = ComplexModel(field1='first',field2='second',field3='third') obj.save_base(raw=True) @@ -440,7 +415,7 @@ def streamTest(format, self): stream.close() for format in serializers.get_serializer_formats(): - setattr(SerializerTests, 'test_'+format+'_serializer', curry(serializerTest, format)) - setattr(SerializerTests, 'test_'+format+'_serializer_fields', curry(fieldsTest, format)) + setattr(SerializerTests, 'test_' + format + '_serializer', curry(serializerTest, format)) + setattr(SerializerTests, 'test_' + format + '_serializer_fields', curry(fieldsTest, format)) if format != 'python': - setattr(SerializerTests, 'test_'+format+'_serializer_stream', curry(streamTest, format)) + setattr(SerializerTests, 'test_' + format + '_serializer_stream', curry(streamTest, format)) diff --git a/tests/regressiontests/templates/filters.py b/tests/regressiontests/templates/filters.py index 3d6284e881..af34c58a9f 100644 --- a/tests/regressiontests/templates/filters.py +++ b/tests/regressiontests/templates/filters.py @@ -328,7 +328,12 @@ def get_filter_tests(): 'join03': (r'{{ a|join:" & " }}', {'a': ['alpha', 'beta & me']}, 'alpha & beta & me'), 'join04': (r'{% autoescape off %}{{ a|join:" & " }}{% endautoescape %}', {'a': ['alpha', 'beta & me']}, 'alpha & beta & me'), - + # Test that joining with unsafe joiners don't result in unsafe strings (#11377) + 'join05': (r'{{ a|join:var }}', {'a': ['alpha', 'beta & me'], 'var': ' & '}, 'alpha & beta & me'), + 'join06': (r'{{ a|join:var }}', {'a': ['alpha', 'beta & me'], 'var': mark_safe(' & ')}, 'alpha & beta & me'), + 'join07': (r'{{ a|join:var|lower }}', {'a': ['Alpha', 'Beta & me'], 'var': ' & ' }, 'alpha & beta & me'), + 'join08': (r'{{ a|join:var|lower }}', {'a': ['Alpha', 'Beta & me'], 'var': mark_safe(' & ')}, 'alpha & beta & me'), + 'date01': (r'{{ d|date:"m" }}', {'d': datetime(2008, 1, 1)}, '01'), 'date02': (r'{{ d|date }}', {'d': datetime(2008, 1, 1)}, 'Jan. 1, 2008'), #Ticket 9520: Make sure |date doesn't blow up on non-dates @@ -341,5 +346,5 @@ def get_filter_tests(): 'add04': (r'{{ i|add:"16" }}', {'i': 'not_an_int'}, 'not_an_int16'), 'add05': (r'{{ l1|add:l2 }}', {'l1': [1, 2], 'l2': [3, 4]}, '[1, 2, 3, 4]'), 'add06': (r'{{ t1|add:t2 }}', {'t1': (3, 4), 't2': (1, 2)}, '(3, 4, 1, 2)'), - 'add07': (r'{{ d|add:t }}', {'d': date(2000, 1, 1), 't': timedelta(10)}, '2000-01-11'), + 'add07': (r'{{ d|add:t }}', {'d': date(2000, 1, 1), 't': timedelta(10)}, 'Jan. 11, 2000'), } diff --git a/tests/regressiontests/templates/loaders.py b/tests/regressiontests/templates/loaders.py index 64a0dc6505..caa3faa6bc 100644 --- a/tests/regressiontests/templates/loaders.py +++ b/tests/regressiontests/templates/loaders.py @@ -15,9 +15,11 @@ import pkg_resources import imp import StringIO import os.path +import warnings from django.template import TemplateDoesNotExist, Context from django.template.loaders.eggs import load_template_source as lts_egg +from django.template.loaders.eggs import Loader as EggLoader from django.template import loader # Mock classes and objects for pkg_resources functions. @@ -53,7 +55,33 @@ def create_egg(name, resources): egg._resources = resources sys.modules[name] = egg -class EggLoader(unittest.TestCase): +class DeprecatedEggLoaderTest(unittest.TestCase): + "Test the deprecated load_template_source interface to the egg loader" + def setUp(self): + pkg_resources._provider_factories[MockLoader] = MockProvider + + self.empty_egg = create_egg("egg_empty", {}) + self.egg_1 = create_egg("egg_1", { + os.path.normcase('templates/y.html') : StringIO.StringIO("y"), + os.path.normcase('templates/x.txt') : StringIO.StringIO("x"), + }) + self._old_installed_apps = settings.INSTALLED_APPS + settings.INSTALLED_APPS = [] + warnings.simplefilter("ignore", PendingDeprecationWarning) + + def tearDown(self): + settings.INSTALLED_APPS = self._old_installed_apps + warnings.resetwarnings() + + def test_existing(self): + "A template can be loaded from an egg" + settings.INSTALLED_APPS = ['egg_1'] + contents, template_name = lts_egg("y.html") + self.assertEqual(contents, "y") + self.assertEqual(template_name, "egg:egg_1:templates/y.html") + + +class EggLoaderTest(unittest.TestCase): def setUp(self): pkg_resources._provider_factories[MockLoader] = MockProvider @@ -71,24 +99,28 @@ class EggLoader(unittest.TestCase): def test_empty(self): "Loading any template on an empty egg should fail" settings.INSTALLED_APPS = ['egg_empty'] - self.assertRaises(TemplateDoesNotExist, lts_egg, "not-existing.html") + egg_loader = EggLoader() + self.assertRaises(TemplateDoesNotExist, egg_loader.load_template_source, "not-existing.html") def test_non_existing(self): "Template loading fails if the template is not in the egg" settings.INSTALLED_APPS = ['egg_1'] - self.assertRaises(TemplateDoesNotExist, lts_egg, "not-existing.html") + egg_loader = EggLoader() + self.assertRaises(TemplateDoesNotExist, egg_loader.load_template_source, "not-existing.html") def test_existing(self): "A template can be loaded from an egg" settings.INSTALLED_APPS = ['egg_1'] - contents, template_name = lts_egg("y.html") + egg_loader = EggLoader() + contents, template_name = egg_loader.load_template_source("y.html") self.assertEqual(contents, "y") self.assertEqual(template_name, "egg:egg_1:templates/y.html") def test_not_installed(self): "Loading an existent template from an egg not included in INSTALLED_APPS should fail" settings.INSTALLED_APPS = [] - self.assertRaises(TemplateDoesNotExist, lts_egg, "y.html") + egg_loader = EggLoader() + self.assertRaises(TemplateDoesNotExist, egg_loader.load_template_source, "y.html") class CachedLoader(unittest.TestCase): def setUp(self): diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py index 5902e8d5e7..bbbcae30eb 100644 --- a/tests/regressiontests/templates/tests.py +++ b/tests/regressiontests/templates/tests.py @@ -506,6 +506,17 @@ class Templates(unittest.TestCase): 'basic-syntax28': ("{{ a.b }}", {'a': SilentGetItemClass()}, ('', 'INVALID')), 'basic-syntax29': ("{{ a.b }}", {'a': SilentAttrClass()}, ('', 'INVALID')), + # Something that starts like a number but has an extra lookup works as a lookup. + 'basic-syntax30': ("{{ 1.2.3 }}", {"1": {"2": {"3": "d"}}}, "d"), + 'basic-syntax31': ("{{ 1.2.3 }}", {"1": {"2": ("a", "b", "c", "d")}}, "d"), + 'basic-syntax32': ("{{ 1.2.3 }}", {"1": (("x", "x", "x", "x"), ("y", "y", "y", "y"), ("a", "b", "c", "d"))}, "d"), + 'basic-syntax33': ("{{ 1.2.3 }}", {"1": ("xxxx", "yyyy", "abcd")}, "d"), + 'basic-syntax34': ("{{ 1.2.3 }}", {"1": ({"x": "x"}, {"y": "y"}, {"z": "z", "3": "d"})}, "d"), + + # Numbers are numbers even if their digits are in the context. + 'basic-syntax35': ("{{ 1 }}", {"1": "abc"}, "1"), + 'basic-syntax36': ("{{ 1.2 }}", {"1": "abc"}, "1.2"), + # List-index syntax allows a template to access a certain item of a subscriptable object. 'list-index01': ("{{ var.1 }}", {"var": ["first item", "second item"]}, "second item"), @@ -592,7 +603,7 @@ class Templates(unittest.TestCase): #filters should accept empty string constants 'filter-syntax20': ('{{ ""|default_if_none:"was none" }}', {}, ""), - + ### COMMENT SYNTAX ######################################################## 'comment-syntax01': ("{# this is hidden #}hello", {}, "hello"), 'comment-syntax02': ("{# this is hidden #}hello{# foo #}", {}, "hello"), @@ -690,6 +701,7 @@ class Templates(unittest.TestCase): 'for-tag-unpack11': ("{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, ("one:1,/two:2,/", "one:1,INVALID/two:2,INVALID/")), 'for-tag-unpack12': ("{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}", {"items": (('one', 1, 'carrot'), ('two', 2))}, ("one:1,carrot/two:2,/", "one:1,carrot/two:2,INVALID/")), 'for-tag-unpack13': ("{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}", {"items": (('one', 1, 'carrot'), ('two', 2, 'cheese'))}, ("one:1,carrot/two:2,cheese/", "one:1,carrot/two:2,cheese/")), + 'for-tag-unpack14': ("{% for x,y in items %}{{ x }}:{{ y }}/{% endfor %}", {"items": (1, 2)}, (":/:/", "INVALID:INVALID/INVALID:INVALID/")), 'for-tag-empty01': ("{% for val in values %}{{ val }}{% empty %}empty text{% endfor %}", {"values": [1, 2, 3]}, "123"), 'for-tag-empty02': ("{% for val in values %}{{ val }}{% empty %}values array empty{% endfor %}", {"values": []}, "values array empty"), 'for-tag-empty03': ("{% for val in values %}{{ val }}{% empty %}values array not found{% endfor %}", {}, "values array not found"), @@ -1285,7 +1297,8 @@ class Templates(unittest.TestCase): # Regression test for #11270. 'cache17': ('{% load cache %}{% cache 10 long_cache_key poem %}Some Content{% endcache %}', {'poem': 'Oh freddled gruntbuggly/Thy micturations are to me/As plurdled gabbleblotchits/On a lurgid bee/That mordiously hath bitled out/Its earted jurtles/Into a rancid festering/Or else I shall rend thee in the gobberwarts with my blurglecruncheon/See if I dont.'}, 'Some Content'), - + + ### AUTOESCAPE TAG ############################################## 'autoescape-tag01': ("{% autoescape off %}hello{% endautoescape %}", {}, "hello"), 'autoescape-tag02': ("{% autoescape off %}{{ first }}{% endautoescape %}", {"first": "<b>hello</b>"}, "<b>hello</b>"), @@ -1314,6 +1327,23 @@ class Templates(unittest.TestCase): # implementation details (fortunately, the (no)autoescape block # tags can be used in those cases) 'autoescape-filtertag01': ("{{ first }}{% filter safe %}{{ first }} x<y{% endfilter %}", {"first": "<a>"}, template.TemplateSyntaxError), + + # ifqeual compares unescaped vales. + 'autoescape-ifequal01': ('{% ifequal var "this & that" %}yes{% endifequal %}', { "var": "this & that" }, "yes" ), + + # Arguments to filters are 'safe' and manipulate their input unescaped. + 'autoescape-filters01': ('{{ var|cut:"&" }}', { "var": "this & that" }, "this that" ), + 'autoescape-filters02': ('{{ var|join:" & \" }}', { "var": ("Tom", "Dick", "Harry") }, "Tom & Dick & Harry" ), + + # Literal strings are safe. + 'autoescape-literals01': ('{{ "this & that" }}',{}, "this & that" ), + + # Iterating over strings outputs safe characters. + 'autoescape-stringiterations01': ('{% for l in var %}{{ l }},{% endfor %}', {'var': 'K&R'}, "K,&,R," ), + + # Escape requirement survives lookup. + 'autoescape-lookup01': ('{{ var.key }}', { "var": {"key": "this & that" }}, "this & that" ), + } diff --git a/tests/regressiontests/test_client_regress/models.py b/tests/regressiontests/test_client_regress/models.py index 6da7ae4445..22b59e54a9 100644 --- a/tests/regressiontests/test_client_regress/models.py +++ b/tests/regressiontests/test_client_regress/models.py @@ -11,6 +11,7 @@ from django.core.urlresolvers import reverse from django.core.exceptions import SuspiciousOperation from django.template import TemplateDoesNotExist, TemplateSyntaxError, Context from django.template import loader +from django.test.client import encode_file class AssertContainsTests(TestCase): def setUp(self): @@ -34,20 +35,20 @@ class AssertContainsTests(TestCase): try: self.assertContains(response, 'text', status_code=999) except AssertionError, e: - self.assertEquals(str(e), "Couldn't retrieve page: Response code was 200 (expected 999)") + self.assertEquals(str(e), "Couldn't retrieve content: Response code was 200 (expected 999)") try: self.assertContains(response, 'text', status_code=999, msg_prefix='abc') except AssertionError, e: - self.assertEquals(str(e), "abc: Couldn't retrieve page: Response code was 200 (expected 999)") + self.assertEquals(str(e), "abc: Couldn't retrieve content: Response code was 200 (expected 999)") try: self.assertNotContains(response, 'text', status_code=999) except AssertionError, e: - self.assertEquals(str(e), "Couldn't retrieve page: Response code was 200 (expected 999)") + self.assertEquals(str(e), "Couldn't retrieve content: Response code was 200 (expected 999)") try: self.assertNotContains(response, 'text', status_code=999, msg_prefix='abc') except AssertionError, e: - self.assertEquals(str(e), "abc: Couldn't retrieve page: Response code was 200 (expected 999)") + self.assertEquals(str(e), "abc: Couldn't retrieve content: Response code was 200 (expected 999)") try: self.assertNotContains(response, 'once') @@ -619,6 +620,7 @@ class ContextTests(TestCase): "Context variables can be retrieved from a single context" response = self.client.get("/test_client_regress/request_data/", data={'foo':'whiz'}) self.assertEqual(response.context.__class__, Context) + self.assertTrue('get-foo' in response.context) self.assertEqual(response.context['get-foo'], 'whiz') self.assertEqual(response.context['request-foo'], 'whiz') self.assertEqual(response.context['data'], 'sausage') @@ -634,6 +636,7 @@ class ContextTests(TestCase): response = self.client.get("/test_client_regress/request_data_extended/", data={'foo':'whiz'}) self.assertEqual(response.context.__class__, ContextList) self.assertEqual(len(response.context), 2) + self.assertTrue('get-foo' in response.context) self.assertEqual(response.context['get-foo'], 'whiz') self.assertEqual(response.context['request-foo'], 'whiz') self.assertEqual(response.context['data'], 'bacon') @@ -821,3 +824,40 @@ class UnicodePayloadTests(TestCase): response = self.client.post("/test_client_regress/parse_unicode_json/", json, content_type="application/json; charset=koi8-r") self.assertEqual(response.content, json.encode('koi8-r')) + +class DummyFile(object): + def __init__(self, filename): + self.name = filename + def read(self): + return 'TEST_FILE_CONTENT' + +class UploadedFileEncodingTest(TestCase): + def test_file_encoding(self): + encoded_file = encode_file('TEST_BOUNDARY', 'TEST_KEY', DummyFile('test_name.bin')) + self.assertEqual('--TEST_BOUNDARY', encoded_file[0]) + self.assertEqual('Content-Disposition: form-data; name="TEST_KEY"; filename="test_name.bin"', encoded_file[1]) + self.assertEqual('TEST_FILE_CONTENT', encoded_file[-1]) + + def test_guesses_content_type_on_file_encoding(self): + self.assertEqual('Content-Type: application/octet-stream', + encode_file('IGNORE', 'IGNORE', DummyFile("file.bin"))[2]) + self.assertEqual('Content-Type: text/plain', + encode_file('IGNORE', 'IGNORE', DummyFile("file.txt"))[2]) + self.assertEqual('Content-Type: application/zip', + encode_file('IGNORE', 'IGNORE', DummyFile("file.zip"))[2]) + self.assertEqual('Content-Type: application/octet-stream', + encode_file('IGNORE', 'IGNORE', DummyFile("file.unknown"))[2]) + +class RequestHeadersTest(TestCase): + def test_client_headers(self): + "A test client can receive custom headers" + response = self.client.get("/test_client_regress/check_headers/", HTTP_X_ARG_CHECK='Testing 123') + self.assertEquals(response.content, "HTTP_X_ARG_CHECK: Testing 123") + self.assertEquals(response.status_code, 200) + + def test_client_headers_redirect(self): + "Test client headers are preserved through redirects" + response = self.client.get("/test_client_regress/check_headers_redirect/", follow=True, HTTP_X_ARG_CHECK='Testing 123') + self.assertEquals(response.content, "HTTP_X_ARG_CHECK: Testing 123") + self.assertRedirects(response, '/test_client_regress/check_headers/', + status_code=301, target_status_code=200) diff --git a/tests/regressiontests/test_client_regress/urls.py b/tests/regressiontests/test_client_regress/urls.py index 99eb7b70be..650d80b909 100644 --- a/tests/regressiontests/test_client_regress/urls.py +++ b/tests/regressiontests/test_client_regress/urls.py @@ -24,4 +24,6 @@ urlpatterns = patterns('', (r'^request_methods/$', views.request_methods_view), (r'^check_unicode/$', views.return_unicode), (r'^parse_unicode_json/$', views.return_json_file), + (r'^check_headers/$', views.check_headers), + (r'^check_headers_redirect/$', redirect_to, {'url': '/test_client_regress/check_headers/'}), ) diff --git a/tests/regressiontests/test_client_regress/views.py b/tests/regressiontests/test_client_regress/views.py index 5ba7cc30a9..40aa61fca7 100644 --- a/tests/regressiontests/test_client_regress/views.py +++ b/tests/regressiontests/test_client_regress/views.py @@ -86,3 +86,8 @@ def return_json_file(request): mimetype='application/json; charset=' + charset) response['Content-Disposition'] = 'attachment; filename=testfile.json' return response + +def check_headers(request): + "A view that responds with value of the X-ARG-CHECK header" + return HttpResponse('HTTP_X_ARG_CHECK: %s' % request.META.get('HTTP_X_ARG_CHECK', 'Undefined')) + diff --git a/tests/regressiontests/urlpatterns_reverse/included_named_urls.py b/tests/regressiontests/urlpatterns_reverse/included_named_urls.py new file mode 100644 index 0000000000..b3f7903b41 --- /dev/null +++ b/tests/regressiontests/urlpatterns_reverse/included_named_urls.py @@ -0,0 +1,10 @@ +from django.conf.urls.defaults import * +from views import empty_view + +urlpatterns = patterns('', + url(r'^$', empty_view, name="named-url3"), + url(r'^extra/(?P<extra>\w+)/$', empty_view, name="named-url4"), + url(r'^(?P<one>\d+)|(?P<two>\d+)/$', empty_view), + (r'^included/', include('regressiontests.urlpatterns_reverse.included_named_urls2')), +) + diff --git a/tests/regressiontests/urlpatterns_reverse/included_named_urls2.py b/tests/regressiontests/urlpatterns_reverse/included_named_urls2.py new file mode 100644 index 0000000000..96c42c3e4d --- /dev/null +++ b/tests/regressiontests/urlpatterns_reverse/included_named_urls2.py @@ -0,0 +1,9 @@ +from django.conf.urls.defaults import * +from views import empty_view + +urlpatterns = patterns('', + url(r'^$', empty_view, name="named-url5"), + url(r'^extra/(?P<extra>\w+)/$', empty_view, name="named-url6"), + url(r'^(?P<one>\d+)|(?P<two>\d+)/$', empty_view), +) + diff --git a/tests/regressiontests/urlpatterns_reverse/included_namespace_urls.py b/tests/regressiontests/urlpatterns_reverse/included_namespace_urls.py index 073190657c..16887e2a9b 100644 --- a/tests/regressiontests/urlpatterns_reverse/included_namespace_urls.py +++ b/tests/regressiontests/urlpatterns_reverse/included_namespace_urls.py @@ -1,5 +1,6 @@ from django.conf.urls.defaults import * from namespace_urls import URLObject +from views import view_class_instance testobj3 = URLObject('testapp', 'test-ns3') @@ -7,7 +8,13 @@ urlpatterns = patterns('regressiontests.urlpatterns_reverse.views', url(r'^normal/$', 'empty_view', name='inc-normal-view'), url(r'^normal/(?P<arg1>\d+)/(?P<arg2>\d+)/$', 'empty_view', name='inc-normal-view'), + url(r'^mixed_args/(\d+)/(?P<arg2>\d+)/$', 'empty_view', name='inc-mixed-args'), + url(r'^no_kwargs/(\d+)/(\d+)/$', 'empty_view', name='inc-no-kwargs'), + + url(r'^view_class/(?P<arg1>\d+)/(?P<arg2>\d+)/$', view_class_instance, name='inc-view-class'), + (r'^test3/', include(testobj3.urls)), (r'^ns-included3/', include('regressiontests.urlpatterns_reverse.included_urls', namespace='inc-ns3')), + (r'^ns-included4/', include('regressiontests.urlpatterns_reverse.namespace_urls', namespace='inc-ns4')), ) diff --git a/tests/regressiontests/urlpatterns_reverse/named_urls.py b/tests/regressiontests/urlpatterns_reverse/named_urls.py new file mode 100644 index 0000000000..d8a61a106c --- /dev/null +++ b/tests/regressiontests/urlpatterns_reverse/named_urls.py @@ -0,0 +1,9 @@ +from django.conf.urls.defaults import * +from views import empty_view + +urlpatterns = patterns('', + url(r'^$', empty_view, name="named-url1"), + url(r'^extra/(?P<extra>\w+)/$', empty_view, name="named-url2"), + url(r'^(?P<one>\d+)|(?P<two>\d+)/$', empty_view), + (r'^included/', include('regressiontests.urlpatterns_reverse.included_named_urls')), +) diff --git a/tests/regressiontests/urlpatterns_reverse/namespace_urls.py b/tests/regressiontests/urlpatterns_reverse/namespace_urls.py index 27cc7f7a22..3d34049932 100644 --- a/tests/regressiontests/urlpatterns_reverse/namespace_urls.py +++ b/tests/regressiontests/urlpatterns_reverse/namespace_urls.py @@ -1,4 +1,5 @@ from django.conf.urls.defaults import * +from views import view_class_instance class URLObject(object): def __init__(self, app_name, namespace): @@ -23,6 +24,14 @@ urlpatterns = patterns('regressiontests.urlpatterns_reverse.views', url(r'^normal/$', 'empty_view', name='normal-view'), url(r'^normal/(?P<arg1>\d+)/(?P<arg2>\d+)/$', 'empty_view', name='normal-view'), + url(r'^mixed_args/(\d+)/(?P<arg2>\d+)/$', 'empty_view', name='mixed-args'), + url(r'^no_kwargs/(\d+)/(\d+)/$', 'empty_view', name='no-kwargs'), + + url(r'^view_class/(?P<arg1>\d+)/(?P<arg2>\d+)/$', view_class_instance, name='view-class'), + + (r'^unnamed/normal/(?P<arg1>\d+)/(?P<arg2>\d+)/$', 'empty_view'), + (r'^unnamed/view_class/(?P<arg1>\d+)/(?P<arg2>\d+)/$', view_class_instance), + (r'^test1/', include(testobj1.urls)), (r'^test2/', include(testobj2.urls)), (r'^default/', include(default_testobj.urls)), diff --git a/tests/regressiontests/urlpatterns_reverse/tests.py b/tests/regressiontests/urlpatterns_reverse/tests.py index 3fcc935da2..a0b98a88d3 100644 --- a/tests/regressiontests/urlpatterns_reverse/tests.py +++ b/tests/regressiontests/urlpatterns_reverse/tests.py @@ -18,7 +18,9 @@ import unittest from django.conf import settings from django.core.exceptions import ImproperlyConfigured -from django.core.urlresolvers import reverse, resolve, NoReverseMatch, Resolver404 +from django.core.urlresolvers import reverse, resolve, NoReverseMatch,\ + Resolver404, ResolverMatch,\ + RegexURLResolver, RegexURLPattern from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect from django.shortcuts import redirect from django.test import TestCase @@ -26,6 +28,41 @@ from django.test import TestCase import urlconf_outer import urlconf_inner import middleware +import views + +resolve_test_data = ( + # These entries are in the format: (path, url_name, app_name, namespace, view_func, args, kwargs) + # Simple case + ('/normal/42/37/', 'normal-view', None, '', views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'}), + ('/view_class/42/37/', 'view-class', None, '', views.view_class_instance, tuple(), {'arg1': '42', 'arg2': '37'}), + ('/included/normal/42/37/', 'inc-normal-view', None, '', views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'}), + ('/included/view_class/42/37/', 'inc-view-class', None, '', views.view_class_instance, tuple(), {'arg1': '42', 'arg2': '37'}), + + # Unnamed args are dropped if you have *any* kwargs in a pattern + ('/mixed_args/42/37/', 'mixed-args', None, '', views.empty_view, tuple(), {'arg2': '37'}), + ('/included/mixed_args/42/37/', 'inc-mixed-args', None, '', views.empty_view, tuple(), {'arg2': '37'}), + + # Unnamed views will be resolved to the function/class name + ('/unnamed/normal/42/37/', 'regressiontests.urlpatterns_reverse.views.empty_view', None, '', views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'}), + ('/unnamed/view_class/42/37/', 'regressiontests.urlpatterns_reverse.views.ViewClass', None, '', views.view_class_instance, tuple(), {'arg1': '42', 'arg2': '37'}), + + # If you have no kwargs, you get an args list. + ('/no_kwargs/42/37/', 'no-kwargs', None, '', views.empty_view, ('42','37'), {}), + ('/included/no_kwargs/42/37/', 'inc-no-kwargs', None, '', views.empty_view, ('42','37'), {}), + + # Namespaces + ('/test1/inner/42/37/', 'urlobject-view', 'testapp', 'test-ns1', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}), + ('/included/test3/inner/42/37/', 'urlobject-view', 'testapp', 'test-ns3', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}), + ('/ns-included1/normal/42/37/', 'inc-normal-view', None, 'inc-ns1', views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'}), + ('/included/test3/inner/42/37/', 'urlobject-view', 'testapp', 'test-ns3', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}), + ('/default/inner/42/37/', 'urlobject-view', 'testapp', 'testapp', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}), + ('/other2/inner/42/37/', 'urlobject-view', 'nodefault', 'other-ns2', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}), + ('/other1/inner/42/37/', 'urlobject-view', 'nodefault', 'other-ns1', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}), + + # Nested namespaces + ('/ns-included1/test3/inner/42/37/', 'urlobject-view', 'testapp', 'inc-ns1:test-ns3', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}), + ('/ns-included1/ns-included4/ns-included2/test3/inner/42/37/', 'urlobject-view', 'testapp', 'inc-ns1:inc-ns4:inc-ns2:test-ns3', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}), +) test_data = ( ('places', '/places/3/', [3], {}), @@ -119,6 +156,10 @@ class URLPatternReverse(TestCase): else: self.assertEquals(got, expected) + def test_reverse_none(self): + # Reversing None should raise an error, not return the last un-named view. + self.assertRaises(NoReverseMatch, reverse, None) + class ResolverTests(unittest.TestCase): def test_non_regex(self): """ @@ -133,6 +174,42 @@ class ResolverTests(unittest.TestCase): self.assertRaises(Resolver404, resolve, 'a') self.assertRaises(Resolver404, resolve, '\\') self.assertRaises(Resolver404, resolve, '.') + + def test_404_tried_urls_have_names(self): + """ + Verifies that the list of URLs that come back from a Resolver404 + exception contains a list in the right format for printing out in + the DEBUG 404 page with both the patterns and URL names, if available. + """ + urls = 'regressiontests.urlpatterns_reverse.named_urls' + # this list matches the expected URL types and names returned when + # you try to resolve a non-existent URL in the first level of included + # URLs in named_urls.py (e.g., '/included/non-existent-url') + url_types_names = [ + [{'type': RegexURLPattern, 'name': 'named-url1'}], + [{'type': RegexURLPattern, 'name': 'named-url2'}], + [{'type': RegexURLPattern, 'name': None}], + [{'type': RegexURLResolver}, {'type': RegexURLPattern, 'name': 'named-url3'}], + [{'type': RegexURLResolver}, {'type': RegexURLPattern, 'name': 'named-url4'}], + [{'type': RegexURLResolver}, {'type': RegexURLPattern, 'name': None}], + [{'type': RegexURLResolver}, {'type': RegexURLResolver}], + ] + try: + resolve('/included/non-existent-url', urlconf=urls) + self.fail('resolve did not raise a 404') + except Resolver404, e: + # make sure we at least matched the root ('/') url resolver: + self.assertTrue('tried' in e.args[0]) + tried = e.args[0]['tried'] + self.assertEqual(len(e.args[0]['tried']), len(url_types_names), 'Wrong number of tried URLs returned. Expected %s, got %s.' % (len(url_types_names), len(e.args[0]['tried']))) + for tried, expected in zip(e.args[0]['tried'], url_types_names): + for t, e in zip(tried, expected): + self.assertTrue(isinstance(t, e['type']), '%s is not an instance of %s' % (t, e['type'])) + if 'name' in e: + if not e['name']: + self.assertTrue(t.name is None, 'Expected no URL name but found %s.' % t.name) + else: + self.assertEqual(t.name, e['name'], 'Wrong URL name. Expected "%s", got "%s".' % (e['name'], t.name)) class ReverseShortcutTests(TestCase): urls = 'regressiontests.urlpatterns_reverse.urls' @@ -229,6 +306,12 @@ class NamespaceTests(TestCase): self.assertEquals('/ns-included1/test3/inner/37/42/', reverse('inc-ns1:test-ns3:urlobject-view', args=[37,42])) self.assertEquals('/ns-included1/test3/inner/42/37/', reverse('inc-ns1:test-ns3:urlobject-view', kwargs={'arg1':42, 'arg2':37})) + def test_nested_namespace_pattern(self): + "Namespaces can be nested" + self.assertEquals('/ns-included1/ns-included4/ns-included1/test3/inner/', reverse('inc-ns1:inc-ns4:inc-ns1:test-ns3:urlobject-view')) + self.assertEquals('/ns-included1/ns-included4/ns-included1/test3/inner/37/42/', reverse('inc-ns1:inc-ns4:inc-ns1:test-ns3:urlobject-view', args=[37,42])) + self.assertEquals('/ns-included1/ns-included4/ns-included1/test3/inner/42/37/', reverse('inc-ns1:inc-ns4:inc-ns1:test-ns3:urlobject-view', kwargs={'arg1':42, 'arg2':37})) + def test_app_lookup_object(self): "A default application namespace can be used for lookup" self.assertEquals('/default/inner/', reverse('testapp:urlobject-view')) @@ -311,9 +394,51 @@ class ErrorHandlerResolutionTests(TestCase): self.assertEqual(self.callable_resolver.resolve404(), handler) self.assertEqual(self.callable_resolver.resolve500(), handler) +class DefaultErrorHandlerTests(TestCase): + urls = 'regressiontests.urlpatterns_reverse.urls_without_full_import' + + def test_default_handler(self): + "If the urls.py doesn't specify handlers, the defaults are used" + try: + response = self.client.get('/test/') + self.assertEquals(response.status_code, 404) + except AttributeError: + self.fail("Shouldn't get an AttributeError due to undefined 404 handler") + + try: + self.assertRaises(ValueError, self.client.get, '/bad_view/') + except AttributeError: + self.fail("Shouldn't get an AttributeError due to undefined 500 handler") + class NoRootUrlConfTests(TestCase): """Tests for handler404 and handler500 if urlconf is None""" urls = None def test_no_handler_exception(self): self.assertRaises(ImproperlyConfigured, self.client.get, '/test/me/') + +class ResolverMatchTests(TestCase): + urls = 'regressiontests.urlpatterns_reverse.namespace_urls' + + def test_urlpattern_resolve(self): + for path, name, app_name, namespace, func, args, kwargs in resolve_test_data: + # Test legacy support for extracting "function, args, kwargs" + match_func, match_args, match_kwargs = resolve(path) + self.assertEqual(match_func, func) + self.assertEqual(match_args, args) + self.assertEqual(match_kwargs, kwargs) + + # Test ResolverMatch capabilities. + match = resolve(path) + self.assertEqual(match.__class__, ResolverMatch) + self.assertEqual(match.url_name, name) + self.assertEqual(match.args, args) + self.assertEqual(match.kwargs, kwargs) + self.assertEqual(match.app_name, app_name) + self.assertEqual(match.namespace, namespace) + self.assertEqual(match.func, func) + + # ... and for legacy purposes: + self.assertEquals(match[0], func) + self.assertEquals(match[1], args) + self.assertEquals(match[2], kwargs) diff --git a/tests/regressiontests/urlpatterns_reverse/urls_without_full_import.py b/tests/regressiontests/urlpatterns_reverse/urls_without_full_import.py new file mode 100644 index 0000000000..75a195ed14 --- /dev/null +++ b/tests/regressiontests/urlpatterns_reverse/urls_without_full_import.py @@ -0,0 +1,10 @@ +# A URLs file that doesn't use the default +# from django.conf.urls.defaults import * +# import pattern. +from django.conf.urls.defaults import patterns, url +from views import empty_view, bad_view + +urlpatterns = patterns('', + url(r'^test_view/$', empty_view, name="test_view"), + url(r'^bad_view/$', bad_view, name="bad_view"), +) diff --git a/tests/regressiontests/urlpatterns_reverse/views.py b/tests/regressiontests/urlpatterns_reverse/views.py index 99c00bde70..fdd742382c 100644 --- a/tests/regressiontests/urlpatterns_reverse/views.py +++ b/tests/regressiontests/urlpatterns_reverse/views.py @@ -1,8 +1,19 @@ +from django.http import HttpResponse + def empty_view(request, *args, **kwargs): - pass + return HttpResponse('') def kwargs_view(request, arg1=1, arg2=2): - pass + return HttpResponse('') def absolute_kwargs_view(request, arg1=1, arg2=2): - pass + return HttpResponse('') + +class ViewClass(object): + def __call__(self, request, *args, **kwargs): + return HttpResponse('') + +view_class_instance = ViewClass() + +def bad_view(request, *args, **kwargs): + raise ValueError("I don't think I'm getting good value for this view") diff --git a/tests/regressiontests/utils/timesince.py b/tests/regressiontests/utils/timesince.py index 04878b272a..5a54bf4c8c 100644 --- a/tests/regressiontests/utils/timesince.py +++ b/tests/regressiontests/utils/timesince.py @@ -88,11 +88,11 @@ u'0 minutes' u'0 minutes' # Timesince should work with both date objects (#9672) ->>> today = datetime.date.today() ->>> timeuntil(today+oneday, today) -u'1 day' ->>> timeuntil(today-oneday, today) -u'0 minutes' ->>> timeuntil(today+oneweek, today) -u'1 week' +>>> today = datetime.date.today() +>>> timeuntil(today+oneday, today) +u'1 day' +>>> timeuntil(today-oneday, today) +u'0 minutes' +>>> timeuntil(today+oneweek, today) +u'1 week' """ diff --git a/tests/regressiontests/views/tests/__init__.py b/tests/regressiontests/views/tests/__init__.py index 697968ee52..edd533e175 100644 --- a/tests/regressiontests/views/tests/__init__.py +++ b/tests/regressiontests/views/tests/__init__.py @@ -2,6 +2,7 @@ from debug import * from defaults import * from generic.create_update import * from generic.date_based import * +from generic.simple import * from i18n import * from specials import * from static import * diff --git a/tests/regressiontests/views/tests/generic/simple.py b/tests/regressiontests/views/tests/generic/simple.py new file mode 100644 index 0000000000..f94b3da439 --- /dev/null +++ b/tests/regressiontests/views/tests/generic/simple.py @@ -0,0 +1,38 @@ +# coding: utf-8 + +from django.test import TestCase + +class RedirectToTest(TestCase): + def test_redirect_to_returns_permanent_redirect(self): + "simple.redirect_to returns a permanent redirect (301) by default" + response = self.client.get('/views/simple/redirect_to/') + self.assertEqual(response.status_code, 301) + self.assertEqual('http://testserver/views/simple/target/', response['Location']) + + def test_redirect_to_can_return_a_temporary_redirect(self): + "simple.redirect_to returns a temporary redirect (302) when explicitely asked to" + response = self.client.get('/views/simple/redirect_to_temp/') + self.assertEqual(response.status_code, 302) + self.assertEqual('http://testserver/views/simple/target/', response['Location']) + + def test_redirect_to_on_empty_url_returns_gone(self): + "simple.redirect_to returns resource gone (410) when given a None url" + response = self.client.get('/views/simple/redirect_to_none/') + self.assertEqual(response.status_code, 410) + + def test_redirect_to_allows_formatted_url_string(self): + "simple.redirect_to uses string interpolation on target url for keyword args" + response = self.client.get('/views/simple/redirect_to_arg/42/') + self.assertEqual(response.status_code, 301) + self.assertEqual('http://testserver/views/simple/target_arg/42/', response['Location']) + + def test_redirect_to_allows_query_string_to_be_passed(self): + "simple.redirect_to configured with query_string=True passes on any query string" + # the default is to not forward the query string + response = self.client.get('/views/simple/redirect_to/?param1=foo¶m2=bar') + self.assertEqual(response.status_code, 301) + self.assertEqual('http://testserver/views/simple/target/', response['Location']) + # views configured with query_string=True however passes the query string along + response = self.client.get('/views/simple/redirect_to_query/?param1=foo¶m2=bar') + self.assertEqual(response.status_code, 301) + self.assertEqual('http://testserver/views/simple/target/?param1=foo¶m2=bar', response['Location']) diff --git a/tests/regressiontests/views/urls.py b/tests/regressiontests/views/urls.py index f5675d0e28..b42700baeb 100644 --- a/tests/regressiontests/views/urls.py +++ b/tests/regressiontests/views/urls.py @@ -77,7 +77,6 @@ urlpatterns += patterns('django.views.generic.date_based', ) # crud generic views. - urlpatterns += patterns('django.views.generic.create_update', (r'^create_update/member/create/article/$', 'create_object', dict(login_required=True, model=Article)), @@ -123,3 +122,12 @@ urlpatterns += patterns('regressiontests.views.views', url(r'view_exception/(?P<n>\d+)/$', 'view_exception', name='view_exception'), url(r'template_exception/(?P<n>\d+)/$', 'template_exception', name='template_exception'), ) + +# simple generic views. +urlpatterns += patterns('django.views.generic.simple', + (r'^simple/redirect_to/$', 'redirect_to', dict(url='/views/simple/target/')), + (r'^simple/redirect_to_temp/$', 'redirect_to', dict(url='/views/simple/target/', permanent=False)), + (r'^simple/redirect_to_none/$', 'redirect_to', dict(url=None)), + (r'^simple/redirect_to_arg/(?P<id>\d+)/$', 'redirect_to', dict(url='/views/simple/target_arg/%(id)s/')), + (r'^simple/redirect_to_query/$', 'redirect_to', dict(url='/views/simple/target/', query_string=True)), +) diff --git a/tests/runtests.py b/tests/runtests.py index 77bc1481d1..e733569212 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -27,6 +27,7 @@ ALWAYS_INSTALLED_APPS = [ 'django.contrib.messages', 'django.contrib.comments', 'django.contrib.admin', + 'django.contrib.admindocs', ] def get_test_models(): @@ -119,22 +120,19 @@ def django_tests(verbosity, interactive, failfast, test_labels): from django.db.models.loading import load_app # Load all the test model apps. + test_labels_set = set([label.split('.')[0] for label in test_labels]) for model_dir, model_name in get_test_models(): model_label = '.'.join([model_dir, model_name]) - try: - # if the model was named on the command line, or - # no models were named (i.e., run all), import - # this model and add it to the list to test. - if not test_labels or model_name in set([label.split('.')[0] for label in test_labels]): - if verbosity >= 1: - print "Importing model %s" % model_name - mod = load_app(model_label) - if mod: - if model_label not in settings.INSTALLED_APPS: - settings.INSTALLED_APPS.append(model_label) - except Exception, e: - sys.stderr.write("Error while importing %s:" % model_name + ''.join(traceback.format_exception(*sys.exc_info())[1:])) - continue + # if the model was named on the command line, or + # no models were named (i.e., run all), import + # this model and add it to the list to test. + if not test_labels or model_name in test_labels_set: + if verbosity >= 2: + print "Importing model %s" % model_name + mod = load_app(model_label) + if mod: + if model_label not in settings.INSTALLED_APPS: + settings.INSTALLED_APPS.append(model_label) # Add tests for invalid models. extra_tests = [] @@ -185,8 +183,8 @@ if __name__ == "__main__": from optparse import OptionParser usage = "%prog [options] [model model model ...]" parser = OptionParser(usage=usage) - parser.add_option('-v','--verbosity', action='store', dest='verbosity', default='0', - type='choice', choices=['0', '1', '2'], + parser.add_option('-v','--verbosity', action='store', dest='verbosity', default='1', + type='choice', choices=['0', '1', '2', '3'], help='Verbosity level; 0=minimal output, 1=normal output, 2=all output') parser.add_option('--noinput', action='store_false', dest='interactive', default=True, help='Tells Django to NOT prompt the user for input of any kind.') |