mirror of
https://github.com/django/django.git
synced 2025-07-04 09:49:12 +00:00
gis: Merged revisions 5491-5539 via svnmerge from
http://code.djangoproject.com/svn/django/trunk git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@5540 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
b0a56a9919
commit
fc779fe55a
8
AUTHORS
8
AUTHORS
@ -100,11 +100,14 @@ answer newbie questions, and generally made Django that much better:
|
||||
Marc Fargas <telenieko@telenieko.com>
|
||||
favo@exoweb.net
|
||||
Bill Fenner <fenner@gmail.com>
|
||||
Stefane Fermgier <sf@fermigier.com>
|
||||
Afonso Fernández Nogueira <fonzzo.django@gmail.com>
|
||||
Matthew Flanagan <http://wadofstuff.blogspot.com>
|
||||
Eric Floehr <eric@intellovations.com>
|
||||
Jorge Gajon <gajon@gajon.org>
|
||||
gandalf@owca.info
|
||||
Baishampayan Ghose
|
||||
glin@seznam.cz
|
||||
martin.glueck@gmail.com
|
||||
GomoX <gomo@datafull.com>
|
||||
Simon Greenhill <dev@simon.net.nz>
|
||||
@ -119,6 +122,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
Ian Holsman <http://feh.holsman.net/>
|
||||
Kieran Holland <http://www.kieranholland.com>
|
||||
Sung-Jin Hong <serialx.net@gmail.com>
|
||||
Richard House <Richard.House@i-logue.com>
|
||||
Robert Rock Howard <http://djangomojo.com/>
|
||||
Jason Huggins <http://www.jrandolph.com/blog/>
|
||||
Hyun Mi Ae
|
||||
@ -170,6 +174,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
mikko@sorl.net
|
||||
mitakummaa@gmail.com
|
||||
mmarshall
|
||||
Reza Mohammadi <reza@zeerak.ir>
|
||||
Eric Moritz <http://eric.themoritzfamily.com/>
|
||||
mrmachine <real.human@mrmachine.net>
|
||||
Robin Munn <http://www.geekforgod.com/>
|
||||
@ -183,6 +188,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
Jay Parlar <parlar@gmail.com>
|
||||
pavithran s <pavithran.s@gmail.com>
|
||||
Barry Pederson <bp@barryp.org>
|
||||
petr.marhoun@gmail.com
|
||||
pgross@thoughtworks.com
|
||||
phaedo <http://phaedo.cx/>
|
||||
phil@produxion.net
|
||||
@ -233,6 +239,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
Amit Upadhyay
|
||||
Geert Vanderkelen
|
||||
viestards.lists@gmail.com
|
||||
Vlado <vlado@labath.org>
|
||||
Milton Waddams
|
||||
wam-djangobug@wamber.net
|
||||
wangchun <yaohua2000@gmail.com>
|
||||
@ -245,6 +252,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
wojtek
|
||||
ye7cakf02@sneakemail.com
|
||||
ymasuda@ethercube.com
|
||||
Jarek Zgoda <jarek.zgoda@gmail.com>
|
||||
Cheng Zhang
|
||||
|
||||
A big THANK YOU goes to:
|
||||
|
@ -48,6 +48,7 @@ LANGUAGES = (
|
||||
('en', gettext_noop('English')),
|
||||
('es', gettext_noop('Spanish')),
|
||||
('es_AR', gettext_noop('Argentinean Spanish')),
|
||||
('fa', gettext_noop('Persian')),
|
||||
('fi', gettext_noop('Finnish')),
|
||||
('fr', gettext_noop('French')),
|
||||
('gl', gettext_noop('Galician')),
|
||||
|
Binary file not shown.
@ -1,20 +1,18 @@
|
||||
# translation of django.po to
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER.
|
||||
# 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: 2007-05-25 13:04+0200\n"
|
||||
"PO-Revision-Date: 2007-05-25 13:04+0200\n"
|
||||
"Last-Translator: Marc Fargas <marc@fargas.com>\n"
|
||||
"POT-Creation-Date: 2007-06-25 17:31+0200\n"
|
||||
"PO-Revision-Date: 2007-06-25 17:47+0200\n"
|
||||
"Last-Translator: Marc Fargas <telenieko@telenieko.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: VIM 7.0\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);"
|
||||
|
||||
#: template/defaultfilters.py:491
|
||||
msgid "yes,no,maybe"
|
||||
@ -42,38 +40,38 @@ msgstr "%.1f MB"
|
||||
msgid "%.1f GB"
|
||||
msgstr "%.1f GB"
|
||||
|
||||
#: newforms/models.py:173 newforms/fields.py:432
|
||||
#: newforms/models.py:172 newforms/fields.py:432
|
||||
msgid "Select a valid choice. That choice is not one of the available choices."
|
||||
msgstr ""
|
||||
"Esculli una opció vàlida; Aquesta opció no és una de les opcions disponibles."
|
||||
|
||||
#: newforms/models.py:186 newforms/fields.py:87 newforms/fields.py:444
|
||||
#: newforms/fields.py:520 newforms/fields.py:531 oldforms/__init__.py:357
|
||||
#: db/models/fields/__init__.py:121 db/models/fields/__init__.py:278
|
||||
#: db/models/fields/__init__.py:675 db/models/fields/__init__.py:686
|
||||
#: newforms/models.py:185 newforms/fields.py:87 newforms/fields.py:444
|
||||
#: newforms/fields.py:522 newforms/fields.py:533 oldforms/__init__.py:357
|
||||
#: db/models/fields/__init__.py:126 db/models/fields/__init__.py:283
|
||||
#: db/models/fields/__init__.py:680 db/models/fields/__init__.py:691
|
||||
msgid "This field is required."
|
||||
msgstr "Aquest camp és obligatori."
|
||||
|
||||
#: newforms/models.py:190 newforms/fields.py:448 newforms/fields.py:524
|
||||
#: newforms/models.py:189 newforms/fields.py:448 newforms/fields.py:526
|
||||
msgid "Enter a list of values."
|
||||
msgstr "Introdueixi una llista de valors."
|
||||
|
||||
#: newforms/models.py:196 newforms/fields.py:457
|
||||
#: newforms/models.py:195 newforms/fields.py:457
|
||||
#, python-format
|
||||
msgid "Select a valid choice. %s is not one of the available choices."
|
||||
msgstr "Esculli una opció vàlida; %s' no és una de les opcions vàlides."
|
||||
|
||||
#: newforms/widgets.py:182 contrib/admin/filterspecs.py:150
|
||||
#: newforms/widgets.py:181 contrib/admin/filterspecs.py:150
|
||||
#: oldforms/__init__.py:577
|
||||
msgid "Unknown"
|
||||
msgstr "Desconegut"
|
||||
|
||||
#: newforms/widgets.py:182 contrib/admin/filterspecs.py:143
|
||||
#: newforms/widgets.py:181 contrib/admin/filterspecs.py:143
|
||||
#: oldforms/__init__.py:577
|
||||
msgid "Yes"
|
||||
msgstr "Si"
|
||||
|
||||
#: newforms/widgets.py:182 contrib/admin/filterspecs.py:143
|
||||
#: newforms/widgets.py:181 contrib/admin/filterspecs.py:143
|
||||
#: oldforms/__init__.py:577
|
||||
msgid "No"
|
||||
msgstr "No"
|
||||
@ -121,11 +119,11 @@ msgstr "Asseguris de que no hi ha més de %s decimals."
|
||||
msgid "Ensure that there are no more than %s digits before the decimal point."
|
||||
msgstr "Asseguris de que no hia ha més de %s dígits decimals."
|
||||
|
||||
#: newforms/fields.py:233
|
||||
#: newforms/fields.py:233 newforms/fields.py:566
|
||||
msgid "Enter a valid date."
|
||||
msgstr "Introdueixi una data vàlida."
|
||||
|
||||
#: newforms/fields.py:260
|
||||
#: newforms/fields.py:260 newforms/fields.py:568
|
||||
msgid "Enter a valid time."
|
||||
msgstr "Introdueixi una hora vàlida."
|
||||
|
||||
@ -198,118 +196,122 @@ msgid "Argentinean Spanish"
|
||||
msgstr "Castellà Argentí"
|
||||
|
||||
#: conf/global_settings.py:51
|
||||
msgid "Persian"
|
||||
msgstr "Persa"
|
||||
|
||||
#: conf/global_settings.py:52
|
||||
msgid "Finnish"
|
||||
msgstr "Finlandès"
|
||||
|
||||
#: conf/global_settings.py:52
|
||||
#: conf/global_settings.py:53
|
||||
msgid "French"
|
||||
msgstr "Francès"
|
||||
|
||||
#: conf/global_settings.py:53
|
||||
#: conf/global_settings.py:54
|
||||
msgid "Galician"
|
||||
msgstr "Galleg"
|
||||
|
||||
#: conf/global_settings.py:54
|
||||
#: conf/global_settings.py:55
|
||||
msgid "Hungarian"
|
||||
msgstr "Húngar"
|
||||
|
||||
#: conf/global_settings.py:55
|
||||
#: conf/global_settings.py:56
|
||||
msgid "Hebrew"
|
||||
msgstr "Hebreu"
|
||||
|
||||
#: conf/global_settings.py:56
|
||||
#: conf/global_settings.py:57
|
||||
msgid "Icelandic"
|
||||
msgstr "Islandès"
|
||||
|
||||
#: conf/global_settings.py:57
|
||||
#: conf/global_settings.py:58
|
||||
msgid "Italian"
|
||||
msgstr "Italià"
|
||||
|
||||
#: conf/global_settings.py:58
|
||||
#: conf/global_settings.py:59
|
||||
msgid "Japanese"
|
||||
msgstr "Japonès"
|
||||
|
||||
#: conf/global_settings.py:59
|
||||
#: conf/global_settings.py:60
|
||||
msgid "Korean"
|
||||
msgstr "Coreà"
|
||||
|
||||
#: conf/global_settings.py:60
|
||||
#: conf/global_settings.py:61
|
||||
msgid "Kannada"
|
||||
msgstr ""
|
||||
|
||||
#: conf/global_settings.py:61
|
||||
msgid "Latvian"
|
||||
msgstr ""
|
||||
|
||||
#: conf/global_settings.py:62
|
||||
msgid "Latvian"
|
||||
msgstr "Letó"
|
||||
|
||||
#: conf/global_settings.py:63
|
||||
msgid "Macedonian"
|
||||
msgstr "Macedoni"
|
||||
|
||||
#: conf/global_settings.py:63
|
||||
#: conf/global_settings.py:64
|
||||
msgid "Dutch"
|
||||
msgstr "Holandès"
|
||||
|
||||
#: conf/global_settings.py:64
|
||||
#: conf/global_settings.py:65
|
||||
msgid "Norwegian"
|
||||
msgstr "Norueg"
|
||||
|
||||
#: conf/global_settings.py:65
|
||||
#: conf/global_settings.py:66
|
||||
msgid "Polish"
|
||||
msgstr "Polac"
|
||||
|
||||
#: conf/global_settings.py:66
|
||||
#: conf/global_settings.py:67
|
||||
msgid "Portugese"
|
||||
msgstr "Portuguès"
|
||||
|
||||
#: conf/global_settings.py:67
|
||||
#: conf/global_settings.py:68
|
||||
msgid "Brazilian"
|
||||
msgstr "Brasileny"
|
||||
|
||||
#: conf/global_settings.py:68
|
||||
#: conf/global_settings.py:69
|
||||
msgid "Romanian"
|
||||
msgstr "Rumanès"
|
||||
|
||||
#: conf/global_settings.py:69
|
||||
#: conf/global_settings.py:70
|
||||
msgid "Russian"
|
||||
msgstr "Rús"
|
||||
|
||||
#: conf/global_settings.py:70
|
||||
#: conf/global_settings.py:71
|
||||
msgid "Slovak"
|
||||
msgstr "Eslovac"
|
||||
|
||||
#: conf/global_settings.py:71
|
||||
#: conf/global_settings.py:72
|
||||
msgid "Slovenian"
|
||||
msgstr "Esloveni"
|
||||
|
||||
#: conf/global_settings.py:72
|
||||
#: conf/global_settings.py:73
|
||||
msgid "Serbian"
|
||||
msgstr "Serbi"
|
||||
|
||||
#: conf/global_settings.py:73
|
||||
#: conf/global_settings.py:74
|
||||
msgid "Swedish"
|
||||
msgstr "Suec"
|
||||
|
||||
#: conf/global_settings.py:74
|
||||
msgid "Tamil"
|
||||
msgstr ""
|
||||
|
||||
#: conf/global_settings.py:75
|
||||
msgid "Telugu"
|
||||
msgstr ""
|
||||
msgid "Tamil"
|
||||
msgstr "Tàmil"
|
||||
|
||||
#: conf/global_settings.py:76
|
||||
msgid "Telugu"
|
||||
msgstr "Telugu"
|
||||
|
||||
#: conf/global_settings.py:77
|
||||
msgid "Turkish"
|
||||
msgstr "Turc"
|
||||
|
||||
#: conf/global_settings.py:77
|
||||
#: conf/global_settings.py:78
|
||||
msgid "Ukrainian"
|
||||
msgstr "Ucranià"
|
||||
|
||||
#: conf/global_settings.py:78
|
||||
#: conf/global_settings.py:79
|
||||
msgid "Simplified Chinese"
|
||||
msgstr "Xinés simplificat"
|
||||
|
||||
#: conf/global_settings.py:79
|
||||
#: conf/global_settings.py:80
|
||||
msgid "Traditional Chinese"
|
||||
msgstr "Xinés tradicional"
|
||||
|
||||
@ -576,17 +578,15 @@ msgid ""
|
||||
"comment:\n"
|
||||
"\n"
|
||||
"%(text)s"
|
||||
msgid_plural ""
|
||||
"This comment was posted by a user who has posted fewer than %(count)s "
|
||||
"comments:\n"
|
||||
"\n"
|
||||
"%(text)s"
|
||||
msgstr[0] ""
|
||||
msgstr ""
|
||||
"Aquest comentari el va enviar un usuari que ha enviat menys de %(count)s "
|
||||
"comentari:\n"
|
||||
"\n"
|
||||
"%(text)s"
|
||||
msgstr[1] ""
|
||||
"Aquest comentari el va enviar un usuari que ha enviat menys de %(count)s "
|
||||
"comentaris:\n"
|
||||
"\n"
|
||||
@ -1218,13 +1218,13 @@ msgstr "Editar aquest objecte (nova finestra)"
|
||||
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/admin/views/auth.py:19 contrib/admin/views/main.py:257
|
||||
#: contrib/admin/views/auth.py:19 contrib/admin/views/main.py:262
|
||||
#, python-format
|
||||
msgid "The %(name)s \"%(obj)s\" was added successfully."
|
||||
msgstr "El/la %(name)s \"%(obj)s\".ha estat agregat/da amb èxit."
|
||||
|
||||
#: contrib/admin/views/auth.py:24 contrib/admin/views/main.py:261
|
||||
#: contrib/admin/views/main.py:347
|
||||
#: contrib/admin/views/auth.py:24 contrib/admin/views/main.py:266
|
||||
#: contrib/admin/views/main.py:352
|
||||
msgid "You may edit it again below."
|
||||
msgstr "Pot editar-lo de nou abaix."
|
||||
|
||||
@ -1241,96 +1241,96 @@ msgstr "Canvi de clau exitós"
|
||||
msgid "Change password: %s"
|
||||
msgstr "Canviar clau: %s"
|
||||
|
||||
#: contrib/admin/views/main.py:223
|
||||
#: contrib/admin/views/main.py:228
|
||||
msgid "Site administration"
|
||||
msgstr "Lloc administratiu"
|
||||
|
||||
#: contrib/admin/views/main.py:271 contrib/admin/views/main.py:356
|
||||
#: contrib/admin/views/main.py:276 contrib/admin/views/main.py:361
|
||||
#, python-format
|
||||
msgid "You may add another %s below."
|
||||
msgstr "Pot agregar un altre %s abaix."
|
||||
|
||||
#: contrib/admin/views/main.py:289
|
||||
#: contrib/admin/views/main.py:294
|
||||
#, python-format
|
||||
msgid "Add %s"
|
||||
msgstr "Agregar %s"
|
||||
|
||||
#: contrib/admin/views/main.py:335
|
||||
#: contrib/admin/views/main.py:340
|
||||
#, python-format
|
||||
msgid "Added %s."
|
||||
msgstr "Agregat %s."
|
||||
|
||||
#: contrib/admin/views/main.py:335 contrib/admin/views/main.py:337
|
||||
#: contrib/admin/views/main.py:339 db/models/manipulators.py:308
|
||||
#: contrib/admin/views/main.py:340 contrib/admin/views/main.py:342
|
||||
#: contrib/admin/views/main.py:344 db/models/manipulators.py:308
|
||||
msgid "and"
|
||||
msgstr "i"
|
||||
|
||||
#: contrib/admin/views/main.py:337
|
||||
#: contrib/admin/views/main.py:342
|
||||
#, python-format
|
||||
msgid "Changed %s."
|
||||
msgstr "Modificat %s."
|
||||
|
||||
#: contrib/admin/views/main.py:339
|
||||
#: contrib/admin/views/main.py:344
|
||||
#, python-format
|
||||
msgid "Deleted %s."
|
||||
msgstr "Eliminat %s."
|
||||
|
||||
#: contrib/admin/views/main.py:342
|
||||
#: contrib/admin/views/main.py:347
|
||||
msgid "No fields changed."
|
||||
msgstr "Cap camp canviat."
|
||||
|
||||
#: contrib/admin/views/main.py:345
|
||||
#: contrib/admin/views/main.py:350
|
||||
#, python-format
|
||||
msgid "The %(name)s \"%(obj)s\" was changed successfully."
|
||||
msgstr "S'ha modificat amb èxist el/la %(name)s \"%(obj)s."
|
||||
|
||||
#: contrib/admin/views/main.py:353
|
||||
#: contrib/admin/views/main.py:358
|
||||
#, python-format
|
||||
msgid ""
|
||||
"The %(name)s \"%(obj)s\" was added successfully. You may edit it again below."
|
||||
msgstr ""
|
||||
"S'ha agregat amb èxit el/la %(name)s \"%(obj)s\". Pot editar-lo de nou abaix."
|
||||
|
||||
#: contrib/admin/views/main.py:391
|
||||
#: contrib/admin/views/main.py:396
|
||||
#, python-format
|
||||
msgid "Change %s"
|
||||
msgstr "Modificar %s"
|
||||
|
||||
#: contrib/admin/views/main.py:476
|
||||
#: contrib/admin/views/main.py:481
|
||||
#, 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/views/main.py:481
|
||||
#: contrib/admin/views/main.py:486
|
||||
#, python-format
|
||||
msgid "One or more %(fieldname)s in %(name)s:"
|
||||
msgstr "Un o més %(fieldname)s en %(name)s:"
|
||||
|
||||
#: contrib/admin/views/main.py:514
|
||||
#: contrib/admin/views/main.py:518
|
||||
#, python-format
|
||||
msgid "The %(name)s \"%(obj)s\" was deleted successfully."
|
||||
msgstr "El/la %(name)s \"%(obj)s\".ha estat eliminat amb èxit."
|
||||
|
||||
#: contrib/admin/views/main.py:517
|
||||
#: contrib/admin/views/main.py:521
|
||||
msgid "Are you sure?"
|
||||
msgstr "Està segur?"
|
||||
|
||||
#: contrib/admin/views/main.py:539
|
||||
#: contrib/admin/views/main.py:543
|
||||
#, python-format
|
||||
msgid "Change history: %s"
|
||||
msgstr "Modificar històric: %s"
|
||||
|
||||
#: contrib/admin/views/main.py:573
|
||||
#: contrib/admin/views/main.py:577
|
||||
#, python-format
|
||||
msgid "Select %s"
|
||||
msgstr "Seleccioni %s"
|
||||
|
||||
#: contrib/admin/views/main.py:573
|
||||
#: contrib/admin/views/main.py:577
|
||||
#, python-format
|
||||
msgid "Select %s to change"
|
||||
msgstr "Seleccioni %s per modificar"
|
||||
|
||||
#: contrib/admin/views/main.py:768
|
||||
#: contrib/admin/views/main.py:772
|
||||
msgid "Database error"
|
||||
msgstr "Error de/en la base de dades"
|
||||
|
||||
@ -1697,35 +1697,35 @@ msgstr "llocs"
|
||||
msgid "Logged out"
|
||||
msgstr "Sessió finalitzada"
|
||||
|
||||
#: contrib/auth/models.py:44 contrib/auth/models.py:64
|
||||
#: contrib/auth/models.py:49 contrib/auth/models.py:69
|
||||
msgid "name"
|
||||
msgstr "nom"
|
||||
|
||||
#: contrib/auth/models.py:46
|
||||
#: contrib/auth/models.py:51
|
||||
msgid "codename"
|
||||
msgstr "nom en clau"
|
||||
|
||||
#: contrib/auth/models.py:49
|
||||
#: contrib/auth/models.py:54
|
||||
msgid "permission"
|
||||
msgstr "permís"
|
||||
|
||||
#: contrib/auth/models.py:50 contrib/auth/models.py:65
|
||||
#: contrib/auth/models.py:55 contrib/auth/models.py:70
|
||||
msgid "permissions"
|
||||
msgstr "permissos"
|
||||
|
||||
#: contrib/auth/models.py:68
|
||||
#: contrib/auth/models.py:73
|
||||
msgid "group"
|
||||
msgstr "grup"
|
||||
|
||||
#: contrib/auth/models.py:69 contrib/auth/models.py:109
|
||||
#: contrib/auth/models.py:74 contrib/auth/models.py:114
|
||||
msgid "groups"
|
||||
msgstr "grups"
|
||||
|
||||
#: contrib/auth/models.py:99
|
||||
#: contrib/auth/models.py:104
|
||||
msgid "username"
|
||||
msgstr "nom d'usuari"
|
||||
|
||||
#: contrib/auth/models.py:99
|
||||
#: contrib/auth/models.py:104
|
||||
msgid ""
|
||||
"Required. 30 characters or fewer. Alphanumeric characters only (letters, "
|
||||
"digits and underscores)."
|
||||
@ -1733,23 +1733,23 @@ msgstr ""
|
||||
"Requerit. 30 o menys caracters. Només caracters alfanumèrics (lletres, "
|
||||
"dígits i guions baixos)."
|
||||
|
||||
#: contrib/auth/models.py:100
|
||||
#: contrib/auth/models.py:105
|
||||
msgid "first name"
|
||||
msgstr "nom propi"
|
||||
|
||||
#: contrib/auth/models.py:101
|
||||
#: contrib/auth/models.py:106
|
||||
msgid "last name"
|
||||
msgstr "cognoms"
|
||||
|
||||
#: contrib/auth/models.py:102
|
||||
#: contrib/auth/models.py:107
|
||||
msgid "e-mail address"
|
||||
msgstr "adreça de correu electrònic"
|
||||
|
||||
#: contrib/auth/models.py:103
|
||||
#: contrib/auth/models.py:108
|
||||
msgid "password"
|
||||
msgstr "contrasenya"
|
||||
|
||||
#: contrib/auth/models.py:103
|
||||
#: contrib/auth/models.py:108
|
||||
msgid ""
|
||||
"Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change "
|
||||
"password form</a>."
|
||||
@ -1757,19 +1757,19 @@ msgstr ""
|
||||
"Utilitzi '[algo]$[salt]$[hexdigest]' o el <a href=\"password/\">formulari de "
|
||||
"canvi de contrasenya</a>."
|
||||
|
||||
#: contrib/auth/models.py:104
|
||||
#: contrib/auth/models.py:109
|
||||
msgid "staff status"
|
||||
msgstr "és membre del personal"
|
||||
|
||||
#: contrib/auth/models.py:104
|
||||
#: contrib/auth/models.py:109
|
||||
msgid "Designates whether the user can log into this admin site."
|
||||
msgstr "Indica si l'usuari pot entrar en el lloc administratiu."
|
||||
|
||||
#: contrib/auth/models.py:105
|
||||
#: contrib/auth/models.py:110
|
||||
msgid "active"
|
||||
msgstr "actiu"
|
||||
|
||||
#: contrib/auth/models.py:105
|
||||
#: contrib/auth/models.py:110
|
||||
msgid ""
|
||||
"Designates whether this user can log into the Django admin. Unselect this "
|
||||
"instead of deleting accounts."
|
||||
@ -1777,11 +1777,11 @@ msgstr ""
|
||||
"Designa si aquest usuari pot iniciar sessió a la interfície administrativa "
|
||||
"Djano. Deselecciona-ho enlloc de esborrar comptes d'usuari."
|
||||
|
||||
#: contrib/auth/models.py:106
|
||||
#: contrib/auth/models.py:111
|
||||
msgid "superuser status"
|
||||
msgstr "estat de superusuari"
|
||||
|
||||
#: contrib/auth/models.py:106
|
||||
#: contrib/auth/models.py:111
|
||||
msgid ""
|
||||
"Designates that this user has all permissions without explicitly assigning "
|
||||
"them."
|
||||
@ -1789,15 +1789,15 @@ msgstr ""
|
||||
"Designa que aquest usuari té tots els permisos sense assignar-los "
|
||||
"explícitament."
|
||||
|
||||
#: contrib/auth/models.py:107
|
||||
#: contrib/auth/models.py:112
|
||||
msgid "last login"
|
||||
msgstr "últim inici de sessió"
|
||||
|
||||
#: contrib/auth/models.py:108
|
||||
#: contrib/auth/models.py:113
|
||||
msgid "date joined"
|
||||
msgstr "data de creació"
|
||||
|
||||
#: contrib/auth/models.py:110
|
||||
#: contrib/auth/models.py:115
|
||||
msgid ""
|
||||
"In addition to the permissions manually assigned, this user will also get "
|
||||
"all permissions granted to each group he/she is in."
|
||||
@ -1805,39 +1805,39 @@ msgstr ""
|
||||
"Junt amb els permissos asignats manualment, aquest usuari tindrà, també, els "
|
||||
"permissos dels grups dels que sigui membre."
|
||||
|
||||
#: contrib/auth/models.py:111
|
||||
#: contrib/auth/models.py:116
|
||||
msgid "user permissions"
|
||||
msgstr "permissos de l'usuari"
|
||||
|
||||
#: contrib/auth/models.py:115
|
||||
#: contrib/auth/models.py:120
|
||||
msgid "user"
|
||||
msgstr "usuari"
|
||||
|
||||
#: contrib/auth/models.py:116
|
||||
#: contrib/auth/models.py:121
|
||||
msgid "users"
|
||||
msgstr "usuaris"
|
||||
|
||||
#: contrib/auth/models.py:122
|
||||
#: contrib/auth/models.py:127
|
||||
msgid "Personal info"
|
||||
msgstr "Informaciò personal"
|
||||
|
||||
#: contrib/auth/models.py:123
|
||||
#: contrib/auth/models.py:128
|
||||
msgid "Permissions"
|
||||
msgstr "permissos"
|
||||
|
||||
#: contrib/auth/models.py:124
|
||||
#: contrib/auth/models.py:129
|
||||
msgid "Important dates"
|
||||
msgstr "Dates importants"
|
||||
|
||||
#: contrib/auth/models.py:125
|
||||
#: contrib/auth/models.py:130
|
||||
msgid "Groups"
|
||||
msgstr "Grups"
|
||||
|
||||
#: contrib/auth/models.py:269
|
||||
#: contrib/auth/models.py:273
|
||||
msgid "message"
|
||||
msgstr "missatge"
|
||||
|
||||
#: contrib/auth/models.py:282
|
||||
#: contrib/auth/models.py:286
|
||||
msgid "AnonymousUser"
|
||||
msgstr "AnonymousUser"
|
||||
|
||||
@ -1861,7 +1861,7 @@ msgstr ""
|
||||
msgid "This account is inactive."
|
||||
msgstr "Aquest compte està inactiu"
|
||||
|
||||
#: contrib/auth/forms.py:85
|
||||
#: contrib/auth/forms.py:84
|
||||
msgid ""
|
||||
"That e-mail address doesn't have an associated user account. Are you sure "
|
||||
"you've registered?"
|
||||
@ -2423,7 +2423,7 @@ msgstr "L'any ha de ser posterior al 1900"
|
||||
msgid "Invalid date: %s"
|
||||
msgstr "Data invàlida: %s"
|
||||
|
||||
#: core/validators.py:149 db/models/fields/__init__.py:463
|
||||
#: core/validators.py:149 db/models/fields/__init__.py:468
|
||||
msgid "Enter a valid date in YYYY-MM-DD format."
|
||||
msgstr "Introdueixi una data vàlida en el forma AAAA-MM-DD."
|
||||
|
||||
@ -2431,7 +2431,7 @@ msgstr "Introdueixi una data vàlida en el forma AAAA-MM-DD."
|
||||
msgid "Enter a valid time in HH:MM format."
|
||||
msgstr "Introdueixi una hora vàlida en el format HH:MM."
|
||||
|
||||
#: core/validators.py:158 db/models/fields/__init__.py:532
|
||||
#: core/validators.py:158 db/models/fields/__init__.py:537
|
||||
msgid "Enter a valid date/time in YYYY-MM-DD HH:MM format."
|
||||
msgstr "Introdueixi un data/hora vàlida en format YYYY-MM-DD HH:MM."
|
||||
|
||||
@ -2558,35 +2558,29 @@ msgstr "Si us plau, introdueixi un número decimal vàlid."
|
||||
#: core/validators.py:423
|
||||
#, 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] ""
|
||||
msgstr ""
|
||||
"Si us plau, introdueixi un número decimal vàlid amb no més de %s digit."
|
||||
msgstr[1] ""
|
||||
"Si us plau, introdueixi un número decimal vàlid amb no més de %s digits."
|
||||
|
||||
#: core/validators.py:426
|
||||
#, 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] ""
|
||||
msgstr ""
|
||||
"Si us plau, introdueixi un número decimal vàlid amb la part entera amb com a "
|
||||
"màxim %s dígit."
|
||||
msgstr[1] ""
|
||||
"Si us plau, introdueixi un número decimal vàlid amb la part entera amb com a "
|
||||
"màxim %s dígits."
|
||||
|
||||
#: core/validators.py:429
|
||||
#, 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] ""
|
||||
msgstr ""
|
||||
"Si us plau, introdueixi un número decimal vàlid amb no més de %s dígit en la "
|
||||
"part decimal."
|
||||
msgstr[1] ""
|
||||
"Si us plau, introdueixi un número decimal vàlid amb no més de %s dígits en "
|
||||
"la part decimal."
|
||||
|
||||
@ -2983,52 +2977,51 @@ msgstr "Ja existeix un %(object)s del tipus %(type)s amb aquest %(field)s."
|
||||
msgid "%(optname)s with this %(fieldname)s already exists."
|
||||
msgstr "Ja existeix %(optname)s amb auqest %(fieldname)s."
|
||||
|
||||
#: db/models/fields/__init__.py:373
|
||||
#: db/models/fields/__init__.py:378
|
||||
msgid "This value must be an integer."
|
||||
msgstr "Aquest valor ha de ser un enter."
|
||||
|
||||
#: db/models/fields/__init__.py:408
|
||||
#: db/models/fields/__init__.py:413
|
||||
msgid "This value must be either True or False."
|
||||
msgstr "Aquest valor ha de ser True (Veritat) o False (Fals)"
|
||||
|
||||
#: db/models/fields/__init__.py:429
|
||||
#: db/models/fields/__init__.py:434
|
||||
msgid "This field cannot be null."
|
||||
msgstr "Aquest camp no pot ser null (estar buit)."
|
||||
|
||||
#: db/models/fields/__init__.py:592
|
||||
#: db/models/fields/__init__.py:597
|
||||
msgid "This value must be a decimal number."
|
||||
msgstr "Aquest valor ha de ser un número decimal."
|
||||
|
||||
#: db/models/fields/__init__.py:695
|
||||
#: db/models/fields/__init__.py:700
|
||||
msgid "Enter a valid filename."
|
||||
msgstr "Introdueixi un nom de fitxer vàlid."
|
||||
|
||||
#: db/models/fields/__init__.py:818
|
||||
#: db/models/fields/__init__.py:824
|
||||
msgid "This value must be either None, True or False."
|
||||
msgstr "Aquest valor ha de ser None (Cap), True (Veritat) o False (Fals)"
|
||||
|
||||
#: db/models/fields/related.py:53
|
||||
#: db/models/fields/related.py:54
|
||||
#, python-format
|
||||
msgid "Please enter a valid %s."
|
||||
msgstr "Si us plau, introdueixi un %s vàlid."
|
||||
|
||||
#: db/models/fields/related.py:642
|
||||
#: db/models/fields/related.py:640
|
||||
msgid "Separate multiple IDs with commas."
|
||||
msgstr "Separi múltiples IDs amb comes."
|
||||
|
||||
#: db/models/fields/related.py:644
|
||||
#: db/models/fields/related.py:642
|
||||
msgid ""
|
||||
"Hold down \"Control\", or \"Command\" on a Mac, to select more than one."
|
||||
msgstr "Premi \"Control\" o \"Command\" en un Mac per escollir més d'un."
|
||||
|
||||
#: db/models/fields/related.py:691
|
||||
#: db/models/fields/related.py:689
|
||||
#, 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] ""
|
||||
msgstr ""
|
||||
"Si us plau, introdueixi IDs de %(self)s vàlids. El valor %(value)r és "
|
||||
"invàlid."
|
||||
msgstr[1] ""
|
||||
"Si us plau, introdueixi IDs de %(self)s vàlids. Els valors %(value)r són "
|
||||
"invàlids."
|
||||
|
||||
|
Binary file not shown.
@ -1,15 +1,14 @@
|
||||
# translation of djangojs.po to
|
||||
# Spanish translation for the django-admin JS files.
|
||||
# Copyright (C)
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# Catalan translation for the django-admin JS files.
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: djangojs\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2007-05-20 18:25+0200\n"
|
||||
"PO-Revision-Date: 2007-05-20 18:24+0200\n"
|
||||
"Last-Translator: Marc Fargas <marc@fargas.com>\n"
|
||||
"PO-Revision-Date: 2007-06-25 17:47+0200\n"
|
||||
"Last-Translator: Marc Fargas <telenieko@telenieko.com>\n"
|
||||
"Language-Team: <es@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -7,25 +7,34 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Django JavaScript Czech translation\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2005-12-17 22:26+0100\n"
|
||||
"PO-Revision-Date: 2006-05-03 12:04+0100\n"
|
||||
"Last-Translator: \n"
|
||||
"POT-Creation-Date: 2007-06-18 11:26+0200\n"
|
||||
"Language-Team: Czech\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\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"
|
||||
"X-Poedit-Language: Czech\n"
|
||||
"X-Poedit-Country: CZECH REPUBLIC\n"
|
||||
"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n>1 && n<5 ? 1 : 2;\n"
|
||||
|
||||
#: 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 ""
|
||||
"Leden Únor Březen Duben Květen Červen Červenec Srpen Září Říjen Listopad "
|
||||
"Prosinec"
|
||||
|
||||
#: contrib/admin/media/js/dateparse.js:33
|
||||
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday"
|
||||
msgstr "Neděle Pondělí Úterý Středa Čtvrtek Pátek Sobota"
|
||||
|
||||
#: contrib/admin/media/js/SelectFilter2.js:33
|
||||
#, perl-format
|
||||
msgid "Available %s"
|
||||
msgstr "K dispozici %s"
|
||||
msgstr "Dostupná %s"
|
||||
|
||||
#: contrib/admin/media/js/SelectFilter2.js:41
|
||||
msgid "Choose all"
|
||||
msgstr "Vybrat vše"
|
||||
msgstr "Vybrat vše"
|
||||
|
||||
#: contrib/admin/media/js/SelectFilter2.js:46
|
||||
msgid "Add"
|
||||
@ -38,75 +47,72 @@ msgstr "Odebrat"
|
||||
#: contrib/admin/media/js/SelectFilter2.js:53
|
||||
#, perl-format
|
||||
msgid "Chosen %s"
|
||||
msgstr "Vybraný %s"
|
||||
msgstr "Vybraná %s"
|
||||
|
||||
#: contrib/admin/media/js/SelectFilter2.js:54
|
||||
msgid "Select your choice(s) and click "
|
||||
msgstr "Vyberte si a klikněte"
|
||||
msgstr "Vyberte si a klikněte "
|
||||
|
||||
#: contrib/admin/media/js/SelectFilter2.js:59
|
||||
msgid "Clear all"
|
||||
msgstr "Vše vymazat"
|
||||
|
||||
#: contrib/admin/media/js/dateparse.js:26
|
||||
#: contrib/admin/media/js/calendar.js:24
|
||||
msgid "January February March April May June July August September October November December"
|
||||
msgstr "Leden Únor Březen Duben Květen Červen Červenec Srpen Září Říjen Listopad Prosinec"
|
||||
|
||||
#: contrib/admin/media/js/dateparse.js:27
|
||||
#, fuzzy
|
||||
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday"
|
||||
msgstr "Neděle Pondělí Úterý Středa Čtvrtek Pátek Sobota"
|
||||
msgstr "Vymazat vše"
|
||||
|
||||
#: contrib/admin/media/js/calendar.js:25
|
||||
#, fuzzy
|
||||
msgid "S M T W T F S"
|
||||
msgstr "N P U S C P S"
|
||||
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:45
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:80
|
||||
#: contrib/admin/media/js/admin/CollapsedFieldsets.js:34
|
||||
#: contrib/admin/media/js/admin/CollapsedFieldsets.js:72
|
||||
msgid "Show"
|
||||
msgstr "Ukázat"
|
||||
|
||||
#: contrib/admin/media/js/admin/CollapsedFieldsets.js:63
|
||||
msgid "Hide"
|
||||
msgstr "Skrýt"
|
||||
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:47
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:81
|
||||
msgid "Now"
|
||||
msgstr "Nyní"
|
||||
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:48
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:51
|
||||
msgid "Clock"
|
||||
msgstr "Hodiny"
|
||||
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:77
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:78
|
||||
msgid "Choose a time"
|
||||
msgstr "Vyberte čas"
|
||||
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:81
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:82
|
||||
msgid "Midnight"
|
||||
msgstr "Půlnoc"
|
||||
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:82
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:83
|
||||
msgid "6 a.m."
|
||||
msgstr "6 ráno"
|
||||
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:83
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:84
|
||||
msgid "Noon"
|
||||
msgstr "Poledne"
|
||||
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:87
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:168
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:88
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:183
|
||||
msgid "Cancel"
|
||||
msgstr "Storno"
|
||||
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:111
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:162
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:128
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:177
|
||||
msgid "Today"
|
||||
msgstr "Dnes"
|
||||
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:114
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:132
|
||||
msgid "Calendar"
|
||||
msgstr "Kalendář"
|
||||
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:160
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:175
|
||||
msgid "Yesterday"
|
||||
msgstr "Včera"
|
||||
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:164
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:179
|
||||
msgid "Tomorrow"
|
||||
msgstr "Zítra"
|
||||
|
||||
|
BIN
django/conf/locale/fa/LC_MESSAGES/django.mo
Normal file
BIN
django/conf/locale/fa/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
2854
django/conf/locale/fa/LC_MESSAGES/django.po
Normal file
2854
django/conf/locale/fa/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
django/conf/locale/fa/LC_MESSAGES/djangojs.mo
Normal file
BIN
django/conf/locale/fa/LC_MESSAGES/djangojs.mo
Normal file
Binary file not shown.
118
django/conf/locale/fa/LC_MESSAGES/djangojs.po
Normal file
118
django/conf/locale/fa/LC_MESSAGES/djangojs.po
Normal file
@ -0,0 +1,118 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2007-06-24 22:09+1000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: contrib/admin/media/js/SelectFilter2.js:33
|
||||
#, perl-format
|
||||
msgid "Available %s"
|
||||
msgstr ""
|
||||
|
||||
#: contrib/admin/media/js/SelectFilter2.js:41
|
||||
msgid "Choose all"
|
||||
msgstr ""
|
||||
|
||||
#: contrib/admin/media/js/SelectFilter2.js:46
|
||||
msgid "Add"
|
||||
msgstr ""
|
||||
|
||||
#: contrib/admin/media/js/SelectFilter2.js:48
|
||||
msgid "Remove"
|
||||
msgstr ""
|
||||
|
||||
#: contrib/admin/media/js/SelectFilter2.js:53
|
||||
#, perl-format
|
||||
msgid "Chosen %s"
|
||||
msgstr ""
|
||||
|
||||
#: contrib/admin/media/js/SelectFilter2.js:54
|
||||
msgid "Select your choice(s) and click "
|
||||
msgstr ""
|
||||
|
||||
#: contrib/admin/media/js/SelectFilter2.js:59
|
||||
msgid "Clear all"
|
||||
msgstr ""
|
||||
|
||||
#: contrib/admin/media/js/dateparse.js:32
|
||||
#: contrib/admin/media/js/calendar.js:24
|
||||
msgid ""
|
||||
"January February March April May June July August September October November "
|
||||
"December"
|
||||
msgstr ""
|
||||
|
||||
#: contrib/admin/media/js/dateparse.js:33
|
||||
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday"
|
||||
msgstr ""
|
||||
|
||||
#: contrib/admin/media/js/calendar.js:25
|
||||
msgid "S M T W T F S"
|
||||
msgstr ""
|
||||
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:47
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:81
|
||||
msgid "Now"
|
||||
msgstr ""
|
||||
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:51
|
||||
msgid "Clock"
|
||||
msgstr ""
|
||||
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:78
|
||||
msgid "Choose a time"
|
||||
msgstr ""
|
||||
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:82
|
||||
msgid "Midnight"
|
||||
msgstr ""
|
||||
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:83
|
||||
msgid "6 a.m."
|
||||
msgstr ""
|
||||
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:84
|
||||
msgid "Noon"
|
||||
msgstr ""
|
||||
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:88
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:183
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:128
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:177
|
||||
msgid "Today"
|
||||
msgstr ""
|
||||
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:132
|
||||
msgid "Calendar"
|
||||
msgstr ""
|
||||
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:175
|
||||
msgid "Yesterday"
|
||||
msgstr ""
|
||||
|
||||
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:179
|
||||
msgid "Tomorrow"
|
||||
msgstr ""
|
||||
|
||||
#: contrib/admin/media/js/admin/CollapsedFieldsets.js:34
|
||||
#: contrib/admin/media/js/admin/CollapsedFieldsets.js:72
|
||||
msgid "Show"
|
||||
msgstr ""
|
||||
|
||||
#: contrib/admin/media/js/admin/CollapsedFieldsets.js:63
|
||||
msgid "Hide"
|
||||
msgstr ""
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -28,7 +28,8 @@ msgstr "To pole jest wymagane."
|
||||
msgid "Ensure your text is less than %s character."
|
||||
msgid_plural "Ensure your text is less than %s characters."
|
||||
msgstr[0] "Upewnij się, że tekst ma mniej niż %s znak."
|
||||
msgstr[1] "Upewnij się, że tekst ma mniej niż %s znaków."
|
||||
msgstr[1] "Upewnij się, że tekst ma mniej niż %s znaki."
|
||||
msgstr[2] "Upewnij się, że tekst ma mniej niż %s znaków."
|
||||
|
||||
#: oldforms/__init__.py:397
|
||||
msgid "Line breaks are not allowed here."
|
||||
@ -75,7 +76,7 @@ msgid "Enter a whole number between 0 and 32,767."
|
||||
msgstr "Proszę wpisać liczbę całkowitą z zakresu od 0 do 32 767"
|
||||
|
||||
#: db/models/manipulators.py:307
|
||||
#, fuzzy, python-format
|
||||
#, python-format
|
||||
msgid "%(object)s with this %(type)s already exists for the given %(field)s."
|
||||
msgstr "%(object)s z %(type)s już istnieje dla %(field)s."
|
||||
|
||||
@ -85,7 +86,7 @@ msgid "and"
|
||||
msgstr "i"
|
||||
|
||||
#: db/models/fields/__init__.py:42
|
||||
#, fuzzy, python-format
|
||||
#, python-format
|
||||
msgid "%(optname)s with this %(fieldname)s already exists."
|
||||
msgstr "Już istnieje %(optname)s z %(fieldname)s."
|
||||
|
||||
@ -142,6 +143,9 @@ msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
"Proszę podać poprawne identyfikatory %(self)s. Wartości %(value)r są "
|
||||
"niepoprawne."
|
||||
msgstr[2] ""
|
||||
"Proszę podać poprawne identyfikatory %(self)s. Wartości %(value)r są "
|
||||
"niepoprawne."
|
||||
|
||||
#: conf/global_settings.py:39
|
||||
msgid "Arabic"
|
||||
@ -370,7 +374,7 @@ msgstr "Niepoprawna data: %s"
|
||||
|
||||
#: core/validators.py:153
|
||||
msgid "Enter a valid time in HH:MM format."
|
||||
msgstr "Proszę wpisać poprawną godzinę w formacie GG:MM."
|
||||
msgstr "Proszę wpisać poprawną godzinę w formacie HH:MM."
|
||||
|
||||
#: core/validators.py:162 newforms/fields.py:271
|
||||
msgid "Enter a valid e-mail address."
|
||||
@ -439,6 +443,7 @@ 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] "Nie wolno przeklinać! Słowo %s nie jest dozwolone."
|
||||
msgstr[1] "Nie wolno przeklinać! Słowa %s nie są dozwolone."
|
||||
msgstr[2] "Nie wolno przeklinać! Słowa %s nie są dozwolone."
|
||||
|
||||
#: core/validators.py:273
|
||||
#, python-format
|
||||
@ -497,13 +502,15 @@ 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] "Proszę wpisać poprawną liczbę dziesiętną o nie więcej niż %s cyfrze."
|
||||
msgstr[1] "Proszę wpisać poprawną liczbę dziesiętną o nie więcej niż %s cyfrach."
|
||||
msgstr[2] "Proszę wpisać poprawną liczbę dziesiętną o nie więcej niż %s cyfrach."
|
||||
|
||||
#: core/validators.py:425
|
||||
#, 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] "Proszę wpisać poprawną liczbę dziesiętną zawierającą nie więcej niż %s cyfry."
|
||||
msgstr[1] "Proszę wpisać poprawną liczbę dziesiętną zawierającą nie więcej niż %s cyfr."
|
||||
msgstr[0] "Proszę wpisać poprawną liczbę dziesiętną zawierającą nie więcej niż %s cyfrę."
|
||||
msgstr[1] "Proszę wpisać poprawną liczbę dziesiętną zawierającą nie więcej niż %s cyfry."
|
||||
msgstr[2] "Proszę wpisać poprawną liczbę dziesiętną zawierającą nie więcej niż %s cyfr."
|
||||
|
||||
#: core/validators.py:428
|
||||
#, python-format
|
||||
@ -515,6 +522,9 @@ msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
"Proszę wpisać poprawną liczbę dziesiętną z dokładnością do %s miejsc po "
|
||||
"przecinku."
|
||||
msgstr[2] ""
|
||||
"Proszę wpisać poprawną liczbę dziesiętną z dokładnością do %s miejsc po "
|
||||
"przecinku."
|
||||
|
||||
#: core/validators.py:438
|
||||
#, python-format
|
||||
@ -668,7 +678,7 @@ msgstr "Wpisz poprawny URL."
|
||||
|
||||
#: newforms/fields.py:313
|
||||
msgid "This URL appears to be a broken link."
|
||||
msgstr "Odnośnik %s jest nieprawidłowy."
|
||||
msgstr "Ten odnośnik jest nieprawidłowy."
|
||||
|
||||
#: contrib/humanize/templatetags/humanize.py:17
|
||||
msgid "th"
|
||||
@ -691,21 +701,24 @@ msgstr "-ci"
|
||||
msgid "%(value).1f million"
|
||||
msgid_plural "%(value).1f million"
|
||||
msgstr[0] "%(value).1f milion"
|
||||
msgstr[1] "%(value).1f milionów"
|
||||
msgstr[1] "%(value).1f miliony"
|
||||
msgstr[2] "%(value).1f milionów"
|
||||
|
||||
#: contrib/humanize/templatetags/humanize.py:50
|
||||
#, python-format
|
||||
msgid "%(value).1f billion"
|
||||
msgid_plural "%(value).1f billion"
|
||||
msgstr[0] "%(value).1f miliard"
|
||||
msgstr[1] "%(value).1f miliardów"
|
||||
msgstr[1] "%(value).1f miliardy"
|
||||
msgstr[2] "%(value).1f miliardów"
|
||||
|
||||
#: contrib/humanize/templatetags/humanize.py:53
|
||||
#, python-format
|
||||
msgid "%(value).1f trillion"
|
||||
msgid_plural "%(value).1f trillion"
|
||||
msgstr[0] "%(value).1f bilion"
|
||||
msgstr[1] "%(value).1f bilionów"
|
||||
msgstr[1] "%(value).1f biliony"
|
||||
msgstr[2] "%(value).1f bilionów"
|
||||
|
||||
#: contrib/humanize/templatetags/humanize.py:68
|
||||
msgid "one"
|
||||
@ -987,6 +1000,15 @@ msgstr[0] ""
|
||||
"\n"
|
||||
"%(text)s"
|
||||
msgstr[1] ""
|
||||
"Ten komentarz został wysłany przez użytkownika, który wysłał mniej niż %"
|
||||
"(count)s komentarze:\n"
|
||||
"\n"
|
||||
"%(text)s"
|
||||
msgstr[2] ""
|
||||
"Ten komentarz został wysłany przez użytkownika, który wysłał mniej niż %"
|
||||
"(count)s komentarzy:\n"
|
||||
"\n"
|
||||
"%(text)s"
|
||||
|
||||
#: contrib/comments/views/comments.py:116
|
||||
#, python-format
|
||||
@ -1574,7 +1596,8 @@ msgstr "Szukaj"
|
||||
msgid "1 result"
|
||||
msgid_plural "%(counter)s results"
|
||||
msgstr[0] "1 wynik"
|
||||
msgstr[1] "%(counter)s wyników"
|
||||
msgstr[1] "%(counter)s wyniki"
|
||||
msgstr[2] "%(counter)s wyników"
|
||||
|
||||
#: contrib/admin/templates/admin/search_form.html:10
|
||||
#, python-format
|
||||
@ -1681,6 +1704,7 @@ msgid "Please correct the error below."
|
||||
msgid_plural "Please correct the errors below."
|
||||
msgstr[0] "Proszę popraw poniższy błąd"
|
||||
msgstr[1] "Proszę popraw poniższe błędy"
|
||||
msgstr[2] "Proszę popraw poniższe błędy"
|
||||
|
||||
#: contrib/admin/templates/admin/change_form.html:50
|
||||
msgid "Ordering"
|
||||
@ -2077,7 +2101,6 @@ msgstr ""
|
||||
msgid "user permissions"
|
||||
msgstr "uprawnienia użytkownika"
|
||||
|
||||
# kurwa
|
||||
#: contrib/auth/models.py:115
|
||||
msgid "user"
|
||||
msgstr "użytkownik"
|
||||
@ -2831,37 +2854,43 @@ msgstr "Gru."
|
||||
msgid "year"
|
||||
msgid_plural "years"
|
||||
msgstr[0] "rok"
|
||||
msgstr[1] "lat"
|
||||
msgstr[1] "lata"
|
||||
msgstr[2] "lat"
|
||||
|
||||
#: utils/timesince.py:13
|
||||
msgid "month"
|
||||
msgid_plural "months"
|
||||
msgstr[0] "miesiąc"
|
||||
msgstr[1] "miesięcy"
|
||||
msgstr[1] "miesięce"
|
||||
msgstr[2] "miesięcy"
|
||||
|
||||
#: utils/timesince.py:14
|
||||
msgid "week"
|
||||
msgid_plural "weeks"
|
||||
msgstr[0] "tydzień"
|
||||
msgstr[1] "tygodni"
|
||||
msgstr[1] "tygodnie"
|
||||
msgstr[2] "tygodni"
|
||||
|
||||
#: utils/timesince.py:15
|
||||
msgid "day"
|
||||
msgid_plural "days"
|
||||
msgstr[0] "dzień"
|
||||
msgstr[1] "dni"
|
||||
msgstr[2] "dni"
|
||||
|
||||
#: utils/timesince.py:16
|
||||
msgid "hour"
|
||||
msgid_plural "hours"
|
||||
msgstr[0] "godzina"
|
||||
msgstr[1] "godzin"
|
||||
msgstr[1] "godziny"
|
||||
msgstr[2] "godzin"
|
||||
|
||||
#: utils/timesince.py:17
|
||||
msgid "minute"
|
||||
msgid_plural "minutes"
|
||||
msgstr[0] "minuta"
|
||||
msgstr[1] "minut"
|
||||
msgstr[1] "minuty"
|
||||
msgstr[2] "minut"
|
||||
|
||||
#: utils/timesince.py:40
|
||||
#, python-format
|
||||
@ -2880,7 +2909,7 @@ msgstr ", %(number)d %(type)s"
|
||||
|
||||
#: utils/dateformat.py:40
|
||||
msgid "p.m."
|
||||
msgstr "popołudniu"
|
||||
msgstr "po południu"
|
||||
|
||||
#: utils/dateformat.py:41
|
||||
msgid "a.m."
|
||||
@ -2888,7 +2917,7 @@ msgstr "rano"
|
||||
|
||||
#: utils/dateformat.py:46
|
||||
msgid "PM"
|
||||
msgstr "popołudniu"
|
||||
msgstr "po południu"
|
||||
|
||||
#: utils/dateformat.py:47
|
||||
msgid "AM"
|
||||
@ -2931,13 +2960,13 @@ msgstr "tak,nie,może"
|
||||
msgid "%(size)d byte"
|
||||
msgid_plural "%(size)d bytes"
|
||||
msgstr[0] "%(size)d bajt"
|
||||
msgstr[1] "%(size)d bajtów"
|
||||
msgstr[2] ""
|
||||
msgstr[1] "%(size)d bajty"
|
||||
msgstr[2] "%(size)d bajtów"
|
||||
|
||||
#: template/defaultfilters.py:522
|
||||
#, python-format
|
||||
msgid "%.1f KB"
|
||||
msgstr "%.1f kB"
|
||||
msgstr "%.1f KB"
|
||||
|
||||
#: template/defaultfilters.py:524
|
||||
#, python-format
|
||||
|
Binary file not shown.
@ -40,7 +40,6 @@ msgid "Chosen %s"
|
||||
msgstr "Wybrano %s"
|
||||
|
||||
#: contrib/admin/media/js/SelectFilter2.js:54
|
||||
#, fuzzy
|
||||
msgid "Select your choice(s) and click "
|
||||
msgstr "Zaznacz swój wybór i kliknij "
|
||||
|
||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -30,7 +30,12 @@ function dismissRelatedLookupPopup(win, chosenId) {
|
||||
function showAddAnotherPopup(triggeringLink) {
|
||||
var name = triggeringLink.id.replace(/^add_/, '');
|
||||
name = name.replace(/\./g, '___');
|
||||
var win = window.open(triggeringLink.href + '?_popup=1', name, 'height=500,width=800,resizable=yes,scrollbars=yes');
|
||||
href = triggeringLink.href
|
||||
if (href.indexOf('?') == -1)
|
||||
href += '?_popup=1';
|
||||
else
|
||||
href += '&_popup=1';
|
||||
var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
|
||||
win.focus();
|
||||
return false;
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ DELETION = 3
|
||||
|
||||
class LogEntryManager(models.Manager):
|
||||
def log_action(self, user_id, content_type_id, object_id, object_repr, action_flag, change_message=''):
|
||||
e = self.model(None, None, user_id, content_type_id, object_id, object_repr[:200], action_flag, change_message)
|
||||
e = self.model(None, None, user_id, content_type_id, str(object_id), object_repr[:200], action_flag, change_message)
|
||||
e.save()
|
||||
|
||||
class LogEntry(models.Model):
|
||||
|
@ -94,15 +94,15 @@ class FieldWidgetNode(template.Node):
|
||||
return cls.nodelists[klass]
|
||||
get_nodelist = classmethod(get_nodelist)
|
||||
|
||||
def iter_render(self, context):
|
||||
def render(self, context):
|
||||
bound_field = template.resolve_variable(self.bound_field_var, context)
|
||||
|
||||
context.push()
|
||||
context['bound_field'] = bound_field
|
||||
|
||||
for chunk in self.get_nodelist(bound_field.field.__class__).iter_render(context):
|
||||
yield chunk
|
||||
output = self.get_nodelist(bound_field.field.__class__).render(context)
|
||||
context.pop()
|
||||
return output
|
||||
|
||||
class FieldWrapper(object):
|
||||
def __init__(self, field ):
|
||||
@ -157,7 +157,7 @@ class EditInlineNode(template.Node):
|
||||
def __init__(self, rel_var):
|
||||
self.rel_var = rel_var
|
||||
|
||||
def iter_render(self, context):
|
||||
def render(self, context):
|
||||
relation = template.resolve_variable(self.rel_var, context)
|
||||
context.push()
|
||||
if relation.field.rel.edit_inline == models.TABULAR:
|
||||
@ -169,9 +169,10 @@ class EditInlineNode(template.Node):
|
||||
original = context.get('original', None)
|
||||
bound_related_object = relation.bind(context['form'], original, bound_related_object_class)
|
||||
context['bound_related_object'] = bound_related_object
|
||||
for chunk in loader.get_template(bound_related_object.template_name()).iter_render(context):
|
||||
yield chunk
|
||||
t = loader.get_template(bound_related_object.template_name())
|
||||
output = t.render(context)
|
||||
context.pop()
|
||||
return output
|
||||
|
||||
def output_all(form_fields):
|
||||
return ''.join([str(f) for f in form_fields])
|
||||
|
@ -7,7 +7,7 @@ class AdminApplistNode(template.Node):
|
||||
def __init__(self, varname):
|
||||
self.varname = varname
|
||||
|
||||
def iter_render(self, context):
|
||||
def render(self, context):
|
||||
from django.db import models
|
||||
from django.utils.text import capfirst
|
||||
app_list = []
|
||||
@ -54,7 +54,7 @@ class AdminApplistNode(template.Node):
|
||||
'models': model_list,
|
||||
})
|
||||
context[self.varname] = app_list
|
||||
return ()
|
||||
return ''
|
||||
|
||||
def get_admin_app_list(parser, token):
|
||||
"""
|
||||
|
@ -10,14 +10,14 @@ class AdminLogNode(template.Node):
|
||||
def __repr__(self):
|
||||
return "<GetAdminLog Node>"
|
||||
|
||||
def iter_render(self, context):
|
||||
def render(self, context):
|
||||
if self.user is None:
|
||||
context[self.varname] = LogEntry.objects.all().select_related()[:self.limit]
|
||||
else:
|
||||
if not self.user.isdigit():
|
||||
self.user = context[self.user].id
|
||||
context[self.varname] = LogEntry.objects.filter(user__id__exact=self.user).select_related()[:self.limit]
|
||||
return ()
|
||||
return ''
|
||||
|
||||
class DoGetAdminLog:
|
||||
"""
|
||||
|
@ -14,6 +14,11 @@ from django.utils.html import escape
|
||||
from django.utils.text import capfirst, get_text_list
|
||||
import operator
|
||||
|
||||
try:
|
||||
set
|
||||
except NameError:
|
||||
from sets import Set as set # Python 2.3 fallback
|
||||
|
||||
from django.contrib.admin.models import LogEntry, ADDITION, CHANGE, DELETION
|
||||
if not LogEntry._meta.installed:
|
||||
raise ImproperlyConfigured, "You'll need to put 'django.contrib.admin' in your INSTALLED_APPS setting before you can use the admin application."
|
||||
@ -489,7 +494,6 @@ def _get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current
|
||||
perms_needed.add(related.opts.verbose_name)
|
||||
|
||||
def delete_stage(request, app_label, model_name, object_id):
|
||||
import sets
|
||||
model = models.get_model(app_label, model_name)
|
||||
object_id = unquote(object_id)
|
||||
if model is None:
|
||||
@ -502,7 +506,7 @@ def delete_stage(request, app_label, model_name, object_id):
|
||||
# Populate deleted_objects, a data structure of all related objects that
|
||||
# will also be deleted.
|
||||
deleted_objects = ['%s: <a href="../../%s/">%s</a>' % (capfirst(opts.verbose_name), object_id, escape(str(obj))), []]
|
||||
perms_needed = sets.Set()
|
||||
perms_needed = set()
|
||||
_get_deleted_objects(deleted_objects, perms_needed, request.user, obj, opts, 1)
|
||||
|
||||
if request.POST: # The user has already confirmed the deletion.
|
||||
|
@ -79,17 +79,17 @@ class PasswordResetForm(oldforms.Manipulator):
|
||||
|
||||
def isValidUserEmail(self, new_data, all_data):
|
||||
"Validates that a user exists with the given e-mail address"
|
||||
try:
|
||||
self.user_cache = User.objects.get(email__iexact=new_data)
|
||||
except User.DoesNotExist:
|
||||
self.users_cache = list(User.objects.filter(email__iexact=new_data))
|
||||
if len(self.users_cache) == 0:
|
||||
raise validators.ValidationError, _("That e-mail address doesn't have an associated user account. Are you sure you've registered?")
|
||||
|
||||
def save(self, domain_override=None, email_template_name='registration/password_reset_email.html'):
|
||||
"Calculates a new password randomly and sends it to the user"
|
||||
from django.core.mail import send_mail
|
||||
for user in self.users_cache:
|
||||
new_pass = User.objects.make_random_password()
|
||||
self.user_cache.set_password(new_pass)
|
||||
self.user_cache.save()
|
||||
user.set_password(new_pass)
|
||||
user.save()
|
||||
if not domain_override:
|
||||
current_site = Site.objects.get_current()
|
||||
site_name = current_site.name
|
||||
@ -99,12 +99,12 @@ class PasswordResetForm(oldforms.Manipulator):
|
||||
t = loader.get_template(email_template_name)
|
||||
c = {
|
||||
'new_password': new_pass,
|
||||
'email': self.user_cache.email,
|
||||
'email': user.email,
|
||||
'domain': domain,
|
||||
'site_name': site_name,
|
||||
'user': self.user_cache,
|
||||
'user': user,
|
||||
}
|
||||
send_mail('Password reset on %s' % site_name, t.render(Context(c)), None, [self.user_cache.email])
|
||||
send_mail('Password reset on %s' % site_name, t.render(Context(c)), None, [user.email])
|
||||
|
||||
class PasswordChangeForm(oldforms.Manipulator):
|
||||
"A form that lets a user change his password."
|
||||
|
@ -5,6 +5,11 @@ from django.contrib.contenttypes.models import ContentType
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
import datetime
|
||||
|
||||
try:
|
||||
set
|
||||
except NameError:
|
||||
from sets import Set as set # Python 2.3 fallback
|
||||
|
||||
def check_password(raw_password, enc_password):
|
||||
"""
|
||||
Returns a boolean of whether the raw_password was correct. Handles
|
||||
@ -175,7 +180,6 @@ class User(models.Model):
|
||||
def get_group_permissions(self):
|
||||
"Returns a list of permission strings that this user has through his/her groups."
|
||||
if not hasattr(self, '_group_perm_cache'):
|
||||
import sets
|
||||
cursor = connection.cursor()
|
||||
# The SQL below works out to the following, after DB quoting:
|
||||
# cursor.execute("""
|
||||
@ -200,13 +204,12 @@ class User(models.Model):
|
||||
backend.quote_name('id'), backend.quote_name('content_type_id'),
|
||||
backend.quote_name('user_id'),)
|
||||
cursor.execute(sql, [self.id])
|
||||
self._group_perm_cache = sets.Set(["%s.%s" % (row[0], row[1]) for row in cursor.fetchall()])
|
||||
self._group_perm_cache = set(["%s.%s" % (row[0], row[1]) for row in cursor.fetchall()])
|
||||
return self._group_perm_cache
|
||||
|
||||
def get_all_permissions(self):
|
||||
if not hasattr(self, '_perm_cache'):
|
||||
import sets
|
||||
self._perm_cache = sets.Set(["%s.%s" % (p.content_type.app_label, p.codename) for p in self.user_permissions.select_related()])
|
||||
self._perm_cache = set(["%s.%s" % (p.content_type.app_label, p.codename) for p in self.user_permissions.select_related()])
|
||||
self._perm_cache.update(self.get_group_permissions())
|
||||
return self._perm_cache
|
||||
|
||||
|
@ -24,7 +24,7 @@ class CommentFormNode(template.Node):
|
||||
self.photo_options, self.rating_options = photo_options, rating_options
|
||||
self.is_public = is_public
|
||||
|
||||
def iter_render(self, context):
|
||||
def render(self, context):
|
||||
from django.conf import settings
|
||||
from django.utils.text import normalize_newlines
|
||||
import base64
|
||||
@ -33,7 +33,7 @@ class CommentFormNode(template.Node):
|
||||
try:
|
||||
self.obj_id = template.resolve_variable(self.obj_id_lookup_var, context)
|
||||
except template.VariableDoesNotExist:
|
||||
return
|
||||
return ''
|
||||
# Validate that this object ID is valid for this content-type.
|
||||
# We only have to do this validation if obj_id_lookup_var is provided,
|
||||
# because do_comment_form() validates hard-coded object IDs.
|
||||
@ -67,9 +67,9 @@ class CommentFormNode(template.Node):
|
||||
context['hash'] = Comment.objects.get_security_hash(context['options'], context['photo_options'], context['rating_options'], context['target'])
|
||||
context['logout_url'] = settings.LOGOUT_URL
|
||||
default_form = loader.get_template(COMMENT_FORM)
|
||||
for chunk in default_form.iter_render(context):
|
||||
yield chunk
|
||||
output = default_form.render(context)
|
||||
context.pop()
|
||||
return output
|
||||
|
||||
class CommentCountNode(template.Node):
|
||||
def __init__(self, package, module, context_var_name, obj_id, var_name, free):
|
||||
@ -77,7 +77,7 @@ class CommentCountNode(template.Node):
|
||||
self.context_var_name, self.obj_id = context_var_name, obj_id
|
||||
self.var_name, self.free = var_name, free
|
||||
|
||||
def iter_render(self, context):
|
||||
def render(self, context):
|
||||
from django.conf import settings
|
||||
manager = self.free and FreeComment.objects or Comment.objects
|
||||
if self.context_var_name is not None:
|
||||
@ -86,7 +86,7 @@ class CommentCountNode(template.Node):
|
||||
content_type__app_label__exact=self.package,
|
||||
content_type__model__exact=self.module, site__id__exact=settings.SITE_ID).count()
|
||||
context[self.var_name] = comment_count
|
||||
return ()
|
||||
return ''
|
||||
|
||||
class CommentListNode(template.Node):
|
||||
def __init__(self, package, module, context_var_name, obj_id, var_name, free, ordering, extra_kwargs=None):
|
||||
@ -96,14 +96,14 @@ class CommentListNode(template.Node):
|
||||
self.ordering = ordering
|
||||
self.extra_kwargs = extra_kwargs or {}
|
||||
|
||||
def iter_render(self, context):
|
||||
def render(self, context):
|
||||
from django.conf import settings
|
||||
get_list_function = self.free and FreeComment.objects.filter or Comment.objects.get_list_with_karma
|
||||
if self.context_var_name is not None:
|
||||
try:
|
||||
self.obj_id = template.resolve_variable(self.context_var_name, context)
|
||||
except template.VariableDoesNotExist:
|
||||
return ()
|
||||
return ''
|
||||
kwargs = {
|
||||
'object_id__exact': self.obj_id,
|
||||
'content_type__app_label__exact': self.package,
|
||||
@ -127,7 +127,7 @@ class CommentListNode(template.Node):
|
||||
comment_list = [c for c in comment_list if not c.is_hidden or (user_id == c.user_id)]
|
||||
|
||||
context[self.var_name] = comment_list
|
||||
return ()
|
||||
return ''
|
||||
|
||||
class DoCommentForm:
|
||||
"""
|
||||
|
@ -32,10 +32,10 @@
|
||||
<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="quiet">(None)</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% endblock %}
|
||||
|
@ -19,7 +19,7 @@ class RedirectFallbackMiddleware(object):
|
||||
except Redirect.DoesNotExist:
|
||||
pass
|
||||
if r is not None:
|
||||
if r == '':
|
||||
if r.new_path == '':
|
||||
return http.HttpResponseGone()
|
||||
return http.HttpResponsePermanentRedirect(r.new_path)
|
||||
|
||||
|
@ -7,9 +7,10 @@ from optparse import OptionParser
|
||||
from django.utils import termcolors
|
||||
import os, re, shutil, sys, textwrap
|
||||
|
||||
# For Python 2.3
|
||||
if not hasattr(__builtins__, 'set'):
|
||||
from sets import Set as set
|
||||
try:
|
||||
set
|
||||
except NameError:
|
||||
from sets import Set as set # Python 2.3 fallback
|
||||
|
||||
# For backwards compatibility: get_version() used to be in this module.
|
||||
get_version = django.get_version
|
||||
@ -58,12 +59,16 @@ def _is_valid_dir_name(s):
|
||||
|
||||
def _get_installed_models(table_list):
|
||||
"Gets a set of all models that are installed, given a list of existing tables"
|
||||
from django.db import models
|
||||
from django.db import backend, models
|
||||
all_models = []
|
||||
for app in models.get_apps():
|
||||
for model in models.get_models(app):
|
||||
all_models.append(model)
|
||||
return set([m for m in all_models if m._meta.db_table in table_list])
|
||||
if backend.uses_case_insensitive_names:
|
||||
converter = str.upper
|
||||
else:
|
||||
converter = lambda x: x
|
||||
return set([m for m in all_models if converter(m._meta.db_table) in map(converter, table_list)])
|
||||
|
||||
def _get_table_list():
|
||||
"Gets a list of all db tables that are physically installed."
|
||||
@ -99,6 +104,7 @@ get_rel_data_type = lambda f: (f.get_internal_type() in ('AutoField', 'PositiveI
|
||||
def get_sql_create(app):
|
||||
"Returns a list of the CREATE TABLE SQL statements for the given app."
|
||||
from django.db import get_creation_module, models
|
||||
|
||||
data_types = get_creation_module().DATA_TYPES
|
||||
|
||||
if not data_types:
|
||||
@ -170,15 +176,20 @@ def _get_sql_model_create(model, known_models=set()):
|
||||
rel_field = f
|
||||
data_type = f.get_internal_type()
|
||||
col_type = data_types[data_type]
|
||||
tablespace = f.db_tablespace or opts.db_tablespace
|
||||
if col_type is not None:
|
||||
# Make the definition (e.g. 'foo VARCHAR(30)') for this field.
|
||||
field_output = [style.SQL_FIELD(backend.quote_name(f.column)),
|
||||
style.SQL_COLTYPE(col_type % rel_field.__dict__)]
|
||||
field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or '')))
|
||||
if f.unique:
|
||||
if f.unique and (not f.primary_key or backend.allows_unique_and_pk):
|
||||
field_output.append(style.SQL_KEYWORD('UNIQUE'))
|
||||
if f.primary_key:
|
||||
field_output.append(style.SQL_KEYWORD('PRIMARY KEY'))
|
||||
if tablespace and backend.supports_tablespaces and (f.unique or f.primary_key) and backend.autoindexes_primary_keys:
|
||||
# We must specify the index tablespace inline, because we
|
||||
# won't be generating a CREATE INDEX statement for this field.
|
||||
field_output.append(backend.get_tablespace_sql(tablespace, inline=True))
|
||||
if f.rel:
|
||||
if f.rel.to in known_models:
|
||||
field_output.append(style.SQL_KEYWORD('REFERENCES') + ' ' + \
|
||||
@ -202,9 +213,19 @@ def _get_sql_model_create(model, known_models=set()):
|
||||
full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(backend.quote_name(opts.db_table)) + ' (']
|
||||
for i, line in enumerate(table_output): # Combine and add commas.
|
||||
full_statement.append(' %s%s' % (line, i < len(table_output)-1 and ',' or ''))
|
||||
full_statement.append(');')
|
||||
full_statement.append(')')
|
||||
if opts.db_tablespace and backend.supports_tablespaces:
|
||||
full_statement.append(backend.get_tablespace_sql(opts.db_tablespace))
|
||||
full_statement.append(';')
|
||||
final_output.append('\n'.join(full_statement))
|
||||
|
||||
if opts.has_auto_field and hasattr(backend, 'get_autoinc_sql'):
|
||||
# Add any extra SQL needed to support auto-incrementing primary keys
|
||||
autoinc_sql = backend.get_autoinc_sql(opts.db_table)
|
||||
if autoinc_sql:
|
||||
for stmt in autoinc_sql:
|
||||
final_output.append(stmt)
|
||||
|
||||
return final_output, pending_references
|
||||
|
||||
def _get_sql_for_pending_references(model, pending_references):
|
||||
@ -212,6 +233,7 @@ def _get_sql_for_pending_references(model, pending_references):
|
||||
Get any ALTER TABLE statements to add constraints after the fact.
|
||||
"""
|
||||
from django.db import backend, get_creation_module
|
||||
from django.db.backends.util import truncate_name
|
||||
data_types = get_creation_module().DATA_TYPES
|
||||
|
||||
final_output = []
|
||||
@ -228,7 +250,7 @@ def _get_sql_for_pending_references(model, pending_references):
|
||||
# So we are careful with character usage here.
|
||||
r_name = '%s_refs_%s_%x' % (r_col, col, abs(hash((r_table, table))))
|
||||
final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \
|
||||
(backend.quote_name(r_table), r_name,
|
||||
(backend.quote_name(r_table), truncate_name(r_name, backend.get_max_name_length()),
|
||||
backend.quote_name(r_col), backend.quote_name(table), backend.quote_name(col),
|
||||
backend.get_deferrable_sql()))
|
||||
del pending_references[model]
|
||||
@ -244,12 +266,18 @@ def _get_many_to_many_sql_for_model(model):
|
||||
final_output = []
|
||||
for f in opts.many_to_many:
|
||||
if not isinstance(f.rel, generic.GenericRel):
|
||||
tablespace = f.db_tablespace or opts.db_tablespace
|
||||
if tablespace and backend.supports_tablespaces and backend.autoindexes_primary_keys:
|
||||
tablespace_sql = ' ' + backend.get_tablespace_sql(tablespace, inline=True)
|
||||
else:
|
||||
tablespace_sql = ''
|
||||
table_output = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + \
|
||||
style.SQL_TABLE(backend.quote_name(f.m2m_db_table())) + ' (']
|
||||
table_output.append(' %s %s %s,' % \
|
||||
table_output.append(' %s %s %s%s,' % \
|
||||
(style.SQL_FIELD(backend.quote_name('id')),
|
||||
style.SQL_COLTYPE(data_types['AutoField']),
|
||||
style.SQL_KEYWORD('NOT NULL PRIMARY KEY')))
|
||||
style.SQL_KEYWORD('NOT NULL PRIMARY KEY'),
|
||||
tablespace_sql))
|
||||
table_output.append(' %s %s %s %s (%s)%s,' % \
|
||||
(style.SQL_FIELD(backend.quote_name(f.m2m_column_name())),
|
||||
style.SQL_COLTYPE(data_types[get_rel_data_type(opts.pk)] % opts.pk.__dict__),
|
||||
@ -264,17 +292,30 @@ def _get_many_to_many_sql_for_model(model):
|
||||
style.SQL_TABLE(backend.quote_name(f.rel.to._meta.db_table)),
|
||||
style.SQL_FIELD(backend.quote_name(f.rel.to._meta.pk.column)),
|
||||
backend.get_deferrable_sql()))
|
||||
table_output.append(' %s (%s, %s)' % \
|
||||
table_output.append(' %s (%s, %s)%s' % \
|
||||
(style.SQL_KEYWORD('UNIQUE'),
|
||||
style.SQL_FIELD(backend.quote_name(f.m2m_column_name())),
|
||||
style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name()))))
|
||||
table_output.append(');')
|
||||
style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name())),
|
||||
tablespace_sql))
|
||||
table_output.append(')')
|
||||
if opts.db_tablespace and backend.supports_tablespaces:
|
||||
# f.db_tablespace is only for indices, so ignore its value here.
|
||||
table_output.append(backend.get_tablespace_sql(opts.db_tablespace))
|
||||
table_output.append(';')
|
||||
final_output.append('\n'.join(table_output))
|
||||
|
||||
# Add any extra SQL needed to support auto-incrementing PKs
|
||||
autoinc_sql = backend.get_autoinc_sql(f.m2m_db_table())
|
||||
if autoinc_sql:
|
||||
for stmt in autoinc_sql:
|
||||
final_output.append(stmt)
|
||||
|
||||
return final_output
|
||||
|
||||
def get_sql_delete(app):
|
||||
"Returns a list of the DROP TABLE SQL statements for the given app."
|
||||
from django.db import backend, connection, models, get_introspection_module
|
||||
from django.db.backends.util import truncate_name
|
||||
introspection = get_introspection_module()
|
||||
|
||||
# This should work even if a connection isn't available
|
||||
@ -288,6 +329,10 @@ def get_sql_delete(app):
|
||||
table_names = introspection.get_table_list(cursor)
|
||||
else:
|
||||
table_names = []
|
||||
if backend.uses_case_insensitive_names:
|
||||
table_name_converter = str.upper
|
||||
else:
|
||||
table_name_converter = lambda x: x
|
||||
|
||||
output = []
|
||||
|
||||
@ -297,7 +342,7 @@ def get_sql_delete(app):
|
||||
references_to_delete = {}
|
||||
app_models = models.get_models(app)
|
||||
for model in app_models:
|
||||
if cursor and model._meta.db_table in table_names:
|
||||
if cursor and table_name_converter(model._meta.db_table) in table_names:
|
||||
# The table exists, so it needs to be dropped
|
||||
opts = model._meta
|
||||
for f in opts.fields:
|
||||
@ -307,7 +352,7 @@ def get_sql_delete(app):
|
||||
to_delete.add(model)
|
||||
|
||||
for model in app_models:
|
||||
if cursor and model._meta.db_table in table_names:
|
||||
if cursor and table_name_converter(model._meta.db_table) in table_names:
|
||||
# Drop the table now
|
||||
output.append('%s %s;' % (style.SQL_KEYWORD('DROP TABLE'),
|
||||
style.SQL_TABLE(backend.quote_name(model._meta.db_table))))
|
||||
@ -317,20 +362,26 @@ def get_sql_delete(app):
|
||||
col = f.column
|
||||
r_table = model._meta.db_table
|
||||
r_col = model._meta.get_field(f.rel.field_name).column
|
||||
r_name = '%s_refs_%s_%x' % (col, r_col, abs(hash((table, r_table))))
|
||||
output.append('%s %s %s %s;' % \
|
||||
(style.SQL_KEYWORD('ALTER TABLE'),
|
||||
style.SQL_TABLE(backend.quote_name(table)),
|
||||
style.SQL_KEYWORD(backend.get_drop_foreignkey_sql()),
|
||||
style.SQL_FIELD(backend.quote_name('%s_refs_%s_%x' % (col, r_col, abs(hash((table, r_table))))))))
|
||||
style.SQL_FIELD(truncate_name(r_name, backend.get_max_name_length()))))
|
||||
del references_to_delete[model]
|
||||
if model._meta.has_auto_field and hasattr(backend, 'get_drop_sequence'):
|
||||
output.append(backend.get_drop_sequence(model._meta.db_table))
|
||||
|
||||
# Output DROP TABLE statements for many-to-many tables.
|
||||
for model in app_models:
|
||||
opts = model._meta
|
||||
for f in opts.many_to_many:
|
||||
if cursor and f.m2m_db_table() in table_names:
|
||||
if cursor and table_name_converter(f.m2m_db_table()) in table_names:
|
||||
output.append("%s %s;" % (style.SQL_KEYWORD('DROP TABLE'),
|
||||
style.SQL_TABLE(backend.quote_name(f.m2m_db_table()))))
|
||||
if hasattr(backend, 'get_drop_sequence'):
|
||||
output.append(backend.get_drop_sequence("%s_%s" % (model._meta.db_table, f.column)))
|
||||
|
||||
|
||||
app_label = app_models[0]._meta.app_label
|
||||
|
||||
@ -433,14 +484,20 @@ def get_sql_indexes_for_model(model):
|
||||
output = []
|
||||
|
||||
for f in model._meta.fields:
|
||||
if f.db_index:
|
||||
if f.db_index and not ((f.primary_key or f.unique) and backend.autoindexes_primary_keys):
|
||||
unique = f.unique and 'UNIQUE ' or ''
|
||||
tablespace = f.db_tablespace or model._meta.db_tablespace
|
||||
if tablespace and backend.supports_tablespaces:
|
||||
tablespace_sql = ' ' + backend.get_tablespace_sql(tablespace)
|
||||
else:
|
||||
tablespace_sql = ''
|
||||
output.append(
|
||||
style.SQL_KEYWORD('CREATE %sINDEX' % unique) + ' ' + \
|
||||
style.SQL_TABLE(backend.quote_name('%s_%s' % (model._meta.db_table, f.column))) + ' ' + \
|
||||
style.SQL_KEYWORD('ON') + ' ' + \
|
||||
style.SQL_TABLE(backend.quote_name(model._meta.db_table)) + ' ' + \
|
||||
"(%s);" % style.SQL_FIELD(backend.quote_name(f.column))
|
||||
"(%s)" % style.SQL_FIELD(backend.quote_name(f.column)) + \
|
||||
"%s;" % tablespace_sql
|
||||
)
|
||||
return output
|
||||
|
||||
@ -464,7 +521,7 @@ def _emit_post_sync_signal(created_models, verbosity, interactive):
|
||||
|
||||
def syncdb(verbosity=1, interactive=True):
|
||||
"Creates the database tables for all apps in INSTALLED_APPS whose tables haven't already been created."
|
||||
from django.db import connection, transaction, models, get_creation_module
|
||||
from django.db import backend, connection, transaction, models, get_creation_module
|
||||
from django.conf import settings
|
||||
|
||||
disable_termcolors()
|
||||
@ -487,6 +544,10 @@ def syncdb(verbosity=1, interactive=True):
|
||||
# Get a list of all existing database tables,
|
||||
# so we know what needs to be added.
|
||||
table_list = _get_table_list()
|
||||
if backend.uses_case_insensitive_names:
|
||||
table_name_converter = str.upper
|
||||
else:
|
||||
table_name_converter = lambda x: x
|
||||
|
||||
# Get a list of already installed *models* so that references work right.
|
||||
seen_models = _get_installed_models(table_list)
|
||||
@ -501,7 +562,7 @@ def syncdb(verbosity=1, interactive=True):
|
||||
# Create the model's database table, if it doesn't already exist.
|
||||
if verbosity >= 2:
|
||||
print "Processing %s.%s model" % (app_name, model._meta.object_name)
|
||||
if model._meta.db_table in table_list:
|
||||
if table_name_converter(model._meta.db_table) in table_list:
|
||||
continue
|
||||
sql, references = _get_sql_model_create(model, seen_models)
|
||||
seen_models.add(model)
|
||||
@ -513,7 +574,7 @@ def syncdb(verbosity=1, interactive=True):
|
||||
print "Creating table %s" % model._meta.db_table
|
||||
for statement in sql:
|
||||
cursor.execute(statement)
|
||||
table_list.append(model._meta.db_table)
|
||||
table_list.append(table_name_converter(model._meta.db_table))
|
||||
|
||||
# Create the m2m tables. This must be done after all tables have been created
|
||||
# to ensure that all referred tables will exist.
|
||||
@ -832,7 +893,7 @@ def inspectdb():
|
||||
except NotImplementedError:
|
||||
indexes = {}
|
||||
for i, row in enumerate(introspection_module.get_table_description(cursor, table_name)):
|
||||
att_name = row[0]
|
||||
att_name = row[0].lower()
|
||||
comment_notes = [] # Holds Field notes, to be displayed in a Python comment.
|
||||
extra_params = {} # Holds Field parameters such as 'db_column'.
|
||||
|
||||
@ -1629,7 +1690,9 @@ def execute_from_command_line(action_mapping=DEFAULT_ACTION_MAPPING, argv=None):
|
||||
if not mod_list:
|
||||
parser.print_usage_and_exit()
|
||||
if action not in NO_SQL_TRANSACTION:
|
||||
print style.SQL_KEYWORD("BEGIN;")
|
||||
from django.db import backend
|
||||
if backend.get_start_transaction_sql():
|
||||
print style.SQL_KEYWORD(backend.get_start_transaction_sql())
|
||||
for mod in mod_list:
|
||||
if action == 'reset':
|
||||
output = action_mapping[action](mod, options.interactive)
|
||||
|
@ -9,7 +9,7 @@ been reviewed for security issues. Don't use it for production use.
|
||||
|
||||
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
|
||||
from types import ListType, StringType
|
||||
import os, re, sys, time, urllib
|
||||
import os, re, sys, time, urllib, mimetypes
|
||||
|
||||
__version__ = "0.1"
|
||||
__all__ = ['WSGIServer','WSGIRequestHandler','demo_app']
|
||||
@ -309,7 +309,7 @@ class ServerHandler(object):
|
||||
"""
|
||||
if not self.result_is_file() and not self.sendfile():
|
||||
for data in self.result:
|
||||
self.write(data, False)
|
||||
self.write(data)
|
||||
self.finish_content()
|
||||
self.close()
|
||||
|
||||
@ -377,7 +377,7 @@ class ServerHandler(object):
|
||||
else:
|
||||
self._write('Status: %s\r\n' % self.status)
|
||||
|
||||
def write(self, data, flush=True):
|
||||
def write(self, data):
|
||||
"""'write()' callable as specified by PEP 333"""
|
||||
|
||||
assert type(data) is StringType,"write() argument must be string"
|
||||
@ -394,7 +394,6 @@ class ServerHandler(object):
|
||||
|
||||
# XXX check Content-Length and truncate if too many bytes written?
|
||||
self._write(data)
|
||||
if flush:
|
||||
self._flush()
|
||||
|
||||
def sendfile(self):
|
||||
@ -422,6 +421,8 @@ class ServerHandler(object):
|
||||
if not self.headers_sent:
|
||||
self.headers['Content-Length'] = "0"
|
||||
self.send_headers()
|
||||
else:
|
||||
pass # XXX check if content-length was too short?
|
||||
|
||||
def close(self):
|
||||
try:
|
||||
@ -629,6 +630,9 @@ class AdminMediaHandler(object):
|
||||
else:
|
||||
status = '200 OK'
|
||||
headers = {}
|
||||
mime_type = mimetypes.guess_type(file_path)[0]
|
||||
if mime_type:
|
||||
headers['Content-Type'] = mime_type
|
||||
output = [fp.read()]
|
||||
fp.close()
|
||||
start_response(status, headers.items())
|
||||
|
@ -9,8 +9,17 @@ a string) and returns a tuple in this format:
|
||||
|
||||
from django.http import Http404
|
||||
from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist
|
||||
from django.utils.functional import memoize
|
||||
import re
|
||||
|
||||
try:
|
||||
reversed
|
||||
except NameError:
|
||||
from django.utils.itercompat import reversed # Python 2.3 fallback
|
||||
|
||||
_resolver_cache = {} # Maps urlconf modules to RegexURLResolver instances.
|
||||
_callable_cache = {} # Maps view and url pattern names to their view functions.
|
||||
|
||||
class Resolver404(Http404):
|
||||
pass
|
||||
|
||||
@ -18,6 +27,34 @@ class NoReverseMatch(Exception):
|
||||
# Don't make this raise an error when used in a template.
|
||||
silent_variable_failure = True
|
||||
|
||||
def get_callable(lookup_view, can_fail=False):
|
||||
"""
|
||||
Convert a string version of a function name to the callable object.
|
||||
|
||||
If the lookup_view is not an import path, it is assumed to be a URL pattern
|
||||
label and the original string is returned.
|
||||
|
||||
If can_fail is True, lookup_view might be a URL pattern label, so errors
|
||||
during the import fail and the string is returned.
|
||||
"""
|
||||
if not callable(lookup_view):
|
||||
mod_name, func_name = get_mod_func(lookup_view)
|
||||
try:
|
||||
if func_name != '':
|
||||
lookup_view = getattr(__import__(mod_name, {}, {}, ['']), func_name)
|
||||
except (ImportError, AttributeError):
|
||||
if not can_fail:
|
||||
raise
|
||||
return lookup_view
|
||||
get_callable = memoize(get_callable, _callable_cache)
|
||||
|
||||
def get_resolver(urlconf):
|
||||
if urlconf is None:
|
||||
from django.conf import settings
|
||||
urlconf = settings.ROOT_URLCONF
|
||||
return RegexURLResolver(r'^/', urlconf)
|
||||
get_resolver = memoize(get_resolver, _resolver_cache)
|
||||
|
||||
def get_mod_func(callback):
|
||||
# Converts 'django.views.news.stories.story_detail' to
|
||||
# ['django.views.news.stories', 'story_detail']
|
||||
@ -129,12 +166,13 @@ class RegexURLPattern(object):
|
||||
def _get_callback(self):
|
||||
if self._callback is not None:
|
||||
return self._callback
|
||||
mod_name, func_name = get_mod_func(self._callback_str)
|
||||
try:
|
||||
self._callback = getattr(__import__(mod_name, {}, {}, ['']), func_name)
|
||||
self._callback = get_callable(self._callback_str)
|
||||
except ImportError, e:
|
||||
mod_name, _ = get_mod_func(self._callback_str)
|
||||
raise ViewDoesNotExist, "Could not import %s. Error was: %s" % (mod_name, str(e))
|
||||
except AttributeError, e:
|
||||
mod_name, func_name = get_mod_func(self._callback_str)
|
||||
raise ViewDoesNotExist, "Tried %s in module %s. Error was: %s" % (func_name, mod_name, str(e))
|
||||
return self._callback
|
||||
callback = property(_get_callback)
|
||||
@ -160,6 +198,19 @@ class RegexURLResolver(object):
|
||||
self.urlconf_name = urlconf_name
|
||||
self.callback = None
|
||||
self.default_kwargs = default_kwargs or {}
|
||||
self._reverse_dict = {}
|
||||
|
||||
def _get_reverse_dict(self):
|
||||
if not self._reverse_dict and hasattr(self.urlconf_module, 'urlpatterns'):
|
||||
for pattern in reversed(self.urlconf_module.urlpatterns):
|
||||
if isinstance(pattern, RegexURLResolver):
|
||||
for key, value in pattern.reverse_dict.iteritems():
|
||||
self._reverse_dict[key] = (pattern,) + value
|
||||
else:
|
||||
self._reverse_dict[pattern.callback] = (pattern,)
|
||||
self._reverse_dict[pattern.name] = (pattern,)
|
||||
return self._reverse_dict
|
||||
reverse_dict = property(_get_reverse_dict)
|
||||
|
||||
def resolve(self, path):
|
||||
tried = []
|
||||
@ -209,24 +260,12 @@ class RegexURLResolver(object):
|
||||
return self._resolve_special('500')
|
||||
|
||||
def reverse(self, lookup_view, *args, **kwargs):
|
||||
if not callable(lookup_view):
|
||||
mod_name, func_name = get_mod_func(lookup_view)
|
||||
try:
|
||||
lookup_view = getattr(__import__(mod_name, {}, {}, ['']), func_name)
|
||||
lookup_view = get_callable(lookup_view, True)
|
||||
except (ImportError, AttributeError):
|
||||
if func_name != '':
|
||||
raise NoReverseMatch
|
||||
for pattern in self.urlconf_module.urlpatterns:
|
||||
if isinstance(pattern, RegexURLResolver):
|
||||
try:
|
||||
return pattern.reverse_helper(lookup_view, *args, **kwargs)
|
||||
except NoReverseMatch:
|
||||
continue
|
||||
elif pattern.callback == lookup_view or pattern.name == lookup_view:
|
||||
try:
|
||||
return pattern.reverse_helper(*args, **kwargs)
|
||||
except NoReverseMatch:
|
||||
continue
|
||||
if lookup_view in self.reverse_dict:
|
||||
return ''.join([reverse_helper(part.regex, *args, **kwargs) for part in self.reverse_dict[lookup_view]])
|
||||
raise NoReverseMatch
|
||||
|
||||
def reverse_helper(self, lookup_view, *args, **kwargs):
|
||||
@ -235,17 +274,10 @@ class RegexURLResolver(object):
|
||||
return result + sub_match
|
||||
|
||||
def resolve(path, urlconf=None):
|
||||
if urlconf is None:
|
||||
from django.conf import settings
|
||||
urlconf = settings.ROOT_URLCONF
|
||||
resolver = RegexURLResolver(r'^/', urlconf)
|
||||
return resolver.resolve(path)
|
||||
return get_resolver(urlconf).resolve(path)
|
||||
|
||||
def reverse(viewname, urlconf=None, args=None, kwargs=None):
|
||||
args = args or []
|
||||
kwargs = kwargs or {}
|
||||
if urlconf is None:
|
||||
from django.conf import settings
|
||||
urlconf = settings.ROOT_URLCONF
|
||||
resolver = RegexURLResolver(r'^/', urlconf)
|
||||
return '/' + resolver.reverse(viewname, *args, **kwargs)
|
||||
return '/' + get_resolver(urlconf).reverse(viewname, *args, **kwargs)
|
||||
|
||||
|
@ -89,7 +89,14 @@ class DatabaseWrapper(local):
|
||||
self.connection.close()
|
||||
self.connection = None
|
||||
|
||||
allows_group_by_ordinal = True
|
||||
allows_unique_and_pk = True
|
||||
autoindexes_primary_keys = True
|
||||
needs_datetime_string_cast = True
|
||||
needs_upper_for_iops = False
|
||||
supports_constraints = True
|
||||
supports_tablespaces = True
|
||||
uses_case_insensitive_names = False
|
||||
|
||||
def quote_name(name):
|
||||
if name.startswith('[') and name.endswith(']'):
|
||||
@ -117,6 +124,9 @@ def get_date_trunc_sql(lookup_type, field_name):
|
||||
if lookup_type=='day':
|
||||
return "Convert(datetime, Convert(varchar(12), %s))" % field_name
|
||||
|
||||
def get_datetime_cast_sql():
|
||||
return None
|
||||
|
||||
def get_limit_offset_sql(limit, offset=None):
|
||||
# TODO: This is a guess. Make sure this is correct.
|
||||
sql = "LIMIT %s" % limit
|
||||
@ -139,6 +149,18 @@ def get_drop_foreignkey_sql():
|
||||
def get_pk_default_value():
|
||||
return "DEFAULT"
|
||||
|
||||
def get_max_name_length():
|
||||
return None
|
||||
|
||||
def get_start_transaction_sql():
|
||||
return "BEGIN;"
|
||||
|
||||
def get_tablespace_sql(tablespace, inline=False):
|
||||
return "ON %s" % quote_name(tablespace)
|
||||
|
||||
def get_autoinc_sql(table):
|
||||
return None
|
||||
|
||||
def get_sql_flush(style, tables, sequences):
|
||||
"""Return a list of SQL statements required to remove all data from
|
||||
all tables in the database (without actually removing the tables
|
||||
|
@ -33,6 +33,7 @@ class DatabaseWrapper:
|
||||
pass # close()
|
||||
|
||||
supports_constraints = False
|
||||
supports_tablespaces = False
|
||||
quote_name = complain
|
||||
dictfetchone = complain
|
||||
dictfetchmany = complain
|
||||
|
@ -134,7 +134,14 @@ class DatabaseWrapper(local):
|
||||
self.server_version = tuple([int(x) for x in m.groups()])
|
||||
return self.server_version
|
||||
|
||||
allows_group_by_ordinal = True
|
||||
allows_unique_and_pk = True
|
||||
autoindexes_primary_keys = False
|
||||
needs_datetime_string_cast = True # MySQLdb requires a typecast for dates
|
||||
needs_upper_for_iops = False
|
||||
supports_constraints = True
|
||||
supports_tablespaces = False
|
||||
uses_case_insensitive_names = False
|
||||
|
||||
def quote_name(name):
|
||||
if name.startswith("`") and name.endswith("`"):
|
||||
@ -167,6 +174,9 @@ def get_date_trunc_sql(lookup_type, field_name):
|
||||
sql = "CAST(DATE_FORMAT(%s, '%s') AS DATETIME)" % (field_name, format_str)
|
||||
return sql
|
||||
|
||||
def get_datetime_cast_sql():
|
||||
return None
|
||||
|
||||
def get_limit_offset_sql(limit, offset=None):
|
||||
sql = "LIMIT "
|
||||
if offset and offset != 0:
|
||||
@ -188,6 +198,15 @@ def get_drop_foreignkey_sql():
|
||||
def get_pk_default_value():
|
||||
return "DEFAULT"
|
||||
|
||||
def get_max_name_length():
|
||||
return None;
|
||||
|
||||
def get_start_transaction_sql():
|
||||
return "BEGIN;"
|
||||
|
||||
def get_autoinc_sql(table):
|
||||
return None
|
||||
|
||||
def get_sql_flush(style, tables, sequences):
|
||||
"""Return a list of SQL statements required to remove all data from
|
||||
all tables in the database (without actually removing the tables
|
||||
|
@ -135,7 +135,14 @@ class DatabaseWrapper(local):
|
||||
self.server_version = tuple([int(x) for x in m.groups()])
|
||||
return self.server_version
|
||||
|
||||
allows_group_by_ordinal = True
|
||||
allows_unique_and_pk = True
|
||||
autoindexes_primary_keys = False
|
||||
needs_datetime_string_cast = True # MySQLdb requires a typecast for dates
|
||||
needs_upper_for_iops = False
|
||||
supports_constraints = True
|
||||
supports_tablespaces = False
|
||||
uses_case_insensitive_names = False
|
||||
|
||||
def quote_name(name):
|
||||
if name.startswith("`") and name.endswith("`"):
|
||||
@ -168,6 +175,9 @@ def get_date_trunc_sql(lookup_type, field_name):
|
||||
sql = "CAST(DATE_FORMAT(%s, '%s') AS DATETIME)" % (field_name, format_str)
|
||||
return sql
|
||||
|
||||
def get_datetime_cast_sql():
|
||||
return None
|
||||
|
||||
def get_limit_offset_sql(limit, offset=None):
|
||||
sql = "LIMIT "
|
||||
if offset and offset != 0:
|
||||
@ -189,6 +199,15 @@ def get_drop_foreignkey_sql():
|
||||
def get_pk_default_value():
|
||||
return "DEFAULT"
|
||||
|
||||
def get_max_name_length():
|
||||
return None;
|
||||
|
||||
def get_start_transaction_sql():
|
||||
return "BEGIN;"
|
||||
|
||||
def get_autoinc_sql(table):
|
||||
return None
|
||||
|
||||
def get_sql_flush(style, tables, sequences):
|
||||
"""Return a list of SQL statements required to remove all data from
|
||||
all tables in the database (without actually removing the tables
|
||||
|
@ -4,12 +4,16 @@ Oracle database backend for Django.
|
||||
Requires cx_Oracle: http://www.python.net/crew/atuining/cx_Oracle/
|
||||
"""
|
||||
|
||||
from django.conf import settings
|
||||
from django.db.backends import util
|
||||
try:
|
||||
import cx_Oracle as Database
|
||||
except ImportError, e:
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
raise ImproperlyConfigured, "Error loading cx_Oracle module: %s" % e
|
||||
import datetime
|
||||
from django.utils.datastructures import SortedDict
|
||||
|
||||
|
||||
DatabaseError = Database.Error
|
||||
IntegrityError = Database.IntegrityError
|
||||
@ -31,7 +35,6 @@ class DatabaseWrapper(local):
|
||||
return self.connection is not None
|
||||
|
||||
def cursor(self):
|
||||
from django.conf import settings
|
||||
if not self._valid_connection():
|
||||
if len(settings.DATABASE_HOST.strip()) == 0:
|
||||
settings.DATABASE_HOST = 'localhost'
|
||||
@ -41,25 +44,37 @@ class DatabaseWrapper(local):
|
||||
else:
|
||||
conn_string = "%s/%s@%s" % (settings.DATABASE_USER, settings.DATABASE_PASSWORD, settings.DATABASE_NAME)
|
||||
self.connection = Database.connect(conn_string, **self.options)
|
||||
return FormatStylePlaceholderCursor(self.connection)
|
||||
cursor = FormatStylePlaceholderCursor(self.connection)
|
||||
# default arraysize of 1 is highly sub-optimal
|
||||
cursor.arraysize = 100
|
||||
# set oracle date to ansi date format
|
||||
cursor.execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD'")
|
||||
cursor.execute("ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'")
|
||||
if settings.DEBUG:
|
||||
return util.CursorDebugWrapper(cursor, self)
|
||||
return cursor
|
||||
|
||||
def _commit(self):
|
||||
if self.connection is not None:
|
||||
self.connection.commit()
|
||||
return self.connection.commit()
|
||||
|
||||
def _rollback(self):
|
||||
if self.connection is not None:
|
||||
try:
|
||||
self.connection.rollback()
|
||||
except Database.NotSupportedError:
|
||||
pass
|
||||
return self.connection.rollback()
|
||||
|
||||
def close(self):
|
||||
if self.connection is not None:
|
||||
self.connection.close()
|
||||
self.connection = None
|
||||
|
||||
allows_group_by_ordinal = False
|
||||
allows_unique_and_pk = False # Suppress UNIQUE/PK for Oracle (ORA-02259)
|
||||
autoindexes_primary_keys = True
|
||||
needs_datetime_string_cast = False
|
||||
needs_upper_for_iops = True
|
||||
supports_constraints = True
|
||||
supports_tablespaces = True
|
||||
uses_case_insensitive_names = True
|
||||
|
||||
class FormatStylePlaceholderCursor(Database.Cursor):
|
||||
"""
|
||||
@ -67,45 +82,75 @@ class FormatStylePlaceholderCursor(Database.Cursor):
|
||||
This fixes it -- but note that if you want to use a literal "%s" in a query,
|
||||
you'll need to use "%%s".
|
||||
"""
|
||||
def _rewrite_args(self, query, params=None):
|
||||
if params is None:
|
||||
params = []
|
||||
else:
|
||||
# cx_Oracle can't handle unicode parameters, so cast to str for now
|
||||
for i, param in enumerate(params):
|
||||
if type(param) == unicode:
|
||||
try:
|
||||
params[i] = param.encode('utf-8')
|
||||
except UnicodeError:
|
||||
params[i] = str(param)
|
||||
args = [(':arg%d' % i) for i in range(len(params))]
|
||||
query = query % tuple(args)
|
||||
# cx_Oracle wants no trailing ';' for SQL statements. For PL/SQL, it
|
||||
# it does want a trailing ';' but not a trailing '/'. However, these
|
||||
# characters must be included in the original query in case the query
|
||||
# is being passed to SQL*Plus.
|
||||
if query.endswith(';') or query.endswith('/'):
|
||||
query = query[:-1]
|
||||
return query, params
|
||||
|
||||
def execute(self, query, params=None):
|
||||
if params is None: params = []
|
||||
query = self.convert_arguments(query, len(params))
|
||||
query, params = self._rewrite_args(query, params)
|
||||
return Database.Cursor.execute(self, query, params)
|
||||
|
||||
def executemany(self, query, params=None):
|
||||
if params is None: params = []
|
||||
query = self.convert_arguments(query, len(params[0]))
|
||||
query, params = self._rewrite_args(query, params)
|
||||
return Database.Cursor.executemany(self, query, params)
|
||||
|
||||
def convert_arguments(self, query, num_params):
|
||||
# replace occurances of "%s" with ":arg" - Oracle requires colons for parameter placeholders.
|
||||
args = [':arg' for i in range(num_params)]
|
||||
return query % tuple(args)
|
||||
|
||||
def quote_name(name):
|
||||
return name
|
||||
# SQL92 requires delimited (quoted) names to be case-sensitive. When
|
||||
# not quoted, Oracle has case-insensitive behavior for identifiers, but
|
||||
# always defaults to uppercase.
|
||||
# We simplify things by making Oracle identifiers always uppercase.
|
||||
if not name.startswith('"') and not name.endswith('"'):
|
||||
name = '"%s"' % util.truncate_name(name.upper(), get_max_name_length())
|
||||
return name.upper()
|
||||
|
||||
dictfetchone = util.dictfetchone
|
||||
dictfetchmany = util.dictfetchmany
|
||||
dictfetchall = util.dictfetchall
|
||||
|
||||
def get_last_insert_id(cursor, table_name, pk_name):
|
||||
query = "SELECT %s_sq.currval from dual" % table_name
|
||||
cursor.execute(query)
|
||||
sq_name = util.truncate_name(table_name, get_max_name_length()-3)
|
||||
cursor.execute('SELECT %s_sq.currval FROM dual' % sq_name)
|
||||
return cursor.fetchone()[0]
|
||||
|
||||
def get_date_extract_sql(lookup_type, table_name):
|
||||
# lookup_type is 'year', 'month', 'day'
|
||||
# http://www.psoug.org/reference/date_func.html
|
||||
# http://download-east.oracle.com/docs/cd/B10501_01/server.920/a96540/functions42a.htm#1017163
|
||||
return "EXTRACT(%s FROM %s)" % (lookup_type, table_name)
|
||||
|
||||
def get_date_trunc_sql(lookup_type, field_name):
|
||||
return "EXTRACT(%s FROM TRUNC(%s))" % (lookup_type, field_name)
|
||||
# lookup_type is 'year', 'month', 'day'
|
||||
# Oracle uses TRUNC() for both dates and numbers.
|
||||
# http://download-east.oracle.com/docs/cd/B10501_01/server.920/a96540/functions155a.htm#SQLRF06151
|
||||
if lookup_type == 'day':
|
||||
sql = 'TRUNC(%s)' % (field_name,)
|
||||
else:
|
||||
sql = "TRUNC(%s, '%s')" % (field_name, lookup_type)
|
||||
return sql
|
||||
|
||||
def get_datetime_cast_sql():
|
||||
return "TO_TIMESTAMP(%s, 'YYYY-MM-DD HH24:MI:SS.FF')"
|
||||
|
||||
def get_limit_offset_sql(limit, offset=None):
|
||||
# Limits and offset are too complicated to be handled here.
|
||||
# Instead, they are handled in django/db/query.py.
|
||||
pass
|
||||
# Instead, they are handled in django/db/backends/oracle/query.py.
|
||||
return ""
|
||||
|
||||
def get_random_function_sql():
|
||||
return "DBMS_RANDOM.RANDOM"
|
||||
@ -117,40 +162,363 @@ def get_fulltext_search_sql(field_name):
|
||||
raise NotImplementedError
|
||||
|
||||
def get_drop_foreignkey_sql():
|
||||
return "DROP FOREIGN KEY"
|
||||
return "DROP CONSTRAINT"
|
||||
|
||||
def get_pk_default_value():
|
||||
return "DEFAULT"
|
||||
|
||||
def get_max_name_length():
|
||||
return 30
|
||||
|
||||
def get_start_transaction_sql():
|
||||
return None
|
||||
|
||||
def get_tablespace_sql(tablespace, inline=False):
|
||||
return "%sTABLESPACE %s" % ((inline and "USING INDEX " or ""), quote_name(tablespace))
|
||||
|
||||
def get_autoinc_sql(table):
|
||||
# To simulate auto-incrementing primary keys in Oracle, we have to
|
||||
# create a sequence and a trigger.
|
||||
sq_name = get_sequence_name(table)
|
||||
tr_name = get_trigger_name(table)
|
||||
sequence_sql = 'CREATE SEQUENCE %s;' % sq_name
|
||||
trigger_sql = """CREATE OR REPLACE TRIGGER %s
|
||||
BEFORE INSERT ON %s
|
||||
FOR EACH ROW
|
||||
WHEN (new.id IS NULL)
|
||||
BEGIN
|
||||
SELECT %s.nextval INTO :new.id FROM dual;
|
||||
END;
|
||||
/""" % (tr_name, quote_name(table), sq_name)
|
||||
return sequence_sql, trigger_sql
|
||||
|
||||
def get_drop_sequence(table):
|
||||
return "DROP SEQUENCE %s;" % quote_name(get_sequence_name(table))
|
||||
|
||||
def _get_sequence_reset_sql():
|
||||
# TODO: colorize this SQL code with style.SQL_KEYWORD(), etc.
|
||||
return """
|
||||
DECLARE
|
||||
startvalue integer;
|
||||
cval integer;
|
||||
BEGIN
|
||||
LOCK TABLE %(table)s IN SHARE MODE;
|
||||
SELECT NVL(MAX(id), 0) INTO startvalue FROM %(table)s;
|
||||
SELECT %(sequence)s.nextval INTO cval FROM dual;
|
||||
cval := startvalue - cval;
|
||||
IF cval != 0 THEN
|
||||
EXECUTE IMMEDIATE 'ALTER SEQUENCE %(sequence)s MINVALUE 0 INCREMENT BY '||cval;
|
||||
SELECT %(sequence)s.nextval INTO cval FROM dual;
|
||||
EXECUTE IMMEDIATE 'ALTER SEQUENCE %(sequence)s INCREMENT BY 1';
|
||||
END IF;
|
||||
COMMIT;
|
||||
END;
|
||||
/"""
|
||||
|
||||
def get_sql_flush(style, tables, sequences):
|
||||
"""Return a list of SQL statements required to remove all data from
|
||||
all tables in the database (without actually removing the tables
|
||||
themselves) and put the database in an empty 'initial' state
|
||||
"""
|
||||
# Return a list of 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements
|
||||
# TODO - SQL not actually tested against Oracle yet!
|
||||
# TODO - autoincrement indices reset required? See other get_sql_flush() implementations
|
||||
sql = ['%s %s;' % \
|
||||
(style.SQL_KEYWORD('TRUNCATE'),
|
||||
# Return a list of 'TRUNCATE x;', 'TRUNCATE y;',
|
||||
# 'TRUNCATE z;'... style SQL statements
|
||||
if tables:
|
||||
# Oracle does support TRUNCATE, but it seems to get us into
|
||||
# FK referential trouble, whereas DELETE FROM table works.
|
||||
sql = ['%s %s %s;' % \
|
||||
(style.SQL_KEYWORD('DELETE'),
|
||||
style.SQL_KEYWORD('FROM'),
|
||||
style.SQL_FIELD(quote_name(table))
|
||||
) for table in tables]
|
||||
# Since we've just deleted all the rows, running our sequence
|
||||
# ALTER code will reset the sequence to 0.
|
||||
for sequence_info in sequences:
|
||||
table_name = sequence_info['table']
|
||||
seq_name = get_sequence_name(table_name)
|
||||
query = _get_sequence_reset_sql() % {'sequence':seq_name,
|
||||
'table':quote_name(table_name)}
|
||||
sql.append(query)
|
||||
return sql
|
||||
else:
|
||||
return []
|
||||
|
||||
def get_sequence_name(table):
|
||||
name_length = get_max_name_length() - 3
|
||||
return '%s_SQ' % util.truncate_name(table, name_length).upper()
|
||||
|
||||
def get_sql_sequence_reset(style, model_list):
|
||||
"Returns a list of the SQL statements to reset sequences for the given models."
|
||||
# No sequence reset required
|
||||
return []
|
||||
from django.db import models
|
||||
output = []
|
||||
query = _get_sequence_reset_sql()
|
||||
for model in model_list:
|
||||
for f in model._meta.fields:
|
||||
if isinstance(f, models.AutoField):
|
||||
sequence_name = get_sequence_name(model._meta.db_table)
|
||||
output.append(query % {'sequence':sequence_name,
|
||||
'table':model._meta.db_table})
|
||||
break # Only one AutoField is allowed per model, so don't bother continuing.
|
||||
for f in model._meta.many_to_many:
|
||||
sequence_name = get_sequence_name(f.m2m_db_table())
|
||||
output.append(query % {'sequence':sequence_name,
|
||||
'table':f.m2m_db_table()})
|
||||
return output
|
||||
|
||||
def get_trigger_name(table):
|
||||
name_length = get_max_name_length() - 3
|
||||
return '%s_TR' % util.truncate_name(table, name_length).upper()
|
||||
|
||||
def get_query_set_class(DefaultQuerySet):
|
||||
"Create a custom QuerySet class for Oracle."
|
||||
|
||||
from django.db import backend, connection
|
||||
from django.db.models.query import EmptyResultSet, GET_ITERATOR_CHUNK_SIZE, quote_only_if_word
|
||||
|
||||
class OracleQuerySet(DefaultQuerySet):
|
||||
|
||||
def iterator(self):
|
||||
"Performs the SELECT database lookup of this QuerySet."
|
||||
|
||||
from django.db.models.query import get_cached_row
|
||||
|
||||
# self._select is a dictionary, and dictionaries' key order is
|
||||
# undefined, so we convert it to a list of tuples.
|
||||
extra_select = self._select.items()
|
||||
|
||||
full_query = None
|
||||
|
||||
try:
|
||||
try:
|
||||
select, sql, params, full_query = self._get_sql_clause(get_full_query=True)
|
||||
except TypeError:
|
||||
select, sql, params = self._get_sql_clause()
|
||||
except EmptyResultSet:
|
||||
raise StopIteration
|
||||
if not full_query:
|
||||
full_query = "SELECT %s%s\n%s" % \
|
||||
((self._distinct and "DISTINCT " or ""),
|
||||
', '.join(select), sql)
|
||||
|
||||
cursor = connection.cursor()
|
||||
cursor.execute(full_query, params)
|
||||
|
||||
fill_cache = self._select_related
|
||||
fields = self.model._meta.fields
|
||||
index_end = len(fields)
|
||||
|
||||
# so here's the logic;
|
||||
# 1. retrieve each row in turn
|
||||
# 2. convert NCLOBs
|
||||
|
||||
while 1:
|
||||
rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
|
||||
if not rows:
|
||||
raise StopIteration
|
||||
for row in rows:
|
||||
row = self.resolve_columns(row, fields)
|
||||
if fill_cache:
|
||||
obj, index_end = get_cached_row(klass=self.model, row=row,
|
||||
index_start=0, max_depth=self._max_related_depth)
|
||||
else:
|
||||
obj = self.model(*row[:index_end])
|
||||
for i, k in enumerate(extra_select):
|
||||
setattr(obj, k[0], row[index_end+i])
|
||||
yield obj
|
||||
|
||||
|
||||
def _get_sql_clause(self, get_full_query=False):
|
||||
from django.db.models.query import fill_table_cache, \
|
||||
handle_legacy_orderlist, orderfield2column
|
||||
|
||||
opts = self.model._meta
|
||||
|
||||
# Construct the fundamental parts of the query: SELECT X FROM Y WHERE Z.
|
||||
select = ["%s.%s" % (backend.quote_name(opts.db_table), backend.quote_name(f.column)) for f in opts.fields]
|
||||
tables = [quote_only_if_word(t) for t in self._tables]
|
||||
joins = SortedDict()
|
||||
where = self._where[:]
|
||||
params = self._params[:]
|
||||
|
||||
# Convert self._filters into SQL.
|
||||
joins2, where2, params2 = self._filters.get_sql(opts)
|
||||
joins.update(joins2)
|
||||
where.extend(where2)
|
||||
params.extend(params2)
|
||||
|
||||
# Add additional tables and WHERE clauses based on select_related.
|
||||
if self._select_related:
|
||||
fill_table_cache(opts, select, tables, where, opts.db_table, [opts.db_table])
|
||||
|
||||
# Add any additional SELECTs.
|
||||
if self._select:
|
||||
select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), backend.quote_name(s[0])) for s in self._select.items()])
|
||||
|
||||
# Start composing the body of the SQL statement.
|
||||
sql = [" FROM", backend.quote_name(opts.db_table)]
|
||||
|
||||
# Compose the join dictionary into SQL describing the joins.
|
||||
if joins:
|
||||
sql.append(" ".join(["%s %s %s ON %s" % (join_type, table, alias, condition)
|
||||
for (alias, (table, join_type, condition)) in joins.items()]))
|
||||
|
||||
# Compose the tables clause into SQL.
|
||||
if tables:
|
||||
sql.append(", " + ", ".join(tables))
|
||||
|
||||
# Compose the where clause into SQL.
|
||||
if where:
|
||||
sql.append(where and "WHERE " + " AND ".join(where))
|
||||
|
||||
# ORDER BY clause
|
||||
order_by = []
|
||||
if self._order_by is not None:
|
||||
ordering_to_use = self._order_by
|
||||
else:
|
||||
ordering_to_use = opts.ordering
|
||||
for f in handle_legacy_orderlist(ordering_to_use):
|
||||
if f == '?': # Special case.
|
||||
order_by.append(backend.get_random_function_sql())
|
||||
else:
|
||||
if f.startswith('-'):
|
||||
col_name = f[1:]
|
||||
order = "DESC"
|
||||
else:
|
||||
col_name = f
|
||||
order = "ASC"
|
||||
if "." in col_name:
|
||||
table_prefix, col_name = col_name.split('.', 1)
|
||||
table_prefix = backend.quote_name(table_prefix) + '.'
|
||||
else:
|
||||
# Use the database table as a column prefix if it wasn't given,
|
||||
# and if the requested column isn't a custom SELECT.
|
||||
if "." not in col_name and col_name not in (self._select or ()):
|
||||
table_prefix = backend.quote_name(opts.db_table) + '.'
|
||||
else:
|
||||
table_prefix = ''
|
||||
order_by.append('%s%s %s' % (table_prefix, backend.quote_name(orderfield2column(col_name, opts)), order))
|
||||
if order_by:
|
||||
sql.append("ORDER BY " + ", ".join(order_by))
|
||||
|
||||
# Look for column name collisions in the select elements
|
||||
# and fix them with an AS alias. This allows us to do a
|
||||
# SELECT * later in the paging query.
|
||||
cols = [clause.split('.')[-1] for clause in select]
|
||||
for index, col in enumerate(cols):
|
||||
if cols.count(col) > 1:
|
||||
col = '%s%d' % (col.replace('"', ''), index)
|
||||
cols[index] = col
|
||||
select[index] = '%s AS %s' % (select[index], col)
|
||||
|
||||
# LIMIT and OFFSET clauses
|
||||
# To support limits and offsets, Oracle requires some funky rewriting of an otherwise normal looking query.
|
||||
select_clause = ",".join(select)
|
||||
distinct = (self._distinct and "DISTINCT " or "")
|
||||
|
||||
if order_by:
|
||||
order_by_clause = " OVER (ORDER BY %s )" % (", ".join(order_by))
|
||||
else:
|
||||
#Oracle's row_number() function always requires an order-by clause.
|
||||
#So we need to define a default order-by, since none was provided.
|
||||
order_by_clause = " OVER (ORDER BY %s.%s)" % \
|
||||
(backend.quote_name(opts.db_table),
|
||||
backend.quote_name(opts.fields[0].db_column or opts.fields[0].column))
|
||||
# limit_and_offset_clause
|
||||
if self._limit is None:
|
||||
assert self._offset is None, "'offset' is not allowed without 'limit'"
|
||||
|
||||
if self._offset is not None:
|
||||
offset = int(self._offset)
|
||||
else:
|
||||
offset = 0
|
||||
if self._limit is not None:
|
||||
limit = int(self._limit)
|
||||
else:
|
||||
limit = None
|
||||
|
||||
limit_and_offset_clause = ''
|
||||
if limit is not None:
|
||||
limit_and_offset_clause = "WHERE rn > %s AND rn <= %s" % (offset, limit+offset)
|
||||
elif offset:
|
||||
limit_and_offset_clause = "WHERE rn > %s" % (offset)
|
||||
|
||||
if len(limit_and_offset_clause) > 0:
|
||||
fmt = \
|
||||
"""SELECT * FROM
|
||||
(SELECT %s%s,
|
||||
ROW_NUMBER()%s AS rn
|
||||
%s)
|
||||
%s"""
|
||||
full_query = fmt % (distinct, select_clause,
|
||||
order_by_clause, ' '.join(sql).strip(),
|
||||
limit_and_offset_clause)
|
||||
else:
|
||||
full_query = None
|
||||
|
||||
if get_full_query:
|
||||
return select, " ".join(sql), params, full_query
|
||||
else:
|
||||
return select, " ".join(sql), params
|
||||
|
||||
def resolve_columns(self, row, fields=()):
|
||||
from django.db.models.fields import DateField, DateTimeField, \
|
||||
TimeField, BooleanField, NullBooleanField, DecimalField, Field
|
||||
values = []
|
||||
for value, field in map(None, row, fields):
|
||||
if isinstance(value, Database.LOB):
|
||||
value = value.read()
|
||||
# Oracle stores empty strings as null. We need to undo this in
|
||||
# order to adhere to the Django convention of using the empty
|
||||
# string instead of null, but only if the field accepts the
|
||||
# empty string.
|
||||
if value is None and isinstance(field, Field) and field.empty_strings_allowed:
|
||||
value = ''
|
||||
# Convert 1 or 0 to True or False
|
||||
elif value in (1, 0) and isinstance(field, (BooleanField, NullBooleanField)):
|
||||
value = bool(value)
|
||||
# Convert floats to decimals
|
||||
elif value is not None and isinstance(field, DecimalField):
|
||||
value = util.typecast_decimal(field.format_number(value))
|
||||
# cx_Oracle always returns datetime.datetime objects for
|
||||
# DATE and TIMESTAMP columns, but Django wants to see a
|
||||
# python datetime.date, .time, or .datetime. We use the type
|
||||
# of the Field to determine which to cast to, but it's not
|
||||
# always available.
|
||||
# As a workaround, we cast to date if all the time-related
|
||||
# values are 0, or to time if the date is 1/1/1900.
|
||||
# This could be cleaned a bit by adding a method to the Field
|
||||
# classes to normalize values from the database (the to_python
|
||||
# method is used for validation and isn't what we want here).
|
||||
elif isinstance(value, Database.Timestamp):
|
||||
# In Python 2.3, the cx_Oracle driver returns its own
|
||||
# Timestamp object that we must convert to a datetime class.
|
||||
if not isinstance(value, datetime.datetime):
|
||||
value = datetime.datetime(value.year, value.month, value.day, value.hour,
|
||||
value.minute, value.second, value.fsecond)
|
||||
if isinstance(field, DateTimeField):
|
||||
pass # DateTimeField subclasses DateField so must be checked first.
|
||||
elif isinstance(field, DateField):
|
||||
value = value.date()
|
||||
elif isinstance(field, TimeField) or (value.year == 1900 and value.month == value.day == 1):
|
||||
value = value.time()
|
||||
elif value.hour == value.minute == value.second == value.microsecond == 0:
|
||||
value = value.date()
|
||||
values.append(value)
|
||||
return values
|
||||
|
||||
return OracleQuerySet
|
||||
|
||||
|
||||
OPERATOR_MAPPING = {
|
||||
'exact': '= %s',
|
||||
'iexact': 'LIKE %s',
|
||||
'contains': 'LIKE %s',
|
||||
'icontains': 'LIKE %s',
|
||||
'iexact': '= UPPER(%s)',
|
||||
'contains': "LIKE %s ESCAPE '\\'",
|
||||
'icontains': "LIKE UPPER(%s) ESCAPE '\\'",
|
||||
'gt': '> %s',
|
||||
'gte': '>= %s',
|
||||
'lt': '< %s',
|
||||
'lte': '<= %s',
|
||||
'startswith': 'LIKE %s',
|
||||
'endswith': 'LIKE %s',
|
||||
'istartswith': 'LIKE %s',
|
||||
'iendswith': 'LIKE %s',
|
||||
'startswith': "LIKE %s ESCAPE '\\'",
|
||||
'endswith': "LIKE %s ESCAPE '\\'",
|
||||
'istartswith': "LIKE UPPER(%s) ESCAPE '\\'",
|
||||
'iendswith': "LIKE UPPER(%s) ESCAPE '\\'",
|
||||
}
|
||||
|
@ -2,9 +2,10 @@ from django.conf import settings
|
||||
import os
|
||||
|
||||
def runshell():
|
||||
args = ''
|
||||
args += settings.DATABASE_USER
|
||||
dsn = settings.DATABASE_USER
|
||||
if settings.DATABASE_PASSWORD:
|
||||
args += "/%s" % settings.DATABASE_PASSWORD
|
||||
args += "@%s" % settings.DATABASE_NAME
|
||||
os.execvp('sqlplus', args)
|
||||
dsn += "/%s" % settings.DATABASE_PASSWORD
|
||||
if settings.DATABASE_NAME:
|
||||
dsn += "@%s" % settings.DATABASE_NAME
|
||||
args = ["sqlplus", "-L", dsn]
|
||||
os.execvp("sqlplus", args)
|
||||
|
@ -1,26 +1,304 @@
|
||||
import sys, time
|
||||
from django.core import management
|
||||
|
||||
# This dictionary maps Field objects to their associated Oracle column
|
||||
# types, as strings. Column-type strings can contain format strings; they'll
|
||||
# be interpolated against the values of Field.__dict__ before being output.
|
||||
# If a column type is set to None, it won't be included in the output.
|
||||
DATA_TYPES = {
|
||||
'AutoField': 'number(38)',
|
||||
'BooleanField': 'number(1)',
|
||||
'CharField': 'varchar2(%(maxlength)s)',
|
||||
'CommaSeparatedIntegerField': 'varchar2(%(maxlength)s)',
|
||||
'DateField': 'date',
|
||||
'DateTimeField': 'date',
|
||||
'DecimalField': 'number(%(max_digits)s, %(decimal_places)s)',
|
||||
'FileField': 'varchar2(100)',
|
||||
'FilePathField': 'varchar2(100)',
|
||||
'FloatField': 'double precision',
|
||||
'ImageField': 'varchar2(100)',
|
||||
'IntegerField': 'integer',
|
||||
'IPAddressField': 'char(15)',
|
||||
'NullBooleanField': 'integer',
|
||||
'OneToOneField': 'integer',
|
||||
'PhoneNumberField': 'varchar(20)',
|
||||
'PositiveIntegerField': 'integer',
|
||||
'PositiveSmallIntegerField': 'smallint',
|
||||
'SlugField': 'varchar(50)',
|
||||
'SmallIntegerField': 'smallint',
|
||||
'TextField': 'long',
|
||||
'TimeField': 'timestamp',
|
||||
'USStateField': 'varchar(2)',
|
||||
'AutoField': 'NUMBER(11)',
|
||||
'BooleanField': 'NUMBER(1) CHECK (%(column)s IN (0,1))',
|
||||
'CharField': 'VARCHAR2(%(maxlength)s)',
|
||||
'CommaSeparatedIntegerField': 'VARCHAR2(%(maxlength)s)',
|
||||
'DateField': 'DATE',
|
||||
'DateTimeField': 'TIMESTAMP',
|
||||
'DecimalField': 'NUMBER(%(max_digits)s, %(decimal_places)s)',
|
||||
'FileField': 'VARCHAR2(100)',
|
||||
'FilePathField': 'VARCHAR2(100)',
|
||||
'FloatField': 'DOUBLE PRECISION',
|
||||
'ImageField': 'VARCHAR2(100)',
|
||||
'IntegerField': 'NUMBER(11)',
|
||||
'IPAddressField': 'VARCHAR2(15)',
|
||||
'NullBooleanField': 'NUMBER(1) CHECK ((%(column)s IN (0,1)) OR (%(column)s IS NULL))',
|
||||
'OneToOneField': 'NUMBER(11)',
|
||||
'PhoneNumberField': 'VARCHAR2(20)',
|
||||
'PositiveIntegerField': 'NUMBER(11) CHECK (%(column)s >= 0)',
|
||||
'PositiveSmallIntegerField': 'NUMBER(11) CHECK (%(column)s >= 0)',
|
||||
'SlugField': 'VARCHAR2(50)',
|
||||
'SmallIntegerField': 'NUMBER(11)',
|
||||
'TextField': 'NCLOB',
|
||||
'TimeField': 'TIMESTAMP',
|
||||
'URLField': 'VARCHAR2(200)',
|
||||
'USStateField': 'CHAR(2)',
|
||||
'NoField': None,
|
||||
}
|
||||
|
||||
TEST_DATABASE_PREFIX = 'test_'
|
||||
PASSWORD = 'Im_a_lumberjack'
|
||||
REMEMBER = {}
|
||||
|
||||
|
||||
def create_test_db(settings, connection, backend, verbosity=1, autoclobber=False):
|
||||
|
||||
TEST_DATABASE_NAME = _test_database_name(settings)
|
||||
TEST_DATABASE_USER = _test_database_user(settings)
|
||||
TEST_DATABASE_PASSWD = _test_database_passwd(settings)
|
||||
TEST_DATABASE_TBLSPACE = _test_database_tblspace(settings)
|
||||
TEST_DATABASE_TBLSPACE_TMP = _test_database_tblspace_tmp(settings)
|
||||
|
||||
parameters = {
|
||||
'dbname': TEST_DATABASE_NAME,
|
||||
'user': TEST_DATABASE_USER,
|
||||
'password': TEST_DATABASE_PASSWD,
|
||||
'tblspace': TEST_DATABASE_TBLSPACE,
|
||||
'tblspace_temp': TEST_DATABASE_TBLSPACE_TMP,
|
||||
}
|
||||
|
||||
REMEMBER['user'] = settings.DATABASE_USER
|
||||
REMEMBER['passwd'] = settings.DATABASE_PASSWORD
|
||||
|
||||
cursor = connection.cursor()
|
||||
if _test_database_create(settings):
|
||||
if verbosity >= 1:
|
||||
print 'Creating test database...'
|
||||
try:
|
||||
_create_test_db(cursor, parameters, verbosity)
|
||||
except Exception, e:
|
||||
sys.stderr.write("Got an error creating the test database: %s\n" % e)
|
||||
if not autoclobber:
|
||||
confirm = raw_input("It appears the test database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_DATABASE_NAME)
|
||||
if autoclobber or confirm == 'yes':
|
||||
try:
|
||||
if verbosity >= 1:
|
||||
print "Destroying old test database..."
|
||||
_destroy_test_db(cursor, parameters, verbosity)
|
||||
if verbosity >= 1:
|
||||
print "Creating test database..."
|
||||
_create_test_db(cursor, parameters, verbosity)
|
||||
except Exception, e:
|
||||
sys.stderr.write("Got an error recreating the test database: %s\n" % e)
|
||||
sys.exit(2)
|
||||
else:
|
||||
print "Tests cancelled."
|
||||
sys.exit(1)
|
||||
|
||||
if _test_user_create(settings):
|
||||
if verbosity >= 1:
|
||||
print "Creating test user..."
|
||||
try:
|
||||
_create_test_user(cursor, parameters, verbosity)
|
||||
except Exception, e:
|
||||
sys.stderr.write("Got an error creating the test user: %s\n" % e)
|
||||
if not autoclobber:
|
||||
confirm = raw_input("It appears the test user, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_DATABASE_USER)
|
||||
if autoclobber or confirm == 'yes':
|
||||
try:
|
||||
if verbosity >= 1:
|
||||
print "Destroying old test user..."
|
||||
_destroy_test_user(cursor, parameters, verbosity)
|
||||
if verbosity >= 1:
|
||||
print "Creating test user..."
|
||||
_create_test_user(cursor, parameters, verbosity)
|
||||
except Exception, e:
|
||||
sys.stderr.write("Got an error recreating the test user: %s\n" % e)
|
||||
sys.exit(2)
|
||||
else:
|
||||
print "Tests cancelled."
|
||||
sys.exit(1)
|
||||
|
||||
connection.close()
|
||||
settings.DATABASE_USER = TEST_DATABASE_USER
|
||||
settings.DATABASE_PASSWORD = TEST_DATABASE_PASSWD
|
||||
|
||||
management.syncdb(verbosity, interactive=False)
|
||||
|
||||
# Get a cursor (even though we don't need one yet). This has
|
||||
# the side effect of initializing the test database.
|
||||
cursor = connection.cursor()
|
||||
|
||||
|
||||
def destroy_test_db(settings, connection, backend, old_database_name, verbosity=1):
|
||||
connection.close()
|
||||
|
||||
TEST_DATABASE_NAME = _test_database_name(settings)
|
||||
TEST_DATABASE_USER = _test_database_user(settings)
|
||||
TEST_DATABASE_PASSWD = _test_database_passwd(settings)
|
||||
TEST_DATABASE_TBLSPACE = _test_database_tblspace(settings)
|
||||
TEST_DATABASE_TBLSPACE_TMP = _test_database_tblspace_tmp(settings)
|
||||
|
||||
settings.DATABASE_NAME = old_database_name
|
||||
settings.DATABASE_USER = REMEMBER['user']
|
||||
settings.DATABASE_PASSWORD = REMEMBER['passwd']
|
||||
|
||||
parameters = {
|
||||
'dbname': TEST_DATABASE_NAME,
|
||||
'user': TEST_DATABASE_USER,
|
||||
'password': TEST_DATABASE_PASSWD,
|
||||
'tblspace': TEST_DATABASE_TBLSPACE,
|
||||
'tblspace_temp': TEST_DATABASE_TBLSPACE_TMP,
|
||||
}
|
||||
|
||||
REMEMBER['user'] = settings.DATABASE_USER
|
||||
REMEMBER['passwd'] = settings.DATABASE_PASSWORD
|
||||
|
||||
cursor = connection.cursor()
|
||||
time.sleep(1) # To avoid "database is being accessed by other users" errors.
|
||||
if _test_user_create(settings):
|
||||
if verbosity >= 1:
|
||||
print 'Destroying test user...'
|
||||
_destroy_test_user(cursor, parameters, verbosity)
|
||||
if _test_database_create(settings):
|
||||
if verbosity >= 1:
|
||||
print 'Destroying test database...'
|
||||
_destroy_test_db(cursor, parameters, verbosity)
|
||||
connection.close()
|
||||
|
||||
|
||||
def _create_test_db(cursor, parameters, verbosity):
|
||||
if verbosity >= 2:
|
||||
print "_create_test_db(): dbname = %s" % parameters['dbname']
|
||||
statements = [
|
||||
"""CREATE TABLESPACE %(tblspace)s
|
||||
DATAFILE '%(tblspace)s.dbf' SIZE 20M
|
||||
REUSE AUTOEXTEND ON NEXT 10M MAXSIZE 100M
|
||||
""",
|
||||
"""CREATE TEMPORARY TABLESPACE %(tblspace_temp)s
|
||||
TEMPFILE '%(tblspace_temp)s.dbf' SIZE 20M
|
||||
REUSE AUTOEXTEND ON NEXT 10M MAXSIZE 100M
|
||||
""",
|
||||
]
|
||||
_execute_statements(cursor, statements, parameters, verbosity)
|
||||
|
||||
|
||||
def _create_test_user(cursor, parameters, verbosity):
|
||||
if verbosity >= 2:
|
||||
print "_create_test_user(): username = %s" % parameters['user']
|
||||
statements = [
|
||||
"""CREATE USER %(user)s
|
||||
IDENTIFIED BY %(password)s
|
||||
DEFAULT TABLESPACE %(tblspace)s
|
||||
TEMPORARY TABLESPACE %(tblspace_temp)s
|
||||
""",
|
||||
"""GRANT CONNECT, RESOURCE TO %(user)s""",
|
||||
]
|
||||
_execute_statements(cursor, statements, parameters, verbosity)
|
||||
|
||||
|
||||
def _destroy_test_db(cursor, parameters, verbosity):
|
||||
if verbosity >= 2:
|
||||
print "_destroy_test_db(): dbname=%s" % parameters['dbname']
|
||||
statements = [
|
||||
'DROP TABLESPACE %(tblspace)s INCLUDING CONTENTS AND DATAFILES CASCADE CONSTRAINTS',
|
||||
'DROP TABLESPACE %(tblspace_temp)s INCLUDING CONTENTS AND DATAFILES CASCADE CONSTRAINTS',
|
||||
]
|
||||
_execute_statements(cursor, statements, parameters, verbosity)
|
||||
|
||||
|
||||
def _destroy_test_user(cursor, parameters, verbosity):
|
||||
if verbosity >= 2:
|
||||
print "_destroy_test_user(): user=%s" % parameters['user']
|
||||
print "Be patient. This can take some time..."
|
||||
statements = [
|
||||
'DROP USER %(user)s CASCADE',
|
||||
]
|
||||
_execute_statements(cursor, statements, parameters, verbosity)
|
||||
|
||||
|
||||
def _execute_statements(cursor, statements, parameters, verbosity):
|
||||
for template in statements:
|
||||
stmt = template % parameters
|
||||
if verbosity >= 2:
|
||||
print stmt
|
||||
try:
|
||||
cursor.execute(stmt)
|
||||
except Exception, err:
|
||||
sys.stderr.write("Failed (%s)\n" % (err))
|
||||
raise
|
||||
|
||||
|
||||
def _test_database_name(settings):
|
||||
name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
|
||||
try:
|
||||
if settings.TEST_DATABASE_NAME:
|
||||
name = settings.TEST_DATABASE_NAME
|
||||
except AttributeError:
|
||||
pass
|
||||
except:
|
||||
raise
|
||||
return name
|
||||
|
||||
|
||||
def _test_database_create(settings):
|
||||
name = True
|
||||
try:
|
||||
if settings.TEST_DATABASE_CREATE:
|
||||
name = True
|
||||
else:
|
||||
name = False
|
||||
except AttributeError:
|
||||
pass
|
||||
except:
|
||||
raise
|
||||
return name
|
||||
|
||||
|
||||
def _test_user_create(settings):
|
||||
name = True
|
||||
try:
|
||||
if settings.TEST_USER_CREATE:
|
||||
name = True
|
||||
else:
|
||||
name = False
|
||||
except AttributeError:
|
||||
pass
|
||||
except:
|
||||
raise
|
||||
return name
|
||||
|
||||
|
||||
def _test_database_user(settings):
|
||||
name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
|
||||
try:
|
||||
if settings.TEST_DATABASE_USER:
|
||||
name = settings.TEST_DATABASE_USER
|
||||
except AttributeError:
|
||||
pass
|
||||
except:
|
||||
raise
|
||||
return name
|
||||
|
||||
|
||||
def _test_database_passwd(settings):
|
||||
name = PASSWORD
|
||||
try:
|
||||
if settings.TEST_DATABASE_PASSWD:
|
||||
name = settings.TEST_DATABASE_PASSWD
|
||||
except AttributeError:
|
||||
pass
|
||||
except:
|
||||
raise
|
||||
return name
|
||||
|
||||
|
||||
def _test_database_tblspace(settings):
|
||||
name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
|
||||
try:
|
||||
if settings.TEST_DATABASE_TBLSPACE:
|
||||
name = settings.TEST_DATABASE_TBLSPACE
|
||||
except AttributeError:
|
||||
pass
|
||||
except:
|
||||
raise
|
||||
return name
|
||||
|
||||
|
||||
def _test_database_tblspace_tmp(settings):
|
||||
name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME + '_temp'
|
||||
try:
|
||||
if settings.TEST_DATABASE_TBLSPACE_TMP:
|
||||
name = settings.TEST_DATABASE_TBLSPACE_TMP
|
||||
except AttributeError:
|
||||
pass
|
||||
except:
|
||||
raise
|
||||
return name
|
||||
|
@ -1,14 +1,19 @@
|
||||
from django.db.backends.oracle.base import quote_name
|
||||
import re
|
||||
import cx_Oracle
|
||||
|
||||
|
||||
foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)")
|
||||
|
||||
def get_table_list(cursor):
|
||||
"Returns a list of table names in the current database."
|
||||
cursor.execute("SELECT TABLE_NAME FROM USER_TABLES")
|
||||
return [row[0] for row in cursor.fetchall()]
|
||||
return [row[0].upper() for row in cursor.fetchall()]
|
||||
|
||||
def get_table_description(cursor, table_name):
|
||||
return table_name
|
||||
"Returns a description of the table, with the DB-API cursor.description interface."
|
||||
cursor.execute("SELECT * FROM %s WHERE ROWNUM < 2" % quote_name(table_name))
|
||||
return cursor.description
|
||||
|
||||
def _name_to_index(cursor, table_name):
|
||||
"""
|
||||
@ -22,7 +27,24 @@ def get_relations(cursor, table_name):
|
||||
Returns a dictionary of {field_index: (field_index_other_table, other_table)}
|
||||
representing all relationships to the given table. Indexes are 0-based.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
cursor.execute("""
|
||||
SELECT ta.column_id - 1, tb.table_name, tb.column_id - 1
|
||||
FROM user_constraints, USER_CONS_COLUMNS ca, USER_CONS_COLUMNS cb,
|
||||
user_tab_cols ta, user_tab_cols tb
|
||||
WHERE user_constraints.table_name = %s AND
|
||||
ta.table_name = %s AND
|
||||
ta.column_name = ca.column_name AND
|
||||
ca.table_name = %s AND
|
||||
user_constraints.constraint_name = ca.constraint_name AND
|
||||
user_constraints.r_constraint_name = cb.constraint_name AND
|
||||
cb.table_name = tb.table_name AND
|
||||
cb.column_name = tb.column_name AND
|
||||
ca.position = cb.position""", [table_name, table_name, table_name])
|
||||
|
||||
relations = {}
|
||||
for row in cursor.fetchall():
|
||||
relations[row[0]] = (row[2], row[1])
|
||||
return relations
|
||||
|
||||
def get_indexes(cursor, table_name):
|
||||
"""
|
||||
@ -31,20 +53,46 @@ def get_indexes(cursor, table_name):
|
||||
{'primary_key': boolean representing whether it's the primary key,
|
||||
'unique': boolean representing whether it's a unique index}
|
||||
"""
|
||||
raise NotImplementedError
|
||||
# This query retrieves each index on the given table, including the
|
||||
# first associated field name
|
||||
# "We were in the nick of time; you were in great peril!"
|
||||
sql = """
|
||||
WITH primarycols AS (
|
||||
SELECT user_cons_columns.table_name, user_cons_columns.column_name, 1 AS PRIMARYCOL
|
||||
FROM user_cons_columns, user_constraints
|
||||
WHERE user_cons_columns.constraint_name = user_constraints.constraint_name AND
|
||||
user_constraints.constraint_type = 'P' AND
|
||||
user_cons_columns.table_name = %s),
|
||||
uniquecols AS (
|
||||
SELECT user_ind_columns.table_name, user_ind_columns.column_name, 1 AS UNIQUECOL
|
||||
FROM user_indexes, user_ind_columns
|
||||
WHERE uniqueness = 'UNIQUE' AND
|
||||
user_indexes.index_name = user_ind_columns.index_name AND
|
||||
user_ind_columns.table_name = %s)
|
||||
SELECT allcols.column_name, primarycols.primarycol, uniquecols.UNIQUECOL
|
||||
FROM (SELECT column_name FROM primarycols UNION SELECT column_name FROM
|
||||
uniquecols) allcols,
|
||||
primarycols, uniquecols
|
||||
WHERE allcols.column_name = primarycols.column_name (+) AND
|
||||
allcols.column_name = uniquecols.column_name (+)
|
||||
"""
|
||||
cursor.execute(sql, [table_name, table_name])
|
||||
indexes = {}
|
||||
for row in cursor.fetchall():
|
||||
# row[1] (idx.indkey) is stored in the DB as an array. It comes out as
|
||||
# a string of space-separated integers. This designates the field
|
||||
# indexes (1-based) of the fields that have indexes on the table.
|
||||
# Here, we skip any indexes across multiple fields.
|
||||
indexes[row[0]] = {'primary_key': row[1], 'unique': row[2]}
|
||||
return indexes
|
||||
|
||||
# Maps type codes to Django Field types.
|
||||
# Maps type objects to Django Field types.
|
||||
DATA_TYPES_REVERSE = {
|
||||
16: 'BooleanField',
|
||||
21: 'SmallIntegerField',
|
||||
23: 'IntegerField',
|
||||
25: 'TextField',
|
||||
869: 'IPAddressField',
|
||||
1043: 'CharField',
|
||||
1082: 'DateField',
|
||||
1083: 'TimeField',
|
||||
1114: 'DateTimeField',
|
||||
1184: 'DateTimeField',
|
||||
1266: 'TimeField',
|
||||
1700: 'DecimalField',
|
||||
cx_Oracle.CLOB: 'TextField',
|
||||
cx_Oracle.DATETIME: 'DateTimeField',
|
||||
cx_Oracle.FIXED_CHAR: 'CharField',
|
||||
cx_Oracle.NCLOB: 'TextField',
|
||||
cx_Oracle.NUMBER: 'DecimalField',
|
||||
cx_Oracle.STRING: 'CharField',
|
||||
cx_Oracle.TIMESTAMP: 'DateTimeField',
|
||||
}
|
||||
|
@ -105,7 +105,14 @@ class DatabaseWrapper(local):
|
||||
self.connection.close()
|
||||
self.connection = None
|
||||
|
||||
allows_group_by_ordinal = True
|
||||
allows_unique_and_pk = True
|
||||
autoindexes_primary_keys = True
|
||||
needs_datetime_string_cast = True
|
||||
needs_upper_for_iops = False
|
||||
supports_constraints = True
|
||||
supports_tablespaces = False
|
||||
uses_case_insensitive_names = False
|
||||
|
||||
def quote_name(name):
|
||||
if name.startswith('"') and name.endswith('"'):
|
||||
@ -138,6 +145,9 @@ def get_date_trunc_sql(lookup_type, field_name):
|
||||
# http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC
|
||||
return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name)
|
||||
|
||||
def get_datetime_cast_sql():
|
||||
return None
|
||||
|
||||
def get_limit_offset_sql(limit, offset=None):
|
||||
sql = "LIMIT %s" % limit
|
||||
if offset and offset != 0:
|
||||
@ -159,6 +169,15 @@ def get_drop_foreignkey_sql():
|
||||
def get_pk_default_value():
|
||||
return "DEFAULT"
|
||||
|
||||
def get_max_name_length():
|
||||
return None
|
||||
|
||||
def get_start_transaction_sql():
|
||||
return "BEGIN;"
|
||||
|
||||
def get_autoinc_sql(table):
|
||||
return None
|
||||
|
||||
def get_sql_flush(style, tables, sequences):
|
||||
"""Return a list of SQL statements required to remove all data from
|
||||
all tables in the database (without actually removing the tables
|
||||
|
@ -73,7 +73,14 @@ class DatabaseWrapper(local):
|
||||
self.connection.close()
|
||||
self.connection = None
|
||||
|
||||
allows_group_by_ordinal = True
|
||||
allows_unique_and_pk = True
|
||||
autoindexes_primary_keys = True
|
||||
needs_datetime_string_cast = False
|
||||
needs_upper_for_iops = False
|
||||
supports_constraints = True
|
||||
supports_tablespaces = False
|
||||
uses_case_insensitive_names = False
|
||||
|
||||
def quote_name(name):
|
||||
if name.startswith('"') and name.endswith('"'):
|
||||
@ -98,6 +105,9 @@ def get_date_trunc_sql(lookup_type, field_name):
|
||||
# http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC
|
||||
return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name)
|
||||
|
||||
def get_datetime_cast_sql():
|
||||
return None
|
||||
|
||||
def get_limit_offset_sql(limit, offset=None):
|
||||
sql = "LIMIT %s" % limit
|
||||
if offset and offset != 0:
|
||||
@ -119,6 +129,15 @@ def get_drop_foreignkey_sql():
|
||||
def get_pk_default_value():
|
||||
return "DEFAULT"
|
||||
|
||||
def get_max_name_length():
|
||||
return None
|
||||
|
||||
def get_start_transaction_sql():
|
||||
return "BEGIN;"
|
||||
|
||||
def get_autoinc_sql(table):
|
||||
return None
|
||||
|
||||
def get_sql_flush(style, tables, sequences):
|
||||
"""Return a list of SQL statements required to remove all data from
|
||||
all tables in the database (without actually removing the tables
|
||||
|
@ -107,7 +107,14 @@ class SQLiteCursorWrapper(Database.Cursor):
|
||||
def convert_query(self, query, num_params):
|
||||
return query % tuple("?" * num_params)
|
||||
|
||||
allows_group_by_ordinal = True
|
||||
allows_unique_and_pk = True
|
||||
autoindexes_primary_keys = True
|
||||
needs_datetime_string_cast = True
|
||||
needs_upper_for_iops = False
|
||||
supports_constraints = False
|
||||
supports_tablespaces = False
|
||||
uses_case_insensitive_names = False
|
||||
|
||||
def quote_name(name):
|
||||
if name.startswith('"') and name.endswith('"'):
|
||||
@ -139,6 +146,9 @@ def get_date_trunc_sql(lookup_type, field_name):
|
||||
# sqlite doesn't support DATE_TRUNC, so we fake it as above.
|
||||
return 'django_date_trunc("%s", %s)' % (lookup_type.lower(), field_name)
|
||||
|
||||
def get_datetime_cast_sql():
|
||||
return None
|
||||
|
||||
def get_limit_offset_sql(limit, offset=None):
|
||||
sql = "LIMIT %s" % limit
|
||||
if offset and offset != 0:
|
||||
@ -160,6 +170,15 @@ def get_drop_foreignkey_sql():
|
||||
def get_pk_default_value():
|
||||
return "NULL"
|
||||
|
||||
def get_max_name_length():
|
||||
return None
|
||||
|
||||
def get_start_transaction_sql():
|
||||
return "BEGIN;"
|
||||
|
||||
def get_autoinc_sql(table):
|
||||
return None
|
||||
|
||||
def get_sql_flush(style, tables, sequences):
|
||||
"""Return a list of SQL statements required to remove all data from
|
||||
all tables in the database (without actually removing the tables
|
||||
|
@ -1,4 +1,5 @@
|
||||
import datetime
|
||||
import md5
|
||||
from time import time
|
||||
|
||||
try:
|
||||
@ -107,6 +108,16 @@ def rev_typecast_decimal(d):
|
||||
return None
|
||||
return str(d)
|
||||
|
||||
def truncate_name(name, length=None):
|
||||
"""Shortens a string to a repeatable mangled version with the given length.
|
||||
"""
|
||||
if length is None or len(name) <= length:
|
||||
return name
|
||||
|
||||
hash = md5.md5(name).hexdigest()[:4]
|
||||
|
||||
return '%s%s' % (name[:length-4], hash)
|
||||
|
||||
##################################################################################
|
||||
# Helper functions for dictfetch* for databases that don't natively support them #
|
||||
##################################################################################
|
||||
|
@ -210,17 +210,18 @@ class Model(object):
|
||||
record_exists = True
|
||||
if pk_set:
|
||||
# Determine whether a record with the primary key already exists.
|
||||
cursor.execute("SELECT 1 FROM %s WHERE %s=%%s LIMIT 1" % \
|
||||
(backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column)), [pk_val])
|
||||
cursor.execute("SELECT COUNT(*) FROM %s WHERE %s=%%s" % \
|
||||
(backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column)),
|
||||
self._meta.pk.get_db_prep_lookup('exact', pk_val))
|
||||
# If it does already exist, do an UPDATE.
|
||||
if cursor.fetchone():
|
||||
if cursor.fetchone()[0] > 0:
|
||||
db_values = [f.get_db_prep_save(f.pre_save(self, False)) for f in non_pks]
|
||||
if db_values:
|
||||
cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % \
|
||||
(backend.quote_name(self._meta.db_table),
|
||||
','.join(['%s=%%s' % backend.quote_name(f.column) for f in non_pks]),
|
||||
backend.quote_name(self._meta.pk.column)),
|
||||
db_values + [pk_val])
|
||||
db_values + self._meta.pk.get_db_prep_lookup('exact', pk_val))
|
||||
else:
|
||||
record_exists = False
|
||||
if not pk_set or not record_exists:
|
||||
|
@ -74,12 +74,16 @@ class Field(object):
|
||||
core=False, rel=None, default=NOT_PROVIDED, editable=True, serialize=True,
|
||||
prepopulate_from=None, unique_for_date=None, unique_for_month=None,
|
||||
unique_for_year=None, validator_list=None, choices=None, radio_admin=None,
|
||||
help_text='', db_column=None):
|
||||
help_text='', db_column=None, db_tablespace=None):
|
||||
self.name = name
|
||||
self.verbose_name = verbose_name
|
||||
self.primary_key = primary_key
|
||||
self.maxlength, self.unique = maxlength, unique
|
||||
self.blank, self.null = blank, null
|
||||
# Oracle treats the empty string ('') as null, so coerce the null
|
||||
# option whenever '' is a possible value.
|
||||
if self.empty_strings_allowed and settings.DATABASE_ENGINE == 'oracle':
|
||||
self.null = True
|
||||
self.core, self.rel, self.default = core, rel, default
|
||||
self.editable = editable
|
||||
self.serialize = serialize
|
||||
@ -91,6 +95,7 @@ class Field(object):
|
||||
self.radio_admin = radio_admin
|
||||
self.help_text = help_text
|
||||
self.db_column = db_column
|
||||
self.db_tablespace = db_tablespace
|
||||
|
||||
# Set db_index to True if the field has a relationship and doesn't explicitly set db_index.
|
||||
self.db_index = db_index
|
||||
@ -201,7 +206,7 @@ class Field(object):
|
||||
if callable(self.default):
|
||||
return self.default()
|
||||
return self.default
|
||||
if not self.empty_strings_allowed or self.null:
|
||||
if not self.empty_strings_allowed or (self.null and settings.DATABASE_ENGINE != 'oracle'):
|
||||
return None
|
||||
return ""
|
||||
|
||||
@ -806,6 +811,7 @@ class IPAddressField(Field):
|
||||
validators.isValidIPAddress4(field_data, None)
|
||||
|
||||
class NullBooleanField(Field):
|
||||
empty_strings_allowed = False
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs['null'] = True
|
||||
Field.__init__(self, *args, **kwargs)
|
||||
@ -875,10 +881,18 @@ class TimeField(Field):
|
||||
Field.__init__(self, verbose_name, name, **kwargs)
|
||||
|
||||
def get_db_prep_lookup(self, lookup_type, value):
|
||||
if lookup_type == 'range':
|
||||
value = [str(v) for v in value]
|
||||
if settings.DATABASE_ENGINE == 'oracle':
|
||||
# Oracle requires a date in order to parse.
|
||||
def prep(value):
|
||||
if isinstance(value, datetime.time):
|
||||
value = datetime.datetime.combine(datetime.date(1900, 1, 1), value)
|
||||
return str(value)
|
||||
else:
|
||||
value = str(value)
|
||||
prep = str
|
||||
if lookup_type == 'range':
|
||||
value = [prep(v) for v in value]
|
||||
else:
|
||||
value = prep(value)
|
||||
return Field.get_db_prep_lookup(self, lookup_type, value)
|
||||
|
||||
def pre_save(self, model_instance, add):
|
||||
@ -896,6 +910,14 @@ class TimeField(Field):
|
||||
# doesn't support microseconds.
|
||||
if settings.DATABASE_ENGINE == 'mysql' and hasattr(value, 'microsecond'):
|
||||
value = value.replace(microsecond=0)
|
||||
if settings.DATABASE_ENGINE == 'oracle':
|
||||
# cx_Oracle expects a datetime.datetime to persist into TIMESTAMP field.
|
||||
if isinstance(value, datetime.time):
|
||||
value = datetime.datetime(1900, 1, 1, value.hour, value.minute,
|
||||
value.second, value.microsecond)
|
||||
elif isinstance(value, basestring):
|
||||
value = datetime.datetime(*(time.strptime(value, '%H:%M:%S')[:6]))
|
||||
else:
|
||||
value = str(value)
|
||||
return Field.get_db_prep_save(self, value)
|
||||
|
||||
|
@ -10,9 +10,10 @@ from django import oldforms
|
||||
from django import newforms as forms
|
||||
from django.dispatch import dispatcher
|
||||
|
||||
# For Python 2.3
|
||||
if not hasattr(__builtins__, 'set'):
|
||||
from sets import Set as set
|
||||
try:
|
||||
set
|
||||
except NameError:
|
||||
from sets import Set as set # Python 2.3 fallback
|
||||
|
||||
# Values for Relation.edit_inline.
|
||||
TABULAR, STACKED = 1, 2
|
||||
@ -335,10 +336,7 @@ def create_many_related_manager(superclass):
|
||||
(target_col_name, self.join_table, source_col_name,
|
||||
target_col_name, ",".join(['%s'] * len(new_ids))),
|
||||
[self._pk_val] + list(new_ids))
|
||||
if cursor.rowcount is not None and cursor.rowcount != 0:
|
||||
existing_ids = set([row[0] for row in cursor.fetchmany(cursor.rowcount)])
|
||||
else:
|
||||
existing_ids = set()
|
||||
existing_ids = set([row[0] for row in cursor.fetchall()])
|
||||
|
||||
# Add the ones that aren't there already
|
||||
for obj_id in (new_ids - existing_ids):
|
||||
|
@ -13,7 +13,7 @@ get_verbose_name = lambda class_name: re.sub('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|
|
||||
|
||||
DEFAULT_NAMES = ('verbose_name', 'db_table', 'ordering',
|
||||
'unique_together', 'permissions', 'get_latest_by',
|
||||
'order_with_respect_to', 'app_label')
|
||||
'order_with_respect_to', 'app_label', 'db_tablespace')
|
||||
|
||||
class Options(object):
|
||||
def __init__(self, meta):
|
||||
@ -27,6 +27,7 @@ class Options(object):
|
||||
self.object_name, self.app_label = None, None
|
||||
self.get_latest_by = None
|
||||
self.order_with_respect_to = None
|
||||
self.db_tablespace = None
|
||||
self.admin = None
|
||||
self.meta = meta
|
||||
self.pk = None
|
||||
@ -59,6 +60,8 @@ class Options(object):
|
||||
del self.meta
|
||||
|
||||
def _prepare(self, model):
|
||||
from django.db import backend
|
||||
from django.db.backends.util import truncate_name
|
||||
if self.order_with_respect_to:
|
||||
self.order_with_respect_to = self.get_field(self.order_with_respect_to)
|
||||
self.ordering = ('_order',)
|
||||
@ -73,6 +76,8 @@ class Options(object):
|
||||
# If the db_table wasn't provided, use the app_label + module_name.
|
||||
if not self.db_table:
|
||||
self.db_table = "%s_%s" % (self.app_label, self.module_name)
|
||||
self.db_table = truncate_name(self.db_table,
|
||||
backend.get_max_name_length())
|
||||
|
||||
def add_field(self, field):
|
||||
# Insert the given field in the order in which it was created, using
|
||||
|
@ -4,12 +4,14 @@ from django.db.models import signals, loading
|
||||
from django.dispatch import dispatcher
|
||||
from django.utils.datastructures import SortedDict
|
||||
from django.contrib.contenttypes import generic
|
||||
import datetime
|
||||
import operator
|
||||
import re
|
||||
|
||||
# For Python 2.3
|
||||
if not hasattr(__builtins__, 'set'):
|
||||
from sets import Set as set
|
||||
try:
|
||||
set
|
||||
except NameError:
|
||||
from sets import Set as set # Python 2.3 fallback
|
||||
|
||||
# The string constant used to separate query parts
|
||||
LOOKUP_SEPARATOR = '__'
|
||||
@ -77,7 +79,7 @@ def quote_only_if_word(word):
|
||||
else:
|
||||
return backend.quote_name(word)
|
||||
|
||||
class QuerySet(object):
|
||||
class _QuerySet(object):
|
||||
"Represents a lazy database lookup for a set of objects"
|
||||
def __init__(self, model=None):
|
||||
self.model = model
|
||||
@ -181,13 +183,18 @@ class QuerySet(object):
|
||||
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
|
||||
|
||||
fill_cache = self._select_related
|
||||
index_end = len(self.model._meta.fields)
|
||||
fields = self.model._meta.fields
|
||||
index_end = len(fields)
|
||||
has_resolve_columns = hasattr(self, 'resolve_columns')
|
||||
while 1:
|
||||
rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
|
||||
if not rows:
|
||||
raise StopIteration
|
||||
for row in rows:
|
||||
if has_resolve_columns:
|
||||
row = self.resolve_columns(row, fields)
|
||||
if fill_cache:
|
||||
obj, index_end = get_cached_row(klass=self.model, row=row,
|
||||
index_start=0, max_depth=self._max_related_depth)
|
||||
@ -551,6 +558,12 @@ class QuerySet(object):
|
||||
|
||||
return select, " ".join(sql), params
|
||||
|
||||
# Use the backend's QuerySet class if it defines one, otherwise use _QuerySet.
|
||||
if hasattr(backend, 'get_query_set_class'):
|
||||
QuerySet = backend.get_query_set_class(_QuerySet)
|
||||
else:
|
||||
QuerySet = _QuerySet
|
||||
|
||||
class ValuesQuerySet(QuerySet):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ValuesQuerySet, self).__init__(*args, **kwargs)
|
||||
@ -565,35 +578,38 @@ class ValuesQuerySet(QuerySet):
|
||||
|
||||
# self._fields is a list of field names to fetch.
|
||||
if self._fields:
|
||||
#columns = [self.model._meta.get_field(f, many_to_many=False).column for f in self._fields]
|
||||
if not self._select:
|
||||
columns = [self.model._meta.get_field(f, many_to_many=False).column for f in self._fields]
|
||||
fields = [self.model._meta.get_field(f, many_to_many=False) for f in self._fields]
|
||||
else:
|
||||
columns = []
|
||||
fields = []
|
||||
for f in self._fields:
|
||||
if f in [field.name for field in self.model._meta.fields]:
|
||||
columns.append( self.model._meta.get_field(f, many_to_many=False).column )
|
||||
fields.append(self.model._meta.get_field(f, many_to_many=False))
|
||||
elif not self._select.has_key( f ):
|
||||
raise FieldDoesNotExist, '%s has no field named %r' % ( self.model._meta.object_name, f )
|
||||
|
||||
field_names = self._fields
|
||||
else: # Default to all fields.
|
||||
columns = [f.column for f in self.model._meta.fields]
|
||||
field_names = [f.attname for f in self.model._meta.fields]
|
||||
fields = self.model._meta.fields
|
||||
field_names = [f.attname for f in fields]
|
||||
|
||||
columns = [f.column for f in fields]
|
||||
select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns]
|
||||
|
||||
# Add any additional SELECTs.
|
||||
if self._select:
|
||||
select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), backend.quote_name(s[0])) for s in self._select.items()])
|
||||
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
|
||||
|
||||
has_resolve_columns = hasattr(self, 'resolve_columns')
|
||||
while 1:
|
||||
rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
|
||||
if not rows:
|
||||
raise StopIteration
|
||||
for row in rows:
|
||||
if has_resolve_columns:
|
||||
row = self.resolve_columns(row, fields)
|
||||
yield dict(zip(field_names, row))
|
||||
|
||||
def _clone(self, klass=None, **kwargs):
|
||||
@ -604,25 +620,49 @@ class ValuesQuerySet(QuerySet):
|
||||
class DateQuerySet(QuerySet):
|
||||
def iterator(self):
|
||||
from django.db.backends.util import typecast_timestamp
|
||||
from django.db.models.fields import DateTimeField
|
||||
self._order_by = () # Clear this because it'll mess things up otherwise.
|
||||
if self._field.null:
|
||||
self._where.append('%s.%s IS NOT NULL' % \
|
||||
(backend.quote_name(self.model._meta.db_table), backend.quote_name(self._field.column)))
|
||||
|
||||
try:
|
||||
select, sql, params = self._get_sql_clause()
|
||||
except EmptyResultSet:
|
||||
raise StopIteration
|
||||
|
||||
sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1 %s' % \
|
||||
table_name = backend.quote_name(self.model._meta.db_table)
|
||||
field_name = backend.quote_name(self._field.column)
|
||||
|
||||
if backend.allows_group_by_ordinal:
|
||||
group_by = '1'
|
||||
else:
|
||||
group_by = backend.get_date_trunc_sql(self._kind,
|
||||
'%s.%s' % (table_name, field_name))
|
||||
|
||||
sql = 'SELECT %s %s GROUP BY %s ORDER BY 1 %s' % \
|
||||
(backend.get_date_trunc_sql(self._kind, '%s.%s' % (backend.quote_name(self.model._meta.db_table),
|
||||
backend.quote_name(self._field.column))), sql, self._order)
|
||||
backend.quote_name(self._field.column))), sql, group_by, self._order)
|
||||
cursor = connection.cursor()
|
||||
cursor.execute(sql, params)
|
||||
# We have to manually run typecast_timestamp(str()) on the results, because
|
||||
# MySQL doesn't automatically cast the result of date functions as datetime
|
||||
# objects -- MySQL returns the values as strings, instead.
|
||||
return [typecast_timestamp(str(row[0])) for row in cursor.fetchall()]
|
||||
|
||||
has_resolve_columns = hasattr(self, 'resolve_columns')
|
||||
needs_datetime_string_cast = backend.needs_datetime_string_cast
|
||||
dates = []
|
||||
# It would be better to use self._field here instead of DateTimeField(),
|
||||
# but in Oracle that will result in a list of datetime.date instead of
|
||||
# datetime.datetime.
|
||||
fields = [DateTimeField()]
|
||||
while 1:
|
||||
rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
|
||||
if not rows:
|
||||
return dates
|
||||
for row in rows:
|
||||
date = row[0]
|
||||
if has_resolve_columns:
|
||||
date = self.resolve_columns([date], fields)[0]
|
||||
elif needs_datetime_string_cast:
|
||||
date = typecast_timestamp(str(date))
|
||||
dates.append(date)
|
||||
|
||||
def _clone(self, klass=None, **kwargs):
|
||||
c = super(DateQuerySet, self)._clone(klass, **kwargs)
|
||||
@ -730,8 +770,17 @@ def get_where_clause(lookup_type, table_prefix, field_name, value):
|
||||
if table_prefix.endswith('.'):
|
||||
table_prefix = backend.quote_name(table_prefix[:-1])+'.'
|
||||
field_name = backend.quote_name(field_name)
|
||||
if type(value) == datetime.datetime and backend.get_datetime_cast_sql():
|
||||
cast_sql = backend.get_datetime_cast_sql()
|
||||
else:
|
||||
cast_sql = '%s'
|
||||
if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith') and backend.needs_upper_for_iops:
|
||||
format = 'UPPER(%s%s) %s'
|
||||
else:
|
||||
format = '%s%s %s'
|
||||
try:
|
||||
return '%s%s %s' % (table_prefix, field_name, (backend.OPERATOR_MAPPING[lookup_type] % '%s'))
|
||||
return format % (table_prefix, field_name,
|
||||
backend.OPERATOR_MAPPING[lookup_type] % cast_sql)
|
||||
except KeyError:
|
||||
pass
|
||||
if lookup_type == 'in':
|
||||
|
@ -222,12 +222,6 @@ class HttpResponse(object):
|
||||
content = ''.join(self._container)
|
||||
if isinstance(content, unicode):
|
||||
content = content.encode(self._charset)
|
||||
|
||||
# If self._container was an iterator, we have just exhausted it, so we
|
||||
# need to save the results for anything else that needs access
|
||||
if not self._is_string:
|
||||
self._container = [content]
|
||||
self._is_string = True
|
||||
return content
|
||||
|
||||
def _set_content(self, value):
|
||||
@ -237,10 +231,14 @@ class HttpResponse(object):
|
||||
content = property(_get_content, _set_content)
|
||||
|
||||
def __iter__(self):
|
||||
for chunk in self._container:
|
||||
self._iterator = self._container.__iter__()
|
||||
return self
|
||||
|
||||
def next(self):
|
||||
chunk = self._iterator.next()
|
||||
if isinstance(chunk, unicode):
|
||||
chunk = chunk.encode(self._charset)
|
||||
yield chunk
|
||||
return chunk
|
||||
|
||||
def close(self):
|
||||
if hasattr(self._container, 'close'):
|
||||
|
@ -27,7 +27,7 @@ __all__ = (
|
||||
EMPTY_VALUES = (None, '')
|
||||
|
||||
try:
|
||||
set # Only available in Python 2.4+
|
||||
set
|
||||
except NameError:
|
||||
from sets import Set as set # Python 2.3 fallback
|
||||
|
||||
@ -516,11 +516,13 @@ class MultiValueField(Field):
|
||||
"""
|
||||
clean_data = []
|
||||
errors = ErrorList()
|
||||
if self.required and not value:
|
||||
if not value or isinstance(value, (list, tuple)):
|
||||
if not value or not [v for v in value if v not in EMPTY_VALUES]:
|
||||
if self.required:
|
||||
raise ValidationError(gettext(u'This field is required.'))
|
||||
elif not self.required and not value:
|
||||
else:
|
||||
return self.compress([])
|
||||
if not isinstance(value, (list, tuple)):
|
||||
else:
|
||||
raise ValidationError(gettext(u'Enter a list of values.'))
|
||||
for i, field in enumerate(self.fields):
|
||||
try:
|
||||
@ -558,5 +560,11 @@ class SplitDateTimeField(MultiValueField):
|
||||
|
||||
def compress(self, data_list):
|
||||
if data_list:
|
||||
# Raise a validation error if time or date is empty
|
||||
# (possible if SplitDateTimeField has required=False).
|
||||
if data_list[0] in EMPTY_VALUES:
|
||||
raise ValidationError(gettext(u'Enter a valid date.'))
|
||||
if data_list[1] in EMPTY_VALUES:
|
||||
raise ValidationError(gettext(u'Enter a valid time.'))
|
||||
return datetime.datetime.combine(*data_list)
|
||||
return None
|
||||
|
@ -159,7 +159,7 @@ class BaseForm(StrAndUnicode):
|
||||
|
||||
def as_p(self):
|
||||
"Returns this form rendered as HTML <p>s."
|
||||
return self._html_output(u'<p>%(label)s %(field)s%(help_text)s</p>', u'<p>%s</p>', '</p>', u' %s', True)
|
||||
return self._html_output(u'<p>%(label)s %(field)s%(help_text)s</p>', u'%s', '</p>', u' %s', True)
|
||||
|
||||
def non_field_errors(self):
|
||||
"""
|
||||
|
@ -3,16 +3,15 @@ HTML Widget classes
|
||||
"""
|
||||
|
||||
try:
|
||||
set # Only available in Python 2.4+
|
||||
set
|
||||
except NameError:
|
||||
from sets import Set as set # Python 2.3 fallback
|
||||
from itertools import chain
|
||||
|
||||
from itertools import chain
|
||||
from django.utils.datastructures import MultiValueDict
|
||||
from django.utils.html import escape
|
||||
from django.utils.translation import gettext
|
||||
from django.utils.encoding import StrAndUnicode, smart_unicode
|
||||
|
||||
from util import flatatt
|
||||
|
||||
__all__ = (
|
||||
|
@ -309,10 +309,6 @@ class FormField(object):
|
||||
return data
|
||||
html2python = staticmethod(html2python)
|
||||
|
||||
def iter_render(self, data):
|
||||
# this even needed?
|
||||
return (self.render(data),)
|
||||
|
||||
def render(self, data):
|
||||
raise NotImplementedError
|
||||
|
||||
|
@ -7,7 +7,7 @@ from django.http import HttpResponse, Http404
|
||||
from django.db.models.manager import Manager
|
||||
|
||||
def render_to_response(*args, **kwargs):
|
||||
return HttpResponse(loader.render_to_iter(*args, **kwargs))
|
||||
return HttpResponse(loader.render_to_string(*args, **kwargs))
|
||||
load_and_render = render_to_response # For backwards compatibility.
|
||||
|
||||
def get_object_or_404(klass, *args, **kwargs):
|
||||
|
@ -55,7 +55,6 @@ times with multiple contexts)
|
||||
'\n<html>\n\n</html>\n'
|
||||
"""
|
||||
import re
|
||||
import types
|
||||
from inspect import getargspec
|
||||
from django.conf import settings
|
||||
from django.template.context import Context, RequestContext, ContextPopException
|
||||
@ -168,12 +167,9 @@ class Template(object):
|
||||
for subnode in node:
|
||||
yield subnode
|
||||
|
||||
def iter_render(self, context):
|
||||
"Display stage -- can be called many times"
|
||||
return self.nodelist.iter_render(context)
|
||||
|
||||
def render(self, context):
|
||||
return ''.join(self.iter_render(context))
|
||||
"Display stage -- can be called many times"
|
||||
return self.nodelist.render(context)
|
||||
|
||||
def compile_string(template_string, origin):
|
||||
"Compiles template_string into NodeList ready for rendering"
|
||||
@ -699,26 +695,10 @@ def resolve_variable(path, context):
|
||||
del bits[0]
|
||||
return current
|
||||
|
||||
class NodeBase(type):
|
||||
def __new__(cls, name, bases, attrs):
|
||||
"""
|
||||
Ensures that either a 'render' or 'render_iter' method is defined on
|
||||
any Node sub-class. This avoids potential infinite loops at runtime.
|
||||
"""
|
||||
if not (isinstance(attrs.get('render'), types.FunctionType) or
|
||||
isinstance(attrs.get('iter_render'), types.FunctionType)):
|
||||
raise TypeError('Unable to create Node subclass without either "render" or "iter_render" method.')
|
||||
return type.__new__(cls, name, bases, attrs)
|
||||
|
||||
class Node(object):
|
||||
__metaclass__ = NodeBase
|
||||
|
||||
def iter_render(self, context):
|
||||
return (self.render(context),)
|
||||
|
||||
def render(self, context):
|
||||
"Return the node rendered as a string"
|
||||
return ''.join(self.iter_render(context))
|
||||
pass
|
||||
|
||||
def __iter__(self):
|
||||
yield self
|
||||
@ -734,12 +714,13 @@ class Node(object):
|
||||
|
||||
class NodeList(list):
|
||||
def render(self, context):
|
||||
return ''.join(self.iter_render(context))
|
||||
|
||||
def iter_render(self, context):
|
||||
bits = []
|
||||
for node in self:
|
||||
for chunk in node.iter_render(context):
|
||||
yield chunk
|
||||
if isinstance(node, Node):
|
||||
bits.append(self.render_node(node, context))
|
||||
else:
|
||||
bits.append(node)
|
||||
return ''.join(bits)
|
||||
|
||||
def get_nodes_by_type(self, nodetype):
|
||||
"Return a list of all nodes of the given type"
|
||||
@ -748,15 +729,13 @@ class NodeList(list):
|
||||
nodes.extend(node.get_nodes_by_type(nodetype))
|
||||
return nodes
|
||||
|
||||
def render_node(self, node, context):
|
||||
return(node.render(context))
|
||||
|
||||
class DebugNodeList(NodeList):
|
||||
def iter_render(self, context):
|
||||
for node in self:
|
||||
if not isinstance(node, Node):
|
||||
yield node
|
||||
continue
|
||||
def render_node(self, node, context):
|
||||
try:
|
||||
for chunk in node.iter_render(context):
|
||||
yield chunk
|
||||
result = node.render(context)
|
||||
except TemplateSyntaxError, e:
|
||||
if not hasattr(e, 'source'):
|
||||
e.source = node.source
|
||||
@ -767,6 +746,7 @@ class DebugNodeList(NodeList):
|
||||
wrapped.source = node.source
|
||||
wrapped.exc_info = exc_info()
|
||||
raise wrapped
|
||||
return result
|
||||
|
||||
class TextNode(Node):
|
||||
def __init__(self, s):
|
||||
@ -775,9 +755,6 @@ class TextNode(Node):
|
||||
def __repr__(self):
|
||||
return "<Text Node: '%s'>" % self.s[:25]
|
||||
|
||||
def iter_render(self, context):
|
||||
return (self.s,)
|
||||
|
||||
def render(self, context):
|
||||
return self.s
|
||||
|
||||
@ -801,9 +778,6 @@ class VariableNode(Node):
|
||||
else:
|
||||
return output
|
||||
|
||||
def iter_render(self, context):
|
||||
return (self.render(context),)
|
||||
|
||||
def render(self, context):
|
||||
output = self.filter_expression.resolve(context)
|
||||
return self.encode_output(output)
|
||||
@ -892,9 +866,6 @@ class Library(object):
|
||||
def __init__(self, vars_to_resolve):
|
||||
self.vars_to_resolve = vars_to_resolve
|
||||
|
||||
#def iter_render(self, context):
|
||||
# return (self.render(context),)
|
||||
|
||||
def render(self, context):
|
||||
resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve]
|
||||
return func(*resolved_vars)
|
||||
@ -917,7 +888,7 @@ class Library(object):
|
||||
def __init__(self, vars_to_resolve):
|
||||
self.vars_to_resolve = vars_to_resolve
|
||||
|
||||
def iter_render(self, context):
|
||||
def render(self, context):
|
||||
resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve]
|
||||
if takes_context:
|
||||
args = [context] + resolved_vars
|
||||
@ -933,7 +904,7 @@ class Library(object):
|
||||
else:
|
||||
t = get_template(file_name)
|
||||
self.nodelist = t.nodelist
|
||||
return self.nodelist.iter_render(context_class(dict))
|
||||
return self.nodelist.render(context_class(dict))
|
||||
|
||||
compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, InclusionNode)
|
||||
compile_func.__doc__ = func.__doc__
|
||||
|
@ -8,18 +8,16 @@ from django.utils.itercompat import groupby
|
||||
import sys
|
||||
import re
|
||||
|
||||
if not hasattr(__builtins__, 'reversed'):
|
||||
# For Python 2.3.
|
||||
# From http://www.python.org/doc/current/tut/node11.html
|
||||
def reversed(data):
|
||||
for index in xrange(len(data)-1, -1, -1):
|
||||
yield data[index]
|
||||
try:
|
||||
reversed
|
||||
except NameError:
|
||||
from django.utils.itercompat import reversed # Python 2.3 fallback
|
||||
|
||||
register = Library()
|
||||
|
||||
class CommentNode(Node):
|
||||
def iter_render(self, context):
|
||||
return ()
|
||||
def render(self, context):
|
||||
return ''
|
||||
|
||||
class CycleNode(Node):
|
||||
def __init__(self, cyclevars, variable_name=None):
|
||||
@ -28,9 +26,6 @@ class CycleNode(Node):
|
||||
self.counter = -1
|
||||
self.variable_name = variable_name
|
||||
|
||||
def iter_render(self, context):
|
||||
return (self.render(context),)
|
||||
|
||||
def render(self, context):
|
||||
self.counter += 1
|
||||
value = self.cyclevars[self.counter % self.cyclevars_len]
|
||||
@ -39,32 +34,29 @@ class CycleNode(Node):
|
||||
return value
|
||||
|
||||
class DebugNode(Node):
|
||||
def iter_render(self, context):
|
||||
def render(self, context):
|
||||
from pprint import pformat
|
||||
for val in context:
|
||||
yield pformat(val)
|
||||
yield "\n\n"
|
||||
yield pformat(sys.modules)
|
||||
output = [pformat(val) for val in context]
|
||||
output.append('\n\n')
|
||||
output.append(pformat(sys.modules))
|
||||
return ''.join(output)
|
||||
|
||||
class FilterNode(Node):
|
||||
def __init__(self, filter_expr, nodelist):
|
||||
self.filter_expr, self.nodelist = filter_expr, nodelist
|
||||
|
||||
def iter_render(self, context):
|
||||
def render(self, context):
|
||||
output = self.nodelist.render(context)
|
||||
# apply filters
|
||||
context.update({'var': output})
|
||||
filtered = self.filter_expr.resolve(context)
|
||||
context.pop()
|
||||
return (filtered,)
|
||||
return filtered
|
||||
|
||||
class FirstOfNode(Node):
|
||||
def __init__(self, vars):
|
||||
self.vars = vars
|
||||
|
||||
def iter_render(self, context):
|
||||
return (self.render(context),)
|
||||
|
||||
def render(self, context):
|
||||
for var in self.vars:
|
||||
try:
|
||||
@ -100,7 +92,8 @@ class ForNode(Node):
|
||||
nodes.extend(self.nodelist_loop.get_nodes_by_type(nodetype))
|
||||
return nodes
|
||||
|
||||
def iter_render(self, context):
|
||||
def render(self, context):
|
||||
nodelist = NodeList()
|
||||
if 'forloop' in context:
|
||||
parentloop = context['forloop']
|
||||
else:
|
||||
@ -108,12 +101,12 @@ class ForNode(Node):
|
||||
context.push()
|
||||
try:
|
||||
values = self.sequence.resolve(context, True)
|
||||
if values is None:
|
||||
values = ()
|
||||
elif not hasattr(values, '__len__'):
|
||||
values = list(values)
|
||||
except VariableDoesNotExist:
|
||||
values = ()
|
||||
values = []
|
||||
if values is None:
|
||||
values = []
|
||||
if not hasattr(values, '__len__'):
|
||||
values = list(values)
|
||||
len_values = len(values)
|
||||
if self.reversed:
|
||||
values = reversed(values)
|
||||
@ -132,17 +125,12 @@ class ForNode(Node):
|
||||
'parentloop': parentloop,
|
||||
}
|
||||
if unpack:
|
||||
# If there are multiple loop variables, unpack the item into
|
||||
# them.
|
||||
# If there are multiple loop variables, unpack the item into them.
|
||||
context.update(dict(zip(self.loopvars, item)))
|
||||
else:
|
||||
context[self.loopvars[0]] = item
|
||||
|
||||
# We inline this to avoid the overhead since ForNode is pretty
|
||||
# common.
|
||||
for node in self.nodelist_loop:
|
||||
for chunk in node.iter_render(context):
|
||||
yield chunk
|
||||
nodelist.append(node.render(context))
|
||||
if unpack:
|
||||
# The loop variables were pushed on to the context so pop them
|
||||
# off again. This is necessary because the tag lets the length
|
||||
@ -151,6 +139,7 @@ class ForNode(Node):
|
||||
# context.
|
||||
context.pop()
|
||||
context.pop()
|
||||
return nodelist.render(context)
|
||||
|
||||
class IfChangedNode(Node):
|
||||
def __init__(self, nodelist, *varlist):
|
||||
@ -158,7 +147,7 @@ class IfChangedNode(Node):
|
||||
self._last_seen = None
|
||||
self._varlist = varlist
|
||||
|
||||
def iter_render(self, context):
|
||||
def render(self, context):
|
||||
if 'forloop' in context and context['forloop']['first']:
|
||||
self._last_seen = None
|
||||
try:
|
||||
@ -176,9 +165,11 @@ class IfChangedNode(Node):
|
||||
self._last_seen = compare_to
|
||||
context.push()
|
||||
context['ifchanged'] = {'firstloop': firstloop}
|
||||
for chunk in self.nodelist.iter_render(context):
|
||||
yield chunk
|
||||
content = self.nodelist.render(context)
|
||||
context.pop()
|
||||
return content
|
||||
else:
|
||||
return ''
|
||||
|
||||
class IfEqualNode(Node):
|
||||
def __init__(self, var1, var2, nodelist_true, nodelist_false, negate):
|
||||
@ -189,7 +180,7 @@ class IfEqualNode(Node):
|
||||
def __repr__(self):
|
||||
return "<IfEqualNode>"
|
||||
|
||||
def iter_render(self, context):
|
||||
def render(self, context):
|
||||
try:
|
||||
val1 = resolve_variable(self.var1, context)
|
||||
except VariableDoesNotExist:
|
||||
@ -199,8 +190,8 @@ class IfEqualNode(Node):
|
||||
except VariableDoesNotExist:
|
||||
val2 = None
|
||||
if (self.negate and val1 != val2) or (not self.negate and val1 == val2):
|
||||
return self.nodelist_true.iter_render(context)
|
||||
return self.nodelist_false.iter_render(context)
|
||||
return self.nodelist_true.render(context)
|
||||
return self.nodelist_false.render(context)
|
||||
|
||||
class IfNode(Node):
|
||||
def __init__(self, bool_exprs, nodelist_true, nodelist_false, link_type):
|
||||
@ -225,7 +216,7 @@ class IfNode(Node):
|
||||
nodes.extend(self.nodelist_false.get_nodes_by_type(nodetype))
|
||||
return nodes
|
||||
|
||||
def iter_render(self, context):
|
||||
def render(self, context):
|
||||
if self.link_type == IfNode.LinkTypes.or_:
|
||||
for ifnot, bool_expr in self.bool_exprs:
|
||||
try:
|
||||
@ -233,8 +224,8 @@ class IfNode(Node):
|
||||
except VariableDoesNotExist:
|
||||
value = None
|
||||
if (value and not ifnot) or (ifnot and not value):
|
||||
return self.nodelist_true.iter_render(context)
|
||||
return self.nodelist_false.iter_render(context)
|
||||
return self.nodelist_true.render(context)
|
||||
return self.nodelist_false.render(context)
|
||||
else:
|
||||
for ifnot, bool_expr in self.bool_exprs:
|
||||
try:
|
||||
@ -242,8 +233,8 @@ class IfNode(Node):
|
||||
except VariableDoesNotExist:
|
||||
value = None
|
||||
if not ((value and not ifnot) or (ifnot and not value)):
|
||||
return self.nodelist_false.iter_render(context)
|
||||
return self.nodelist_true.iter_render(context)
|
||||
return self.nodelist_false.render(context)
|
||||
return self.nodelist_true.render(context)
|
||||
|
||||
class LinkTypes:
|
||||
and_ = 0,
|
||||
@ -254,16 +245,16 @@ class RegroupNode(Node):
|
||||
self.target, self.expression = target, expression
|
||||
self.var_name = var_name
|
||||
|
||||
def iter_render(self, context):
|
||||
def render(self, context):
|
||||
obj_list = self.target.resolve(context, True)
|
||||
if obj_list == None: # target_var wasn't found in context; fail silently
|
||||
context[self.var_name] = []
|
||||
return ()
|
||||
return ''
|
||||
# List of dictionaries in the format
|
||||
# {'grouper': 'key', 'list': [list of contents]}.
|
||||
context[self.var_name] = [{'grouper':key, 'list':list(val)} for key, val in
|
||||
groupby(obj_list, lambda v, f=self.expression.resolve: f(v, True))]
|
||||
return ()
|
||||
return ''
|
||||
|
||||
def include_is_allowed(filepath):
|
||||
for root in settings.ALLOWED_INCLUDE_ROOTS:
|
||||
@ -275,10 +266,10 @@ class SsiNode(Node):
|
||||
def __init__(self, filepath, parsed):
|
||||
self.filepath, self.parsed = filepath, parsed
|
||||
|
||||
def iter_render(self, context):
|
||||
def render(self, context):
|
||||
if not include_is_allowed(self.filepath):
|
||||
if settings.DEBUG:
|
||||
return ("[Didn't have permission to include file]",)
|
||||
return "[Didn't have permission to include file]"
|
||||
else:
|
||||
return '' # Fail silently for invalid includes.
|
||||
try:
|
||||
@ -289,25 +280,23 @@ class SsiNode(Node):
|
||||
output = ''
|
||||
if self.parsed:
|
||||
try:
|
||||
return Template(output, name=self.filepath).iter_render(context)
|
||||
t = Template(output, name=self.filepath)
|
||||
return t.render(context)
|
||||
except TemplateSyntaxError, e:
|
||||
if settings.DEBUG:
|
||||
return "[Included template had syntax error: %s]" % e
|
||||
else:
|
||||
return '' # Fail silently for invalid included templates.
|
||||
return (output,)
|
||||
return output
|
||||
|
||||
class LoadNode(Node):
|
||||
def iter_render(self, context):
|
||||
return ()
|
||||
def render(self, context):
|
||||
return ''
|
||||
|
||||
class NowNode(Node):
|
||||
def __init__(self, format_string):
|
||||
self.format_string = format_string
|
||||
|
||||
def iter_render(self, context):
|
||||
return (self.render(context),)
|
||||
|
||||
def render(self, context):
|
||||
from datetime import datetime
|
||||
from django.utils.dateformat import DateFormat
|
||||
@ -336,9 +325,6 @@ class TemplateTagNode(Node):
|
||||
def __init__(self, tagtype):
|
||||
self.tagtype = tagtype
|
||||
|
||||
def iter_render(self, context):
|
||||
return (self.render(context),)
|
||||
|
||||
def render(self, context):
|
||||
return self.mapping.get(self.tagtype, '')
|
||||
|
||||
@ -348,18 +334,18 @@ class URLNode(Node):
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
|
||||
def iter_render(self, context):
|
||||
def render(self, context):
|
||||
from django.core.urlresolvers import reverse, NoReverseMatch
|
||||
args = [arg.resolve(context) for arg in self.args]
|
||||
kwargs = dict([(k, v.resolve(context)) for k, v in self.kwargs.items()])
|
||||
try:
|
||||
return (reverse(self.view_name, args=args, kwargs=kwargs),)
|
||||
return reverse(self.view_name, args=args, kwargs=kwargs)
|
||||
except NoReverseMatch:
|
||||
try:
|
||||
project_name = settings.SETTINGS_MODULE.split('.')[0]
|
||||
return reverse(project_name + '.' + self.view_name, args=args, kwargs=kwargs)
|
||||
except NoReverseMatch:
|
||||
return ()
|
||||
return ''
|
||||
|
||||
class WidthRatioNode(Node):
|
||||
def __init__(self, val_expr, max_expr, max_width):
|
||||
@ -367,9 +353,6 @@ class WidthRatioNode(Node):
|
||||
self.max_expr = max_expr
|
||||
self.max_width = max_width
|
||||
|
||||
def iter_render(self, context):
|
||||
return (self.render(context),)
|
||||
|
||||
def render(self, context):
|
||||
try:
|
||||
value = self.val_expr.resolve(context)
|
||||
@ -393,13 +376,13 @@ class WithNode(Node):
|
||||
def __repr__(self):
|
||||
return "<WithNode>"
|
||||
|
||||
def iter_render(self, context):
|
||||
def render(self, context):
|
||||
val = self.var.resolve(context)
|
||||
context.push()
|
||||
context[self.name] = val
|
||||
for chunk in self.nodelist.iter_render(context):
|
||||
yield chunk
|
||||
output = self.nodelist.render(context)
|
||||
context.pop()
|
||||
return output
|
||||
|
||||
#@register.tag
|
||||
def comment(parser, token):
|
||||
|
@ -87,12 +87,14 @@ def get_template_from_string(source, origin=None, name=None):
|
||||
"""
|
||||
return Template(source, origin, name)
|
||||
|
||||
def _render_setup(template_name, dictionary=None, context_instance=None):
|
||||
def render_to_string(template_name, dictionary=None, context_instance=None):
|
||||
"""
|
||||
Common setup code for render_to_string and render_to_iter.
|
||||
Loads the given template_name and renders it with the given dictionary as
|
||||
context. The template_name may be a string to load a single template using
|
||||
get_template, or it may be a tuple to use select_template to find one of
|
||||
the templates in the list. Returns a string.
|
||||
"""
|
||||
if dictionary is None:
|
||||
dictionary = {}
|
||||
dictionary = dictionary or {}
|
||||
if isinstance(template_name, (list, tuple)):
|
||||
t = select_template(template_name)
|
||||
else:
|
||||
@ -101,28 +103,7 @@ def _render_setup(template_name, dictionary=None, context_instance=None):
|
||||
context_instance.update(dictionary)
|
||||
else:
|
||||
context_instance = Context(dictionary)
|
||||
return t, context_instance
|
||||
|
||||
def render_to_string(template_name, dictionary=None, context_instance=None):
|
||||
"""
|
||||
Loads the given template_name and renders it with the given dictionary as
|
||||
context. The template_name may be a string to load a single template using
|
||||
get_template, or it may be a tuple to use select_template to find one of
|
||||
the templates in the list. Returns a string.
|
||||
"""
|
||||
t, c = _render_setup(template_name, dictionary=dictionary, context_instance=context_instance)
|
||||
return t.render(c)
|
||||
|
||||
def render_to_iter(template_name, dictionary=None, context_instance=None):
|
||||
"""
|
||||
Loads the given template_name and renders it with the given dictionary as
|
||||
context. The template_name may be a string to load a single template using
|
||||
get_template, or it may be a tuple to use select_template to find one of
|
||||
the templates in the list. Returns a string.
|
||||
"""
|
||||
t, c = _render_setup(template_name, dictionary=dictionary, context_instance=context_instance)
|
||||
return t.iter_render(c)
|
||||
|
||||
return t.render(context_instance)
|
||||
|
||||
def select_template(template_name_list):
|
||||
"Given a list of template names, returns the first that can be loaded."
|
||||
|
@ -15,14 +15,14 @@ class BlockNode(Node):
|
||||
def __repr__(self):
|
||||
return "<Block Node: %s. Contents: %r>" % (self.name, self.nodelist)
|
||||
|
||||
def iter_render(self, context):
|
||||
def render(self, context):
|
||||
context.push()
|
||||
# Save context in case of block.super().
|
||||
self.context = context
|
||||
context['block'] = self
|
||||
for chunk in self.nodelist.iter_render(context):
|
||||
yield chunk
|
||||
result = self.nodelist.render(context)
|
||||
context.pop()
|
||||
return result
|
||||
|
||||
def super(self):
|
||||
if self.parent:
|
||||
@ -59,7 +59,7 @@ class ExtendsNode(Node):
|
||||
else:
|
||||
return get_template_from_string(source, origin, parent)
|
||||
|
||||
def iter_render(self, context):
|
||||
def render(self, context):
|
||||
compiled_parent = self.get_parent(context)
|
||||
parent_is_child = isinstance(compiled_parent.nodelist[0], ExtendsNode)
|
||||
parent_blocks = dict([(n.name, n) for n in compiled_parent.nodelist.get_nodes_by_type(BlockNode)])
|
||||
@ -79,7 +79,7 @@ class ExtendsNode(Node):
|
||||
parent_block.parent = block_node.parent
|
||||
parent_block.add_parent(parent_block.nodelist)
|
||||
parent_block.nodelist = block_node.nodelist
|
||||
return compiled_parent.iter_render(context)
|
||||
return compiled_parent.render(context)
|
||||
|
||||
class ConstantIncludeNode(Node):
|
||||
def __init__(self, template_path):
|
||||
@ -91,26 +91,27 @@ class ConstantIncludeNode(Node):
|
||||
raise
|
||||
self.template = None
|
||||
|
||||
def iter_render(self, context):
|
||||
def render(self, context):
|
||||
if self.template:
|
||||
return self.template.iter_render(context)
|
||||
return ()
|
||||
return self.template.render(context)
|
||||
else:
|
||||
return ''
|
||||
|
||||
class IncludeNode(Node):
|
||||
def __init__(self, template_name):
|
||||
self.template_name = template_name
|
||||
|
||||
def iter_render(self, context):
|
||||
def render(self, context):
|
||||
try:
|
||||
template_name = resolve_variable(self.template_name, context)
|
||||
t = get_template(template_name)
|
||||
return t.iter_render(context)
|
||||
return t.render(context)
|
||||
except TemplateSyntaxError, e:
|
||||
if settings.TEMPLATE_DEBUG:
|
||||
raise
|
||||
return ()
|
||||
return ''
|
||||
except:
|
||||
return () # Fail silently for invalid included templates.
|
||||
return '' # Fail silently for invalid included templates.
|
||||
|
||||
def do_block(parser, token):
|
||||
"""
|
||||
|
@ -1,6 +1,7 @@
|
||||
import sys, time
|
||||
from django.conf import settings
|
||||
from django.db import connection, transaction, backend
|
||||
from django.db import connection, backend, get_creation_module
|
||||
from django.core import management, mail
|
||||
from django.core import management, mail
|
||||
from django.dispatch import dispatcher
|
||||
from django.test import signals
|
||||
@ -11,22 +12,13 @@ from django.template import Template
|
||||
TEST_DATABASE_PREFIX = 'test_'
|
||||
|
||||
def instrumented_test_render(self, context):
|
||||
"""
|
||||
An instrumented Template render method, providing a signal that can be
|
||||
intercepted by the test system Client.
|
||||
"""An instrumented Template render method, providing a signal
|
||||
that can be intercepted by the test system Client
|
||||
|
||||
"""
|
||||
dispatcher.send(signal=signals.template_rendered, sender=self, template=self, context=context)
|
||||
return self.nodelist.render(context)
|
||||
|
||||
def instrumented_test_iter_render(self, context):
|
||||
"""
|
||||
An instrumented Template iter_render method, providing a signal that can be
|
||||
intercepted by the test system Client.
|
||||
"""
|
||||
for chunk in self.nodelist.iter_render(context):
|
||||
yield chunk
|
||||
dispatcher.send(signal=signals.template_rendered, sender=self, template=self, context=context)
|
||||
|
||||
class TestSMTPConnection(object):
|
||||
"""A substitute SMTP connection for use during test sessions.
|
||||
The test connection stores email messages in a dummy outbox,
|
||||
@ -53,9 +45,7 @@ def setup_test_environment():
|
||||
|
||||
"""
|
||||
Template.original_render = Template.render
|
||||
Template.original_iter_render = Template.iter_render
|
||||
Template.render = instrumented_test_render
|
||||
Template.iter_render = instrumented_test_render
|
||||
|
||||
mail.original_SMTPConnection = mail.SMTPConnection
|
||||
mail.SMTPConnection = TestSMTPConnection
|
||||
@ -70,8 +60,7 @@ def teardown_test_environment():
|
||||
|
||||
"""
|
||||
Template.render = Template.original_render
|
||||
Template.iter_render = Template.original_iter_render
|
||||
del Template.original_render, Template.original_iter_render
|
||||
del Template.original_render
|
||||
|
||||
mail.SMTPConnection = mail.original_SMTPConnection
|
||||
del mail.original_SMTPConnection
|
||||
@ -100,6 +89,12 @@ def get_postgresql_create_suffix():
|
||||
return ''
|
||||
|
||||
def create_test_db(verbosity=1, autoclobber=False):
|
||||
# If the database backend wants to create the test DB itself, let it
|
||||
creation_module = get_creation_module()
|
||||
if hasattr(creation_module, "create_test_db"):
|
||||
creation_module.create_test_db(settings, connection, backend, verbosity, autoclobber)
|
||||
return
|
||||
|
||||
if verbosity >= 1:
|
||||
print "Creating test database..."
|
||||
# If we're using SQLite, it's more convenient to test against an
|
||||
@ -154,6 +149,12 @@ def create_test_db(verbosity=1, autoclobber=False):
|
||||
cursor = connection.cursor()
|
||||
|
||||
def destroy_test_db(old_database_name, verbosity=1):
|
||||
# If the database wants to drop the test DB itself, let it
|
||||
creation_module = get_creation_module()
|
||||
if hasattr(creation_module, "destroy_test_db"):
|
||||
creation_module.destroy_test_db(settings, connection, backend, old_database_name, verbosity)
|
||||
return
|
||||
|
||||
# Unless we're using SQLite, remove the test database to clean up after
|
||||
# ourselves. Connect to the previous database (not the test database)
|
||||
# to do so, because it's not allowed to delete a database while being
|
||||
|
@ -3,6 +3,21 @@ def curry(_curried_func, *args, **kwargs):
|
||||
return _curried_func(*(args+moreargs), **dict(kwargs, **morekwargs))
|
||||
return _curried
|
||||
|
||||
def memoize(func, cache):
|
||||
"""
|
||||
Wrap a function so that results for any argument tuple are stored in
|
||||
'cache'. Note that the args to the function must be usable as dictionary
|
||||
keys.
|
||||
"""
|
||||
def wrapper(*args):
|
||||
if args in cache:
|
||||
return cache[args]
|
||||
|
||||
result = func(*args)
|
||||
cache[args] = result
|
||||
return result
|
||||
return wrapper
|
||||
|
||||
class Promise:
|
||||
"""
|
||||
This is just a base class for the proxy class created in
|
||||
|
@ -53,16 +53,18 @@ def fix_ampersands(value):
|
||||
|
||||
def urlize(text, trim_url_limit=None, nofollow=False):
|
||||
"""
|
||||
Converts any URLs in text into clickable links. Works on http://, https:// and
|
||||
www. links. Links can have trailing punctuation (periods, commas, close-parens)
|
||||
and leading punctuation (opening parens) and it'll still do the right thing.
|
||||
Converts any URLs in text into clickable links. Works on http://, https://
|
||||
and www. links. Links can have trailing punctuation (periods, commas,
|
||||
close-parens) and leading punctuation (opening parens) and it'll still do
|
||||
the right thing.
|
||||
|
||||
If trim_url_limit is not None, the URLs in link text will be limited to
|
||||
trim_url_limit characters.
|
||||
If trim_url_limit is not None, the URLs in link text longer than this limit
|
||||
will truncated to trim_url_limit-3 characters and appended with an elipsis.
|
||||
|
||||
If nofollow is True, the URLs in link text will get a rel="nofollow" attribute.
|
||||
If nofollow is True, the URLs in link text will get a rel="nofollow"
|
||||
attribute.
|
||||
"""
|
||||
trim_url = lambda x, limit=trim_url_limit: limit is not None and (x[:limit] + (len(x) >=limit and '...' or '')) or x
|
||||
trim_url = lambda x, limit=trim_url_limit: limit is not None and (len(x) > limit and ('%s...' % x[:max(0, limit - 3)])) or x
|
||||
words = word_split_re.split(text)
|
||||
nofollow_attr = nofollow and ' rel="nofollow"' or ''
|
||||
for i, word in enumerate(words):
|
||||
|
@ -34,7 +34,7 @@ def groupby(iterable, keyfunc=None):
|
||||
keyfunc = lambda x:x
|
||||
iterable = iter(iterable)
|
||||
l = [iterable.next()]
|
||||
lastkey = keyfunc(l)
|
||||
lastkey = keyfunc(l[0])
|
||||
for item in iterable:
|
||||
key = keyfunc(item)
|
||||
if key != lastkey:
|
||||
@ -45,6 +45,12 @@ def groupby(iterable, keyfunc=None):
|
||||
l.append(item)
|
||||
yield lastkey, l
|
||||
|
||||
# Not really in itertools, since it's a builtin in Python 2.4 and later, but it
|
||||
# does operate as an iterator.
|
||||
def reversed(data):
|
||||
for index in xrange(len(data)-1, -1, -1):
|
||||
yield data[index]
|
||||
|
||||
if hasattr(itertools, 'tee'):
|
||||
tee = itertools.tee
|
||||
else:
|
||||
|
@ -137,7 +137,7 @@ def technical_500_response(request, exc_type, exc_value, tb):
|
||||
'template_does_not_exist': template_does_not_exist,
|
||||
'loader_debug_info': loader_debug_info,
|
||||
})
|
||||
return HttpResponseServerError(t.iter_render(c), mimetype='text/html')
|
||||
return HttpResponseServerError(t.render(c), mimetype='text/html')
|
||||
|
||||
def technical_404_response(request, exception):
|
||||
"Create a technical 404 error response. The exception should be the Http404."
|
||||
@ -160,7 +160,7 @@ def technical_404_response(request, exception):
|
||||
'request_protocol': request.is_secure() and "https" or "http",
|
||||
'settings': get_safe_settings(),
|
||||
})
|
||||
return HttpResponseNotFound(t.iter_render(c), mimetype='text/html')
|
||||
return HttpResponseNotFound(t.render(c), mimetype='text/html')
|
||||
|
||||
def empty_urlconf(request):
|
||||
"Create an empty URLconf 404 error response."
|
||||
@ -168,7 +168,7 @@ def empty_urlconf(request):
|
||||
c = Context({
|
||||
'project_name': settings.SETTINGS_MODULE.split('.')[0]
|
||||
})
|
||||
return HttpResponseNotFound(t.iter_render(c), mimetype='text/html')
|
||||
return HttpResponseNotFound(t.render(c), mimetype='text/html')
|
||||
|
||||
def _get_lines_from_file(filename, lineno, context_lines, loader=None, module_name=None):
|
||||
"""
|
||||
|
@ -76,7 +76,7 @@ def page_not_found(request, template_name='404.html'):
|
||||
The path of the requested URL (e.g., '/app/pages/bad_page/')
|
||||
"""
|
||||
t = loader.get_template(template_name) # You need to create a 404.html template.
|
||||
return http.HttpResponseNotFound(t.iter_render(RequestContext(request, {'request_path': request.path})))
|
||||
return http.HttpResponseNotFound(t.render(RequestContext(request, {'request_path': request.path})))
|
||||
|
||||
def server_error(request, template_name='500.html'):
|
||||
"""
|
||||
@ -86,4 +86,4 @@ def server_error(request, template_name='500.html'):
|
||||
Context: None
|
||||
"""
|
||||
t = loader.get_template(template_name) # You need to create a 500.html template.
|
||||
return http.HttpResponseServerError(t.iter_render(Context({})))
|
||||
return http.HttpResponseServerError(t.render(Context({})))
|
||||
|
@ -68,7 +68,7 @@ def create_object(request, model, template_name=None,
|
||||
c[key] = value()
|
||||
else:
|
||||
c[key] = value
|
||||
return HttpResponse(t.iter_render(c))
|
||||
return HttpResponse(t.render(c))
|
||||
|
||||
def update_object(request, model, object_id=None, slug=None,
|
||||
slug_field=None, template_name=None, template_loader=loader,
|
||||
@ -141,7 +141,7 @@ def update_object(request, model, object_id=None, slug=None,
|
||||
c[key] = value()
|
||||
else:
|
||||
c[key] = value
|
||||
response = HttpResponse(t.iter_render(c))
|
||||
response = HttpResponse(t.render(c))
|
||||
populate_xheaders(request, response, model, getattr(object, object._meta.pk.attname))
|
||||
return response
|
||||
|
||||
@ -195,6 +195,6 @@ def delete_object(request, model, post_delete_redirect,
|
||||
c[key] = value()
|
||||
else:
|
||||
c[key] = value
|
||||
response = HttpResponse(t.iter_render(c))
|
||||
response = HttpResponse(t.render(c))
|
||||
populate_xheaders(request, response, model, getattr(object, object._meta.pk.attname))
|
||||
return response
|
||||
|
@ -44,7 +44,7 @@ def archive_index(request, queryset, date_field, num_latest=15,
|
||||
c[key] = value()
|
||||
else:
|
||||
c[key] = value
|
||||
return HttpResponse(t.iter_render(c), mimetype=mimetype)
|
||||
return HttpResponse(t.render(c), mimetype=mimetype)
|
||||
|
||||
def archive_year(request, year, queryset, date_field, template_name=None,
|
||||
template_loader=loader, extra_context=None, allow_empty=False,
|
||||
@ -92,7 +92,7 @@ def archive_year(request, year, queryset, date_field, template_name=None,
|
||||
c[key] = value()
|
||||
else:
|
||||
c[key] = value
|
||||
return HttpResponse(t.iter_render(c), mimetype=mimetype)
|
||||
return HttpResponse(t.render(c), mimetype=mimetype)
|
||||
|
||||
def archive_month(request, year, month, queryset, date_field,
|
||||
month_format='%b', template_name=None, template_loader=loader,
|
||||
@ -158,7 +158,7 @@ def archive_month(request, year, month, queryset, date_field,
|
||||
c[key] = value()
|
||||
else:
|
||||
c[key] = value
|
||||
return HttpResponse(t.iter_render(c), mimetype=mimetype)
|
||||
return HttpResponse(t.render(c), mimetype=mimetype)
|
||||
|
||||
def archive_week(request, year, week, queryset, date_field,
|
||||
template_name=None, template_loader=loader,
|
||||
@ -206,7 +206,7 @@ def archive_week(request, year, week, queryset, date_field,
|
||||
c[key] = value()
|
||||
else:
|
||||
c[key] = value
|
||||
return HttpResponse(t.iter_render(c), mimetype=mimetype)
|
||||
return HttpResponse(t.render(c), mimetype=mimetype)
|
||||
|
||||
def archive_day(request, year, month, day, queryset, date_field,
|
||||
month_format='%b', day_format='%d', template_name=None,
|
||||
@ -270,7 +270,7 @@ def archive_day(request, year, month, day, queryset, date_field,
|
||||
c[key] = value()
|
||||
else:
|
||||
c[key] = value
|
||||
return HttpResponse(t.iter_render(c), mimetype=mimetype)
|
||||
return HttpResponse(t.render(c), mimetype=mimetype)
|
||||
|
||||
def archive_today(request, **kwargs):
|
||||
"""
|
||||
@ -339,6 +339,6 @@ def object_detail(request, year, month, day, queryset, date_field,
|
||||
c[key] = value()
|
||||
else:
|
||||
c[key] = value
|
||||
response = HttpResponse(t.iter_render(c), mimetype=mimetype)
|
||||
response = HttpResponse(t.render(c), mimetype=mimetype)
|
||||
populate_xheaders(request, response, model, getattr(obj, obj._meta.pk.name))
|
||||
return response
|
||||
|
@ -84,7 +84,7 @@ def object_list(request, queryset, paginate_by=None, page=None,
|
||||
model = queryset.model
|
||||
template_name = "%s/%s_list.html" % (model._meta.app_label, model._meta.object_name.lower())
|
||||
t = template_loader.get_template(template_name)
|
||||
return HttpResponse(t.iter_render(c), mimetype=mimetype)
|
||||
return HttpResponse(t.render(c), mimetype=mimetype)
|
||||
|
||||
def object_detail(request, queryset, object_id=None, slug=None,
|
||||
slug_field=None, template_name=None, template_name_field=None,
|
||||
@ -126,6 +126,6 @@ def object_detail(request, queryset, object_id=None, slug=None,
|
||||
c[key] = value()
|
||||
else:
|
||||
c[key] = value
|
||||
response = HttpResponse(t.iter_render(c), mimetype=mimetype)
|
||||
response = HttpResponse(t.render(c), mimetype=mimetype)
|
||||
populate_xheaders(request, response, model, getattr(obj, obj._meta.pk.name))
|
||||
return response
|
||||
|
@ -15,7 +15,7 @@ def direct_to_template(request, template, extra_context={}, mimetype=None, **kwa
|
||||
dictionary[key] = value
|
||||
c = RequestContext(request, dictionary)
|
||||
t = loader.get_template(template)
|
||||
return HttpResponse(t.iter_render(c), mimetype=mimetype)
|
||||
return HttpResponse(t.render(c), mimetype=mimetype)
|
||||
|
||||
def redirect_to(request, url, **kwargs):
|
||||
"""
|
||||
|
@ -92,7 +92,7 @@ def directory_index(path, fullpath):
|
||||
'directory' : path + '/',
|
||||
'file_list' : files,
|
||||
})
|
||||
return HttpResponse(t.iter_render(c))
|
||||
return HttpResponse(t.render(c))
|
||||
|
||||
def was_modified_since(header=None, mtime=0, size=0):
|
||||
"""
|
||||
|
@ -325,7 +325,7 @@ Manually checking a user's password
|
||||
|
||||
If you'd like to manually authenticate a user by comparing a
|
||||
plain-text password to the hashed password in the database, use the
|
||||
convenience function `django.contrib.auth.models.check_password`. It
|
||||
convenience function ``django.contrib.auth.models.check_password``. It
|
||||
takes two arguments: the plain-text password to check, and the full
|
||||
value of a user's ``password`` field in the database to check against,
|
||||
and returns ``True`` if they match, ``False`` otherwise.
|
||||
|
@ -104,7 +104,7 @@ Lawrence, Kansas, USA.
|
||||
|
||||
`Wilson Miner`_
|
||||
Wilson's design-fu makes us all look like rock stars. By day, he's an
|
||||
interactive designer for `Apple`. Don't ask him what he's working on, or
|
||||
interactive designer for `Apple`_. Don't ask him what he's working on, or
|
||||
he'll have to kill you. He lives in San Francisco.
|
||||
|
||||
On IRC, Wilson goes by ``wilsonian``.
|
||||
@ -301,7 +301,7 @@ means it can run on a variety of server platforms.
|
||||
|
||||
If you want to use Django with a database, which is probably the case, you'll
|
||||
also need a database engine. PostgreSQL_ is recommended, because we're
|
||||
PostgreSQL fans, and MySQL_ and `SQLite 3`_ are also supported.
|
||||
PostgreSQL fans, and MySQL_, `SQLite 3`_, and Oracle_ are also supported.
|
||||
|
||||
.. _Python: http://www.python.org/
|
||||
.. _Apache 2: http://httpd.apache.org/
|
||||
@ -310,6 +310,7 @@ PostgreSQL fans, and MySQL_ and `SQLite 3`_ are also supported.
|
||||
.. _PostgreSQL: http://www.postgresql.org/
|
||||
.. _MySQL: http://www.mysql.com/
|
||||
.. _`SQLite 3`: http://www.sqlite.org/
|
||||
.. _Oracle: http://www.oracle.com/
|
||||
|
||||
Do I lose anything by using Python 2.3 versus newer Python versions, such as Python 2.5?
|
||||
----------------------------------------------------------------------------------------
|
||||
|
@ -754,10 +754,10 @@ If the results are paginated, the context will contain these extra variables:
|
||||
|
||||
* ``previous``: The previous page number, as an integer. This is 1-based.
|
||||
|
||||
* `last_on_page`: The number of the
|
||||
* ``last_on_page``: The number of the
|
||||
last result on the current page. This is 1-based.
|
||||
|
||||
* `first_on_page`: The number of the
|
||||
* ``first_on_page``: The number of the
|
||||
first result on the current page. This is 1-based.
|
||||
|
||||
* ``pages``: The total number of pages, as an integer.
|
||||
|
@ -17,8 +17,10 @@ probably already have it installed.
|
||||
Install Apache and mod_python
|
||||
=============================
|
||||
|
||||
If you just want to experiment with Django, skip this step. Django comes with
|
||||
its own Web server for development purposes.
|
||||
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_python`_.
|
||||
mod_python is similar to mod_perl -- it embeds Python within Apache and loads
|
||||
@ -62,6 +64,8 @@ installed.
|
||||
|
||||
* If you're using SQLite, you'll need pysqlite_. Use version 2.0.3 or higher.
|
||||
|
||||
* If you're using Oracle, you'll need cx_Oracle_, version 4.3.1 or higher.
|
||||
|
||||
.. _PostgreSQL: http://www.postgresql.org/
|
||||
.. _MySQL: http://www.mysql.com/
|
||||
.. _Django's ticket system: http://code.djangoproject.com/report/1
|
||||
@ -71,6 +75,7 @@ installed.
|
||||
.. _SQLite: http://www.sqlite.org/
|
||||
.. _pysqlite: http://initd.org/tracker/pysqlite
|
||||
.. _MySQL backend: ../databases/
|
||||
.. _cx_Oracle: http://www.python.net/crew/atuining/cx_Oracle/
|
||||
|
||||
Remove any old versions of Django
|
||||
=================================
|
||||
@ -83,23 +88,20 @@ If you installed Django using ``setup.py install``, uninstalling
|
||||
is as simple as deleting the ``django`` directory from your Python
|
||||
``site-packages``.
|
||||
|
||||
If you installed Django from a Python Egg, remove the Django ``.egg`` file,
|
||||
If you installed Django from a Python egg, remove the Django ``.egg`` file,
|
||||
and remove the reference to the egg in the file named ``easy-install.pth``.
|
||||
This file should also be located in your ``site-packages`` directory.
|
||||
|
||||
.. admonition:: Where are my ``site-packages`` stored?
|
||||
|
||||
The location of the ``site-packages`` directory depends on the operating
|
||||
system, and the location in which Python was installed. However, the
|
||||
following locations are common:
|
||||
system, and the location in which Python was installed. To find out your
|
||||
system's ``site-packages`` location, execute the following::
|
||||
|
||||
* If you're using Linux: ``/usr/lib/python2.X/site-packages``
|
||||
python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()"
|
||||
|
||||
* If you're using Windows: ``C:\Python2.X\lib\site-packages``
|
||||
|
||||
* If you're using MacOSX: ``/Library/Python2.X/site-packages`` or
|
||||
``/Library/Frameworks/Python.framework/Versions/2.X/lib/python2.X/site-packages/``
|
||||
(in later releases).
|
||||
(Note that this should be run from a shell prompt, not a Python interactive
|
||||
prompt.)
|
||||
|
||||
Install the Django code
|
||||
=======================
|
||||
@ -138,12 +140,15 @@ latest bug fixes and improvements, follow these instructions:
|
||||
|
||||
1. Make sure you have Subversion_ installed.
|
||||
2. Check out the Django code into your Python ``site-packages`` directory.
|
||||
|
||||
On Linux / Mac OSX / Unix, do this::
|
||||
|
||||
svn co http://code.djangoproject.com/svn/django/trunk/ django_src
|
||||
ln -s `pwd`/django_src/django /usr/lib/python2.3/site-packages/django
|
||||
ln -s `pwd`/django_src/django SITE-PACKAGES-DIR/django
|
||||
|
||||
(In the above line, change ``python2.3`` to match your current Python version.)
|
||||
(In the above line, change ``SITE-PACKAGES-DIR`` to match the location of
|
||||
your system's ``site-packages`` directory, as explained in the
|
||||
"Where are my ``site-packages`` stored?" section above.)
|
||||
|
||||
On Windows, do this::
|
||||
|
||||
|
@ -492,6 +492,11 @@ has ``null=True``, that means it has two possible values for "no data":
|
||||
possible values for "no data;" Django convention is to use the empty
|
||||
string, not ``NULL``.
|
||||
|
||||
.. note::
|
||||
Due to database limitations, when using the Oracle backend the
|
||||
``null=True`` option will be coerced for string-based fields that can
|
||||
blank, and the value ``NULL`` will be stored to denote the empty string.
|
||||
|
||||
``blank``
|
||||
~~~~~~~~~
|
||||
|
||||
@ -586,6 +591,13 @@ scenes.
|
||||
If ``True``, ``django-admin.py sqlindexes`` will output a ``CREATE INDEX``
|
||||
statement for this field.
|
||||
|
||||
``db_tablespace``
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
If this field is indexed, the name of the database tablespace to use for the
|
||||
index. The default is the ``db_tablespace`` of the model, if any. If the
|
||||
backend doesn't support tablespaces, this option is ignored.
|
||||
|
||||
``default``
|
||||
~~~~~~~~~~~
|
||||
|
||||
@ -996,6 +1008,12 @@ If your database table name is an SQL reserved word, or contains characters
|
||||
that aren't allowed in Python variable names -- notably, the hyphen --
|
||||
that's OK. Django quotes column and table names behind the scenes.
|
||||
|
||||
``db_tablespace``
|
||||
-----------------
|
||||
|
||||
The name of the database tablespace to use for the model. If the backend
|
||||
doesn't support tablespaces, this option is ignored.
|
||||
|
||||
``get_latest_by``
|
||||
-----------------
|
||||
|
||||
@ -1876,11 +1894,11 @@ used by the SQLite Python bindings. This is for the sake of consistency and
|
||||
sanity.)
|
||||
|
||||
A final note: If all you want to do is a custom ``WHERE`` clause, you can just
|
||||
just the ``where``, ``tables`` and ``params`` arguments to the standard lookup
|
||||
use the ``where``, ``tables`` and ``params`` arguments to the standard lookup
|
||||
API. See `Other lookup options`_.
|
||||
|
||||
.. _Python DB-API: http://www.python.org/peps/pep-0249.html
|
||||
.. _Other lookup options: ../db-api/#extra-params-select-where-tables
|
||||
.. _Other lookup options: ../db-api/#extra-select-none-where-none-params-none-tables-none
|
||||
.. _transaction handling: ../transactions/
|
||||
|
||||
Overriding default model methods
|
||||
|
@ -110,7 +110,7 @@ shortly.
|
||||
Creating ``Form`` instances
|
||||
---------------------------
|
||||
|
||||
A ``Form`` instance is either **bound** or **unbound** to a set of data.
|
||||
A ``Form`` instance is either **bound** to a set of data, or **unbound**.
|
||||
|
||||
* If it's **bound** to a set of data, it's capable of validating that data
|
||||
and rendering the form as HTML with the data displayed in the HTML.
|
||||
|
@ -244,9 +244,9 @@ DATABASE_ENGINE
|
||||
|
||||
Default: ``''`` (Empty string)
|
||||
|
||||
Which database backend to use. Either ``'postgresql_psycopg2'``,
|
||||
``'postgresql'``, ``'mysql'``, ``'mysql_old'``, ``'sqlite3'`` or
|
||||
``'ado_mssql'``.
|
||||
The database backend to use. Either ``'postgresql_psycopg2'``,
|
||||
``'postgresql'``, ``'mysql'``, ``'mysql_old'``, ``'sqlite3'``,
|
||||
``'oracle'``, or ``'ado_mssql'``.
|
||||
|
||||
DATABASE_HOST
|
||||
-------------
|
||||
|
@ -1266,7 +1266,8 @@ Converts URLs in plain text into clickable links.
|
||||
urlizetrunc
|
||||
~~~~~~~~~~~
|
||||
|
||||
Converts URLs into clickable links, truncating URLs to the given character limit.
|
||||
Converts URLs into clickable links, truncating URLs longer than the given
|
||||
character limit.
|
||||
|
||||
**Argument:** Length to truncate URLs to
|
||||
|
||||
|
@ -342,7 +342,7 @@ If ``TEMPLATE_CONTEXT_PROCESSORS`` contains this processor, every
|
||||
|
||||
* ``user`` -- An ``auth.User`` instance representing the currently
|
||||
logged-in user (or an ``AnonymousUser`` instance, if the client isn't
|
||||
logged in). See the `user authentication docs`.
|
||||
logged in). See the `user authentication docs`_.
|
||||
|
||||
* ``messages`` -- A list of messages (as strings) for the currently
|
||||
logged-in user. Behind the scenes, this calls
|
||||
@ -693,15 +693,14 @@ how the compilation works and how the rendering works.
|
||||
|
||||
When Django compiles a template, it splits the raw template text into
|
||||
''nodes''. Each node is an instance of ``django.template.Node`` and has
|
||||
either a ``render()`` or ``iter_render()`` method. A compiled template is,
|
||||
simply, a list of ``Node`` objects. When you call ``render()`` on a compiled
|
||||
template object, the template calls ``render()`` on each ``Node`` in its node
|
||||
list, with the given context. The results are all concatenated together to
|
||||
form the output of the template.
|
||||
a ``render()`` method. A compiled template is, simply, a list of ``Node``
|
||||
objects. When you call ``render()`` on a compiled template object, the template
|
||||
calls ``render()`` on each ``Node`` in its node list, with the given context.
|
||||
The results are all concatenated together to form the output of the template.
|
||||
|
||||
Thus, to define a custom template tag, you specify how the raw template tag is
|
||||
converted into a ``Node`` (the compilation function), and what the node's
|
||||
``render()`` or ``iter_render()`` method does.
|
||||
``render()`` method does.
|
||||
|
||||
Writing the compilation function
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -771,8 +770,7 @@ Writing the renderer
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The second step in writing custom tags is to define a ``Node`` subclass that
|
||||
has a ``render()`` method (we will discuss the ``iter_render()`` alternative
|
||||
in `Improving rendering speed`_, below).
|
||||
has a ``render()`` method.
|
||||
|
||||
Continuing the above example, we need to define ``CurrentTimeNode``::
|
||||
|
||||
@ -1177,48 +1175,6 @@ For more examples of complex rendering, see the source code for ``{% if %}``,
|
||||
|
||||
.. _configuration:
|
||||
|
||||
Improving rendering speed
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
For most practical purposes, the ``render()`` method on a ``Node`` will be
|
||||
sufficient and the simplest way to implement a new tag. However, if your
|
||||
template tag is expected to produce large strings via ``render()``, you can
|
||||
speed up the rendering process (and reduce memory usage) using iterative
|
||||
rendering via the ``iter_render()`` method.
|
||||
|
||||
The ``iter_render()`` method should either be an iterator that yields string
|
||||
chunks, one at a time, or a method that returns a sequence of string chunks.
|
||||
The template renderer will join the successive chunks together when creating
|
||||
the final output. The improvement over the ``render()`` method here is that
|
||||
you do not need to create one large string containing all the output of the
|
||||
``Node``, instead you can produce the output in smaller chunks.
|
||||
|
||||
By way of example, here's a trivial ``Node`` subclass that simply returns the
|
||||
contents of a file it is given::
|
||||
|
||||
class FileNode(Node):
|
||||
def __init__(self, filename):
|
||||
self.filename = filename
|
||||
|
||||
def iter_render(self):
|
||||
for line in file(self.filename):
|
||||
yield line
|
||||
|
||||
For very large files, the full file contents will never be read entirely into
|
||||
memory when this tag is used, which is a useful optimisation.
|
||||
|
||||
If you define an ``iter_render()`` method on your ``Node`` subclass, you do
|
||||
not need to define a ``render()`` method. The reverse is true as well: the
|
||||
default ``Node.iter_render()`` method will call your ``render()`` method if
|
||||
necessary. A useful side-effect of this is that you can develop a new tag
|
||||
using ``render()`` and producing all the output at once, which is easy to
|
||||
debug. Then you can rewrite the method as an iterator, rename it to
|
||||
``iter_render()`` and everything will still work.
|
||||
|
||||
It is compulsory, however, to define *either* ``render()`` or ``iter_render()``
|
||||
in your subclass. If you omit them both, a ``TypeError`` will be raised when
|
||||
the code is imported.
|
||||
|
||||
Configuring the template system in standalone mode
|
||||
==================================================
|
||||
|
||||
@ -1250,4 +1206,3 @@ is of obvious interest.
|
||||
|
||||
.. _settings file: ../settings/#using-settings-without-the-django-settings-module-environment-variable
|
||||
.. _settings documentation: ../settings/
|
||||
|
||||
|
@ -10,7 +10,7 @@ poll application.
|
||||
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 poll.
|
||||
* An admin site that lets you add, change and delete polls.
|
||||
|
||||
We'll assume you have `Django installed`_ already. You can tell Django is
|
||||
installed by running the Python interactive interpreter and typing
|
||||
|
0
tests/regressiontests/datatypes/__init__.py
Normal file
0
tests/regressiontests/datatypes/__init__.py
Normal file
59
tests/regressiontests/datatypes/models.py
Normal file
59
tests/regressiontests/datatypes/models.py
Normal file
@ -0,0 +1,59 @@
|
||||
"""
|
||||
This is a basic model to test saving and loading boolean and date-related
|
||||
types, which in the past were problematic for some database backends.
|
||||
"""
|
||||
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
|
||||
class Donut(models.Model):
|
||||
name = models.CharField(maxlength=100)
|
||||
is_frosted = models.BooleanField(default=False)
|
||||
has_sprinkles = models.NullBooleanField()
|
||||
baked_date = models.DateField(null=True)
|
||||
baked_time = models.TimeField(null=True)
|
||||
consumed_at = models.DateTimeField(null=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ('consumed_at',)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
__test__ = {'API_TESTS': """
|
||||
# No donuts are in the system yet.
|
||||
>>> Donut.objects.all()
|
||||
[]
|
||||
|
||||
>>> d = Donut(name='Apple Fritter')
|
||||
|
||||
# Ensure we're getting True and False, not 0 and 1
|
||||
>>> d.is_frosted
|
||||
False
|
||||
>>> d.has_sprinkles
|
||||
>>> d.has_sprinkles = True
|
||||
>>> d.has_sprinkles == True
|
||||
True
|
||||
>>> d.save()
|
||||
>>> d2 = Donut.objects.all()[0]
|
||||
>>> d2
|
||||
<Donut: Apple Fritter>
|
||||
>>> d2.is_frosted == False
|
||||
True
|
||||
>>> d2.has_sprinkles == True
|
||||
True
|
||||
|
||||
>>> import datetime
|
||||
>>> d2.baked_date = datetime.date(year=1938, month=6, day=4)
|
||||
>>> d2.baked_time = datetime.time(hour=5, minute=30)
|
||||
>>> d2.consumed_at = datetime.datetime(year=2007, month=4, day=20, hour=16, minute=19, second=59)
|
||||
>>> d2.save()
|
||||
|
||||
>>> d3 = Donut.objects.all()[0]
|
||||
>>> d3.baked_date
|
||||
datetime.date(1938, 6, 4)
|
||||
>>> d3.baked_time
|
||||
datetime.time(5, 30)
|
||||
>>> d3.consumed_at
|
||||
datetime.datetime(2007, 4, 20, 16, 19, 59)
|
||||
"""}
|
@ -121,7 +121,18 @@ u'\xcb'
|
||||
'<a href="http://short.com/" rel="nofollow">http://short.com/</a>'
|
||||
|
||||
>>> urlizetrunc('http://www.google.co.uk/search?hl=en&q=some+long+url&btnG=Search&meta=', 20)
|
||||
'<a href="http://www.google.co.uk/search?hl=en&q=some+long+url&btnG=Search&meta=" rel="nofollow">http://www.google.co...</a>'
|
||||
'<a href="http://www.google.co.uk/search?hl=en&q=some+long+url&btnG=Search&meta=" rel="nofollow">http://www.google...</a>'
|
||||
|
||||
# Check truncating of URIs which are the exact length
|
||||
>>> uri = 'http://31characteruri.com/test/'
|
||||
>>> len(uri)
|
||||
31
|
||||
>>> urlizetrunc(uri, 31)
|
||||
'<a href="http://31characteruri.com/test/" rel="nofollow">http://31characteruri.com/test/</a>'
|
||||
>>> urlizetrunc(uri, 30)
|
||||
'<a href="http://31characteruri.com/test/" rel="nofollow">http://31characteruri.com/t...</a>'
|
||||
>>> urlizetrunc(uri, 2)
|
||||
'<a href="http://31characteruri.com/test/" rel="nofollow">...</a>'
|
||||
|
||||
>>> wordcount('')
|
||||
0
|
||||
|
@ -1859,8 +1859,12 @@ ValidationError: [u'Enter a valid date.']
|
||||
>>> f = SplitDateTimeField(required=False)
|
||||
>>> f.clean([datetime.date(2006, 1, 10), datetime.time(7, 30)])
|
||||
datetime.datetime(2006, 1, 10, 7, 30)
|
||||
>>> f.clean(['2006-01-10', '07:30'])
|
||||
datetime.datetime(2006, 1, 10, 7, 30)
|
||||
>>> f.clean(None)
|
||||
>>> f.clean('')
|
||||
>>> f.clean([''])
|
||||
>>> f.clean(['', ''])
|
||||
>>> f.clean('hello')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
@ -1877,6 +1881,18 @@ ValidationError: [u'Enter a valid time.']
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a valid date.']
|
||||
>>> f.clean(['2006-01-10', ''])
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a valid time.']
|
||||
>>> f.clean(['2006-01-10'])
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a valid time.']
|
||||
>>> f.clean(['', '07:30'])
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a valid date.']
|
||||
|
||||
#########
|
||||
# Forms #
|
||||
@ -1958,11 +1974,11 @@ AttributeError: 'Person' object has no attribute 'cleaned_data'
|
||||
<li><ul class="errorlist"><li>This field is required.</li></ul><label for="id_last_name">Last name:</label> <input type="text" name="last_name" id="id_last_name" /></li>
|
||||
<li><ul class="errorlist"><li>This field is required.</li></ul><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /></li>
|
||||
>>> print p.as_p()
|
||||
<p><ul class="errorlist"><li>This field is required.</li></ul></p>
|
||||
<ul class="errorlist"><li>This field is required.</li></ul>
|
||||
<p><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></p>
|
||||
<p><ul class="errorlist"><li>This field is required.</li></ul></p>
|
||||
<ul class="errorlist"><li>This field is required.</li></ul>
|
||||
<p><label for="id_last_name">Last name:</label> <input type="text" name="last_name" id="id_last_name" /></p>
|
||||
<p><ul class="errorlist"><li>This field is required.</li></ul></p>
|
||||
<ul class="errorlist"><li>This field is required.</li></ul>
|
||||
<p><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /></p>
|
||||
|
||||
If you don't pass any values to the Form's __init__(), or if you pass None,
|
||||
@ -2668,7 +2684,7 @@ its field's order in the form.
|
||||
<li>Last name: <input type="text" name="last_name" value="Lennon" /></li>
|
||||
<li>Birthday: <input type="text" name="birthday" value="1940-10-9" /><input type="hidden" name="hidden_text" /></li>
|
||||
>>> print p.as_p()
|
||||
<p><ul class="errorlist"><li>(Hidden field hidden_text) This field is required.</li></ul></p>
|
||||
<ul class="errorlist"><li>(Hidden field hidden_text) This field is required.</li></ul>
|
||||
<p>First name: <input type="text" name="first_name" value="John" /></p>
|
||||
<p>Last name: <input type="text" name="last_name" value="Lennon" /></p>
|
||||
<p>Birthday: <input type="text" name="birthday" value="1940-10-9" /><input type="hidden" name="hidden_text" /></p>
|
||||
|
@ -15,6 +15,7 @@ from django.utils.functional import curry
|
||||
from django.core import serializers
|
||||
from django.db import transaction
|
||||
from django.core import management
|
||||
from django.conf import settings
|
||||
|
||||
from models import *
|
||||
try:
|
||||
@ -116,10 +117,13 @@ test_data = [
|
||||
(data_obj, 31, DateTimeData, None),
|
||||
(data_obj, 40, EmailData, "hovercraft@example.com"),
|
||||
(data_obj, 41, EmailData, None),
|
||||
(data_obj, 42, EmailData, ""),
|
||||
(data_obj, 50, FileData, 'file:///foo/bar/whiz.txt'),
|
||||
(data_obj, 51, FileData, None),
|
||||
(data_obj, 52, FileData, ""),
|
||||
(data_obj, 60, FilePathData, "/foo/bar/whiz.txt"),
|
||||
(data_obj, 61, FilePathData, None),
|
||||
(data_obj, 62, FilePathData, ""),
|
||||
(data_obj, 70, DecimalData, decimal.Decimal('12.345')),
|
||||
(data_obj, 71, DecimalData, decimal.Decimal('-12.345')),
|
||||
(data_obj, 72, DecimalData, decimal.Decimal('0.0')),
|
||||
@ -146,6 +150,7 @@ test_data = [
|
||||
(data_obj, 131, PositiveSmallIntegerData, None),
|
||||
(data_obj, 140, SlugData, "this-is-a-slug"),
|
||||
(data_obj, 141, SlugData, None),
|
||||
(data_obj, 142, SlugData, ""),
|
||||
(data_obj, 150, SmallData, 12),
|
||||
(data_obj, 151, SmallData, -12),
|
||||
(data_obj, 152, SmallData, 0),
|
||||
@ -160,8 +165,10 @@ The end."""),
|
||||
(data_obj, 171, TimeData, None),
|
||||
(data_obj, 180, USStateData, "MA"),
|
||||
(data_obj, 181, USStateData, None),
|
||||
(data_obj, 182, USStateData, ""),
|
||||
(data_obj, 190, XMLData, "<foo></foo>"),
|
||||
(data_obj, 191, XMLData, None),
|
||||
(data_obj, 192, XMLData, ""),
|
||||
|
||||
(generic_obj, 200, GenericData, ['Generic Object 1', 'tag1', 'tag2']),
|
||||
(generic_obj, 201, GenericData, ['Generic Object 2', 'tag2', 'tag3']),
|
||||
@ -241,6 +248,15 @@ The end."""),
|
||||
# (pk_obj, 790, XMLPKData, "<foo></foo>"),
|
||||
]
|
||||
|
||||
# Because Oracle treats the empty string as NULL, Oracle is expected to fail
|
||||
# when field.empty_strings_allowed is True and the value is None; skip these
|
||||
# tests.
|
||||
if settings.DATABASE_ENGINE == 'oracle':
|
||||
test_data = [data for data in test_data
|
||||
if not (data[0] == data_obj and
|
||||
data[2]._meta.get_field('data').empty_strings_allowed and
|
||||
data[3] is None)]
|
||||
|
||||
# Dynamically create serializer tests to ensure that all
|
||||
# registered serializers are automatically tested.
|
||||
class SerializerTests(unittest.TestCase):
|
||||
|
@ -725,7 +725,7 @@ class Templates(unittest.TestCase):
|
||||
'url01' : ('{% url regressiontests.templates.views.client client.id %}', {'client': {'id': 1}}, '/url_tag/client/1/'),
|
||||
'url02' : ('{% url regressiontests.templates.views.client_action client.id, action="update" %}', {'client': {'id': 1}}, '/url_tag/client/1/update/'),
|
||||
'url03' : ('{% url regressiontests.templates.views.index %}', {}, '/url_tag/'),
|
||||
'url04' : ('{% url named-client client.id %}', {'client': {'id': 1}}, '/url_tag/named-client/1/'),
|
||||
'url04' : ('{% url named.client client.id %}', {'client': {'id': 1}}, '/url_tag/named-client/1/'),
|
||||
|
||||
# Failures
|
||||
'url-fail01' : ('{% url %}', {}, template.TemplateSyntaxError),
|
||||
|
@ -7,5 +7,5 @@ urlpatterns = patterns('',
|
||||
(r'^$', views.index),
|
||||
(r'^client/(\d+)/$', views.client),
|
||||
(r'^client/(\d+)/(?P<action>[^/]+)/$', views.client_action),
|
||||
url(r'^named-client/(\d+)/$', views.client, name="named-client"),
|
||||
url(r'^named-client/(\d+)/$', views.client, name="named.client"),
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user