Merged w/trunk up to r10333. This will probably be the last commit to this repo because all important functionality here has been merged into trunk.
1.1 --- a/AUTHORS Sat Mar 28 12:05:48 2009 -0500
1.2 +++ b/AUTHORS Wed Apr 01 13:05:19 2009 -0500
1.3 @@ -79,7 +79,7 @@
1.4 Brett Cannon <brett@python.org>
1.5 Ricardo Javier Cárdenes Medina <ricardo.cardenes@gmail.com>
1.6 Jeremy Carbaugh <jcarbaugh@gmail.com>
1.7 - carljm <carl@dirtcircle.com>
1.8 + Carl Meyer <carl@dirtcircle.com>
1.9 Graham Carlyle <graham.carlyle@maplecroft.net>
1.10 Antonio Cavedoni <http://cavedoni.com/>
1.11 C8E
1.12 @@ -242,6 +242,7 @@
1.13 konrad@gwu.edu
1.14 knox <christobzr@gmail.com>
1.15 David Krauth
1.16 + Kevin Kubasik <kevin@kubasik.net>
1.17 kurtiss@meetro.com
1.18 Denis Kuzmichyov <kuzmichyov@gmail.com>
1.19 Panos Laganakos <panos.laganakos@gmail.com>
1.20 @@ -364,6 +365,7 @@
1.21 Ivan Sagalaev (Maniac) <http://www.softwaremaniacs.org/>
1.22 Vinay Sajip <vinay_sajip@yahoo.co.uk>
1.23 Kadesarin Sanjek
1.24 + Massimo Scamarcia <massimo.scamarcia@gmail.com>
1.25 David Schein
1.26 Bernd Schlapsi
1.27 scott@staplefish.com
2.1 --- a/MANIFEST.in Sat Mar 28 12:05:48 2009 -0500
2.2 +++ b/MANIFEST.in Wed Apr 01 13:05:19 2009 -0500
2.3 @@ -10,6 +10,7 @@
2.4 recursive-include scripts *
2.5 recursive-include examples *
2.6 recursive-include extras *
2.7 +recursive-include tests *
2.8 recursive-include django/conf/locale *
2.9 recursive-include django/contrib/admin/templates *
2.10 recursive-include django/contrib/admin/media *
3.1 Binary file django/conf/locale/it/LC_MESSAGES/django.mo has changed
4.1 --- a/django/conf/locale/it/LC_MESSAGES/django.po Sat Mar 28 12:05:48 2009 -0500
4.2 +++ b/django/conf/locale/it/LC_MESSAGES/django.po Wed Apr 01 13:05:19 2009 -0500
4.3 @@ -1,4 +1,4 @@
4.4 -# translation of django.po to Italiano
4.5 +# translation of django.po to Italian
4.6 # Italian translation of Django.
4.7 # Copyright (C) 2006 the Lawrence Journal-World
4.8 # This file is distributed under the same license as the Django package.
4.9 @@ -6,8 +6,8 @@
4.10 msgstr ""
4.11 "Project-Id-Version: django\n"
4.12 "Report-Msgid-Bugs-To: \n"
4.13 -"POT-Creation-Date: 2008-11-14 08:07+0100\n"
4.14 -"PO-Revision-Date: 2008-11-14 08:10+0100\n"
4.15 +"POT-Creation-Date: 2009-03-29 14:34+0200\n"
4.16 +"PO-Revision-Date: 2009-03-29 14:58+0200\n"
4.17 "Last-Translator: Nicola Larosa <nico@tekNico.net>\n"
4.18 "Language-Team: Italiano\n"
4.19 "MIME-Version: 1.0\n"
4.20 @@ -101,118 +101,122 @@
4.21 msgstr "ebraico"
4.22
4.23 #: conf/global_settings.py:65
4.24 +msgid "Hindi"
4.25 +msgstr "hindi"
4.26 +
4.27 +#: conf/global_settings.py:66
4.28 msgid "Croatian"
4.29 msgstr "croato"
4.30
4.31 -#: conf/global_settings.py:66
4.32 +#: conf/global_settings.py:67
4.33 msgid "Icelandic"
4.34 msgstr "islandese"
4.35
4.36 -#: conf/global_settings.py:67
4.37 +#: conf/global_settings.py:68
4.38 msgid "Italian"
4.39 msgstr "italiano"
4.40
4.41 -#: conf/global_settings.py:68
4.42 +#: conf/global_settings.py:69
4.43 msgid "Japanese"
4.44 msgstr "giapponese"
4.45
4.46 -#: conf/global_settings.py:69
4.47 +#: conf/global_settings.py:70
4.48 msgid "Georgian"
4.49 msgstr "georgiano"
4.50
4.51 -#: conf/global_settings.py:70
4.52 +#: conf/global_settings.py:71
4.53 msgid "Korean"
4.54 msgstr "coreano"
4.55
4.56 -#: conf/global_settings.py:71
4.57 +#: conf/global_settings.py:72
4.58 msgid "Khmer"
4.59 msgstr "khmer"
4.60
4.61 -#: conf/global_settings.py:72
4.62 +#: conf/global_settings.py:73
4.63 msgid "Kannada"
4.64 msgstr "kannada"
4.65
4.66 -#: conf/global_settings.py:73
4.67 +#: conf/global_settings.py:74
4.68 msgid "Latvian"
4.69 msgstr "lettone"
4.70
4.71 -#: conf/global_settings.py:74
4.72 +#: conf/global_settings.py:75
4.73 msgid "Lithuanian"
4.74 msgstr "lituano"
4.75
4.76 -#: conf/global_settings.py:75
4.77 +#: conf/global_settings.py:76
4.78 msgid "Macedonian"
4.79 msgstr "macedone"
4.80
4.81 -#: conf/global_settings.py:76
4.82 +#: conf/global_settings.py:77
4.83 msgid "Dutch"
4.84 msgstr "olandese"
4.85
4.86 -#: conf/global_settings.py:77
4.87 +#: conf/global_settings.py:78
4.88 msgid "Norwegian"
4.89 msgstr "norvegese"
4.90
4.91 -#: conf/global_settings.py:78
4.92 +#: conf/global_settings.py:79
4.93 msgid "Polish"
4.94 msgstr "polacco"
4.95
4.96 -#: conf/global_settings.py:79
4.97 +#: conf/global_settings.py:80
4.98 msgid "Portuguese"
4.99 msgstr "portoghese"
4.100
4.101 -#: conf/global_settings.py:80
4.102 +#: conf/global_settings.py:81
4.103 msgid "Brazilian Portuguese"
4.104 msgstr "brasiliano portoghese"
4.105
4.106 -#: conf/global_settings.py:81
4.107 +#: conf/global_settings.py:82
4.108 msgid "Romanian"
4.109 msgstr "rumeno"
4.110
4.111 -#: conf/global_settings.py:82
4.112 +#: conf/global_settings.py:83
4.113 msgid "Russian"
4.114 msgstr "russo"
4.115
4.116 -#: conf/global_settings.py:83
4.117 +#: conf/global_settings.py:84
4.118 msgid "Slovak"
4.119 msgstr "slovacco"
4.120
4.121 -#: conf/global_settings.py:84
4.122 +#: conf/global_settings.py:85
4.123 msgid "Slovenian"
4.124 msgstr "sloveno"
4.125
4.126 -#: conf/global_settings.py:85
4.127 +#: conf/global_settings.py:86
4.128 msgid "Serbian"
4.129 msgstr "serbo"
4.130
4.131 -#: conf/global_settings.py:86
4.132 +#: conf/global_settings.py:87
4.133 msgid "Swedish"
4.134 msgstr "svedese"
4.135
4.136 -#: conf/global_settings.py:87
4.137 +#: conf/global_settings.py:88
4.138 msgid "Tamil"
4.139 msgstr "tamil"
4.140
4.141 -#: conf/global_settings.py:88
4.142 +#: conf/global_settings.py:89
4.143 msgid "Telugu"
4.144 msgstr "telugu"
4.145
4.146 -#: conf/global_settings.py:89
4.147 +#: conf/global_settings.py:90
4.148 msgid "Thai"
4.149 msgstr "thai"
4.150
4.151 -#: conf/global_settings.py:90
4.152 +#: conf/global_settings.py:91
4.153 msgid "Turkish"
4.154 msgstr "turco"
4.155
4.156 -#: conf/global_settings.py:91
4.157 +#: conf/global_settings.py:92
4.158 msgid "Ukrainian"
4.159 msgstr "ucraino"
4.160
4.161 -#: conf/global_settings.py:92
4.162 +#: conf/global_settings.py:93
4.163 msgid "Simplified Chinese"
4.164 msgstr "cinese semplificato"
4.165
4.166 -#: conf/global_settings.py:93
4.167 +#: conf/global_settings.py:94
4.168 msgid "Traditional Chinese"
4.169 msgstr "cinese tradizionale"
4.170
4.171 @@ -225,43 +229,47 @@
4.172 "<h3>Di %s:</h3>\n"
4.173 "<ul>\n"
4.174
4.175 -#: contrib/admin/filterspecs.py:74 contrib/admin/filterspecs.py:91
4.176 -#: contrib/admin/filterspecs.py:146 contrib/admin/filterspecs.py:172
4.177 +#: contrib/admin/filterspecs.py:75 contrib/admin/filterspecs.py:92
4.178 +#: contrib/admin/filterspecs.py:147 contrib/admin/filterspecs.py:173
4.179 msgid "All"
4.180 msgstr "Tutti"
4.181
4.182 -#: contrib/admin/filterspecs.py:112
4.183 +#: contrib/admin/filterspecs.py:113
4.184 msgid "Any date"
4.185 msgstr "Qualsiasi data"
4.186
4.187 -#: contrib/admin/filterspecs.py:113
4.188 +#: contrib/admin/filterspecs.py:114
4.189 msgid "Today"
4.190 msgstr "Oggi"
4.191
4.192 -#: contrib/admin/filterspecs.py:116
4.193 +#: contrib/admin/filterspecs.py:117
4.194 msgid "Past 7 days"
4.195 msgstr "Ultimi 7 giorni"
4.196
4.197 -#: contrib/admin/filterspecs.py:118
4.198 +#: contrib/admin/filterspecs.py:119
4.199 msgid "This month"
4.200 msgstr "Questo mese"
4.201
4.202 -#: contrib/admin/filterspecs.py:120
4.203 +#: contrib/admin/filterspecs.py:121
4.204 msgid "This year"
4.205 msgstr "Quest'anno"
4.206
4.207 -#: contrib/admin/filterspecs.py:146 forms/widgets.py:390
4.208 +#: contrib/admin/filterspecs.py:147 forms/widgets.py:413
4.209 msgid "Yes"
4.210 msgstr "Sì"
4.211
4.212 -#: contrib/admin/filterspecs.py:146 forms/widgets.py:390
4.213 +#: contrib/admin/filterspecs.py:147 forms/widgets.py:413
4.214 msgid "No"
4.215 msgstr "No"
4.216
4.217 -#: contrib/admin/filterspecs.py:153 forms/widgets.py:390
4.218 +#: contrib/admin/filterspecs.py:154 forms/widgets.py:413
4.219 msgid "Unknown"
4.220 msgstr "Sconosciuto"
4.221
4.222 +#: contrib/admin/helpers.py:14
4.223 +msgid "Action:"
4.224 +msgstr "Azione:"
4.225 +
4.226 #: contrib/admin/models.py:19
4.227 msgid "action time"
4.228 msgstr "momento dell'azione"
4.229 @@ -290,100 +298,117 @@
4.230 msgid "log entries"
4.231 msgstr "voci di log"
4.232
4.233 -#: contrib/admin/options.py:60 contrib/admin/options.py:121
4.234 +#: contrib/admin/options.py:131 contrib/admin/options.py:145
4.235 msgid "None"
4.236 msgstr "Nessuno"
4.237
4.238 -#: contrib/admin/options.py:338
4.239 +#: contrib/admin/options.py:498
4.240 +#, python-format
4.241 +msgid "Successfully deleted %(count)d %(items)s."
4.242 +msgstr "Cancellati con successo %(count)d %(items)s."
4.243 +
4.244 +#: contrib/admin/options.py:505 contrib/admin/options.py:1012
4.245 +msgid "Are you sure?"
4.246 +msgstr "Sei sicuro?"
4.247 +
4.248 +#: contrib/admin/options.py:523
4.249 +#, python-format
4.250 +msgid "Delete selected %(verbose_name_plural)s"
4.251 +msgstr "Cancellati i %(verbose_name_plural)s selezionati"
4.252 +
4.253 +#: contrib/admin/options.py:531
4.254 #, python-format
4.255 msgid "Changed %s."
4.256 msgstr "%s modificato."
4.257
4.258 -#: contrib/admin/options.py:338 contrib/admin/options.py:348
4.259 -#: contrib/comments/templates/comments/preview.html:15 forms/models.py:288
4.260 +#: contrib/admin/options.py:531 contrib/admin/options.py:541
4.261 +#: contrib/comments/templates/comments/preview.html:15 forms/models.py:296
4.262 msgid "and"
4.263 msgstr "e"
4.264
4.265 -#: contrib/admin/options.py:343
4.266 +#: contrib/admin/options.py:536
4.267 #, python-format
4.268 msgid "Added %(name)s \"%(object)s\"."
4.269 msgstr "Aggiunti %(name)s \"%(object)s\"."
4.270
4.271 -#: contrib/admin/options.py:347
4.272 +#: contrib/admin/options.py:540
4.273 #, python-format
4.274 msgid "Changed %(list)s for %(name)s \"%(object)s\"."
4.275 msgstr "Cambiati %(list)s per %(name)s \"%(object)s\"."
4.276
4.277 -#: contrib/admin/options.py:352
4.278 +#: contrib/admin/options.py:545
4.279 #, python-format
4.280 msgid "Deleted %(name)s \"%(object)s\"."
4.281 msgstr "Cancellati %(name)s \"%(object)s\"."
4.282
4.283 -#: contrib/admin/options.py:356
4.284 +#: contrib/admin/options.py:549
4.285 msgid "No fields changed."
4.286 msgstr "Nessun campo modificato."
4.287
4.288 -#: contrib/admin/options.py:417 contrib/auth/admin.py:51
4.289 +#: contrib/admin/options.py:610 contrib/auth/admin.py:67
4.290 #, python-format
4.291 msgid "The %(name)s \"%(obj)s\" was added successfully."
4.292 msgstr "%(name)s \"%(obj)s\" è stato aggiunto correttamente."
4.293
4.294 -#: contrib/admin/options.py:421 contrib/admin/options.py:454
4.295 -#: contrib/auth/admin.py:59
4.296 +#: contrib/admin/options.py:614 contrib/admin/options.py:647
4.297 +#: contrib/auth/admin.py:75
4.298 msgid "You may edit it again below."
4.299 msgstr "È possibile modificarlo nuovamente qui sotto."
4.300
4.301 -#: contrib/admin/options.py:431 contrib/admin/options.py:464
4.302 +#: contrib/admin/options.py:624 contrib/admin/options.py:657
4.303 #, python-format
4.304 msgid "You may add another %s below."
4.305 msgstr "Puoi aggiungere un altro %s qui sotto."
4.306
4.307 -#: contrib/admin/options.py:452
4.308 +#: contrib/admin/options.py:645
4.309 #, python-format
4.310 msgid "The %(name)s \"%(obj)s\" was changed successfully."
4.311 msgstr "%(name)s \"%(obj)s\" modificato correttamente."
4.312
4.313 -#: contrib/admin/options.py:460
4.314 +#: contrib/admin/options.py:653
4.315 #, python-format
4.316 msgid "The %(name)s \"%(obj)s\" was added successfully. You may edit it again below."
4.317 msgstr ""
4.318 "%(name)s \"%(obj)s\" aggiunto correttamente. Puoi modificarlo ancora qui "
4.319 "sotto."
4.320
4.321 -#: contrib/admin/options.py:528
4.322 +#: contrib/admin/options.py:774
4.323 #, python-format
4.324 msgid "Add %s"
4.325 msgstr "Aggiungi %s"
4.326
4.327 -#: contrib/admin/options.py:559 contrib/admin/options.py:673
4.328 +#: contrib/admin/options.py:805 contrib/admin/options.py:990
4.329 #, python-format
4.330 msgid "%(name)s object with primary key %(key)r does not exist."
4.331 msgstr "L'oggetto %(name)s con chiave primaria %(key)r non esiste."
4.332
4.333 -#: contrib/admin/options.py:606
4.334 +#: contrib/admin/options.py:862
4.335 #, python-format
4.336 msgid "Change %s"
4.337 msgstr "Modifica %s"
4.338
4.339 -#: contrib/admin/options.py:638
4.340 +#: contrib/admin/options.py:894
4.341 msgid "Database error"
4.342 msgstr "Errore nel database"
4.343
4.344 -#: contrib/admin/options.py:688
4.345 +#: contrib/admin/options.py:930
4.346 +#, python-format
4.347 +msgid "%(count)s %(name)s was changed successfully."
4.348 +msgid_plural "%(count)s %(name)s were changed successfully."
4.349 +msgstr[0] "%(count)s %(name)s è stato modificato correttamente."
4.350 +msgstr[1] "%(count)s %(name)s sono stati modificati correttamente."
4.351 +
4.352 +#: contrib/admin/options.py:1005
4.353 #, python-format
4.354 msgid "The %(name)s \"%(obj)s\" was deleted successfully."
4.355 msgstr "%(name)s \"%(obj)s\" cancellato correttamente."
4.356
4.357 -#: contrib/admin/options.py:695
4.358 -msgid "Are you sure?"
4.359 -msgstr "Sei sicuro?"
4.360 -
4.361 -#: contrib/admin/options.py:724
4.362 +#: contrib/admin/options.py:1041
4.363 #, python-format
4.364 msgid "Change history: %s"
4.365 msgstr "Tracciato delle modifiche: %s"
4.366
4.367 -#: contrib/admin/sites.py:16 contrib/admin/views/decorators.py:14
4.368 +#: contrib/admin/sites.py:15 contrib/admin/views/decorators.py:14
4.369 #: contrib/auth/forms.py:80
4.370 msgid ""
4.371 "Please enter a correct username and password. Note that both fields are case-"
4.372 @@ -392,11 +417,11 @@
4.373 "Inserisci nome utente e password corretti. In entrambi i campi le maiuscole "
4.374 "sono significative."
4.375
4.376 -#: contrib/admin/sites.py:226 contrib/admin/views/decorators.py:40
4.377 +#: contrib/admin/sites.py:250 contrib/admin/views/decorators.py:40
4.378 msgid "Please log in again, because your session has expired."
4.379 msgstr "Effettua di nuovo l'accesso, perché la tua sessione è scaduta."
4.380
4.381 -#: contrib/admin/sites.py:233 contrib/admin/views/decorators.py:47
4.382 +#: contrib/admin/sites.py:257 contrib/admin/views/decorators.py:47
4.383 msgid ""
4.384 "Looks like your browser isn't configured to accept cookies. Please enable "
4.385 "cookies, reload this page, and try again."
4.386 @@ -404,37 +429,37 @@
4.387 "Il browser non sembra configurato per accettare i cookie. Una volta "
4.388 "abilitati, ricarica la pagina e riprova."
4.389
4.390 -#: contrib/admin/sites.py:249 contrib/admin/sites.py:255
4.391 +#: contrib/admin/sites.py:273 contrib/admin/sites.py:279
4.392 #: contrib/admin/views/decorators.py:66
4.393 msgid "Usernames cannot contain the '@' character."
4.394 msgstr "I nomi utente non possono contenere il carattere '@'."
4.395
4.396 -#: contrib/admin/sites.py:252 contrib/admin/views/decorators.py:62
4.397 +#: contrib/admin/sites.py:276 contrib/admin/views/decorators.py:62
4.398 #, python-format
4.399 msgid "Your e-mail address is not your username. Try '%s' instead."
4.400 msgstr "Il nome utente non è costituito dall'indirizzo e-mail. Prova con '%s'."
4.401
4.402 -#: contrib/admin/sites.py:312
4.403 +#: contrib/admin/sites.py:336
4.404 msgid "Site administration"
4.405 msgstr "Amministrazione sito"
4.406
4.407 -#: contrib/admin/sites.py:325 contrib/admin/templates/admin/login.html:26
4.408 +#: contrib/admin/sites.py:349 contrib/admin/templates/admin/login.html:26
4.409 #: contrib/admin/templates/registration/password_reset_complete.html:14
4.410 #: contrib/admin/views/decorators.py:20
4.411 msgid "Log in"
4.412 msgstr "Accedi"
4.413
4.414 -#: contrib/admin/sites.py:372
4.415 +#: contrib/admin/sites.py:396
4.416 #, python-format
4.417 msgid "%s administration"
4.418 msgstr "Amministrazione %s"
4.419
4.420 -#: contrib/admin/util.py:138
4.421 +#: contrib/admin/util.py:168
4.422 #, python-format
4.423 msgid "One or more %(fieldname)s in %(name)s: %(obj)s"
4.424 msgstr "Uno o più %(fieldname)s in %(name)s: %(obj)s"
4.425
4.426 -#: contrib/admin/util.py:143
4.427 +#: contrib/admin/util.py:173
4.428 #, python-format
4.429 msgid "One or more %(fieldname)s in %(name)s:"
4.430 msgstr "Uno o più %(fieldname)s in %(name)s:"
4.431 @@ -474,10 +499,11 @@
4.432
4.433 #: contrib/admin/templates/admin/500.html:4
4.434 #: contrib/admin/templates/admin/app_index.html:8
4.435 -#: contrib/admin/templates/admin/base.html:31
4.436 +#: contrib/admin/templates/admin/base.html:33
4.437 #: contrib/admin/templates/admin/change_form.html:17
4.438 -#: contrib/admin/templates/admin/change_list.html:8
4.439 +#: contrib/admin/templates/admin/change_list.html:20
4.440 #: contrib/admin/templates/admin/delete_confirmation.html:6
4.441 +#: contrib/admin/templates/admin/delete_selected_confirmation.html:6
4.442 #: contrib/admin/templates/admin/invalid_setup.html:4
4.443 #: contrib/admin/templates/admin/object_history.html:6
4.444 #: contrib/admin/templates/admin/auth/user/change_password.html:10
4.445 @@ -512,24 +538,33 @@
4.446 "Si è verificato un errore. È stato riportato agli amministratori del sito "
4.447 "via e-mail e verrà corretto a breve. Grazie per la tua pazienza."
4.448
4.449 +#: contrib/admin/templates/admin/actions.html:4
4.450 +msgid "Run the selected action"
4.451 +msgstr "Esegui l'azione selezionata"
4.452 +
4.453 +#: contrib/admin/templates/admin/actions.html:4
4.454 +#: contrib/admin/templates/admin/search_form.html:8
4.455 +msgid "Go"
4.456 +msgstr "Vai"
4.457 +
4.458 #: contrib/admin/templates/admin/app_index.html:10
4.459 #: contrib/admin/templates/admin/index.html:19
4.460 #, python-format
4.461 msgid "%(name)s"
4.462 msgstr "%(name)s"
4.463
4.464 -#: contrib/admin/templates/admin/base.html:26
4.465 +#: contrib/admin/templates/admin/base.html:28
4.466 msgid "Welcome,"
4.467 msgstr "Benvenuto,"
4.468
4.469 -#: contrib/admin/templates/admin/base.html:26
4.470 +#: contrib/admin/templates/admin/base.html:28
4.471 #: contrib/admin/templates/registration/password_change_done.html:3
4.472 #: contrib/admin/templates/registration/password_change_form.html:3
4.473 #: contrib/admindocs/templates/admin_doc/bookmarklets.html:3
4.474 msgid "Documentation"
4.475 msgstr "Documentazione"
4.476
4.477 -#: contrib/admin/templates/admin/base.html:26
4.478 +#: contrib/admin/templates/admin/base.html:28
4.479 #: contrib/admin/templates/admin/auth/user/change_password.html:13
4.480 #: contrib/admin/templates/admin/auth/user/change_password.html:46
4.481 #: contrib/admin/templates/registration/password_change_done.html:3
4.482 @@ -537,7 +572,7 @@
4.483 msgid "Change password"
4.484 msgstr "Cambia la password"
4.485
4.486 -#: contrib/admin/templates/admin/base.html:26
4.487 +#: contrib/admin/templates/admin/base.html:28
4.488 #: contrib/admin/templates/registration/password_change_done.html:3
4.489 #: contrib/admin/templates/registration/password_change_form.html:3
4.490 msgid "Log out"
4.491 @@ -568,23 +603,24 @@
4.492 msgstr "Vedi sul sito"
4.493
4.494 #: contrib/admin/templates/admin/change_form.html:38
4.495 +#: contrib/admin/templates/admin/change_list.html:49
4.496 #: contrib/admin/templates/admin/auth/user/change_password.html:22
4.497 msgid "Please correct the error below."
4.498 msgid_plural "Please correct the errors below."
4.499 msgstr[0] "Correggi gli errori qui sotto."
4.500 msgstr[1] "Correggi gli errori qui sotto."
4.501
4.502 -#: contrib/admin/templates/admin/change_list.html:16
4.503 +#: contrib/admin/templates/admin/change_list.html:41
4.504 #, python-format
4.505 msgid "Add %(name)s"
4.506 msgstr "Aggiungi %(name)s"
4.507
4.508 -#: contrib/admin/templates/admin/change_list.html:26
4.509 +#: contrib/admin/templates/admin/change_list.html:60
4.510 msgid "Filter"
4.511 msgstr "Filtro"
4.512
4.513 #: contrib/admin/templates/admin/delete_confirmation.html:10
4.514 -#: contrib/admin/templates/admin/submit_line.html:4 forms/formsets.py:246
4.515 +#: contrib/admin/templates/admin/submit_line.html:4 forms/formsets.py:251
4.516 msgid "Delete"
4.517 msgstr "Cancella"
4.518
4.519 @@ -609,9 +645,32 @@
4.520 "oggetti collegati seguenti saranno cancellati:"
4.521
4.522 #: contrib/admin/templates/admin/delete_confirmation.html:28
4.523 +#: contrib/admin/templates/admin/delete_selected_confirmation.html:33
4.524 msgid "Yes, I'm sure"
4.525 msgstr "Sì, sono sicuro"
4.526
4.527 +#: contrib/admin/templates/admin/delete_selected_confirmation.html:9
4.528 +msgid "Delete multiple objects"
4.529 +msgstr "Cancella più oggetti"
4.530 +
4.531 +#: contrib/admin/templates/admin/delete_selected_confirmation.html:15
4.532 +#, python-format
4.533 +msgid ""
4.534 +"Deleting the %(object_name)s would result in deleting related objects, but "
4.535 +"your account doesn't have permission to delete the following types of "
4.536 +"objects:"
4.537 +msgstr ""
4.538 +"La cancellazione di %(object_name)s causerebbe la "
4.539 +"cancellazione di oggetti collegati, ma il tuo account non ha i permessi per "
4.540 +"cancellare gli oggetti dei seguenti tipi:"
4.541 +
4.542 +#: contrib/admin/templates/admin/delete_selected_confirmation.html:22
4.543 +#, python-format
4.544 +msgid ""
4.545 +"Are you sure you want to delete the selected %(object_name)s objects? All of "
4.546 +"the following objects and it's related items will be deleted:"
4.547 +msgstr "Sicuro di voler cancellare i %(object_name)s selezionati? Tutti i seguenti oggetti, e i loro oggetti collegati, saranno cancellati:"
4.548 +
4.549 #: contrib/admin/templates/admin/filter.html:2
4.550 #, python-format
4.551 msgid " By %(filter_title)s "
4.552 @@ -674,7 +733,7 @@
4.553 msgstr "Azione"
4.554
4.555 #: contrib/admin/templates/admin/object_history.html:30
4.556 -#: utils/translation/trans_real.py:404
4.557 +#: utils/translation/trans_real.py:400
4.558 msgid "DATETIME_FORMAT"
4.559 msgstr "j F Y, H:i"
4.560
4.561 @@ -690,10 +749,6 @@
4.562 msgid "Show all"
4.563 msgstr "Mostra tutto"
4.564
4.565 -#: contrib/admin/templates/admin/search_form.html:8
4.566 -msgid "Go"
4.567 -msgstr "Vai"
4.568 -
4.569 #: contrib/admin/templates/admin/search_form.html:10
4.570 #, python-format
4.571 msgid "1 result"
4.572 @@ -737,13 +792,13 @@
4.573
4.574 #: contrib/admin/templates/admin/auth/user/add_form.html:20
4.575 #: contrib/admin/templates/admin/auth/user/change_password.html:33
4.576 -#: contrib/auth/forms.py:17 contrib/auth/forms.py:60 contrib/auth/forms.py:184
4.577 +#: contrib/auth/forms.py:17 contrib/auth/forms.py:60 contrib/auth/forms.py:185
4.578 msgid "Password"
4.579 msgstr "Password"
4.580
4.581 #: contrib/admin/templates/admin/auth/user/add_form.html:26
4.582 #: contrib/admin/templates/admin/auth/user/change_password.html:39
4.583 -#: contrib/auth/forms.py:185
4.584 +#: contrib/auth/forms.py:186
4.585 msgid "Password (again)"
4.586 msgstr "Password (di nuovo)"
4.587
4.588 @@ -913,166 +968,166 @@
4.589 msgid "Reset my password"
4.590 msgstr "Reimposta la mia password"
4.591
4.592 -#: contrib/admin/templatetags/admin_list.py:284
4.593 +#: contrib/admin/templatetags/admin_list.py:299
4.594 msgid "All dates"
4.595 msgstr "Tutte le date"
4.596
4.597 -#: contrib/admin/views/main.py:69
4.598 +#: contrib/admin/views/main.py:70
4.599 #, python-format
4.600 msgid "Select %s"
4.601 msgstr "Scegli %s"
4.602
4.603 -#: contrib/admin/views/main.py:69
4.604 +#: contrib/admin/views/main.py:70
4.605 #, python-format
4.606 msgid "Select %s to change"
4.607 msgstr "Scegli %s da modificare"
4.608
4.609 -#: contrib/admin/views/template.py:36 contrib/sites/models.py:38
4.610 +#: contrib/admin/views/template.py:37 contrib/sites/models.py:38
4.611 msgid "site"
4.612 msgstr "sito"
4.613
4.614 -#: contrib/admin/views/template.py:38
4.615 +#: contrib/admin/views/template.py:39
4.616 msgid "template"
4.617 msgstr "modello"
4.618
4.619 -#: contrib/admindocs/views.py:57 contrib/admindocs/views.py:59
4.620 -#: contrib/admindocs/views.py:61
4.621 +#: contrib/admindocs/views.py:58 contrib/admindocs/views.py:60
4.622 +#: contrib/admindocs/views.py:62
4.623 msgid "tag:"
4.624 msgstr "tag:"
4.625
4.626 -#: contrib/admindocs/views.py:90 contrib/admindocs/views.py:92
4.627 -#: contrib/admindocs/views.py:94
4.628 +#: contrib/admindocs/views.py:91 contrib/admindocs/views.py:93
4.629 +#: contrib/admindocs/views.py:95
4.630 msgid "filter:"
4.631 msgstr "filtro:"
4.632
4.633 -#: contrib/admindocs/views.py:154 contrib/admindocs/views.py:156
4.634 -#: contrib/admindocs/views.py:158
4.635 +#: contrib/admindocs/views.py:155 contrib/admindocs/views.py:157
4.636 +#: contrib/admindocs/views.py:159
4.637 msgid "view:"
4.638 msgstr "view:"
4.639
4.640 -#: contrib/admindocs/views.py:186
4.641 +#: contrib/admindocs/views.py:187
4.642 #, python-format
4.643 msgid "App %r not found"
4.644 msgstr "Appl. %r non trovata"
4.645
4.646 -#: contrib/admindocs/views.py:193
4.647 +#: contrib/admindocs/views.py:194
4.648 #, python-format
4.649 msgid "Model %(model_name)r not found in app %(app_label)r"
4.650 msgstr "Modello %(model_name)r non trovato nell'appl. %(app_label)r"
4.651
4.652 -#: contrib/admindocs/views.py:205
4.653 +#: contrib/admindocs/views.py:206
4.654 #, python-format
4.655 msgid "the related `%(app_label)s.%(data_type)s` object"
4.656 msgstr "l'oggetto `%(app_label)s.%(data_type)s` collegato"
4.657
4.658 -#: contrib/admindocs/views.py:205 contrib/admindocs/views.py:227
4.659 -#: contrib/admindocs/views.py:241 contrib/admindocs/views.py:246
4.660 +#: contrib/admindocs/views.py:206 contrib/admindocs/views.py:228
4.661 +#: contrib/admindocs/views.py:242 contrib/admindocs/views.py:247
4.662 msgid "model:"
4.663 msgstr "modello:"
4.664
4.665 -#: contrib/admindocs/views.py:236
4.666 +#: contrib/admindocs/views.py:237
4.667 #, python-format
4.668 msgid "related `%(app_label)s.%(object_name)s` objects"
4.669 msgstr "oggetti `%(app_label)s.%(object_name)s` collegati"
4.670
4.671 -#: contrib/admindocs/views.py:241
4.672 +#: contrib/admindocs/views.py:242
4.673 #, python-format
4.674 msgid "all %s"
4.675 msgstr "tutti %s"
4.676
4.677 -#: contrib/admindocs/views.py:246
4.678 +#: contrib/admindocs/views.py:247
4.679 #, python-format
4.680 msgid "number of %s"
4.681 msgstr "numero di %s"
4.682
4.683 -#: contrib/admindocs/views.py:251
4.684 +#: contrib/admindocs/views.py:252
4.685 #, python-format
4.686 msgid "Fields on %s objects"
4.687 msgstr "Campi sugli oggetti %s"
4.688
4.689 -#: contrib/admindocs/views.py:314 contrib/admindocs/views.py:325
4.690 -#: contrib/admindocs/views.py:327 contrib/admindocs/views.py:333
4.691 -#: contrib/admindocs/views.py:334 contrib/admindocs/views.py:336
4.692 +#: contrib/admindocs/views.py:315 contrib/admindocs/views.py:326
4.693 +#: contrib/admindocs/views.py:328 contrib/admindocs/views.py:334
4.694 +#: contrib/admindocs/views.py:335 contrib/admindocs/views.py:337
4.695 msgid "Integer"
4.696 msgstr "Intero"
4.697
4.698 -#: contrib/admindocs/views.py:315
4.699 +#: contrib/admindocs/views.py:316
4.700 msgid "Boolean (Either True or False)"
4.701 msgstr "Booleano (True o False)"
4.702
4.703 -#: contrib/admindocs/views.py:316 contrib/admindocs/views.py:335
4.704 +#: contrib/admindocs/views.py:317 contrib/admindocs/views.py:336
4.705 #, python-format
4.706 msgid "String (up to %(max_length)s)"
4.707 msgstr "Stringa (fino a %(max_length)s)"
4.708
4.709 -#: contrib/admindocs/views.py:317
4.710 +#: contrib/admindocs/views.py:318
4.711 msgid "Comma-separated integers"
4.712 msgstr "Interi separati da virgola"
4.713
4.714 -#: contrib/admindocs/views.py:318
4.715 +#: contrib/admindocs/views.py:319
4.716 msgid "Date (without time)"
4.717 msgstr "Data (senza ora)"
4.718
4.719 -#: contrib/admindocs/views.py:319
4.720 +#: contrib/admindocs/views.py:320
4.721 msgid "Date (with time)"
4.722 msgstr "Data (con ora)"
4.723
4.724 -#: contrib/admindocs/views.py:320
4.725 +#: contrib/admindocs/views.py:321
4.726 msgid "Decimal number"
4.727 msgstr "Numero decimale"
4.728
4.729 -#: contrib/admindocs/views.py:321
4.730 +#: contrib/admindocs/views.py:322
4.731 msgid "E-mail address"
4.732 msgstr "Indirizzo e-mail"
4.733
4.734 -#: contrib/admindocs/views.py:322 contrib/admindocs/views.py:323
4.735 -#: contrib/admindocs/views.py:326
4.736 +#: contrib/admindocs/views.py:323 contrib/admindocs/views.py:324
4.737 +#: contrib/admindocs/views.py:327
4.738 msgid "File path"
4.739 msgstr "Percorso di file"
4.740
4.741 -#: contrib/admindocs/views.py:324
4.742 +#: contrib/admindocs/views.py:325
4.743 msgid "Floating point number"
4.744 msgstr "Numero decimale"
4.745
4.746 -#: contrib/admindocs/views.py:328 contrib/comments/models.py:58
4.747 +#: contrib/admindocs/views.py:329 contrib/comments/models.py:58
4.748 msgid "IP address"
4.749 msgstr "indirizzo IP"
4.750
4.751 -#: contrib/admindocs/views.py:330
4.752 +#: contrib/admindocs/views.py:331
4.753 msgid "Boolean (Either True, False or None)"
4.754 msgstr "Booleano (True, False o None)"
4.755
4.756 -#: contrib/admindocs/views.py:331
4.757 +#: contrib/admindocs/views.py:332
4.758 msgid "Relation to parent model"
4.759 msgstr "Collegamento a modello padre"
4.760
4.761 -#: contrib/admindocs/views.py:332
4.762 +#: contrib/admindocs/views.py:333
4.763 msgid "Phone number"
4.764 msgstr "Numero di telefono"
4.765
4.766 -#: contrib/admindocs/views.py:337
4.767 +#: contrib/admindocs/views.py:338
4.768 msgid "Text"
4.769 msgstr "Testo"
4.770
4.771 -#: contrib/admindocs/views.py:338
4.772 +#: contrib/admindocs/views.py:339
4.773 msgid "Time"
4.774 msgstr "Ora"
4.775
4.776 -#: contrib/admindocs/views.py:339 contrib/comments/forms.py:21
4.777 +#: contrib/admindocs/views.py:340 contrib/comments/forms.py:95
4.778 #: contrib/comments/templates/comments/moderation_queue.html:37
4.779 #: contrib/flatpages/admin.py:8 contrib/flatpages/models.py:7
4.780 msgid "URL"
4.781 msgstr "URL"
4.782
4.783 -#: contrib/admindocs/views.py:340
4.784 +#: contrib/admindocs/views.py:341
4.785 msgid "U.S. state (two uppercase letters)"
4.786 msgstr "Stato USA (due lettere maiuscole)"
4.787
4.788 -#: contrib/admindocs/views.py:341
4.789 +#: contrib/admindocs/views.py:342
4.790 msgid "XML text"
4.791 msgstr "Testo XML"
4.792
4.793 -#: contrib/admindocs/views.py:367
4.794 +#: contrib/admindocs/views.py:368
4.795 #, python-format
4.796 msgid "%s does not appear to be a urlpattern object"
4.797 msgstr "%s non sembra essere un oggetto urlpattern"
4.798 @@ -1164,15 +1219,15 @@
4.799 msgid "Groups"
4.800 msgstr "Gruppi"
4.801
4.802 -#: contrib/auth/admin.py:64
4.803 +#: contrib/auth/admin.py:80
4.804 msgid "Add user"
4.805 msgstr "Aggiungi utente"
4.806
4.807 -#: contrib/auth/admin.py:90
4.808 +#: contrib/auth/admin.py:106
4.809 msgid "Password changed successfully."
4.810 msgstr "La password è stata cambiata correttamente."
4.811
4.812 -#: contrib/auth/admin.py:96
4.813 +#: contrib/auth/admin.py:112
4.814 #, python-format
4.815 msgid "Change password: %s"
4.816 msgstr "Cambia la password: %s"
4.817 @@ -1198,8 +1253,8 @@
4.818 msgid "A user with that username already exists."
4.819 msgstr "Un utente con questo nome·è già presente."
4.820
4.821 -#: contrib/auth/forms.py:36 contrib/auth/forms.py:154
4.822 -#: contrib/auth/forms.py:196
4.823 +#: contrib/auth/forms.py:36 contrib/auth/forms.py:155
4.824 +#: contrib/auth/forms.py:197
4.825 msgid "The two password fields didn't match."
4.826 msgstr "I due campi password non corrispondono."
4.827
4.828 @@ -1227,24 +1282,24 @@
4.829 "Questo indirizzo email non è associato ad alcun account utente. Sei sicuro "
4.830 "di esserti registrato?"
4.831
4.832 -#: contrib/auth/forms.py:134
4.833 +#: contrib/auth/forms.py:135
4.834 #, python-format
4.835 msgid "Password reset on %s"
4.836 msgstr "Password reimpostata su %s"
4.837
4.838 -#: contrib/auth/forms.py:142
4.839 +#: contrib/auth/forms.py:143
4.840 msgid "New password"
4.841 msgstr "Nuova password"
4.842
4.843 -#: contrib/auth/forms.py:143
4.844 +#: contrib/auth/forms.py:144
4.845 msgid "New password confirmation"
4.846 msgstr "Conferma nuova password"
4.847
4.848 -#: contrib/auth/forms.py:168
4.849 +#: contrib/auth/forms.py:169
4.850 msgid "Old password"
4.851 msgstr "Password attuale"
4.852
4.853 -#: contrib/auth/forms.py:176
4.854 +#: contrib/auth/forms.py:177
4.855 msgid "Your old password was entered incorrectly. Please enter it again."
4.856 msgstr "La password attuale non è stata inserita correttamente: va inserita di nuovo."
4.857
4.858 @@ -1368,7 +1423,7 @@
4.859 msgid "Logged out"
4.860 msgstr "Accesso annullato"
4.861
4.862 -#: contrib/auth/management/commands/createsuperuser.py:23 forms/fields.py:428
4.863 +#: contrib/auth/management/commands/createsuperuser.py:23 forms/fields.py:429
4.864 msgid "Enter a valid e-mail address."
4.865 msgstr "Inserisci un indirizzo e-mail valido."
4.866
4.867 @@ -1380,31 +1435,31 @@
4.868 msgid "Metadata"
4.869 msgstr "Metadati"
4.870
4.871 -#: contrib/comments/forms.py:19
4.872 +#: contrib/comments/forms.py:93
4.873 #: contrib/comments/templates/comments/moderation_queue.html:34
4.874 msgid "Name"
4.875 msgstr "Nome"
4.876
4.877 -#: contrib/comments/forms.py:20
4.878 +#: contrib/comments/forms.py:94
4.879 msgid "Email address"
4.880 msgstr "Indirizzo email"
4.881
4.882 -#: contrib/comments/forms.py:22
4.883 +#: contrib/comments/forms.py:96
4.884 #: contrib/comments/templates/comments/moderation_queue.html:35
4.885 msgid "Comment"
4.886 msgstr "Commento"
4.887
4.888 -#: contrib/comments/forms.py:25
4.889 -msgid "If you enter anything in this field your comment will be treated as spam"
4.890 -msgstr "Se inserisci qualcosa in questo campo il tuo commento verrà considerato spam"
4.891 -
4.892 -#: contrib/comments/forms.py:125
4.893 +#: contrib/comments/forms.py:173
4.894 #, python-format
4.895 msgid "Watch your mouth! The word %s is not allowed here."
4.896 msgid_plural "Watch your mouth! The words %s are not allowed here."
4.897 msgstr[0] "Modera i termini: la parola %s non è ammessa."
4.898 msgstr[1] "Modera i termini: le parole %s non sono ammesse."
4.899
4.900 +#: contrib/comments/forms.py:180
4.901 +msgid "If you enter anything in this field your comment will be treated as spam"
4.902 +msgstr "Se inserisci qualcosa in questo campo il tuo commento verrà considerato spam"
4.903 +
4.904 #: contrib/comments/models.py:23
4.905 msgid "object ID"
4.906 msgstr "ID dell'oggetto"
4.907 @@ -2024,6 +2079,82 @@
4.908 msgid "The Chilean RUT is not valid."
4.909 msgstr "Il RUT cileno non è valido."
4.910
4.911 +#: contrib/localflavor/cz/cz_regions.py:8
4.912 +msgid "Prague"
4.913 +msgstr "Praga"
4.914 +
4.915 +#: contrib/localflavor/cz/cz_regions.py:9
4.916 +msgid "Central Bohemian Region"
4.917 +msgstr "Regione Boema Centrale"
4.918 +
4.919 +#: contrib/localflavor/cz/cz_regions.py:10
4.920 +msgid "South Bohemian Region"
4.921 +msgstr "Regione Boema del Sud"
4.922 +
4.923 +#: contrib/localflavor/cz/cz_regions.py:11
4.924 +msgid "Pilsen Region"
4.925 +msgstr "Regione di Pilsen"
4.926 +
4.927 +#: contrib/localflavor/cz/cz_regions.py:12
4.928 +msgid "Carlsbad Region"
4.929 +msgstr "Regione di Carlsbad"
4.930 +
4.931 +#: contrib/localflavor/cz/cz_regions.py:13
4.932 +msgid "Usti Region"
4.933 +msgstr "Regione di Usti"
4.934 +
4.935 +#: contrib/localflavor/cz/cz_regions.py:14
4.936 +msgid "Liberec Region"
4.937 +msgstr "Regione di Liberec"
4.938 +
4.939 +#: contrib/localflavor/cz/cz_regions.py:15
4.940 +msgid "Hradec Region"
4.941 +msgstr "Regione di Hradec"
4.942 +
4.943 +#: contrib/localflavor/cz/cz_regions.py:16
4.944 +msgid "Pardubice Region"
4.945 +msgstr "Regione di Pardubice"
4.946 +
4.947 +#: contrib/localflavor/cz/cz_regions.py:17
4.948 +msgid "Vysocina Region"
4.949 +msgstr "Regione di Vysocina"
4.950 +
4.951 +#: contrib/localflavor/cz/cz_regions.py:18
4.952 +msgid "South Moravian Region"
4.953 +msgstr "Regione della Moravia del Sud"
4.954 +
4.955 +#: contrib/localflavor/cz/cz_regions.py:19
4.956 +msgid "Olomouc Region"
4.957 +msgstr "Regione di Olomouc"
4.958 +
4.959 +#: contrib/localflavor/cz/cz_regions.py:20
4.960 +msgid "Zlin Region"
4.961 +msgstr "Regione di Zlin"
4.962 +
4.963 +#: contrib/localflavor/cz/cz_regions.py:21
4.964 +msgid "Moravian-Silesian Region"
4.965 +msgstr "Regione della Moravia-Silesia"
4.966 +
4.967 +#: contrib/localflavor/cz/forms.py:27 contrib/localflavor/sk/forms.py:30
4.968 +msgid "Enter a postal code in the format XXXXX or XXX XX."
4.969 +msgstr "Inserisci un codice postale nel formato XXXXX o XXX XX ."
4.970 +
4.971 +#: contrib/localflavor/cz/forms.py:47
4.972 +msgid "Enter a birth number in the format XXXXXX/XXXX or XXXXXXXXXX."
4.973 +msgstr "Inserisci un numero di nascita nel formato XXXXXX/XXXX o XXXXXXXXXX."
4.974 +
4.975 +#: contrib/localflavor/cz/forms.py:48
4.976 +msgid "Invalid optional parameter Gender, valid values are 'f' and 'm'"
4.977 +msgstr "Parametro opzionale 'Sesso' non valido, i valori validi sono 'f' ed 'm'"
4.978 +
4.979 +#: contrib/localflavor/cz/forms.py:49
4.980 +msgid "Enter a valid birth number."
4.981 +msgstr "Inserisci un numero di nascita valido."
4.982 +
4.983 +#: contrib/localflavor/cz/forms.py:106
4.984 +msgid "Enter a valid IC number."
4.985 +msgstr "Inserisci un numero di IC valido."
4.986 +
4.987 #: contrib/localflavor/de/de_states.py:5
4.988 msgid "Baden-Wuerttemberg"
4.989 msgstr "Baden-Wuerttemberg"
4.990 @@ -2948,10 +3079,6 @@
4.991 msgid "Enter a valid postal code in the format XXXXXX"
4.992 msgstr "Inserisci un codice postale valido nel formato XXXXXX."
4.993
4.994 -#: contrib/localflavor/sk/forms.py:30
4.995 -msgid "Enter a postal code in the format XXXXX or XXX XX."
4.996 -msgstr "Inserisci un codice postale nel formato XXXXX o XXX XX ."
4.997 -
4.998 #: contrib/localflavor/sk/sk_districts.py:8
4.999 msgid "Banska Bystrica"
4.1000 msgstr "Banska Bystrica"
4.1001 @@ -3702,52 +3829,56 @@
4.1002 msgid "sites"
4.1003 msgstr "siti"
4.1004
4.1005 -#: db/models/fields/__init__.py:348 db/models/fields/__init__.py:683
4.1006 +#: db/models/fields/__init__.py:356 db/models/fields/__init__.py:700
4.1007 msgid "This value must be an integer."
4.1008 msgstr "Questo valore deve essere un intero."
4.1009
4.1010 -#: db/models/fields/__init__.py:379
4.1011 +#: db/models/fields/__init__.py:387
4.1012 msgid "This value must be either True or False."
4.1013 msgstr "Questo valore deve essere True o False."
4.1014
4.1015 -#: db/models/fields/__init__.py:412
4.1016 +#: db/models/fields/__init__.py:420
4.1017 msgid "This field cannot be null."
4.1018 msgstr "Questo campo non può essere nullo."
4.1019
4.1020 -#: db/models/fields/__init__.py:428
4.1021 +#: db/models/fields/__init__.py:436
4.1022 msgid "Enter only digits separated by commas."
4.1023 msgstr "Inserisci solo cifre separate da virgole."
4.1024
4.1025 -#: db/models/fields/__init__.py:459
4.1026 +#: db/models/fields/__init__.py:467
4.1027 msgid "Enter a valid date in YYYY-MM-DD format."
4.1028 msgstr "Inserisci una data valida in formato AAAA-MM-GG."
4.1029
4.1030 -#: db/models/fields/__init__.py:468
4.1031 +#: db/models/fields/__init__.py:476
4.1032 #, python-format
4.1033 msgid "Invalid date: %s"
4.1034 msgstr "Data non valida: %s"
4.1035
4.1036 -#: db/models/fields/__init__.py:532 db/models/fields/__init__.py:550
4.1037 +#: db/models/fields/__init__.py:540 db/models/fields/__init__.py:558
4.1038 msgid "Enter a valid date/time in YYYY-MM-DD HH:MM[:ss[.uuuuuu]] format."
4.1039 msgstr "Inserisci una data/ora valida nel formato AAAA-MM-GG OO:MM[ss[.uuuuuu]]."
4.1040
4.1041 -#: db/models/fields/__init__.py:586
4.1042 +#: db/models/fields/__init__.py:594
4.1043 msgid "This value must be a decimal number."
4.1044 msgstr "Questo valore deve essere un numero decimale."
4.1045
4.1046 -#: db/models/fields/__init__.py:719
4.1047 +#: db/models/fields/__init__.py:676
4.1048 +msgid "This value must be a float."
4.1049 +msgstr "Questo valore deve essere un numero a virgola mobile."
4.1050 +
4.1051 +#: db/models/fields/__init__.py:736
4.1052 msgid "This value must be either None, True or False."
4.1053 msgstr "Questo valore deve essere None, True o False."
4.1054
4.1055 -#: db/models/fields/__init__.py:817 db/models/fields/__init__.py:831
4.1056 +#: db/models/fields/__init__.py:839 db/models/fields/__init__.py:853
4.1057 msgid "Enter a valid time in HH:MM[:ss[.uuuuuu]] format."
4.1058 msgstr "Inserisci un ora valida nel formato OO:MM[ss[.uuuuuu]]."
4.1059
4.1060 -#: db/models/fields/related.py:761
4.1061 +#: db/models/fields/related.py:787
4.1062 msgid "Hold down \"Control\", or \"Command\" on a Mac, to select more than one."
4.1063 msgstr "Tieni premuto \"Control\", o \"Command\" su Mac, per selezionarne più di uno."
4.1064
4.1065 -#: db/models/fields/related.py:838
4.1066 +#: db/models/fields/related.py:865
4.1067 #, python-format
4.1068 msgid "Please enter valid %(self)s IDs. The value %(value)r is invalid."
4.1069 msgid_plural "Please enter valid %(self)s IDs. The values %(value)r are invalid."
4.1070 @@ -3809,109 +3940,111 @@
4.1071 msgid "Ensure that there are no more than %s digits before the decimal point."
4.1072 msgstr "Assicurarsi che non vi siano più di %s cifre prima della virgola."
4.1073
4.1074 -#: forms/fields.py:287 forms/fields.py:849
4.1075 +#: forms/fields.py:288 forms/fields.py:850
4.1076 msgid "Enter a valid date."
4.1077 msgstr "Inserisci una data valida."
4.1078
4.1079 -#: forms/fields.py:321 forms/fields.py:850
4.1080 +#: forms/fields.py:322 forms/fields.py:851
4.1081 msgid "Enter a valid time."
4.1082 msgstr "Inserisci un ora valida."
4.1083
4.1084 -#: forms/fields.py:360
4.1085 +#: forms/fields.py:361
4.1086 msgid "Enter a valid date/time."
4.1087 msgstr "Inserisci una coppia data/ora valida."
4.1088
4.1089 -#: forms/fields.py:446
4.1090 +#: forms/fields.py:447
4.1091 msgid "No file was submitted. Check the encoding type on the form."
4.1092 msgstr "Non è stato inviato alcun file. Verifica il tipo di codifica della form."
4.1093
4.1094 -#: forms/fields.py:447
4.1095 +#: forms/fields.py:448
4.1096 msgid "No file was submitted."
4.1097 msgstr "Nessun file è stato inviato."
4.1098
4.1099 -#: forms/fields.py:448
4.1100 +#: forms/fields.py:449
4.1101 msgid "The submitted file is empty."
4.1102 msgstr "Il file inviato è vuoto."
4.1103
4.1104 -#: forms/fields.py:477
4.1105 +#: forms/fields.py:478
4.1106 msgid ""
4.1107 "Upload a valid image. The file you uploaded was either not an image or a "
4.1108 "corrupted image."
4.1109 msgstr "Carica un'immagine valida. Il file caricato non è un'immagine o è corrotto."
4.1110
4.1111 -#: forms/fields.py:538
4.1112 +#: forms/fields.py:539
4.1113 msgid "Enter a valid URL."
4.1114 msgstr "Inserisci una URL valida."
4.1115
4.1116 -#: forms/fields.py:539
4.1117 +#: forms/fields.py:540
4.1118 msgid "This URL appears to be a broken link."
4.1119 msgstr "Questa URL non sembra funzionare."
4.1120
4.1121 -#: forms/fields.py:618 forms/fields.py:696
4.1122 +#: forms/fields.py:619 forms/fields.py:697
4.1123 #, python-format
4.1124 msgid "Select a valid choice. %(value)s is not one of the available choices."
4.1125 msgstr "Scegli un'opzione valida. '%(value)s non compare tra quelle disponibili."
4.1126
4.1127 -#: forms/fields.py:697 forms/fields.py:758 forms/models.py:720
4.1128 +#: forms/fields.py:698 forms/fields.py:759 forms/models.py:729
4.1129 msgid "Enter a list of values."
4.1130 msgstr "Inserisci una lista di valori."
4.1131
4.1132 -#: forms/fields.py:878
4.1133 +#: forms/fields.py:879
4.1134 msgid "Enter a valid IPv4 address."
4.1135 msgstr "Inserisci un indirizzo IPv4 valido."
4.1136
4.1137 -#: forms/fields.py:888
4.1138 +#: forms/fields.py:889
4.1139 msgid "Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."
4.1140 msgstr ""
4.1141 "Inserisci uno 'slug' valido contenente lettere, cifre, sottolineati o "
4.1142 "trattini."
4.1143
4.1144 -#: forms/formsets.py:242 forms/formsets.py:244
4.1145 +#: forms/formsets.py:247 forms/formsets.py:249
4.1146 msgid "Order"
4.1147 msgstr "Ordine"
4.1148
4.1149 -#: forms/models.py:281 forms/models.py:290
4.1150 +#: forms/models.py:289 forms/models.py:298
4.1151 #, python-format
4.1152 msgid "%(model_name)s with this %(field_label)s already exists."
4.1153 msgstr "%(model_name)s con questo %(field_label)s esiste già."
4.1154
4.1155 -#: forms/models.py:587
4.1156 +#: forms/models.py:602
4.1157 msgid "The inline foreign key did not match the parent instance primary key."
4.1158 -msgstr "La foreign key inline non concorda con la chiave primaria dell'istanza principale."
4.1159 -
4.1160 -#: forms/models.py:650
4.1161 +msgstr ""
4.1162 +"La foreign key inline non concorda con la chiave primaria dell'istanza "
4.1163 +"principale."
4.1164 +
4.1165 +#: forms/models.py:659
4.1166 msgid "Select a valid choice. That choice is not one of the available choices."
4.1167 msgstr ""
4.1168 "Scegli un'opzione valida. La scelta effettuata non compare tra quelle "
4.1169 "disponibili."
4.1170
4.1171 -#: forms/models.py:721
4.1172 +#: forms/models.py:730
4.1173 #, python-format
4.1174 msgid "Select a valid choice. %s is not one of the available choices."
4.1175 msgstr "Scegli un'opzione valida. %s non compare tra quelle disponibili."
4.1176
4.1177 -#: template/defaultfilters.py:741
4.1178 +#: template/defaultfilters.py:751
4.1179 msgid "yes,no,maybe"
4.1180 msgstr "sì,no,forse"
4.1181
4.1182 -#: template/defaultfilters.py:772
4.1183 +#: template/defaultfilters.py:782
4.1184 #, python-format
4.1185 msgid "%(size)d byte"
4.1186 msgid_plural "%(size)d bytes"
4.1187 msgstr[0] "%(size)d byte"
4.1188 msgstr[1] "%(size)d byte"
4.1189
4.1190 -#: template/defaultfilters.py:774
4.1191 +#: template/defaultfilters.py:784
4.1192 #, python-format
4.1193 msgid "%.1f KB"
4.1194 msgstr "%.1f KB"
4.1195
4.1196 -#: template/defaultfilters.py:776
4.1197 +#: template/defaultfilters.py:786
4.1198 #, python-format
4.1199 msgid "%.1f MB"
4.1200 msgstr "%.1f MB"
4.1201
4.1202 -#: template/defaultfilters.py:777
4.1203 +#: template/defaultfilters.py:787
4.1204 #, python-format
4.1205 msgid "%.1f GB"
4.1206 msgstr "%.1f GB"
4.1207 @@ -4174,19 +4307,19 @@
4.1208 msgid ", %(number)d %(type)s"
4.1209 msgstr ", %(number)d %(type)s"
4.1210
4.1211 -#: utils/translation/trans_real.py:403
4.1212 +#: utils/translation/trans_real.py:399
4.1213 msgid "DATE_FORMAT"
4.1214 msgstr "j F Y"
4.1215
4.1216 -#: utils/translation/trans_real.py:405
4.1217 +#: utils/translation/trans_real.py:401
4.1218 msgid "TIME_FORMAT"
4.1219 msgstr "H:i"
4.1220
4.1221 -#: utils/translation/trans_real.py:421
4.1222 +#: utils/translation/trans_real.py:417
4.1223 msgid "YEAR_MONTH_FORMAT"
4.1224 msgstr "Y F"
4.1225
4.1226 -#: utils/translation/trans_real.py:422
4.1227 +#: utils/translation/trans_real.py:418
4.1228 msgid "MONTH_DAY_FORMAT"
4.1229 msgstr "F j"
4.1230
5.1 Binary file django/conf/locale/pl/LC_MESSAGES/django.mo has changed
6.1 --- a/django/conf/locale/pl/LC_MESSAGES/django.po Sat Mar 28 12:05:48 2009 -0500
6.2 +++ b/django/conf/locale/pl/LC_MESSAGES/django.po Wed Apr 01 13:05:19 2009 -0500
6.3 @@ -5,7 +5,7 @@
6.4 msgstr ""
6.5 "Project-Id-Version: Django\n"
6.6 "Report-Msgid-Bugs-To: \n"
6.7 -"POT-Creation-Date: 2009-03-25 09:18+0100\n"
6.8 +"POT-Creation-Date: 2009-03-31 09:52+0200\n"
6.9 "PO-Revision-Date: 2008-02-25 15:53+0100\n"
6.10 "Last-Translator: Jarek Zgoda <jarek.zgoda@gmail.com>\n"
6.11 "MIME-Version: 1.0\n"
6.12 @@ -320,7 +320,7 @@
6.13 msgstr "Zmieniono %s"
6.14
6.15 #: contrib/admin/options.py:531 contrib/admin/options.py:541
6.16 -#: contrib/comments/templates/comments/preview.html:15 forms/models.py:296
6.17 +#: contrib/comments/templates/comments/preview.html:15 forms/models.py:306
6.18 msgid "and"
6.19 msgstr "i"
6.20
6.21 @@ -463,27 +463,27 @@
6.22 msgid "One or more %(fieldname)s in %(name)s:"
6.23 msgstr "Jedno lub więcej %(fieldname)s w %(name)s:"
6.24
6.25 -#: contrib/admin/widgets.py:70
6.26 +#: contrib/admin/widgets.py:71
6.27 msgid "Date:"
6.28 msgstr "Data:"
6.29
6.30 -#: contrib/admin/widgets.py:70
6.31 +#: contrib/admin/widgets.py:71
6.32 msgid "Time:"
6.33 msgstr "Czas:"
6.34
6.35 -#: contrib/admin/widgets.py:94
6.36 +#: contrib/admin/widgets.py:95
6.37 msgid "Currently:"
6.38 msgstr "Teraz:"
6.39
6.40 -#: contrib/admin/widgets.py:94
6.41 +#: contrib/admin/widgets.py:95
6.42 msgid "Change:"
6.43 msgstr "Zmień:"
6.44
6.45 -#: contrib/admin/widgets.py:123
6.46 +#: contrib/admin/widgets.py:124
6.47 msgid "Lookup"
6.48 msgstr "Szukaj"
6.49
6.50 -#: contrib/admin/widgets.py:230
6.51 +#: contrib/admin/widgets.py:236
6.52 msgid "Add Another"
6.53 msgstr "Dodaj kolejny"
6.54
6.55 @@ -565,8 +565,8 @@
6.56 msgstr "Dokumentacja"
6.57
6.58 #: contrib/admin/templates/admin/base.html:28
6.59 -#: contrib/admin/templates/admin/auth/user/change_password.html:13
6.60 -#: contrib/admin/templates/admin/auth/user/change_password.html:46
6.61 +#: contrib/admin/templates/admin/auth/user/change_password.html:14
6.62 +#: contrib/admin/templates/admin/auth/user/change_password.html:47
6.63 #: contrib/admin/templates/registration/password_change_done.html:3
6.64 #: contrib/admin/templates/registration/password_change_form.html:3
6.65 msgid "Change password"
6.66 @@ -604,7 +604,7 @@
6.67
6.68 #: contrib/admin/templates/admin/change_form.html:38
6.69 #: contrib/admin/templates/admin/change_list.html:49
6.70 -#: contrib/admin/templates/admin/auth/user/change_password.html:22
6.71 +#: contrib/admin/templates/admin/auth/user/change_password.html:23
6.72 msgid "Please correct the error below."
6.73 msgid_plural "Please correct the errors below."
6.74 msgstr[0] "Proszę popraw poniższy błąd."
6.75 @@ -621,7 +621,7 @@
6.76 msgstr "Filtr"
6.77
6.78 #: contrib/admin/templates/admin/delete_confirmation.html:10
6.79 -#: contrib/admin/templates/admin/submit_line.html:4 forms/formsets.py:251
6.80 +#: contrib/admin/templates/admin/submit_line.html:4 forms/formsets.py:276
6.81 msgid "Delete"
6.82 msgstr "Usuń"
6.83
6.84 @@ -794,23 +794,23 @@
6.85 msgstr "Nazwa użytkownika"
6.86
6.87 #: contrib/admin/templates/admin/auth/user/add_form.html:20
6.88 -#: contrib/admin/templates/admin/auth/user/change_password.html:33
6.89 +#: contrib/admin/templates/admin/auth/user/change_password.html:34
6.90 #: contrib/auth/forms.py:17 contrib/auth/forms.py:60 contrib/auth/forms.py:185
6.91 msgid "Password"
6.92 msgstr "Hasło"
6.93
6.94 #: contrib/admin/templates/admin/auth/user/add_form.html:26
6.95 -#: contrib/admin/templates/admin/auth/user/change_password.html:39
6.96 +#: contrib/admin/templates/admin/auth/user/change_password.html:40
6.97 #: contrib/auth/forms.py:186
6.98 msgid "Password (again)"
6.99 msgstr "Hasło (powtórz)"
6.100
6.101 #: contrib/admin/templates/admin/auth/user/add_form.html:27
6.102 -#: contrib/admin/templates/admin/auth/user/change_password.html:40
6.103 +#: contrib/admin/templates/admin/auth/user/change_password.html:41
6.104 msgid "Enter the same password as above, for verification."
6.105 msgstr "Podaj powyższe hasło w celu weryfikacji."
6.106
6.107 -#: contrib/admin/templates/admin/auth/user/change_password.html:26
6.108 +#: contrib/admin/templates/admin/auth/user/change_password.html:27
6.109 #, python-format
6.110 msgid "Enter a new password for the user <strong>%(username)s</strong>."
6.111 msgstr "Podaj nowe hasło dla użytkownika <strong>%(username)s</strong>."
6.112 @@ -1231,7 +1231,7 @@
6.113 msgstr "Zmień hasło: %s"
6.114
6.115 #: contrib/auth/forms.py:15 contrib/auth/forms.py:48
6.116 -#: contrib/auth/models.py:127
6.117 +#: contrib/auth/models.py:128
6.118 msgid ""
6.119 "Required. 30 characters or fewer. Alphanumeric characters only (letters, "
6.120 "digits and underscores)."
6.121 @@ -1321,31 +1321,31 @@
6.122 msgid "group"
6.123 msgstr "grupa"
6.124
6.125 -#: contrib/auth/models.py:91 contrib/auth/models.py:137
6.126 +#: contrib/auth/models.py:91 contrib/auth/models.py:138
6.127 msgid "groups"
6.128 msgstr "grupy"
6.129
6.130 -#: contrib/auth/models.py:127
6.131 +#: contrib/auth/models.py:128
6.132 msgid "username"
6.133 msgstr "użytkownik"
6.134
6.135 -#: contrib/auth/models.py:128
6.136 +#: contrib/auth/models.py:129
6.137 msgid "first name"
6.138 msgstr "imię"
6.139
6.140 -#: contrib/auth/models.py:129
6.141 +#: contrib/auth/models.py:130
6.142 msgid "last name"
6.143 msgstr "nazwisko"
6.144
6.145 -#: contrib/auth/models.py:130
6.146 +#: contrib/auth/models.py:131
6.147 msgid "e-mail address"
6.148 msgstr "adres e-mail"
6.149
6.150 -#: contrib/auth/models.py:131
6.151 +#: contrib/auth/models.py:132
6.152 msgid "password"
6.153 msgstr "hasło"
6.154
6.155 -#: contrib/auth/models.py:131
6.156 +#: contrib/auth/models.py:132
6.157 msgid ""
6.158 "Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change "
6.159 "password form</a>."
6.160 @@ -1353,19 +1353,19 @@
6.161 "Użyj '[algo]$[salt]$[hexdigest]' lub <a href=\"password/\">formularza zmiany "
6.162 "hasła</a>."
6.163
6.164 -#: contrib/auth/models.py:132
6.165 +#: contrib/auth/models.py:133
6.166 msgid "staff status"
6.167 msgstr "w zespole"
6.168
6.169 -#: contrib/auth/models.py:132
6.170 +#: contrib/auth/models.py:133
6.171 msgid "Designates whether the user can log into this admin site."
6.172 msgstr "Oznacza czy użytkownik może zalogować się do panelu admina."
6.173
6.174 -#: contrib/auth/models.py:133
6.175 +#: contrib/auth/models.py:134
6.176 msgid "active"
6.177 msgstr "aktywny"
6.178
6.179 -#: contrib/auth/models.py:133
6.180 +#: contrib/auth/models.py:134
6.181 msgid ""
6.182 "Designates whether this user should be treated as active. Unselect this "
6.183 "instead of deleting accounts."
6.184 @@ -1373,11 +1373,11 @@
6.185 "Oznacza czy użytkownika należy uważać za aktywnego. Odznacz to, zamiast "
6.186 "usuwać konta."
6.187
6.188 -#: contrib/auth/models.py:134
6.189 +#: contrib/auth/models.py:135
6.190 msgid "superuser status"
6.191 msgstr "status administratora"
6.192
6.193 -#: contrib/auth/models.py:134
6.194 +#: contrib/auth/models.py:135
6.195 msgid ""
6.196 "Designates that this user has all permissions without explicitly assigning "
6.197 "them."
6.198 @@ -1385,15 +1385,15 @@
6.199 "Oznacza, że ten użytkownik ma wszystkie uprawnienia bez jawnego "
6.200 "przypisywania ich."
6.201
6.202 -#: contrib/auth/models.py:135
6.203 +#: contrib/auth/models.py:136
6.204 msgid "last login"
6.205 msgstr "ostatnio zalogowany"
6.206
6.207 -#: contrib/auth/models.py:136
6.208 +#: contrib/auth/models.py:137
6.209 msgid "date joined"
6.210 msgstr "data przyłączenia"
6.211
6.212 -#: contrib/auth/models.py:138
6.213 +#: contrib/auth/models.py:139
6.214 msgid ""
6.215 "In addition to the permissions manually assigned, this user will also get "
6.216 "all permissions granted to each group he/she is in."
6.217 @@ -1401,19 +1401,19 @@
6.218 "Oprócz uprawnień przypisanych bezpośrednio użytkownikowi otrzyma on "
6.219 "uprawnienia grup, do których należy."
6.220
6.221 -#: contrib/auth/models.py:139
6.222 +#: contrib/auth/models.py:140
6.223 msgid "user permissions"
6.224 msgstr "uprawnienia użytkownika"
6.225
6.226 -#: contrib/auth/models.py:143
6.227 +#: contrib/auth/models.py:144
6.228 msgid "user"
6.229 msgstr "użytkownik"
6.230
6.231 -#: contrib/auth/models.py:144
6.232 +#: contrib/auth/models.py:145
6.233 msgid "users"
6.234 msgstr "użytkownicy"
6.235
6.236 -#: contrib/auth/models.py:300
6.237 +#: contrib/auth/models.py:301
6.238 msgid "message"
6.239 msgstr "wiadomość"
6.240
6.241 @@ -3872,14 +3872,14 @@
6.242 msgid "Enter a valid time in HH:MM[:ss[.uuuuuu]] format."
6.243 msgstr "Proszę wpisać poprawną godzinę w formacie HH:MM[:ss[.uuuuuu]]."
6.244
6.245 -#: db/models/fields/related.py:787
6.246 +#: db/models/fields/related.py:792
6.247 msgid ""
6.248 "Hold down \"Control\", or \"Command\" on a Mac, to select more than one."
6.249 msgstr ""
6.250 "Przytrzymaj wciśnięty klawisz \"Ctrl\" lub \"Command\" na Mac'u aby "
6.251 "zaznaczyć więcej niż jeden wybór."
6.252
6.253 -#: db/models/fields/related.py:865
6.254 +#: db/models/fields/related.py:870
6.255 #, python-format
6.256 msgid "Please enter valid %(self)s IDs. The value %(value)r is invalid."
6.257 msgid_plural ""
6.258 @@ -3949,11 +3949,11 @@
6.259 msgid "Ensure that there are no more than %s digits before the decimal point."
6.260 msgstr "Upewnij się, że jest nie więcej niż %s miejsc przed przecinkiem."
6.261
6.262 -#: forms/fields.py:288 forms/fields.py:850
6.263 +#: forms/fields.py:288 forms/fields.py:855
6.264 msgid "Enter a valid date."
6.265 msgstr "Wpisz poprawną datę."
6.266
6.267 -#: forms/fields.py:322 forms/fields.py:851
6.268 +#: forms/fields.py:322 forms/fields.py:856
6.269 msgid "Enter a valid time."
6.270 msgstr "Wpisz poprawną godzinę."
6.271
6.272 @@ -3973,7 +3973,15 @@
6.273 msgid "The submitted file is empty."
6.274 msgstr "Wysłany plik jest pusty."
6.275
6.276 -#: forms/fields.py:478
6.277 +#: forms/fields.py:450
6.278 +#, python-format
6.279 +msgid ""
6.280 +"Ensure this filename has at most %(max)d characters (it has %(length)d)."
6.281 +msgstr ""
6.282 +"Upewnij się, że nazwa tego pliku ma co najwyżej %(max)d znaków (ma długość %"
6.283 +"(length)d)."
6.284 +
6.285 +#: forms/fields.py:483
6.286 msgid ""
6.287 "Upload a valid image. The file you uploaded was either not an image or a "
6.288 "corrupted image."
6.289 @@ -3981,60 +3989,66 @@
6.290 "Wgraj poprawny plik graficzny. Ten, który został wgrany, nie jest obrazem, "
6.291 "albo jest uszkodzony."
6.292
6.293 -#: forms/fields.py:539
6.294 +#: forms/fields.py:544
6.295 msgid "Enter a valid URL."
6.296 msgstr "Wpisz poprawny URL."
6.297
6.298 -#: forms/fields.py:540
6.299 +#: forms/fields.py:545
6.300 msgid "This URL appears to be a broken link."
6.301 msgstr "Ten odnośnik jest nieprawidłowy."
6.302
6.303 -#: forms/fields.py:619 forms/fields.py:697
6.304 +#: forms/fields.py:624 forms/fields.py:702
6.305 #, python-format
6.306 msgid "Select a valid choice. %(value)s is not one of the available choices."
6.307 msgstr ""
6.308 "Wybierz poprawną wartość. %(value)s nie jest jednym z dostępnych wyborów."
6.309
6.310 -#: forms/fields.py:698 forms/fields.py:759 forms/models.py:729
6.311 +#: forms/fields.py:703 forms/fields.py:764 forms/models.py:764
6.312 msgid "Enter a list of values."
6.313 msgstr "Podaj listę wartości."
6.314
6.315 -#: forms/fields.py:879
6.316 +#: forms/fields.py:884
6.317 msgid "Enter a valid IPv4 address."
6.318 msgstr "Wprowadź poprawny adres IPv4."
6.319
6.320 -#: forms/fields.py:889
6.321 +#: forms/fields.py:894
6.322 msgid ""
6.323 "Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."
6.324 msgstr "To pole może zawierać jedynie litery, cyfry, podkreślenia i myślniki."
6.325
6.326 -#: forms/formsets.py:247 forms/formsets.py:249
6.327 +#: forms/formsets.py:272 forms/formsets.py:274
6.328 msgid "Order"
6.329 msgstr "Porządek"
6.330
6.331 -#: forms/models.py:289 forms/models.py:298
6.332 +#: forms/models.py:299 forms/models.py:308
6.333 #, python-format
6.334 msgid "%(model_name)s with this %(field_label)s already exists."
6.335 msgstr "%(field_label)s już istnieje w %(model_name)s."
6.336
6.337 -#: forms/models.py:602
6.338 +#: forms/models.py:639
6.339 msgid "The inline foreign key did not match the parent instance primary key."
6.340 msgstr "Osadzony klucz obcy nie pasuje do klucza głównego obiektu rodzica."
6.341
6.342 -#: forms/models.py:659
6.343 +#: forms/models.py:694
6.344 msgid "Select a valid choice. That choice is not one of the available choices."
6.345 msgstr "Wybierz poprawną wartość. Podana nie jest jednym z dostępnych wyborów."
6.346
6.347 -#: forms/models.py:730
6.348 +#: forms/models.py:765
6.349 #, python-format
6.350 msgid "Select a valid choice. %s is not one of the available choices."
6.351 msgstr "Wybierz poprawną wartość. %s nie jest jednym z dostępnych wyborów."
6.352
6.353 -#: template/defaultfilters.py:751
6.354 +#: forms/models.py:767
6.355 +#, python-format
6.356 +msgid "\"%s\" is not a valid value for a primary key."
6.357 +msgstr "\"%s\" nie jest poprawną wartością klucza głównego."
6.358 +
6.359 +
6.360 +#: template/defaultfilters.py:757
6.361 msgid "yes,no,maybe"
6.362 msgstr "tak,nie,może"
6.363
6.364 -#: template/defaultfilters.py:782
6.365 +#: template/defaultfilters.py:788
6.366 #, python-format
6.367 msgid "%(size)d byte"
6.368 msgid_plural "%(size)d bytes"
6.369 @@ -4042,17 +4056,17 @@
6.370 msgstr[1] "%(size)d bajty"
6.371 msgstr[2] "%(size)d bajtów"
6.372
6.373 -#: template/defaultfilters.py:784
6.374 +#: template/defaultfilters.py:790
6.375 #, python-format
6.376 msgid "%.1f KB"
6.377 msgstr "%.1f KB"
6.378
6.379 -#: template/defaultfilters.py:786
6.380 +#: template/defaultfilters.py:792
6.381 #, python-format
6.382 msgid "%.1f MB"
6.383 msgstr "%.1f MB"
6.384
6.385 -#: template/defaultfilters.py:787
6.386 +#: template/defaultfilters.py:793
6.387 #, python-format
6.388 msgid "%.1f GB"
6.389 msgstr "%.1f GB"
6.390 @@ -4307,16 +4321,16 @@
6.391 msgstr[1] "minuty"
6.392 msgstr[2] "minut"
6.393
6.394 -#: utils/timesince.py:43
6.395 +#: utils/timesince.py:45
6.396 msgid "minutes"
6.397 msgstr "minuty"
6.398
6.399 -#: utils/timesince.py:48
6.400 +#: utils/timesince.py:50
6.401 #, python-format
6.402 msgid "%(number)d %(type)s"
6.403 msgstr "%(number)d %(type)s"
6.404
6.405 -#: utils/timesince.py:54
6.406 +#: utils/timesince.py:56
6.407 #, python-format
6.408 msgid ", %(number)d %(type)s"
6.409 msgstr ", %(number)d %(type)s"
7.1 --- a/django/contrib/admin/__init__.py Sat Mar 28 12:05:48 2009 -0500
7.2 +++ b/django/contrib/admin/__init__.py Wed Apr 01 13:05:19 2009 -0500
7.3 @@ -15,7 +15,7 @@
7.4 may want.
7.5 """
7.6 # Bail out if autodiscover didn't finish loading from a previous call so
7.7 - # that we avoid running autodiscover again when the URLConf is loaded by
7.8 + # that we avoid running autodiscover again when the URLconf is loaded by
7.9 # the exception handler to resolve the handler500 view. This prevents an
7.10 # admin.py module with errors from re-registering models and raising a
7.11 # spurious AlreadyRegistered exception (see #8245).
8.1 --- a/django/contrib/admin/filterspecs.py Sat Mar 28 12:05:48 2009 -0500
8.2 +++ b/django/contrib/admin/filterspecs.py Wed Apr 01 13:05:19 2009 -0500
8.3 @@ -90,7 +90,7 @@
8.4 yield {'selected': self.lookup_val is None,
8.5 'query_string': cl.get_query_string({}, [self.lookup_kwarg]),
8.6 'display': _('All')}
8.7 - for k, v in self.field.choices:
8.8 + for k, v in self.field.flatchoices:
8.9 yield {'selected': smart_unicode(k) == self.lookup_val,
8.10 'query_string': cl.get_query_string({self.lookup_kwarg: k}),
8.11 'display': v}
9.1 --- a/django/contrib/admin/media/css/changelists.css Sat Mar 28 12:05:48 2009 -0500
9.2 +++ b/django/contrib/admin/media/css/changelists.css Wed Apr 01 13:05:19 2009 -0500
9.3 @@ -228,11 +228,20 @@
9.4 border-right: 1px solid #ddd;
9.5 }
9.6
9.7 +#changelist table input {
9.8 + margin: 0;
9.9 +}
9.10 +
9.11 +#changelist table tbody tr.selected {
9.12 + background-color: #FFFFCC;
9.13 +}
9.14 +
9.15 #changelist .actions {
9.16 - color: #666;
9.17 + color: #999;
9.18 padding: 3px;
9.19 + border-top: 1px solid #fff;
9.20 border-bottom: 1px solid #ddd;
9.21 - background: #e1e1e1 url(../img/admin/nav-bg.gif) top left repeat-x;
9.22 + background: white url(../img/admin/nav-bg-reverse.gif) 0 -10px repeat-x;
9.23 }
9.24
9.25 #changelist .actions:last-child {
9.26 @@ -241,15 +250,20 @@
9.27
9.28 #changelist .actions select {
9.29 border: 1px solid #aaa;
9.30 - margin: 0 0.5em;
9.31 + margin-left: 0.5em;
9.32 padding: 1px 2px;
9.33 }
9.34
9.35 #changelist .actions label {
9.36 font-size: 11px;
9.37 - margin: 0 0.5em;
9.38 + margin-left: 0.5em;
9.39 }
9.40
9.41 #changelist #action-toggle {
9.42 display: none;
9.43 }
9.44 +
9.45 +#changelist .actions .button {
9.46 + font-size: 11px;
9.47 + padding: 1px 2px;
9.48 +}
10.1 --- a/django/contrib/admin/media/js/actions.js Sat Mar 28 12:05:48 2009 -0500
10.2 +++ b/django/contrib/admin/media/js/actions.js Wed Apr 01 13:05:19 2009 -0500
10.3 @@ -1,19 +1,37 @@
10.4 var Actions = {
10.5 init: function() {
10.6 - selectAll = document.getElementById('action-toggle');
10.7 + var selectAll = document.getElementById('action-toggle');
10.8 if (selectAll) {
10.9 selectAll.style.display = 'inline';
10.10 addEvent(selectAll, 'click', function() {
10.11 Actions.checker(selectAll.checked);
10.12 });
10.13 }
10.14 + var changelistTable = document.getElementsBySelector('#changelist table')[0];
10.15 + addEvent(changelistTable, 'click', function(e) {
10.16 + if (!e) { var e = window.event; }
10.17 + var target = e.target ? e.target : e.srcElement;
10.18 + if (target.nodeType == 3) { target = target.parentNode; }
10.19 + if (target.className == 'action-select') {
10.20 + var tr = target.parentNode.parentNode;
10.21 + Actions.toggleRow(tr, target.checked);
10.22 + }
10.23 + });
10.24 + },
10.25 + toggleRow: function(tr, checked) {
10.26 + if (checked && tr.className.indexOf('selected') == -1) {
10.27 + tr.className += ' selected';
10.28 + } else if (!checked) {
10.29 + tr.className = tr.className.replace(' selected', '');
10.30 + }
10.31 },
10.32 checker: function(checked) {
10.33 - actionCheckboxes = document.getElementsBySelector('tr input.action-select');
10.34 + var actionCheckboxes = document.getElementsBySelector('tr input.action-select');
10.35 for(var i = 0; i < actionCheckboxes.length; i++) {
10.36 actionCheckboxes[i].checked = checked;
10.37 + Actions.toggleRow(actionCheckboxes[i].parentNode.parentNode, checked);
10.38 }
10.39 }
10.40 -}
10.41 +};
10.42
10.43 addEvent(window, 'load', Actions.init);
11.1 --- a/django/contrib/admin/options.py Sat Mar 28 12:05:48 2009 -0500
11.2 +++ b/django/contrib/admin/options.py Wed Apr 01 13:05:19 2009 -0500
11.3 @@ -791,7 +791,7 @@
11.4 opts = model._meta
11.5
11.6 try:
11.7 - obj = model._default_manager.get(pk=unquote(object_id))
11.8 + obj = self.queryset(request).get(pk=unquote(object_id))
11.9 except model.DoesNotExist:
11.10 # Don't raise Http404 just yet, because we haven't checked
11.11 # permissions yet. We don't want an unauthenticated user to be able
11.12 @@ -976,7 +976,7 @@
11.13 app_label = opts.app_label
11.14
11.15 try:
11.16 - obj = self.model._default_manager.get(pk=unquote(object_id))
11.17 + obj = self.queryset(request).get(pk=unquote(object_id))
11.18 except self.model.DoesNotExist:
11.19 # Don't raise Http404 just yet, because we haven't checked
11.20 # permissions yet. We don't want an unauthenticated user to be able
12.1 --- a/django/contrib/admin/sites.py Sat Mar 28 12:05:48 2009 -0500
12.2 +++ b/django/contrib/admin/sites.py Wed Apr 01 13:05:19 2009 -0500
12.3 @@ -24,7 +24,7 @@
12.4 class AdminSite(object):
12.5 """
12.6 An AdminSite object encapsulates an instance of the Django admin application, ready
12.7 - to be hooked in to your URLConf. Models are registered with the AdminSite using the
12.8 + to be hooked in to your URLconf. Models are registered with the AdminSite using the
12.9 register() method, and the root() method can then be used as a Django view function
12.10 that presents a full admin interface for the collection of registered models.
12.11 """
12.12 @@ -398,7 +398,8 @@
12.13 'root_path': self.root_path,
12.14 }
12.15 context.update(extra_context or {})
12.16 - return render_to_response(self.app_index_template or 'admin/app_index.html', context,
12.17 + return render_to_response(self.app_index_template or ('admin/%s/app_index.html' % app_label,
12.18 + 'admin/app_index.html'), context,
12.19 context_instance=template.RequestContext(request)
12.20 )
12.21
13.1 --- a/django/contrib/admin/templates/admin/auth/user/change_password.html Sat Mar 28 12:05:48 2009 -0500
13.2 +++ b/django/contrib/admin/templates/admin/auth/user/change_password.html Wed Apr 01 13:05:19 2009 -0500
13.3 @@ -8,6 +8,7 @@
13.4 {% block breadcrumbs %}{% if not is_popup %}
13.5 <div class="breadcrumbs">
13.6 <a href="../../../../">{% trans "Home" %}</a> ›
13.7 + <a href="../../../">{{ opts.app_label|capfirst|escape }}</a> ›
13.8 <a href="../../">{{ opts.verbose_name_plural|capfirst }}</a> ›
13.9 <a href="../">{{ original|truncatewords:"18" }}</a> ›
13.10 {% trans 'Change password' %}
14.1 --- a/django/contrib/admin/templatetags/admin_list.py Sat Mar 28 12:05:48 2009 -0500
14.2 +++ b/django/contrib/admin/templatetags/admin_list.py Wed Apr 01 13:05:19 2009 -0500
14.3 @@ -70,7 +70,7 @@
14.4
14.5 def result_headers(cl):
14.6 lookup_opts = cl.lookup_opts
14.7 -
14.8 +
14.9 for i, field_name in enumerate(cl.list_display):
14.10 attr = None
14.11 try:
14.12 @@ -97,7 +97,7 @@
14.13 raise AttributeError, \
14.14 "'%s' model or '%s' objects have no attribute '%s'" % \
14.15 (lookup_opts.object_name, cl.model_admin.__class__, field_name)
14.16 -
14.17 +
14.18 try:
14.19 header = attr.short_description
14.20 except AttributeError:
14.21 @@ -205,8 +205,8 @@
14.22 result_repr = EMPTY_CHANGELIST_VALUE
14.23 # Fields with choices are special: Use the representation
14.24 # of the choice.
14.25 - elif f.choices:
14.26 - result_repr = dict(f.choices).get(field_val, EMPTY_CHANGELIST_VALUE)
14.27 + elif f.flatchoices:
14.28 + result_repr = dict(f.flatchoices).get(field_val, EMPTY_CHANGELIST_VALUE)
14.29 else:
14.30 result_repr = escape(field_val)
14.31 if force_unicode(result_repr) == '':
14.32 @@ -237,7 +237,7 @@
14.33 result_repr = conditional_escape(result_repr)
14.34 yield mark_safe(u'<td%s>%s</td>' % (row_class, result_repr))
14.35 if form:
14.36 - yield mark_safe(force_unicode(form[cl.model._meta.pk.attname]))
14.37 + yield mark_safe(force_unicode(form[cl.model._meta.pk.name]))
14.38
14.39 def results(cl):
14.40 if cl.formset:
15.1 --- a/django/contrib/admin/validation.py Sat Mar 28 12:05:48 2009 -0500
15.2 +++ b/django/contrib/admin/validation.py Wed Apr 01 13:05:19 2009 -0500
15.3 @@ -76,7 +76,7 @@
15.4 field = opts.get_field_by_name(field_name)[0]
15.5 except models.FieldDoesNotExist:
15.6 raise ImproperlyConfigured("'%s.list_editable[%d]' refers to a "
15.7 - "field, '%s', not defiend on %s."
15.8 + "field, '%s', not defined on %s."
15.9 % (cls.__name__, idx, field_name, model.__name__))
15.10 if field_name not in cls.list_display:
15.11 raise ImproperlyConfigured("'%s.list_editable[%d]' refers to "
16.1 --- a/django/contrib/admin/widgets.py Sat Mar 28 12:05:48 2009 -0500
16.2 +++ b/django/contrib/admin/widgets.py Wed Apr 01 13:05:19 2009 -0500
16.3 @@ -12,6 +12,7 @@
16.4 from django.utils.safestring import mark_safe
16.5 from django.utils.encoding import force_unicode
16.6 from django.conf import settings
16.7 +from django.core.urlresolvers import reverse, NoReverseMatch
16.8
16.9 class FilteredSelectMultiple(forms.SelectMultiple):
16.10 """
16.11 @@ -219,13 +220,18 @@
16.12
16.13 def render(self, name, value, *args, **kwargs):
16.14 rel_to = self.rel.to
16.15 - related_url = '../../../%s/%s/' % (rel_to._meta.app_label, rel_to._meta.object_name.lower())
16.16 + info = (rel_to._meta.app_label, rel_to._meta.object_name.lower())
16.17 + try:
16.18 + related_info = (self.admin_site.name,) + info
16.19 + related_url = reverse('%sadmin_%s_%s_add' % related_info)
16.20 + except NoReverseMatch:
16.21 + related_url = '../../../%s/%s/add/' % info
16.22 self.widget.choices = self.choices
16.23 output = [self.widget.render(name, value, *args, **kwargs)]
16.24 if rel_to in self.admin_site._registry: # If the related object has an admin interface:
16.25 # TODO: "id_" is hard-coded here. This should instead use the correct
16.26 # API to determine the ID dynamically.
16.27 - output.append(u'<a href="%sadd/" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' % \
16.28 + output.append(u'<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' % \
16.29 (related_url, name))
16.30 output.append(u'<img src="%simg/admin/icon_addlink.gif" width="10" height="10" alt="%s"/></a>' % (settings.ADMIN_MEDIA_PREFIX, _('Add Another')))
16.31 return mark_safe(u''.join(output))
17.1 --- a/django/contrib/admindocs/views.py Sat Mar 28 12:05:48 2009 -0500
17.2 +++ b/django/contrib/admindocs/views.py Wed Apr 01 13:05:19 2009 -0500
17.3 @@ -264,7 +264,7 @@
17.4 else:
17.5 site_obj = GenericSite()
17.6 for dir in settings_mod.TEMPLATE_DIRS:
17.7 - template_file = os.path.join(dir, "%s.html" % template)
17.8 + template_file = os.path.join(dir, template)
17.9 templates.append({
17.10 'file': template_file,
17.11 'exists': os.path.exists(template_file),
18.1 --- a/django/contrib/auth/admin.py Sat Mar 28 12:05:48 2009 -0500
18.2 +++ b/django/contrib/auth/admin.py Wed Apr 01 13:05:19 2009 -0500
18.3 @@ -27,7 +27,7 @@
18.4 add_form = UserCreationForm
18.5 change_password_form = AdminPasswordChangeForm
18.6 list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff')
18.7 - list_filter = ('is_staff', 'is_superuser')
18.8 + list_filter = ('is_staff', 'is_superuser', 'is_active')
18.9 search_fields = ('username', 'first_name', 'last_name', 'email')
18.10 ordering = ('username',)
18.11 filter_horizontal = ('user_permissions',)
19.1 --- a/django/contrib/auth/decorators.py Sat Mar 28 12:05:48 2009 -0500
19.2 +++ b/django/contrib/auth/decorators.py Wed Apr 01 13:05:19 2009 -0500
19.3 @@ -56,8 +56,19 @@
19.4 self.test_func = test_func
19.5 self.login_url = login_url
19.6 self.redirect_field_name = redirect_field_name
19.7 - update_wrapper(self, view_func)
19.8
19.9 + # We can't blindly apply update_wrapper because it udpates __dict__ and
19.10 + # if the view function is already a _CheckLogin object then
19.11 + # self.test_func and friends will get stomped. However, we also can't
19.12 + # *not* update the wrapper's dict because then view function attributes
19.13 + # don't get updated into the wrapper. So we need to split the
19.14 + # difference: don't let update_wrapper update __dict__, but then update
19.15 + # the (parts of) __dict__ that we care about ourselves.
19.16 + update_wrapper(self, view_func, updated=())
19.17 + for k in view_func.__dict__:
19.18 + if k not in self.__dict__:
19.19 + self.__dict__[k] = view_func.__dict__[k]
19.20 +
19.21 def __get__(self, obj, cls=None):
19.22 view_func = self.view_func.__get__(obj, cls)
19.23 return _CheckLogin(view_func, self.test_func, self.login_url, self.redirect_field_name)
20.1 --- a/django/contrib/auth/models.py Sat Mar 28 12:05:48 2009 -0500
20.2 +++ b/django/contrib/auth/models.py Wed Apr 01 13:05:19 2009 -0500
20.3 @@ -111,6 +111,7 @@
20.4 u.is_active = True
20.5 u.is_superuser = True
20.6 u.save()
20.7 + return u
20.8
20.9 def make_random_password(self, length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'):
20.10 "Generates a random password with the given length and given allowed_chars"
21.1 --- a/django/contrib/auth/tests/__init__.py Sat Mar 28 12:05:48 2009 -0500
21.2 +++ b/django/contrib/auth/tests/__init__.py Wed Apr 01 13:05:19 2009 -0500
21.3 @@ -1,6 +1,6 @@
21.4 from django.contrib.auth.tests.basic import BASIC_TESTS
21.5 from django.contrib.auth.tests.views \
21.6 - import PasswordResetTest, ChangePasswordTest
21.7 + import PasswordResetTest, ChangePasswordTest, LoginTest, LogoutTest
21.8 from django.contrib.auth.tests.forms import FORM_TESTS
21.9 from django.contrib.auth.tests.remote_user \
21.10 import RemoteUserTest, RemoteUserNoCreateTest, RemoteUserCustomTest
21.11 @@ -14,4 +14,6 @@
21.12 'FORM_TESTS': FORM_TESTS,
21.13 'TOKEN_GENERATOR_TESTS': TOKEN_GENERATOR_TESTS,
21.14 'CHANGEPASSWORD_TESTS': ChangePasswordTest,
21.15 + 'LOGIN_TESTS': LoginTest,
21.16 + 'LOGOUT_TESTS': LogoutTest,
21.17 }
22.1 --- a/django/contrib/auth/tests/basic.py Sat Mar 28 12:05:48 2009 -0500
22.2 +++ b/django/contrib/auth/tests/basic.py Wed Apr 01 13:05:19 2009 -0500
22.3 @@ -24,6 +24,8 @@
22.4 False
22.5 >>> u.is_active
22.6 True
22.7 +>>> u.is_superuser
22.8 +False
22.9
22.10 >>> a = AnonymousUser()
22.11 >>> a.is_authenticated()
22.12 @@ -32,11 +34,22 @@
22.13 False
22.14 >>> a.is_active
22.15 False
22.16 +>>> a.is_superuser
22.17 +False
22.18 >>> a.groups.all()
22.19 []
22.20 >>> a.user_permissions.all()
22.21 []
22.22
22.23 +# superuser tests.
22.24 +>>> super = User.objects.create_superuser('super', 'super@example.com', 'super')
22.25 +>>> super.is_superuser
22.26 +True
22.27 +>>> super.is_active
22.28 +True
22.29 +>>> super.is_staff
22.30 +True
22.31 +
22.32 #
22.33 # Tests for createsuperuser management command.
22.34 # It's nearly impossible to test the interactive mode -- a command test helper
23.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
23.2 +++ b/django/contrib/auth/tests/urls.py Wed Apr 01 13:05:19 2009 -0500
23.3 @@ -0,0 +1,9 @@
23.4 +from django.conf.urls.defaults import patterns
23.5 +from django.contrib.auth.urls import urlpatterns
23.6 +
23.7 +# special urls for auth test cases
23.8 +urlpatterns += patterns('',
23.9 + (r'^logout/custom_query/$', 'django.contrib.auth.views.logout', dict(redirect_field_name='follow')),
23.10 + (r'^logout/next_page/$', 'django.contrib.auth.views.logout', dict(next_page='/somewhere/')),
23.11 +)
23.12 +
24.1 --- a/django/contrib/auth/tests/views.py Sat Mar 28 12:05:48 2009 -0500
24.2 +++ b/django/contrib/auth/tests/views.py Wed Apr 01 13:05:19 2009 -0500
24.3 @@ -1,11 +1,14 @@
24.4 -
24.5 import os
24.6 import re
24.7
24.8 from django.conf import settings
24.9 +from django.contrib.auth import SESSION_KEY
24.10 +from django.contrib.auth.forms import AuthenticationForm
24.11 +from django.contrib.sites.models import Site, RequestSite
24.12 from django.contrib.auth.models import User
24.13 from django.test import TestCase
24.14 from django.core import mail
24.15 +from django.core.urlresolvers import reverse
24.16
24.17 class PasswordResetTest(TestCase):
24.18 fixtures = ['authtestdata.json']
24.19 @@ -47,8 +50,8 @@
24.20
24.21 def test_confirm_invalid(self):
24.22 url, path = self._test_confirm_start()
24.23 - # Lets munge the token in the path, but keep the same length,
24.24 - # in case the URL conf will reject a different length
24.25 + # Let's munge the token in the path, but keep the same length,
24.26 + # in case the URLconf will reject a different length.
24.27 path = path[:-5] + ("0"*4) + path[-1]
24.28
24.29 response = self.client.get(path)
24.30 @@ -162,3 +165,71 @@
24.31 self.fail_login()
24.32 self.login(password='password1')
24.33
24.34 +class LoginTest(TestCase):
24.35 + fixtures = ['authtestdata.json']
24.36 + urls = 'django.contrib.auth.urls'
24.37 +
24.38 + def setUp(self):
24.39 + self.old_TEMPLATE_DIRS = settings.TEMPLATE_DIRS
24.40 + settings.TEMPLATE_DIRS = (os.path.join(os.path.dirname(__file__), 'templates'),)
24.41 +
24.42 + def tearDown(self):
24.43 + settings.TEMPLATE_DIRS = self.old_TEMPLATE_DIRS
24.44 +
24.45 + def test_current_site_in_context_after_login(self):
24.46 + response = self.client.get(reverse('django.contrib.auth.views.login'))
24.47 + self.assertEquals(response.status_code, 200)
24.48 + site = Site.objects.get_current()
24.49 + self.assertEquals(response.context['site'], site)
24.50 + self.assertEquals(response.context['site_name'], site.name)
24.51 + self.assert_(isinstance(response.context['form'], AuthenticationForm),
24.52 + 'Login form is not an AuthenticationForm')
24.53 +
24.54 +class LogoutTest(TestCase):
24.55 + fixtures = ['authtestdata.json']
24.56 + urls = 'django.contrib.auth.tests.urls'
24.57 +
24.58 + def login(self, password='password'):
24.59 + response = self.client.post('/login/', {
24.60 + 'username': 'testclient',
24.61 + 'password': password
24.62 + }
24.63 + )
24.64 + self.assertEquals(response.status_code, 302)
24.65 + self.assert_(response['Location'].endswith(settings.LOGIN_REDIRECT_URL))
24.66 + self.assert_(SESSION_KEY in self.client.session)
24.67 +
24.68 + def confirm_logged_out(self):
24.69 + self.assert_(SESSION_KEY not in self.client.session)
24.70 +
24.71 + def test_logout_default(self):
24.72 + "Logout without next_page option renders the default template"
24.73 + self.login()
24.74 + response = self.client.get('/logout/')
24.75 + self.assertEquals(200, response.status_code)
24.76 + self.assert_('Logged out' in response.content)
24.77 + self.confirm_logged_out()
24.78 +
24.79 + def test_logout_with_next_page_specified(self):
24.80 + "Logout with next_page option given redirects to specified resource"
24.81 + self.login()
24.82 + response = self.client.get('/logout/next_page/')
24.83 + self.assertEqual(response.status_code, 302)
24.84 + self.assert_(response['Location'].endswith('/somewhere/'))
24.85 + self.confirm_logged_out()
24.86 +
24.87 + def test_logout_with_redirect_argument(self):
24.88 + "Logout with query string redirects to specified resource"
24.89 + self.login()
24.90 + response = self.client.get('/logout/?next=/login/')
24.91 + self.assertEqual(response.status_code, 302)
24.92 + self.assert_(response['Location'].endswith('/login/'))
24.93 + self.confirm_logged_out()
24.94 +
24.95 + def test_logout_with_custom_redirect_argument(self):
24.96 + "Logout with custom query string redirects to specified resource"
24.97 + self.login()
24.98 + response = self.client.get('/logout/custom_query/?follow=/somewhere/')
24.99 + self.assertEqual(response.status_code, 302)
24.100 + self.assert_(response['Location'].endswith('/somewhere/'))
24.101 + self.confirm_logged_out()
24.102 \ No newline at end of file
25.1 --- a/django/contrib/auth/views.py Sat Mar 28 12:05:48 2009 -0500
25.2 +++ b/django/contrib/auth/views.py Wed Apr 01 13:05:19 2009 -0500
25.3 @@ -38,16 +38,23 @@
25.4 return render_to_response(template_name, {
25.5 'form': form,
25.6 redirect_field_name: redirect_to,
25.7 + 'site': current_site,
25.8 'site_name': current_site.name,
25.9 }, context_instance=RequestContext(request))
25.10 login = never_cache(login)
25.11
25.12 -def logout(request, next_page=None, template_name='registration/logged_out.html'):
25.13 +def logout(request, next_page=None, template_name='registration/logged_out.html', redirect_field_name=REDIRECT_FIELD_NAME):
25.14 "Logs out the user and displays 'You are logged out' message."
25.15 from django.contrib.auth import logout
25.16 logout(request)
25.17 if next_page is None:
25.18 - return render_to_response(template_name, {'title': _('Logged out')}, context_instance=RequestContext(request))
25.19 + redirect_to = request.REQUEST.get(redirect_field_name, '')
25.20 + if redirect_to:
25.21 + return HttpResponseRedirect(redirect_to)
25.22 + else:
25.23 + return render_to_response(template_name, {
25.24 + 'title': _('Logged out')
25.25 + }, context_instance=RequestContext(request))
25.26 else:
25.27 # Redirect to this page until the session has been cleared.
25.28 return HttpResponseRedirect(next_page or request.path)
26.1 --- a/django/contrib/contenttypes/generic.py Sat Mar 28 12:05:48 2009 -0500
26.2 +++ b/django/contrib/contenttypes/generic.py Wed Apr 01 13:05:19 2009 -0500
26.3 @@ -253,6 +253,8 @@
26.4
26.5 def add(self, *objs):
26.6 for obj in objs:
26.7 + if not isinstance(obj, self.model):
26.8 + raise TypeError, "'%s' instance expected" % self.model._meta.object_name
26.9 setattr(obj, self.content_type_field_name, self.content_type)
26.10 setattr(obj, self.object_id_field_name, self.pk_val)
26.11 obj.save()
27.1 --- a/django/contrib/formtools/test_urls.py Sat Mar 28 12:05:48 2009 -0500
27.2 +++ b/django/contrib/formtools/test_urls.py Wed Apr 01 13:05:19 2009 -0500
27.3 @@ -1,9 +1,7 @@
27.4 +"""
27.5 +This is a URLconf to be loaded by tests.py. Add any URLs needed for tests only.
27.6 """
27.7
27.8 -This is a urlconf to be loaded by tests.py. Add any urls needed
27.9 -for tests only.
27.10 -
27.11 -"""
27.12 from django.conf.urls.defaults import *
27.13 from django.contrib.formtools.tests import *
27.14
28.1 --- a/django/contrib/gis/admin/widgets.py Sat Mar 28 12:05:48 2009 -0500
28.2 +++ b/django/contrib/gis/admin/widgets.py Wed Apr 01 13:05:19 2009 -0500
28.3 @@ -21,7 +21,7 @@
28.4
28.5 # Defaulting the WKT value to a blank string -- this
28.6 # will be tested in the JavaScript and the appropriate
28.7 - # interfaace will be constructed.
28.8 + # interface will be constructed.
28.9 self.params['wkt'] = ''
28.10
28.11 # If a string reaches here (via a validation error on another
28.12 @@ -46,7 +46,7 @@
28.13 # Transforming the geometry to the projection used on the
28.14 # OpenLayers map.
28.15 srid = self.params['srid']
28.16 - if value.srid != srid:
28.17 + if value.srid != srid:
28.18 try:
28.19 ogr = value.ogr
28.20 ogr.transform(srid)
28.21 @@ -55,14 +55,14 @@
28.22 wkt = ''
28.23 else:
28.24 wkt = value.wkt
28.25 -
28.26 +
28.27 # Setting the parameter WKT with that of the transformed
28.28 # geometry.
28.29 self.params['wkt'] = wkt
28.30
28.31 return loader.render_to_string(self.template, self.params,
28.32 context_instance=geo_context)
28.33 -
28.34 +
28.35 def map_options(self):
28.36 "Builds the map options hash for the OpenLayers template."
28.37
28.38 @@ -74,8 +74,8 @@
28.39
28.40 # An array of the parameter name, the name of their OpenLayers
28.41 # counterpart, and the type of variable they are.
28.42 - map_types = [('srid', 'projection', 'srid'),
28.43 - ('display_srid', 'displayProjection', 'srid'),
28.44 + map_types = [('srid', 'projection', 'srid'),
28.45 + ('display_srid', 'displayProjection', 'srid'),
28.46 ('units', 'units', str),
28.47 ('max_resolution', 'maxResolution', float),
28.48 ('max_extent', 'maxExtent', 'bounds'),
29.1 --- a/django/contrib/gis/db/backend/mysql/field.py Sat Mar 28 12:05:48 2009 -0500
29.2 +++ b/django/contrib/gis/db/backend/mysql/field.py Wed Apr 01 13:05:19 2009 -0500
29.3 @@ -13,20 +13,20 @@
29.4 def _geom_index(self, style, db_table):
29.5 """
29.6 Creates a spatial index for the geometry column. If MyISAM tables are
29.7 - used an R-Tree index is created, otherwise a B-Tree index is created.
29.8 + used an R-Tree index is created, otherwise a B-Tree index is created.
29.9 Thus, for best spatial performance, you should use MyISAM tables
29.10 - (which do not support transactions). For more information, see Ch.
29.11 + (which do not support transactions). For more information, see Ch.
29.12 16.6.1 of the MySQL 5.0 documentation.
29.13 """
29.14
29.15 # Getting the index name.
29.16 idx_name = '%s_%s_id' % (db_table, self.column)
29.17 -
29.18 - sql = style.SQL_KEYWORD('CREATE SPATIAL INDEX ') + \
29.19 - style.SQL_TABLE(qn(idx_name)) + \
29.20 - style.SQL_KEYWORD(' ON ') + \
29.21 - style.SQL_TABLE(qn(db_table)) + '(' + \
29.22 - style.SQL_FIELD(qn(self.column)) + ');'
29.23 +
29.24 + sql = (style.SQL_KEYWORD('CREATE SPATIAL INDEX ') +
29.25 + style.SQL_TABLE(qn(idx_name)) +
29.26 + style.SQL_KEYWORD(' ON ') +
29.27 + style.SQL_TABLE(qn(db_table)) + '(' +
29.28 + style.SQL_FIELD(qn(self.column)) + ');')
29.29 return sql
29.30
29.31 def post_create_sql(self, style, db_table):
29.32 @@ -46,8 +46,8 @@
29.33
29.34 def get_placeholder(self, value):
29.35 """
29.36 - The placeholder here has to include MySQL's WKT constructor. Because
29.37 - MySQL does not support spatial transformations, there is no need to
29.38 + The placeholder here has to include MySQL's WKT constructor. Because
29.39 + MySQL does not support spatial transformations, there is no need to
29.40 modify the placeholder based on the contents of the given value.
29.41 """
29.42 return '%s(%%s)' % GEOM_FROM_TEXT
30.1 --- a/django/contrib/gis/db/backend/oracle/field.py Sat Mar 28 12:05:48 2009 -0500
30.2 +++ b/django/contrib/gis/db/backend/oracle/field.py Wed Apr 01 13:05:19 2009 -0500
30.3 @@ -39,18 +39,18 @@
30.4
30.5 # Constructing the SQL that will be used to insert information about
30.6 # the geometry column into the USER_GSDO_GEOM_METADATA table.
30.7 - meta_sql = style.SQL_KEYWORD('INSERT INTO ') + \
30.8 - style.SQL_TABLE('USER_SDO_GEOM_METADATA') + \
30.9 - ' (%s, %s, %s, %s)\n ' % tuple(map(qn, ['TABLE_NAME', 'COLUMN_NAME', 'DIMINFO', 'SRID'])) + \
30.10 - style.SQL_KEYWORD(' VALUES ') + '(\n ' + \
30.11 - style.SQL_TABLE(gqn(db_table)) + ',\n ' + \
30.12 - style.SQL_FIELD(gqn(self.column)) + ',\n ' + \
30.13 - style.SQL_KEYWORD("MDSYS.SDO_DIM_ARRAY") + '(\n ' + \
30.14 - style.SQL_KEYWORD("MDSYS.SDO_DIM_ELEMENT") + \
30.15 - ("('LONG', %s, %s, %s),\n " % (self._extent[0], self._extent[2], self._tolerance)) + \
30.16 - style.SQL_KEYWORD("MDSYS.SDO_DIM_ELEMENT") + \
30.17 - ("('LAT', %s, %s, %s)\n ),\n" % (self._extent[1], self._extent[3], self._tolerance)) + \
30.18 - ' %s\n );' % self.srid
30.19 + meta_sql = (style.SQL_KEYWORD('INSERT INTO ') +
30.20 + style.SQL_TABLE('USER_SDO_GEOM_METADATA') +
30.21 + ' (%s, %s, %s, %s)\n ' % tuple(map(qn, ['TABLE_NAME', 'COLUMN_NAME', 'DIMINFO', 'SRID'])) +
30.22 + style.SQL_KEYWORD(' VALUES ') + '(\n ' +
30.23 + style.SQL_TABLE(gqn(db_table)) + ',\n ' +
30.24 + style.SQL_FIELD(gqn(self.column)) + ',\n ' +
30.25 + style.SQL_KEYWORD("MDSYS.SDO_DIM_ARRAY") + '(\n ' +
30.26 + style.SQL_KEYWORD("MDSYS.SDO_DIM_ELEMENT") +
30.27 + ("('LONG', %s, %s, %s),\n " % (self._extent[0], self._extent[2], self._tolerance)) +
30.28 + style.SQL_KEYWORD("MDSYS.SDO_DIM_ELEMENT") +
30.29 + ("('LAT', %s, %s, %s)\n ),\n" % (self._extent[1], self._extent[3], self._tolerance)) +
30.30 + ' %s\n );' % self.srid)
30.31 return meta_sql
30.32
30.33 def _geom_index(self, style, db_table):
30.34 @@ -59,14 +59,14 @@
30.35 # Getting the index name, Oracle doesn't allow object
30.36 # names > 30 characters.
30.37 idx_name = truncate_name('%s_%s_id' % (db_table, self.column), 30)
30.38 -
30.39 - sql = style.SQL_KEYWORD('CREATE INDEX ') + \
30.40 - style.SQL_TABLE(qn(idx_name)) + \
30.41 - style.SQL_KEYWORD(' ON ') + \
30.42 - style.SQL_TABLE(qn(db_table)) + '(' + \
30.43 - style.SQL_FIELD(qn(self.column)) + ') ' + \
30.44 - style.SQL_KEYWORD('INDEXTYPE IS ') + \
30.45 - style.SQL_TABLE('MDSYS.SPATIAL_INDEX') + ';'
30.46 +
30.47 + sql = (style.SQL_KEYWORD('CREATE INDEX ') +
30.48 + style.SQL_TABLE(qn(idx_name)) +
30.49 + style.SQL_KEYWORD(' ON ') +
30.50 + style.SQL_TABLE(qn(db_table)) + '(' +
30.51 + style.SQL_FIELD(qn(self.column)) + ') ' +
30.52 + style.SQL_KEYWORD('INDEXTYPE IS ') +
30.53 + style.SQL_TABLE('MDSYS.SPATIAL_INDEX') + ';')
30.54 return sql
30.55
30.56 def post_create_sql(self, style, db_table):
30.57 @@ -86,7 +86,7 @@
30.58 def db_type(self):
30.59 "The Oracle geometric data type is MDSYS.SDO_GEOMETRY."
30.60 return 'MDSYS.SDO_GEOMETRY'
30.61 -
30.62 +
30.63 def get_placeholder(self, value):
30.64 """
30.65 Provides a proper substitution value for Geometries that are not in the
31.1 --- a/django/contrib/gis/db/backend/postgis/field.py Sat Mar 28 12:05:48 2009 -0500
31.2 +++ b/django/contrib/gis/db/backend/postgis/field.py Wed Apr 01 13:05:19 2009 -0500
31.3 @@ -19,35 +19,35 @@
31.4 Takes the style object (provides syntax highlighting) and the
31.5 database table as parameters.
31.6 """
31.7 - sql = style.SQL_KEYWORD('SELECT ') + \
31.8 - style.SQL_TABLE('AddGeometryColumn') + '(' + \
31.9 - style.SQL_TABLE(gqn(db_table)) + ', ' + \
31.10 - style.SQL_FIELD(gqn(self.column)) + ', ' + \
31.11 - style.SQL_FIELD(str(self.srid)) + ', ' + \
31.12 - style.SQL_COLTYPE(gqn(self.geom_type)) + ', ' + \
31.13 - style.SQL_KEYWORD(str(self.dim)) + ');'
31.14 + sql = (style.SQL_KEYWORD('SELECT ') +
31.15 + style.SQL_TABLE('AddGeometryColumn') + '(' +
31.16 + style.SQL_TABLE(gqn(db_table)) + ', ' +
31.17 + style.SQL_FIELD(gqn(self.column)) + ', ' +
31.18 + style.SQL_FIELD(str(self.srid)) + ', ' +
31.19 + style.SQL_COLTYPE(gqn(self.geom_type)) + ', ' +
31.20 + style.SQL_KEYWORD(str(self.dim)) + ');')
31.21
31.22 if not self.null:
31.23 # Add a NOT NULL constraint to the field
31.24 - sql += '\n' + \
31.25 - style.SQL_KEYWORD('ALTER TABLE ') + \
31.26 - style.SQL_TABLE(qn(db_table)) + \
31.27 - style.SQL_KEYWORD(' ALTER ') + \
31.28 - style.SQL_FIELD(qn(self.column)) + \
31.29 - style.SQL_KEYWORD(' SET NOT NULL') + ';'
31.30 + sql += ('\n' +
31.31 + style.SQL_KEYWORD('ALTER TABLE ') +
31.32 + style.SQL_TABLE(qn(db_table)) +
31.33 + style.SQL_KEYWORD(' ALTER ') +
31.34 + style.SQL_FIELD(qn(self.column)) +
31.35 + style.SQL_KEYWORD(' SET NOT NULL') + ';')
31.36 return sql
31.37 -
31.38 +
31.39 def _geom_index(self, style, db_table,
31.40 index_type='GIST', index_opts='GIST_GEOMETRY_OPS'):
31.41 "Creates a GiST index for this geometry field."
31.42 - sql = style.SQL_KEYWORD('CREATE INDEX ') + \
31.43 - style.SQL_TABLE(qn('%s_%s_id' % (db_table, self.column))) + \
31.44 - style.SQL_KEYWORD(' ON ') + \
31.45 - style.SQL_TABLE(qn(db_table)) + \
31.46 - style.SQL_KEYWORD(' USING ') + \
31.47 - style.SQL_COLTYPE(index_type) + ' ( ' + \
31.48 - style.SQL_FIELD(qn(self.column)) + ' ' + \
31.49 - style.SQL_KEYWORD(index_opts) + ' );'
31.50 + sql = (style.SQL_KEYWORD('CREATE INDEX ') +
31.51 + style.SQL_TABLE(qn('%s_%s_id' % (db_table, self.column))) +
31.52 + style.SQL_KEYWORD(' ON ') +
31.53 + style.SQL_TABLE(qn(db_table)) +
31.54 + style.SQL_KEYWORD(' USING ') +
31.55 + style.SQL_COLTYPE(index_type) + ' ( ' +
31.56 + style.SQL_FIELD(qn(self.column)) + ' ' +
31.57 + style.SQL_KEYWORD(index_opts) + ' );')
31.58 return sql
31.59
31.60 def post_create_sql(self, style, db_table):
31.61 @@ -69,10 +69,10 @@
31.62
31.63 def _post_delete_sql(self, style, db_table):
31.64 "Drops the geometry column."
31.65 - sql = style.SQL_KEYWORD('SELECT ') + \
31.66 - style.SQL_KEYWORD('DropGeometryColumn') + '(' + \
31.67 - style.SQL_TABLE(gqn(db_table)) + ', ' + \
31.68 - style.SQL_FIELD(gqn(self.column)) + ');'
31.69 + sql = (style.SQL_KEYWORD('SELECT ') +
31.70 + style.SQL_KEYWORD('DropGeometryColumn') + '(' +
31.71 + style.SQL_TABLE(gqn(db_table)) + ', ' +
31.72 + style.SQL_FIELD(gqn(self.column)) + ');')
31.73 return sql
31.74
31.75 def db_type(self):
32.1 --- a/django/contrib/gis/db/backend/spatialite/__init__.py Sat Mar 28 12:05:48 2009 -0500
32.2 +++ b/django/contrib/gis/db/backend/spatialite/__init__.py Wed Apr 01 13:05:19 2009 -0500
32.3 @@ -4,21 +4,30 @@
32.4 from django.conf import settings
32.5 from django.db.backends.signals import connection_created
32.6
32.7 -from django.contrib.gis.db.backend.adaptor import WKTAdaptor
32.8 from django.contrib.gis.db.backend.base import BaseSpatialBackend
32.9 +from django.contrib.gis.db.backend.spatialite.adaptor import SpatiaLiteAdaptor
32.10 from django.contrib.gis.db.backend.spatialite.creation import create_test_spatial_db
32.11 from django.contrib.gis.db.backend.spatialite.field import SpatiaLiteField
32.12 from django.contrib.gis.db.backend.spatialite.models import GeometryColumns, SpatialRefSys
32.13 from django.contrib.gis.db.backend.spatialite.query import *
32.14
32.15 +# Here we are figuring out the path to the SpatiLite library (`libspatialite`).
32.16 +# If it's not in the system PATH, it may be set manually in the settings via
32.17 +# the `SPATIALITE_LIBRARY_PATH` setting.
32.18 spatialite_lib = getattr(settings, 'SPATIALITE_LIBRARY_PATH', find_library('spatialite'))
32.19 if spatialite_lib:
32.20 def initialize_spatialite(sender=None, **kwargs):
32.21 + """
32.22 + This function initializes the pysqlite2 connection to enable the
32.23 + loading of extensions, and to load up the SpatiaLite library
32.24 + extension.
32.25 + """
32.26 from django.db import connection
32.27 connection.connection.enable_load_extension(True)
32.28 - connection.cursor().execute("select load_extension(%s)", (spatialite_lib,))
32.29 + connection.cursor().execute("SELECT load_extension(%s)", (spatialite_lib,))
32.30 connection_created.connect(initialize_spatialite)
32.31 else:
32.32 + # No SpatiaLite library found.
32.33 raise Exception('Unable to locate SpatiaLite, needed to use GeoDjango with sqlite3.')
32.34
32.35 SpatialBackend = BaseSpatialBackend(name='spatialite', spatialite=True,
32.36 @@ -43,7 +52,7 @@
32.37 translate=TRANSLATE,
32.38 union=UNION,
32.39 unionagg=UNIONAGG,
32.40 - Adaptor=WKTAdaptor,
32.41 + Adaptor=SpatiaLiteAdaptor,
32.42 Field=SpatiaLiteField,
32.43 GeometryColumns=GeometryColumns,
32.44 SpatialRefSys=SpatialRefSys,
33.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
33.2 +++ b/django/contrib/gis/db/backend/spatialite/adaptor.py Wed Apr 01 13:05:19 2009 -0500
33.3 @@ -0,0 +1,8 @@
33.4 +from django.db.backends.sqlite3.base import Database
33.5 +from django.contrib.gis.db.backend.adaptor import WKTAdaptor
33.6 +
33.7 +class SpatiaLiteAdaptor(WKTAdaptor):
33.8 + "SQLite adaptor for geometry objects."
33.9 + def __conform__(self, protocol):
33.10 + if protocol is Database.PrepareProtocol:
33.11 + return str(self)
34.1 --- a/django/contrib/gis/db/backend/spatialite/creation.py Sat Mar 28 12:05:48 2009 -0500
34.2 +++ b/django/contrib/gis/db/backend/spatialite/creation.py Wed Apr 01 13:05:19 2009 -0500
34.3 @@ -1,18 +1,12 @@
34.4 import os
34.5 -
34.6 from django.conf import settings
34.7 from django.core.management import call_command
34.8 from django.db import connection
34.9 -from django.db.backends.creation import TEST_DATABASE_PREFIX
34.10 -from django.contrib.gis.db.backend.util import getstatusoutput
34.11 -
34.12 -def spatialite_bin():
34.13 - return getattr(settings, 'SPATIALITE', 'spatialite')
34.14
34.15 def spatialite_init_file():
34.16 - # SPATIALITE_SQL_FILE may be placed in settings to tell
34.17 + # SPATIALITE_SQL may be placed in settings to tell
34.18 # GeoDjango to use a specific user-supplied file.
34.19 - return getattr(settings, 'SPATIALITE_SQL_FILE', 'init_spatialite-2.2.sql')
34.20 + return getattr(settings, 'SPATIALITE_SQL', 'init_spatialite-2.2.sql')
34.21
34.22 def create_test_spatial_db(verbosity=1, autoclobber=False, interactive=False):
34.23 "Creates a spatial database based on the settings."
34.24 @@ -21,40 +15,47 @@
34.25 if settings.DATABASE_ENGINE != 'sqlite3':
34.26 raise Exception('SpatiaLite database creation only supported on sqlite3 platform.')
34.27
34.28 - db_name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
34.29 + # Getting the test database name using the the SQLite backend's
34.30 + # `_create_test_db`. Unless `TEST_DATABASE_NAME` is defined,
34.31 + # it returns ":memory:".
34.32 + db_name = connection.creation._create_test_db(verbosity, autoclobber)
34.33
34.34 - # Now adding in the PostGIS routines.
34.35 - load_spatialite_sql(db_name, verbosity=verbosity)
34.36 -
34.37 - if verbosity >= 1: print 'Creation of spatial database %s successful.' % db_name
34.38 + # Closing out the current connection to the database set in
34.39 + # originally in the settings. This makes it so `initialize_spatialite`
34.40 + # function will be run on the connection for the _test_ database instead.
34.41 + connection.close()
34.42
34.43 # Point to the new database
34.44 - connection.close()
34.45 settings.DATABASE_NAME = db_name
34.46 connection.settings_dict["DATABASE_NAME"] = db_name
34.47 can_rollback = connection.creation._rollback_works()
34.48 settings.DATABASE_SUPPORTS_TRANSACTIONS = can_rollback
34.49 connection.settings_dict["DATABASE_SUPPORTS_TRANSACTIONS"] = can_rollback
34.50
34.51 + # Finally, loading up the SpatiaLite SQL file.
34.52 + load_spatialite_sql(db_name, verbosity=verbosity)
34.53 +
34.54 + if verbosity >= 1:
34.55 + print 'Creation of spatial database %s successful.' % db_name
34.56 +
34.57 # Syncing the database
34.58 call_command('syncdb', verbosity=verbosity, interactive=interactive)
34.59
34.60 def load_spatialite_sql(db_name, verbosity=1):
34.61 """
34.62 - This routine loads up the SpatiaLite SQL file init_spatialite-2.2.sql.
34.63 + This routine loads up the SpatiaLite SQL file.
34.64 """
34.65 - if os.path.isabs(db_name):
34.66 - db_file = db_name
34.67 - else:
34.68 - db_file = os.path.join(os.getcwd(), db_name)
34.69 + # Getting the location of the SpatiaLite SQL file, and confirming
34.70 + # it exists.
34.71 + spatialite_sql = spatialite_init_file()
34.72 + if not os.path.isfile(spatialite_sql):
34.73 + raise Exception('Could not find the SpatiaLite initialization SQL file: %s' % spatialite_sql)
34.74
34.75 - cmd = '%(spatialite)s %(db_file)s < %(sql_file)s'
34.76 - params = {'spatialite' : spatialite_bin(),
34.77 - 'db_file' : db_file,
34.78 - 'sql_file' : spatialite_init_file(),
34.79 - }
34.80 -
34.81 - status, output = getstatusoutput(cmd % params)
34.82 - if status:
34.83 - raise Exception("Error executing 'spatialite' command: %s\n" % output)
34.84 -
34.85 + # Opening up the SpatiaLite SQL initialization file and executing
34.86 + # as a script.
34.87 + sql_fh = open(spatialite_sql, 'r')
34.88 + try:
34.89 + cur = connection.cursor()
34.90 + cur.executescript(sql_fh.read())
34.91 + finally:
34.92 + sql_fh.close()
35.1 --- a/django/contrib/gis/db/backend/spatialite/field.py Sat Mar 28 12:05:48 2009 -0500
35.2 +++ b/django/contrib/gis/db/backend/spatialite/field.py Wed Apr 01 13:05:19 2009 -0500
35.3 @@ -19,33 +19,22 @@
35.4 Takes the style object (provides syntax highlighting) and the
35.5 database table as parameters.
35.6 """
35.7 - sql = style.SQL_KEYWORD('SELECT ') + \
35.8 - style.SQL_TABLE('AddGeometryColumn') + '(' + \
35.9 - style.SQL_TABLE(gqn(db_table)) + ', ' + \
35.10 - style.SQL_FIELD(gqn(self.column)) + ', ' + \
35.11 - style.SQL_FIELD(str(self.srid)) + ', ' + \
35.12 - style.SQL_COLTYPE(gqn(self.geom_type)) + ', ' + \
35.13 - style.SQL_KEYWORD(str(self.dim)) + ');'
35.14 + sql = (style.SQL_KEYWORD('SELECT ') +
35.15 + style.SQL_TABLE('AddGeometryColumn') + '(' +
35.16 + style.SQL_TABLE(gqn(db_table)) + ', ' +
35.17 + style.SQL_FIELD(gqn(self.column)) + ', ' +
35.18 + style.SQL_FIELD(str(self.srid)) + ', ' +
35.19 + style.SQL_COLTYPE(gqn(self.geom_type)) + ', ' +
35.20 + style.SQL_KEYWORD(str(self.dim)) + ');')
35.21
35.22 - # XXX Alas, sqlite3 does not support this kind of ALTER.
35.23 - # XXX Maybe we should create the column in the usual
35.24 - # XXX way and use RecoverGeometryColumn() instead?
35.25 - #if not self.null:
35.26 - # # Add a NOT NULL constraint to the field
35.27 - # sql += '\n' + \
35.28 - # style.SQL_KEYWORD('ALTER TABLE ') + \
35.29 - # style.SQL_TABLE(gqn(db_table)) + \
35.30 - # style.SQL_KEYWORD(' ALTER ') + \
35.31 - # style.SQL_FIELD(gqn(self.column)) + \
35.32 - # style.SQL_KEYWORD(' SET NOT NULL') + ';'
35.33 return sql
35.34 -
35.35 +
35.36 def _geom_index(self, style, db_table):
35.37 "Creates a spatial index for this geometry field."
35.38 - sql = style.SQL_KEYWORD('SELECT ') + \
35.39 - style.SQL_TABLE('CreateSpatialIndex') + '(' + \
35.40 - style.SQL_TABLE(gqn(db_table)) + ', ' + \
35.41 - style.SQL_FIELD(gqn(self.column)) + ');'
35.42 + sql = (style.SQL_KEYWORD('SELECT ') +
35.43 + style.SQL_TABLE('CreateSpatialIndex') + '(' +
35.44 + style.SQL_TABLE(gqn(db_table)) + ', ' +
35.45 + style.SQL_FIELD(gqn(self.column)) + ');')
35.46 return sql
35.47
35.48 def post_create_sql(self, style, db_table):
35.49 @@ -66,10 +55,10 @@
35.50
35.51 def _post_delete_sql(self, style, db_table):
35.52 "Drops the geometry column."
35.53 - sql = style.SQL_KEYWORD('SELECT ') + \
35.54 - style.SQL_KEYWORD('DropGeometryColumn') + '(' + \
35.55 - style.SQL_TABLE(gqn(db_table)) + ', ' + \
35.56 - style.SQL_FIELD(gqn(self.column)) + ');'
35.57 + sql = (style.SQL_KEYWORD('SELECT ') +
35.58 + style.SQL_KEYWORD('DropGeometryColumn') + '(' +
35.59 + style.SQL_TABLE(gqn(db_table)) + ', ' +
35.60 + style.SQL_FIELD(gqn(self.column)) + ');')
35.61 return sql
35.62
35.63 def db_type(self):
35.64 @@ -83,7 +72,7 @@
35.65 """
35.66 Provides a proper substitution value for Geometries that are not in the
35.67 SRID of the field. Specifically, this routine will substitute in the
35.68 - Transform() function call.
35.69 + Transform() and GeomFromText() function call(s).
35.70 """
35.71 if value is None or value.srid == self.srid:
35.72 return '%s(%%s,%s)' % (GEOM_FROM_TEXT, self.srid)
36.1 --- a/django/contrib/gis/db/backend/util.py Sat Mar 28 12:05:48 2009 -0500
36.2 +++ b/django/contrib/gis/db/backend/util.py Wed Apr 01 13:05:19 2009 -0500
36.3 @@ -1,3 +1,8 @@
36.4 +"""
36.5 +A collection of utility routines and classes used by the spatial
36.6 +backends.
36.7 +"""
36.8 +
36.9 def getstatusoutput(cmd):
36.10 """
36.11 Executes a shell command on the platform using subprocess.Popen and
37.1 --- a/django/contrib/gis/db/models/__init__.py Sat Mar 28 12:05:48 2009 -0500
37.2 +++ b/django/contrib/gis/db/models/__init__.py Wed Apr 01 13:05:19 2009 -0500
37.3 @@ -7,14 +7,8 @@
37.4 # The GeoManager
37.5 from django.contrib.gis.db.models.manager import GeoManager
37.6
37.7 -# The GeoQ object
37.8 -from django.contrib.gis.db.models.query import GeoQ
37.9 -
37.10 # The geographic-enabled fields.
37.11 from django.contrib.gis.db.models.fields import \
37.12 GeometryField, PointField, LineStringField, PolygonField, \
37.13 MultiPointField, MultiLineStringField, MultiPolygonField, \
37.14 GeometryCollectionField
37.15 -
37.16 -# The geographic mixin class.
37.17 -from mixin import GeoMixin
38.1 --- a/django/contrib/gis/db/models/fields/__init__.py Sat Mar 28 12:05:48 2009 -0500
38.2 +++ b/django/contrib/gis/db/models/fields/__init__.py Wed Apr 01 13:05:19 2009 -0500
38.3 @@ -13,7 +13,6 @@
38.4 warn('This attribute has been deprecated, pleas use "%s" instead.' % func.__name__[1:])
38.5 return property(func)
38.6
38.7 -#TODO: Flesh out widgets; consider adding support for OGR Geometry proxies.
38.8 class GeometryField(SpatialBackend.Field):
38.9 "The base GIS field -- maps to the OpenGIS Specification Geometry type."
38.10
38.11 @@ -57,17 +56,14 @@
38.12 kwargs['verbose_name'] = verbose_name
38.13
38.14 super(GeometryField, self).__init__(**kwargs) # Calling the parent initializtion function
38.15 -
38.16 - # These properties are for backwards-compatibility.
38.17 +
38.18 + # The following properties are for formerly private variables that are now
38.19 + # public for GeometryField. Because of their use by third-party applications,
38.20 + # a deprecation warning is issued to notify them to use new attribute name.
38.21 def _deprecated_warning(self, old_name, new_name):
38.22 from warnings import warn
38.23 warn('The `%s` attribute name is deprecated, please update your code to use `%s` instead.' %
38.24 (old_name, new_name))
38.25 -
38.26 - @property
38.27 - def _dim(self):
38.28 - self._deprecated_warning('_dim', 'dim')
38.29 - return self.dim
38.30
38.31 @property
38.32 def _geom(self):
38.33 @@ -84,16 +80,6 @@
38.34 self._deprecated_warning('_srid', 'srid')
38.35 return self.srid
38.36
38.37 - @property
38.38 - def _unit(self):
38.39 - self._deprecated_warning('_unit', 'units')
38.40 - return self.units
38.41 -
38.42 - @property
38.43 - def _unit_name(self):
38.44 - self._deprecated_warning('_unit_name', 'units_name')
38.45 - return self.units_name
38.46 -
38.47 ### Routines specific to GeometryField ###
38.48 @property
38.49 def geodetic(self):
39.1 --- a/django/contrib/gis/db/models/mixin.py Sat Mar 28 12:05:48 2009 -0500
39.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
39.3 @@ -1,11 +0,0 @@
39.4 -# Until model subclassing is a possibility, a mixin class is used to add
39.5 -# the necessary functions that may be contributed for geographic objects.
39.6 -class GeoMixin:
39.7 - """
39.8 - The Geographic Mixin class provides routines for geographic objects,
39.9 - however, it is no longer necessary, since all of its previous functions
39.10 - may now be accessed via the GeometryProxy. This mixin is only provided
39.11 - for backwards-compatibility purposes, and will be eventually removed
39.12 - (unless the need arises again).
39.13 - """
39.14 - pass
40.1 --- a/django/contrib/gis/db/models/query.py Sat Mar 28 12:05:48 2009 -0500
40.2 +++ b/django/contrib/gis/db/models/query.py Wed Apr 01 13:05:19 2009 -0500
40.3 @@ -1,6 +1,6 @@
40.4 from django.core.exceptions import ImproperlyConfigured
40.5 from django.db import connection
40.6 -from django.db.models.query import sql, QuerySet, Q
40.7 +from django.db.models.query import QuerySet, Q, ValuesQuerySet, ValuesListQuerySet
40.8
40.9 from django.contrib.gis.db.backend import SpatialBackend
40.10 from django.contrib.gis.db.models import aggregates
40.11 @@ -9,25 +9,28 @@
40.12 from django.contrib.gis.measure import Area, Distance
40.13 from django.contrib.gis.models import get_srid_info
40.14
40.15 -# For backwards-compatibility; Q object should work just fine
40.16 -# after queryset-refactor.
40.17 -class GeoQ(Q): pass
40.18 -
40.19 -class GeomSQL(object):
40.20 - "Simple wrapper object for geometric SQL."
40.21 - def __init__(self, geo_sql):
40.22 - self.sql = geo_sql
40.23 -
40.24 - def as_sql(self, *args, **kwargs):
40.25 - return self.sql
40.26 -
40.27 class GeoQuerySet(QuerySet):
40.28 "The Geographic QuerySet."
40.29
40.30 + ### Methods overloaded from QuerySet ###
40.31 def __init__(self, model=None, query=None):
40.32 super(GeoQuerySet, self).__init__(model=model, query=query)
40.33 self.query = query or GeoQuery(self.model, connection)
40.34
40.35 + def values(self, *fields):
40.36 + return self._clone(klass=GeoValuesQuerySet, setup=True, _fields=fields)
40.37 +
40.38 + def values_list(self, *fields, **kwargs):
40.39 + flat = kwargs.pop('flat', False)
40.40 + if kwargs:
40.41 + raise TypeError('Unexpected keyword arguments to values_list: %s'
40.42 + % (kwargs.keys(),))
40.43 + if flat and len(fields) > 1:
40.44 + raise TypeError("'flat' is not valid when values_list is called with more than one field.")
40.45 + return self._clone(klass=GeoValuesListQuerySet, setup=True, flat=flat,
40.46 + _fields=fields)
40.47 +
40.48 + ### GeoQuerySet Methods ###
40.49 def area(self, tolerance=0.05, **kwargs):
40.50 """
40.51 Returns the area of the geographic field in an `area` attribute on
40.52 @@ -198,8 +201,7 @@
40.53 """
40.54 if SpatialBackend.spatialite:
40.55 if z != 0.0:
40.56 - raise NotImplementedError, \
40.57 - 'SpatiaLite does not support 3D scaling.'
40.58 + raise NotImplementedError('SpatiaLite does not support 3D scaling.')
40.59 s = {'procedure_fmt' : '%(geo_col)s,%(x)s,%(y)s',
40.60 'procedure_args' : {'x' : x, 'y' : y},
40.61 'select_field' : GeomField(),
40.62 @@ -237,8 +239,7 @@
40.63 """
40.64 if SpatialBackend.spatialite:
40.65 if z != 0.0:
40.66 - raise NotImplementedError, \
40.67 - 'SpatiaLite does not support 3D translation.'
40.68 + raise NotImplementedError('SpatiaLite does not support 3D translation.')
40.69 s = {'procedure_fmt' : '%(geo_col)s,%(x)s,%(y)s',
40.70 'procedure_args' : {'x' : x, 'y' : y},
40.71 'select_field' : GeomField(),
40.72 @@ -598,3 +599,14 @@
40.73 return self.query._field_column(geo_field, parent_model._meta.db_table)
40.74 else:
40.75 return self.query._field_column(geo_field)
40.76 +
40.77 +class GeoValuesQuerySet(ValuesQuerySet):
40.78 + def __init__(self, *args, **kwargs):
40.79 + super(GeoValuesQuerySet, self).__init__(*args, **kwargs)
40.80 + # This flag tells `resolve_columns` to run the values through
40.81 + # `convert_values`. This ensures that Geometry objects instead
40.82 + # of string values are returned with `values()` or `values_list()`.
40.83 + self.query.geo_values = True
40.84 +
40.85 +class GeoValuesListQuerySet(GeoValuesQuerySet, ValuesListQuerySet):
40.86 + pass
41.1 --- a/django/contrib/gis/db/models/sql/query.py Sat Mar 28 12:05:48 2009 -0500
41.2 +++ b/django/contrib/gis/db/models/sql/query.py Wed Apr 01 13:05:19 2009 -0500
41.3 @@ -14,6 +14,8 @@
41.4 ALL_TERMS = sql.constants.QUERY_TERMS.copy()
41.5 ALL_TERMS.update(SpatialBackend.gis_terms)
41.6
41.7 +TABLE_NAME = sql.constants.TABLE_NAME
41.8 +
41.9 class GeoQuery(sql.Query):
41.10 """
41.11 A single spatial SQL query.
41.12 @@ -64,10 +66,15 @@
41.13 else:
41.14 col_aliases = set()
41.15 if self.select:
41.16 + only_load = self.deferred_to_columns()
41.17 # This loop customized for GeoQuery.
41.18 for col, field in izip(self.select, self.select_fields):
41.19 if isinstance(col, (list, tuple)):
41.20 - r = self.get_field_select(field, col[0])
41.21 + alias, column = col
41.22 + table = self.alias_map[alias][TABLE_NAME]
41.23 + if table in only_load and col not in only_load[table]:
41.24 + continue
41.25 + r = self.get_field_select(field, alias)
41.26 if with_aliases:
41.27 if col[1] in col_aliases:
41.28 c_alias = 'Col%d' % len(col_aliases)
41.29 @@ -75,7 +82,7 @@
41.30 aliases.add(c_alias)
41.31 col_aliases.add(c_alias)
41.32 else:
41.33 - result.append('%s AS %s' % (r, col[1]))
41.34 + result.append('%s AS %s' % (r, qn2(col[1])))
41.35 aliases.add(r)
41.36 col_aliases.add(col[1])
41.37 else:
41.38 @@ -101,7 +108,7 @@
41.39 alias is not None and ' AS %s' % alias or ''
41.40 )
41.41 for alias, aggregate in self.aggregate_select.items()
41.42 - ])
41.43 + ])
41.44
41.45 # This loop customized for GeoQuery.
41.46 for (table, col), field in izip(self.related_select_cols, self.related_select_fields):
41.47 @@ -123,10 +130,14 @@
41.48 start_alias=None, opts=None, as_pairs=False):
41.49 """
41.50 Computes the default columns for selecting every field in the base
41.51 - model.
41.52 + model. Will sometimes be called to pull in related models (e.g. via
41.53 + select_related), in which case "opts" and "start_alias" will be given
41.54 + to provide a starting point for the traversal.
41.55
41.56 Returns a list of strings, quoted appropriately for use in SQL
41.57 - directly, as well as a set of aliases used in the select statement.
41.58 + directly, as well as a set of aliases used in the select statement (if
41.59 + 'as_pairs' is True, returns a list of (alias, col_name) pairs instead
41.60 + of strings as the first component and None as the second component).
41.61
41.62 This routine is overridden from Query to handle customized selection of
41.63 geometry columns.
41.64 @@ -134,22 +145,34 @@
41.65 result = []
41.66 if opts is None:
41.67 opts = self.model._meta
41.68 + aliases = set()
41.69 + only_load = self.deferred_to_columns()
41.70 + proxied_model = opts.proxy and opts.proxy_for_model or 0
41.71 if start_alias:
41.72 - table_alias = start_alias
41.73 - else:
41.74 - table_alias = self.tables[0]
41.75 - root_pk = opts.pk.column
41.76 - seen = {None: table_alias}
41.77 - aliases = set()
41.78 + seen = {None: start_alias}
41.79 for field, model in opts.get_fields_with_model():
41.80 - try:
41.81 - alias = seen[model]
41.82 - except KeyError:
41.83 - alias = self.join((table_alias, model._meta.db_table,
41.84 - root_pk, model._meta.pk.column))
41.85 - seen[model] = alias
41.86 + if start_alias:
41.87 + try:
41.88 + alias = seen[model]
41.89 + except KeyError:
41.90 + if model is proxied_model:
41.91 + alias = start_alias
41.92 + else:
41.93 + link_field = opts.get_ancestor_link(model)
41.94 + alias = self.join((start_alias, model._meta.db_table,
41.95 + link_field.column, model._meta.pk.column))
41.96 + seen[model] = alias
41.97 + else:
41.98 + # If we're starting from the base model of the queryset, the
41.99 + # aliases will have already been set up in pre_sql_setup(), so
41.100 + # we can save time here.
41.101 + alias = self.included_inherited_models[model]
41.102 + table = self.alias_map[alias][TABLE_NAME]
41.103 + if table in only_load and field.column not in only_load[table]:
41.104 + continue
41.105 if as_pairs:
41.106 result.append((alias, field.column))
41.107 + aliases.add(alias)
41.108 continue
41.109 # This part of the function is customized for GeoQuery. We
41.110 # see if there was any custom selection specified in the
41.111 @@ -166,8 +189,6 @@
41.112 aliases.add(r)
41.113 if with_aliases:
41.114 col_aliases.add(field.column)
41.115 - if as_pairs:
41.116 - return result, None
41.117 return result, aliases
41.118
41.119 def resolve_columns(self, row, fields=()):
41.120 @@ -191,8 +212,8 @@
41.121 # distance objects added by GeoQuerySet methods).
41.122 values = [self.convert_values(v, self.extra_select_fields.get(a, None))
41.123 for v, a in izip(row[rn_offset:index_start], aliases)]
41.124 - if SpatialBackend.oracle:
41.125 - # This is what happens normally in OracleQuery's `resolve_columns`.
41.126 + if SpatialBackend.oracle or getattr(self, 'geo_values', False):
41.127 + # We resolve the columns
41.128 for value, field in izip(row[index_start:], fields):
41.129 values.append(self.convert_values(value, field))
41.130 else:
41.131 @@ -215,7 +236,7 @@
41.132 value = Distance(**{field.distance_att : value})
41.133 elif isinstance(field, AreaField):
41.134 value = Area(**{field.area_att : value})
41.135 - elif isinstance(field, GeomField) and value:
41.136 + elif isinstance(field, (GeomField, GeometryField)) and value:
41.137 value = SpatialBackend.Geometry(value)
41.138 return value
41.139
42.1 --- a/django/contrib/gis/models.py Sat Mar 28 12:05:48 2009 -0500
42.2 +++ b/django/contrib/gis/models.py Wed Apr 01 13:05:19 2009 -0500
42.3 @@ -286,6 +286,7 @@
42.4 srs_wkt = SpatialReference(fetched[0]).wkt
42.5 else:
42.6 srs_wkt = fetched[0]
42.7 + connection.close()
42.8
42.9 # Getting metadata associated with the spatial reference system identifier.
42.10 # Specifically, getting the unit information and spheroid information
43.1 --- a/django/contrib/gis/tests/relatedapp/models.py Sat Mar 28 12:05:48 2009 -0500
43.2 +++ b/django/contrib/gis/tests/relatedapp/models.py Wed Apr 01 13:05:19 2009 -0500
43.3 @@ -2,15 +2,16 @@
43.4 from django.contrib.localflavor.us.models import USStateField
43.5
43.6 class Location(models.Model):
43.7 - name = models.CharField(max_length=50)
43.8 point = models.PointField()
43.9 objects = models.GeoManager()
43.10 + def __unicode__(self): return self.point.wkt
43.11
43.12 class City(models.Model):
43.13 name = models.CharField(max_length=50)
43.14 state = USStateField()
43.15 location = models.ForeignKey(Location)
43.16 objects = models.GeoManager()
43.17 + def __unicode__(self): return self.name
43.18
43.19 class AugmentedLocation(Location):
43.20 extra_text = models.TextField(blank=True)
44.1 --- a/django/contrib/gis/tests/relatedapp/tests.py Sat Mar 28 12:05:48 2009 -0500
44.2 +++ b/django/contrib/gis/tests/relatedapp/tests.py Wed Apr 01 13:05:19 2009 -0500
44.3 @@ -118,7 +118,7 @@
44.4 # Regression test for #9752.
44.5 l = list(DirectoryEntry.objects.all().select_related())
44.6
44.7 - def test6_f_expressions(self):
44.8 + def test06_f_expressions(self):
44.9 "Testing F() expressions on GeometryFields."
44.10 # Constructing a dummy parcel border and getting the City instance for
44.11 # assigning the FK.
44.12 @@ -166,6 +166,31 @@
44.13 self.assertEqual(1, len(qs))
44.14 self.assertEqual('P1', qs[0].name)
44.15
44.16 + def test07_values(self):
44.17 + "Testing values() and values_list() and GeoQuerySets."
44.18 + # GeoQuerySet and GeoValuesQuerySet, and GeoValuesListQuerySet respectively.
44.19 + gqs = Location.objects.all()
44.20 + gvqs = Location.objects.values()
44.21 + gvlqs = Location.objects.values_list()
44.22 +
44.23 + # Incrementing through each of the models, dictionaries, and tuples
44.24 + # returned by the different types of GeoQuerySets.
44.25 + for m, d, t in zip(gqs, gvqs, gvlqs):
44.26 + # The values should be Geometry objects and not raw strings returned
44.27 + # by the spatial database.
44.28 + self.failUnless(isinstance(d['point'], SpatialBackend.Geometry))
44.29 + self.failUnless(isinstance(t[1], SpatialBackend.Geometry))
44.30 + self.assertEqual(m.point, d['point'])
44.31 + self.assertEqual(m.point, t[1])
44.32 +
44.33 + # Test disabled until #10572 is resolved.
44.34 + #def test08_defer_only(self):
44.35 + # "Testing defer() and only() on Geographic models."
44.36 + # qs = Location.objects.all()
44.37 + # def_qs = Location.objects.defer('point')
44.38 + # for loc, def_loc in zip(qs, def_qs):
44.39 + # self.assertEqual(loc.point, def_loc.point)
44.40 +
44.41 # TODO: Related tests for KML, GML, and distance lookups.
44.42
44.43 def suite():
45.1 --- a/django/contrib/gis/tests/test_spatialrefsys.py Sat Mar 28 12:05:48 2009 -0500
45.2 +++ b/django/contrib/gis/tests/test_spatialrefsys.py Wed Apr 01 13:05:19 2009 -0500
45.3 @@ -1,6 +1,6 @@
45.4 import unittest
45.5 -from django.contrib.gis.tests.utils import mysql, spatialite, no_mysql, no_spatialite, oracle, postgis
45.6 -if not mysql and not spatialite:
45.7 +from django.contrib.gis.tests.utils import mysql, no_mysql, oracle, postgis, spatialite
45.8 +if not mysql:
45.9 from django.contrib.gis.models import SpatialRefSys
45.10
45.11 test_srs = ({'srid' : 4326,
45.12 @@ -9,7 +9,7 @@
45.13 'srtext' : 'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]',
45.14 'proj4' : '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs ',
45.15 'spheroid' : 'WGS 84', 'name' : 'WGS 84',
45.16 - 'geographic' : True, 'projected' : False,
45.17 + 'geographic' : True, 'projected' : False, 'spatialite' : True,
45.18 'ellipsoid' : (6378137.0, 6356752.3, 298.257223563), # From proj's "cs2cs -le" and Wikipedia (semi-minor only)
45.19 'eprec' : (1, 1, 9),
45.20 },
45.21 @@ -19,7 +19,7 @@
45.22 'srtext' : 'PROJCS["NAD83 / Texas South Central",GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6269"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4269"]],PROJECTION["Lambert_Conformal_Conic_2SP"],PARAMETER["standard_parallel_1",30.28333333333333],PARAMETER["standard_parallel_2",28.38333333333333],PARAMETER["latitude_of_origin",27.83333333333333],PARAMETER["central_meridian",-99],PARAMETER["false_easting",600000],PARAMETER["false_northing",4000000],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","32140"]]',
45.23 'proj4' : '+proj=lcc +lat_1=30.28333333333333 +lat_2=28.38333333333333 +lat_0=27.83333333333333 +lon_0=-99 +x_0=600000 +y_0=4000000 +ellps=GRS80 +datum=NAD83 +units=m +no_defs ',
45.24 'spheroid' : 'GRS 1980', 'name' : 'NAD83 / Texas South Central',
45.25 - 'geographic' : False, 'projected' : True,
45.26 + 'geographic' : False, 'projected' : True, 'spatialite' : False,
45.27 'ellipsoid' : (6378137.0, 6356752.31414, 298.257222101), # From proj's "cs2cs -le" and Wikipedia (semi-minor only)
45.28 'eprec' : (1, 5, 10),
45.29 },
45.30 @@ -28,7 +28,6 @@
45.31 class SpatialRefSysTest(unittest.TestCase):
45.32
45.33 @no_mysql
45.34 - @no_spatialite
45.35 def test01_retrieve(self):
45.36 "Testing retrieval of SpatialRefSys model objects."
45.37 for sd in test_srs:
45.38 @@ -50,7 +49,6 @@
45.39 self.assertEqual(sd['proj4'], srs.proj4text)
45.40
45.41 @no_mysql
45.42 - @no_spatialite
45.43 def test02_osr(self):
45.44 "Testing getting OSR objects from SpatialRefSys model objects."
45.45 for sd in test_srs:
45.46 @@ -58,16 +56,21 @@
45.47 self.assertEqual(True, sr.spheroid.startswith(sd['spheroid']))
45.48 self.assertEqual(sd['geographic'], sr.geographic)
45.49 self.assertEqual(sd['projected'], sr.projected)
45.50 - self.assertEqual(True, sr.name.startswith(sd['name']))
45.51 +
45.52 + if not (spatialite and not sd['spatialite']):
45.53 + # Can't get 'NAD83 / Texas South Central' from PROJ.4 string
45.54 + # on SpatiaLite
45.55 + self.assertEqual(True, sr.name.startswith(sd['name']))
45.56
45.57 # Testing the SpatialReference object directly.
45.58 - if postgis:
45.59 + if postgis or spatialite:
45.60 srs = sr.srs
45.61 self.assertEqual(sd['proj4'], srs.proj4)
45.62 - self.assertEqual(sd['srtext'], srs.wkt)
45.63 + # No `srtext` field in the `spatial_ref_sys` table in SpatiaLite
45.64 + if not spatialite:
45.65 + self.assertEqual(sd['srtext'], srs.wkt)
45.66
45.67 @no_mysql
45.68 - @no_spatialite
45.69 def test03_ellipsoid(self):
45.70 "Testing the ellipsoid property."
45.71 for sd in test_srs:
46.1 --- a/django/contrib/gis/utils/srs.py Sat Mar 28 12:05:48 2009 -0500
46.2 +++ b/django/contrib/gis/utils/srs.py Wed Apr 01 13:05:19 2009 -0500
46.3 @@ -10,7 +10,7 @@
46.4 >>> add_postgis_srs(SpatialReference(900913))
46.5
46.6 Keyword Arguments:
46.7 - auth_name: This keyword may be customized with the value of the
46.8 + auth_name: This keyword may be customized with the value of the
46.9 `auth_name` field. Defaults to 'EPSG'.
46.10
46.11 auth_srid: This keyword may be customized with the value of the
47.1 --- a/django/contrib/localflavor/it/it_province.py Sat Mar 28 12:05:48 2009 -0500
47.2 +++ b/django/contrib/localflavor/it/it_province.py Wed Apr 01 13:05:19 2009 -0500
47.3 @@ -10,7 +10,7 @@
47.4 ('AT', 'Asti'),
47.5 ('AV', 'Avellino'),
47.6 ('BA', 'Bari'),
47.7 -# ('BT', 'Barletta-Andria-Trani'), # active starting from 2009
47.8 + ('BT', 'Barletta-Andria-Trani'), # active starting from 2009
47.9 ('BL', 'Belluno'),
47.10 ('BN', 'Benevento'),
47.11 ('BG', 'Bergamo'),
47.12 @@ -33,7 +33,7 @@
47.13 ('KR', 'Crotone'),
47.14 ('CN', 'Cuneo'),
47.15 ('EN', 'Enna'),
47.16 -# ('FM', 'Fermo'), # active starting from 2009
47.17 + ('FM', 'Fermo'), # active starting from 2009
47.18 ('FE', 'Ferrara'),
47.19 ('FI', 'Firenze'),
47.20 ('FG', 'Foggia'),
47.21 @@ -60,7 +60,7 @@
47.22 ('ME', 'Messina'),
47.23 ('MI', 'Milano'),
47.24 ('MO', 'Modena'),
47.25 -# ('MB', 'Monza e Brianza'), # active starting from 2009
47.26 + ('MB', 'Monza e Brianza'), # active starting from 2009
47.27 ('NA', 'Napoli'),
47.28 ('NO', 'Novara'),
47.29 ('NU', 'Nuoro'),
48.1 --- a/django/core/cache/backends/locmem.py Sat Mar 28 12:05:48 2009 -0500
48.2 +++ b/django/core/cache/backends/locmem.py Wed Apr 01 13:05:19 2009 -0500
48.3 @@ -58,8 +58,11 @@
48.4 self._lock.reader_leaves()
48.5 self._lock.writer_enters()
48.6 try:
48.7 - del self._cache[key]
48.8 - del self._expire_info[key]
48.9 + try:
48.10 + del self._cache[key]
48.11 + del self._expire_info[key]
48.12 + except KeyError:
48.13 + pass
48.14 return default
48.15 finally:
48.16 self._lock.writer_leaves()
49.1 --- a/django/core/management/base.py Sat Mar 28 12:05:48 2009 -0500
49.2 +++ b/django/core/management/base.py Wed Apr 01 13:05:19 2009 -0500
49.3 @@ -73,7 +73,7 @@
49.4 output and, if the command is intended to produce a block of
49.5 SQL statements, will be wrapped in ``BEGIN`` and ``COMMIT``.
49.6
49.7 - 4. If ``handle()`` raised a ``ComandError``, ``execute()`` will
49.8 + 4. If ``handle()`` raised a ``CommandError``, ``execute()`` will
49.9 instead print an error message to ``stderr``.
49.10
49.11 Thus, the ``handle()`` method is typically the starting point for
50.1 --- a/django/core/urlresolvers.py Sat Mar 28 12:05:48 2009 -0500
50.2 +++ b/django/core/urlresolvers.py Wed Apr 01 13:05:19 2009 -0500
50.3 @@ -24,7 +24,7 @@
50.4 from django.utils.itercompat import reversed # Python 2.3 fallback
50.5 from sets import Set as set
50.6
50.7 -_resolver_cache = {} # Maps urlconf modules to RegexURLResolver instances.
50.8 +_resolver_cache = {} # Maps URLconf modules to RegexURLResolver instances.
50.9 _callable_cache = {} # Maps view and url pattern names to their view functions.
50.10
50.11 # SCRIPT_NAME prefixes for each thread are stored here. If there's no entry for
50.12 @@ -141,7 +141,7 @@
50.13 class RegexURLResolver(object):
50.14 def __init__(self, regex, urlconf_name, default_kwargs=None):
50.15 # regex is a string representing a regular expression.
50.16 - # urlconf_name is a string representing the module containing urlconfs.
50.17 + # urlconf_name is a string representing the module containing URLconfs.
50.18 self.regex = re.compile(regex, re.UNICODE)
50.19 self.urlconf_name = urlconf_name
50.20 if not isinstance(urlconf_name, basestring):
51.1 --- a/django/db/backends/signals.py Sat Mar 28 12:05:48 2009 -0500
51.2 +++ b/django/db/backends/signals.py Wed Apr 01 13:05:19 2009 -0500
51.3 @@ -1,6 +1,3 @@
51.4 from django.dispatch import Signal
51.5
51.6 connection_created = Signal()
51.7 -from django.dispatch import Signal
51.8 -
51.9 -connection_created = Signal()
52.1 --- a/django/db/models/fields/files.py Sat Mar 28 12:05:48 2009 -0500
52.2 +++ b/django/db/models/fields/files.py Wed Apr 01 13:05:19 2009 -0500
52.3 @@ -222,7 +222,7 @@
52.4 setattr(instance, self.name, data)
52.5
52.6 def formfield(self, **kwargs):
52.7 - defaults = {'form_class': forms.FileField}
52.8 + defaults = {'form_class': forms.FileField, 'max_length': self.max_length}
52.9 # If a file has been provided previously, then the form doesn't require
52.10 # that a new file is provided this time.
52.11 # The code to mark the form field as not required is used by
53.1 --- a/django/db/models/fields/related.py Sat Mar 28 12:05:48 2009 -0500
53.2 +++ b/django/db/models/fields/related.py Wed Apr 01 13:05:19 2009 -0500
53.3 @@ -210,8 +210,8 @@
53.4 (value, instance._meta.object_name,
53.5 self.related.get_accessor_name(), self.related.opts.object_name))
53.6
53.7 - # Set the value of the related field
53.8 - setattr(value, self.related.field.rel.get_related_field().attname, instance)
53.9 + # Set the value of the related field to the value of the related object's related field
53.10 + setattr(value, self.related.field.attname, getattr(instance, self.related.field.rel.get_related_field().attname))
53.11
53.12 # Since we already know what the related object is, seed the related
53.13 # object caches now, too. This avoids another db hit if you get the
53.14 @@ -332,6 +332,8 @@
53.15
53.16 def add(self, *objs):
53.17 for obj in objs:
53.18 + if not isinstance(obj, self.model):
53.19 + raise TypeError, "'%s' instance expected" % self.model._meta.object_name
53.20 setattr(obj, rel_field.name, instance)
53.21 obj.save()
53.22 add.alters_data = True
53.23 @@ -452,11 +454,14 @@
53.24
53.25 # If there aren't any objects, there is nothing to do.
53.26 if objs:
53.27 + from django.db.models.base import Model
53.28 # Check that all the objects are of the right type
53.29 new_ids = set()
53.30 for obj in objs:
53.31 if isinstance(obj, self.model):
53.32 new_ids.add(obj._get_pk_val())
53.33 + elif isinstance(obj, Model):
53.34 + raise TypeError, "'%s' instance expected" % self.model._meta.object_name
53.35 else:
53.36 new_ids.add(obj)
53.37 # Add the newly created or already existing objects to the join table.
54.1 --- a/django/db/models/sql/query.py Sat Mar 28 12:05:48 2009 -0500
54.2 +++ b/django/db/models/sql/query.py Wed Apr 01 13:05:19 2009 -0500
54.3 @@ -784,8 +784,6 @@
54.4 aliases.add(r)
54.5 if with_aliases:
54.6 col_aliases.add(field.column)
54.7 - if as_pairs:
54.8 - return result, aliases
54.9 return result, aliases
54.10
54.11 def get_from_clause(self):
55.1 --- a/django/forms/fields.py Sat Mar 28 12:05:48 2009 -0500
55.2 +++ b/django/forms/fields.py Wed Apr 01 13:05:19 2009 -0500
55.3 @@ -447,9 +447,11 @@
55.4 'invalid': _(u"No file was submitted. Check the encoding type on the form."),
55.5 'missing': _(u"No file was submitted."),
55.6 'empty': _(u"The submitted file is empty."),
55.7 + 'max_length': _(u'Ensure this filename has at most %(max)d characters (it has %(length)d).'),
55.8 }
55.9
55.10 def __init__(self, *args, **kwargs):
55.11 + self.max_length = kwargs.pop('max_length', None)
55.12 super(FileField, self).__init__(*args, **kwargs)
55.13
55.14 def clean(self, data, initial=None):
55.15 @@ -466,6 +468,9 @@
55.16 except AttributeError:
55.17 raise ValidationError(self.error_messages['invalid'])
55.18
55.19 + if self.max_length is not None and len(file_name) > self.max_length:
55.20 + error_values = {'max': self.max_length, 'length': len(file_name)}
55.21 + raise ValidationError(self.error_messages['max_length'] % error_values)
55.22 if not file_name:
55.23 raise ValidationError(self.error_messages['invalid'])
55.24 if not file_size:
56.1 --- a/django/forms/forms.py Sat Mar 28 12:05:48 2009 -0500
56.2 +++ b/django/forms/forms.py Wed Apr 01 13:05:19 2009 -0500
56.3 @@ -205,6 +205,15 @@
56.4 """
56.5 return self.errors.get(NON_FIELD_ERRORS, self.error_class())
56.6
56.7 + def _raw_value(self, fieldname):
56.8 + """
56.9 + Returns the raw_value for a particular field name. This is just a
56.10 + convenient wrapper around widget.value_from_datadict.
56.11 + """
56.12 + field = self.fields[fieldname]
56.13 + prefix = self.add_prefix(fieldname)
56.14 + return field.widget.value_from_datadict(self.data, self.files, prefix)
56.15 +
56.16 def full_clean(self):
56.17 """
56.18 Cleans all of self.data and populates self._errors and
57.1 --- a/django/forms/formsets.py Sat Mar 28 12:05:48 2009 -0500
57.2 +++ b/django/forms/formsets.py Wed Apr 01 13:05:19 2009 -0500
57.3 @@ -4,7 +4,7 @@
57.4 from django.utils.translation import ugettext as _
57.5 from fields import IntegerField, BooleanField
57.6 from widgets import Media, HiddenInput
57.7 -from util import ErrorList, ValidationError
57.8 +from util import ErrorList, ErrorDict, ValidationError
57.9
57.10 __all__ = ('BaseFormSet', 'all_valid')
57.11
57.12 @@ -40,39 +40,51 @@
57.13 self.error_class = error_class
57.14 self._errors = None
57.15 self._non_form_errors = None
57.16 - # initialization is different depending on whether we recieved data, initial, or nothing
57.17 - if data or files:
57.18 - self.management_form = ManagementForm(data, auto_id=self.auto_id, prefix=self.prefix)
57.19 - if self.management_form.is_valid():
57.20 - self._total_form_count = self.management_form.cleaned_data[TOTAL_FORM_COUNT]
57.21 - self._initial_form_count = self.management_form.cleaned_data[INITIAL_FORM_COUNT]
57.22 - else:
57.23 - raise ValidationError('ManagementForm data is missing or has been tampered with')
57.24 - else:
57.25 - if initial:
57.26 - self._initial_form_count = len(initial)
57.27 - if self._initial_form_count > self.max_num and self.max_num > 0:
57.28 - self._initial_form_count = self.max_num
57.29 - self._total_form_count = self._initial_form_count + self.extra
57.30 - else:
57.31 - self._initial_form_count = 0
57.32 - self._total_form_count = self.extra
57.33 - if self._total_form_count > self.max_num and self.max_num > 0:
57.34 - self._total_form_count = self.max_num
57.35 - initial = {TOTAL_FORM_COUNT: self._total_form_count,
57.36 - INITIAL_FORM_COUNT: self._initial_form_count}
57.37 - self.management_form = ManagementForm(initial=initial, auto_id=self.auto_id, prefix=self.prefix)
57.38 -
57.39 # construct the forms in the formset
57.40 self._construct_forms()
57.41
57.42 def __unicode__(self):
57.43 return self.as_table()
57.44
57.45 + def _management_form(self):
57.46 + """Returns the ManagementForm instance for this FormSet."""
57.47 + if self.data or self.files:
57.48 + form = ManagementForm(self.data, auto_id=self.auto_id, prefix=self.prefix)
57.49 + if not form.is_valid():
57.50 + raise ValidationError('ManagementForm data is missing or has been tampered with')
57.51 + else:
57.52 + form = ManagementForm(auto_id=self.auto_id, prefix=self.prefix, initial={
57.53 + TOTAL_FORM_COUNT: self.total_form_count(),
57.54 + INITIAL_FORM_COUNT: self.initial_form_count()
57.55 + })
57.56 + return form
57.57 + management_form = property(_management_form)
57.58 +
57.59 + def total_form_count(self):
57.60 + """Returns the total number of forms in this FormSet."""
57.61 + if self.data or self.files:
57.62 + return self.management_form.cleaned_data[TOTAL_FORM_COUNT]
57.63 + else:
57.64 + total_forms = self.initial_form_count() + self.extra
57.65 + if total_forms > self.max_num > 0:
57.66 + total_forms = self.max_num
57.67 + return total_forms
57.68 +
57.69 + def initial_form_count(self):
57.70 + """Returns the number of forms that are required in this FormSet."""
57.71 + if self.data or self.files:
57.72 + return self.management_form.cleaned_data[INITIAL_FORM_COUNT]
57.73 + else:
57.74 + # Use the length of the inital data if it's there, 0 otherwise.
57.75 + initial_forms = self.initial and len(self.initial) or 0
57.76 + if initial_forms > self.max_num > 0:
57.77 + initial_forms = self.max_num
57.78 + return initial_forms
57.79 +
57.80 def _construct_forms(self):
57.81 # instantiate all the forms and put them in self.forms
57.82 self.forms = []
57.83 - for i in xrange(self._total_form_count):
57.84 + for i in xrange(self.total_form_count()):
57.85 self.forms.append(self._construct_form(i))
57.86
57.87 def _construct_form(self, i, **kwargs):
57.88 @@ -89,7 +101,7 @@
57.89 except IndexError:
57.90 pass
57.91 # Allow extra forms to be empty.
57.92 - if i >= self._initial_form_count:
57.93 + if i >= self.initial_form_count():
57.94 defaults['empty_permitted'] = True
57.95 defaults.update(kwargs)
57.96 form = self.form(**defaults)
57.97 @@ -97,13 +109,13 @@
57.98 return form
57.99
57.100 def _get_initial_forms(self):
57.101 - """Return a list of all the intial forms in this formset."""
57.102 - return self.forms[:self._initial_form_count]
57.103 + """Return a list of all the initial forms in this formset."""
57.104 + return self.forms[:self.initial_form_count()]
57.105 initial_forms = property(_get_initial_forms)
57.106
57.107 def _get_extra_forms(self):
57.108 """Return a list of all the extra forms in this formset."""
57.109 - return self.forms[self._initial_form_count:]
57.110 + return self.forms[self.initial_form_count():]
57.111 extra_forms = property(_get_extra_forms)
57.112
57.113 # Maybe this should just go away?
57.114 @@ -127,10 +139,10 @@
57.115 # that have had their deletion widget set to True
57.116 if not hasattr(self, '_deleted_form_indexes'):
57.117 self._deleted_form_indexes = []
57.118 - for i in range(0, self._total_form_count):
57.119 + for i in range(0, self.total_form_count()):
57.120 form = self.forms[i]
57.121 # if this is an extra form and hasn't changed, don't consider it
57.122 - if i >= self._initial_form_count and not form.has_changed():
57.123 + if i >= self.initial_form_count() and not form.has_changed():
57.124 continue
57.125 if form.cleaned_data[DELETION_FIELD_NAME]:
57.126 self._deleted_form_indexes.append(i)
57.127 @@ -140,7 +152,7 @@
57.128 def _get_ordered_forms(self):
57.129 """
57.130 Returns a list of form in the order specified by the incoming data.
57.131 - Raises an AttributeError if deletion is not allowed.
57.132 + Raises an AttributeError if ordering is not allowed.
57.133 """
57.134 if not self.is_valid() or not self.can_order:
57.135 raise AttributeError("'%s' object has no attribute 'ordered_forms'" % self.__class__.__name__)
57.136 @@ -150,10 +162,10 @@
57.137 # by the form data.
57.138 if not hasattr(self, '_ordering'):
57.139 self._ordering = []
57.140 - for i in range(0, self._total_form_count):
57.141 + for i in range(0, self.total_form_count()):
57.142 form = self.forms[i]
57.143 # if this is an extra form and hasn't changed, don't consider it
57.144 - if i >= self._initial_form_count and not form.has_changed():
57.145 + if i >= self.initial_form_count() and not form.has_changed():
57.146 continue
57.147 # don't add data marked for deletion to self.ordered_data
57.148 if self.can_delete and form.cleaned_data[DELETION_FIELD_NAME]:
57.149 @@ -209,8 +221,20 @@
57.150 # We loop over every form.errors here rather than short circuiting on the
57.151 # first failure to make sure validation gets triggered for every form.
57.152 forms_valid = True
57.153 - for errors in self.errors:
57.154 - if bool(errors):
57.155 + for i in range(0, self.total_form_count()):
57.156 + form = self.forms[i]
57.157 + if self.can_delete:
57.158 + # The way we lookup the value of the deletion field here takes
57.159 + # more code than we'd like, but the form's cleaned_data will
57.160 + # not exist if the form is invalid.
57.161 + field = form.fields[DELETION_FIELD_NAME]
57.162 + raw_value = form._raw_value(DELETION_FIELD_NAME)
57.163 + should_delete = field.clean(raw_value)
57.164 + if should_delete:
57.165 + # This form is going to be deleted so any of its errors
57.166 + # should not cause the entire formset to be invalid.
57.167 + continue
57.168 + if bool(self.errors[i]):
57.169 forms_valid = False
57.170 return forms_valid and not bool(self.non_form_errors())
57.171
57.172 @@ -221,7 +245,7 @@
57.173 self._errors = []
57.174 if not self.is_bound: # Stop further processing.
57.175 return
57.176 - for i in range(0, self._total_form_count):
57.177 + for i in range(0, self.total_form_count()):
57.178 form = self.forms[i]
57.179 self._errors.append(form.errors)
57.180 # Give self.clean() a chance to do cross-form validation.
57.181 @@ -243,7 +267,7 @@
57.182 """A hook for adding extra fields on to each form instance."""
57.183 if self.can_order:
57.184 # Only pre-fill the ordering field for initial forms.
57.185 - if index < self._initial_form_count:
57.186 + if index < self.initial_form_count():
57.187 form.fields[ORDERING_FIELD_NAME] = IntegerField(label=_(u'Order'), initial=index+1, required=False)
57.188 else:
57.189 form.fields[ORDERING_FIELD_NAME] = IntegerField(label=_(u'Order'), required=False)
58.1 --- a/django/forms/models.py Sat Mar 28 12:05:48 2009 -0500
58.2 +++ b/django/forms/models.py Wed Apr 01 13:05:19 2009 -0500
58.3 @@ -54,6 +54,10 @@
58.4 # callable upload_to can use the values from other fields.
58.5 if isinstance(f, models.FileField):
58.6 file_field_list.append(f)
58.7 + # OneToOneField doesn't allow assignment of None. Guard against that
58.8 + # instead of allowing it and throwing an error.
58.9 + if isinstance(f, models.OneToOneField) and cleaned_data[f.name] is None:
58.10 + pass
58.11 else:
58.12 f.save_form_data(instance, cleaned_data[f.name])
58.13
58.14 @@ -266,7 +270,13 @@
58.15
58.16 lookup_kwargs = {}
58.17 for field_name in unique_check:
58.18 - lookup_kwargs[field_name] = self.cleaned_data[field_name]
58.19 + lookup_value = self.cleaned_data[field_name]
58.20 + # ModelChoiceField will return an object instance rather than
58.21 + # a raw primary key value, so convert it to a pk value before
58.22 + # using it in a lookup.
58.23 + if isinstance(self.fields[field_name], ModelChoiceField):
58.24 + lookup_value = lookup_value.pk
58.25 + lookup_kwargs[field_name] = lookup_value
58.26
58.27 qs = self.instance.__class__._default_manager.filter(**lookup_kwargs)
58.28
58.29 @@ -357,12 +367,17 @@
58.30 queryset=None, **kwargs):
58.31 self.queryset = queryset
58.32 defaults = {'data': data, 'files': files, 'auto_id': auto_id, 'prefix': prefix}
58.33 - defaults['initial'] = [model_to_dict(obj) for obj in self.get_queryset()]
58.34 defaults.update(kwargs)
58.35 super(BaseModelFormSet, self).__init__(**defaults)
58.36
58.37 + def initial_form_count(self):
58.38 + """Returns the number of forms that are required in this FormSet."""
58.39 + if not (self.data or self.files):
58.40 + return len(self.get_queryset())
58.41 + return super(BaseModelFormSet, self).initial_form_count()
58.42 +
58.43 def _construct_form(self, i, **kwargs):
58.44 - if i < self._initial_form_count:
58.45 + if i < self.initial_form_count():
58.46 kwargs['instance'] = self.get_queryset()[i]
58.47 return super(BaseModelFormSet, self)._construct_form(i, **kwargs)
58.48
58.49 @@ -380,11 +395,11 @@
58.50
58.51 def save_new(self, form, commit=True):
58.52 """Saves and returns a new model instance for the given form."""
58.53 - return save_instance(form, self.model(), exclude=[self._pk_field.name], commit=commit)
58.54 + return form.save(commit=commit)
58.55
58.56 def save_existing(self, form, instance, commit=True):
58.57 """Saves and returns an existing model instance for the given form."""
58.58 - return save_instance(form, instance, exclude=[self._pk_field.name], commit=commit)
58.59 + return form.save(commit=commit)
58.60
58.61 def save(self, commit=True):
58.62 """Saves model instances for every form, adding and changing instances
58.63 @@ -410,16 +425,22 @@
58.64 existing_objects[obj.pk] = obj
58.65 saved_instances = []
58.66 for form in self.initial_forms:
58.67 - obj = existing_objects[form.cleaned_data[self._pk_field.name]]
58.68 - if self.can_delete and form.cleaned_data[DELETION_FIELD_NAME]:
58.69 - self.deleted_objects.append(obj)
58.70 - obj.delete()
58.71 - else:
58.72 - if form.changed_data:
58.73 - self.changed_objects.append((obj, form.changed_data))
58.74 - saved_instances.append(self.save_existing(form, obj, commit=commit))
58.75 - if not commit:
58.76 - self.saved_forms.append(form)
58.77 + pk_name = self._pk_field.name
58.78 + raw_pk_value = form._raw_value(pk_name)
58.79 + pk_value = form.fields[pk_name].clean(raw_pk_value).pk
58.80 + obj = existing_objects[pk_value]
58.81 + if self.can_delete:
58.82 + raw_delete_value = form._raw_value(DELETION_FIELD_NAME)
58.83 + should_delete = form.fields[DELETION_FIELD_NAME].clean(raw_delete_value)
58.84 + if should_delete:
58.85 + self.deleted_objects.append(obj)
58.86 + obj.delete()
58.87 + continue
58.88 + if form.changed_data:
58.89 + self.changed_objects.append((obj, form.changed_data))
58.90 + saved_instances.append(self.save_existing(form, obj, commit=commit))
58.91 + if not commit:
58.92 + self.saved_forms.append(form)
58.93 return saved_instances
58.94
58.95 def save_new_objects(self, commit=True):
58.96 @@ -429,8 +450,11 @@
58.97 continue
58.98 # If someone has marked an add form for deletion, don't save the
58.99 # object.
58.100 - if self.can_delete and form.cleaned_data[DELETION_FIELD_NAME]:
58.101 - continue
58.102 + if self.can_delete:
58.103 + raw_delete_value = form._raw_value(DELETION_FIELD_NAME)
58.104 + should_delete = form.fields[DELETION_FIELD_NAME].clean(raw_delete_value)
58.105 + if should_delete:
58.106 + continue
58.107 self.new_objects.append(self.save_new(form, commit=commit))
58.108 if not commit:
58.109 self.saved_forms.append(form)
58.110 @@ -438,10 +462,23 @@
58.111
58.112 def add_fields(self, form, index):
58.113 """Add a hidden field for the object's primary key."""
58.114 - from django.db.models import AutoField
58.115 + from django.db.models import AutoField, OneToOneField, ForeignKey
58.116 self._pk_field = pk = self.model._meta.pk
58.117 - if pk.auto_created or isinstance(pk, AutoField):
58.118 - form.fields[self._pk_field.name] = IntegerField(required=False, widget=HiddenInput)
58.119 + # If a pk isn't editable, then it won't be on the form, so we need to
58.120 + # add it here so we can tell which object is which when we get the
58.121 + # data back. Generally, pk.editable should be false, but for some
58.122 + # reason, auto_created pk fields and AutoField's editable attribute is
58.123 + # True, so check for that as well.
58.124 + if (not pk.editable) or (pk.auto_created or isinstance(pk, AutoField)):
58.125 + try:
58.126 + pk_value = self.get_queryset()[index].pk
58.127 + except IndexError:
58.128 + pk_value = None
58.129 + if isinstance(pk, OneToOneField) or isinstance(pk, ForeignKey):
58.130 + qs = pk.rel.to._default_manager.get_query_set()
58.131 + else:
58.132 + qs = self.model._default_manager.get_query_set()
58.133 + form.fields[self._pk_field.name] = ModelChoiceField(qs, initial=pk_value, required=False, widget=HiddenInput)
58.134 super(BaseModelFormSet, self).add_fields(form, index)
58.135
58.136 def modelformset_factory(model, form=ModelForm, formfield_callback=lambda f: f.formfield(),
58.137 @@ -477,11 +514,15 @@
58.138 super(BaseInlineFormSet, self).__init__(data, files, prefix=prefix,
58.139 queryset=qs)
58.140
58.141 - def _construct_forms(self):
58.142 + def initial_form_count(self):
58.143 if self.save_as_new:
58.144 - self._total_form_count = self._initial_form_count
58.145 - self._initial_form_count = 0
58.146 - super(BaseInlineFormSet, self)._construct_forms()
58.147 + return 0
58.148 + return super(BaseInlineFormSet, self).initial_form_count()
58.149 +
58.150 + def total_form_count(self):
58.151 + if self.save_as_new:
58.152 + return super(BaseInlineFormSet, self).initial_form_count()
58.153 + return super(BaseInlineFormSet, self).total_form_count()
58.154
58.155 def _construct_form(self, i, **kwargs):
58.156 form = super(BaseInlineFormSet, self)._construct_form(i, **kwargs)
58.157 @@ -498,21 +539,26 @@
58.158 get_default_prefix = classmethod(get_default_prefix)
58.159
58.160 def save_new(self, form, commit=True):
58.161 - fk_attname = self.fk.get_attname()
58.162 - kwargs = {fk_attname: self.instance.pk}
58.163 - new_obj = self.model(**kwargs)
58.164 - if fk_attname == self._pk_field.attname or self._pk_field.auto_created:
58.165 - exclude = [self._pk_field.name]
58.166 - else:
58.167 - exclude = []
58.168 - return save_instance(form, new_obj, exclude=exclude, commit=commit)
58.169 + # Use commit=False so we can assign the parent key afterwards, then
58.170 + # save the object.
58.171 + obj = form.save(commit=False)
58.172 + setattr(obj, self.fk.get_attname(), self.instance.pk)
58.173 + obj.save()
58.174 + # form.save_m2m() can be called via the formset later on if commit=False
58.175 + if commit and hasattr(form, 'save_m2m'):
58.176 + form.save_m2m()
58.177 + return obj
58.178
58.179 def add_fields(self, form, index):
58.180 super(BaseInlineFormSet, self).add_fields(form, index)
58.181 if self._pk_field == self.fk:
58.182 form.fields[self._pk_field.name] = InlineForeignKeyField(self.instance, pk_field=True)
58.183 else:
58.184 - form.fields[self.fk.name] = InlineForeignKeyField(self.instance, label=form.fields[self.fk.name].label)
58.185 + # The foreign key field might not be on the form, so we poke at the
58.186 + # Model field to get the label, since we need that for error messages.
58.187 + form.fields[self.fk.name] = InlineForeignKeyField(self.instance,
58.188 + label=getattr(form.fields.get(self.fk.name), 'label', capfirst(self.fk.verbose_name))
58.189 + )
58.190
58.191 def _get_foreign_key(parent_model, model, fk_name=None):
58.192 """
58.193 @@ -620,8 +666,6 @@
58.194 # ensure the we compare the values as equal types.
58.195 if force_unicode(value) != force_unicode(self.parent_instance.pk):
58.196 raise ValidationError(self.error_messages['invalid_choice'])
58.197 - if self.pk_field:
58.198 - return self.parent_instance.pk
58.199 return self.parent_instance
58.200
58.201 class ModelChoiceIterator(object):
58.202 @@ -729,6 +773,7 @@
58.203 'list': _(u'Enter a list of values.'),
58.204 'invalid_choice': _(u'Select a valid choice. %s is not one of the'
58.205 u' available choices.'),
58.206 + 'invalid_pk_value': _(u'"%s" is not a valid value for a primary key.')
58.207 }
58.208
58.209 def __init__(self, queryset, cache_choices=False, required=True,
58.210 @@ -751,6 +796,8 @@
58.211 obj = self.queryset.get(pk=val)
58.212 except self.queryset.model.DoesNotExist:
58.213 raise ValidationError(self.error_messages['invalid_choice'] % val)
58.214 + except ValueError:
58.215 + raise ValidationError(self.error_messages['invalid_pk_value'] % val)
58.216 else:
58.217 final_values.append(obj)
58.218 return final_values
59.1 --- a/django/forms/widgets.py Sat Mar 28 12:05:48 2009 -0500
59.2 +++ b/django/forms/widgets.py Wed Apr 01 13:05:19 2009 -0500
59.3 @@ -422,7 +422,12 @@
59.4
59.5 def value_from_datadict(self, data, files, name):
59.6 value = data.get(name, None)
59.7 - return {u'2': True, u'3': False, True: True, False: False}.get(value, None)
59.8 + return {u'2': True,
59.9 + True: True,
59.10 + 'True': True,
59.11 + u'3': False,
59.12 + 'False': False,
59.13 + False: False}.get(value, None)
59.14
59.15 def _has_changed(self, initial, data):
59.16 # Sometimes data or initial could be None or u'' which should be the
60.1 --- a/django/http/__init__.py Sat Mar 28 12:05:48 2009 -0500
60.2 +++ b/django/http/__init__.py Wed Apr 01 13:05:19 2009 -0500
60.3 @@ -189,7 +189,7 @@
60.4 for key, value in dict.items(self):
60.5 dict.__setitem__(result, copy.deepcopy(key, memo), copy.deepcopy(value, memo))
60.6 return result
60.7 -
60.8 +
60.9 def setlist(self, key, list_):
60.10 self._assert_mutable()
60.11 key = str_to_unicode(key, self.encoding)
61.1 --- a/django/http/multipartparser.py Sat Mar 28 12:05:48 2009 -0500
61.2 +++ b/django/http/multipartparser.py Wed Apr 01 13:05:19 2009 -0500
61.3 @@ -34,8 +34,6 @@
61.4
61.5 ``MultiValueDict.parse()`` reads the input stream in ``chunk_size`` chunks
61.6 and returns a tuple of ``(MultiValueDict(POST), MultiValueDict(FILES))``. If
61.7 - ``file_upload_dir`` is defined files will be streamed to temporary files in
61.8 - that directory.
61.9 """
61.10 def __init__(self, META, input_data, upload_handlers, encoding=None):
61.11 """
61.12 @@ -44,7 +42,7 @@
61.13 :META:
61.14 The standard ``META`` dictionary in Django request objects.
61.15 :input_data:
61.16 - The raw post data, as a bytestring.
61.17 + The raw post data, as a file-like object.
61.18 :upload_handler:
61.19 An UploadHandler instance that performs operations on the uploaded
61.20 data.
62.1 --- a/django/template/context.py Sat Mar 28 12:05:48 2009 -0500
62.2 +++ b/django/template/context.py Wed Apr 01 13:05:19 2009 -0500
62.3 @@ -1,4 +1,3 @@
62.4 -from django.conf import settings
62.5 from django.core.exceptions import ImproperlyConfigured
62.6 from django.utils.importlib import import_module
62.7
62.8 @@ -71,6 +70,7 @@
62.9 # This is a function rather than module-level procedural code because we only
62.10 # want it to execute if somebody uses RequestContext.
62.11 def get_standard_processors():
62.12 + from django.conf import settings
62.13 global _standard_context_processors
62.14 if _standard_context_processors is None:
62.15 processors = []
63.1 --- a/django/template/defaultfilters.py Sat Mar 28 12:05:48 2009 -0500
63.2 +++ b/django/template/defaultfilters.py Wed Apr 01 13:05:19 2009 -0500
63.3 @@ -149,7 +149,9 @@
63.4 except InvalidOperation:
63.5 if input_val in special_floats:
63.6 return input_val
63.7 - else:
63.8 + try:
63.9 + d = Decimal(force_unicode(float(text)))
63.10 + except (ValueError, InvalidOperation, TypeError, UnicodeEncodeError):
63.11 return u''
63.12 try:
63.13 p = int(arg)
63.14 @@ -515,12 +517,18 @@
63.15
63.16 def length(value):
63.17 """Returns the length of the value - useful for lists."""
63.18 - return len(value)
63.19 + try:
63.20 + return len(value)
63.21 + except (ValueError, TypeError):
63.22 + return ''
63.23 length.is_safe = True
63.24
63.25 def length_is(value, arg):
63.26 """Returns a boolean of whether the value's length is the argument."""
63.27 - return len(value) == int(arg)
63.28 + try:
63.29 + return len(value) == int(arg)
63.30 + except (ValueError, TypeError):
63.31 + return ''
63.32 length_is.is_safe = False
63.33
63.34 def random(value):
64.1 --- a/django/template/loader_tags.py Sat Mar 28 12:05:48 2009 -0500
64.2 +++ b/django/template/loader_tags.py Wed Apr 01 13:05:19 2009 -0500
64.3 @@ -158,7 +158,7 @@
64.4 name of the parent template to extend (if it evaluates to a string) or as
64.5 the parent tempate itelf (if it evaluates to a Template object).
64.6 """
64.7 - bits = token.contents.split()
64.8 + bits = token.split_contents()
64.9 if len(bits) != 2:
64.10 raise TemplateSyntaxError, "'%s' takes one argument" % bits[0]
64.11 parent_name, parent_name_expr = None, None
64.12 @@ -179,7 +179,7 @@
64.13
64.14 {% include "foo/some_include" %}
64.15 """
64.16 - bits = token.contents.split()
64.17 + bits = token.split_contents()
64.18 if len(bits) != 2:
64.19 raise TemplateSyntaxError, "%r tag takes one argument: the name of the template to be included" % bits[0]
64.20 path = bits[1]
65.1 --- a/django/test/client.py Sat Mar 28 12:05:48 2009 -0500
65.2 +++ b/django/test/client.py Wed Apr 01 13:05:19 2009 -0500
65.3 @@ -431,12 +431,14 @@
65.4
65.5 def logout(self):
65.6 """
65.7 - Removes the authenticated user's cookies.
65.8 + Removes the authenticated user's cookies and session object.
65.9
65.10 Causes the authenticated user to be logged out.
65.11 """
65.12 session = import_module(settings.SESSION_ENGINE).SessionStore()
65.13 - session.delete(session_key=self.cookies[settings.SESSION_COOKIE_NAME].value)
65.14 + session_cookie = self.cookies.get(settings.SESSION_COOKIE_NAME)
65.15 + if session_cookie:
65.16 + session.delete(session_key=session_cookie.value)
65.17 self.cookies = SimpleCookie()
65.18
65.19 def _handle_redirects(self, response):
66.1 --- a/django/test/testcases.py Sat Mar 28 12:05:48 2009 -0500
66.2 +++ b/django/test/testcases.py Wed Apr 01 13:05:19 2009 -0500
66.3 @@ -34,7 +34,7 @@
66.4 real_savepoint_commit = transaction.savepoint_commit
66.5 real_savepoint_rollback = transaction.savepoint_rollback
66.6
66.7 -def nop(x=None):
66.8 +def nop(*args, **kwargs):
66.9 return
66.10
66.11 def disable_transaction_methods():
67.1 --- a/django/utils/cache.py Sat Mar 28 12:05:48 2009 -0500
67.2 +++ b/django/utils/cache.py Wed Apr 01 13:05:19 2009 -0500
67.3 @@ -146,6 +146,11 @@
67.4 return 'views.decorators.cache.cache_page.%s.%s.%s' % (
67.5 key_prefix, iri_to_uri(request.path), ctx.hexdigest())
67.6
67.7 +def _generate_cache_header_key(key_prefix, request):
67.8 + """Returns a cache key for the header cache."""
67.9 + return 'views.decorators.cache.cache_header.%s.%s' % (
67.10 + key_prefix, iri_to_uri(request.path))
67.11 +
67.12 def get_cache_key(request, key_prefix=None):
67.13 """
67.14 Returns a cache key based on the request path. It can be used in the
67.15 @@ -158,8 +163,7 @@
67.16 """
67.17 if key_prefix is None:
67.18 key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
67.19 - cache_key = 'views.decorators.cache.cache_header.%s.%s' % (
67.20 - key_prefix, iri_to_uri(request.path))
67.21 + cache_key = _generate_cache_header_key(key_prefix, request)
67.22 headerlist = cache.get(cache_key, None)
67.23 if headerlist is not None:
67.24 return _generate_cache_key(request, headerlist, key_prefix)
67.25 @@ -183,8 +187,7 @@
67.26 key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
67.27 if cache_timeout is None:
67.28 cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS
67.29 - cache_key = 'views.decorators.cache.cache_header.%s.%s' % (
67.30 - key_prefix, iri_to_uri(request.path))
67.31 + cache_key = _generate_cache_header_key(key_prefix, request)
67.32 if response.has_header('Vary'):
67.33 headerlist = ['HTTP_'+header.upper().replace('-', '_')
67.34 for header in cc_delim_re.split(response['Vary'])]
68.1 --- a/django/utils/datastructures.py Sat Mar 28 12:05:48 2009 -0500
68.2 +++ b/django/utils/datastructures.py Wed Apr 01 13:05:19 2009 -0500
68.3 @@ -222,7 +222,18 @@
68.4 dict.__setitem__(result, copy.deepcopy(key, memo),
68.5 copy.deepcopy(value, memo))
68.6 return result
68.7 -
68.8 +
68.9 + def __getstate__(self):
68.10 + obj_dict = self.__dict__.copy()
68.11 + obj_dict['_data'] = dict([(k, self.getlist(k)) for k in self])
68.12 + return obj_dict
68.13 +
68.14 + def __setstate__(self, obj_dict):
68.15 + data = obj_dict.pop('_data', {})
68.16 + for k, v in data.items():
68.17 + self.setlist(k, v)
68.18 + self.__dict__.update(obj_dict)
68.19 +
68.20 def get(self, key, default=None):
68.21 """
68.22 Returns the last data value for the passed key. If key doesn't exist
68.23 @@ -283,10 +294,19 @@
68.24 """Returns a list of (key, list) pairs."""
68.25 return super(MultiValueDict, self).items()
68.26
68.27 + def iterlists(self):
68.28 + """Yields (key, list) pairs."""
68.29 + return super(MultiValueDict, self).iteritems()
68.30 +
68.31 def values(self):
68.32 """Returns a list of the last value on every key list."""
68.33 return [self[key] for key in self.keys()]
68.34 -
68.35 +
68.36 + def itervalues(self):
68.37 + """Yield the last value on every key list."""
68.38 + for key in self.iterkeys():
68.39 + yield self[key]
68.40 +
68.41 def copy(self):
68.42 """Returns a copy of this object."""
68.43 return self.__deepcopy__()
69.1 --- a/django/utils/html.py Sat Mar 28 12:05:48 2009 -0500
69.2 +++ b/django/utils/html.py Wed Apr 01 13:05:19 2009 -0500
69.3 @@ -46,9 +46,9 @@
69.4 value = re.sub(r'\r\n|\r|\n', '\n', force_unicode(value)) # normalize newlines
69.5 paras = re.split('\n{2,}', value)
69.6 if autoescape:
69.7 - paras = [u'<p>%s</p>' % escape(p.strip()).replace('\n', '<br />') for p in paras]
69.8 + paras = [u'<p>%s</p>' % escape(p).replace('\n', '<br />') for p in paras]
69.9 else:
69.10 - paras = [u'<p>%s</p>' % p.strip().replace('\n', '<br />') for p in paras]
69.11 + paras = [u'<p>%s</p>' % p.replace('\n', '<br />') for p in paras]
69.12 return u'\n\n'.join(paras)
69.13 linebreaks = allow_lazy(linebreaks, unicode)
69.14
70.1 --- a/django/utils/text.py Sat Mar 28 12:05:48 2009 -0500
70.2 +++ b/django/utils/text.py Wed Apr 01 13:05:19 2009 -0500
70.3 @@ -219,27 +219,27 @@
70.4 smart_split = allow_lazy(smart_split, unicode)
70.5
70.6 def _replace_entity(match):
70.7 - text = match.group(1)
70.8 - if text[0] == u'#':
70.9 - text = text[1:]
70.10 - try:
70.11 - if text[0] in u'xX':
70.12 - c = int(text[1:], 16)
70.13 - else:
70.14 - c = int(text)
70.15 - return unichr(c)
70.16 - except ValueError:
70.17 - return match.group(0)
70.18 - else:
70.19 - try:
70.20 - return unichr(name2codepoint[text])
70.21 - except (ValueError, KeyError):
70.22 - return match.group(0)
70.23 + text = match.group(1)
70.24 + if text[0] == u'#':
70.25 + text = text[1:]
70.26 + try:
70.27 + if text[0] in u'xX':
70.28 + c = int(text[1:], 16)
70.29 + else:
70.30 + c = int(text)
70.31 + return unichr(c)
70.32 + except ValueError:
70.33 + return match.group(0)
70.34 + else:
70.35 + try:
70.36 + return unichr(name2codepoint[text])
70.37 + except (ValueError, KeyError):
70.38 + return match.group(0)
70.39
70.40 _entity_re = re.compile(r"&(#?[xX]?(?:[0-9a-fA-F]+|\w{1,8}));")
70.41
70.42 def unescape_entities(text):
70.43 - return _entity_re.sub(_replace_entity, text)
70.44 + return _entity_re.sub(_replace_entity, text)
70.45 unescape_entities = allow_lazy(unescape_entities, unicode)
70.46
70.47 def unescape_string_literal(s):
70.48 @@ -261,4 +261,3 @@
70.49 quote = s[0]
70.50 return s[1:-1].replace(r'\%s' % quote, quote).replace(r'\\', '\\')
70.51 unescape_string_literal = allow_lazy(unescape_string_literal)
70.52 -
71.1 --- a/django/utils/timesince.py Sat Mar 28 12:05:48 2009 -0500
71.2 +++ b/django/utils/timesince.py Wed Apr 01 13:05:19 2009 -0500
71.3 @@ -25,9 +25,11 @@
71.4 (60 * 60, lambda n: ungettext('hour', 'hours', n)),
71.5 (60, lambda n: ungettext('minute', 'minutes', n))
71.6 )
71.7 - # Convert datetime.date to datetime.datetime for comparison
71.8 - if d.__class__ is not datetime.datetime:
71.9 + # Convert datetime.date to datetime.datetime for comparison.
71.10 + if not isinstance(d, datetime.datetime):
71.11 d = datetime.datetime(d.year, d.month, d.day)
71.12 + if now and not isinstance(now, datetime.datetime):
71.13 + now = datetime.datetime(now.year, now.month, now.day)
71.14
71.15 if not now:
71.16 if d.tzinfo:
72.1 --- a/django/views/debug.py Sat Mar 28 12:05:48 2009 -0500
72.2 +++ b/django/views/debug.py Wed Apr 01 13:05:19 2009 -0500
72.3 @@ -611,6 +611,28 @@
72.4 {% else %}
72.5 <p>No POST data</p>
72.6 {% endif %}
72.7 + <h3 id="files-info">FILES</h3>
72.8 + {% if request.FILES %}
72.9 + <table class="req">
72.10 + <thead>
72.11 + <tr>
72.12 + <th>Variable</th>
72.13 + <th>Value</th>
72.14 + </tr>
72.15 + </thead>
72.16 + <tbody>
72.17 + {% for var in request.FILES.items %}
72.18 + <tr>
72.19 + <td>{{ var.0 }}</td>
72.20 + <td class="code"><div>{{ var.1|pprint }}</div></td>
72.21 + </tr>
72.22 + {% endfor %}
72.23 + </tbody>
72.24 + </table>
72.25 + {% else %}
72.26 + <p>No FILES data</p>
72.27 + {% endif %}
72.28 +
72.29
72.30 <h3 id="cookie-info">COOKIES</h3>
72.31 {% if request.COOKIES %}
73.1 --- a/docs/conf.py Sat Mar 28 12:05:48 2009 -0500
73.2 +++ b/docs/conf.py Wed Apr 01 13:05:19 2009 -0500
73.3 @@ -70,6 +70,11 @@
73.4 # The name of the Pygments (syntax highlighting) style to use.
73.5 pygments_style = 'trac'
73.6
73.7 +# Sphinx will recurse into subversion configuration folders and try to read
73.8 +# any document file within. These should be ignored.
73.9 +# Note: exclude_dirnames is new in Sphinx 0.5
73.10 +exclude_dirnames = ['.svn']
73.11 +
73.12 # Options for HTML output
73.13 # -----------------------
73.14
74.1 --- a/docs/faq/usage.txt Sat Mar 28 12:05:48 2009 -0500
74.2 +++ b/docs/faq/usage.txt Wed Apr 01 13:05:19 2009 -0500
74.3 @@ -8,8 +8,8 @@
74.4
74.5 Make sure that:
74.6
74.7 - * The environment variable DJANGO_SETTINGS_MODULE is set to a fully-qualified
74.8 - Python module (i.e. "mysite.settings").
74.9 + * The environment variable DJANGO_SETTINGS_MODULE is set to a
74.10 + fully-qualified Python module (i.e. "mysite.settings").
74.11
74.12 * Said module is on ``sys.path`` (``import mysite.settings`` should work).
74.13
74.14 @@ -45,24 +45,35 @@
74.15 How do I use image and file fields?
74.16 -----------------------------------
74.17
74.18 -Using a :class:`~django.db.models.FileField` or an
74.19 +Using a :class:`~django.db.models.FileField` or an
74.20 :class:`~django.db.models.ImageField` in a model takes a few steps:
74.21
74.22 - #. In your settings file, you'll need to define :setting:`MEDIA_ROOT` as the
74.23 - full path to a directory where you'd like Django to store uploaded files.
74.24 - (For performance, these files are not stored in the database.) Define
74.25 - :setting:`MEDIA_URL` as the base public URL of that directory. Make sure
74.26 - that this directory is writable by the Web server's user account.
74.27 + #. In your settings file, you'll need to define :setting:`MEDIA_ROOT` as
74.28 + the full path to a directory where you'd like Django to store uploaded
74.29 + files. (For performance, these files are not stored in the database.)
74.30 + Define :setting:`MEDIA_URL` as the base public URL of that directory.
74.31 + Make sure that this directory is writable by the Web server's user
74.32 + account.
74.33
74.34 - #. Add the :class:`~django.db.models.FileField` or
74.35 - :class:`~django.db.models.ImageField` to your model, making sure to
74.36 - define the :attr:`~django.db.models.FileField.upload_to` option to tell
74.37 - Django to which subdirectory of :setting:`MEDIA_ROOT` it should upload
74.38 + #. Add the :class:`~django.db.models.FileField` or
74.39 + :class:`~django.db.models.ImageField` to your model, making sure to
74.40 + define the :attr:`~django.db.models.FileField.upload_to` option to tell
74.41 + Django to which subdirectory of :setting:`MEDIA_ROOT` it should upload
74.42 files.
74.43
74.44 - #. All that will be stored in your database is a path to the file
74.45 + #. All that will be stored in your database is a path to the file
74.46 (relative to :setting:`MEDIA_ROOT`). You'll most likely want to use the
74.47 convenience :attr:`~django.core.files.File.url` attribute provided by
74.48 Django. For example, if your :class:`~django.db.models.ImageField` is
74.49 called ``mug_shot``, you can get the absolute URL to your image in a
74.50 template with ``{{ object.mug_shot.url }}``.
74.51 +
74.52 +How do I make a variable available to all my templates?
74.53 +-------------------------------------------------------
74.54 +
74.55 +Sometimes your templates just all need the same thing. A common example would
74.56 +be dynamically-generated menus. At first glance, it seems logical to simply
74.57 +add a common dictionary to the template context.
74.58 +
74.59 +The correct solution is to use a ``RequestContext``. Details on how to do this
74.60 +are here: :ref:`subclassing-context-requestcontext`.
75.1 --- a/docs/glossary.txt Sat Mar 28 12:05:48 2009 -0500
75.2 +++ b/docs/glossary.txt Wed Apr 01 13:05:19 2009 -0500
75.3 @@ -13,8 +13,8 @@
75.4 See :ref:`topics-db-models`.
75.5
75.6 generic view
75.7 - A higher-order :term:`view` function that abstracts common idioms and patterns
75.8 - found in view development and abstracts them.
75.9 + A higher-order :term:`view` function that provides an abstract/generic
75.10 + implementation of a common idiom or pattern found in view development.
75.11
75.12 See :ref:`ref-generic-views`.
75.13
75.14 @@ -71,8 +71,9 @@
75.15 the last bit (``spring``) is the slug.
75.16
75.17 template
75.18 - A chunk of text that separates the presentation of a document from its
75.19 - data.
75.20 + A chunk of text that acts as formatting for representing data. A
75.21 + template helps to abstract the presentation of data from the data
75.22 + itself.
75.23
75.24 See :ref:`topics-templates`.
75.25
76.1 --- a/docs/howto/custom-model-fields.txt Sat Mar 28 12:05:48 2009 -0500
76.2 +++ b/docs/howto/custom-model-fields.txt Wed Apr 01 13:05:19 2009 -0500
76.3 @@ -45,7 +45,7 @@
76.4 self.east = east
76.5 self.south = south
76.6 self.west = west
76.7 -
76.8 +
76.9 # ... (other possibly useful methods omitted) ...
76.10
76.11 .. _Bridge: http://en.wikipedia.org/wiki/Contract_bridge
76.12 @@ -198,15 +198,13 @@
76.13 * :attr:`~django.db.models.Field.blank`
76.14 * :attr:`~django.db.models.Field.null`
76.15 * :attr:`~django.db.models.Field.db_index`
76.16 - * :attr:`~django.db.models.Field.core`
76.17 * :attr:`~django.db.models.Field.rel`: Used for related fields (like
76.18 :class:`ForeignKey`). For advanced use only.
76.19 * :attr:`~django.db.models.Field.default`
76.20 * :attr:`~django.db.models.Field.editable`
76.21 - * :attr:`~django.db.models.Field.serialize`: If ``False``, the field will
76.22 + * :attr:`~django.db.models.Field.serialize`: If ``False``, the field will
76.23 not be serialized when the model is passed to Django's :ref:`serializers
76.24 <topics-serialization>`. Defaults to ``True``.
76.25 - * :attr:`~django.db.models.Field.prepopulate_from`
76.26 * :attr:`~django.db.models.Field.unique_for_date`
76.27 * :attr:`~django.db.models.Field.unique_for_month`
76.28 * :attr:`~django.db.models.Field.unique_for_year`
76.29 @@ -216,6 +214,9 @@
76.30 * :attr:`~django.db.models.Field.db_tablespace`: Currently only used with
76.31 the Oracle backend and only for index creation. You can usually ignore
76.32 this option.
76.33 + * :attr:`~django.db.models.Field.auto_created`: True if the field was
76.34 + automatically created, as for the `OneToOneField` used by model
76.35 + inheritance. For advanced use only.
76.36
76.37 All of the options without an explanation in the above list have the same
76.38 meaning they do for normal Django fields. See the :ref:`field documentation
76.39 @@ -254,7 +255,7 @@
76.40 Useful methods
76.41 --------------
76.42
76.43 -Once you've created your :class:`~django.db.models.Field` subclass and set up up
76.44 +Once you've created your :class:`~django.db.models.Field` subclass and set up
76.45 the ``__metaclass__``, you might consider overriding a few standard methods,
76.46 depending on your field's behavior. The list of methods below is in
76.47 approximately decreasing order of importance, so start from the top.
76.48 @@ -419,9 +420,9 @@
76.49
76.50 Same as the above, but called when the Field value must be *saved* to the
76.51 database. As the default implementation just calls ``get_db_prep_value``, you
76.52 -shouldn't need to implement this method unless your custom field need a special
76.53 -conversion when being saved that is not the same as the used for normal query
76.54 -parameters (which is implemented by ``get_db_prep_value``).
76.55 +shouldn't need to implement this method unless your custom field needs a
76.56 +special conversion when being saved that is not the same as the conversion used
76.57 +for normal query parameters (which is implemented by ``get_db_prep_value``).
76.58
76.59 Preprocessing values before saving
76.60 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
76.61 @@ -522,7 +523,7 @@
76.62 defaults.update(kwargs)
76.63 return super(HandField, self).formfield(**defaults)
76.64
76.65 -This assumes we're imported a ``MyFormField`` field class (which has its own
76.66 +This assumes we've imported a ``MyFormField`` field class (which has its own
76.67 default widget). This document doesn't cover the details of writing custom form
76.68 fields.
76.69
77.1 --- a/docs/howto/deployment/fastcgi.txt Sat Mar 28 12:05:48 2009 -0500
77.2 +++ b/docs/howto/deployment/fastcgi.txt Wed Apr 01 13:05:19 2009 -0500
77.3 @@ -1,8 +1,8 @@
77.4 .. _howto-deployment-fastcgi:
77.5
77.6 -===========================================
77.7 -How to use Django with FastCGI, SCGI or AJP
77.8 -===========================================
77.9 +============================================
77.10 +How to use Django with FastCGI, SCGI, or AJP
77.11 +============================================
77.12
77.13 .. highlight:: bash
77.14
77.15 @@ -288,6 +288,19 @@
77.16 specifying multiple entries in the ``fastcgi.server`` directive. Add one
77.17 FastCGI host for each.
77.18
77.19 +Cherokee setup
77.20 +==============
77.21 +
77.22 +Cherokee is a very fast, flexible and easy to configure Web Server. It
77.23 +supports the widespread technologies nowadays: FastCGI, SCGI, PHP, CGI, SSI,
77.24 +TLS and SSL encrypted connections, Virtual hosts, Authentication, on the fly
77.25 +encoding, Load Balancing, Apache compatible log files, Data Base Balancer,
77.26 +Reverse HTTP Proxy and much more.
77.27 +
77.28 +The Cherokee project provides a documentation to `setting up Django`_ with Cherokee.
77.29 +
77.30 +.. _setting up Django: http://www.cherokee-project.com/doc/cookbook_django.html
77.31 +
77.32 Running Django on a shared-hosting provider with Apache
77.33 =======================================================
77.34
77.35 @@ -379,5 +392,3 @@
77.36 As an example of how to use it, if your Django configuration is serving all of
77.37 the URLs under ``'/'`` and you wanted to use this setting, you would set
77.38 ``FORCE_SCRIPT_NAME = ''`` in your settings file.
77.39 -
77.40 -
78.1 --- a/docs/howto/deployment/index.txt Sat Mar 28 12:05:48 2009 -0500
78.2 +++ b/docs/howto/deployment/index.txt Wed Apr 01 13:05:19 2009 -0500
78.3 @@ -11,23 +11,18 @@
78.4 .. toctree::
78.5 :maxdepth: 1
78.6
78.7 + modwsgi
78.8 modpython
78.9 + modwsgi
78.10 fastcgi
78.11
78.12 -:ref:`Deploying under mod_python <howto-deployment-modpython>` is the
78.13 -recommended deployment method; start there if you're not sure which path you'd
78.14 -like to go down.
78.15 +If you're new to deploying Django and/or Python, we'd recommend you try
78.16 +:ref:`mod_wsgi <howto-deployment-modwsgi>` first. In most cases it'll be the easiest,
78.17 +fastest, and most stable deployment choice.
78.18
78.19 .. seealso::
78.20
78.21 * `Chapter 20 of The Django Book`_ discusses deployment and especially
78.22 scaling in more detail.
78.23
78.24 - * `mod_wsgi`_ is a newcomer to the Python deployment world, but it's rapidly
78.25 - gaining traction. Currently there's a few hoops you have to jump through to
78.26 - `use mod_wsgi with Django`_, but mod_wsgi tends to get rave reviews from
78.27 - those who use it.
78.28 -
78.29 .. _chapter 20 of the django book: http://djangobook.com/en/1.0/chapter20/
78.30 -.. _mod_wsgi: http://code.google.com/p/modwsgi/
78.31 -.. _use mod_wsgi with Django: http://code.google.com/p/modwsgi/wiki/IntegrationWithDjango
78.32 \ No newline at end of file
79.1 --- a/docs/howto/deployment/modpython.txt Sat Mar 28 12:05:48 2009 -0500
79.2 +++ b/docs/howto/deployment/modpython.txt Wed Apr 01 13:05:19 2009 -0500
79.3 @@ -17,8 +17,8 @@
79.4 Django requires Apache 2.x and mod_python 3.x, and you should use Apache's
79.5 `prefork MPM`_, as opposed to the `worker MPM`_.
79.6
79.7 -You may also be interested in :ref:`How to use Django with FastCGI, SCGI or AJP
79.8 -<howto-deployment-fastcgi>` (which also covers SCGI and AJP).
79.9 +You may also be interested in :ref:`How to use Django with FastCGI, SCGI, or
79.10 +AJP <howto-deployment-fastcgi>`.
79.11
79.12 .. _Apache: http://httpd.apache.org/
79.13 .. _mod_python: http://www.modpython.org/
79.14 @@ -38,7 +38,7 @@
79.15 SetHandler python-program
79.16 PythonHandler django.core.handlers.modpython
79.17 SetEnv DJANGO_SETTINGS_MODULE mysite.settings
79.18 - PythonOption django.root /mysite
79.19 + PythonOption django.root /mysite
79.20 PythonDebug On
79.21 </Location>
79.22
79.23 @@ -58,12 +58,12 @@
79.24 django.root ...`` line. The value set on that line (the last item) should
79.25 match the string given in the ``<Location ...>`` directive. The effect of this
79.26 is that Django will automatically strip the ``/mysite`` string from the front
79.27 -of any URLs before matching them against your ``URLConf`` patterns. If you
79.28 -later move your site to live under ``/mysite2``, you will not have to change
79.29 -anything except the ``django.root`` option in the config file.
79.30 +of any URLs before matching them against your URLconf patterns. If you later
79.31 +move your site to live under ``/mysite2``, you will not have to change anything
79.32 +except the ``django.root`` option in the config file.
79.33
79.34 When using ``django.root`` you should make sure that what's left, after the
79.35 -prefix has been removed, begins with a slash. Your URLConf patterns that are
79.36 +prefix has been removed, begins with a slash. Your URLconf patterns that are
79.37 expecting an initial slash will then work correctly. In the above example,
79.38 since we want to send things like ``/mysite/admin/`` to ``/admin/``, we need
79.39 to remove the string ``/mysite`` from the beginning, so that is the
79.40 @@ -84,7 +84,7 @@
79.41 SetHandler python-program
79.42 PythonHandler django.core.handlers.modpython
79.43 SetEnv DJANGO_SETTINGS_MODULE mysite.settings
79.44 - PythonOption django.root /mysite
79.45 + PythonOption django.root /mysite
79.46 PythonDebug On
79.47 **PythonPath "['/path/to/project'] + sys.path"**
79.48 </Location>
79.49 @@ -215,8 +215,10 @@
79.50 Django -- for serving media. Here are some good choices:
79.51
79.52 * lighttpd_
79.53 + * Nginx_
79.54 * TUX_
79.55 * A stripped-down version of Apache_
79.56 + * Cherokee_
79.57
79.58 If, however, you have no option but to serve media files on the same Apache
79.59 ``VirtualHost`` as Django, here's how you can turn off mod_python for a
79.60 @@ -249,8 +251,10 @@
79.61
79.62
79.63 .. _lighttpd: http://www.lighttpd.net/
79.64 +.. _Nginx: http://wiki.codemongers.com/Main
79.65 .. _TUX: http://en.wikipedia.org/wiki/TUX_web_server
79.66 .. _Apache: http://httpd.apache.org/
79.67 +.. _Cherokee: http://www.cherokee-project.com/
79.68
79.69 .. _howto-deployment-modpython-serving-the-admin-files:
79.70
79.71 @@ -273,7 +277,7 @@
79.72 document root. This way, all of your Django-related files -- code **and**
79.73 templates -- stay in one place, and you'll still be able to ``svn
79.74 update`` your code to get the latest admin templates, if they change.
79.75 -
79.76 +
79.77 2. Or, copy the admin media files so that they live within your Apache
79.78 document root.
79.79
79.80 @@ -337,7 +341,7 @@
79.81 1. It may be because your Python code is importing the "pyexpat" module,
79.82 which may conflict with the version embedded in Apache. For full
79.83 information, see `Expat Causing Apache Crash`_.
79.84 -
79.85 +
79.86 2. It may be because you're running mod_python and mod_php in the same
79.87 Apache instance, with MySQL as your database backend. In some cases,
79.88 this causes a known mod_python issue due to version conflicts in PHP and
79.89 @@ -361,5 +365,3 @@
79.90 .. _Expat Causing Apache Crash: http://www.dscpl.com.au/articles/modpython-006.html
79.91 .. _mod_python FAQ entry: http://modpython.org/FAQ/faqw.py?req=show&file=faq02.013.htp
79.92 .. _Getting mod_python Working: http://www.dscpl.com.au/articles/modpython-001.html
79.93 -
79.94 -
80.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
80.2 +++ b/docs/howto/deployment/modwsgi.txt Wed Apr 01 13:05:19 2009 -0500
80.3 @@ -0,0 +1,68 @@
80.4 +.. _howto-deployment-modwsgi:
80.5 +
80.6 +==========================================
80.7 +How to use Django with Apache and mod_wsgi
80.8 +==========================================
80.9 +
80.10 +Deploying Django with Apache_ and `mod_wsgi`_ is the recommended way to get
80.11 +Django into production.
80.12 +
80.13 +.. _Apache: http://httpd.apache.org/
80.14 +.. _mod_wsgi: http://code.google.com/p/modwsgi/
80.15 +
80.16 +mod_wsgi is an Apache module which can be used to host any Python application
80.17 +which supports the `Python WSGI interface`_, including Django. Django will work
80.18 +with any version of Apache which supports mod_wsgi.
80.19 +
80.20 +.. _python wsgi interface: http://www.python.org/dev/peps/pep-0333/
80.21 +
80.22 +The `official mod_wsgi documentation`_ is fantastic; it's your source for all
80.23 +the details about how to use mod_wsgi. You'll probably want to start with the
80.24 +`installation and configuration documentation`_.
80.25 +
80.26 +.. _official mod_wsgi documentation: http://code.google.com/p/modwsgi/
80.27 +.. _installation and configuration documentation: http://code.google.com/p/modwsgi/wiki/InstallationInstructions
80.28 +
80.29 +Basic Configuration
80.30 +===================
80.31 +
80.32 +Once you've got mod_wsgi installed and activated, edit your ``httpd.conf`` file
80.33 +and add::
80.34 +
80.35 + WSGIScriptAlias / /path/to/mysite/apache/django.wsgi
80.36 +
80.37 +The first bit aboveis the url you want to be serving your application at (``/``
80.38 +indicates the root url), and the second is the location of a "WSGI file" -- see
80.39 +below -- on your system, usually inside of your project. This tells Apache
80.40 +to serve any request below the given URL using the WSGI application defined by that file.
80.41 +
80.42 +Next we'll need to actually create this WSGI application, so create the file
80.43 +mentioned in the second part of ``WSGIScriptAlias`` and add::
80.44 +
80.45 + import os
80.46 + import sys
80.47 +
80.48 + os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings'
80.49 +
80.50 + import django.core.handlers.wsgi
80.51 + application = django.core.handlers.wsgi.WSGIHandler()
80.52 +
80.53 +If your project is not on your ``PYTHONPATH`` by default you can add::
80.54 +
80.55 + sys.path.append('/usr/local/django')
80.56 +
80.57 +just above the ``import`` line to place your project on the path. Remember to
80.58 +replace 'mysite.settings' with your correct settings file.
80.59 +
80.60 +See the :ref:`Apache/mod_python documentation<howto-deployment-modpython>` for
80.61 +directions on serving static media, and the `mod_wsgi documentation`_ for an
80.62 +explanation of other directives and configuration options you can use.
80.63 +
80.64 +Details
80.65 +=======
80.66 +
80.67 +For more details, see the `mod_wsgi documentation`_, which explains the above in
80.68 +more detail, and walks through all the various options you've got when deploying
80.69 +under mod_wsgi.
80.70 +
80.71 +.. _mod_wsgi documentation: http://code.google.com/p/modwsgi/wiki/IntegrationWithDjango
80.72 \ No newline at end of file
81.1 --- a/docs/howto/jython.txt Sat Mar 28 12:05:48 2009 -0500
81.2 +++ b/docs/howto/jython.txt Wed Apr 01 13:05:19 2009 -0500
81.3 @@ -17,18 +17,8 @@
81.4 Installing Jython
81.5 =================
81.6
81.7 -Django works with Jython versions 2.5 and higher.
81.8 -
81.9 -.. warning::
81.10 -
81.11 - As of the writing of this document, a version of Jython compatible with
81.12 - Django has not yet been released. The forthcoming 2.5a2+ release will be be
81.13 - the first in which Django will work out of the box.
81.14 -
81.15 - In the meantime, you'll need to use the latest development version of Jython
81.16 - obtained from Jython's SVN repository.
81.17 -
81.18 -Download Jython at http://www.jython.org/.
81.19 +Django works with Jython versions 2.5b3 and higher. Download Jython at
81.20 +http://www.jython.org/.
81.21
81.22 Creating a servlet container
81.23 ============================
82.1 --- a/docs/howto/static-files.txt Sat Mar 28 12:05:48 2009 -0500
82.2 +++ b/docs/howto/static-files.txt Wed Apr 01 13:05:19 2009 -0500
82.3 @@ -10,7 +10,7 @@
82.4 Django itself doesn't serve static (media) files, such as images, style sheets,
82.5 or video. It leaves that job to whichever Web server you choose.
82.6
82.7 -The reasoning here is that standard Web servers, such as Apache_ and lighttpd_,
82.8 +The reasoning here is that standard Web servers, such as Apache_, lighttpd_ and Cherokee_,
82.9 are much more fine-tuned at serving static files than a Web application
82.10 framework.
82.11
82.12 @@ -19,6 +19,7 @@
82.13
82.14 .. _Apache: http://httpd.apache.org/
82.15 .. _lighttpd: http://www.lighttpd.net/
82.16 +.. _Cherokee: http://www.cherokee-project.com/
82.17
82.18 The big, fat disclaimer
82.19 =======================
82.20 @@ -72,6 +73,9 @@
82.21 (r'^site_media/(?P<path>.*)$', 'django.views.static.serve',
82.22 {'document_root': settings.STATIC_DOC_ROOT}),
82.23
82.24 +Be careful not to use the same path as your :setting:`ADMIN_MEDIA_PREFIX` (which defaults
82.25 +to ``/media/``) as this will overwrite your URLconf entry.
82.26 +
82.27 Directory listings
82.28 ==================
82.29
83.1 --- a/docs/index.txt Sat Mar 28 12:05:48 2009 -0500
83.2 +++ b/docs/index.txt Wed Apr 01 13:05:19 2009 -0500
83.3 @@ -33,47 +33,121 @@
83.4 First steps
83.5 ===========
83.6
83.7 - * **From scratch:** :ref:`Overview <intro-overview>` | :ref:`Installation <intro-install>`
83.8 - * **Tutorial:** :ref:`Part 1 <intro-tutorial01>` | :ref:`Part 2 <intro-tutorial02>` | :ref:`Part 3 <intro-tutorial03>` | :ref:`Part 4 <intro-tutorial04>`
83.9 + * **From scratch:**
83.10 + :ref:`Overview <intro-overview>` |
83.11 + :ref:`Installation <intro-install>`
83.12 +
83.13 + * **Tutorial:**
83.14 + :ref:`Part 1 <intro-tutorial01>` |
83.15 + :ref:`Part 2 <intro-tutorial02>` |
83.16 + :ref:`Part 3 <intro-tutorial03>` |
83.17 + :ref:`Part 4 <intro-tutorial04>`
83.18
83.19 The model layer
83.20 ===============
83.21
83.22 - * **Models:** :ref:`Model syntax <topics-db-models>` | :ref:`Field types <ref-models-fields>` | :ref:`Meta options <ref-models-options>`
83.23 - * **QuerySets:** :ref:`Executing queries <topics-db-queries>` | :ref:`QuerySet method reference <ref-models-querysets>`
83.24 - * **Model instances:** :ref:`Instance methods <ref-models-instances>` | :ref:`Accessing related objects <ref-models-relations>`
83.25 - * **Advanced:** :ref:`Managers <topics-db-managers>` | :ref:`Raw SQL <topics-db-sql>` | :ref:`Transactions <topics-db-transactions>` | :ref:`Aggregation <topics-db-aggregation>` | :ref:`Custom fields <howto-custom-model-fields>`
83.26 - * **Other:** :ref:`Supported databases <ref-databases>` | :ref:`Legacy databases <howto-legacy-databases>` | :ref:`Providing initial data <howto-initial-data>`
83.27 + * **Models:**
83.28 + :ref:`Model syntax <topics-db-models>` |
83.29 + :ref:`Field types <ref-models-fields>` |
83.30 + :ref:`Meta options <ref-models-options>`
83.31 +
83.32 + * **QuerySets:**
83.33 + :ref:`Executing queries <topics-db-queries>` |
83.34 + :ref:`QuerySet method reference <ref-models-querysets>`
83.35 +
83.36 + * **Model instances:**
83.37 + :ref:`Instance methods <ref-models-instances>` |
83.38 + :ref:`Accessing related objects <ref-models-relations>`
83.39 +
83.40 + * **Advanced:**
83.41 + :ref:`Managers <topics-db-managers>` |
83.42 + :ref:`Raw SQL <topics-db-sql>` |
83.43 + :ref:`Transactions <topics-db-transactions>` |
83.44 + :ref:`Aggregation <topics-db-aggregation>` |
83.45 + :ref:`Custom fields <howto-custom-model-fields>`
83.46 +
83.47 + * **Other:**
83.48 + :ref:`Supported databases <ref-databases>` |
83.49 + :ref:`Legacy databases <howto-legacy-databases>` |
83.50 + :ref:`Providing initial data <howto-initial-data>`
83.51
83.52 The template layer
83.53 ==================
83.54
83.55 - * **For designers:** :ref:`Syntax overview <topics-templates>` | :ref:`Built-in tags and filters <ref-templates-builtins>`
83.56 - * **For programmers:** :ref:`Template API <ref-templates-api>` | :ref:`Custom tags and filters <howto-custom-template-tags>`
83.57 + * **For designers:**
83.58 + :ref:`Syntax overview <topics-templates>` |
83.59 + :ref:`Built-in tags and filters <ref-templates-builtins>`
83.60 +
83.61 + * **For programmers:**
83.62 + :ref:`Template API <ref-templates-api>` |
83.63 + :ref:`Custom tags and filters <howto-custom-template-tags>`
83.64
83.65 The view layer
83.66 ==============
83.67
83.68 - * **The basics:** :ref:`URLconfs <topics-http-urls>` | :ref:`View functions <topics-http-views>` | :ref:`Shortcuts <topics-http-shortcuts>`
83.69 - * **Reference:** :ref:`Request/response objects <ref-request-response>`
83.70 - * **File uploads:** :ref:`Overview <topics-http-file-uploads>` | :ref:`File objects <ref-files-file>` | :ref:`Storage API <ref-files-storage>` | :ref:`Managing files <topics-files>` | :ref:`Custom storage <howto-custom-file-storage>`
83.71 - * **Advanced:** :ref:`Generic views <ref-generic-views>` | :ref:`Generating CSV <howto-outputting-csv>` | :ref:`Generating PDF <howto-outputting-pdf>`
83.72 - * **Middleware:** :ref:`Overview <topics-http-middleware>` | :ref:`Built-in middleware classes <ref-middleware>`
83.73 + * **The basics:**
83.74 + :ref:`URLconfs <topics-http-urls>` |
83.75 + :ref:`View functions <topics-http-views>` |
83.76 + :ref:`Shortcuts <topics-http-shortcuts>`
83.77 +
83.78 + * **Reference:** :ref:`Request/response objects <ref-request-response>`
83.79 +
83.80 + * **File uploads:**
83.81 + :ref:`Overview <topics-http-file-uploads>` |
83.82 + :ref:`File objects <ref-files-file>` |
83.83 + :ref:`Storage API <ref-files-storage>` |
83.84 + :ref:`Managing files <topics-files>` |
83.85 + :ref:`Custom storage <howto-custom-file-storage>`
83.86 +
83.87 + * **Advanced:**
83.88 + :ref:`Generic views <ref-generic-views>` |
83.89 + :ref:`Generating CSV <howto-outputting-csv>` |
83.90 + :ref:`Generating PDF <howto-outputting-pdf>`
83.91 +
83.92 + * **Middleware:**
83.93 + :ref:`Overview <topics-http-middleware>` |
83.94 + :ref:`Built-in middleware classes <ref-middleware>`
83.95
83.96 Forms
83.97 =====
83.98
83.99 - * **The basics:** :ref:`Overview <topics-forms-index>` | :ref:`Form API <ref-forms-api>` | :ref:`Built-in fields <ref-forms-fields>` | :ref:`Built-in widgets <ref-forms-widgets>`
83.100 - * **Advanced:** :ref:`Forms for models <topics-forms-modelforms>` | :ref:`Integrating media <topics-forms-media>` | :ref:`Formsets <topics-forms-formsets>` | :ref:`Customizing validation <ref-forms-validation>`
83.101 - * **Extras:** :ref:`Form preview <ref-contrib-formtools-form-preview>` | :ref:`Form wizard <ref-contrib-formtools-form-wizard>`
83.102 + * **The basics:**
83.103 + :ref:`Overview <topics-forms-index>` |
83.104 + :ref:`Form API <ref-forms-api>` |
83.105 + :ref:`Built-in fields <ref-forms-fields>` |
83.106 + :ref:`Built-in widgets <ref-forms-widgets>`
83.107 +
83.108 + * **Advanced:**
83.109 + :ref:`Forms for models <topics-forms-modelforms>` |
83.110 + :ref:`Integrating media <topics-forms-media>` |
83.111 + :ref:`Formsets <topics-forms-formsets>` |
83.112 + :ref:`Customizing validation <ref-forms-validation>`
83.113 +
83.114 + * **Extras:**
83.115 + :ref:`Form preview <ref-contrib-formtools-form-preview>` |
83.116 + :ref:`Form wizard <ref-contrib-formtools-form-wizard>`
83.117
83.118 The development process
83.119 =======================
83.120
83.121 - * **Settings:** :ref:`Overview <topics-settings>` | :ref:`Full list of settings <ref-settings>`
83.122 - * **django-admin.py and manage.py:** :ref:`Overview <ref-django-admin>` | :ref:`Adding custom commands <howto-custom-management-commands>`
83.123 - * **Testing:** :ref:`Overview <topics-testing>`
83.124 - * **Deployment:** :ref:`Overview <howto-deployment-index>` | :ref:`Apache/mod_python <howto-deployment-modpython>` | :ref:`FastCGI/SCGI/AJP <howto-deployment-fastcgi>` | :ref:`Apache authentication <howto-apache-auth>` | :ref:`Serving static files <howto-static-files>` | :ref:`Tracking code errors by e-mail <howto-error-reporting>`
83.125 + * **Settings:**
83.126 + :ref:`Overview <topics-settings>` |
83.127 + :ref:`Full list of settings <ref-settings>`
83.128 +
83.129 + * **django-admin.py and manage.py:**
83.130 + :ref:`Overview <ref-django-admin>` |
83.131 + :ref:`Adding custom commands <howto-custom-management-commands>`
83.132 +
83.133 + * **Testing:** :ref:`Overview <topics-testing>`
83.134 +
83.135 + * **Deployment:**
83.136 + :ref:`Overview <howto-deployment-index>` |
83.137 + :ref:`Apache/mod_wsgi <howto-deployment-modwsgi>` |
83.138 + :ref:`Apache/mod_python <howto-deployment-modpython>` |
83.139 + :ref:`FastCGI/SCGI/AJP <howto-deployment-fastcgi>` |
83.140 + :ref:`Apache authentication <howto-apache-auth>` |
83.141 + :ref:`Serving static files <howto-static-files>` |
83.142 + :ref:`Tracking code errors by e-mail <howto-error-reporting>`
83.143
83.144 Other batteries included
83.145 ========================
83.146 @@ -106,10 +180,22 @@
83.147 The Django open-source project
83.148 ==============================
83.149
83.150 - * **Community:** :ref:`How to get involved <internals-contributing>` | :ref:`The release process <internals-release-process>` | :ref:`Team of committers <internals-committers>`
83.151 - * **Design philosophies:** :ref:`Overview <misc-design-philosophies>`
83.152 - * **Documentation:** :ref:`About this documentation <internals-documentation>`
83.153 - * **Third-party distributions:** :ref:`Overview <misc-distributions>`
83.154 - * **Django over time:** :ref:`API stability <misc-api-stability>` | :ref:`Archive of release notes <releases-index>` | `Backwards-incompatible changes`_
83.155 + * **Community:**
83.156 + :ref:`How to get involved <internals-contributing>` |
83.157 + :ref:`The release process <internals-release-process>` |
83.158 + :ref:`Team of committers <internals-committers>`
83.159 +
83.160 + * **Design philosophies:**
83.161 + :ref:`Overview <misc-design-philosophies>`
83.162 +
83.163 + * **Documentation:**
83.164 + :ref:`About this documentation <internals-documentation>`
83.165 +
83.166 + * **Third-party distributions:**
83.167 + :ref:`Overview <misc-distributions>`
83.168 +
83.169 + * **Django over time:**
83.170 + :ref:`API stability <misc-api-stability>` |
83.171 + :ref:`Archive of release notes <releases-index>` | `Backwards-incompatible changes`_
83.172
83.173 .. _Backwards-incompatible changes: http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges
84.1 --- a/docs/internals/release-process.txt Sat Mar 28 12:05:48 2009 -0500
84.2 +++ b/docs/internals/release-process.txt Wed Apr 01 13:05:19 2009 -0500
84.3 @@ -108,7 +108,7 @@
84.4 1.3.2, etc.
84.5
84.6 * Security releases will be applied to trunk, a ``1.3.X`` branch and a
84.7 - ``1.2.X`` branch. Security fixes will trigger the release of of ``1.3.1``,
84.8 + ``1.2.X`` branch. Security fixes will trigger the release of ``1.3.1``,
84.9 ``1.2.1``, etc.
84.10
84.11 .. _release-process:
85.1 --- a/docs/intro/tutorial01.txt Sat Mar 28 12:05:48 2009 -0500
85.2 +++ b/docs/intro/tutorial01.txt Wed Apr 01 13:05:19 2009 -0500
85.3 @@ -618,7 +618,7 @@
85.4 Note the addition of ``import datetime`` to reference Python's standard
85.5 ``datetime`` module.
85.6
85.7 -Let's jump back into the Python interactive shell by running
85.8 +Save these changes and start a new Python interactive shell by running
85.9 ``python manage.py shell`` again::
85.10
85.11 >>> from mysite.polls.models import Poll, Choice
86.1 --- a/docs/intro/tutorial02.txt Sat Mar 28 12:05:48 2009 -0500
86.2 +++ b/docs/intro/tutorial02.txt Wed Apr 01 13:05:19 2009 -0500
86.3 @@ -170,14 +170,14 @@
86.4 Customize the admin form
86.5 ========================
86.6
86.7 -Take a few minutes to marvel at all the code you didn't have to write. When you
86.8 -call ``admin.site.register(Poll)``, Django just lets you edit the object and
86.9 -"guess" at how to display it within the admin. Often you'll want to control how
86.10 -the admin looks and works. You'll do this by telling Django about the options
86.11 +Take a few minutes to marvel at all the code you didn't have to write. By
86.12 +registering the Poll model with ``admin.site.register(Poll)``, Django was able
86.13 +to construct a default form representation. Often, you'll want to customize how
86.14 +the admin form looks and works. You'll do this by telling Django the options
86.15 you want when you register the object.
86.16
86.17 -Let's see how this works by reordering the fields on the edit form. Replace the
86.18 -``admin.site.register(Poll)`` line with::
86.19 +Let's see how this works by re-ordering the fields on the edit form. Replace
86.20 +the ``admin.site.register(Poll)`` line with::
86.21
86.22 class PollAdmin(admin.ModelAdmin):
86.23 fields = ['pub_date', 'question']
87.1 --- a/docs/intro/tutorial03.txt Sat Mar 28 12:05:48 2009 -0500
87.2 +++ b/docs/intro/tutorial03.txt Wed Apr 01 13:05:19 2009 -0500
87.3 @@ -117,8 +117,8 @@
87.4
87.5 Note that these regular expressions do not search GET and POST parameters, or
87.6 the domain name. For example, in a request to ``http://www.example.com/myapp/``,
87.7 -the URLconf will look for ``/myapp/``. In a request to
87.8 -``http://www.example.com/myapp/?page=3``, the URLconf will look for ``/myapp/``.
87.9 +the URLconf will look for ``myapp/``. In a request to
87.10 +``http://www.example.com/myapp/?page=3``, the URLconf will look for ``myapp/``.
87.11
87.12 If you need help with regular expressions, see `Wikipedia's entry`_ and the
87.13 `Python documentation`_. Also, the O'Reilly book "Mastering Regular Expressions"
88.1 --- a/docs/intro/tutorial04.txt Sat Mar 28 12:05:48 2009 -0500
88.2 +++ b/docs/intro/tutorial04.txt Wed Apr 01 13:05:19 2009 -0500
88.3 @@ -47,10 +47,10 @@
88.4 through its loop
88.5
88.6 Now, let's create a Django view that handles the submitted data and does
88.7 -something with it. Remember, in :ref:`Tutorial 3 <intro-tutorial03>`, we created
88.8 -a URLconf for the polls application that includes this line::
88.9 +something with it. Remember, in :ref:`Tutorial 3 <intro-tutorial03>`, we
88.10 +created a URLconf for the polls application that includes this line::
88.11
88.12 - (r'^(?P<poll_id>\d+)/vote/$', 'mysite.polls.views.vote'),
88.13 + (r'^(?P<poll_id>\d+)/vote/$', 'vote'),
88.14
88.15 So let's create a ``vote()`` function in ``mysite/polls/views.py``::
88.16
89.1 --- a/docs/ref/contrib/admin/index.txt Sat Mar 28 12:05:48 2009 -0500
89.2 +++ b/docs/ref/contrib/admin/index.txt Wed Apr 01 13:05:19 2009 -0500
89.3 @@ -408,9 +408,6 @@
89.4 list_display = ('first_name', 'last_name', 'birthday')
89.5 list_display_links = ('first_name', 'last_name')
89.6
89.7 -Finally, note that in order to use ``list_display_links``, you must define
89.8 -``list_display``, too.
89.9 -
89.10 .. _admin-list-editable:
89.11
89.12 ``list_editable``
89.13 @@ -428,7 +425,7 @@
89.14 ``list_editable`` interacts with a couple of other options in particular
89.15 ways; you should note the following rules:
89.16
89.17 - * To use ``list_editable`` you must have defined ``ordering`` defined on
89.18 + * To use ``list_editable`` you must have defined ``ordering`` on
89.19 either your model or your ``ModelAdmin``.
89.20
89.21 * Any field in ``list_editable`` must also be in ``list_display``. You
89.22 @@ -680,7 +677,7 @@
89.23 A list of actions to make available on the change list page. See
89.24 :ref:`ref-contrib-admin-actions` for details.
89.25
89.26 -``actions_on_top``, ``actions_on_buttom``
89.27 +``actions_on_top``, ``actions_on_bottom``
89.28 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
89.29
89.30 Controls where on the page the actions bar appears. By default, the admin
89.31 @@ -835,8 +832,17 @@
89.32 ============================
89.33
89.34 The admin interface has the ability to edit models on the same page as a
89.35 -parent model. These are called inlines. You can add them to a model by
89.36 -specifying them in a ``ModelAdmin.inlines`` attribute::
89.37 +parent model. These are called inlines. Suppose you have these two models::
89.38 +
89.39 + class Author(models.Model):
89.40 + name = models.CharField(max_length=100)
89.41 +
89.42 + class Book(models.Model):
89.43 + author = models.ForeignKey(Author)
89.44 + title = models.CharField(max_length=100)
89.45 +
89.46 +You can edit the books authored by an author on the author page. You add
89.47 +inlines to a model by specifying them in a ``ModelAdmin.inlines``::
89.48
89.49 class BookInline(admin.TabularInline):
89.50 model = Book
89.51 @@ -881,7 +887,7 @@
89.52 ``form``
89.53 ~~~~~~~~
89.54
89.55 -The value for ``form`` is inherited from ``ModelAdmin``. This is what is
89.56 +The value for ``form`` defaults to ``BaseModelForm``. This is what is
89.57 passed through to ``formset_factory`` when creating the formset for this
89.58 inline.
89.59
89.60 @@ -1124,6 +1130,7 @@
89.61 Not every template in ``contrib\admin\templates\admin`` may be overridden per
89.62 app or per model. The following can:
89.63
89.64 + * ``app_index.html``
89.65 * ``change_form.html``
89.66 * ``change_list.html``
89.67 * ``delete_confirmation.html``
89.68 @@ -1168,7 +1175,7 @@
89.69
89.70 The last step in setting up the Django admin is to hook your ``AdminSite``
89.71 instance into your URLconf. Do this by pointing a given URL at the
89.72 -``AdminSite.root`` method.
89.73 +``AdminSite.urls`` method.
89.74
89.75 In this example, we register the default ``AdminSite`` instance
89.76 ``django.contrib.admin.site`` at the URL ``/admin/`` ::
90.1 --- a/docs/ref/contrib/comments/index.txt Sat Mar 28 12:05:48 2009 -0500
90.2 +++ b/docs/ref/contrib/comments/index.txt Wed Apr 01 13:05:19 2009 -0500
90.3 @@ -155,9 +155,10 @@
90.4 {% get_comment_form for event as form %}
90.5 <form action="{% comment_form_target %}" method="POST">
90.6 {{ form }}
90.7 - <p class="submit">
90.8 - <input type="submit" name="preview" class="submit-post" value="Preview">
90.9 - </p>
90.10 + <tr>
90.11 + <td></td>
90.12 + <td><input type="submit" name="preview" class="submit-post" value="Preview"></td>
90.13 + </tr>
90.14 </form>
90.15
90.16 Be sure to read the `notes on the comment form`_, below, for some special
91.1 --- a/docs/ref/contrib/contenttypes.txt Sat Mar 28 12:05:48 2009 -0500
91.2 +++ b/docs/ref/contrib/contenttypes.txt Wed Apr 01 13:05:19 2009 -0500
91.3 @@ -303,7 +303,20 @@
91.4 >>> b.tags.all()
91.5 [<TaggedItem: django>, <TaggedItem: python>]
91.6
91.7 -If you don't add the reverse relationship, you can do the lookup manually::
91.8 +Just as :class:`django.contrib.contenttypes.generic.GenericForeignKey`
91.9 +accepts the names of the content-type and object-ID fields as
91.10 +arguments, so too does ``GenericRelation``; if the model which has the
91.11 +generic foreign key is using non-default names for those fields, you
91.12 +must pass the names of the fields when setting up a
91.13 +``GenericRelation`` to it. For example, if the ``TaggedItem`` model
91.14 +referred to above used fields named ``content_type_fk`` and
91.15 +``object_primary_key`` to create its generic foreign key, then a
91.16 +``GenericRelation`` back to it would need to be defined like so::
91.17 +
91.18 + tags = generic.GenericRelation('content_type_fk', 'object_primary_key')
91.19 +
91.20 +Of course, if you don't add the reverse relationship, you can do the
91.21 +same types of lookups manually::
91.22
91.23 >>> b = Bookmark.objects.get(url='http://www.djangoproject.com/')
91.24 >>> bookmark_type = ContentType.objects.get_for_model(b)
91.25 @@ -311,6 +324,14 @@
91.26 ... object_id=b.id)
91.27 [<TaggedItem: django>, <TaggedItem: python>]
91.28
91.29 +Note that if the model with a :class:`~django.contrib.contenttypes.generic.GenericForeignKey`
91.30 +that you're referring to uses a non-default value for ``ct_field`` or ``fk_field``
91.31 +(e.g. the :mod:`django.contrib.comments` app uses ``ct_field="object_pk"``),
91.32 +you'll need to pass ``content_type_field`` and ``object_id_field`` to
91.33 +:class:`~django.contrib.contenttypes.generic.GenericRelation`.::
91.34 +
91.35 + comments = generic.GenericRelation(Comment, content_type_field="content_type", object_id_field="object_pk")
91.36 +
91.37 Note that if you delete an object that has a
91.38 :class:`~django.contrib.contenttypes.generic.GenericRelation`, any objects
91.39 which have a :class:`~django.contrib.contenttypes.generic.GenericForeignKey`
92.1 --- a/docs/ref/contrib/formtools/form-wizard.txt Sat Mar 28 12:05:48 2009 -0500
92.2 +++ b/docs/ref/contrib/formtools/form-wizard.txt Wed Apr 01 13:05:19 2009 -0500
92.3 @@ -290,7 +290,7 @@
92.4 .. method:: FormWizard.render_template
92.5
92.6 Renders the template for the given step, returning an
92.7 - :class:`~django.http.HttpResponseRedirect` object.
92.8 + :class:`~django.http.HttpResponse` object.
92.9
92.10 Override this method if you want to add a custom context, return a different
92.11 MIME type, etc. If you only need to override the template name, use
93.1 --- a/docs/ref/databases.txt Sat Mar 28 12:05:48 2009 -0500
93.2 +++ b/docs/ref/databases.txt Wed Apr 01 13:05:19 2009 -0500
93.3 @@ -26,7 +26,7 @@
93.4 faulty`_. Users of these releases of PostgreSQL are advised to upgrade to
93.5 `Release 8.2.5`_ or later. Django will raise a ``NotImplementedError`` if you
93.6 attempt to use the ``StdDev(sample=False)`` or ``Variance(sample=False)``
93.7 -aggregate with an database backend falls within the affected release range.
93.8 +aggregate with a database backend that falls within the affected release range.
93.9
93.10 .. _known to be faulty: http://archives.postgresql.org/pgsql-bugs/2007-07/msg00046.php
93.11 .. _Release 8.2.5: http://developer.postgresql.org/pgdocs/postgres/release-8-2-5.html
93.12 @@ -35,7 +35,7 @@
93.13 ---------------------
93.14
93.15 :ref:`By default <topics-db-transactions>`, Django starts a transaction when a
93.16 -database connection if first used and commits the result at the end of the
93.17 +database connection is first used and commits the result at the end of the
93.18 request/response handling. The PostgreSQL backends normally operate the same
93.19 as any other Django backend in this respect.
93.20
93.21 @@ -86,10 +86,10 @@
93.22 MySQL notes
93.23 ===========
93.24
93.25 -Django expects the database to support transactions, referential integrity,
93.26 -and Unicode support (UTF-8 encoding). Fortunately, MySQL_ has all these
93.27 -features as available as far back as 3.23. While it may be possible to use
93.28 -3.23 or 4.0, you'll probably have less trouble if you use 4.1 or 5.0.
93.29 +Django expects the database to support transactions, referential integrity, and
93.30 +Unicode (UTF-8 encoding). Fortunately, MySQL_ has all these features as
93.31 +available as far back as 3.23. While it may be possible to use 3.23 or 4.0,
93.32 +you'll probably have less trouble if you use 4.1 or 5.0.
93.33
93.34 MySQL 4.1
93.35 ---------
93.36 @@ -351,44 +351,44 @@
93.37
93.38 .. _documented at sqlite.org: http://www.sqlite.org/faq.html#q18
93.39
93.40 -Versions prior to 3.3.6
93.41 -------------------------
93.42 +SQLite 3.3.6 or newer strongly recommended
93.43 +------------------------------------------
93.44
93.45 -Versions of SQLite 3.3.5 and older `contain a bug`_ when handling ``ORDER BY``
93.46 -parameters. This can cause problems when you use the ``select`` parameter for
93.47 -the ``extra()`` QuerySet method. The bug can be identified by the error message
93.48 -``OperationalError: ORDER BY terms must not be non-integer constants``. The
93.49 -problem can be solved updating SQLite to version 3.3.6 or newer, possibly also
93.50 -updating the ``pysqlite2`` Python module in the process.
93.51 +Versions of SQLite 3.3.5 and older contains the following bugs:
93.52
93.53 -.. _contain a bug: http://www.sqlite.org/cvstrac/tktview?tn=1768
93.54 + * A bug when `handling`_ ``ORDER BY`` parameters. This can cause problems when
93.55 + you use the ``select`` parameter for the ``extra()`` QuerySet method. The bug
93.56 + can be identified by the error message ``OperationalError: ORDER BY terms
93.57 + must not be non-integer constants``.
93.58
93.59 -This has a very low impact because 3.3.6 was released in April 2006, so most
93.60 -current binary distributions for different platforms include newer version of
93.61 -SQLite usable from Python through either the ``pysqlite2`` or the ``sqlite3``
93.62 -modules.
93.63 + * A bug when handling `aggregation`_ together with DateFields and
93.64 + DecimalFields.
93.65
93.66 -However, in the case of Windows, the official binary distribution of the stable
93.67 -release of Python 2.5 (2.5.2, as of this writing) includes SQLite 3.3.4, so the bug can
93.68 -make itself evident in that platform. There are (as of Django 1.0) even three
93.69 -tests in the Django test suite that will fail when run under this setup. As
93.70 -described above, this can be solved by downloading and installing a newer
93.71 -version of ``pysqlite2`` (``pysqlite-2.x.x.win32-py2.5.exe``) that includes and
93.72 -uses a newer version of SQLite. Python 2.6 ships with a newer version of
93.73 -SQLite and is not affected by this issue.
93.74 +.. _handling: http://www.sqlite.org/cvstrac/tktview?tn=1768
93.75 +.. _aggregation: http://code.djangoproject.com/ticket/10031
93.76
93.77 -If you are in such platform and find yourself in the need to update
93.78 -``pysqlite``/SQLite, you will also need to manually modify the
93.79 -``django/db/backends/sqlite3/base.py`` file in the Django source tree so it
93.80 -attempts to import ``pysqlite2`` before than ``sqlite3`` and so it can take
93.81 -advantage of the new ``pysqlite2``/SQLite versions.
93.82 +SQLite 3.3.6 was released in April 2006, so most current binary distributions
93.83 +for different platforms include newer version of SQLite usable from Python
93.84 +through either the ``pysqlite2`` or the ``sqlite3`` modules.
93.85 +
93.86 +However, some platform/Python version combinations include older versions of
93.87 +SQLite (e.g. the official binary distribution of Python 2.5 for Windows, 2.5.4
93.88 +as of this writing, includes SQLite 3.3.4). There are (as of Django 1.1) even
93.89 +some tests in the Django test suite that will fail when run under this setup.
93.90 +
93.91 +As described :ref:`below<using-newer-versions-of-pysqlite>`, this can be solved
93.92 +by downloading and installing a newer version of ``pysqlite2``
93.93 +(``pysqlite-2.x.x.win32-py2.5.exe`` in the described case) that includes and
93.94 +uses a newer version of SQLite. Python 2.6 for Windows ships with a version of
93.95 +SQLite that is not affected by these issues.
93.96
93.97 Version 3.5.9
93.98 -------------
93.99
93.100 -The Ubuntu "Intrepid Ibex" SQLite 3.5.9-3 package contains a bug that causes
93.101 -problems with the evaluation of query expressions. If you are using Ubuntu
93.102 -"Intrepid Ibex", you will need to find an alternate source for SQLite
93.103 +The Ubuntu "Intrepid Ibex" (8.10) SQLite 3.5.9-3 package contains a bug that
93.104 +causes problems with the evaluation of query expressions. If you are using
93.105 +Ubuntu "Intrepid Ibex", you will need to update the package to version
93.106 +3.5.9-3ubuntu1 or newer (recommended) or find an alternate source for SQLite
93.107 packages, or install SQLite from source.
93.108
93.109 At one time, Debian Lenny shipped with the same malfunctioning SQLite 3.5.9-3
93.110 @@ -411,6 +411,21 @@
93.111 3.6.3 (released September 22, 2008) or later, or downgrade to an earlier
93.112 version of SQLite.
93.113
93.114 +.. _using-newer-versions-of-pysqlite:
93.115 +
93.116 +Using newer versions of the SQLite DB-API 2.0 driver
93.117 +----------------------------------------------------
93.118 +
93.119 +.. versionadded:: 1.1
93.120 +
93.121 +For versions of Python 2.5 or newer that include ``sqlite3`` in the standard
93.122 +library Django will now use a ``pysqlite2`` interface in preference to
93.123 +``sqlite3`` if it finds one is available.
93.124 +
93.125 +This provides the ability to upgrade both the DB-API 2.0 interface or SQLite 3
93.126 +itself to versions newer than the ones included with your particular Python
93.127 +binary distribution, if needed.
93.128 +
93.129 .. _oracle-notes:
93.130
93.131 Oracle notes
94.1 --- a/docs/ref/django-admin.txt Sat Mar 28 12:05:48 2009 -0500
94.2 +++ b/docs/ref/django-admin.txt Wed Apr 01 13:05:19 2009 -0500
94.3 @@ -341,6 +341,9 @@
94.4 application, ``<dirname>/foo/bar/mydata.json`` for each directory in
94.5 ``FIXTURE_DIRS``, and the literal path ``foo/bar/mydata.json``.
94.6
94.7 +When fixture files are processed, the data is saved to the database as is.
94.8 +Model defined ``save`` methods and ``pre_save`` signals are not called.
94.9 +
94.10 Note that the order in which fixture files are processed is undefined. However,
94.11 all fixture data is installed as a single transaction, so data in
94.12 one fixture can reference data in another fixture. If the database backend
95.1 --- a/docs/ref/forms/fields.txt Sat Mar 28 12:05:48 2009 -0500
95.2 +++ b/docs/ref/forms/fields.txt Wed Apr 01 13:05:19 2009 -0500
95.3 @@ -174,6 +174,16 @@
95.4 >>> f.errors
95.5 {'url': [u'This field is required.'], 'name': [u'This field is required.']}
95.6
95.7 +Instead of a constant, you can also pass any callable::
95.8 +
95.9 + >>> import datetime
95.10 + >>> class DateForm(forms.Form):
95.11 + ... day = forms.DateField(initial=datetime.date.today)
95.12 + >>> print DateForm()
95.13 + <tr><th>Day:</th><td><input type="text" name="day" value="12/23/2008" /><td></tr>
95.14 +
95.15 +The callable will be evaluated only when the unbound form is displayed, not when it is defined.
95.16 +
95.17 ``widget``
95.18 ~~~~~~~~~~
95.19
96.1 --- a/docs/ref/models/fields.txt Sat Mar 28 12:05:48 2009 -0500
96.2 +++ b/docs/ref/models/fields.txt Wed Apr 01 13:05:19 2009 -0500
96.3 @@ -7,6 +7,8 @@
96.4 .. module:: django.db.models.fields
96.5 :synopsis: Built-in field types.
96.6
96.7 +.. currentmodule:: django.db.models
96.8 +
96.9 This document contains all the gory details about all the `field options`_ and
96.10 `field types`_ Django's got to offer.
96.11
96.12 @@ -239,7 +241,8 @@
96.13 field, a :exc:`django.db.IntegrityError` will be raised by the model's
96.14 :meth:`~django.db.models.Model.save` method.
96.15
96.16 -This option is valid on all field types except :class:`ManyToManyField`.
96.17 +This option is valid on all field types except :class:`ManyToManyField` and
96.18 +:class:`FileField`.
96.19
96.20 ``unique_for_date``
96.21 -------------------
96.22 @@ -365,13 +368,14 @@
96.23
96.24 .. class:: DateField([auto_now=False, auto_now_add=False, **options])
96.25
96.26 -A date field. Has a few extra optional arguments:
96.27 +A date, represented in Python by a ``datetime.date`` instance. Has a few extra,
96.28 +optional arguments:
96.29
96.30 .. attribute:: DateField.auto_now
96.31
96.32 Automatically set the field to now every time the object is saved. Useful
96.33 - for "last-modified" timestamps. Note that the current date is *always* used;
96.34 - it's not just a default value that you can override.
96.35 + for "last-modified" timestamps. Note that the current date is *always*
96.36 + used; it's not just a default value that you can override.
96.37
96.38 .. attribute:: DateField.auto_now_add
96.39
96.40 @@ -380,18 +384,19 @@
96.41 it's not just a default value that you can override.
96.42
96.43 The admin represents this as an ``<input type="text">`` with a JavaScript
96.44 -calendar, and a shortcut for "Today". The JavaScript calendar will always start
96.45 -the week on a Sunday.
96.46 +calendar, and a shortcut for "Today". The JavaScript calendar will always
96.47 +start the week on a Sunday.
96.48
96.49 ``DateTimeField``
96.50 -----------------
96.51
96.52 .. class:: DateTimeField([auto_now=False, auto_now_add=False, **options])
96.53
96.54 -A date and time field. Takes the same extra options as :class:`DateField`.
96.55 +A date and time, represented in Python by a ``datetime.datetime`` instance.
96.56 +Takes the same extra arguments as :class:`DateField`.
96.57
96.58 -The admin represents this as two ``<input type="text">`` fields, with JavaScript
96.59 -shortcuts.
96.60 +The admin represents this as two ``<input type="text">`` fields, with
96.61 +JavaScript shortcuts.
96.62
96.63 ``DecimalField``
96.64 ----------------
96.65 @@ -435,7 +440,13 @@
96.66
96.67 .. class:: FileField(upload_to=None, [max_length=100, **options])
96.68
96.69 -A file-upload field. Has one **required** argument:
96.70 +A file-upload field.
96.71 +
96.72 +.. note::
96.73 + The ``primary_key`` and ``unique`` arguments are not supported, and will
96.74 + raise a ``TypeError`` if used.
96.75 +
96.76 +Has one **required** argument:
96.77
96.78 .. attribute:: FileField.upload_to
96.79
96.80 @@ -706,9 +717,11 @@
96.81
96.82 .. class:: TimeField([auto_now=False, auto_now_add=False, **options])
96.83
96.84 -A time. Accepts the same auto-population options as :class:`DateField` and
96.85 -:class:`DateTimeField`. The admin represents this as an ``<input type="text">``
96.86 -with some JavaScript shortcuts.
96.87 +A time, represented in Python by a ``datetime.time`` instance. Accepts the same
96.88 +auto-population options as :class:`DateField`.
96.89 +
96.90 +The admin represents this as an ``<input type="text">`` with some JavaScript
96.91 +shortcuts.
96.92
96.93 ``URLField``
96.94 ------------
96.95 @@ -720,7 +733,10 @@
96.96 .. attribute:: URLField.verify_exists
96.97
96.98 If ``True`` (the default), the URL given will be checked for existence
96.99 - (i.e., the URL actually loads and doesn't give a 404 response).
96.100 + (i.e., the URL actually loads and doesn't give a 404 response). It should
96.101 + be noted that when using the single-threaded development server, validating
96.102 + a url being serverd by the same server will hang.
96.103 + This should not be a problem for multithreaded servers.
96.104
96.105 The admin represents this as an ``<input type="text">`` (a single-line input).
96.106
97.1 --- a/docs/ref/models/instances.txt Sat Mar 28 12:05:48 2009 -0500
97.2 +++ b/docs/ref/models/instances.txt Wed Apr 01 13:05:19 2009 -0500
97.3 @@ -18,12 +18,13 @@
97.4 Creating objects
97.5 ================
97.6
97.7 -To create a new instance of a model, just instantiate it like any other Python class:
97.8 +To create a new instance of a model, just instantiate it like any other Python
97.9 +class:
97.10
97.11 .. class:: Model(**kwargs)
97.12
97.13 -The keyword arguments to are simply the names of the fields you've defined on
97.14 -your model. Note that instantiating a model in no way touches your database; for
97.15 +The keyword arguments are simply the names of the fields you've defined on your
97.16 +model. Note that instantiating a model in no way touches your database; for
97.17 that, you need to ``save()``.
97.18
97.19 Saving objects
97.20 @@ -289,7 +290,7 @@
97.21
97.22 The problem with the way we wrote ``get_absolute_url()`` above is that it
97.23 slightly violates the DRY principle: the URL for this object is defined both
97.24 -in the URLConf file and in the model.
97.25 +in the URLconf file and in the model.
97.26
97.27 You can further decouple your models from the URLconf using the ``permalink``
97.28 decorator:
98.1 --- a/docs/ref/models/querysets.txt Sat Mar 28 12:05:48 2009 -0500
98.2 +++ b/docs/ref/models/querysets.txt Wed Apr 01 13:05:19 2009 -0500
98.3 @@ -154,7 +154,7 @@
98.4
98.5 SELECT ...
98.6 WHERE NOT pub_date > '2005-1-3'
98.7 - AND NOT headline = 'Hello'
98.8 + OR NOT headline = 'Hello'
98.9
98.10 Note the second example is more restrictive.
98.11
98.12 @@ -1484,8 +1484,18 @@
98.13 A boolean full-text search, taking advantage of full-text indexing. This is
98.14 like ``contains`` but is significantly faster due to full-text indexing.
98.15
98.16 +Example::
98.17 +
98.18 + Entry.objects.filter(headline__search="+Django -jazz Python")
98.19 +
98.20 +SQL equivalent::
98.21 +
98.22 + SELECT ... WHERE MATCH(tablename, headline) AGAINST (+Django -jazz Python IN BOOLEAN MODE);
98.23 +
98.24 Note this is only available in MySQL and requires direct manipulation of the
98.25 -database to add the full-text index.
98.26 +database to add the full-text index. By default Django uses BOOLEAN MODE for
98.27 +full text searches. `Please check MySQL documentation for additional details. <http://dev.mysql.com/doc/refman/5.1/en/fulltext-boolean.html>`_
98.28 +
98.29
98.30 regex
98.31 ~~~~~
99.1 --- a/docs/ref/models/relations.txt Sat Mar 28 12:05:48 2009 -0500
99.2 +++ b/docs/ref/models/relations.txt Wed Apr 01 13:05:19 2009 -0500
99.3 @@ -42,7 +42,7 @@
99.4 .... body_text='Hi',
99.5 .... pub_date=datetime.date(2005, 1, 1)
99.6 .... )
99.7 - >>> e.save()
99.8 + >>> e.save(force_insert=True)
99.9
99.10 Note that there's no need to specify the keyword argument of the model that
99.11 defines the relationship. In the above example, we don't pass the parameter
100.1 --- a/docs/ref/request-response.txt Sat Mar 28 12:05:48 2009 -0500
100.2 +++ b/docs/ref/request-response.txt Wed Apr 01 13:05:19 2009 -0500
100.3 @@ -14,11 +14,11 @@
100.4
100.5 When a page is requested, Django creates an :class:`HttpRequest` object that
100.6 contains metadata about the request. Then Django loads the appropriate view,
100.7 -passing the :class:`HttpRequest` as the first argument to the view function. Each
100.8 -view is responsible for returning an :class:`HttpResponse` object.
100.9 +passing the :class:`HttpRequest` as the first argument to the view function.
100.10 +Each view is responsible for returning an :class:`HttpResponse` object.
100.11
100.12 -This document explains the APIs for :class:`HttpRequest` and :class:`HttpResponse`
100.13 -objects.
100.14 +This document explains the APIs for :class:`HttpRequest` and
100.15 +:class:`HttpResponse` objects.
100.16
100.17 HttpRequest objects
100.18 ===================
100.19 @@ -103,7 +103,8 @@
100.20 * ``read(num_bytes=None)`` -- Read a number of bytes from the file.
100.21 * ``name`` -- The name of the uploaded file.
100.22 * ``size`` -- The size, in bytes, of the uploaded file.
100.23 - * ``chunks(chunk_size=None)`` -- A generator that yields sequential chunks of data.
100.24 + * ``chunks(chunk_size=None)`` -- A generator that yields sequential
100.25 + chunks of data.
100.26
100.27 See :ref:`topics-files` for more information.
100.28
100.29 @@ -229,9 +230,10 @@
100.30
100.31 .. versionadded:: 1.0
100.32
100.33 - Returns ``True`` if the request was made via an ``XMLHttpRequest``, by checking
100.34 - the ``HTTP_X_REQUESTED_WITH`` header for the string ``'XMLHttpRequest'``. The
100.35 - following major JavaScript libraries all send this header:
100.36 + Returns ``True`` if the request was made via an ``XMLHttpRequest``, by
100.37 + checking the ``HTTP_X_REQUESTED_WITH`` header for the string
100.38 + ``'XMLHttpRequest'``. The following major JavaScript libraries all send this
100.39 + header:
100.40
100.41 * jQuery
100.42 * Dojo
100.43 @@ -317,6 +319,17 @@
100.44 >>> q = QueryDict('a=1&a=2&a=3')
100.45 >>> q.items()
100.46 [('a', '3')]
100.47 +
100.48 +.. method:: QueryDict.iteritems()
100.49 +
100.50 + Just like the standard dictionary ``iteritems()`` method. Like
100.51 + :meth:`QueryDict.items()` this uses the same last-value logic as
100.52 + :meth:`QueryDict.__getitem()__`.
100.53 +
100.54 +.. method:: QueryDict.iterlists()
100.55 +
100.56 + Like :meth:`QueryDict.iteritems()` except it includes all values, as a list,
100.57 + for each member of the dictionary.
100.58
100.59 .. method:: QueryDict.values()
100.60
100.61 @@ -327,6 +340,10 @@
100.62 >>> q.values()
100.63 ['3']
100.64
100.65 +.. method:: QueryDict.itervalues()
100.66 +
100.67 + Just like :meth:`QueryDict.values()`, except an iterator.
100.68 +
100.69 In addition, ``QueryDict`` has the following methods:
100.70
100.71 .. method:: QueryDict.copy()
101.1 --- a/docs/ref/signals.txt Sat Mar 28 12:05:48 2009 -0500
101.2 +++ b/docs/ref/signals.txt Wed Apr 01 13:05:19 2009 -0500
101.3 @@ -30,6 +30,10 @@
101.4 If you override these methods on your model, you must call the parent class'
101.5 methods for this signals to be sent.
101.6
101.7 + Note also that Django stores signal handlers as weak references by default,
101.8 + so if your handler is a local function, it may be garbage collected. To
101.9 + prevent this, pass ``weak=False`` when you call the signal's :meth:`~django.dispatch.Signal.connect`.
101.10 +
101.11 pre_init
101.12 --------
101.13
102.1 --- a/docs/ref/templates/builtins.txt Sat Mar 28 12:05:48 2009 -0500
102.2 +++ b/docs/ref/templates/builtins.txt Wed Apr 01 13:05:19 2009 -0500
102.3 @@ -866,6 +866,13 @@
102.4 ``datetime.datetime.now()``), the output will be the string
102.5 ``'Wed 09 Jan 2008'``.
102.6
102.7 +When used without a format string::
102.8 +
102.9 + {{ value|date }}
102.10 +
102.11 +...the formatting string defined in the :setting:`DATE_FORMAT` setting will be
102.12 +used.
102.13 +
102.14 .. templatefilter:: default
102.15
102.16 default
102.17 @@ -1424,6 +1431,13 @@
102.18 If ``value`` is equivalent to ``datetime.datetime.now()``, the output will be
102.19 the string ``"01:23"``.
102.20
102.21 +When used without a format string::
102.22 +
102.23 + {{ value|time }}
102.24 +
102.25 +...the formatting string defined in the :setting:`TIME_FORMAT` setting will be
102.26 +used.
102.27 +
102.28 .. templatefilter:: timesince
102.29
102.30 timesince
103.1 --- a/docs/releases/1.1-alpha-1.txt Sat Mar 28 12:05:48 2009 -0500
103.2 +++ b/docs/releases/1.1-alpha-1.txt Wed Apr 01 13:05:19 2009 -0500
103.3 @@ -82,7 +82,7 @@
103.4 (sending admin requests to the ``admin.site.root`` view still works, but URLs
103.5 in the admin will not be "reversible" when configured this way).
103.6
103.7 -* The ``include()`` function in Django URLConf modules can now accept sequences
103.8 +* The ``include()`` function in Django URLconf modules can now accept sequences
103.9 of URL patterns (generated by ``patterns()``) in addition to module names.
103.10
103.11 * Instances of Django forms (see `the forms overview <topics-forms-index>`_ now
104.1 --- a/docs/topics/auth.txt Sat Mar 28 12:05:48 2009 -0500
104.2 +++ b/docs/topics/auth.txt Wed Apr 01 13:05:19 2009 -0500
104.3 @@ -463,6 +463,12 @@
104.4 instance of the user profile model associated with that
104.5 :class:`~django.contrib.auth.models.User`.
104.6
104.7 +The method :class:`~django.contrib.auth.models.User.get_profile()`
104.8 +does not create the profile, if it does not exist. You need to
104.9 +register a handler for the signal
104.10 +:attr:`django.db.models.signals.post_save` on the User model, and, in
104.11 +the handler, if created=True, create the associated user profile.
104.12 +
104.13 For more information, see `Chapter 12 of the Django book`_.
104.14
104.15 .. _Chapter 12 of the Django book: http://www.djangobook.com/en/1.0/chapter12/#cn222
104.16 @@ -745,7 +751,7 @@
104.17 <p>Your username and password didn't match. Please try again.</p>
104.18 {% endif %}
104.19
104.20 - <form method="post" action=".">
104.21 + <form method="post" action="{% url django.contrib.auth.views.login %}">
104.22 <table>
104.23 <tr>
104.24 <td>{{ form.username.label_tag }}</td>
104.25 @@ -868,6 +874,34 @@
104.26 * ``login_url``: The URL of the login page to redirect to. This will
104.27 default to :setting:`settings.LOGIN_URL <LOGIN_URL>` if not supplied.
104.28
104.29 +.. function:: password_reset_confirm(request[,uidb36, token, template_name, token_generator, set_password_form, post_reset_redirect])
104.30 +
104.31 + Presents a form for entering a new password.
104.32 +
104.33 + **Optional arguments:**
104.34 +
104.35 + * ``uidb36``: The user's id encoded in base 36. This will default to
104.36 + ``None``.
104.37 + * ``token``: Token to check that the password is valid. This will default to ``None``.
104.38 + * ``template_name``: The full name of a template to display the confirm
104.39 + password view. Default value is :file:`registration/password_reset_confirm.html`.
104.40 + * ``token_generator``: Instance of the class to check the password. This
104.41 + will default to ``default_token_generator``, it's an instance of
104.42 + ``django.contrib.auth.tokens.PasswordResetTokenGenerator``.
104.43 + * ``set_password_form``: Form that will use to set the password. This will
104.44 + default to ``SetPasswordForm``.
104.45 + * ``post_reset_redirect``: URL to redirect after the password reset
104.46 + done. This will default to ``None``.
104.47 +
104.48 +.. function:: password_reset_complete(request[,template_name])
104.49 +
104.50 + Presents a view that informs that the password has been changed very well.
104.51 +
104.52 + **Optional arguments:**
104.53 +
104.54 + * ``template_name``: The full name of a template to display the view.
104.55 + This will default to :file:`registration/password_reset_complete.html`.
104.56 +
104.57 Built-in forms
104.58 --------------
104.59
104.60 @@ -1125,10 +1159,10 @@
104.61 Users
104.62 -----
104.63
104.64 -The currently logged-in user, either a
104.65 -:class:`~django.contrib.auth.models.User` instance or an
104.66 -:class:`~django.contrib.auth.models.AnonymousUser` instance, is stored in the
104.67 -template variable ``{{ user }}``:
104.68 +When rendering a template :class:`~django.template.context.RequestContext`, the
104.69 +currently logged-in user, either a :class:`~django.contrib.auth.models.User`
104.70 +instance or an :class:`~django.contrib.auth.models.AnonymousUser` instance, is
104.71 +stored in the template variable ``{{ user }}``:
104.72
104.73 .. code-block:: html
104.74
104.75 @@ -1138,6 +1172,9 @@
104.76 <p>Welcome, new user. Please log in.</p>
104.77 {% endif %}
104.78
104.79 +This template context variable is not available if a ``RequestContext`` is not
104.80 +being used.
104.81 +
104.82 Permissions
104.83 -----------
104.84
105.1 --- a/docs/topics/db/aggregation.txt Sat Mar 28 12:05:48 2009 -0500
105.2 +++ b/docs/topics/db/aggregation.txt Wed Apr 01 13:05:19 2009 -0500
105.3 @@ -146,12 +146,11 @@
105.4 model being queried. However, sometimes the value you want to aggregate
105.5 will belong to a model that is related to the model you are querying.
105.6
105.7 -When specifying the field to be aggregated in an aggregate functions,
105.8 -Django will allow you to use the same
105.9 -:ref:`double underscore notation <field-lookups-intro>` that is used
105.10 -when referring to related fields in filters. Django will then handle
105.11 -any table joins that are required to retrieve and aggregate the
105.12 -related value.
105.13 +When specifying the field to be aggregated in an aggregate function, Django
105.14 +will allow you to use the same :ref:`double underscore notation
105.15 +<field-lookups-intro>` that is used when referring to related fields in
105.16 +filters. Django will then handle any table joins that are required to retrieve
105.17 +and aggregate the related value.
105.18
105.19 For example, to find the price range of books offered in each store,
105.20 you could use the annotation::
106.1 --- a/docs/topics/db/models.txt Sat Mar 28 12:05:48 2009 -0500
106.2 +++ b/docs/topics/db/models.txt Wed Apr 01 13:05:19 2009 -0500
106.3 @@ -1,8 +1,8 @@
106.4 .. _topics-db-models:
106.5
106.6 -==============
106.7 -Writing models
106.8 -==============
106.9 +======
106.10 +Models
106.11 +======
106.12
106.13 .. module:: django.db.models
106.14
106.15 @@ -871,7 +871,7 @@
106.16 name will end up being different. For example::
106.17
106.18 class Base(models.Model):
106.19 - m2m = models.ManyToMany(OtherModel, related_name="%(class)s_related")
106.20 + m2m = models.ManyToManyField(OtherModel, related_name="%(class)s_related")
106.21
106.22 class Meta:
106.23 abstract = True
106.24 @@ -1019,10 +1019,11 @@
106.25 original.
106.26
106.27 Proxy models are declared like normal models. You tell Django that it's a
106.28 -proxy model by setting the :attr:`~django.db.models.Options.proxy` attribute to of the ``Meta`` class to ``True``.
106.29 +proxy model by setting the :attr:`~django.db.models.Options.proxy` attribute of
106.30 +the ``Meta`` class to ``True``.
106.31
106.32 For example, suppose you want to add a method to the standard ``User`` model
106.33 -that will make be used in your templates. You can do it like this::
106.34 +that will be used in your templates. You can do it like this::
106.35
106.36 from django.contrib.auth.models import User
106.37
107.1 --- a/docs/topics/forms/formsets.txt Sat Mar 28 12:05:48 2009 -0500
107.2 +++ b/docs/topics/forms/formsets.txt Wed Apr 01 13:05:19 2009 -0500
107.3 @@ -133,6 +133,20 @@
107.4 you are adding new forms via JavaScript, you should increment the count fields
107.5 in this form as well.
107.6
107.7 +.. versionadded:: 1.1
107.8 +
107.9 +``total_form_count`` and ``initial_form_count``
107.10 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
107.11 +
107.12 +``BaseModelFormSet`` has a couple of methods that are closely related to the
107.13 +``ManagementForm``, ``total_form_count`` and ``initial_form_count``.
107.14 +
107.15 +``total_form_count`` returns the total number of forms in this formset.
107.16 +``initial_form_count`` returns the number of forms in the formset that were
107.17 +pre-filled, and is also used to determine how many forms are required. You
107.18 +will probably never need to override either of these methods, so please be
107.19 +sure you understand what they do before doing so.
107.20 +
107.21 Custom formset validation
107.22 ~~~~~~~~~~~~~~~~~~~~~~~~~
107.23
108.1 --- a/docs/topics/forms/index.txt Sat Mar 28 12:05:48 2009 -0500
108.2 +++ b/docs/topics/forms/index.txt Wed Apr 01 13:05:19 2009 -0500
108.3 @@ -293,7 +293,7 @@
108.4 message.
108.5
108.6 ``field.is_hidden``
108.7 - This attribute is ``True`` is the form field is a hidden field and
108.8 + This attribute is ``True`` if the form field is a hidden field and
108.9 ``False`` otherwise. It's not particularly useful as a template
108.10 variable, but could be useful in conditional tests such as::
108.11
109.1 --- a/docs/topics/http/shortcuts.txt Sat Mar 28 12:05:48 2009 -0500
109.2 +++ b/docs/topics/http/shortcuts.txt Wed Apr 01 13:05:19 2009 -0500
109.3 @@ -26,7 +26,7 @@
109.4 ------------------
109.5
109.6 ``template``
109.7 - The full name of a template to use.
109.8 + The full name of a template to use or sequence of template names.
109.9
109.10 Optional arguments
109.11 ------------------
109.12 @@ -76,7 +76,7 @@
109.13 # View code here...
109.14 t = loader.get_template('myapp/template.html')
109.15 c = Context({'foo': 'bar'})
109.16 - r = HttpResponse(t.render(c),
109.17 + return HttpResponse(t.render(c),
109.18 mimetype="application/xhtml+xml")
109.19
109.20 ``redirect``
110.1 --- a/docs/topics/http/urls.txt Sat Mar 28 12:05:48 2009 -0500
110.2 +++ b/docs/topics/http/urls.txt Wed Apr 01 13:05:19 2009 -0500
110.3 @@ -623,16 +623,50 @@
110.4 .. admonition:: Make sure your views are all correct
110.5
110.6 As part of working out which URL names map to which patterns, the
110.7 - ``reverse()`` function has to import all of your URLConf files and examine
110.8 + ``reverse()`` function has to import all of your URLconf files and examine
110.9 the name of each view. This involves importing each view function. If
110.10 there are *any* errors whilst importing any of your view functions, it
110.11 will cause ``reverse()`` to raise an error, even if that view function is
110.12 not the one you are trying to reverse.
110.13
110.14 - Make sure that any views you reference in your URLConf files exist and can
110.15 + Make sure that any views you reference in your URLconf files exist and can
110.16 be imported correctly. Do not include lines that reference views you
110.17 haven't written yet, because those views will not be importable.
110.18
110.19 +resolve()
110.20 +---------
110.21 +
110.22 +The :func:`django.core.urlresolvers.resolve` function can be used for resolving
110.23 +URL paths to the corresponding view functions. It has the following signature:
110.24 +
110.25 +.. currentmodule:: django.core.urlresolvers
110.26 +.. function:: resolve(path, urlconf=None)
110.27 +
110.28 +``path`` is the URL path you want to resolve. As with ``reverse()`` above, you
110.29 +don't need to worry about the ``urlconf`` parameter. The function returns the
110.30 +triple (view function, arguments, keyword arguments).
110.31 +
110.32 +For example, it can be used for testing if a view would raise a ``Http404``
110.33 +error before redirecting to it::
110.34 +
110.35 + from urlparse import urlparse
110.36 + from django.core.urlresolvers import resolve
110.37 + from django.http import HttpResponseRedirect, Http404
110.38 +
110.39 + def myview(request):
110.40 + next = request.META.get('HTTP_REFERER', None) or '/'
110.41 + response = HttpResponseRedirect(next)
110.42 +
110.43 + # modify the request and response as required, e.g. change locale
110.44 + # and set corresponding locale cookie
110.45 +
110.46 + view, args, kwargs = resolve(urlparse(next)[2])
110.47 + kwargs['request'] = request
110.48 + try:
110.49 + view(*args, **kwargs)
110.50 + except Http404:
110.51 + return HttpResponseRedirect('/')
110.52 + return response
110.53
110.54 permalink()
110.55 -----------
111.1 --- a/docs/topics/install.txt Sat Mar 28 12:05:48 2009 -0500
111.2 +++ b/docs/topics/install.txt Wed Apr 01 13:05:19 2009 -0500
111.3 @@ -80,7 +80,7 @@
111.4 * If you're using SQLite and either Python 2.3 or Python 2.4, you'll need
111.5 pysqlite_. Use version 2.0.3 or higher. Python 2.5 ships with an SQLite
111.6 wrapper in the standard library, so you don't need to install anything extra
111.7 - in that case.
111.8 + in that case. Please read the SQLite backend :ref:`notes<sqlite-notes>`.
111.9
111.10 * If you're using Oracle, you'll need a copy of cx_Oracle_, but please
111.11 read the database-specific notes for the
111.12 @@ -106,7 +106,7 @@
111.13 .. _compiled Windows version: http://stickpeople.com/projects/python/win-psycopg/
111.14 .. _MySQLdb: http://sourceforge.net/projects/mysql-python
111.15 .. _SQLite: http://www.sqlite.org/
111.16 -.. _pysqlite: http://initd.org/pub/software/pysqlite/
111.17 +.. _pysqlite: http://pysqlite.org/
111.18 .. _cx_Oracle: http://cx-oracle.sourceforge.net/
111.19 .. _Oracle: http://www.oracle.com/
111.20
111.21 @@ -132,14 +132,14 @@
111.22 The location of the ``site-packages`` directory depends on the operating
111.23 system, and the location in which Python was installed. To find out your
111.24 system's ``site-packages`` location, execute the following:
111.25 -
111.26 +
111.27 .. code-block:: bash
111.28
111.29 python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()"
111.30
111.31 (Note that this should be run from a shell prompt, not a Python interactive
111.32 prompt.)
111.33 -
111.34 +
111.35 .. _install-django-code:
111.36
111.37 Install the Django code
111.38 @@ -190,7 +190,7 @@
111.39 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
111.40
111.41 .. admonition:: Tracking Django development
111.42 -
111.43 +
111.44 If you decide to use the latest development version of Django,
111.45 you'll want to pay close attention to `the development timeline`_,
111.46 and you'll want to keep an eye on `the list of
111.47 @@ -219,7 +219,7 @@
111.48 3. Next, make sure that the Python interpreter can load Django's code. There
111.49 are various ways of accomplishing this. One of the most convenient, on
111.50 Linux, Mac OSX or other Unix-like systems, is to use a symbolic link:
111.51 -
111.52 +
111.53 .. code-block:: bash
111.54
111.55 ln -s `pwd`/django-trunk/django SITE-PACKAGES-DIR/django
111.56 @@ -248,7 +248,7 @@
111.57 4. On Unix-like systems, create a symbolic link to the file
111.58 ``django-trunk/django/bin/django-admin.py`` in a directory on your system
111.59 path, such as ``/usr/local/bin``. For example:
111.60 -
111.61 +
111.62 .. code-block:: bash
111.63
111.64 ln -s `pwd`/django-trunk/django/bin/django-admin.py /usr/local/bin
112.1 --- a/docs/topics/testing.txt Sat Mar 28 12:05:48 2009 -0500
112.2 +++ b/docs/topics/testing.txt Wed Apr 01 13:05:19 2009 -0500
112.3 @@ -138,10 +138,11 @@
112.4 In the case of model tests, note that the test runner takes care of creating
112.5 its own test database. That is, any test that accesses a database -- by
112.6 creating and saving model instances, for example -- will not affect your
112.7 -production database. Each doctest begins with a "blank slate" -- a fresh
112.8 -database containing an empty table for each model. (See the section on
112.9 -fixtures, below, for more on this.) Note that to use this feature, the database
112.10 -user Django is connecting as must have ``CREATE DATABASE`` rights.
112.11 +production database. However, the database is not refreshed between doctests,
112.12 +so if your doctest requires a certain state you should consider flushin the
112.13 +database or loading a fixture. (See the section on fixtures, below, for more
112.14 +on this.) Note that to use this feature, the database user Django is connecting
112.15 +as must have ``CREATE DATABASE`` rights.
112.16
112.17 For more details about how doctest works, see the `standard library
112.18 documentation for doctest`_.
113.1 --- a/tests/modeltests/many_to_many/models.py Sat Mar 28 12:05:48 2009 -0500
113.2 +++ b/tests/modeltests/many_to_many/models.py Wed Apr 01 13:05:19 2009 -0500
113.3 @@ -61,6 +61,12 @@
113.4 # Adding a second time is OK
113.5 >>> a2.publications.add(p3)
113.6
113.7 +# Adding an object of the wrong type raises TypeError
113.8 +>>> a2.publications.add(a1)
113.9 +Traceback (most recent call last):
113.10 +...
113.11 +TypeError: 'Publication' instance expected
113.12 +
113.13 # Add a Publication directly via publications.add by using keyword arguments.
113.14 >>> new_publication = a2.publications.create(title='Highlights for Children')
113.15
114.1 --- a/tests/modeltests/many_to_one/models.py Sat Mar 28 12:05:48 2009 -0500
114.2 +++ b/tests/modeltests/many_to_one/models.py Wed Apr 01 13:05:19 2009 -0500
114.3 @@ -72,6 +72,13 @@
114.4 >>> r2.article_set.add(new_article2)
114.5 >>> new_article2.reporter.id
114.6 2
114.7 +
114.8 +# Adding an object of the wrong type raises TypeError
114.9 +>>> r.article_set.add(r2)
114.10 +Traceback (most recent call last):
114.11 +...
114.12 +TypeError: 'Article' instance expected
114.13 +
114.14 >>> r.article_set.all()
114.15 [<Article: John's second story>, <Article: This is a test>]
114.16 >>> r2.article_set.all()
115.1 --- a/tests/modeltests/model_forms/models.py Sat Mar 28 12:05:48 2009 -0500
115.2 +++ b/tests/modeltests/model_forms/models.py Wed Apr 01 13:05:19 2009 -0500
115.3 @@ -93,7 +93,7 @@
115.4
115.5 class TextFile(models.Model):
115.6 description = models.CharField(max_length=20)
115.7 - file = models.FileField(storage=temp_storage, upload_to='tests')
115.8 + file = models.FileField(storage=temp_storage, upload_to='tests', max_length=15)
115.9
115.10 def __unicode__(self):
115.11 return self.description
115.12 @@ -857,6 +857,10 @@
115.13 Traceback (most recent call last):
115.14 ...
115.15 ValidationError: [u'Enter a list of values.']
115.16 +>>> f.clean(['fail'])
115.17 +Traceback (most recent call last):
115.18 +...
115.19 +ValidationError: [u'"fail" is not a valid value for a primary key.']
115.20
115.21 # Add a Category object *after* the ModelMultipleChoiceField has already been
115.22 # instantiated. This proves clean() checks the database during clean() rather
115.23 @@ -1018,6 +1022,11 @@
115.24 >>> instance.file
115.25 <FieldFile: tests/test1.txt>
115.26
115.27 +# Check if the max_length attribute has been inherited from the model.
115.28 +>>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test-maxlength.txt', 'hello world')})
115.29 +>>> f.is_valid()
115.30 +False
115.31 +
115.32 # Edit an instance that already has the file defined in the model. This will not
115.33 # save the file again, but leave it exactly as it is.
115.34
116.1 --- a/tests/modeltests/model_formsets/models.py Sat Mar 28 12:05:48 2009 -0500
116.2 +++ b/tests/modeltests/model_formsets/models.py Wed Apr 01 13:05:19 2009 -0500
116.3 @@ -1,6 +1,4 @@
116.4 -
116.5 import datetime
116.6 -
116.7 from django import forms
116.8 from django.db import models
116.9
116.10 @@ -27,7 +25,7 @@
116.11
116.12 def __unicode__(self):
116.13 return self.title
116.14 -
116.15 +
116.16 class BookWithCustomPK(models.Model):
116.17 my_pk = models.DecimalField(max_digits=5, decimal_places=0, primary_key=True)
116.18 author = models.ForeignKey(Author)
116.19 @@ -35,13 +33,13 @@
116.20
116.21 def __unicode__(self):
116.22 return u'%s: %s' % (self.my_pk, self.title)
116.23 -
116.24 +
116.25 class AlternateBook(Book):
116.26 notes = models.CharField(max_length=100)
116.27 -
116.28 +
116.29 def __unicode__(self):
116.30 return u'%s - %s' % (self.title, self.notes)
116.31 -
116.32 +
116.33 class AuthorMeeting(models.Model):
116.34 name = models.CharField(max_length=100)
116.35 authors = models.ManyToManyField(Author)
116.36 @@ -68,7 +66,7 @@
116.37 auto_id = models.AutoField(primary_key=True)
116.38 name = models.CharField(max_length=100)
116.39 place = models.ForeignKey(Place)
116.40 -
116.41 +
116.42 def __unicode__(self):
116.43 return "%s at %s" % (self.name, self.place)
116.44
116.45 @@ -81,7 +79,7 @@
116.46 class OwnerProfile(models.Model):
116.47 owner = models.OneToOneField(Owner, primary_key=True)
116.48 age = models.PositiveIntegerField()
116.49 -
116.50 +
116.51 def __unicode__(self):
116.52 return "%s is %d" % (self.owner.name, self.age)
116.53
116.54 @@ -114,17 +112,17 @@
116.55 # using inlineformset_factory.
116.56 class Repository(models.Model):
116.57 name = models.CharField(max_length=25)
116.58 -
116.59 +
116.60 def __unicode__(self):
116.61 return self.name
116.62
116.63 class Revision(models.Model):
116.64 repository = models.ForeignKey(Repository)
116.65 revision = models.CharField(max_length=40)
116.66 -
116.67 +
116.68 class Meta:
116.69 unique_together = (("repository", "revision"),)
116.70 -
116.71 +
116.72 def __unicode__(self):
116.73 return u"%s (%s)" % (self.revision, unicode(self.repository))
116.74
116.75 @@ -146,7 +144,21 @@
116.76 class Player(models.Model):
116.77 team = models.ForeignKey(Team, null=True)
116.78 name = models.CharField(max_length=100)
116.79 -
116.80 +
116.81 + def __unicode__(self):
116.82 + return self.name
116.83 +
116.84 +# Models for testing custom ModelForm save methods in formsets and inline formsets
116.85 +class Poet(models.Model):
116.86 + name = models.CharField(max_length=100)
116.87 +
116.88 + def __unicode__(self):
116.89 + return self.name
116.90 +
116.91 +class Poem(models.Model):
116.92 + poet = models.ForeignKey(Poet)
116.93 + name = models.CharField(max_length=100)
116.94 +
116.95 def __unicode__(self):
116.96 return self.name
116.97
116.98 @@ -337,13 +349,44 @@
116.99
116.100 >>> AuthorFormSet = modelformset_factory(Author, max_num=2)
116.101 >>> formset = AuthorFormSet(queryset=qs)
116.102 ->>> [sorted(x.items()) for x in formset.initial]
116.103 -[[('id', 1), ('name', u'Charles Baudelaire')], [('id', 3), ('name', u'Paul Verlaine')]]
116.104 +>>> [x.name for x in formset.get_queryset()]
116.105 +[u'Charles Baudelaire', u'Paul Verlaine']
116.106
116.107 >>> AuthorFormSet = modelformset_factory(Author, max_num=3)
116.108 >>> formset = AuthorFormSet(queryset=qs)
116.109 ->>> [sorted(x.items()) for x in formset.initial]
116.110 -[[('id', 1), ('name', u'Charles Baudelaire')], [('id', 3), ('name', u'Paul Verlaine')], [('id', 2), ('name', u'Walt Whitman')]]
116.111 +>>> [x.name for x in formset.get_queryset()]
116.112 +[u'Charles Baudelaire', u'Paul Verlaine', u'Walt Whitman']
116.113 +
116.114 +
116.115 +# ModelForm with a custom save method in a formset ###########################
116.116 +
116.117 +>>> class PoetForm(forms.ModelForm):
116.118 +... def save(self, commit=True):
116.119 +... # change the name to "Vladimir Mayakovsky" just to be a jerk.
116.120 +... author = super(PoetForm, self).save(commit=False)
116.121 +... author.name = u"Vladimir Mayakovsky"
116.122 +... if commit:
116.123 +... author.save()
116.124 +... return author
116.125 +
116.126 +>>> PoetFormSet = modelformset_factory(Poet, form=PoetForm)
116.127 +
116.128 +>>> data = {
116.129 +... 'form-TOTAL_FORMS': '3', # the number of forms rendered
116.130 +... 'form-INITIAL_FORMS': '0', # the number of forms with initial data
116.131 +... 'form-0-name': 'Walt Whitman',
116.132 +... 'form-1-name': 'Charles Baudelaire',
116.133 +... 'form-2-name': '',
116.134 +... }
116.135 +
116.136 +>>> qs = Poet.objects.all()
116.137 +>>> formset = PoetFormSet(data=data, queryset=qs)
116.138 +>>> formset.is_valid()
116.139 +True
116.140 +
116.141 +>>> formset.save()
116.142 +[<Poet: Vladimir Mayakovsky>, <Poet: Vladimir Mayakovsky>]
116.143 +
116.144
116.145 # Model inheritance in model formsets ########################################
116.146
116.147 @@ -553,6 +596,36 @@
116.148 [<AlternateBook: Flowers of Evil - English translation of Les Fleurs du Mal>]
116.149
116.150
116.151 +# ModelForm with a custom save method in an inline formset ###################
116.152 +
116.153 +>>> class PoemForm(forms.ModelForm):
116.154 +... def save(self, commit=True):
116.155 +... # change the name to "Brooklyn Bridge" just to be a jerk.
116.156 +... poem = super(PoemForm, self).save(commit=False)
116.157 +... poem.name = u"Brooklyn Bridge"
116.158 +... if commit:
116.159 +... poem.save()
116.160 +... return poem
116.161 +
116.162 +>>> PoemFormSet = inlineformset_factory(Poet, Poem, form=PoemForm)
116.163 +
116.164 +>>> data = {
116.165 +... 'poem_set-TOTAL_FORMS': '3', # the number of forms rendered
116.166 +... 'poem_set-INITIAL_FORMS': '0', # the number of forms with initial data
116.167 +... 'poem_set-0-name': 'The Cloud in Trousers',
116.168 +... 'poem_set-1-name': 'I',
116.169 +... 'poem_set-2-name': '',
116.170 +... }
116.171 +
116.172 +>>> poet = Poet.objects.create(name='Vladimir Mayakovsky')
116.173 +>>> formset = PoemFormSet(data=data, instance=poet)
116.174 +>>> formset.is_valid()
116.175 +True
116.176 +
116.177 +>>> formset.save()
116.178 +[<Poem: Brooklyn Bridge>, <Poem: Brooklyn Bridge>]
116.179 +
116.180 +
116.181 # Test a custom primary key ###################################################
116.182
116.183 We need to ensure that it is displayed
117.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
117.2 +++ b/tests/modeltests/model_formsets/tests.py Wed Apr 01 13:05:19 2009 -0500
117.3 @@ -0,0 +1,70 @@
117.4 +from django.test import TestCase
117.5 +from django.forms.models import modelformset_factory
117.6 +from modeltests.model_formsets.models import Poet, Poem
117.7 +
117.8 +class DeletionTests(TestCase):
117.9 + def test_deletion(self):
117.10 + PoetFormSet = modelformset_factory(Poet, can_delete=True)
117.11 + poet = Poet.objects.create(name='test')
117.12 + data = {
117.13 + 'form-TOTAL_FORMS': u'1',
117.14 + 'form-INITIAL_FORMS': u'1',
117.15 + 'form-0-id': u'1',
117.16 + 'form-0-name': u'test',
117.17 + 'form-0-DELETE': u'on',
117.18 + }
117.19 + formset = PoetFormSet(data, queryset=Poet.objects.all())
117.20 + formset.save()
117.21 + self.assertTrue(formset.is_valid())
117.22 + self.assertEqual(Poet.objects.count(), 0)
117.23 +
117.24 + def test_add_form_deletion_when_invalid(self):
117.25 + """
117.26 + Make sure that an add form that is filled out, but marked for deletion
117.27 + doesn't cause validation errors.
117.28 + """
117.29 + PoetFormSet = modelformset_factory(Poet, can_delete=True)
117.30 + data = {
117.31 + 'form-TOTAL_FORMS': u'1',
117.32 + 'form-INITIAL_FORMS': u'0',
117.33 + 'form-0-id': u'',
117.34 + 'form-0-name': u'x' * 1000,
117.35 + }
117.36 + formset = PoetFormSet(data, queryset=Poet.objects.all())
117.37 + # Make sure this form doesn't pass validation.
117.38 + self.assertEqual(formset.is_valid(), False)
117.39 + self.assertEqual(Poet.objects.count(), 0)
117.40 +
117.41 + # Then make sure that it *does* pass validation and delete the object,
117.42 + # even though the data isn't actually valid.
117.43 + data['form-0-DELETE'] = 'on'
117.44 + formset = PoetFormSet(data, queryset=Poet.objects.all())
117.45 + self.assertEqual(formset.is_valid(), True)
117.46 + formset.save()
117.47 + self.assertEqual(Poet.objects.count(), 0)
117.48 +
117.49 + def test_change_form_deletion_when_invalid(self):
117.50 + """
117.51 + Make sure that an add form that is filled out, but marked for deletion
117.52 + doesn't cause validation errors.
117.53 + """
117.54 + PoetFormSet = modelformset_factory(Poet, can_delete=True)
117.55 + poet = Poet.objects.create(name='test')
117.56 + data = {
117.57 + 'form-TOTAL_FORMS': u'1',
117.58 + 'form-INITIAL_FORMS': u'1',
117.59 + 'form-0-id': u'1',
117.60 + 'form-0-name': u'x' * 1000,
117.61 + }
117.62 + formset = PoetFormSet(data, queryset=Poet.objects.all())
117.63 + # Make sure this form doesn't pass validation.
117.64 + self.assertEqual(formset.is_valid(), False)
117.65 + self.assertEqual(Poet.objects.count(), 1)
117.66 +
117.67 + # Then make sure that it *does* pass validation and delete the object,
117.68 + # even though the data isn't actually valid.
117.69 + data['form-0-DELETE'] = 'on'
117.70 + formset = PoetFormSet(data, queryset=Poet.objects.all())
117.71 + self.assertEqual(formset.is_valid(), True)
117.72 + formset.save()
117.73 + self.assertEqual(Poet.objects.count(), 0)
118.1 --- a/tests/modeltests/one_to_one/models.py Sat Mar 28 12:05:48 2009 -0500
118.2 +++ b/tests/modeltests/one_to_one/models.py Wed Apr 01 13:05:19 2009 -0500
118.3 @@ -79,6 +79,8 @@
118.4 <Restaurant: Ace Hardware the restaurant>
118.5 >>> r.place
118.6 <Place: Ace Hardware the place>
118.7 +>>> p2.id
118.8 +2
118.9
118.10 # Set the place back again, using assignment in the reverse direction.
118.11 >>> p1.restaurant = r
119.1 --- a/tests/modeltests/test_client/models.py Sat Mar 28 12:05:48 2009 -0500
119.2 +++ b/tests/modeltests/test_client/models.py Wed Apr 01 13:05:19 2009 -0500
119.3 @@ -423,4 +423,3 @@
119.4 self.assertEqual(mail.outbox[1].from_email, 'from@example.com')
119.5 self.assertEqual(mail.outbox[1].to[0], 'second@example.com')
119.6 self.assertEqual(mail.outbox[1].to[1], 'third@example.com')
119.7 -
120.1 --- a/tests/regressiontests/admin_views/customadmin.py Sat Mar 28 12:05:48 2009 -0500
120.2 +++ b/tests/regressiontests/admin_views/customadmin.py Wed Apr 01 13:05:19 2009 -0500
120.3 @@ -28,3 +28,4 @@
120.4 site.register(models.Article, models.ArticleAdmin)
120.5 site.register(models.Section, inlines=[models.ArticleInline])
120.6 site.register(models.Thing, models.ThingAdmin)
120.7 +site.register(models.Fabric, models.FabricAdmin)
121.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
121.2 +++ b/tests/regressiontests/admin_views/fixtures/admin-views-fabrics.xml Wed Apr 01 13:05:19 2009 -0500
121.3 @@ -0,0 +1,12 @@
121.4 +<?xml version="1.0" encoding="utf-8"?>
121.5 +<django-objects version="1.0">
121.6 + <object pk="1" model="admin_views.fabric">
121.7 + <field type="CharField" name="surface">x</field>
121.8 + </object>
121.9 + <object pk="2" model="admin_views.fabric">
121.10 + <field type="CharField" name="surface">y</field>
121.11 + </object>
121.12 + <object pk="3" model="admin_views.fabric">
121.13 + <field type="CharField" name="surface">plain</field>
121.14 + </object>
121.15 +</django-objects>
122.1 --- a/tests/regressiontests/admin_views/models.py Sat Mar 28 12:05:48 2009 -0500
122.2 +++ b/tests/regressiontests/admin_views/models.py Wed Apr 01 13:05:19 2009 -0500
122.3 @@ -135,6 +135,21 @@
122.4 class ThingAdmin(admin.ModelAdmin):
122.5 list_filter = ('color',)
122.6
122.7 +class Fabric(models.Model):
122.8 + NG_CHOICES = (
122.9 + ('Textured', (
122.10 + ('x', 'Horizontal'),
122.11 + ('y', 'Vertical'),
122.12 + )
122.13 + ),
122.14 + ('plain', 'Smooth'),
122.15 + )
122.16 + surface = models.CharField(max_length=20, choices=NG_CHOICES)
122.17 +
122.18 +class FabricAdmin(admin.ModelAdmin):
122.19 + list_display = ('surface',)
122.20 + list_filter = ('surface',)
122.21 +
122.22 class Person(models.Model):
122.23 GENDER_CHOICES = (
122.24 (1, "Male"),
122.25 @@ -143,10 +158,10 @@
122.26 name = models.CharField(max_length=100)
122.27 gender = models.IntegerField(choices=GENDER_CHOICES)
122.28 alive = models.BooleanField()
122.29 -
122.30 +
122.31 def __unicode__(self):
122.32 return self.name
122.33 -
122.34 +
122.35 class Meta:
122.36 ordering = ["id"]
122.37
122.38 @@ -236,6 +251,40 @@
122.39 class ExternalSubscriberAdmin(admin.ModelAdmin):
122.40 actions = [external_mail, redirect_to]
122.41
122.42 +class Media(models.Model):
122.43 + name = models.CharField(max_length=60)
122.44 +
122.45 +class Podcast(Media):
122.46 + release_date = models.DateField()
122.47 +
122.48 +class PodcastAdmin(admin.ModelAdmin):
122.49 + list_display = ('name', 'release_date')
122.50 + list_editable = ('release_date',)
122.51 +
122.52 + ordering = ('name',)
122.53 +
122.54 +class Parent(models.Model):
122.55 + name = models.CharField(max_length=128)
122.56 +
122.57 +class Child(models.Model):
122.58 + parent = models.ForeignKey(Parent, editable=False)
122.59 + name = models.CharField(max_length=30, blank=True)
122.60 +
122.61 +class ChildInline(admin.StackedInline):
122.62 + model = Child
122.63 +
122.64 +class ParentAdmin(admin.ModelAdmin):
122.65 + model = Parent
122.66 + inlines = [ChildInline]
122.67 +
122.68 +class EmptyModel(models.Model):
122.69 + def __unicode__(self):
122.70 + return "Primary key = %s" % self.id
122.71 +
122.72 +class EmptyModelAdmin(admin.ModelAdmin):
122.73 + def queryset(self, request):
122.74 + return super(EmptyModelAdmin, self).queryset(request).filter(pk__gt=1)
122.75 +
122.76 admin.site.register(Article, ArticleAdmin)
122.77 admin.site.register(CustomArticle, CustomArticleAdmin)
122.78 admin.site.register(Section, inlines=[ArticleInline])
122.79 @@ -246,6 +295,10 @@
122.80 admin.site.register(Persona, PersonaAdmin)
122.81 admin.site.register(Subscriber, SubscriberAdmin)
122.82 admin.site.register(ExternalSubscriber, ExternalSubscriberAdmin)
122.83 +admin.site.register(Podcast, PodcastAdmin)
122.84 +admin.site.register(Parent, ParentAdmin)
122.85 +admin.site.register(EmptyModel, EmptyModelAdmin)
122.86 +admin.site.register(Fabric, FabricAdmin)
122.87
122.88 # We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2.
122.89 # That way we cover all four cases:
122.90 @@ -259,5 +312,3 @@
122.91 admin.site.register(Book, inlines=[ChapterInline])
122.92 admin.site.register(Promo)
122.93 admin.site.register(ChapterXtra1)
122.94 -
122.95 -
123.1 --- a/tests/regressiontests/admin_views/tests.py Sat Mar 28 12:05:48 2009 -0500
123.2 +++ b/tests/regressiontests/admin_views/tests.py Wed Apr 01 13:05:19 2009 -0500
123.3 @@ -1,6 +1,7 @@
123.4 # coding: utf-8
123.5
123.6 import re
123.7 +import datetime
123.8
123.9 from django.test import TestCase
123.10 from django.contrib.auth.models import User, Permission
123.11 @@ -12,7 +13,7 @@
123.12 from django.utils.html import escape
123.13
123.14 # local test models
123.15 -from models import Article, CustomArticle, Section, ModelWithStringPrimaryKey, Person, Persona, FooAccount, BarAccount, Subscriber, ExternalSubscriber
123.16 +from models import Article, CustomArticle, Section, ModelWithStringPrimaryKey, Person, Persona, FooAccount, BarAccount, Subscriber, ExternalSubscriber, Podcast, EmptyModel
123.17
123.18 try:
123.19 set
123.20 @@ -20,7 +21,7 @@
123.21 from sets import Set as set
123.22
123.23 class AdminViewBasicTest(TestCase):
123.24 - fixtures = ['admin-views-users.xml', 'admin-views-colors.xml']
123.25 + fixtures = ['admin-views-users.xml', 'admin-views-colors.xml', 'admin-views-fabrics.xml']
123.26
123.27 # Store the bit of the URL where the admin is registered as a class
123.28 # variable. That way we can test a second AdminSite just by subclassing
123.29 @@ -181,6 +182,37 @@
123.30 response = self.client.get('/test_admin/%s/admin_views/thing/' % self.urlbit, {'color__id__exact': 'StringNotInteger!'})
123.31 self.assertRedirects(response, '/test_admin/%s/admin_views/thing/?e=1' % self.urlbit)
123.32
123.33 + def testNamedGroupFieldChoicesChangeList(self):
123.34 + """
123.35 + Ensures the admin changelist shows correct values in the relevant column
123.36 + for rows corresponding to instances of a model in which a named group
123.37 + has been used in the choices option of a field.
123.38 + """
123.39 + response = self.client.get('/test_admin/%s/admin_views/fabric/' % self.urlbit)
123.40 + self.failUnlessEqual(response.status_code, 200)
123.41 + self.failUnless(
123.42 + '<a href="1/">Horizontal</a>' in response.content and
123.43 + '<a href="2/">Vertical</a>' in response.content,
123.44 + "Changelist table isn't showing the right human-readable values set by a model field 'choices' option named group."
123.45 + )
123.46 +
123.47 + def testNamedGroupFieldChoicesFilter(self):
123.48 + """
123.49 + Ensures the filter UI shows correctly when at least one named group has
123.50 + been used in the choices option of a model field.
123.51 + """
123.52 + response = self.client.get('/test_admin/%s/admin_views/fabric/' % self.urlbit)
123.53 + self.failUnlessEqual(response.status_code, 200)
123.54 + self.failUnless(
123.55 + '<div id="changelist-filter">' in response.content,
123.56 + "Expected filter not found in changelist view."
123.57 + )
123.58 + self.failUnless(
123.59 + '<a href="?surface__exact=x">Horizontal</a>' in response.content and
123.60 + '<a href="?surface__exact=y">Vertical</a>' in response.content,
123.61 + "Changelist filter isn't showing options contained inside a model field 'choices' option named group."
123.62 + )
123.63 +
123.64 class CustomModelAdminTest(AdminViewBasicTest):
123.65 urlbit = "admin2"
123.66
123.67 @@ -740,6 +772,12 @@
123.68 def tearDown(self):
123.69 self.client.logout()
123.70
123.71 + def test_inheritance(self):
123.72 + Podcast.objects.create(name="This Week in Django",
123.73 + release_date=datetime.date.today())
123.74 + response = self.client.get('/test_admin/admin/admin_views/podcast/')
123.75 + self.failUnlessEqual(response.status_code, 200)
123.76 +
123.77 def test_changelist_input_html(self):
123.78 response = self.client.get('/test_admin/admin/admin_views/person/')
123.79 # 2 inputs per object(the field and the hidden id field) = 6
123.80 @@ -939,3 +977,44 @@
123.81 }
123.82 response = self.client.post('/test_admin/admin/admin_views/externalsubscriber/', action_data)
123.83 self.failUnlessEqual(response.status_code, 302)
123.84 +
123.85 +class TestInlineNotEditable(TestCase):
123.86 + fixtures = ['admin-views-users.xml']
123.87 +
123.88 + def setUp(self):
123.89 + result = self.client.login(username='super', password='secret')
123.90 + self.failUnlessEqual(result, True)
123.91 +
123.92 + def tearDown(self):
123.93 + self.client.logout()
123.94 +
123.95 + def test(self):
123.96 + """
123.97 + InlineModelAdmin broken?
123.98 + """
123.99 + response = self.client.get('/test_admin/admin/admin_views/parent/add/')
123.100 + self.failUnlessEqual(response.status_code, 200)
123.101 +
123.102 +
123.103 +class AdminCustomQuerysetTest(TestCase):
123.104 + fixtures = ['admin-views-users.xml']
123.105 +
123.106 + def setUp(self):
123.107 + self.client.login(username='super', password='secret')
123.108 + self.pks = [EmptyModel.objects.create().id for i in range(3)]
123.109 +
123.110 + def test_changelist_view(self):
123.111 + response = self.client.get('/test_admin/admin/admin_views/emptymodel/')
123.112 + for i in self.pks:
123.113 + if i > 1:
123.114 + self.assertContains(response, 'Primary key = %s' % i)
123.115 + else:
123.116 + self.assertNotContains(response, 'Primary key = %s' % i)
123.117 +
123.118 + def test_change_view(self):
123.119 + for i in self.pks:
123.120 + response = self.client.get('/test_admin/admin/admin_views/emptymodel/%s/' % i)
123.121 + if i > 1:
123.122 + self.assertEqual(response.status_code, 200)
123.123 + else:
123.124 + self.assertEqual(response.status_code, 404)
124.1 --- a/tests/regressiontests/admin_widgets/tests.py Sat Mar 28 12:05:48 2009 -0500
124.2 +++ b/tests/regressiontests/admin_widgets/tests.py Wed Apr 01 13:05:19 2009 -0500
124.3 @@ -110,3 +110,16 @@
124.4 response = self.client.get("/widget_admin/admin_widgets/cartire/add/")
124.5 self.assert_("BMW M3" not in response.content)
124.6 self.assert_("Volkswagon Passat" in response.content)
124.7 +
124.8 +class AdminForeignKeyWidgetChangeList(DjangoTestCase):
124.9 + fixtures = ["admin-widgets-users.xml"]
124.10 +
124.11 + def setUp(self):
124.12 + self.client.login(username="super", password="secret")
124.13 +
124.14 + def tearDown(self):
124.15 + self.client.logout()
124.16 +
124.17 + def test_changelist_foreignkey(self):
124.18 + response = self.client.get('/widget_admin/admin_widgets/car/')
124.19 + self.failUnless('/widget_admin/auth/user/add/' in response.content)
125.1 --- a/tests/regressiontests/admin_widgets/widgetadmin.py Sat Mar 28 12:05:48 2009 -0500
125.2 +++ b/tests/regressiontests/admin_widgets/widgetadmin.py Wed Apr 01 13:05:19 2009 -0500
125.3 @@ -8,6 +8,9 @@
125.4 class WidgetAdmin(admin.AdminSite):
125.5 pass
125.6
125.7 +class CarAdmin(admin.ModelAdmin):
125.8 + list_display = ['make', 'model', 'owner']
125.9 + list_editable = ['owner']
125.10
125.11 class CarTireAdmin(admin.ModelAdmin):
125.12 def formfield_for_foreignkey(self, db_field, request, **kwargs):
125.13 @@ -18,5 +21,6 @@
125.14
125.15 site = WidgetAdmin()
125.16
125.17 -site.register(models.Car)
125.18 +site.register(models.User)
125.19 +site.register(models.Car, CarAdmin)
125.20 site.register(models.CarTire, CarTireAdmin)
126.1 --- a/tests/regressiontests/backends/tests.py Sat Mar 28 12:05:48 2009 -0500
126.2 +++ b/tests/regressiontests/backends/tests.py Wed Apr 01 13:05:19 2009 -0500
126.3 @@ -20,15 +20,12 @@
126.4 >>> cursor = connection.cursor()
126.5 """
126.6 # -*- coding: utf-8 -*-
126.7 -
126.8 -# Unit tests for specific database backends.
126.9 -
126.10 +# Unit and doctests for specific database backends.
126.11 import unittest
126.12 -
126.13 from django.db import connection
126.14 +from django.db.backends.signals import connection_created
126.15 from django.conf import settings
126.16
126.17 -
126.18 class Callproc(unittest.TestCase):
126.19
126.20 def test_dbms_session(self):
126.21 @@ -42,6 +39,23 @@
126.22 else:
126.23 return True
126.24
126.25 +def connection_created_test(sender, **kwargs):
126.26 + print 'connection_created signal'
126.27 +
126.28 +__test__ = {'API_TESTS': ''}
126.29 +
126.30 +# Unfortunately with sqlite3 the in-memory test database cannot be
126.31 +# closed, and so it cannot be re-opened during testing, and so we
126.32 +# sadly disable this test for now.
126.33 +if settings.DATABASE_ENGINE != 'sqlite3':
126.34 + __test__['API_TESTS'] += """
126.35 +>>> connection_created.connect(connection_created_test)
126.36 +>>> connection.close() # Ensure the connection is closed
126.37 +>>> cursor = connection.cursor()
126.38 +connection_created signal
126.39 +>>> connection_created.disconnect(connection_created_test)
126.40 +>>> cursor = connection.cursor()
126.41 +"""
126.42
126.43 if __name__ == '__main__':
126.44 unittest.main()
127.1 --- a/tests/regressiontests/datastructures/tests.py Sat Mar 28 12:05:48 2009 -0500
127.2 +++ b/tests/regressiontests/datastructures/tests.py Wed Apr 01 13:05:19 2009 -0500
127.3 @@ -45,6 +45,8 @@
127.4 ['Adrian', 'Simon']
127.5 >>> list(d.iteritems())
127.6 [('position', 'Developer'), ('name', 'Simon')]
127.7 +>>> list(d.iterlists())
127.8 +[('position', ['Developer']), ('name', ['Adrian', 'Simon'])]
127.9 >>> d['lastname']
127.10 Traceback (most recent call last):
127.11 ...
127.12 @@ -58,6 +60,10 @@
127.13 >>> d.setlist('lastname', ['Holovaty', 'Willison'])
127.14 >>> d.getlist('lastname')
127.15 ['Holovaty', 'Willison']
127.16 +>>> d.values()
127.17 +['Developer', 'Simon', 'Willison']
127.18 +>>> list(d.itervalues())
127.19 +['Developer', 'Simon', 'Willison']
127.20
127.21 ### SortedDict #################################################################
127.22
128.1 --- a/tests/regressiontests/decorators/tests.py Sat Mar 28 12:05:48 2009 -0500
128.2 +++ b/tests/regressiontests/decorators/tests.py Wed Apr 01 13:05:19 2009 -0500
128.3 @@ -29,6 +29,7 @@
128.4 fully_decorated = never_cache(fully_decorated)
128.5
128.6 # django.contrib.auth.decorators
128.7 +# Apply user_passes_test twice to check #9474
128.8 fully_decorated = user_passes_test(lambda u:True)(fully_decorated)
128.9 fully_decorated = login_required(fully_decorated)
128.10 fully_decorated = permission_required('change_world')(fully_decorated)
128.11 @@ -54,3 +55,33 @@
128.12 self.assertEquals(fully_decorated.__name__, 'fully_decorated')
128.13 self.assertEquals(fully_decorated.__doc__, 'Expected __doc__')
128.14 self.assertEquals(fully_decorated.__dict__['anything'], 'Expected __dict__')
128.15 +
128.16 + def test_user_passes_test_composition(self):
128.17 + """
128.18 + Test that the user_passes_test decorator can be applied multiple times
128.19 + (#9474).
128.20 + """
128.21 + def test1(user):
128.22 + user.decorators_applied.append('test1')
128.23 + return True
128.24 +
128.25 + def test2(user):
128.26 + user.decorators_applied.append('test2')
128.27 + return True
128.28 +
128.29 + def callback(request):
128.30 + return request.user.decorators_applied
128.31 +
128.32 + callback = user_passes_test(test1)(callback)
128.33 + callback = user_passes_test(test2)(callback)
128.34 +
128.35 + class DummyUser(object): pass
128.36 + class DummyRequest(object): pass
128.37 +
128.38 + request = DummyRequest()
128.39 + request.user = DummyUser()
128.40 + request.user.decorators_applied = []
128.41 + response = callback(request)
128.42 +
128.43 + self.assertEqual(response, ['test2', 'test1'])
128.44 +
129.1 --- a/tests/regressiontests/defaultfilters/tests.py Sat Mar 28 12:05:48 2009 -0500
129.2 +++ b/tests/regressiontests/defaultfilters/tests.py Wed Apr 01 13:05:19 2009 -0500
129.3 @@ -35,8 +35,8 @@
129.4 u''
129.5 >>> floatformat(13.1031, u'bar')
129.6 u'13.1031'
129.7 ->>> floatformat(18.125, 2)
129.8 -u'18.13'
129.9 +>>> floatformat(18.125, 2)
129.10 +u'18.13'
129.11 >>> floatformat(u'foo', u'bar')
129.12 u''
129.13 >>> floatformat(u'¿Cómo esta usted?')
129.14 @@ -53,6 +53,15 @@
129.15 >>> floatformat(nan) == unicode(nan)
129.16 True
129.17
129.18 +>>> class FloatWrapper(object):
129.19 +... def __init__(self, value):
129.20 +... self.value = value
129.21 +... def __float__(self):
129.22 +... return self.value
129.23 +
129.24 +>>> floatformat(FloatWrapper(11.000001), -2)
129.25 +u'11.00'
129.26 +
129.27 >>> addslashes(u'"double quotes" and \'single quotes\'')
129.28 u'\\"double quotes\\" and \\\'single quotes\\\''
129.29
129.30 @@ -180,23 +189,23 @@
129.31 u'<a href="http://31characteruri.com/test/" rel="nofollow">...</a>'
129.32
129.33 # Check normal urlize
129.34 ->>> urlize('http://google.com')
129.35 +>>> urlize('http://google.com')
129.36 u'<a href="http://google.com" rel="nofollow">http://google.com</a>'
129.37
129.38 ->>> urlize('http://google.com/')
129.39 +>>> urlize('http://google.com/')
129.40 u'<a href="http://google.com/" rel="nofollow">http://google.com/</a>'
129.41
129.42 ->>> urlize('www.google.com')
129.43 +>>> urlize('www.google.com')
129.44 u'<a href="http://www.google.com" rel="nofollow">www.google.com</a>'
129.45
129.46 ->>> urlize('djangoproject.org')
129.47 +>>> urlize('djangoproject.org')
129.48 u'<a href="http://djangoproject.org" rel="nofollow">djangoproject.org</a>'
129.49
129.50 ->>> urlize('info@djangoproject.org')
129.51 +>>> urlize('info@djangoproject.org')
129.52 u'<a href="mailto:info@djangoproject.org">info@djangoproject.org</a>'
129.53
129.54 # Check urlize with https addresses
129.55 ->>> urlize('https://google.com')
129.56 +>>> urlize('https://google.com')
129.57 u'<a href="https://google.com" rel="nofollow">https://google.com</a>'
129.58
129.59
130.1 --- a/tests/regressiontests/forms/fields.py Sat Mar 28 12:05:48 2009 -0500
130.2 +++ b/tests/regressiontests/forms/fields.py Wed Apr 01 13:05:19 2009 -0500
130.3 @@ -845,6 +845,21 @@
130.4 >>> type(f.clean(SimpleUploadedFile('name', 'Some File Content'), 'files/test4.pdf'))
130.5 <class 'django.core.files.uploadedfile.SimpleUploadedFile'>
130.6
130.7 +>>> f = FileField(max_length = 5)
130.8 +>>> f.clean(SimpleUploadedFile('test_maxlength.txt', 'hello world'))
130.9 +Traceback (most recent call last):
130.10 +...
130.11 +ValidationError: [u'Ensure this filename has at most 5 characters (it has 18).']
130.12 +
130.13 +>>> f.clean('', 'files/test1.pdf')
130.14 +'files/test1.pdf'
130.15 +
130.16 +>>> f.clean(None, 'files/test2.pdf')
130.17 +'files/test2.pdf'
130.18 +
130.19 +>>> type(f.clean(SimpleUploadedFile('name', 'Some File Content')))
130.20 +<class 'django.core.files.uploadedfile.SimpleUploadedFile'>
130.21 +
130.22 # URLField ##################################################################
130.23
130.24 >>> f = URLField()
131.1 --- a/tests/regressiontests/forms/formsets.py Sat Mar 28 12:05:48 2009 -0500
131.2 +++ b/tests/regressiontests/forms/formsets.py Wed Apr 01 13:05:19 2009 -0500
131.3 @@ -241,7 +241,7 @@
131.4
131.5 # FormSets with deletion ######################################################
131.6
131.7 -We can easily add deletion ability to a FormSet with an agrument to
131.8 +We can easily add deletion ability to a FormSet with an argument to
131.9 formset_factory. This will add a boolean field to each form instance. When
131.10 that boolean field is True, the form will be in formset.deleted_forms
131.11
131.12 @@ -286,6 +286,34 @@
131.13 >>> [form.cleaned_data for form in formset.deleted_forms]
131.14 [{'votes': 900, 'DELETE': True, 'choice': u'Fergie'}]
131.15
131.16 +If we fill a form with something and then we check the can_delete checkbox for
131.17 +that form, that form's errors should not make the entire formset invalid since
131.18 +it's going to be deleted.
131.19 +
131.20 +>>> class CheckForm(Form):
131.21 +... field = IntegerField(min_value=100)
131.22 +
131.23 +>>> data = {
131.24 +... 'check-TOTAL_FORMS': '3', # the number of forms rendered
131.25 +... 'check-INITIAL_FORMS': '2', # the number of forms with initial data
131.26 +... 'check-0-field': '200',
131.27 +... 'check-0-DELETE': '',
131.28 +... 'check-1-field': '50',
131.29 +... 'check-1-DELETE': 'on',
131.30 +... 'check-2-field': '',
131.31 +... 'check-2-DELETE': '',
131.32 +... }
131.33 +>>> CheckFormSet = formset_factory(CheckForm, can_delete=True)
131.34 +>>> formset = CheckFormSet(data, prefix='check')
131.35 +>>> formset.is_valid()
131.36 +True
131.37 +
131.38 +If we remove the deletion flag now we will have our validation back.
131.39 +
131.40 +>>> data['check-1-DELETE'] = ''
131.41 +>>> formset = CheckFormSet(data, prefix='check')
131.42 +>>> formset.is_valid()
131.43 +False
131.44
131.45 # FormSets with ordering ######################################################
131.46
132.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
132.2 +++ b/tests/regressiontests/formwizard/forms.py Wed Apr 01 13:05:19 2009 -0500
132.3 @@ -0,0 +1,18 @@
132.4 +from django import forms
132.5 +from django.contrib.formtools.wizard import FormWizard
132.6 +from django.http import HttpResponse
132.7 +
132.8 +class Page1(forms.Form):
132.9 + name = forms.CharField(max_length=100)
132.10 + thirsty = forms.NullBooleanField()
132.11 +
132.12 +class Page2(forms.Form):
132.13 + address1 = forms.CharField(max_length=100)
132.14 + address2 = forms.CharField(max_length=100)
132.15 +
132.16 +class Page3(forms.Form):
132.17 + random_crap = forms.CharField(max_length=100)
132.18 +
132.19 +class ContactWizard(FormWizard):
132.20 + def done(self, request, form_list):
132.21 + return HttpResponse("")
133.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
133.2 +++ b/tests/regressiontests/formwizard/templates/forms/wizard.html Wed Apr 01 13:05:19 2009 -0500
133.3 @@ -0,0 +1,13 @@
133.4 +<html>
133.5 + <body>
133.6 + <p>Step {{ step }} of {{ step_count }}</p>
133.7 + <form action="." method="post">
133.8 + <table>
133.9 + {{ form }}
133.10 + </table>
133.11 + <input type="hidden" name="{{ step_field }}" value="{{ step0 }}" />
133.12 + {{ previous_fields|safe }}
133.13 + <input type="submit">
133.14 + </form>
133.15 + </body>
133.16 +</html>
133.17 \ No newline at end of file
134.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
134.2 +++ b/tests/regressiontests/formwizard/tests.py Wed Apr 01 13:05:19 2009 -0500
134.3 @@ -0,0 +1,59 @@
134.4 +import re
134.5 +from django import forms
134.6 +from django.test import TestCase
134.7 +
134.8 +class FormWizardWithNullBooleanField(TestCase):
134.9 + urls = 'regressiontests.formwizard.urls'
134.10 +
134.11 + input_re = re.compile('name="([^"]+)" value="([^"]+)"')
134.12 +
134.13 + wizard_url = '/wiz/'
134.14 + wizard_step_data = (
134.15 + {
134.16 + '0-name': 'Pony',
134.17 + '0-thirsty': '2',
134.18 + },
134.19 + {
134.20 + '1-address1': '123 Main St',
134.21 + '1-address2': 'Djangoland',
134.22 + },
134.23 + {
134.24 + '2-random_crap': 'blah blah',
134.25 + }
134.26 + )
134.27 +
134.28 + def grabFieldData(self, response):
134.29 + """
134.30 + Pull the appropriate field data from the context to pass to the next wizard step
134.31 + """
134.32 + previous_fields = response.context['previous_fields']
134.33 + fields = {'wizard_step': response.context['step0']}
134.34 +
134.35 + def grab(m):
134.36 + fields[m.group(1)] = m.group(2)
134.37 + return ''
134.38 +
134.39 + self.input_re.sub(grab, previous_fields)
134.40 + return fields
134.41 +
134.42 + def checkWizardStep(self, response, step_no):
134.43 + """
134.44 + Helper function to test each step of the wizard
134.45 + - Make sure the call succeeded
134.46 + - Make sure response is the proper step number
134.47 + - return the result from the post for the next step
134.48 + """
134.49 + step_count = len(self.wizard_step_data)
134.50 +
134.51 + self.assertEqual(response.status_code, 200)
134.52 + self.assertContains(response, 'Step %d of %d' % (step_no, step_count))
134.53 +
134.54 + data = self.grabFieldData(response)
134.55 + data.update(self.wizard_step_data[step_no - 1])
134.56 +
134.57 + return self.client.post(self.wizard_url, data)
134.58 +
134.59 + def testWizard(self):
134.60 + response = self.client.get(self.wizard_url)
134.61 + for step_no in range(1, len(self.wizard_step_data) + 1):
134.62 + response = self.checkWizardStep(response, step_no)
135.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
135.2 +++ b/tests/regressiontests/formwizard/urls.py Wed Apr 01 13:05:19 2009 -0500
135.3 @@ -0,0 +1,6 @@
135.4 +from django.conf.urls.defaults import *
135.5 +from forms import ContactWizard, Page1, Page2, Page3
135.6 +
135.7 +urlpatterns = patterns('',
135.8 + url(r'^wiz/$', ContactWizard([Page1, Page2, Page3])),
135.9 + )
136.1 --- a/tests/regressiontests/httpwrappers/tests.py Sat Mar 28 12:05:48 2009 -0500
136.2 +++ b/tests/regressiontests/httpwrappers/tests.py Wed Apr 01 13:05:19 2009 -0500
136.3 @@ -396,10 +396,18 @@
136.4 # Pickling a QueryDict #
136.5 ########################
136.6 >>> import pickle
136.7 +>>> q = QueryDict('')
136.8 +>>> q1 = pickle.loads(pickle.dumps(q, 2))
136.9 +>>> q == q1
136.10 +True
136.11 >>> q = QueryDict('a=b&c=d')
136.12 >>> q1 = pickle.loads(pickle.dumps(q, 2))
136.13 >>> q == q1
136.14 True
136.15 +>>> q = QueryDict('a=b&c=d&a=1')
136.16 +>>> q1 = pickle.loads(pickle.dumps(q, 2))
136.17 +>>> q == q1
136.18 +True
136.19
136.20 ######################################
136.21 # HttpResponse with Unicode headers #
137.1 --- a/tests/regressiontests/inline_formsets/models.py Sat Mar 28 12:05:48 2009 -0500
137.2 +++ b/tests/regressiontests/inline_formsets/models.py Wed Apr 01 13:05:19 2009 -0500
137.3 @@ -13,6 +13,19 @@
137.4 school = models.ForeignKey(School)
137.5 name = models.CharField(max_length=100)
137.6
137.7 +class Poet(models.Model):
137.8 + name = models.CharField(max_length=100)
137.9 +
137.10 + def __unicode__(self):
137.11 + return self.name
137.12 +
137.13 +class Poem(models.Model):
137.14 + poet = models.ForeignKey(Poet)
137.15 + name = models.CharField(max_length=100)
137.16 +
137.17 + def __unicode__(self):
137.18 + return self.name
137.19 +
137.20 __test__ = {'API_TESTS': """
137.21
137.22 >>> from django.forms.models import inlineformset_factory
138.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
138.2 +++ b/tests/regressiontests/inline_formsets/tests.py Wed Apr 01 13:05:19 2009 -0500
138.3 @@ -0,0 +1,76 @@
138.4 +from django.test import TestCase
138.5 +from django.forms.models import inlineformset_factory
138.6 +from regressiontests.inline_formsets.models import Poet, Poem
138.7 +
138.8 +class DeletionTests(TestCase):
138.9 + def test_deletion(self):
138.10 + PoemFormSet = inlineformset_factory(Poet, Poem, can_delete=True)
138.11 + poet = Poet.objects.create(name='test')
138.12 + poet.poem_set.create(name='test poem')
138.13 + data = {
138.14 + 'poem_set-TOTAL_FORMS': u'1',
138.15 + 'poem_set-INITIAL_FORMS': u'1',
138.16 + 'poem_set-0-id': u'1',
138.17 + 'poem_set-0-poem': u'1',
138.18 + 'poem_set-0-name': u'test',
138.19 + 'poem_set-0-DELETE': u'on',
138.20 + }
138.21 + formset = PoemFormSet(data, instance=poet)
138.22 + formset.save()
138.23 + self.assertTrue(formset.is_valid())
138.24 + self.assertEqual(Poem.objects.count(), 0)
138.25 +
138.26 + def test_add_form_deletion_when_invalid(self):
138.27 + """
138.28 + Make sure that an add form that is filled out, but marked for deletion
138.29 + doesn't cause validation errors.
138.30 + """
138.31 + PoemFormSet = inlineformset_factory(Poet, Poem, can_delete=True)
138.32 + poet = Poet.objects.create(name='test')
138.33 + data = {
138.34 + 'poem_set-TOTAL_FORMS': u'1',
138.35 + 'poem_set-INITIAL_FORMS': u'0',
138.36 + 'poem_set-0-id': u'',
138.37 + 'poem_set-0-poem': u'1',
138.38 + 'poem_set-0-name': u'x' * 1000,
138.39 + }
138.40 + formset = PoemFormSet(data, instance=poet)
138.41 + # Make sure this form doesn't pass validation.
138.42 + self.assertEqual(formset.is_valid(), False)
138.43 + self.assertEqual(Poem.objects.count(), 0)
138.44 +
138.45 + # Then make sure that it *does* pass validation and delete the object,
138.46 + # even though the data isn't actually valid.
138.47 + data['poem_set-0-DELETE'] = 'on'
138.48 + formset = PoemFormSet(data, instance=poet)
138.49 + self.assertEqual(formset.is_valid(), True)
138.50 + formset.save()
138.51 + self.assertEqual(Poem.objects.count(), 0)
138.52 +
138.53 + def test_change_form_deletion_when_invalid(self):
138.54 + """
138.55 + Make sure that a change form that is filled out, but marked for deletion
138.56 + doesn't cause validation errors.
138.57 + """
138.58 + PoemFormSet = inlineformset_factory(Poet, Poem, can_delete=True)
138.59 + poet = Poet.objects.create(name='test')
138.60 + poet.poem_set.create(name='test poem')
138.61 + data = {
138.62 + 'poem_set-TOTAL_FORMS': u'1',
138.63 + 'poem_set-INITIAL_FORMS': u'1',
138.64 + 'poem_set-0-id': u'1',
138.65 + 'poem_set-0-poem': u'1',
138.66 + 'poem_set-0-name': u'x' * 1000,
138.67 + }
138.68 + formset = PoemFormSet(data, instance=poet)
138.69 + # Make sure this form doesn't pass validation.
138.70 + self.assertEqual(formset.is_valid(), False)
138.71 + self.assertEqual(Poem.objects.count(), 1)
138.72 +
138.73 + # Then make sure that it *does* pass validation and delete the object,
138.74 + # even though the data isn't actually valid.
138.75 + data['poem_set-0-DELETE'] = 'on'
138.76 + formset = PoemFormSet(data, instance=poet)
138.77 + self.assertEqual(formset.is_valid(), True)
138.78 + formset.save()
138.79 + self.assertEqual(Poem.objects.count(), 0)
139.1 --- a/tests/regressiontests/templates/filters.py Sat Mar 28 12:05:48 2009 -0500
139.2 +++ b/tests/regressiontests/templates/filters.py Wed Apr 01 13:05:19 2009 -0500
139.3 @@ -7,7 +7,7 @@
139.4 consistent.
139.5 """
139.6
139.7 -from datetime import datetime, timedelta
139.8 +from datetime import date, datetime, timedelta
139.9
139.10 from django.utils.tzinfo import LocalTimezone, FixedOffset
139.11 from django.utils.safestring import mark_safe
139.12 @@ -28,6 +28,8 @@
139.13 now = datetime.now()
139.14 now_tz = datetime.now(LocalTimezone(now))
139.15 now_tz_i = datetime.now(FixedOffset((3 * 60) + 15)) # imaginary time zone
139.16 + today = date.today()
139.17 +
139.18 return {
139.19 # Default compare with datetime.now()
139.20 'filter-timesince01' : ('{{ a|timesince }}', {'a': datetime.now() + timedelta(minutes=-1, seconds = -10)}, '1 minute'),
139.21 @@ -55,6 +57,10 @@
139.22 'filter-timesince15' : ('{{ a|timesince:b }}', {'a': now, 'b': now_tz_i}, ''),
139.23 'filter-timesince16' : ('{{ a|timesince:b }}', {'a': now_tz_i, 'b': now}, ''),
139.24
139.25 + # Regression for #9065 (two date objects).
139.26 + 'filter-timesince17' : ('{{ a|timesince:b }}', {'a': today, 'b': today}, '0 minutes'),
139.27 + 'filter-timesince18' : ('{{ a|timesince:b }}', {'a': today, 'b': today + timedelta(hours=24)}, '1 day'),
139.28 +
139.29 # Default compare with datetime.now()
139.30 'filter-timeuntil01' : ('{{ a|timeuntil }}', {'a':datetime.now() + timedelta(minutes=2, seconds = 10)}, '2 minutes'),
139.31 'filter-timeuntil02' : ('{{ a|timeuntil }}', {'a':(datetime.now() + timedelta(days=1, seconds = 10))}, '1 day'),
139.32 @@ -74,6 +80,10 @@
139.33 'filter-timeuntil10' : ('{{ a|timeuntil }}', {'a': now_tz_i}, '0 minutes'),
139.34 'filter-timeuntil11' : ('{{ a|timeuntil:b }}', {'a': now_tz_i, 'b': now_tz}, '0 minutes'),
139.35
139.36 + # Regression for #9065 (two date objects).
139.37 + 'filter-timeuntil12' : ('{{ a|timeuntil:b }}', {'a': today, 'b': today}, '0 minutes'),
139.38 + 'filter-timeuntil13' : ('{{ a|timeuntil:b }}', {'a': today, 'b': today - timedelta(hours=24)}, '1 day'),
139.39 +
139.40 'filter-addslash01': ("{% autoescape off %}{{ a|addslashes }} {{ b|addslashes }}{% endautoescape %}", {"a": "<a>'", "b": mark_safe("<a>'")}, ur"<a>\' <a>\'"),
139.41 'filter-addslash02': ("{{ a|addslashes }} {{ b|addslashes }}", {"a": "<a>'", "b": mark_safe("<a>'")}, ur"<a>\' <a>\'"),
139.42
139.43 @@ -281,13 +291,33 @@
139.44 'escapejs01': (r'{{ a|escapejs }}', {'a': 'testing\r\njavascript \'string" <b>escaping</b>'}, 'testing\\x0D\\x0Ajavascript \\x27string\\x22 \\x3Cb\\x3Eescaping\\x3C/b\\x3E'),
139.45 'escapejs02': (r'{% autoescape off %}{{ a|escapejs }}{% endautoescape %}', {'a': 'testing\r\njavascript \'string" <b>escaping</b>'}, 'testing\\x0D\\x0Ajavascript \\x27string\\x22 \\x3Cb\\x3Eescaping\\x3C/b\\x3E'),
139.46
139.47 +
139.48 + # length filter.
139.49 + 'length01': ('{{ list|length }}', {'list': ['4', None, True, {}]}, '4'),
139.50 + 'length02': ('{{ list|length }}', {'list': []}, '0'),
139.51 + 'length03': ('{{ string|length }}', {'string': ''}, '0'),
139.52 + 'length04': ('{{ string|length }}', {'string': 'django'}, '6'),
139.53 + # Invalid uses that should fail silently.
139.54 + 'length05': ('{{ int|length }}', {'int': 7}, ''),
139.55 + 'length06': ('{{ None|length }}', {'None': None}, ''),
139.56 +
139.57 + # length_is filter.
139.58 + 'length_is01': ('{% if some_list|length_is:"4" %}Four{% endif %}', {'some_list': ['4', None, True, {}]}, 'Four'),
139.59 + 'length_is02': ('{% if some_list|length_is:"4" %}Four{% else %}Not Four{% endif %}', {'some_list': ['4', None, True, {}, 17]}, 'Not Four'),
139.60 + 'length_is03': ('{% if mystring|length_is:"4" %}Four{% endif %}', {'mystring': 'word'}, 'Four'),
139.61 + 'length_is04': ('{% if mystring|length_is:"4" %}Four{% else %}Not Four{% endif %}', {'mystring': 'Python'}, 'Not Four'),
139.62 + 'length_is05': ('{% if mystring|length_is:"4" %}Four{% else %}Not Four{% endif %}', {'mystring': ''}, 'Not Four'),
139.63 + 'length_is06': ('{% with var|length as my_length %}{{ my_length }}{% endwith %}', {'var': 'django'}, '6'),
139.64 # Boolean return value from length_is should not be coerced to a string
139.65 - 'lengthis01': (r'{% if "X"|length_is:0 %}Length is 0{% else %}Length not 0{% endif %}', {}, 'Length not 0'),
139.66 - 'lengthis02': (r'{% if "X"|length_is:1 %}Length is 1{% else %}Length not 1{% endif %}', {}, 'Length is 1'),
139.67 + 'length_is07': (r'{% if "X"|length_is:0 %}Length is 0{% else %}Length not 0{% endif %}', {}, 'Length not 0'),
139.68 + 'length_is08': (r'{% if "X"|length_is:1 %}Length is 1{% else %}Length not 1{% endif %}', {}, 'Length is 1'),
139.69 + # Invalid uses that should fail silently.
139.70 + 'length_is09': ('{{ var|length_is:"fish" }}', {'var': 'django'}, ''),
139.71 + 'length_is10': ('{{ int|length_is:"1" }}', {'int': 7}, ''),
139.72 + 'length_is11': ('{{ none|length_is:"1" }}', {'none': None}, ''),
139.73
139.74 'join01': (r'{{ a|join:", " }}', {'a': ['alpha', 'beta & me']}, 'alpha, beta & me'),
139.75 'join02': (r'{% autoescape off %}{{ a|join:", " }}{% endautoescape %}', {'a': ['alpha', 'beta & me']}, 'alpha, beta & me'),
139.76 'join03': (r'{{ a|join:" & " }}', {'a': ['alpha', 'beta & me']}, 'alpha & beta & me'),
139.77 'join04': (r'{% autoescape off %}{{ a|join:" & " }}{% endautoescape %}', {'a': ['alpha', 'beta & me']}, 'alpha & beta & me'),
139.78 }
139.79 -
140.1 --- a/tests/regressiontests/templates/tests.py Sat Mar 28 12:05:48 2009 -0500
140.2 +++ b/tests/regressiontests/templates/tests.py Wed Apr 01 13:05:19 2009 -0500
140.3 @@ -6,9 +6,11 @@
140.4 # before importing 'template'.
140.5 settings.configure()
140.6
140.7 +from datetime import datetime, timedelta
140.8 import os
140.9 +import sys
140.10 +import traceback
140.11 import unittest
140.12 -from datetime import datetime, timedelta
140.13
140.14 from django import template
140.15 from django.core import urlresolvers
140.16 @@ -207,10 +209,11 @@
140.17 try:
140.18 test_template = loader.get_template(name)
140.19 output = self.render(test_template, vals)
140.20 - except Exception, e:
140.21 - if e.__class__ != result:
140.22 - raise
140.23 - failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Got %s, exception: %s" % (invalid_str, name, e.__class__, e))
140.24 + except Exception:
140.25 + exc_type, exc_value, exc_tb = sys.exc_info()
140.26 + if exc_type != result:
140.27 + tb = '\n'.join(traceback.format_exception(exc_type, exc_value, exc_tb))
140.28 + failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Got %s, exception: %s\n%s" % (invalid_str, name, exc_type, exc_value, tb))
140.29 continue
140.30 if output != result:
140.31 failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Expected %r, got %r" % (invalid_str, name, result, output))
140.32 @@ -227,7 +230,8 @@
140.33 settings.TEMPLATE_DEBUG = old_td
140.34 settings.TEMPLATE_STRING_IF_INVALID = old_invalid
140.35
140.36 - self.assertEqual(failures, [], '\n'.join(failures))
140.37 + self.assertEqual(failures, [], "Tests failed:\n%s\n%s" %
140.38 + ('-'*70, ("\n%s\n" % ('-'*70)).join(failures)))
140.39
140.40 def render(self, test_template, vals):
140.41 return test_template.render(template.Context(vals[1]))
140.42 @@ -658,6 +662,8 @@
140.43 'include02': ('{% include "basic-syntax02" %}', {'headline': 'Included'}, "Included"),
140.44 'include03': ('{% include template_name %}', {'template_name': 'basic-syntax02', 'headline': 'Included'}, "Included"),
140.45 'include04': ('a{% include "nonexistent" %}b', {}, "ab"),
140.46 + 'include 05': ('template with a space', {}, 'template with a space'),
140.47 + 'include06': ('{% include "include 05"%}', {}, 'template with a space'),
140.48
140.49 ### NAMED ENDBLOCKS #######################################################
140.50
140.51 @@ -757,6 +763,12 @@
140.52 # Inheritance from a template that doesn't have any blocks
140.53 'inheritance27': ("{% extends 'inheritance26' %}", {}, 'no tags'),
140.54
140.55 + # Set up a base template with a space in it.
140.56 + 'inheritance 28': ("{% block first %}!{% endblock %}", {}, '!'),
140.57 +
140.58 + # Inheritance from a template with a space in its name should work.
140.59 + 'inheritance29': ("{% extends 'inheritance 28' %}", {}, '!'),
140.60 +
140.61 ### I18N ##################################################################
140.62
140.63 # {% spaceless %} tag
141.1 --- a/tests/regressiontests/test_client_regress/models.py Sat Mar 28 12:05:48 2009 -0500
141.2 +++ b/tests/regressiontests/test_client_regress/models.py Wed Apr 01 13:05:19 2009 -0500
141.3 @@ -444,7 +444,7 @@
141.4 urls = 'regressiontests.test_client_regress.urls'
141.5
141.6 def test_urlconf_was_changed(self):
141.7 - "TestCase can enforce a custom URLConf on a per-test basis"
141.8 + "TestCase can enforce a custom URLconf on a per-test basis"
141.9 url = reverse('arg_view', args=['somename'])
141.10 self.assertEquals(url, '/arg_view/somename/')
141.11
141.12 @@ -505,6 +505,14 @@
141.13 self.assertEqual(response.status_code, 200)
141.14 self.assertEqual(response.content, 'YES')
141.15
141.16 + def test_logout(self):
141.17 + """Logout should work whether the user is logged in or not (#9978)."""
141.18 + self.client.logout()
141.19 + login = self.client.login(username='testclient',password='password')
141.20 + self.failUnless(login, 'Could not log in')
141.21 + self.client.logout()
141.22 + self.client.logout()
141.23 +
141.24 class RequestMethodTests(TestCase):
141.25 def test_get(self):
141.26 "Request a view via request method GET"
142.1 --- a/tests/regressiontests/views/tests/__init__.py Sat Mar 28 12:05:48 2009 -0500
142.2 +++ b/tests/regressiontests/views/tests/__init__.py Wed Apr 01 13:05:19 2009 -0500
142.3 @@ -3,3 +3,4 @@
142.4 from static import *
142.5 from generic.date_based import *
142.6 from generic.create_update import *
142.7 +from debug import *
143.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
143.2 +++ b/tests/regressiontests/views/tests/debug.py Wed Apr 01 13:05:19 2009 -0500
143.3 @@ -0,0 +1,21 @@
143.4 +from django.conf import settings
143.5 +from django.core.files.uploadedfile import SimpleUploadedFile
143.6 +from django.test import TestCase
143.7 +
143.8 +class DebugViewTests(TestCase):
143.9 + def setUp(self):
143.10 + settings.DEBUG = True
143.11 +
143.12 + def tearDown(self):
143.13 + settings.DEBUG = False
143.14 +
143.15 + def test_files(self):
143.16 + response = self.client.get('/views/raises/')
143.17 + self.assertEquals(response.status_code, 500)
143.18 +
143.19 + data = {
143.20 + 'file_data.txt': SimpleUploadedFile('file_data.txt', 'haha'),
143.21 + }
143.22 + response = self.client.post('/views/raises/', data)
143.23 + self.failUnless('file_data.txt' in response.content)
143.24 + self.failIf('haha' in response.content)
144.1 --- a/tests/regressiontests/views/urls.py Sat Mar 28 12:05:48 2009 -0500
144.2 +++ b/tests/regressiontests/views/urls.py Wed Apr 01 13:05:19 2009 -0500
144.3 @@ -82,3 +82,8 @@
144.4 (r'^create_update/no_url/update/article/(?P<slug>[-\w]+)/$',
144.5 'update_object', dict(slug_field='slug', model=UrlArticle)),
144.6 )
144.7 +
144.8 +# a view that raises an exception for the debug view
144.9 +urlpatterns += patterns('',
144.10 + (r'^raises/$', views.raises)
144.11 +)
145.1 --- a/tests/regressiontests/views/views.py Sat Mar 28 12:05:48 2009 -0500
145.2 +++ b/tests/regressiontests/views/views.py Wed Apr 01 13:05:19 2009 -0500
145.3 @@ -1,5 +1,8 @@
145.4 +import sys
145.5 +
145.6 from django.http import HttpResponse
145.7 from django import forms
145.8 +from django.views.debug import technical_500_response
145.9 from django.views.generic.create_update import create_object
145.10
145.11 from models import Article
145.12 @@ -27,3 +30,9 @@
145.13 return create_object(request,
145.14 post_save_redirect='/views/create_update/view/article/%(slug)s/',
145.15 form_class=SlugChangingArticleForm)
145.16 +
145.17 +def raises(request):
145.18 + try:
145.19 + raise Exception
145.20 + except Exception:
145.21 + return technical_500_response(request, *sys.exc_info())