mirror of
https://github.com/django/django.git
synced 2025-07-06 18:59:13 +00:00
[soc2009/model-validation] Merged to trunk at r11791
git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/model-validation@11798 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
30ea350dab
commit
3b895d4a9a
1
AUTHORS
1
AUTHORS
@ -17,6 +17,7 @@ The PRIMARY AUTHORS are (and/or have been):
|
||||
* Justin Bronn
|
||||
* Karen Tracey
|
||||
* Jannis Leidel
|
||||
* James Tauber
|
||||
|
||||
More information on the main contributors to Django can be found in
|
||||
docs/internals/committers.txt.
|
||||
|
Binary file not shown.
@ -5,7 +5,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2009-07-07 15:04+0200\n"
|
||||
"POT-Creation-Date: 2009-11-30 11:19+0100\n"
|
||||
"PO-Revision-Date: 2009-03-24 13:28+0100\n"
|
||||
"Last-Translator: Django Catalan Group <django-cat@googlegroups.com>\n"
|
||||
"Language-Team: Catalan <ca@li.org>\n"
|
||||
@ -223,7 +223,7 @@ msgstr "xinès tradicional"
|
||||
msgid "Successfully deleted %(count)d %(items)s."
|
||||
msgstr "Eliminat/s %(count)d %(items)s satisfactòriament."
|
||||
|
||||
#: contrib/admin/actions.py:67 contrib/admin/options.py:1025
|
||||
#: contrib/admin/actions.py:67 contrib/admin/options.py:1033
|
||||
msgid "Are you sure?"
|
||||
msgstr "Esteu segurs?"
|
||||
|
||||
@ -266,15 +266,15 @@ msgstr "Aquest mes"
|
||||
msgid "This year"
|
||||
msgstr "Aquest any"
|
||||
|
||||
#: contrib/admin/filterspecs.py:147 forms/widgets.py:434
|
||||
#: contrib/admin/filterspecs.py:147 forms/widgets.py:435
|
||||
msgid "Yes"
|
||||
msgstr "Si"
|
||||
|
||||
#: contrib/admin/filterspecs.py:147 forms/widgets.py:434
|
||||
#: contrib/admin/filterspecs.py:147 forms/widgets.py:435
|
||||
msgid "No"
|
||||
msgstr "No"
|
||||
|
||||
#: contrib/admin/filterspecs.py:154 forms/widgets.py:434
|
||||
#: contrib/admin/filterspecs.py:154 forms/widgets.py:435
|
||||
msgid "Unknown"
|
||||
msgstr "Desconegut"
|
||||
|
||||
@ -310,61 +310,61 @@ msgstr "entrada del registre"
|
||||
msgid "log entries"
|
||||
msgstr "entrades del registre"
|
||||
|
||||
#: contrib/admin/options.py:133 contrib/admin/options.py:147
|
||||
#: contrib/admin/options.py:134 contrib/admin/options.py:148
|
||||
msgid "None"
|
||||
msgstr "cap"
|
||||
|
||||
#: contrib/admin/options.py:519
|
||||
#: contrib/admin/options.py:521
|
||||
#, python-format
|
||||
msgid "Changed %s."
|
||||
msgstr "Modificat %s."
|
||||
|
||||
#: contrib/admin/options.py:519 contrib/admin/options.py:529
|
||||
#: contrib/comments/templates/comments/preview.html:16 forms/models.py:388
|
||||
#: forms/models.py:600
|
||||
#: contrib/admin/options.py:521 contrib/admin/options.py:531
|
||||
#: contrib/comments/templates/comments/preview.html:16 forms/models.py:384
|
||||
#: forms/models.py:596
|
||||
msgid "and"
|
||||
msgstr "i"
|
||||
|
||||
#: contrib/admin/options.py:524
|
||||
#: contrib/admin/options.py:526
|
||||
#, python-format
|
||||
msgid "Added %(name)s \"%(object)s\"."
|
||||
msgstr "Afegit %(name)s \"%(object)s\""
|
||||
|
||||
#: contrib/admin/options.py:528
|
||||
#: contrib/admin/options.py:530
|
||||
#, python-format
|
||||
msgid "Changed %(list)s for %(name)s \"%(object)s\"."
|
||||
msgstr "Modificat %(list)s per a %(name)s \"%(object)s\"."
|
||||
|
||||
#: contrib/admin/options.py:533
|
||||
#: contrib/admin/options.py:535
|
||||
#, python-format
|
||||
msgid "Deleted %(name)s \"%(object)s\"."
|
||||
msgstr "Eliminat %(name)s \"%(object)s\"."
|
||||
|
||||
#: contrib/admin/options.py:537
|
||||
#: contrib/admin/options.py:539
|
||||
msgid "No fields changed."
|
||||
msgstr "Cap camp canviat."
|
||||
|
||||
#: contrib/admin/options.py:598 contrib/auth/admin.py:67
|
||||
#: contrib/admin/options.py:601 contrib/auth/admin.py:67
|
||||
#, python-format
|
||||
msgid "The %(name)s \"%(obj)s\" was added successfully."
|
||||
msgstr "El/la %(name)s \"%(obj)s\".ha estat afegit/da amb èxit."
|
||||
|
||||
#: contrib/admin/options.py:602 contrib/admin/options.py:635
|
||||
#: contrib/admin/options.py:605 contrib/admin/options.py:638
|
||||
#: contrib/auth/admin.py:75
|
||||
msgid "You may edit it again below."
|
||||
msgstr "Podeu editar-lo de nou a baix."
|
||||
|
||||
#: contrib/admin/options.py:612 contrib/admin/options.py:645
|
||||
#: contrib/admin/options.py:615 contrib/admin/options.py:648
|
||||
#, python-format
|
||||
msgid "You may add another %s below."
|
||||
msgstr "Podeu afegir un altre %s a baix."
|
||||
|
||||
#: contrib/admin/options.py:633
|
||||
#: contrib/admin/options.py:636
|
||||
#, python-format
|
||||
msgid "The %(name)s \"%(obj)s\" was changed successfully."
|
||||
msgstr "S'ha modificat amb èxit el/la %(name)s \"%(obj)s."
|
||||
|
||||
#: contrib/admin/options.py:641
|
||||
#: contrib/admin/options.py:644
|
||||
#, python-format
|
||||
msgid ""
|
||||
"The %(name)s \"%(obj)s\" was added successfully. You may edit it again below."
|
||||
@ -372,43 +372,43 @@ msgstr ""
|
||||
"S'ha afegit exitosament el/la %(name)s \"%(obj)s\". Pot editar-lo de nou "
|
||||
"abaix."
|
||||
|
||||
#: contrib/admin/options.py:772
|
||||
#: contrib/admin/options.py:777
|
||||
#, python-format
|
||||
msgid "Add %s"
|
||||
msgstr "Afegir %s"
|
||||
|
||||
#: contrib/admin/options.py:803 contrib/admin/options.py:1003
|
||||
#: contrib/admin/options.py:809 contrib/admin/options.py:1011
|
||||
#, python-format
|
||||
msgid "%(name)s object with primary key %(key)r does not exist."
|
||||
msgstr "No existèix cap objecte %(name)s amb la clau primària %(key)r."
|
||||
|
||||
#: contrib/admin/options.py:860
|
||||
#: contrib/admin/options.py:866
|
||||
#, python-format
|
||||
msgid "Change %s"
|
||||
msgstr "Modificar %s"
|
||||
|
||||
#: contrib/admin/options.py:904
|
||||
#: contrib/admin/options.py:910
|
||||
msgid "Database error"
|
||||
msgstr "Error de base de dades"
|
||||
|
||||
#: contrib/admin/options.py:940
|
||||
#: contrib/admin/options.py:946
|
||||
#, python-format
|
||||
msgid "%(count)s %(name)s was changed successfully."
|
||||
msgid_plural "%(count)s %(name)s were changed successfully."
|
||||
msgstr[0] "%(count)s %(name)s s'ha modificat amb èxit."
|
||||
msgstr[1] "%(count)s %(name)s s'han modificat amb èxit."
|
||||
|
||||
#: contrib/admin/options.py:1018
|
||||
#: contrib/admin/options.py:1026
|
||||
#, python-format
|
||||
msgid "The %(name)s \"%(obj)s\" was deleted successfully."
|
||||
msgstr "El/la %(name)s \"%(obj)s\" ha estat eliminat amb èxit."
|
||||
|
||||
#: contrib/admin/options.py:1054
|
||||
#: contrib/admin/options.py:1063
|
||||
#, python-format
|
||||
msgid "Change history: %s"
|
||||
msgstr "Modificar històric: %s"
|
||||
|
||||
#: contrib/admin/sites.py:20 contrib/admin/views/decorators.py:14
|
||||
#: contrib/admin/sites.py:22 contrib/admin/views/decorators.py:14
|
||||
#: contrib/auth/forms.py:80
|
||||
msgid ""
|
||||
"Please enter a correct username and password. Note that both fields are case-"
|
||||
@ -417,11 +417,11 @@ msgstr ""
|
||||
"Si us plau, introduïu un nom d'usuari i contrasenya vàlids. Tingueu en "
|
||||
"compte que tots dos camps son sensibles a majúscules i minúscules."
|
||||
|
||||
#: contrib/admin/sites.py:278 contrib/admin/views/decorators.py:40
|
||||
#: contrib/admin/sites.py:292 contrib/admin/views/decorators.py:40
|
||||
msgid "Please log in again, because your session has expired."
|
||||
msgstr "Si us plau, identifiqueu-vos de nou doncs la vostra sessió ha expirat."
|
||||
|
||||
#: contrib/admin/sites.py:285 contrib/admin/views/decorators.py:47
|
||||
#: contrib/admin/sites.py:299 contrib/admin/views/decorators.py:47
|
||||
msgid ""
|
||||
"Looks like your browser isn't configured to accept cookies. Please enable "
|
||||
"cookies, reload this page, and try again."
|
||||
@ -430,29 +430,29 @@ msgstr ""
|
||||
"'cookies' (galetes). Si us plau, habiliteu les 'cookies', recarregueu "
|
||||
"aquesta pàgina i proveu-ho de nou. "
|
||||
|
||||
#: contrib/admin/sites.py:301 contrib/admin/sites.py:307
|
||||
#: contrib/admin/sites.py:315 contrib/admin/sites.py:321
|
||||
#: contrib/admin/views/decorators.py:66
|
||||
msgid "Usernames cannot contain the '@' character."
|
||||
msgstr "Els noms d'usuari no poden contenir el caracter '@'."
|
||||
|
||||
#: contrib/admin/sites.py:304 contrib/admin/views/decorators.py:62
|
||||
#: contrib/admin/sites.py:318 contrib/admin/views/decorators.py:62
|
||||
#, python-format
|
||||
msgid "Your e-mail address is not your username. Try '%s' instead."
|
||||
msgstr ""
|
||||
"La vostra adreça de correu no és el vostre nom d'usuari. Provi '%s' en tot "
|
||||
"cas."
|
||||
|
||||
#: contrib/admin/sites.py:360
|
||||
#: contrib/admin/sites.py:374
|
||||
msgid "Site administration"
|
||||
msgstr "Lloc administratiu"
|
||||
|
||||
#: contrib/admin/sites.py:373 contrib/admin/templates/admin/login.html:26
|
||||
#: contrib/admin/sites.py:388 contrib/admin/templates/admin/login.html:26
|
||||
#: contrib/admin/templates/registration/password_reset_complete.html:14
|
||||
#: contrib/admin/views/decorators.py:20
|
||||
msgid "Log in"
|
||||
msgstr "Iniciar sessió"
|
||||
|
||||
#: contrib/admin/sites.py:417
|
||||
#: contrib/admin/sites.py:433
|
||||
#, python-format
|
||||
msgid "%s administration"
|
||||
msgstr "Administració de %s"
|
||||
@ -467,27 +467,27 @@ msgstr "Un o més %(fieldname)s en %(name)s: %(obj)s"
|
||||
msgid "One or more %(fieldname)s in %(name)s:"
|
||||
msgstr "Un o més %(fieldname)s en %(name)s:"
|
||||
|
||||
#: contrib/admin/widgets.py:71
|
||||
#: contrib/admin/widgets.py:72
|
||||
msgid "Date:"
|
||||
msgstr "Data:"
|
||||
|
||||
#: contrib/admin/widgets.py:71
|
||||
#: contrib/admin/widgets.py:72
|
||||
msgid "Time:"
|
||||
msgstr "Hora:"
|
||||
|
||||
#: contrib/admin/widgets.py:95
|
||||
#: contrib/admin/widgets.py:96
|
||||
msgid "Currently:"
|
||||
msgstr "Actualment:"
|
||||
|
||||
#: contrib/admin/widgets.py:95
|
||||
#: contrib/admin/widgets.py:96
|
||||
msgid "Change:"
|
||||
msgstr "Modificar:"
|
||||
|
||||
#: contrib/admin/widgets.py:124
|
||||
#: contrib/admin/widgets.py:125
|
||||
msgid "Lookup"
|
||||
msgstr "Cercar"
|
||||
|
||||
#: contrib/admin/widgets.py:236
|
||||
#: contrib/admin/widgets.py:237
|
||||
msgid "Add Another"
|
||||
msgstr "Afegir un altre"
|
||||
|
||||
@ -502,7 +502,7 @@ msgstr "Ho sentim, però no s'ha pogut trobar la pàgina sol·licitada"
|
||||
|
||||
#: contrib/admin/templates/admin/500.html:4
|
||||
#: contrib/admin/templates/admin/app_index.html:8
|
||||
#: contrib/admin/templates/admin/base.html:31
|
||||
#: contrib/admin/templates/admin/base.html:54
|
||||
#: contrib/admin/templates/admin/change_form.html:17
|
||||
#: contrib/admin/templates/admin/change_list.html:25
|
||||
#: contrib/admin/templates/admin/delete_confirmation.html:6
|
||||
@ -555,18 +555,18 @@ msgstr "Anar"
|
||||
msgid "%(name)s"
|
||||
msgstr "%(name)s"
|
||||
|
||||
#: contrib/admin/templates/admin/base.html:26
|
||||
#: contrib/admin/templates/admin/base.html:27
|
||||
msgid "Welcome,"
|
||||
msgstr "Benvingut/da,"
|
||||
|
||||
#: contrib/admin/templates/admin/base.html:26
|
||||
#: contrib/admin/templates/admin/base.html:32
|
||||
#: contrib/admin/templates/registration/password_change_done.html:3
|
||||
#: contrib/admin/templates/registration/password_change_form.html:3
|
||||
#: contrib/admindocs/templates/admin_doc/bookmarklets.html:3
|
||||
msgid "Documentation"
|
||||
msgstr "Documentació"
|
||||
|
||||
#: contrib/admin/templates/admin/base.html:26
|
||||
#: contrib/admin/templates/admin/base.html:40
|
||||
#: contrib/admin/templates/admin/auth/user/change_password.html:14
|
||||
#: contrib/admin/templates/admin/auth/user/change_password.html:47
|
||||
#: contrib/admin/templates/registration/password_change_done.html:3
|
||||
@ -574,7 +574,7 @@ msgstr "Documentació"
|
||||
msgid "Change password"
|
||||
msgstr "Canviar contrasenya"
|
||||
|
||||
#: contrib/admin/templates/admin/base.html:26
|
||||
#: contrib/admin/templates/admin/base.html:47
|
||||
#: contrib/admin/templates/registration/password_change_done.html:3
|
||||
#: contrib/admin/templates/registration/password_change_form.html:3
|
||||
msgid "Log out"
|
||||
@ -600,7 +600,7 @@ msgstr "Històric"
|
||||
|
||||
#: contrib/admin/templates/admin/change_form.html:28
|
||||
#: contrib/admin/templates/admin/edit_inline/stacked.html:13
|
||||
#: contrib/admin/templates/admin/edit_inline/tabular.html:27
|
||||
#: contrib/admin/templates/admin/edit_inline/tabular.html:28
|
||||
msgid "View on site"
|
||||
msgstr "Veure al lloc"
|
||||
|
||||
@ -670,9 +670,9 @@ msgstr ""
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Are you sure you want to delete the selected %(object_name)s objects? All of "
|
||||
"the following objects and it's related items will be deleted:"
|
||||
"the following objects and their related items will be deleted:"
|
||||
msgstr ""
|
||||
"Esteu segurs de voler esborrar els/les %(object_name)s seleccionats?Tots "
|
||||
"Esteu segurs de voler esborrar els/les %(object_name)s seleccionats? Tots "
|
||||
"aquests objectes i els seus elements relacionats s'esborraran:"
|
||||
|
||||
#: contrib/admin/templates/admin/filter.html:2
|
||||
@ -736,7 +736,6 @@ msgid "User"
|
||||
msgstr "Usuari"
|
||||
|
||||
#: contrib/admin/templates/admin/object_history.html:24
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:33
|
||||
msgid "Action"
|
||||
msgstr "Acció"
|
||||
|
||||
@ -985,7 +984,7 @@ msgstr "Adreça de correu electrònic:"
|
||||
msgid "Reset my password"
|
||||
msgstr "Restablir la meva contrasenya"
|
||||
|
||||
#: contrib/admin/templatetags/admin_list.py:299
|
||||
#: contrib/admin/templatetags/admin_list.py:304
|
||||
msgid "All dates"
|
||||
msgstr "Totes les dates"
|
||||
|
||||
@ -1007,145 +1006,144 @@ msgstr "lloc"
|
||||
msgid "template"
|
||||
msgstr "plantilla"
|
||||
|
||||
#: contrib/admindocs/views.py:58 contrib/admindocs/views.py:60
|
||||
#: contrib/admindocs/views.py:62
|
||||
#: contrib/admindocs/views.py:61 contrib/admindocs/views.py:63
|
||||
#: contrib/admindocs/views.py:65
|
||||
msgid "tag:"
|
||||
msgstr "etiqueta:"
|
||||
|
||||
#: contrib/admindocs/views.py:91 contrib/admindocs/views.py:93
|
||||
#: contrib/admindocs/views.py:95
|
||||
#: contrib/admindocs/views.py:94 contrib/admindocs/views.py:96
|
||||
#: contrib/admindocs/views.py:98
|
||||
msgid "filter:"
|
||||
msgstr "filtre:"
|
||||
|
||||
#: contrib/admindocs/views.py:155 contrib/admindocs/views.py:157
|
||||
#: contrib/admindocs/views.py:159
|
||||
#: contrib/admindocs/views.py:158 contrib/admindocs/views.py:160
|
||||
#: contrib/admindocs/views.py:162
|
||||
msgid "view:"
|
||||
msgstr "vista:"
|
||||
|
||||
#: contrib/admindocs/views.py:187
|
||||
#: contrib/admindocs/views.py:190
|
||||
#, python-format
|
||||
msgid "App %r not found"
|
||||
msgstr "No s'ha pogut trobar l'aplicació %r"
|
||||
|
||||
#: contrib/admindocs/views.py:194
|
||||
#: contrib/admindocs/views.py:197
|
||||
#, python-format
|
||||
msgid "Model %(model_name)r not found in app %(app_label)r"
|
||||
msgstr "El model %(model_name)r no s'ha trobat en l'aplicació %(app_label)r"
|
||||
|
||||
#: contrib/admindocs/views.py:206
|
||||
#: contrib/admindocs/views.py:209
|
||||
#, python-format
|
||||
msgid "the related `%(app_label)s.%(data_type)s` object"
|
||||
msgstr "l'objecte relacionat `%(app_label)s.%(data_type)s`"
|
||||
|
||||
#: contrib/admindocs/views.py:206 contrib/admindocs/views.py:225
|
||||
#: contrib/admindocs/views.py:230 contrib/admindocs/views.py:244
|
||||
#: contrib/admindocs/views.py:258 contrib/admindocs/views.py:263
|
||||
#: contrib/admindocs/views.py:209 contrib/admindocs/views.py:228
|
||||
#: contrib/admindocs/views.py:233 contrib/admindocs/views.py:247
|
||||
#: contrib/admindocs/views.py:261 contrib/admindocs/views.py:266
|
||||
msgid "model:"
|
||||
msgstr "model:"
|
||||
|
||||
#: contrib/admindocs/views.py:221 contrib/admindocs/views.py:253
|
||||
#: contrib/admindocs/views.py:224 contrib/admindocs/views.py:256
|
||||
#, python-format
|
||||
msgid "related `%(app_label)s.%(object_name)s` objects"
|
||||
msgstr "objectes relacionats `%(app_label)s.%(object_name)s`"
|
||||
|
||||
#: contrib/admindocs/views.py:225 contrib/admindocs/views.py:258
|
||||
#: contrib/admindocs/views.py:228 contrib/admindocs/views.py:261
|
||||
#, python-format
|
||||
msgid "all %s"
|
||||
msgstr "tots %s"
|
||||
|
||||
#: contrib/admindocs/views.py:230 contrib/admindocs/views.py:263
|
||||
#: contrib/admindocs/views.py:233 contrib/admindocs/views.py:266
|
||||
#, python-format
|
||||
msgid "number of %s"
|
||||
msgstr "nombre de %s"
|
||||
|
||||
#: contrib/admindocs/views.py:268
|
||||
#: contrib/admindocs/views.py:271
|
||||
#, python-format
|
||||
msgid "Fields on %s objects"
|
||||
msgstr "Camps en objectes %s"
|
||||
|
||||
#: contrib/admindocs/views.py:331 contrib/admindocs/views.py:342
|
||||
#: contrib/admindocs/views.py:344 contrib/admindocs/views.py:350
|
||||
#: contrib/admindocs/views.py:351 contrib/admindocs/views.py:353
|
||||
#: contrib/admindocs/views.py:334 contrib/admindocs/views.py:345
|
||||
#: contrib/admindocs/views.py:347 contrib/admindocs/views.py:353
|
||||
#: contrib/admindocs/views.py:354 contrib/admindocs/views.py:356
|
||||
msgid "Integer"
|
||||
msgstr "Enter"
|
||||
|
||||
#: contrib/admindocs/views.py:332
|
||||
#: contrib/admindocs/views.py:335
|
||||
msgid "Boolean (Either True or False)"
|
||||
msgstr "Booleà (Verdader o Fals)"
|
||||
|
||||
#: contrib/admindocs/views.py:333 contrib/admindocs/views.py:352
|
||||
#: contrib/admindocs/views.py:336 contrib/admindocs/views.py:355
|
||||
#, python-format
|
||||
msgid "String (up to %(max_length)s)"
|
||||
msgstr "Cadena (de fins a %(max_length)s)"
|
||||
|
||||
#: contrib/admindocs/views.py:334
|
||||
#: contrib/admindocs/views.py:337
|
||||
msgid "Comma-separated integers"
|
||||
msgstr "Enters separats per comes"
|
||||
|
||||
#: contrib/admindocs/views.py:335
|
||||
#: contrib/admindocs/views.py:338
|
||||
msgid "Date (without time)"
|
||||
msgstr "Data (sense hora)"
|
||||
|
||||
#: contrib/admindocs/views.py:336
|
||||
#: contrib/admindocs/views.py:339
|
||||
msgid "Date (with time)"
|
||||
msgstr "Data (amb hora)"
|
||||
|
||||
#: contrib/admindocs/views.py:337
|
||||
#: contrib/admindocs/views.py:340
|
||||
msgid "Decimal number"
|
||||
msgstr "Número decimal"
|
||||
|
||||
#: contrib/admindocs/views.py:338
|
||||
#: contrib/admindocs/views.py:341
|
||||
msgid "E-mail address"
|
||||
msgstr "Adreça de correu electrònic"
|
||||
|
||||
#: contrib/admindocs/views.py:339 contrib/admindocs/views.py:340
|
||||
#: contrib/admindocs/views.py:343
|
||||
#: contrib/admindocs/views.py:342 contrib/admindocs/views.py:343
|
||||
#: contrib/admindocs/views.py:346
|
||||
msgid "File path"
|
||||
msgstr "Ruta del fitxer"
|
||||
|
||||
#: contrib/admindocs/views.py:341
|
||||
#: contrib/admindocs/views.py:344
|
||||
msgid "Floating point number"
|
||||
msgstr "Número amb punt de coma flotant"
|
||||
|
||||
#: contrib/admindocs/views.py:345 contrib/comments/models.py:60
|
||||
#: contrib/admindocs/views.py:348 contrib/comments/models.py:60
|
||||
msgid "IP address"
|
||||
msgstr "Adreça IP"
|
||||
|
||||
#: contrib/admindocs/views.py:347
|
||||
#: contrib/admindocs/views.py:350
|
||||
msgid "Boolean (Either True, False or None)"
|
||||
msgstr "Booleà (Verdader, Fals o 'None' (cap))"
|
||||
|
||||
#: contrib/admindocs/views.py:348
|
||||
#: contrib/admindocs/views.py:351
|
||||
msgid "Relation to parent model"
|
||||
msgstr "Relació amb el model pare"
|
||||
|
||||
#: contrib/admindocs/views.py:349
|
||||
#: contrib/admindocs/views.py:352
|
||||
msgid "Phone number"
|
||||
msgstr "Número de telèfon"
|
||||
|
||||
#: contrib/admindocs/views.py:354
|
||||
#: contrib/admindocs/views.py:357
|
||||
msgid "Text"
|
||||
msgstr "Text"
|
||||
|
||||
#: contrib/admindocs/views.py:355
|
||||
#: contrib/admindocs/views.py:358
|
||||
msgid "Time"
|
||||
msgstr "Hora"
|
||||
|
||||
#: contrib/admindocs/views.py:356 contrib/comments/forms.py:95
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:37
|
||||
#: contrib/admindocs/views.py:359 contrib/comments/forms.py:95
|
||||
#: contrib/flatpages/admin.py:8 contrib/flatpages/models.py:7
|
||||
msgid "URL"
|
||||
msgstr "URL"
|
||||
|
||||
#: contrib/admindocs/views.py:357
|
||||
#: contrib/admindocs/views.py:360
|
||||
msgid "U.S. state (two uppercase letters)"
|
||||
msgstr "Estat dels E.U.A. (dues lletres majúscules)"
|
||||
|
||||
#: contrib/admindocs/views.py:358
|
||||
#: contrib/admindocs/views.py:361
|
||||
msgid "XML text"
|
||||
msgstr "Text XML"
|
||||
|
||||
#: contrib/admindocs/views.py:384
|
||||
#: contrib/admindocs/views.py:387
|
||||
#, python-format
|
||||
msgid "%s does not appear to be a urlpattern object"
|
||||
msgstr "%s no sembla ser un objecte 'urlpattern'"
|
||||
@ -1438,22 +1436,54 @@ msgstr "usuaris"
|
||||
msgid "message"
|
||||
msgstr "missatge"
|
||||
|
||||
#: contrib/auth/views.py:56
|
||||
#: contrib/auth/views.py:60
|
||||
msgid "Logged out"
|
||||
msgstr "Sessió finalitzada"
|
||||
|
||||
#: contrib/auth/management/commands/createsuperuser.py:23 forms/fields.py:429
|
||||
#: contrib/auth/management/commands/createsuperuser.py:23 forms/fields.py:428
|
||||
msgid "Enter a valid e-mail address."
|
||||
msgstr "Introduïu una adreça de correu vàlida."
|
||||
|
||||
#: contrib/comments/admin.py:11
|
||||
#: contrib/comments/admin.py:12
|
||||
msgid "Content"
|
||||
msgstr "contingut"
|
||||
|
||||
#: contrib/comments/admin.py:14
|
||||
#: contrib/comments/admin.py:15
|
||||
msgid "Metadata"
|
||||
msgstr "metadades"
|
||||
|
||||
# Context problem... waitting for comments from django-i18n
|
||||
#: contrib/comments/admin.py:39
|
||||
msgid "flagged"
|
||||
msgstr "marcat"
|
||||
|
||||
#: contrib/comments/admin.py:40
|
||||
msgid "Flag selected comments"
|
||||
msgstr "Marcar els comentaris seleccionats"
|
||||
|
||||
#: contrib/comments/admin.py:43
|
||||
msgid "approved"
|
||||
msgstr "aprovat"
|
||||
|
||||
#: contrib/comments/admin.py:44
|
||||
msgid "Approve selected comments"
|
||||
msgstr "Aprovar els comentaris seleccionats"
|
||||
|
||||
#: contrib/comments/admin.py:47
|
||||
msgid "removed"
|
||||
msgstr "eliminat"
|
||||
|
||||
#: contrib/comments/admin.py:48
|
||||
msgid "Remove selected comments"
|
||||
msgstr "Eliminar els comentaris seleccionats"
|
||||
|
||||
#: contrib/comments/admin.py:60
|
||||
#, python-format
|
||||
msgid "1 comment was successfully %(action)s."
|
||||
msgid_plural "%(count)s comments were successfully %(action)s."
|
||||
msgstr[0] "1 comentari ha estat %(action)s satisfactòriament."
|
||||
msgstr[1] "%(count)s comentaris han estat %(action)s satisfactòriament."
|
||||
|
||||
#: contrib/comments/feeds.py:13
|
||||
#, python-format
|
||||
msgid "%(site_name)s comments"
|
||||
@ -1465,7 +1495,6 @@ msgid "Latest comments on %(site_name)s"
|
||||
msgstr "Últims comentaris a %(site_name)s."
|
||||
|
||||
#: contrib/comments/forms.py:93
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:34
|
||||
msgid "Name"
|
||||
msgstr "nom"
|
||||
|
||||
@ -1474,7 +1503,6 @@ msgid "Email address"
|
||||
msgstr "Adreça de correu electrònic"
|
||||
|
||||
#: contrib/comments/forms.py:96
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:35
|
||||
msgid "Comment"
|
||||
msgstr "Comentari"
|
||||
|
||||
@ -1606,7 +1634,6 @@ msgid "Really make this comment public?"
|
||||
msgstr "Realment vol fer aquest comentari públic?"
|
||||
|
||||
#: contrib/comments/templates/comments/approve.html:12
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:49
|
||||
msgid "Approve"
|
||||
msgstr "Aprovar"
|
||||
|
||||
@ -1631,7 +1658,6 @@ msgid "Really remove this comment?"
|
||||
msgstr "Realment vol eliminar aquest comentari?"
|
||||
|
||||
#: contrib/comments/templates/comments/delete.html:12
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:53
|
||||
msgid "Remove"
|
||||
msgstr "Eliminar"
|
||||
|
||||
@ -1666,39 +1692,6 @@ msgstr "Publicar"
|
||||
msgid "Preview"
|
||||
msgstr "Vista prèvia"
|
||||
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:4
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:19
|
||||
msgid "Comment moderation queue"
|
||||
msgstr "Cua de moderació de comentaris"
|
||||
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:26
|
||||
msgid "No comments to moderate"
|
||||
msgstr "No hi ha comentaris per a moderar"
|
||||
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:36
|
||||
msgid "Email"
|
||||
msgstr "Correu electrònic"
|
||||
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:38
|
||||
msgid "Authenticated?"
|
||||
msgstr "Autentificat?"
|
||||
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:39
|
||||
msgid "IP Address"
|
||||
msgstr "Adreça IP"
|
||||
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:40
|
||||
msgid "Date posted"
|
||||
msgstr "Data d'enviament"
|
||||
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:63
|
||||
msgid "yes"
|
||||
msgstr "si"
|
||||
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:63
|
||||
msgid "no"
|
||||
msgstr "no"
|
||||
|
||||
#: contrib/comments/templates/comments/posted.html:4
|
||||
msgid "Thanks for commenting"
|
||||
msgstr "Gràcies per comentar"
|
||||
@ -1793,7 +1786,7 @@ msgstr "pàgina estàtica"
|
||||
msgid "flat pages"
|
||||
msgstr "pàgines estàtiques"
|
||||
|
||||
#: contrib/formtools/wizard.py:130
|
||||
#: contrib/formtools/wizard.py:132
|
||||
msgid ""
|
||||
"We apologize, but your form has expired. Please continue filling out the "
|
||||
"form from this page."
|
||||
@ -2615,6 +2608,10 @@ msgstr "Validació invàlida del número de compte bancari."
|
||||
msgid "Enter a valid Finnish social security number."
|
||||
msgstr "Introduïu un número vàlid de la seguretat social finlandesa."
|
||||
|
||||
#: contrib/localflavor/fr/forms.py:30
|
||||
msgid "Phone numbers must be in 0X XX XX XX XX format."
|
||||
msgstr "Els números de telèfon han de estar en el format 0X XX XX XX XX."
|
||||
|
||||
#: contrib/localflavor/in_/forms.py:14
|
||||
msgid "Enter a zip code in the format XXXXXXX."
|
||||
msgstr "Introduïu un codi zip en el format XXXXXXX."
|
||||
@ -3053,7 +3050,8 @@ msgstr "Validació invàlida del número tributari (NIP)."
|
||||
#: contrib/localflavor/pl/forms.py:109
|
||||
msgid "National Business Register Number (REGON) consists of 9 or 14 digits."
|
||||
msgstr ""
|
||||
"El número nacional de registre de negocis (REGON) consisteix en 9 o 14 dígits."
|
||||
"El número nacional de registre de negocis (REGON) consisteix en 9 o 14 "
|
||||
"dígits."
|
||||
|
||||
#: contrib/localflavor/pl/forms.py:110
|
||||
msgid "Wrong checksum for the National Business Register Number (REGON)."
|
||||
@ -3943,14 +3941,14 @@ msgstr "Aquest valor ha de ser None (Cap), True (Veritat) o False (Fals)"
|
||||
msgid "Enter a valid time in HH:MM[:ss[.uuuuuu]] format."
|
||||
msgstr "Introduïu una hora vàlida en el format HH:MM[:ss[.uuuuuu]]."
|
||||
|
||||
#: db/models/fields/related.py:816
|
||||
#: db/models/fields/related.py:869
|
||||
msgid ""
|
||||
"Hold down \"Control\", or \"Command\" on a Mac, to select more than one."
|
||||
msgstr ""
|
||||
"Premeu la tecla \"Control\" -o \"Command\" en un Mac- per seleccionar més "
|
||||
"d'un valor."
|
||||
|
||||
#: db/models/fields/related.py:894
|
||||
#: db/models/fields/related.py:930
|
||||
#, python-format
|
||||
msgid "Please enter valid %(self)s IDs. The value %(value)r is invalid."
|
||||
msgid_plural ""
|
||||
@ -3962,95 +3960,95 @@ msgstr[1] ""
|
||||
"Si us plau, introduïu IDs de %(self)s vàlids. Els valors %(value)r són "
|
||||
"invàlids."
|
||||
|
||||
#: forms/fields.py:54
|
||||
#: forms/fields.py:53
|
||||
msgid "This field is required."
|
||||
msgstr "Aquest camp és obligatori."
|
||||
|
||||
#: forms/fields.py:55
|
||||
#: forms/fields.py:54
|
||||
msgid "Enter a valid value."
|
||||
msgstr "Introduïu un valor vàlid."
|
||||
|
||||
#: forms/fields.py:138
|
||||
#: forms/fields.py:137
|
||||
#, python-format
|
||||
msgid "Ensure this value has at most %(max)d characters (it has %(length)d)."
|
||||
msgstr ""
|
||||
"Assegureu-vos de que el valor té com a màxim %(max)d caràcters (en té %"
|
||||
"(length)d)."
|
||||
|
||||
#: forms/fields.py:139
|
||||
#: forms/fields.py:138
|
||||
#, python-format
|
||||
msgid "Ensure this value has at least %(min)d characters (it has %(length)d)."
|
||||
msgstr ""
|
||||
"Assegureu-vos de que el valor té com a mínim %(min)d caràcters (en té %"
|
||||
"(length)d)."
|
||||
|
||||
#: forms/fields.py:166
|
||||
#: forms/fields.py:165
|
||||
msgid "Enter a whole number."
|
||||
msgstr "Introduïu un número sencer."
|
||||
|
||||
#: forms/fields.py:167 forms/fields.py:196 forms/fields.py:225
|
||||
#: forms/fields.py:166 forms/fields.py:195 forms/fields.py:224
|
||||
#, python-format
|
||||
msgid "Ensure this value is less than or equal to %s."
|
||||
msgstr "Aquest valor ha de ser menor o igual a %s."
|
||||
|
||||
#: forms/fields.py:168 forms/fields.py:197 forms/fields.py:226
|
||||
#: forms/fields.py:167 forms/fields.py:196 forms/fields.py:225
|
||||
#, python-format
|
||||
msgid "Ensure this value is greater than or equal to %s."
|
||||
msgstr "Assegureu-vos de que aquest valor sigui superior o igual a %s."
|
||||
|
||||
#: forms/fields.py:195 forms/fields.py:224
|
||||
#: forms/fields.py:194 forms/fields.py:223
|
||||
msgid "Enter a number."
|
||||
msgstr "Introduïu un número."
|
||||
|
||||
#: forms/fields.py:227
|
||||
#: forms/fields.py:226
|
||||
#, python-format
|
||||
msgid "Ensure that there are no more than %s digits in total."
|
||||
msgstr "Assegureu-vos de que no hi ha més de %s dígits en total."
|
||||
|
||||
#: forms/fields.py:228
|
||||
#: forms/fields.py:227
|
||||
#, python-format
|
||||
msgid "Ensure that there are no more than %s decimal places."
|
||||
msgstr "Assegureu-vos de que no hi ha més de %s decimals."
|
||||
|
||||
#: forms/fields.py:229
|
||||
#: forms/fields.py:228
|
||||
#, python-format
|
||||
msgid "Ensure that there are no more than %s digits before the decimal point."
|
||||
msgstr "Assegureu-vos de que no hi ha més de %s dígits decimals."
|
||||
|
||||
#: forms/fields.py:288 forms/fields.py:863
|
||||
#: forms/fields.py:287 forms/fields.py:862
|
||||
msgid "Enter a valid date."
|
||||
msgstr "Introduïu una data vàlida."
|
||||
|
||||
#: forms/fields.py:322 forms/fields.py:864
|
||||
#: forms/fields.py:321 forms/fields.py:863
|
||||
msgid "Enter a valid time."
|
||||
msgstr "Introduïu una hora vàlida."
|
||||
|
||||
#: forms/fields.py:361
|
||||
#: forms/fields.py:360
|
||||
msgid "Enter a valid date/time."
|
||||
msgstr "Introduïu una data/hora vàlides."
|
||||
|
||||
#: forms/fields.py:447
|
||||
#: forms/fields.py:446
|
||||
msgid "No file was submitted. Check the encoding type on the form."
|
||||
msgstr ""
|
||||
"No s'ha enviat cap fitxer. Comprovi el tipus de codificació del formulari."
|
||||
|
||||
#: forms/fields.py:448
|
||||
#: forms/fields.py:447
|
||||
msgid "No file was submitted."
|
||||
msgstr "No s'ha enviat cap fitxer."
|
||||
|
||||
#: forms/fields.py:449
|
||||
#: forms/fields.py:448
|
||||
msgid "The submitted file is empty."
|
||||
msgstr "El fitxer enviat està buit."
|
||||
|
||||
#: forms/fields.py:450
|
||||
#: forms/fields.py:449
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Ensure this filename has at most %(max)d characters (it has %(length)d)."
|
||||
msgstr ""
|
||||
"Assegureu-vos de que el valor té com a màxim %(max)d caràcters "
|
||||
"(en té %(length)d)."
|
||||
"Assegureu-vos de que el valor té com a màxim %(max)d caràcters (en té %"
|
||||
"(length)d)."
|
||||
|
||||
#: forms/fields.py:483
|
||||
#: forms/fields.py:482
|
||||
msgid ""
|
||||
"Upload a valid image. The file you uploaded was either not an image or a "
|
||||
"corrupted image."
|
||||
@ -4058,28 +4056,28 @@ msgstr ""
|
||||
"Envieu una imatge vàlida. El fitxer que heu enviat no era una imatge o "
|
||||
"estava corrupte."
|
||||
|
||||
#: forms/fields.py:544
|
||||
#: forms/fields.py:543
|
||||
msgid "Enter a valid URL."
|
||||
msgstr "Introduïu una URL vàlida."
|
||||
|
||||
#: forms/fields.py:545
|
||||
#: forms/fields.py:544
|
||||
msgid "This URL appears to be a broken link."
|
||||
msgstr "Aquesta URL sembla ser un enllaç trencat."
|
||||
|
||||
#: forms/fields.py:625 forms/fields.py:703
|
||||
#: forms/fields.py:624 forms/fields.py:702
|
||||
#, python-format
|
||||
msgid "Select a valid choice. %(value)s is not one of the available choices."
|
||||
msgstr "Esculliu una opció vàlida. %(value)s no és una de les opcions vàlides."
|
||||
|
||||
#: forms/fields.py:704 forms/fields.py:765 forms/models.py:1003
|
||||
#: forms/fields.py:703 forms/fields.py:764 forms/models.py:999
|
||||
msgid "Enter a list of values."
|
||||
msgstr "Introduïu una llista de valors."
|
||||
|
||||
#: forms/fields.py:892
|
||||
#: forms/fields.py:891
|
||||
msgid "Enter a valid IPv4 address."
|
||||
msgstr "Introduïu una adreça IPv4 vàlida."
|
||||
|
||||
#: forms/fields.py:902
|
||||
#: forms/fields.py:901
|
||||
msgid ""
|
||||
"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."
|
||||
msgstr ""
|
||||
@ -4090,56 +4088,58 @@ msgstr ""
|
||||
msgid "Order"
|
||||
msgstr "Ordre"
|
||||
|
||||
#: forms/models.py:367
|
||||
#: forms/models.py:363
|
||||
#, python-format
|
||||
msgid "%(field_name)s must be unique for %(date_field)s %(lookup)s."
|
||||
msgstr "El camp %(field_name)s ha de ser únic per a %(lookup)s %(date_field)s."
|
||||
|
||||
#: forms/models.py:381 forms/models.py:389
|
||||
#: forms/models.py:377 forms/models.py:385
|
||||
#, python-format
|
||||
msgid "%(model_name)s with this %(field_label)s already exists."
|
||||
msgstr "Ja existeix %(model_name)s amb aquest %(field_label)s."
|
||||
|
||||
#: forms/models.py:594
|
||||
#: forms/models.py:590
|
||||
#, python-format
|
||||
msgid "Please correct the duplicate data for %(field)s."
|
||||
msgstr "Si us plau, corregiu la dada duplicada per a %(field)s."
|
||||
|
||||
#: forms/models.py:598
|
||||
#: forms/models.py:594
|
||||
#, python-format
|
||||
msgid "Please correct the duplicate data for %(field)s, which must be unique."
|
||||
msgstr "Si us plau, corregiu la dada duplicada per a %(field)s, la qual ha de ser única."
|
||||
msgstr ""
|
||||
"Si us plau, corregiu la dada duplicada per a %(field)s, la qual ha de ser "
|
||||
"única."
|
||||
|
||||
#: forms/models.py:604
|
||||
#: forms/models.py:600
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Please correct the duplicate data for %(field_name)s which must be unique "
|
||||
"for the %(lookup)s in %(date_field)s."
|
||||
msgstr ""
|
||||
"Si us plau, corregiu la dada duplicada per a %(field_name)s, "
|
||||
"la qual ha de ser única per a la cerca %(lookup)s en %(date_field)s."
|
||||
"Si us plau, corregiu la dada duplicada per a %(field_name)s, la qual ha de "
|
||||
"ser única per a la cerca %(lookup)s en %(date_field)s."
|
||||
|
||||
#: forms/models.py:612
|
||||
#: forms/models.py:608
|
||||
msgid "Please correct the duplicate values below."
|
||||
msgstr "Si us plau, corregiu els valors duplicats a baix."
|
||||
|
||||
#: forms/models.py:867
|
||||
#: forms/models.py:863
|
||||
msgid "The inline foreign key did not match the parent instance primary key."
|
||||
msgstr ""
|
||||
"La clau forànea en línea no coincideix amb la clau primària de la instància "
|
||||
"del pare"
|
||||
|
||||
#: forms/models.py:930
|
||||
#: forms/models.py:926
|
||||
msgid "Select a valid choice. That choice is not one of the available choices."
|
||||
msgstr ""
|
||||
"Escolli una opció vàlida; Aquesta opció no és una de les opcions disponibles."
|
||||
|
||||
#: forms/models.py:1004
|
||||
#: forms/models.py:1000
|
||||
#, python-format
|
||||
msgid "Select a valid choice. %s is not one of the available choices."
|
||||
msgstr "Escolliu una opció vàlida; %s' no és una de les opcions vàlides."
|
||||
|
||||
#: forms/models.py:1006
|
||||
#: forms/models.py:1002
|
||||
#, python-format
|
||||
msgid "\"%s\" is not a valid value for a primary key."
|
||||
msgstr "\"%s\" no és un valor vàlid per a una clau primària."
|
||||
@ -4459,6 +4459,30 @@ msgstr "El/la %(verbose_name)s s'ha actualtzat amb èxit."
|
||||
msgid "The %(verbose_name)s was deleted."
|
||||
msgstr "El %(verbose_name)s s'ha eliminat."
|
||||
|
||||
#~ msgid "Comment moderation queue"
|
||||
#~ msgstr "Cua de moderació de comentaris"
|
||||
|
||||
#~ msgid "No comments to moderate"
|
||||
#~ msgstr "No hi ha comentaris per a moderar"
|
||||
|
||||
#~ msgid "Email"
|
||||
#~ msgstr "Correu electrònic"
|
||||
|
||||
#~ msgid "Authenticated?"
|
||||
#~ msgstr "Autentificat?"
|
||||
|
||||
#~ msgid "IP Address"
|
||||
#~ msgstr "Adreça IP"
|
||||
|
||||
#~ msgid "Date posted"
|
||||
#~ msgstr "Data d'enviament"
|
||||
|
||||
#~ msgid "yes"
|
||||
#~ msgstr "si"
|
||||
|
||||
#~ msgid "no"
|
||||
#~ msgstr "no"
|
||||
|
||||
#, fuzzy
|
||||
#~ msgid "verbose_name"
|
||||
#~ msgid_plural "verbose_name_plural"
|
||||
|
Binary file not shown.
@ -5,8 +5,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2009-07-07 15:15+0200\n"
|
||||
"PO-Revision-Date: 2009-07-07 15:22+0200\n"
|
||||
"POT-Creation-Date: 2009-11-30 11:27+0100\n"
|
||||
"PO-Revision-Date: 2009-11-30 11:31+0100\n"
|
||||
"Last-Translator: Django Spanish Team <django-cat@googlegroups.com>Language-"
|
||||
"Team: Django Spanish Team <django-cat@googlegroups.com>MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -222,7 +222,7 @@ msgstr "chino tradicional"
|
||||
msgid "Successfully deleted %(count)d %(items)s."
|
||||
msgstr "Eliminado/s %(count)d %(items)s satisfactoriamente."
|
||||
|
||||
#: contrib/admin/actions.py:67 contrib/admin/options.py:1025
|
||||
#: contrib/admin/actions.py:67 contrib/admin/options.py:1033
|
||||
msgid "Are you sure?"
|
||||
msgstr "¿Está seguro?"
|
||||
|
||||
@ -265,15 +265,15 @@ msgstr "Este mes"
|
||||
msgid "This year"
|
||||
msgstr "Este año"
|
||||
|
||||
#: contrib/admin/filterspecs.py:147 forms/widgets.py:434
|
||||
#: contrib/admin/filterspecs.py:147 forms/widgets.py:435
|
||||
msgid "Yes"
|
||||
msgstr "Sí"
|
||||
|
||||
#: contrib/admin/filterspecs.py:147 forms/widgets.py:434
|
||||
#: contrib/admin/filterspecs.py:147 forms/widgets.py:435
|
||||
msgid "No"
|
||||
msgstr "No"
|
||||
|
||||
#: contrib/admin/filterspecs.py:154 forms/widgets.py:434
|
||||
#: contrib/admin/filterspecs.py:154 forms/widgets.py:435
|
||||
msgid "Unknown"
|
||||
msgstr "Desconocido"
|
||||
|
||||
@ -309,104 +309,104 @@ msgstr "entrada de registro"
|
||||
msgid "log entries"
|
||||
msgstr "entradas de registro"
|
||||
|
||||
#: contrib/admin/options.py:133 contrib/admin/options.py:147
|
||||
#: contrib/admin/options.py:134 contrib/admin/options.py:148
|
||||
msgid "None"
|
||||
msgstr "Ninguno"
|
||||
|
||||
#: contrib/admin/options.py:519
|
||||
#: contrib/admin/options.py:521
|
||||
#, python-format
|
||||
msgid "Changed %s."
|
||||
msgstr "Modificado/a %s."
|
||||
|
||||
#: contrib/admin/options.py:519 contrib/admin/options.py:529
|
||||
#: contrib/comments/templates/comments/preview.html:16 forms/models.py:388
|
||||
#: forms/models.py:600
|
||||
#: contrib/admin/options.py:521 contrib/admin/options.py:531
|
||||
#: contrib/comments/templates/comments/preview.html:16 forms/models.py:384
|
||||
#: forms/models.py:596
|
||||
msgid "and"
|
||||
msgstr "y"
|
||||
|
||||
#: contrib/admin/options.py:524
|
||||
#: contrib/admin/options.py:526
|
||||
#, python-format
|
||||
msgid "Added %(name)s \"%(object)s\"."
|
||||
msgstr "Añadido/a \"%(object)s\" %(name)s."
|
||||
|
||||
#: contrib/admin/options.py:528
|
||||
#: contrib/admin/options.py:530
|
||||
#, python-format
|
||||
msgid "Changed %(list)s for %(name)s \"%(object)s\"."
|
||||
msgstr "Modificados %(list)s para \"%(object)s\" %(name)s."
|
||||
|
||||
#: contrib/admin/options.py:533
|
||||
#: contrib/admin/options.py:535
|
||||
#, python-format
|
||||
msgid "Deleted %(name)s \"%(object)s\"."
|
||||
msgstr "Eliminado/a \"%(object)s\" %(name)s."
|
||||
|
||||
#: contrib/admin/options.py:537
|
||||
#: contrib/admin/options.py:539
|
||||
msgid "No fields changed."
|
||||
msgstr "No ha cambiado ningún campo."
|
||||
|
||||
#: contrib/admin/options.py:598 contrib/auth/admin.py:67
|
||||
#: contrib/admin/options.py:601 contrib/auth/admin.py:67
|
||||
#, python-format
|
||||
msgid "The %(name)s \"%(obj)s\" was added successfully."
|
||||
msgstr "Se añadió con éxito el %(name)s \"%(obj)s\"."
|
||||
|
||||
#: contrib/admin/options.py:602 contrib/admin/options.py:635
|
||||
#: contrib/admin/options.py:605 contrib/admin/options.py:638
|
||||
#: contrib/auth/admin.py:75
|
||||
msgid "You may edit it again below."
|
||||
msgstr "Puede editarlo de nuevo abajo."
|
||||
|
||||
#: contrib/admin/options.py:612 contrib/admin/options.py:645
|
||||
#: contrib/admin/options.py:615 contrib/admin/options.py:648
|
||||
#, python-format
|
||||
msgid "You may add another %s below."
|
||||
msgstr "Puede añadir otro %s abajo."
|
||||
|
||||
#: contrib/admin/options.py:633
|
||||
#: contrib/admin/options.py:636
|
||||
#, python-format
|
||||
msgid "The %(name)s \"%(obj)s\" was changed successfully."
|
||||
msgstr "Se modificó con éxito el %(name)s \"%(obj)s\"."
|
||||
|
||||
#: contrib/admin/options.py:641
|
||||
#: contrib/admin/options.py:644
|
||||
#, python-format
|
||||
msgid ""
|
||||
"The %(name)s \"%(obj)s\" was added successfully. You may edit it again below."
|
||||
msgstr ""
|
||||
"Se añadió con éxito el %(name)s \"%(obj)s. Puede editarlo de nuevo abajo."
|
||||
|
||||
#: contrib/admin/options.py:772
|
||||
#: contrib/admin/options.py:777
|
||||
#, python-format
|
||||
msgid "Add %s"
|
||||
msgstr "Añadir %s"
|
||||
|
||||
#: contrib/admin/options.py:803 contrib/admin/options.py:1003
|
||||
#: contrib/admin/options.py:809 contrib/admin/options.py:1011
|
||||
#, python-format
|
||||
msgid "%(name)s object with primary key %(key)r does not exist."
|
||||
msgstr "No existe ningún objeto %(name)s con la clave primaria %(key)r."
|
||||
|
||||
#: contrib/admin/options.py:860
|
||||
#: contrib/admin/options.py:866
|
||||
#, python-format
|
||||
msgid "Change %s"
|
||||
msgstr "Modificar %s"
|
||||
|
||||
#: contrib/admin/options.py:904
|
||||
#: contrib/admin/options.py:910
|
||||
msgid "Database error"
|
||||
msgstr "Error en la base de datos"
|
||||
|
||||
#: contrib/admin/options.py:940
|
||||
#: contrib/admin/options.py:946
|
||||
#, python-format
|
||||
msgid "%(count)s %(name)s was changed successfully."
|
||||
msgid_plural "%(count)s %(name)s were changed successfully."
|
||||
msgstr[0] "%(count)s %(name)s fué modificado con éxito."
|
||||
msgstr[1] "%(count)s %(name)s fueron modificados con éxito."
|
||||
|
||||
#: contrib/admin/options.py:1018
|
||||
#: contrib/admin/options.py:1026
|
||||
#, python-format
|
||||
msgid "The %(name)s \"%(obj)s\" was deleted successfully."
|
||||
msgstr "Se eliminó con éxito el %(name)s \"%(obj)s\"."
|
||||
|
||||
#: contrib/admin/options.py:1054
|
||||
#: contrib/admin/options.py:1063
|
||||
#, python-format
|
||||
msgid "Change history: %s"
|
||||
msgstr "Histórico de modificaciones: %s"
|
||||
|
||||
#: contrib/admin/sites.py:20 contrib/admin/views/decorators.py:14
|
||||
#: contrib/admin/sites.py:22 contrib/admin/views/decorators.py:14
|
||||
#: contrib/auth/forms.py:80
|
||||
msgid ""
|
||||
"Please enter a correct username and password. Note that both fields are case-"
|
||||
@ -415,11 +415,11 @@ msgstr ""
|
||||
"Por favor, introduzca un nombre de usuario y contraseña correctos. Note que "
|
||||
"ambos campos son sensibles a mayúsculas/minúsculas."
|
||||
|
||||
#: contrib/admin/sites.py:278 contrib/admin/views/decorators.py:40
|
||||
#: contrib/admin/sites.py:292 contrib/admin/views/decorators.py:40
|
||||
msgid "Please log in again, because your session has expired."
|
||||
msgstr "Por favor, inicie sesión de nuevo, ya que su sesión ha caducado."
|
||||
|
||||
#: contrib/admin/sites.py:285 contrib/admin/views/decorators.py:47
|
||||
#: contrib/admin/sites.py:299 contrib/admin/views/decorators.py:47
|
||||
msgid ""
|
||||
"Looks like your browser isn't configured to accept cookies. Please enable "
|
||||
"cookies, reload this page, and try again."
|
||||
@ -427,29 +427,29 @@ msgstr ""
|
||||
"Parece que su navegador no está configurado para aceptar cookies. "
|
||||
"Actívelas , recargue esta página, e inténtelo de nuevo."
|
||||
|
||||
#: contrib/admin/sites.py:301 contrib/admin/sites.py:307
|
||||
#: contrib/admin/sites.py:315 contrib/admin/sites.py:321
|
||||
#: contrib/admin/views/decorators.py:66
|
||||
msgid "Usernames cannot contain the '@' character."
|
||||
msgstr "Los nombres de usuario no pueden contener el carácter '@'."
|
||||
|
||||
#: contrib/admin/sites.py:304 contrib/admin/views/decorators.py:62
|
||||
#: contrib/admin/sites.py:318 contrib/admin/views/decorators.py:62
|
||||
#, python-format
|
||||
msgid "Your e-mail address is not your username. Try '%s' instead."
|
||||
msgstr ""
|
||||
"Su dirección de correo no es su nombre de usuario. Pruebe con '%s' en su "
|
||||
"lugar."
|
||||
|
||||
#: contrib/admin/sites.py:360
|
||||
#: contrib/admin/sites.py:374
|
||||
msgid "Site administration"
|
||||
msgstr "Sitio administrativo"
|
||||
|
||||
#: contrib/admin/sites.py:373 contrib/admin/templates/admin/login.html:26
|
||||
#: contrib/admin/sites.py:388 contrib/admin/templates/admin/login.html:26
|
||||
#: contrib/admin/templates/registration/password_reset_complete.html:14
|
||||
#: contrib/admin/views/decorators.py:20
|
||||
msgid "Log in"
|
||||
msgstr "Iniciar sesión"
|
||||
|
||||
#: contrib/admin/sites.py:417
|
||||
#: contrib/admin/sites.py:433
|
||||
#, python-format
|
||||
msgid "%s administration"
|
||||
msgstr "Administración de %s"
|
||||
@ -464,27 +464,27 @@ msgstr "Uno o más %(fieldname)s en %(name)s: %(obj)s"
|
||||
msgid "One or more %(fieldname)s in %(name)s:"
|
||||
msgstr "Uno o más %(fieldname)s en %(name)s:"
|
||||
|
||||
#: contrib/admin/widgets.py:71
|
||||
#: contrib/admin/widgets.py:72
|
||||
msgid "Date:"
|
||||
msgstr "Fecha:"
|
||||
|
||||
#: contrib/admin/widgets.py:71
|
||||
#: contrib/admin/widgets.py:72
|
||||
msgid "Time:"
|
||||
msgstr "Hora:"
|
||||
|
||||
#: contrib/admin/widgets.py:95
|
||||
#: contrib/admin/widgets.py:96
|
||||
msgid "Currently:"
|
||||
msgstr "Actualmente:"
|
||||
|
||||
#: contrib/admin/widgets.py:95
|
||||
#: contrib/admin/widgets.py:96
|
||||
msgid "Change:"
|
||||
msgstr "Modificar:"
|
||||
|
||||
#: contrib/admin/widgets.py:124
|
||||
#: contrib/admin/widgets.py:125
|
||||
msgid "Lookup"
|
||||
msgstr "Buscar"
|
||||
|
||||
#: contrib/admin/widgets.py:236
|
||||
#: contrib/admin/widgets.py:237
|
||||
msgid "Add Another"
|
||||
msgstr "Añadir otro"
|
||||
|
||||
@ -499,7 +499,7 @@ msgstr "Lo sentimos, pero no se encuentra la página solicitada."
|
||||
|
||||
#: contrib/admin/templates/admin/500.html:4
|
||||
#: contrib/admin/templates/admin/app_index.html:8
|
||||
#: contrib/admin/templates/admin/base.html:31
|
||||
#: contrib/admin/templates/admin/base.html:54
|
||||
#: contrib/admin/templates/admin/change_form.html:17
|
||||
#: contrib/admin/templates/admin/change_list.html:25
|
||||
#: contrib/admin/templates/admin/delete_confirmation.html:6
|
||||
@ -553,18 +553,18 @@ msgstr "Ir"
|
||||
msgid "%(name)s"
|
||||
msgstr "%(name)s"
|
||||
|
||||
#: contrib/admin/templates/admin/base.html:26
|
||||
#: contrib/admin/templates/admin/base.html:27
|
||||
msgid "Welcome,"
|
||||
msgstr "Bienvenido/a,"
|
||||
|
||||
#: contrib/admin/templates/admin/base.html:26
|
||||
#: contrib/admin/templates/admin/base.html:32
|
||||
#: contrib/admin/templates/registration/password_change_done.html:3
|
||||
#: contrib/admin/templates/registration/password_change_form.html:3
|
||||
#: contrib/admindocs/templates/admin_doc/bookmarklets.html:3
|
||||
msgid "Documentation"
|
||||
msgstr "Documentación"
|
||||
|
||||
#: contrib/admin/templates/admin/base.html:26
|
||||
#: contrib/admin/templates/admin/base.html:40
|
||||
#: contrib/admin/templates/admin/auth/user/change_password.html:14
|
||||
#: contrib/admin/templates/admin/auth/user/change_password.html:47
|
||||
#: contrib/admin/templates/registration/password_change_done.html:3
|
||||
@ -572,7 +572,7 @@ msgstr "Documentación"
|
||||
msgid "Change password"
|
||||
msgstr "Cambiar contraseña"
|
||||
|
||||
#: contrib/admin/templates/admin/base.html:26
|
||||
#: contrib/admin/templates/admin/base.html:47
|
||||
#: contrib/admin/templates/registration/password_change_done.html:3
|
||||
#: contrib/admin/templates/registration/password_change_form.html:3
|
||||
msgid "Log out"
|
||||
@ -598,7 +598,7 @@ msgstr "Histórico"
|
||||
|
||||
#: contrib/admin/templates/admin/change_form.html:28
|
||||
#: contrib/admin/templates/admin/edit_inline/stacked.html:13
|
||||
#: contrib/admin/templates/admin/edit_inline/tabular.html:27
|
||||
#: contrib/admin/templates/admin/edit_inline/tabular.html:28
|
||||
msgid "View on site"
|
||||
msgstr "Ver en el sitio"
|
||||
|
||||
@ -668,9 +668,9 @@ msgstr ""
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Are you sure you want to delete the selected %(object_name)s objects? All of "
|
||||
"the following objects and it's related items will be deleted:"
|
||||
"the following objects and their related items will be deleted:"
|
||||
msgstr ""
|
||||
"¿Está seguro de que quiere borrar los %(object_name)s? Los siguientes "
|
||||
"¿Está seguro de que quiere eliminar los %(object_name)s seleccionados? Los siguientes "
|
||||
"objetos y sus elementos relacionados serán eliminados:"
|
||||
|
||||
#: contrib/admin/templates/admin/filter.html:2
|
||||
@ -734,7 +734,6 @@ msgid "User"
|
||||
msgstr "Usuario"
|
||||
|
||||
#: contrib/admin/templates/admin/object_history.html:24
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:33
|
||||
msgid "Action"
|
||||
msgstr "Acción"
|
||||
|
||||
@ -987,7 +986,7 @@ msgstr "Dirección de correo electrónico:"
|
||||
msgid "Reset my password"
|
||||
msgstr "Restablecer mi contraseña"
|
||||
|
||||
#: contrib/admin/templatetags/admin_list.py:299
|
||||
#: contrib/admin/templatetags/admin_list.py:304
|
||||
msgid "All dates"
|
||||
msgstr "Todas las fechas"
|
||||
|
||||
@ -1009,146 +1008,145 @@ msgstr "sitio"
|
||||
msgid "template"
|
||||
msgstr "plantilla"
|
||||
|
||||
#: contrib/admindocs/views.py:58 contrib/admindocs/views.py:60
|
||||
#: contrib/admindocs/views.py:62
|
||||
#: contrib/admindocs/views.py:61 contrib/admindocs/views.py:63
|
||||
#: contrib/admindocs/views.py:65
|
||||
msgid "tag:"
|
||||
msgstr "etiqueta:"
|
||||
|
||||
#: contrib/admindocs/views.py:91 contrib/admindocs/views.py:93
|
||||
#: contrib/admindocs/views.py:95
|
||||
#: contrib/admindocs/views.py:94 contrib/admindocs/views.py:96
|
||||
#: contrib/admindocs/views.py:98
|
||||
msgid "filter:"
|
||||
msgstr "filtro:"
|
||||
|
||||
#: contrib/admindocs/views.py:155 contrib/admindocs/views.py:157
|
||||
#: contrib/admindocs/views.py:159
|
||||
#: contrib/admindocs/views.py:158 contrib/admindocs/views.py:160
|
||||
#: contrib/admindocs/views.py:162
|
||||
msgid "view:"
|
||||
msgstr "vista:"
|
||||
|
||||
#: contrib/admindocs/views.py:187
|
||||
#: contrib/admindocs/views.py:190
|
||||
#, python-format
|
||||
msgid "App %r not found"
|
||||
msgstr "Aplicación %r no encontrada"
|
||||
|
||||
#: contrib/admindocs/views.py:194
|
||||
#: contrib/admindocs/views.py:197
|
||||
#, python-format
|
||||
msgid "Model %(model_name)r not found in app %(app_label)r"
|
||||
msgstr ""
|
||||
"El modelo %(model_name)r no se ha encontrado en la aplicación %(app_label)r"
|
||||
|
||||
#: contrib/admindocs/views.py:206
|
||||
#: contrib/admindocs/views.py:209
|
||||
#, python-format
|
||||
msgid "the related `%(app_label)s.%(data_type)s` object"
|
||||
msgstr "el objeto relacionado `%(app_label)s.%(data_type)s`"
|
||||
|
||||
#: contrib/admindocs/views.py:206 contrib/admindocs/views.py:225
|
||||
#: contrib/admindocs/views.py:230 contrib/admindocs/views.py:244
|
||||
#: contrib/admindocs/views.py:258 contrib/admindocs/views.py:263
|
||||
#: contrib/admindocs/views.py:209 contrib/admindocs/views.py:228
|
||||
#: contrib/admindocs/views.py:233 contrib/admindocs/views.py:247
|
||||
#: contrib/admindocs/views.py:261 contrib/admindocs/views.py:266
|
||||
msgid "model:"
|
||||
msgstr "modelo:"
|
||||
|
||||
#: contrib/admindocs/views.py:221 contrib/admindocs/views.py:253
|
||||
#: contrib/admindocs/views.py:224 contrib/admindocs/views.py:256
|
||||
#, python-format
|
||||
msgid "related `%(app_label)s.%(object_name)s` objects"
|
||||
msgstr "los objetos relacionados `%(app_label)s.%(object_name)s`"
|
||||
|
||||
#: contrib/admindocs/views.py:225 contrib/admindocs/views.py:258
|
||||
#: contrib/admindocs/views.py:228 contrib/admindocs/views.py:261
|
||||
#, python-format
|
||||
msgid "all %s"
|
||||
msgstr "todo %s"
|
||||
|
||||
#: contrib/admindocs/views.py:230 contrib/admindocs/views.py:263
|
||||
#: contrib/admindocs/views.py:233 contrib/admindocs/views.py:266
|
||||
#, python-format
|
||||
msgid "number of %s"
|
||||
msgstr "número de %s"
|
||||
|
||||
#: contrib/admindocs/views.py:268
|
||||
#: contrib/admindocs/views.py:271
|
||||
#, python-format
|
||||
msgid "Fields on %s objects"
|
||||
msgstr "Campos en %s objetos"
|
||||
|
||||
#: contrib/admindocs/views.py:331 contrib/admindocs/views.py:342
|
||||
#: contrib/admindocs/views.py:344 contrib/admindocs/views.py:350
|
||||
#: contrib/admindocs/views.py:351 contrib/admindocs/views.py:353
|
||||
#: contrib/admindocs/views.py:334 contrib/admindocs/views.py:345
|
||||
#: contrib/admindocs/views.py:347 contrib/admindocs/views.py:353
|
||||
#: contrib/admindocs/views.py:354 contrib/admindocs/views.py:356
|
||||
msgid "Integer"
|
||||
msgstr "Entero"
|
||||
|
||||
#: contrib/admindocs/views.py:332
|
||||
#: contrib/admindocs/views.py:335
|
||||
msgid "Boolean (Either True or False)"
|
||||
msgstr "Booleano (Verdadero o Falso)"
|
||||
|
||||
#: contrib/admindocs/views.py:333 contrib/admindocs/views.py:352
|
||||
#: contrib/admindocs/views.py:336 contrib/admindocs/views.py:355
|
||||
#, python-format
|
||||
msgid "String (up to %(max_length)s)"
|
||||
msgstr "Cadena (máximo %(max_length)s)"
|
||||
|
||||
#: contrib/admindocs/views.py:334
|
||||
#: contrib/admindocs/views.py:337
|
||||
msgid "Comma-separated integers"
|
||||
msgstr "Enteros separados por comas"
|
||||
|
||||
#: contrib/admindocs/views.py:335
|
||||
#: contrib/admindocs/views.py:338
|
||||
msgid "Date (without time)"
|
||||
msgstr "Fecha (sin hora)"
|
||||
|
||||
#: contrib/admindocs/views.py:336
|
||||
#: contrib/admindocs/views.py:339
|
||||
msgid "Date (with time)"
|
||||
msgstr "Fecha (con hora)"
|
||||
|
||||
#: contrib/admindocs/views.py:337
|
||||
#: contrib/admindocs/views.py:340
|
||||
msgid "Decimal number"
|
||||
msgstr "Número decimal"
|
||||
|
||||
#: contrib/admindocs/views.py:338
|
||||
#: contrib/admindocs/views.py:341
|
||||
msgid "E-mail address"
|
||||
msgstr "Dirección de correo electrónico"
|
||||
|
||||
#: contrib/admindocs/views.py:339 contrib/admindocs/views.py:340
|
||||
#: contrib/admindocs/views.py:343
|
||||
#: contrib/admindocs/views.py:342 contrib/admindocs/views.py:343
|
||||
#: contrib/admindocs/views.py:346
|
||||
msgid "File path"
|
||||
msgstr "Ruta de fichero"
|
||||
|
||||
#: contrib/admindocs/views.py:341
|
||||
#: contrib/admindocs/views.py:344
|
||||
msgid "Floating point number"
|
||||
msgstr "Número en coma flotante"
|
||||
|
||||
#: contrib/admindocs/views.py:345 contrib/comments/models.py:60
|
||||
#: contrib/admindocs/views.py:348 contrib/comments/models.py:60
|
||||
msgid "IP address"
|
||||
msgstr "Dirección IP"
|
||||
|
||||
#: contrib/admindocs/views.py:347
|
||||
#: contrib/admindocs/views.py:350
|
||||
msgid "Boolean (Either True, False or None)"
|
||||
msgstr "Booleano (Verdadero, Falso o Nulo)"
|
||||
|
||||
#: contrib/admindocs/views.py:348
|
||||
#: contrib/admindocs/views.py:351
|
||||
msgid "Relation to parent model"
|
||||
msgstr "Relación con el modelo padre"
|
||||
|
||||
#: contrib/admindocs/views.py:349
|
||||
#: contrib/admindocs/views.py:352
|
||||
msgid "Phone number"
|
||||
msgstr "Número de teléfono"
|
||||
|
||||
#: contrib/admindocs/views.py:354
|
||||
#: contrib/admindocs/views.py:357
|
||||
msgid "Text"
|
||||
msgstr "Texto"
|
||||
|
||||
#: contrib/admindocs/views.py:355
|
||||
#: contrib/admindocs/views.py:358
|
||||
msgid "Time"
|
||||
msgstr "Hora"
|
||||
|
||||
#: contrib/admindocs/views.py:356 contrib/comments/forms.py:95
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:37
|
||||
#: contrib/admindocs/views.py:359 contrib/comments/forms.py:95
|
||||
#: contrib/flatpages/admin.py:8 contrib/flatpages/models.py:7
|
||||
msgid "URL"
|
||||
msgstr "URL"
|
||||
|
||||
#: contrib/admindocs/views.py:357
|
||||
#: contrib/admindocs/views.py:360
|
||||
msgid "U.S. state (two uppercase letters)"
|
||||
msgstr "Estado de los EEUU (dos letras mayúsculas)"
|
||||
|
||||
#: contrib/admindocs/views.py:358
|
||||
#: contrib/admindocs/views.py:361
|
||||
msgid "XML text"
|
||||
msgstr "Texto XML"
|
||||
|
||||
#: contrib/admindocs/views.py:384
|
||||
#: contrib/admindocs/views.py:387
|
||||
#, python-format
|
||||
msgid "%s does not appear to be a urlpattern object"
|
||||
msgstr "%s no parece ser un objeto urlpattern"
|
||||
@ -1441,22 +1439,53 @@ msgstr "usuarios"
|
||||
msgid "message"
|
||||
msgstr "mensaje"
|
||||
|
||||
#: contrib/auth/views.py:56
|
||||
#: contrib/auth/views.py:60
|
||||
msgid "Logged out"
|
||||
msgstr "Sesión terminada"
|
||||
|
||||
#: contrib/auth/management/commands/createsuperuser.py:23 forms/fields.py:429
|
||||
#: contrib/auth/management/commands/createsuperuser.py:23 forms/fields.py:428
|
||||
msgid "Enter a valid e-mail address."
|
||||
msgstr "Introduzca una dirección de correo electrónico válida."
|
||||
|
||||
#: contrib/comments/admin.py:11
|
||||
#: contrib/comments/admin.py:12
|
||||
msgid "Content"
|
||||
msgstr "contenido"
|
||||
|
||||
#: contrib/comments/admin.py:14
|
||||
#: contrib/comments/admin.py:15
|
||||
msgid "Metadata"
|
||||
msgstr "metadatos"
|
||||
|
||||
#: contrib/comments/admin.py:39
|
||||
msgid "flagged"
|
||||
msgstr "marcado"
|
||||
|
||||
#: contrib/comments/admin.py:40
|
||||
msgid "Flag selected comments"
|
||||
msgstr "Marcar los comentarios seleccionados"
|
||||
|
||||
#: contrib/comments/admin.py:43
|
||||
msgid "approved"
|
||||
msgstr "aprobado"
|
||||
|
||||
#: contrib/comments/admin.py:44
|
||||
msgid "Approve selected comments"
|
||||
msgstr "aprobar los comentarios seleccionados"
|
||||
|
||||
#: contrib/comments/admin.py:47
|
||||
msgid "removed"
|
||||
msgstr "eliminado"
|
||||
|
||||
#: contrib/comments/admin.py:48
|
||||
msgid "Remove selected comments"
|
||||
msgstr "Eliminar los comentarios seleccionados"
|
||||
|
||||
#: contrib/comments/admin.py:60
|
||||
#, python-format
|
||||
msgid "1 comment was successfully %(action)s."
|
||||
msgid_plural "%(count)s comments were successfully %(action)s."
|
||||
msgstr[0] "1 comentarios ha sido %(action)s satisfactoriamente."
|
||||
msgstr[1] "%(count)s comentarios han sido %(action)s satisfactoriamente."
|
||||
|
||||
#: contrib/comments/feeds.py:13
|
||||
#, python-format
|
||||
msgid "%(site_name)s comments"
|
||||
@ -1468,7 +1497,6 @@ msgid "Latest comments on %(site_name)s"
|
||||
msgstr "Últimos comentarios en %(site_name)s"
|
||||
|
||||
#: contrib/comments/forms.py:93
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:34
|
||||
msgid "Name"
|
||||
msgstr "Nombre"
|
||||
|
||||
@ -1477,7 +1505,6 @@ msgid "Email address"
|
||||
msgstr "dirección de correo electrónico"
|
||||
|
||||
#: contrib/comments/forms.py:96
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:35
|
||||
msgid "Comment"
|
||||
msgstr "Comentario"
|
||||
|
||||
@ -1598,14 +1625,13 @@ msgstr "marcas de comentario"
|
||||
|
||||
#: contrib/comments/templates/comments/approve.html:4
|
||||
msgid "Approve a comment"
|
||||
msgstr "Aprovar un comentario"
|
||||
msgstr "Aprobar un comentario"
|
||||
|
||||
#: contrib/comments/templates/comments/approve.html:7
|
||||
msgid "Really make this comment public?"
|
||||
msgstr "Realmente desea hacer este comentario público?"
|
||||
|
||||
#: contrib/comments/templates/comments/approve.html:12
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:49
|
||||
msgid "Approve"
|
||||
msgstr "Aprobar"
|
||||
|
||||
@ -1631,7 +1657,6 @@ msgid "Really remove this comment?"
|
||||
msgstr "¿Realmente desea eliminar este comentario?"
|
||||
|
||||
#: contrib/comments/templates/comments/delete.html:12
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:53
|
||||
msgid "Remove"
|
||||
msgstr "Eliminar"
|
||||
|
||||
@ -1665,39 +1690,6 @@ msgstr "Enviar"
|
||||
msgid "Preview"
|
||||
msgstr "Previsualizar"
|
||||
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:4
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:19
|
||||
msgid "Comment moderation queue"
|
||||
msgstr "Cola de moderación de comentarios"
|
||||
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:26
|
||||
msgid "No comments to moderate"
|
||||
msgstr "No hay comentarios por moderar"
|
||||
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:36
|
||||
msgid "Email"
|
||||
msgstr "Correo electrónico"
|
||||
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:38
|
||||
msgid "Authenticated?"
|
||||
msgstr "¿Autentificado?"
|
||||
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:39
|
||||
msgid "IP Address"
|
||||
msgstr "Dirección IP"
|
||||
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:40
|
||||
msgid "Date posted"
|
||||
msgstr "Fecha de envío"
|
||||
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:63
|
||||
msgid "yes"
|
||||
msgstr "sí"
|
||||
|
||||
#: contrib/comments/templates/comments/moderation_queue.html:63
|
||||
msgid "no"
|
||||
msgstr "no"
|
||||
|
||||
#: contrib/comments/templates/comments/posted.html:4
|
||||
msgid "Thanks for commenting"
|
||||
msgstr "Gracias por comentar"
|
||||
@ -1790,7 +1782,7 @@ msgstr "página estática"
|
||||
msgid "flat pages"
|
||||
msgstr "páginas estáticas"
|
||||
|
||||
#: contrib/formtools/wizard.py:130
|
||||
#: contrib/formtools/wizard.py:132
|
||||
msgid ""
|
||||
"We apologize, but your form has expired. Please continue filling out the "
|
||||
"form from this page."
|
||||
@ -1815,8 +1807,8 @@ msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr ""
|
||||
"Ocurrió un error al transformar la geometria al SRID de la geometria "
|
||||
"del campo de formulario."
|
||||
"Ocurrió un error al transformar la geometria al SRID de la geometria del "
|
||||
"campo de formulario."
|
||||
|
||||
#: contrib/humanize/templatetags/humanize.py:19
|
||||
msgid "th"
|
||||
@ -2611,6 +2603,10 @@ msgstr "El número de cuenta bancaria es incorrecto."
|
||||
msgid "Enter a valid Finnish social security number."
|
||||
msgstr "Introduzca un número de seguro social finlandés válido."
|
||||
|
||||
#: contrib/localflavor/fr/forms.py:30
|
||||
msgid "Phone numbers must be in 0X XX XX XX XX format."
|
||||
msgstr "Los números de teléfono deben tener el formato 0X XX XX XX XX."
|
||||
|
||||
#: contrib/localflavor/in_/forms.py:14
|
||||
msgid "Enter a zip code in the format XXXXXXX."
|
||||
msgstr "Introduzca un código postal en el formato XXXXXXX."
|
||||
@ -3052,7 +3048,8 @@ msgstr "El Número de Identificación Tributaria (NIP) es incorrecto."
|
||||
#: contrib/localflavor/pl/forms.py:109
|
||||
msgid "National Business Register Number (REGON) consists of 9 or 14 digits."
|
||||
msgstr ""
|
||||
"El Número Nacional de Registro de Negocios (REGON) consiste en 9 o 14 dígitos."
|
||||
"El Número Nacional de Registro de Negocios (REGON) consiste en 9 o 14 "
|
||||
"dígitos."
|
||||
|
||||
#: contrib/localflavor/pl/forms.py:110
|
||||
msgid "Wrong checksum for the National Business Register Number (REGON)."
|
||||
@ -3941,14 +3938,14 @@ msgstr "Este valor debe ser Verdadero, Falso o Ninguno."
|
||||
msgid "Enter a valid time in HH:MM[:ss[.uuuuuu]] format."
|
||||
msgstr "Introduzca una hora válida en formato HH:MM[:ss[.uuuuuu]]."
|
||||
|
||||
#: db/models/fields/related.py:816
|
||||
#: db/models/fields/related.py:869
|
||||
msgid ""
|
||||
"Hold down \"Control\", or \"Command\" on a Mac, to select more than one."
|
||||
msgstr ""
|
||||
"Mantenga presionado \"Control\", o \"Command\" en un Mac, para seleccionar "
|
||||
"más de una opción."
|
||||
|
||||
#: db/models/fields/related.py:894
|
||||
#: db/models/fields/related.py:930
|
||||
#, python-format
|
||||
msgid "Please enter valid %(self)s IDs. The value %(value)r is invalid."
|
||||
msgid_plural ""
|
||||
@ -3960,88 +3957,88 @@ msgstr[1] ""
|
||||
"Por favor, introduzca IDs de %(self)s válidos. Los valores %(value)r no son "
|
||||
"válidos."
|
||||
|
||||
#: forms/fields.py:54
|
||||
#: forms/fields.py:53
|
||||
msgid "This field is required."
|
||||
msgstr "Este campo es obligatorio."
|
||||
|
||||
#: forms/fields.py:55
|
||||
#: forms/fields.py:54
|
||||
msgid "Enter a valid value."
|
||||
msgstr "Introduzca un valor correcto."
|
||||
|
||||
#: forms/fields.py:138
|
||||
#: forms/fields.py:137
|
||||
#, python-format
|
||||
msgid "Ensure this value has at most %(max)d characters (it has %(length)d)."
|
||||
msgstr ""
|
||||
"Asegúrese de que su texto tiene a lo más %(max)d caracteres (actualmente "
|
||||
"tiene %(length)d)."
|
||||
|
||||
#: forms/fields.py:139
|
||||
#: forms/fields.py:138
|
||||
#, python-format
|
||||
msgid "Ensure this value has at least %(min)d characters (it has %(length)d)."
|
||||
msgstr ""
|
||||
"Asegúrese de que su texto tiene al menos %(min)d caracteres (actualmente "
|
||||
"tiene %(length)d)."
|
||||
|
||||
#: forms/fields.py:166
|
||||
#: forms/fields.py:165
|
||||
msgid "Enter a whole number."
|
||||
msgstr "Introduzca un número entero."
|
||||
|
||||
#: forms/fields.py:167 forms/fields.py:196 forms/fields.py:225
|
||||
#: forms/fields.py:166 forms/fields.py:195 forms/fields.py:224
|
||||
#, python-format
|
||||
msgid "Ensure this value is less than or equal to %s."
|
||||
msgstr "Asegúrese de que este valor es menor o igual a %s."
|
||||
|
||||
#: forms/fields.py:168 forms/fields.py:197 forms/fields.py:226
|
||||
#: forms/fields.py:167 forms/fields.py:196 forms/fields.py:225
|
||||
#, python-format
|
||||
msgid "Ensure this value is greater than or equal to %s."
|
||||
msgstr "Asegúrese de que este valor es mayor o igual a %s."
|
||||
|
||||
#: forms/fields.py:195 forms/fields.py:224
|
||||
#: forms/fields.py:194 forms/fields.py:223
|
||||
msgid "Enter a number."
|
||||
msgstr "Introduzca un número."
|
||||
|
||||
#: forms/fields.py:227
|
||||
#: forms/fields.py:226
|
||||
#, python-format
|
||||
msgid "Ensure that there are no more than %s digits in total."
|
||||
msgstr "Asegúrese de que no hay más de %s dígitos en total."
|
||||
|
||||
#: forms/fields.py:228
|
||||
#: forms/fields.py:227
|
||||
#, python-format
|
||||
msgid "Ensure that there are no more than %s decimal places."
|
||||
msgstr "Asegúrese de que no hay más de %s decimales."
|
||||
|
||||
#: forms/fields.py:229
|
||||
#: forms/fields.py:228
|
||||
#, python-format
|
||||
msgid "Ensure that there are no more than %s digits before the decimal point."
|
||||
msgstr "Asegúrese de que no hay más de %s dígitos antes de la coma decimal."
|
||||
|
||||
#: forms/fields.py:288 forms/fields.py:863
|
||||
#: forms/fields.py:287 forms/fields.py:862
|
||||
msgid "Enter a valid date."
|
||||
msgstr "Introduzca una fecha válida."
|
||||
|
||||
#: forms/fields.py:322 forms/fields.py:864
|
||||
#: forms/fields.py:321 forms/fields.py:863
|
||||
msgid "Enter a valid time."
|
||||
msgstr "Introduzca una hora válida."
|
||||
|
||||
#: forms/fields.py:361
|
||||
#: forms/fields.py:360
|
||||
msgid "Enter a valid date/time."
|
||||
msgstr "Introduzca una fecha/hora válida."
|
||||
|
||||
#: forms/fields.py:447
|
||||
#: forms/fields.py:446
|
||||
msgid "No file was submitted. Check the encoding type on the form."
|
||||
msgstr ""
|
||||
"No se ha enviado ningún fichero. Compruebe el tipo de codificación en el "
|
||||
"formulario."
|
||||
|
||||
#: forms/fields.py:448
|
||||
#: forms/fields.py:447
|
||||
msgid "No file was submitted."
|
||||
msgstr "No se ha enviado ningún fichero"
|
||||
|
||||
#: forms/fields.py:449
|
||||
#: forms/fields.py:448
|
||||
msgid "The submitted file is empty."
|
||||
msgstr "El fichero enviado está vacío."
|
||||
|
||||
#: forms/fields.py:450
|
||||
#: forms/fields.py:449
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Ensure this filename has at most %(max)d characters (it has %(length)d)."
|
||||
@ -4049,7 +4046,7 @@ msgstr ""
|
||||
"Asegúrese de que su texto tiene no más de %(max)d caracteres (actualmente "
|
||||
"tiene %(length)d)."
|
||||
|
||||
#: forms/fields.py:483
|
||||
#: forms/fields.py:482
|
||||
msgid ""
|
||||
"Upload a valid image. The file you uploaded was either not an image or a "
|
||||
"corrupted image."
|
||||
@ -4057,29 +4054,29 @@ msgstr ""
|
||||
"Envíe una imagen válida. El fichero que ha enviado no era una imagen o se "
|
||||
"trataba de una imagen corrupta."
|
||||
|
||||
#: forms/fields.py:544
|
||||
#: forms/fields.py:543
|
||||
msgid "Enter a valid URL."
|
||||
msgstr "Introduzca una URL válida."
|
||||
|
||||
#: forms/fields.py:545
|
||||
#: forms/fields.py:544
|
||||
msgid "This URL appears to be a broken link."
|
||||
msgstr "La URL parece ser un enlace roto."
|
||||
|
||||
#: forms/fields.py:625 forms/fields.py:703
|
||||
#: forms/fields.py:624 forms/fields.py:702
|
||||
#, python-format
|
||||
msgid "Select a valid choice. %(value)s is not one of the available choices."
|
||||
msgstr ""
|
||||
"Escoja una opción válida. %(value)s no es una de las opciones disponibles."
|
||||
|
||||
#: forms/fields.py:704 forms/fields.py:765 forms/models.py:1003
|
||||
#: forms/fields.py:703 forms/fields.py:764 forms/models.py:999
|
||||
msgid "Enter a list of values."
|
||||
msgstr "Introduzca una lista de valores."
|
||||
|
||||
#: forms/fields.py:892
|
||||
#: forms/fields.py:891
|
||||
msgid "Enter a valid IPv4 address."
|
||||
msgstr "Introduzca una dirección IPv4 válida."
|
||||
|
||||
#: forms/fields.py:902
|
||||
#: forms/fields.py:901
|
||||
msgid ""
|
||||
"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."
|
||||
msgstr ""
|
||||
@ -4090,28 +4087,28 @@ msgstr ""
|
||||
msgid "Order"
|
||||
msgstr "Orden"
|
||||
|
||||
#: forms/models.py:367
|
||||
#: forms/models.py:363
|
||||
#, python-format
|
||||
msgid "%(field_name)s must be unique for %(date_field)s %(lookup)s."
|
||||
msgstr "El campo %(field_name)s debe ser único para %(lookup)s %(date_field)s"
|
||||
|
||||
#: forms/models.py:381 forms/models.py:389
|
||||
#: forms/models.py:377 forms/models.py:385
|
||||
#, python-format
|
||||
msgid "%(model_name)s with this %(field_label)s already exists."
|
||||
msgstr "Ya existe %(model_name)s con este %(field_label)s."
|
||||
|
||||
#: forms/models.py:594
|
||||
#: forms/models.py:590
|
||||
#, python-format
|
||||
msgid "Please correct the duplicate data for %(field)s."
|
||||
msgstr "Por favor, corrija el dato duplicado para %(field)s."
|
||||
|
||||
#: forms/models.py:598
|
||||
#: forms/models.py:594
|
||||
#, python-format
|
||||
msgid "Please correct the duplicate data for %(field)s, which must be unique."
|
||||
msgstr ""
|
||||
"Por favor corriga el dato duplicado para %(field)s, el cual debe ser único."
|
||||
|
||||
#: forms/models.py:604
|
||||
#: forms/models.py:600
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Please correct the duplicate data for %(field_name)s which must be unique "
|
||||
@ -4120,26 +4117,26 @@ msgstr ""
|
||||
"Por favor corriga los datos duplicados para %(field_name)s el cual debe ser "
|
||||
"único para %(lookup)s en %(date_field)s."
|
||||
|
||||
#: forms/models.py:612
|
||||
#: forms/models.py:608
|
||||
msgid "Please correct the duplicate values below."
|
||||
msgstr "Por favor, corrija los valores duplicados abajo."
|
||||
|
||||
#: forms/models.py:867
|
||||
#: forms/models.py:863
|
||||
msgid "The inline foreign key did not match the parent instance primary key."
|
||||
msgstr ""
|
||||
"La clave foránea en linea no coincide con la clave primaria de la instancia "
|
||||
"padre."
|
||||
|
||||
#: forms/models.py:930
|
||||
#: forms/models.py:926
|
||||
msgid "Select a valid choice. That choice is not one of the available choices."
|
||||
msgstr "Escoja una opción válida. Esa opción no está entre las disponibles."
|
||||
|
||||
#: forms/models.py:1004
|
||||
#: forms/models.py:1000
|
||||
#, python-format
|
||||
msgid "Select a valid choice. %s is not one of the available choices."
|
||||
msgstr "Escoja una opción válida; '%s' no es una de las opciones disponibles."
|
||||
|
||||
#: forms/models.py:1006
|
||||
#: forms/models.py:1002
|
||||
#, python-format
|
||||
msgid "\"%s\" is not a valid value for a primary key."
|
||||
msgstr "\"%s\" no es un valor válido para una clave primaria."
|
||||
@ -4459,6 +4456,30 @@ msgstr "Se actualizó con éxito el %(verbose_name)s."
|
||||
msgid "The %(verbose_name)s was deleted."
|
||||
msgstr "El/La %(verbose_name)s ha sido borrado."
|
||||
|
||||
#~ msgid "Comment moderation queue"
|
||||
#~ msgstr "Cola de moderación de comentarios"
|
||||
|
||||
#~ msgid "No comments to moderate"
|
||||
#~ msgstr "No hay comentarios por moderar"
|
||||
|
||||
#~ msgid "Email"
|
||||
#~ msgstr "Correo electrónico"
|
||||
|
||||
#~ msgid "Authenticated?"
|
||||
#~ msgstr "¿Autentificado?"
|
||||
|
||||
#~ msgid "IP Address"
|
||||
#~ msgstr "Dirección IP"
|
||||
|
||||
#~ msgid "Date posted"
|
||||
#~ msgstr "Fecha de envío"
|
||||
|
||||
#~ msgid "yes"
|
||||
#~ msgstr "sí"
|
||||
|
||||
#~ msgid "no"
|
||||
#~ msgstr "no"
|
||||
|
||||
#~ msgid "verbose_name"
|
||||
#~ msgid_plural "verbose_name_plural"
|
||||
#~ msgstr[0] "verbose_name"
|
||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -452,7 +452,7 @@ class AdminSite(object):
|
||||
import warnings
|
||||
warnings.warn(
|
||||
"AdminSite.root() is deprecated; use include(admin.site.urls) instead.",
|
||||
PendingDeprecationWarning
|
||||
DeprecationWarning
|
||||
)
|
||||
|
||||
#
|
||||
|
@ -149,7 +149,7 @@ def validate(cls, model):
|
||||
validate_inline(inline, cls, model)
|
||||
|
||||
def validate_inline(cls, parent, parent_model):
|
||||
|
||||
|
||||
# model is already verified to exist and be a Model
|
||||
if cls.fk_name: # default value is None
|
||||
f = get_field(cls, cls.model, cls.model._meta, 'fk_name', cls.fk_name)
|
||||
@ -196,6 +196,11 @@ def validate_base(cls, model):
|
||||
check_isseq(cls, 'fields', cls.fields)
|
||||
for field in cls.fields:
|
||||
check_formfield(cls, model, opts, 'fields', field)
|
||||
f = get_field(cls, model, opts, 'fields', field)
|
||||
if isinstance(f, models.ManyToManyField) and not f.rel.through._meta.auto_created:
|
||||
raise ImproperlyConfigured("'%s.fields' can't include the ManyToManyField "
|
||||
"field '%s' because '%s' manually specifies "
|
||||
"a 'through' model." % (cls.__name__, field, field))
|
||||
if cls.fieldsets:
|
||||
raise ImproperlyConfigured('Both fieldsets and fields are specified in %s.' % cls.__name__)
|
||||
if len(cls.fields) > len(set(cls.fields)):
|
||||
@ -214,11 +219,28 @@ def validate_base(cls, model):
|
||||
raise ImproperlyConfigured("'fields' key is required in "
|
||||
"%s.fieldsets[%d][1] field options dict."
|
||||
% (cls.__name__, idx))
|
||||
for fields in fieldset[1]['fields']:
|
||||
# The entry in fields might be a tuple. If it is a standalone
|
||||
# field, make it into a tuple to make processing easier.
|
||||
if type(fields) != tuple:
|
||||
fields = (fields,)
|
||||
for field in fields:
|
||||
check_formfield(cls, model, opts, "fieldsets[%d][1]['fields']" % idx, field)
|
||||
try:
|
||||
f = opts.get_field(field)
|
||||
if isinstance(f, models.ManyToManyField) and not f.rel.through._meta.auto_created:
|
||||
raise ImproperlyConfigured("'%s.fieldsets[%d][1]['fields']' "
|
||||
"can't include the ManyToManyField field '%s' because "
|
||||
"'%s' manually specifies a 'through' model." % (
|
||||
cls.__name__, idx, field, field))
|
||||
except models.FieldDoesNotExist:
|
||||
# If we can't find a field on the model that matches,
|
||||
# it could be an extra field on the form.
|
||||
pass
|
||||
flattened_fieldsets = flatten_fieldsets(cls.fieldsets)
|
||||
if len(flattened_fieldsets) > len(set(flattened_fieldsets)):
|
||||
raise ImproperlyConfigured('There are duplicate field(s) in %s.fieldsets' % cls.__name__)
|
||||
for field in flattened_fieldsets:
|
||||
check_formfield(cls, model, opts, "fieldsets[%d][1]['fields']" % idx, field)
|
||||
|
||||
|
||||
# form
|
||||
if hasattr(cls, 'form') and not issubclass(cls.form, BaseModelForm):
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
Please note that MySQL only supports bounding box queries, also
|
||||
known as MBRs (Minimum Bounding Rectangles). Moreover, spatial
|
||||
indices may only be used on MyISAM tables -- if you need
|
||||
indices may only be used on MyISAM tables -- if you need
|
||||
transactions, take a look at PostGIS.
|
||||
"""
|
||||
from django.db import connection
|
||||
@ -38,7 +38,7 @@ MISC_TERMS = ['isnull']
|
||||
# Assacceptable lookup types for Oracle spatial.
|
||||
MYSQL_GIS_TERMS = MYSQL_GIS_FUNCTIONS.keys()
|
||||
MYSQL_GIS_TERMS += MISC_TERMS
|
||||
MYSQL_GIS_TERMS = dict((term, None) for term in MYSQL_GIS_TERMS) # Making dictionary
|
||||
MYSQL_GIS_TERMS = dict((term, None) for term in MYSQL_GIS_TERMS) # Making dictionary
|
||||
|
||||
def get_geo_where_clause(table_alias, name, lookup_type, geo_annot):
|
||||
"Returns the SQL WHERE clause for use in MySQL spatial SQL construction."
|
||||
@ -49,7 +49,7 @@ def get_geo_where_clause(table_alias, name, lookup_type, geo_annot):
|
||||
lookup_info = MYSQL_GIS_FUNCTIONS.get(lookup_type, False)
|
||||
if lookup_info:
|
||||
return "%s(%s, %%s)" % (lookup_info, geo_col)
|
||||
|
||||
|
||||
# Handling 'isnull' lookup type
|
||||
# TODO: Is this needed because MySQL cannot handle NULL
|
||||
# geometries in its spatial indices.
|
||||
|
@ -3,8 +3,8 @@
|
||||
routine for Oracle Spatial.
|
||||
|
||||
Please note that WKT support is broken on the XE version, and thus
|
||||
this backend will not work on such platforms. Specifically, XE lacks
|
||||
support for an internal JVM, and Java libraries are required to use
|
||||
this backend will not work on such platforms. Specifically, XE lacks
|
||||
support for an internal JVM, and Java libraries are required to use
|
||||
the WKT constructors.
|
||||
"""
|
||||
import re
|
||||
@ -31,10 +31,10 @@ TRANSFORM = 'SDO_CS.TRANSFORM'
|
||||
UNION = 'SDO_GEOM.SDO_UNION'
|
||||
UNIONAGG = 'SDO_AGGR_UNION'
|
||||
|
||||
# We want to get SDO Geometries as WKT because it is much easier to
|
||||
# instantiate GEOS proxies from WKT than SDO_GEOMETRY(...) strings.
|
||||
# However, this adversely affects performance (i.e., Java is called
|
||||
# to convert to WKT on every query). If someone wishes to write a
|
||||
# We want to get SDO Geometries as WKT because it is much easier to
|
||||
# instantiate GEOS proxies from WKT than SDO_GEOMETRY(...) strings.
|
||||
# However, this adversely affects performance (i.e., Java is called
|
||||
# to convert to WKT on every query). If someone wishes to write a
|
||||
# SDO_GEOMETRY(...) parser in Python, let me know =)
|
||||
GEOM_SELECT = 'SDO_UTIL.TO_WKTGEOMETRY(%s)'
|
||||
|
||||
@ -50,7 +50,7 @@ class SDOOperation(SpatialFunction):
|
||||
class SDODistance(SpatialFunction):
|
||||
"Class for Distance queries."
|
||||
def __init__(self, op, tolerance=0.05):
|
||||
super(SDODistance, self).__init__(DISTANCE, end_subst=', %s) %%s %%s' % tolerance,
|
||||
super(SDODistance, self).__init__(DISTANCE, end_subst=', %s) %%s %%s' % tolerance,
|
||||
operator=op, result='%%s')
|
||||
|
||||
class SDOGeomRelate(SpatialFunction):
|
||||
@ -59,7 +59,7 @@ class SDOGeomRelate(SpatialFunction):
|
||||
# SDO_GEOM.RELATE(...) has a peculiar argument order: column, mask, geom, tolerance.
|
||||
# Moreover, the runction result is the mask (e.g., 'DISJOINT' instead of 'TRUE').
|
||||
end_subst = "%s%s) %s '%s'" % (', %%s, ', tolerance, '=', mask)
|
||||
beg_subst = "%%s(%%s, '%s'" % mask
|
||||
beg_subst = "%%s(%%s, '%s'" % mask
|
||||
super(SDOGeomRelate, self).__init__('SDO_GEOM.RELATE', beg_subst=beg_subst, end_subst=end_subst)
|
||||
|
||||
class SDORelate(SpatialFunction):
|
||||
@ -81,7 +81,7 @@ DISTANCE_FUNCTIONS = {
|
||||
'distance_gte' : (SDODistance('>='), dtypes),
|
||||
'distance_lt' : (SDODistance('<'), dtypes),
|
||||
'distance_lte' : (SDODistance('<='), dtypes),
|
||||
'dwithin' : (SDOOperation('SDO_WITHIN_DISTANCE',
|
||||
'dwithin' : (SDOOperation('SDO_WITHIN_DISTANCE',
|
||||
beg_subst="%s(%s, %%s, 'distance=%%s'"), dtypes),
|
||||
}
|
||||
|
||||
@ -118,7 +118,7 @@ def get_geo_where_clause(table_alias, name, lookup_type, geo_annot):
|
||||
# See if a Oracle Geometry function matches the lookup type next
|
||||
lookup_info = ORACLE_GEOMETRY_FUNCTIONS.get(lookup_type, False)
|
||||
if lookup_info:
|
||||
# Lookup types that are tuples take tuple arguments, e.g., 'relate' and
|
||||
# Lookup types that are tuples take tuple arguments, e.g., 'relate' and
|
||||
# 'dwithin' lookup types.
|
||||
if isinstance(lookup_info, tuple):
|
||||
# First element of tuple is lookup type, second element is the type
|
||||
@ -128,15 +128,15 @@ def get_geo_where_clause(table_alias, name, lookup_type, geo_annot):
|
||||
# Ensuring that a tuple _value_ was passed in from the user
|
||||
if not isinstance(geo_annot.value, tuple):
|
||||
raise TypeError('Tuple required for `%s` lookup type.' % lookup_type)
|
||||
if len(geo_annot.value) != 2:
|
||||
if len(geo_annot.value) != 2:
|
||||
raise ValueError('2-element tuple required for %s lookup type.' % lookup_type)
|
||||
|
||||
|
||||
# Ensuring the argument type matches what we expect.
|
||||
if not isinstance(geo_annot.value[1], arg_type):
|
||||
raise TypeError('Argument type should be %s, got %s instead.' % (arg_type, type(geo_annot.value[1])))
|
||||
|
||||
if lookup_type == 'relate':
|
||||
# The SDORelate class handles construction for these queries,
|
||||
# The SDORelate class handles construction for these queries,
|
||||
# and verifies the mask argument.
|
||||
return sdo_op(geo_annot.value[1]).as_sql(geo_col)
|
||||
else:
|
||||
@ -144,7 +144,7 @@ def get_geo_where_clause(table_alias, name, lookup_type, geo_annot):
|
||||
return sdo_op.as_sql(geo_col)
|
||||
else:
|
||||
# Lookup info is a SDOOperation instance, whose `as_sql` method returns
|
||||
# the SQL necessary for the geometry function call. For example:
|
||||
# the SQL necessary for the geometry function call. For example:
|
||||
# SDO_CONTAINS("geoapp_country"."poly", SDO_GEOMTRY('POINT(5 23)', 4326)) = 'TRUE'
|
||||
return lookup_info.as_sql(geo_col)
|
||||
elif lookup_type == 'isnull':
|
||||
|
@ -18,18 +18,21 @@ SpatialBackend = BaseSpatialBackend(name='postgis', postgis=True,
|
||||
distance_spheroid=DISTANCE_SPHEROID,
|
||||
envelope=ENVELOPE,
|
||||
extent=EXTENT,
|
||||
extent3d=EXTENT3D,
|
||||
gis_terms=POSTGIS_TERMS,
|
||||
geojson=ASGEOJSON,
|
||||
gml=ASGML,
|
||||
intersection=INTERSECTION,
|
||||
kml=ASKML,
|
||||
length=LENGTH,
|
||||
length3d=LENGTH3D,
|
||||
length_spheroid=LENGTH_SPHEROID,
|
||||
make_line=MAKE_LINE,
|
||||
mem_size=MEM_SIZE,
|
||||
num_geom=NUM_GEOM,
|
||||
num_points=NUM_POINTS,
|
||||
perimeter=PERIMETER,
|
||||
perimeter3d=PERIMETER3D,
|
||||
point_on_surface=POINT_ON_SURFACE,
|
||||
scale=SCALE,
|
||||
select=GEOM_SELECT,
|
||||
|
@ -2,7 +2,7 @@
|
||||
This object provides quoting for GEOS geometries into PostgreSQL/PostGIS.
|
||||
"""
|
||||
|
||||
from django.contrib.gis.db.backend.postgis.query import GEOM_FROM_WKB
|
||||
from django.contrib.gis.db.backend.postgis.query import GEOM_FROM_EWKB
|
||||
from psycopg2 import Binary
|
||||
from psycopg2.extensions import ISQLQuote
|
||||
|
||||
@ -11,7 +11,7 @@ class PostGISAdaptor(object):
|
||||
"Initializes on the geometry."
|
||||
# Getting the WKB (in string form, to allow easy pickling of
|
||||
# the adaptor) and the SRID from the geometry.
|
||||
self.wkb = str(geom.wkb)
|
||||
self.ewkb = str(geom.ewkb)
|
||||
self.srid = geom.srid
|
||||
|
||||
def __conform__(self, proto):
|
||||
@ -30,7 +30,7 @@ class PostGISAdaptor(object):
|
||||
def getquoted(self):
|
||||
"Returns a properly quoted string for use in PostgreSQL/PostGIS."
|
||||
# Want to use WKB, so wrap with psycopg2 Binary() to quote properly.
|
||||
return "%s(%s, %s)" % (GEOM_FROM_WKB, Binary(self.wkb), self.srid or -1)
|
||||
return "%s(E%s)" % (GEOM_FROM_EWKB, Binary(self.ewkb))
|
||||
|
||||
def prepare_database_save(self, unused):
|
||||
return self
|
||||
|
@ -63,17 +63,21 @@ if MAJOR_VERSION >= 1:
|
||||
DISTANCE_SPHERE = get_func('distance_sphere')
|
||||
DISTANCE_SPHEROID = get_func('distance_spheroid')
|
||||
ENVELOPE = get_func('Envelope')
|
||||
EXTENT = get_func('extent')
|
||||
EXTENT = get_func('Extent')
|
||||
EXTENT3D = get_func('Extent3D')
|
||||
GEOM_FROM_TEXT = get_func('GeomFromText')
|
||||
GEOM_FROM_EWKB = get_func('GeomFromEWKB')
|
||||
GEOM_FROM_WKB = get_func('GeomFromWKB')
|
||||
INTERSECTION = get_func('Intersection')
|
||||
LENGTH = get_func('Length')
|
||||
LENGTH3D = get_func('Length3D')
|
||||
LENGTH_SPHEROID = get_func('length_spheroid')
|
||||
MAKE_LINE = get_func('MakeLine')
|
||||
MEM_SIZE = get_func('mem_size')
|
||||
NUM_GEOM = get_func('NumGeometries')
|
||||
NUM_POINTS = get_func('npoints')
|
||||
PERIMETER = get_func('Perimeter')
|
||||
PERIMETER3D = get_func('Perimeter3D')
|
||||
POINT_ON_SURFACE = get_func('PointOnSurface')
|
||||
SCALE = get_func('Scale')
|
||||
SNAP_TO_GRID = get_func('SnapToGrid')
|
||||
|
@ -24,6 +24,9 @@ class Collect(GeoAggregate):
|
||||
class Extent(GeoAggregate):
|
||||
name = 'Extent'
|
||||
|
||||
class Extent3D(GeoAggregate):
|
||||
name = 'Extent3D'
|
||||
|
||||
class MakeLine(GeoAggregate):
|
||||
name = 'MakeLine'
|
||||
|
||||
|
@ -34,6 +34,9 @@ class GeoManager(Manager):
|
||||
def extent(self, *args, **kwargs):
|
||||
return self.get_query_set().extent(*args, **kwargs)
|
||||
|
||||
def extent3d(self, *args, **kwargs):
|
||||
return self.get_query_set().extent3d(*args, **kwargs)
|
||||
|
||||
def geojson(self, *args, **kwargs):
|
||||
return self.get_query_set().geojson(*args, **kwargs)
|
||||
|
||||
|
@ -110,6 +110,14 @@ class GeoQuerySet(QuerySet):
|
||||
"""
|
||||
return self._spatial_aggregate(aggregates.Extent, **kwargs)
|
||||
|
||||
def extent3d(self, **kwargs):
|
||||
"""
|
||||
Returns the aggregate extent, in 3D, of the features in the
|
||||
GeoQuerySet. It is returned as a 6-tuple, comprising:
|
||||
(xmin, ymin, zmin, xmax, ymax, zmax).
|
||||
"""
|
||||
return self._spatial_aggregate(aggregates.Extent3D, **kwargs)
|
||||
|
||||
def geojson(self, precision=8, crs=False, bbox=False, **kwargs):
|
||||
"""
|
||||
Returns a GeoJSON representation of the geomtry field in a `geojson`
|
||||
@ -524,12 +532,14 @@ class GeoQuerySet(QuerySet):
|
||||
else:
|
||||
dist_att = Distance.unit_attname(geo_field.units_name)
|
||||
|
||||
# Shortcut booleans for what distance function we're using.
|
||||
# Shortcut booleans for what distance function we're using and
|
||||
# whether the geometry field is 3D.
|
||||
distance = func == 'distance'
|
||||
length = func == 'length'
|
||||
perimeter = func == 'perimeter'
|
||||
if not (distance or length or perimeter):
|
||||
raise ValueError('Unknown distance function: %s' % func)
|
||||
geom_3d = geo_field.dim == 3
|
||||
|
||||
# The field's get_db_prep_lookup() is used to get any
|
||||
# extra distance parameters. Here we set up the
|
||||
@ -604,7 +614,7 @@ class GeoQuerySet(QuerySet):
|
||||
# some error checking is required.
|
||||
if not isinstance(geo_field, PointField):
|
||||
raise ValueError('Spherical distance calculation only supported on PointFields.')
|
||||
if not str(SpatialBackend.Geometry(buffer(params[0].wkb)).geom_type) == 'Point':
|
||||
if not str(SpatialBackend.Geometry(buffer(params[0].ewkb)).geom_type) == 'Point':
|
||||
raise ValueError('Spherical distance calculation only supported with Point Geometry parameters')
|
||||
# The `function` procedure argument needs to be set differently for
|
||||
# geodetic distance calculations.
|
||||
@ -617,9 +627,16 @@ class GeoQuerySet(QuerySet):
|
||||
elif length or perimeter:
|
||||
procedure_fmt = '%(geo_col)s'
|
||||
if geodetic and length:
|
||||
# There's no `length_sphere`
|
||||
# There's no `length_sphere`, and `length_spheroid` also
|
||||
# works on 3D geometries.
|
||||
procedure_fmt += ',%(spheroid)s'
|
||||
procedure_args.update({'function' : SpatialBackend.length_spheroid, 'spheroid' : where[1]})
|
||||
elif geom_3d and SpatialBackend.postgis:
|
||||
# Use 3D variants of perimeter and length routines on PostGIS.
|
||||
if perimeter:
|
||||
procedure_args.update({'function' : SpatialBackend.perimeter3d})
|
||||
elif length:
|
||||
procedure_args.update({'function' : SpatialBackend.length3d})
|
||||
|
||||
# Setting up the settings for `_spatial_attribute`.
|
||||
s = {'select_field' : DistanceField(dist_att),
|
||||
|
@ -11,6 +11,9 @@ geo_template = '%(function)s(%(field)s)'
|
||||
def convert_extent(box):
|
||||
raise NotImplementedError('Aggregate extent not implemented for this spatial backend.')
|
||||
|
||||
def convert_extent3d(box):
|
||||
raise NotImplementedError('Aggregate 3D extent not implemented for this spatial backend.')
|
||||
|
||||
def convert_geom(wkt, geo_field):
|
||||
raise NotImplementedError('Aggregate method not implemented for this spatial backend.')
|
||||
|
||||
@ -23,6 +26,14 @@ if SpatialBackend.postgis:
|
||||
xmax, ymax = map(float, ur.split())
|
||||
return (xmin, ymin, xmax, ymax)
|
||||
|
||||
def convert_extent3d(box3d):
|
||||
# Box text will be something like "BOX3D(-90.0 30.0 1, -85.0 40.0 2)";
|
||||
# parsing out and returning as a 4-tuple.
|
||||
ll, ur = box3d[6:-1].split(',')
|
||||
xmin, ymin, zmin = map(float, ll.split())
|
||||
xmax, ymax, zmax = map(float, ur.split())
|
||||
return (xmin, ymin, zmin, xmax, ymax, zmax)
|
||||
|
||||
def convert_geom(hex, geo_field):
|
||||
if hex: return SpatialBackend.Geometry(hex)
|
||||
else: return None
|
||||
@ -94,7 +105,7 @@ class Collect(GeoAggregate):
|
||||
sql_function = SpatialBackend.collect
|
||||
|
||||
class Extent(GeoAggregate):
|
||||
is_extent = True
|
||||
is_extent = '2D'
|
||||
sql_function = SpatialBackend.extent
|
||||
|
||||
if SpatialBackend.oracle:
|
||||
@ -102,6 +113,10 @@ if SpatialBackend.oracle:
|
||||
Extent.conversion_class = GeomField
|
||||
Extent.sql_template = '%(function)s(%(field)s)'
|
||||
|
||||
class Extent3D(GeoAggregate):
|
||||
is_extent = '3D'
|
||||
sql_function = SpatialBackend.extent3d
|
||||
|
||||
class MakeLine(GeoAggregate):
|
||||
conversion_class = GeomField
|
||||
sql_function = SpatialBackend.make_line
|
||||
|
@ -262,7 +262,10 @@ class GeoQuery(sql.Query):
|
||||
"""
|
||||
if isinstance(aggregate, self.aggregates_module.GeoAggregate):
|
||||
if aggregate.is_extent:
|
||||
return self.aggregates_module.convert_extent(value)
|
||||
if aggregate.is_extent == '3D':
|
||||
return self.aggregates_module.convert_extent3d(value)
|
||||
else:
|
||||
return self.aggregates_module.convert_extent(value)
|
||||
else:
|
||||
return self.aggregates_module.convert_geom(value, aggregate.source)
|
||||
else:
|
||||
|
@ -214,13 +214,7 @@ class OGRGeometry(GDALBase):
|
||||
@property
|
||||
def geom_type(self):
|
||||
"Returns the Type for this Geometry."
|
||||
try:
|
||||
return OGRGeomType(capi.get_geom_type(self.ptr))
|
||||
except OGRException:
|
||||
# VRT datasources return an invalid geometry type
|
||||
# number, but a valid name -- we'll try that instead.
|
||||
# See: http://trac.osgeo.org/gdal/ticket/2491
|
||||
return OGRGeomType(capi.get_geom_name(self.ptr))
|
||||
return OGRGeomType(capi.get_geom_type(self.ptr))
|
||||
|
||||
@property
|
||||
def geom_name(self):
|
||||
@ -684,4 +678,11 @@ GEO_CLASSES = {1 : Point,
|
||||
6 : MultiPolygon,
|
||||
7 : GeometryCollection,
|
||||
101: LinearRing,
|
||||
1 + OGRGeomType.wkb25bit : Point,
|
||||
2 + OGRGeomType.wkb25bit : LineString,
|
||||
3 + OGRGeomType.wkb25bit : Polygon,
|
||||
4 + OGRGeomType.wkb25bit : MultiPoint,
|
||||
5 + OGRGeomType.wkb25bit : MultiLineString,
|
||||
6 + OGRGeomType.wkb25bit : MultiPolygon,
|
||||
7 + OGRGeomType.wkb25bit : GeometryCollection,
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ from django.contrib.gis.gdal.error import OGRException
|
||||
class OGRGeomType(object):
|
||||
"Encapulates OGR Geometry Types."
|
||||
|
||||
wkb25bit = -2147483648
|
||||
|
||||
# Dictionary of acceptable OGRwkbGeometryType s and their string names.
|
||||
_types = {0 : 'Unknown',
|
||||
1 : 'Point',
|
||||
@ -15,6 +17,13 @@ class OGRGeomType(object):
|
||||
7 : 'GeometryCollection',
|
||||
100 : 'None',
|
||||
101 : 'LinearRing',
|
||||
1 + wkb25bit: 'Point25D',
|
||||
2 + wkb25bit: 'LineString25D',
|
||||
3 + wkb25bit: 'Polygon25D',
|
||||
4 + wkb25bit: 'MultiPoint25D',
|
||||
5 + wkb25bit : 'MultiLineString25D',
|
||||
6 + wkb25bit : 'MultiPolygon25D',
|
||||
7 + wkb25bit : 'GeometryCollection25D',
|
||||
}
|
||||
# Reverse type dictionary, keyed by lower-case of the name.
|
||||
_str_types = dict([(v.lower(), k) for k, v in _types.items()])
|
||||
@ -68,7 +77,7 @@ class OGRGeomType(object):
|
||||
@property
|
||||
def django(self):
|
||||
"Returns the Django GeometryField for this OGR Type."
|
||||
s = self.name
|
||||
s = self.name.replace('25D', '')
|
||||
if s in ('LinearRing', 'None'):
|
||||
return None
|
||||
elif s == 'Unknown':
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Needed ctypes routines
|
||||
from ctypes import byref
|
||||
from ctypes import c_double, byref
|
||||
|
||||
# Other GDAL imports.
|
||||
from django.contrib.gis.gdal.base import GDALBase
|
||||
@ -7,11 +7,12 @@ from django.contrib.gis.gdal.envelope import Envelope, OGREnvelope
|
||||
from django.contrib.gis.gdal.error import OGRException, OGRIndexError, SRSException
|
||||
from django.contrib.gis.gdal.feature import Feature
|
||||
from django.contrib.gis.gdal.field import OGRFieldTypes
|
||||
from django.contrib.gis.gdal.geometries import OGRGeomType
|
||||
from django.contrib.gis.gdal.geomtype import OGRGeomType
|
||||
from django.contrib.gis.gdal.geometries import OGRGeometry
|
||||
from django.contrib.gis.gdal.srs import SpatialReference
|
||||
|
||||
# GDAL ctypes function prototypes.
|
||||
from django.contrib.gis.gdal.prototypes import ds as capi, srs as srs_api
|
||||
from django.contrib.gis.gdal.prototypes import ds as capi, geom as geom_api, srs as srs_api
|
||||
|
||||
# For more information, see the OGR C API source code:
|
||||
# http://www.gdal.org/ogr/ogr__api_8h.html
|
||||
@ -156,6 +157,29 @@ class Layer(GDALBase):
|
||||
return [capi.get_field_precision(capi.get_field_defn(self._ldefn, i))
|
||||
for i in xrange(self.num_fields)]
|
||||
|
||||
def _get_spatial_filter(self):
|
||||
try:
|
||||
return OGRGeometry(geom_api.clone_geom(capi.get_spatial_filter(self.ptr)))
|
||||
except OGRException:
|
||||
return None
|
||||
|
||||
def _set_spatial_filter(self, filter):
|
||||
if isinstance(filter, OGRGeometry):
|
||||
capi.set_spatial_filter(self.ptr, filter.ptr)
|
||||
elif isinstance(filter, (tuple, list)):
|
||||
if not len(filter) == 4:
|
||||
raise ValueError('Spatial filter list/tuple must have 4 elements.')
|
||||
# Map c_double onto params -- if a bad type is passed in it
|
||||
# will be caught here.
|
||||
xmin, ymin, xmax, ymax = map(c_double, filter)
|
||||
capi.set_spatial_filter_rect(self.ptr, xmin, ymin, xmax, ymax)
|
||||
elif filter is None:
|
||||
capi.set_spatial_filter(self.ptr, None)
|
||||
else:
|
||||
raise TypeError('Spatial filter must be either an OGRGeometry instance, a 4-tuple, or None.')
|
||||
|
||||
spatial_filter = property(_get_spatial_filter, _set_spatial_filter)
|
||||
|
||||
#### Layer Methods ####
|
||||
def get_fields(self, field_name):
|
||||
"""
|
||||
|
@ -3,7 +3,7 @@
|
||||
related data structures. OGR_Dr_*, OGR_DS_*, OGR_L_*, OGR_F_*,
|
||||
OGR_Fld_* routines are relevant here.
|
||||
"""
|
||||
from ctypes import c_char_p, c_int, c_long, c_void_p, POINTER
|
||||
from ctypes import c_char_p, c_double, c_int, c_long, c_void_p, POINTER
|
||||
from django.contrib.gis.gdal.envelope import OGREnvelope
|
||||
from django.contrib.gis.gdal.libgdal import lgdal
|
||||
from django.contrib.gis.gdal.prototypes.generation import \
|
||||
@ -38,6 +38,9 @@ get_layer_srs = srs_output(lgdal.OGR_L_GetSpatialRef, [c_void_p])
|
||||
get_next_feature = voidptr_output(lgdal.OGR_L_GetNextFeature, [c_void_p])
|
||||
reset_reading = void_output(lgdal.OGR_L_ResetReading, [c_void_p], errcheck=False)
|
||||
test_capability = int_output(lgdal.OGR_L_TestCapability, [c_void_p, c_char_p])
|
||||
get_spatial_filter = geom_output(lgdal.OGR_L_GetSpatialFilter, [c_void_p])
|
||||
set_spatial_filter = void_output(lgdal.OGR_L_SetSpatialFilter, [c_void_p, c_void_p], errcheck=False)
|
||||
set_spatial_filter_rect = void_output(lgdal.OGR_L_SetSpatialFilterRect, [c_void_p, c_double, c_double, c_double, c_double], errcheck=False)
|
||||
|
||||
### Feature Definition Routines ###
|
||||
get_fd_geom_type = int_output(lgdal.OGR_FD_GetGeomType, [c_void_p])
|
||||
|
@ -1,13 +1,11 @@
|
||||
import os, os.path, unittest
|
||||
from django.contrib.gis.gdal import DataSource, Envelope, OGRException, OGRIndexError
|
||||
from django.contrib.gis.gdal import DataSource, Envelope, OGRGeometry, OGRException, OGRIndexError
|
||||
from django.contrib.gis.gdal.field import OFTReal, OFTInteger, OFTString
|
||||
from django.contrib import gis
|
||||
|
||||
# Path for SHP files
|
||||
data_path = os.path.join(os.path.dirname(gis.__file__), 'tests' + os.sep + 'data')
|
||||
def get_ds_file(name, ext):
|
||||
|
||||
|
||||
return os.sep.join([data_path, name, name + '.%s' % ext])
|
||||
|
||||
# Test SHP data source object
|
||||
@ -25,7 +23,7 @@ ds_list = (TestDS('test_point', nfeat=5, nfld=3, geom='POINT', gtype=1, driver='
|
||||
srs_wkt='GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]',
|
||||
field_values={'dbl' : [float(i) for i in range(1, 6)], 'int' : range(1, 6), 'str' : [str(i) for i in range(1, 6)]},
|
||||
fids=range(5)),
|
||||
TestDS('test_vrt', ext='vrt', nfeat=3, nfld=3, geom='POINT', gtype=1, driver='VRT',
|
||||
TestDS('test_vrt', ext='vrt', nfeat=3, nfld=3, geom='POINT', gtype='Point25D', driver='VRT',
|
||||
fields={'POINT_X' : OFTString, 'POINT_Y' : OFTString, 'NUM' : OFTString}, # VRT uses CSV, which all types are OFTString.
|
||||
extent=(1.0, 2.0, 100.0, 523.5), # Min/Max from CSV
|
||||
field_values={'POINT_X' : ['1.0', '5.0', '100.0'], 'POINT_Y' : ['2.0', '23.0', '523.5'], 'NUM' : ['5', '17', '23']},
|
||||
@ -191,7 +189,41 @@ class DataSourceTest(unittest.TestCase):
|
||||
if hasattr(source, 'srs_wkt'):
|
||||
self.assertEqual(source.srs_wkt, g.srs.wkt)
|
||||
|
||||
def test06_spatial_filter(self):
|
||||
"Testing the Layer.spatial_filter property."
|
||||
ds = DataSource(get_ds_file('cities', 'shp'))
|
||||
lyr = ds[0]
|
||||
|
||||
# When not set, it should be None.
|
||||
self.assertEqual(None, lyr.spatial_filter)
|
||||
|
||||
# Must be set a/an OGRGeometry or 4-tuple.
|
||||
self.assertRaises(TypeError, lyr._set_spatial_filter, 'foo')
|
||||
|
||||
# Setting the spatial filter with a tuple/list with the extent of
|
||||
# a buffer centering around Pueblo.
|
||||
self.assertRaises(ValueError, lyr._set_spatial_filter, range(5))
|
||||
filter_extent = (-105.609252, 37.255001, -103.609252, 39.255001)
|
||||
lyr.spatial_filter = (-105.609252, 37.255001, -103.609252, 39.255001)
|
||||
self.assertEqual(OGRGeometry.from_bbox(filter_extent), lyr.spatial_filter)
|
||||
feats = [feat for feat in lyr]
|
||||
self.assertEqual(1, len(feats))
|
||||
self.assertEqual('Pueblo', feats[0].get('Name'))
|
||||
|
||||
# Setting the spatial filter with an OGRGeometry for buffer centering
|
||||
# around Houston.
|
||||
filter_geom = OGRGeometry('POLYGON((-96.363151 28.763374,-94.363151 28.763374,-94.363151 30.763374,-96.363151 30.763374,-96.363151 28.763374))')
|
||||
lyr.spatial_filter = filter_geom
|
||||
self.assertEqual(filter_geom, lyr.spatial_filter)
|
||||
feats = [feat for feat in lyr]
|
||||
self.assertEqual(1, len(feats))
|
||||
self.assertEqual('Houston', feats[0].get('Name'))
|
||||
|
||||
# Clearing the spatial filter by setting it to None. Now
|
||||
# should indicate that there are 3 features in the Layer.
|
||||
lyr.spatial_filter = None
|
||||
self.assertEqual(3, len(lyr))
|
||||
|
||||
def suite():
|
||||
s = unittest.TestSuite()
|
||||
s.addTest(unittest.makeSuite(DataSourceTest))
|
||||
|
@ -46,6 +46,13 @@ class OGRGeomTest(unittest.TestCase):
|
||||
self.assertEqual(0, gt.num)
|
||||
self.assertEqual('Unknown', gt.name)
|
||||
|
||||
def test00b_geomtype_25d(self):
|
||||
"Testing OGRGeomType object with 25D types."
|
||||
wkb25bit = OGRGeomType.wkb25bit
|
||||
self.failUnless(OGRGeomType(wkb25bit + 1) == 'Point25D')
|
||||
self.failUnless(OGRGeomType('MultiLineString25D') == (5 + wkb25bit))
|
||||
self.assertEqual('GeometryCollectionField', OGRGeomType('GeometryCollection25D').django)
|
||||
|
||||
def test01a_wkt(self):
|
||||
"Testing WKT output."
|
||||
for g in wkt_out:
|
||||
@ -418,6 +425,17 @@ class OGRGeomTest(unittest.TestCase):
|
||||
xmax, ymax = max(x), max(y)
|
||||
self.assertEqual((xmin, ymin, xmax, ymax), poly.extent)
|
||||
|
||||
def test16_25D(self):
|
||||
"Testing 2.5D geometries."
|
||||
pnt_25d = OGRGeometry('POINT(1 2 3)')
|
||||
self.assertEqual('Point25D', pnt_25d.geom_type.name)
|
||||
self.assertEqual(3.0, pnt_25d.z)
|
||||
self.assertEqual(3, pnt_25d.coord_dim)
|
||||
ls_25d = OGRGeometry('LINESTRING(1 1 1,2 2 2,3 3 3)')
|
||||
self.assertEqual('LineString25D', ls_25d.geom_type.name)
|
||||
self.assertEqual([1.0, 2.0, 3.0], ls_25d.z)
|
||||
self.assertEqual(3, ls_25d.coord_dim)
|
||||
|
||||
def suite():
|
||||
s = unittest.TestSuite()
|
||||
s.addTest(unittest.makeSuite(OGRGeomTest))
|
||||
|
@ -357,33 +357,53 @@ class GEOSGeometry(GEOSBase, ListMixin):
|
||||
#### Output Routines ####
|
||||
@property
|
||||
def ewkt(self):
|
||||
"Returns the EWKT (WKT + SRID) of the Geometry."
|
||||
"""
|
||||
Returns the EWKT (WKT + SRID) of the Geometry. Note that Z values
|
||||
are *not* included in this representation because GEOS does not yet
|
||||
support serializing them.
|
||||
"""
|
||||
if self.get_srid(): return 'SRID=%s;%s' % (self.srid, self.wkt)
|
||||
else: return self.wkt
|
||||
|
||||
@property
|
||||
def wkt(self):
|
||||
"Returns the WKT (Well-Known Text) of the Geometry."
|
||||
"Returns the WKT (Well-Known Text) representation of this Geometry."
|
||||
return wkt_w.write(self)
|
||||
|
||||
@property
|
||||
def hex(self):
|
||||
"""
|
||||
Returns the HEX of the Geometry -- please note that the SRID is not
|
||||
included in this representation, because the GEOS C library uses
|
||||
-1 by default, even if the SRID is set.
|
||||
Returns the WKB of this Geometry in hexadecimal form. Please note
|
||||
that the SRID and Z values are not included in this representation
|
||||
because it is not a part of the OGC specification (use the `hexewkb`
|
||||
property instead).
|
||||
"""
|
||||
# A possible faster, all-python, implementation:
|
||||
# str(self.wkb).encode('hex')
|
||||
return wkb_w.write_hex(self)
|
||||
|
||||
@property
|
||||
def hexewkb(self):
|
||||
"""
|
||||
Returns the EWKB of this Geometry in hexadecimal form. This is an
|
||||
extension of the WKB specification that includes SRID and Z values
|
||||
that are a part of this geometry.
|
||||
"""
|
||||
if self.hasz:
|
||||
if not GEOS_PREPARE:
|
||||
# See: http://trac.osgeo.org/geos/ticket/216
|
||||
raise GEOSException('Upgrade GEOS to 3.1 to get valid 3D HEXEWKB.')
|
||||
return ewkb_w3d.write_hex(self)
|
||||
else:
|
||||
return ewkb_w.write_hex(self)
|
||||
|
||||
@property
|
||||
def json(self):
|
||||
"""
|
||||
Returns GeoJSON representation of this Geometry if GDAL 1.5+
|
||||
is installed.
|
||||
"""
|
||||
if gdal.GEOJSON:
|
||||
if gdal.GEOJSON:
|
||||
return self.ogr.json
|
||||
else:
|
||||
raise GEOSException('GeoJSON output only supported on GDAL 1.5+.')
|
||||
@ -391,9 +411,28 @@ class GEOSGeometry(GEOSBase, ListMixin):
|
||||
|
||||
@property
|
||||
def wkb(self):
|
||||
"Returns the WKB of the Geometry as a buffer."
|
||||
"""
|
||||
Returns the WKB (Well-Known Binary) representation of this Geometry
|
||||
as a Python buffer. SRID and Z values are not included, use the
|
||||
`ewkb` property instead.
|
||||
"""
|
||||
return wkb_w.write(self)
|
||||
|
||||
@property
|
||||
def ewkb(self):
|
||||
"""
|
||||
Return the EWKB representation of this Geometry as a Python buffer.
|
||||
This is an extension of the WKB specification that includes any SRID
|
||||
and Z values that are a part of this geometry.
|
||||
"""
|
||||
if self.hasz:
|
||||
if not GEOS_PREPARE:
|
||||
# See: http://trac.osgeo.org/geos/ticket/216
|
||||
raise GEOSException('Upgrade GEOS to 3.1 to get valid 3D EWKB.')
|
||||
return ewkb_w3d.write(self)
|
||||
else:
|
||||
return ewkb_w.write(self)
|
||||
|
||||
@property
|
||||
def kml(self):
|
||||
"Returns the KML representation of this Geometry."
|
||||
@ -617,7 +656,7 @@ GEOS_CLASSES = {0 : Point,
|
||||
}
|
||||
|
||||
# Similarly, import the GEOS I/O instances here to avoid conflicts.
|
||||
from django.contrib.gis.geos.io import wkt_r, wkt_w, wkb_r, wkb_w
|
||||
from django.contrib.gis.geos.io import wkt_r, wkt_w, wkb_r, wkb_w, ewkb_w, ewkb_w3d
|
||||
|
||||
# If supported, import the PreparedGeometry class.
|
||||
if GEOS_PREPARE:
|
||||
|
@ -14,19 +14,19 @@ class IOBase(GEOSBase):
|
||||
"Base class for GEOS I/O objects."
|
||||
def __init__(self):
|
||||
# Getting the pointer with the constructor.
|
||||
self.ptr = self.constructor()
|
||||
self.ptr = self._constructor()
|
||||
|
||||
def __del__(self):
|
||||
# Cleaning up with the appropriate destructor.
|
||||
if self._ptr: self.destructor(self._ptr)
|
||||
if self._ptr: self._destructor(self._ptr)
|
||||
|
||||
### WKT Reading and Writing objects ###
|
||||
|
||||
# Non-public class for internal use because its `read` method returns
|
||||
# _pointers_ instead of a GEOSGeometry object.
|
||||
class _WKTReader(IOBase):
|
||||
constructor = capi.wkt_reader_create
|
||||
destructor = capi.wkt_reader_destroy
|
||||
_constructor = capi.wkt_reader_create
|
||||
_destructor = capi.wkt_reader_destroy
|
||||
ptr_type = capi.WKT_READ_PTR
|
||||
|
||||
def read(self, wkt):
|
||||
@ -39,8 +39,8 @@ class WKTReader(_WKTReader):
|
||||
return GEOSGeometry(super(WKTReader, self).read(wkt))
|
||||
|
||||
class WKTWriter(IOBase):
|
||||
constructor = capi.wkt_writer_create
|
||||
destructor = capi.wkt_writer_destroy
|
||||
_constructor = capi.wkt_writer_create
|
||||
_destructor = capi.wkt_writer_destroy
|
||||
ptr_type = capi.WKT_WRITE_PTR
|
||||
|
||||
def write(self, geom):
|
||||
@ -51,8 +51,8 @@ class WKTWriter(IOBase):
|
||||
|
||||
# Non-public class for the same reason as _WKTReader above.
|
||||
class _WKBReader(IOBase):
|
||||
constructor = capi.wkb_reader_create
|
||||
destructor = capi.wkb_reader_destroy
|
||||
_constructor = capi.wkb_reader_create
|
||||
_destructor = capi.wkb_reader_destroy
|
||||
ptr_type = capi.WKB_READ_PTR
|
||||
|
||||
def read(self, wkb):
|
||||
@ -71,8 +71,8 @@ class WKBReader(_WKBReader):
|
||||
return GEOSGeometry(super(WKBReader, self).read(wkb))
|
||||
|
||||
class WKBWriter(IOBase):
|
||||
constructor = capi.wkb_writer_create
|
||||
destructor = capi.wkb_writer_destroy
|
||||
_constructor = capi.wkb_writer_create
|
||||
_destructor = capi.wkb_writer_destroy
|
||||
ptr_type = capi.WKB_WRITE_PTR
|
||||
|
||||
def write(self, geom):
|
||||
@ -121,3 +121,10 @@ wkt_r = _WKTReader()
|
||||
wkt_w = WKTWriter()
|
||||
wkb_r = _WKBReader()
|
||||
wkb_w = WKBWriter()
|
||||
|
||||
# These instances are for writing EWKB in 2D and 3D.
|
||||
ewkb_w = WKBWriter()
|
||||
ewkb_w.srid = True
|
||||
ewkb_w3d = WKBWriter()
|
||||
ewkb_w3d.srid = True
|
||||
ewkb_w3d.outdim = 3
|
||||
|
@ -62,17 +62,16 @@ def string_from_geom(func):
|
||||
|
||||
### ctypes prototypes ###
|
||||
|
||||
# Deprecated creation routines from WKB, HEX, WKT
|
||||
# Deprecated creation and output routines from WKB, HEX, WKT
|
||||
from_hex = bin_constructor(lgeos.GEOSGeomFromHEX_buf)
|
||||
from_wkb = bin_constructor(lgeos.GEOSGeomFromWKB_buf)
|
||||
from_wkt = geom_output(lgeos.GEOSGeomFromWKT, [c_char_p])
|
||||
|
||||
# Output routines
|
||||
to_hex = bin_output(lgeos.GEOSGeomToHEX_buf)
|
||||
to_wkb = bin_output(lgeos.GEOSGeomToWKB_buf)
|
||||
to_wkt = string_from_geom(lgeos.GEOSGeomToWKT)
|
||||
|
||||
# The GEOS geometry type, typeid, num_coordites and number of geometries
|
||||
# The GEOS geometry type, typeid, num_coordinates and number of geometries
|
||||
geos_normalize = int_from_geom(lgeos.GEOSNormalize)
|
||||
geos_type = string_from_geom(lgeos.GEOSGeomType)
|
||||
geos_typeid = int_from_geom(lgeos.GEOSGeomTypeId)
|
||||
|
@ -71,6 +71,49 @@ class GEOSTest(unittest.TestCase):
|
||||
geom = fromstr(g.wkt)
|
||||
self.assertEqual(g.hex, geom.hex)
|
||||
|
||||
def test01b_hexewkb(self):
|
||||
"Testing (HEX)EWKB output."
|
||||
from binascii import a2b_hex
|
||||
|
||||
pnt_2d = Point(0, 1, srid=4326)
|
||||
pnt_3d = Point(0, 1, 2, srid=4326)
|
||||
|
||||
# OGC-compliant HEX will not have SRID nor Z value.
|
||||
self.assertEqual(ogc_hex, pnt_2d.hex)
|
||||
self.assertEqual(ogc_hex, pnt_3d.hex)
|
||||
|
||||
# HEXEWKB should be appropriate for its dimension -- have to use an
|
||||
# a WKBWriter w/dimension set accordingly, else GEOS will insert
|
||||
# garbage into 3D coordinate if there is none. Also, GEOS has a
|
||||
# a bug in versions prior to 3.1 that puts the X coordinate in
|
||||
# place of Z; an exception should be raised on those versions.
|
||||
self.assertEqual(hexewkb_2d, pnt_2d.hexewkb)
|
||||
if GEOS_PREPARE:
|
||||
self.assertEqual(hexewkb_3d, pnt_3d.hexewkb)
|
||||
self.assertEqual(True, GEOSGeometry(hexewkb_3d).hasz)
|
||||
else:
|
||||
try:
|
||||
hexewkb = pnt_3d.hexewkb
|
||||
except GEOSException:
|
||||
pass
|
||||
else:
|
||||
self.fail('Should have raised GEOSException.')
|
||||
|
||||
# Same for EWKB.
|
||||
self.assertEqual(buffer(a2b_hex(hexewkb_2d)), pnt_2d.ewkb)
|
||||
if GEOS_PREPARE:
|
||||
self.assertEqual(buffer(a2b_hex(hexewkb_3d)), pnt_3d.ewkb)
|
||||
else:
|
||||
try:
|
||||
ewkb = pnt_3d.ewkb
|
||||
except GEOSException:
|
||||
pass
|
||||
else:
|
||||
self.fail('Should have raised GEOSException')
|
||||
|
||||
# Redundant sanity check.
|
||||
self.assertEqual(4326, GEOSGeometry(hexewkb_2d).srid)
|
||||
|
||||
def test01c_kml(self):
|
||||
"Testing KML output."
|
||||
for tg in wkt_out:
|
||||
|
@ -9,9 +9,10 @@ def geo_suite():
|
||||
some backends).
|
||||
"""
|
||||
from django.conf import settings
|
||||
from django.contrib.gis.geos import GEOS_PREPARE
|
||||
from django.contrib.gis.gdal import HAS_GDAL
|
||||
from django.contrib.gis.utils import HAS_GEOIP
|
||||
from django.contrib.gis.tests.utils import mysql
|
||||
from django.contrib.gis.tests.utils import postgis, mysql
|
||||
|
||||
# The test suite.
|
||||
s = unittest.TestSuite()
|
||||
@ -32,6 +33,10 @@ def geo_suite():
|
||||
if not mysql:
|
||||
test_apps.append('distapp')
|
||||
|
||||
# Only PostGIS using GEOS 3.1+ can support 3D so far.
|
||||
if postgis and GEOS_PREPARE:
|
||||
test_apps.append('geo3d')
|
||||
|
||||
if HAS_GDAL:
|
||||
# These tests require GDAL.
|
||||
test_suite_names.extend(['test_spatialrefsys', 'test_geoforms'])
|
||||
@ -164,20 +169,3 @@ def run_tests(test_labels, verbosity=1, interactive=True, extra_tests=[], suite=
|
||||
|
||||
# Returning the total failures and errors
|
||||
return len(result.failures) + len(result.errors)
|
||||
|
||||
# Class for creating a fake module with a run method. This is for the
|
||||
# GEOS and GDAL tests that were moved to their respective modules.
|
||||
class _DeprecatedTestModule(object):
|
||||
def __init__(self, mod_name):
|
||||
self.mod_name = mod_name
|
||||
|
||||
def run(self):
|
||||
from warnings import warn
|
||||
warn('This test module is deprecated because it has moved to ' \
|
||||
'`django.contrib.gis.%s.tests` and will disappear in 1.2.' %
|
||||
self.mod_name, DeprecationWarning)
|
||||
tests = import_module('django.contrib.gis.%s.tests' % self.mod_name)
|
||||
tests.run()
|
||||
|
||||
test_geos = _DeprecatedTestModule('geos')
|
||||
test_gdal = _DeprecatedTestModule('gdal')
|
||||
|
@ -1,7 +1,7 @@
|
||||
<OGRVRTDataSource>
|
||||
<OGRVRTLayer name="test_vrt">
|
||||
<SrcDataSource relativeToVRT="1">test_vrt.csv</SrcDataSource>
|
||||
<GeometryType>wkbPoint</GeometryType>
|
||||
<GeometryType>wkbPoint25D</GeometryType>
|
||||
<GeometryField encoding="PointFromColumns" x="POINT_X" y="POINT_Y" z="NUM"/>
|
||||
</OGRVRTLayer>
|
||||
</OGRVRTDataSource>
|
0
django/contrib/gis/tests/geo3d/__init__.py
Normal file
0
django/contrib/gis/tests/geo3d/__init__.py
Normal file
69
django/contrib/gis/tests/geo3d/models.py
Normal file
69
django/contrib/gis/tests/geo3d/models.py
Normal file
@ -0,0 +1,69 @@
|
||||
from django.contrib.gis.db import models
|
||||
|
||||
class City3D(models.Model):
|
||||
name = models.CharField(max_length=30)
|
||||
point = models.PointField(dim=3)
|
||||
objects = models.GeoManager()
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
class Interstate2D(models.Model):
|
||||
name = models.CharField(max_length=30)
|
||||
line = models.LineStringField(srid=4269)
|
||||
objects = models.GeoManager()
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
class Interstate3D(models.Model):
|
||||
name = models.CharField(max_length=30)
|
||||
line = models.LineStringField(dim=3, srid=4269)
|
||||
objects = models.GeoManager()
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
class InterstateProj2D(models.Model):
|
||||
name = models.CharField(max_length=30)
|
||||
line = models.LineStringField(srid=32140)
|
||||
objects = models.GeoManager()
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
class InterstateProj3D(models.Model):
|
||||
name = models.CharField(max_length=30)
|
||||
line = models.LineStringField(dim=3, srid=32140)
|
||||
objects = models.GeoManager()
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
class Polygon2D(models.Model):
|
||||
name = models.CharField(max_length=30)
|
||||
poly = models.PolygonField(srid=32140)
|
||||
objects = models.GeoManager()
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
class Polygon3D(models.Model):
|
||||
name = models.CharField(max_length=30)
|
||||
poly = models.PolygonField(dim=3, srid=32140)
|
||||
objects = models.GeoManager()
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
class Point2D(models.Model):
|
||||
point = models.PointField()
|
||||
objects = models.GeoManager()
|
||||
|
||||
class Point3D(models.Model):
|
||||
point = models.PointField(dim=3)
|
||||
objects = models.GeoManager()
|
||||
|
||||
class MultiPoint3D(models.Model):
|
||||
mpoint = models.MultiPointField(dim=3)
|
||||
objects = models.GeoManager()
|
234
django/contrib/gis/tests/geo3d/tests.py
Normal file
234
django/contrib/gis/tests/geo3d/tests.py
Normal file
@ -0,0 +1,234 @@
|
||||
import os, re, unittest
|
||||
from django.contrib.gis.db.models import Union, Extent3D
|
||||
from django.contrib.gis.geos import GEOSGeometry, Point, Polygon
|
||||
from django.contrib.gis.utils import LayerMapping, LayerMapError
|
||||
|
||||
from models import City3D, Interstate2D, Interstate3D, \
|
||||
InterstateProj2D, InterstateProj3D, \
|
||||
Point2D, Point3D, MultiPoint3D, Polygon2D, Polygon3D
|
||||
|
||||
data_path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', 'data'))
|
||||
city_file = os.path.join(data_path, 'cities', 'cities.shp')
|
||||
vrt_file = os.path.join(data_path, 'test_vrt', 'test_vrt.vrt')
|
||||
|
||||
# The coordinates of each city, with Z values corresponding to their
|
||||
# altitude in meters.
|
||||
city_data = (
|
||||
('Houston', (-95.363151, 29.763374, 18)),
|
||||
('Dallas', (-96.801611, 32.782057, 147)),
|
||||
('Oklahoma City', (-97.521157, 34.464642, 380)),
|
||||
('Wellington', (174.783117, -41.315268, 14)),
|
||||
('Pueblo', (-104.609252, 38.255001, 1433)),
|
||||
('Lawrence', (-95.235060, 38.971823, 251)),
|
||||
('Chicago', (-87.650175, 41.850385, 181)),
|
||||
('Victoria', (-123.305196, 48.462611, 15)),
|
||||
)
|
||||
|
||||
# Reference mapping of city name to its altitude (Z value).
|
||||
city_dict = dict((name, coords) for name, coords in city_data)
|
||||
|
||||
# 3D freeway data derived from the National Elevation Dataset:
|
||||
# http://seamless.usgs.gov/products/9arc.php
|
||||
interstate_data = (
|
||||
('I-45',
|
||||
'LINESTRING(-95.3708481 29.7765870 11.339,-95.3694580 29.7787980 4.536,-95.3690305 29.7797359 9.762,-95.3691886 29.7812450 12.448,-95.3696447 29.7850144 10.457,-95.3702511 29.7868518 9.418,-95.3706724 29.7881286 14.858,-95.3711632 29.7896157 15.386,-95.3714525 29.7936267 13.168,-95.3717848 29.7955007 15.104,-95.3717719 29.7969804 16.516,-95.3717305 29.7982117 13.923,-95.3717254 29.8000778 14.385,-95.3719875 29.8013539 15.160,-95.3720575 29.8026785 15.544,-95.3721321 29.8040912 14.975,-95.3722074 29.8050998 15.688,-95.3722779 29.8060430 16.099,-95.3733818 29.8076750 15.197,-95.3741563 29.8103686 17.268,-95.3749458 29.8129927 19.857,-95.3763564 29.8144557 15.435)',
|
||||
( 11.339, 4.536, 9.762, 12.448, 10.457, 9.418, 14.858,
|
||||
15.386, 13.168, 15.104, 16.516, 13.923, 14.385, 15.16 ,
|
||||
15.544, 14.975, 15.688, 16.099, 15.197, 17.268, 19.857,
|
||||
15.435),
|
||||
),
|
||||
)
|
||||
|
||||
# Bounding box polygon for inner-loop of Houston (in projected coordinate
|
||||
# system 32140), with elevation values from the National Elevation Dataset
|
||||
# (see above).
|
||||
bbox_wkt = 'POLYGON((941527.97 4225693.20,962596.48 4226349.75,963152.57 4209023.95,942051.75 4208366.38,941527.97 4225693.20))'
|
||||
bbox_z = (21.71, 13.21, 9.12, 16.40, 21.71)
|
||||
def gen_bbox():
|
||||
bbox_2d = GEOSGeometry(bbox_wkt, srid=32140)
|
||||
bbox_3d = Polygon(tuple((x, y, z) for (x, y), z in zip(bbox_2d[0].coords, bbox_z)), srid=32140)
|
||||
return bbox_2d, bbox_3d
|
||||
|
||||
class Geo3DTest(unittest.TestCase):
|
||||
"""
|
||||
Only a subset of the PostGIS routines are 3D-enabled, and this TestCase
|
||||
tries to test the features that can handle 3D and that are also
|
||||
available within GeoDjango. For more information, see the PostGIS docs
|
||||
on the routines that support 3D:
|
||||
|
||||
http://postgis.refractions.net/documentation/manual-1.4/ch08.html#PostGIS_3D_Functions
|
||||
"""
|
||||
|
||||
def test01_3d(self):
|
||||
"Test the creation of 3D models."
|
||||
# 3D models for the rest of the tests will be populated in here.
|
||||
# For each 3D data set create model (and 2D version if necessary),
|
||||
# retrieve, and assert geometry is in 3D and contains the expected
|
||||
# 3D values.
|
||||
for name, pnt_data in city_data:
|
||||
x, y, z = pnt_data
|
||||
pnt = Point(x, y, z, srid=4326)
|
||||
City3D.objects.create(name=name, point=pnt)
|
||||
city = City3D.objects.get(name=name)
|
||||
self.failUnless(city.point.hasz)
|
||||
self.assertEqual(z, city.point.z)
|
||||
|
||||
# Interstate (2D / 3D and Geographic/Projected variants)
|
||||
for name, line, exp_z in interstate_data:
|
||||
line_3d = GEOSGeometry(line, srid=4269)
|
||||
# Using `hex` attribute because it omits 3D.
|
||||
line_2d = GEOSGeometry(line_3d.hex, srid=4269)
|
||||
|
||||
# Creating a geographic and projected version of the
|
||||
# interstate in both 2D and 3D.
|
||||
Interstate3D.objects.create(name=name, line=line_3d)
|
||||
InterstateProj3D.objects.create(name=name, line=line_3d)
|
||||
Interstate2D.objects.create(name=name, line=line_2d)
|
||||
InterstateProj2D.objects.create(name=name, line=line_2d)
|
||||
|
||||
# Retrieving and making sure it's 3D and has expected
|
||||
# Z values -- shouldn't change because of coordinate system.
|
||||
interstate = Interstate3D.objects.get(name=name)
|
||||
interstate_proj = InterstateProj3D.objects.get(name=name)
|
||||
for i in [interstate, interstate_proj]:
|
||||
self.failUnless(i.line.hasz)
|
||||
self.assertEqual(exp_z, tuple(i.line.z))
|
||||
|
||||
# Creating 3D Polygon.
|
||||
bbox2d, bbox3d = gen_bbox()
|
||||
Polygon2D.objects.create(name='2D BBox', poly=bbox2d)
|
||||
Polygon3D.objects.create(name='3D BBox', poly=bbox3d)
|
||||
p3d = Polygon3D.objects.get(name='3D BBox')
|
||||
self.failUnless(p3d.poly.hasz)
|
||||
self.assertEqual(bbox3d, p3d.poly)
|
||||
|
||||
def test01a_3d_layermapping(self):
|
||||
"Testing LayerMapping on 3D models."
|
||||
from models import Point2D, Point3D
|
||||
|
||||
point_mapping = {'point' : 'POINT'}
|
||||
mpoint_mapping = {'mpoint' : 'MULTIPOINT'}
|
||||
|
||||
# The VRT is 3D, but should still be able to map sans the Z.
|
||||
lm = LayerMapping(Point2D, vrt_file, point_mapping, transform=False)
|
||||
lm.save()
|
||||
self.assertEqual(3, Point2D.objects.count())
|
||||
|
||||
# The city shapefile is 2D, and won't be able to fill the coordinates
|
||||
# in the 3D model -- thus, a LayerMapError is raised.
|
||||
self.assertRaises(LayerMapError, LayerMapping,
|
||||
Point3D, city_file, point_mapping, transform=False)
|
||||
|
||||
# 3D model should take 3D data just fine.
|
||||
lm = LayerMapping(Point3D, vrt_file, point_mapping, transform=False)
|
||||
lm.save()
|
||||
self.assertEqual(3, Point3D.objects.count())
|
||||
|
||||
# Making sure LayerMapping.make_multi works right, by converting
|
||||
# a Point25D into a MultiPoint25D.
|
||||
lm = LayerMapping(MultiPoint3D, vrt_file, mpoint_mapping, transform=False)
|
||||
lm.save()
|
||||
self.assertEqual(3, MultiPoint3D.objects.count())
|
||||
|
||||
def test02a_kml(self):
|
||||
"Test GeoQuerySet.kml() with Z values."
|
||||
h = City3D.objects.kml(precision=6).get(name='Houston')
|
||||
# KML should be 3D.
|
||||
# `SELECT ST_AsKML(point, 6) FROM geo3d_city3d WHERE name = 'Houston';`
|
||||
ref_kml_regex = re.compile(r'^<Point><coordinates>-95.363\d+,29.763\d+,18</coordinates></Point>$')
|
||||
self.failUnless(ref_kml_regex.match(h.kml))
|
||||
|
||||
def test02b_geojson(self):
|
||||
"Test GeoQuerySet.geojson() with Z values."
|
||||
h = City3D.objects.geojson(precision=6).get(name='Houston')
|
||||
# GeoJSON should be 3D
|
||||
# `SELECT ST_AsGeoJSON(point, 6) FROM geo3d_city3d WHERE name='Houston';`
|
||||
ref_json_regex = re.compile(r'^{"type":"Point","coordinates":\[-95.363151,29.763374,18(\.0+)?\]}$')
|
||||
self.failUnless(ref_json_regex.match(h.geojson))
|
||||
|
||||
def test03a_union(self):
|
||||
"Testing the Union aggregate of 3D models."
|
||||
# PostGIS query that returned the reference EWKT for this test:
|
||||
# `SELECT ST_AsText(ST_Union(point)) FROM geo3d_city3d;`
|
||||
ref_ewkt = 'SRID=4326;MULTIPOINT(-123.305196 48.462611 15,-104.609252 38.255001 1433,-97.521157 34.464642 380,-96.801611 32.782057 147,-95.363151 29.763374 18,-95.23506 38.971823 251,-87.650175 41.850385 181,174.783117 -41.315268 14)'
|
||||
ref_union = GEOSGeometry(ref_ewkt)
|
||||
union = City3D.objects.aggregate(Union('point'))['point__union']
|
||||
self.failUnless(union.hasz)
|
||||
self.assertEqual(ref_union, union)
|
||||
|
||||
def test03b_extent(self):
|
||||
"Testing the Extent3D aggregate for 3D models."
|
||||
# `SELECT ST_Extent3D(point) FROM geo3d_city3d;`
|
||||
ref_extent3d = (-123.305196, -41.315268, 14,174.783117, 48.462611, 1433)
|
||||
extent1 = City3D.objects.aggregate(Extent3D('point'))['point__extent3d']
|
||||
extent2 = City3D.objects.extent3d()
|
||||
|
||||
def check_extent3d(extent3d, tol=6):
|
||||
for ref_val, ext_val in zip(ref_extent3d, extent3d):
|
||||
self.assertAlmostEqual(ref_val, ext_val, tol)
|
||||
|
||||
for e3d in [extent1, extent2]:
|
||||
check_extent3d(e3d)
|
||||
|
||||
def test04_perimeter(self):
|
||||
"Testing GeoQuerySet.perimeter() on 3D fields."
|
||||
# Reference query for values below:
|
||||
# `SELECT ST_Perimeter3D(poly), ST_Perimeter2D(poly) FROM geo3d_polygon3d;`
|
||||
ref_perim_3d = 76859.2620451
|
||||
ref_perim_2d = 76859.2577803
|
||||
tol = 6
|
||||
self.assertAlmostEqual(ref_perim_2d,
|
||||
Polygon2D.objects.perimeter().get(name='2D BBox').perimeter.m,
|
||||
tol)
|
||||
self.assertAlmostEqual(ref_perim_3d,
|
||||
Polygon3D.objects.perimeter().get(name='3D BBox').perimeter.m,
|
||||
tol)
|
||||
|
||||
def test05_length(self):
|
||||
"Testing GeoQuerySet.length() on 3D fields."
|
||||
# ST_Length_Spheroid Z-aware, and thus does not need to use
|
||||
# a separate function internally.
|
||||
# `SELECT ST_Length_Spheroid(line, 'SPHEROID["GRS 1980",6378137,298.257222101]')
|
||||
# FROM geo3d_interstate[2d|3d];`
|
||||
tol = 3
|
||||
ref_length_2d = 4368.1721949481
|
||||
ref_length_3d = 4368.62547052088
|
||||
self.assertAlmostEqual(ref_length_2d,
|
||||
Interstate2D.objects.length().get(name='I-45').length.m,
|
||||
tol)
|
||||
self.assertAlmostEqual(ref_length_3d,
|
||||
Interstate3D.objects.length().get(name='I-45').length.m,
|
||||
tol)
|
||||
|
||||
# Making sure `ST_Length3D` is used on for a projected
|
||||
# and 3D model rather than `ST_Length`.
|
||||
# `SELECT ST_Length(line) FROM geo3d_interstateproj2d;`
|
||||
ref_length_2d = 4367.71564892392
|
||||
# `SELECT ST_Length3D(line) FROM geo3d_interstateproj3d;`
|
||||
ref_length_3d = 4368.16897234101
|
||||
self.assertAlmostEqual(ref_length_2d,
|
||||
InterstateProj2D.objects.length().get(name='I-45').length.m,
|
||||
tol)
|
||||
self.assertAlmostEqual(ref_length_3d,
|
||||
InterstateProj3D.objects.length().get(name='I-45').length.m,
|
||||
tol)
|
||||
|
||||
def test06_scale(self):
|
||||
"Testing GeoQuerySet.scale() on Z values."
|
||||
# Mapping of City name to reference Z values.
|
||||
zscales = (-3, 4, 23)
|
||||
for zscale in zscales:
|
||||
for city in City3D.objects.scale(1.0, 1.0, zscale):
|
||||
self.assertEqual(city_dict[city.name][2] * zscale, city.scale.z)
|
||||
|
||||
def test07_translate(self):
|
||||
"Testing GeoQuerySet.translate() on Z values."
|
||||
ztranslations = (5.23, 23, -17)
|
||||
for ztrans in ztranslations:
|
||||
for city in City3D.objects.translate(0, 0, ztrans):
|
||||
self.assertEqual(city_dict[city.name][2] + ztrans, city.translate.z)
|
||||
|
||||
def suite():
|
||||
s = unittest.TestSuite()
|
||||
s.addTest(unittest.makeSuite(Geo3DTest))
|
||||
return s
|
1
django/contrib/gis/tests/geo3d/views.py
Normal file
1
django/contrib/gis/tests/geo3d/views.py
Normal file
@ -0,0 +1 @@
|
||||
# Create your views here.
|
@ -33,4 +33,6 @@ class GeoRegressionTests(unittest.TestCase):
|
||||
"Testing `extent` on a table with a single point, see #11827."
|
||||
pnt = City.objects.get(name='Pueblo').point
|
||||
ref_ext = (pnt.x, pnt.y, pnt.x, pnt.y)
|
||||
self.assertEqual(ref_ext, City.objects.filter(name='Pueblo').extent())
|
||||
extent = City.objects.filter(name='Pueblo').extent()
|
||||
for ref_val, val in zip(ref_ext, extent):
|
||||
self.assertAlmostEqual(ref_val, val, 4)
|
||||
|
@ -171,3 +171,10 @@ json_geoms = (TestGeom('POINT(100 0)', json='{ "type": "Point", "coordinates": [
|
||||
not_equal=True,
|
||||
),
|
||||
)
|
||||
|
||||
# For testing HEX(EWKB).
|
||||
ogc_hex = '01010000000000000000000000000000000000F03F'
|
||||
# `SELECT ST_AsHEXEWKB(ST_GeomFromText('POINT(0 1)', 4326));`
|
||||
hexewkb_2d = '0101000020E61000000000000000000000000000000000F03F'
|
||||
# `SELECT ST_AsHEXEWKB(ST_GeomFromEWKT('SRID=4326;POINT(0 1 2)'));`
|
||||
hexewkb_3d = '01010000A0E61000000000000000000000000000000000F03F0000000000000040'
|
||||
|
@ -10,7 +10,7 @@ if HAS_GDAL:
|
||||
try:
|
||||
# LayerMapping requires DJANGO_SETTINGS_MODULE to be set,
|
||||
# so this needs to be in try/except.
|
||||
from django.contrib.gis.utils.layermapping import LayerMapping
|
||||
from django.contrib.gis.utils.layermapping import LayerMapping, LayerMapError
|
||||
except:
|
||||
pass
|
||||
|
||||
|
@ -133,6 +133,9 @@ class LayerMapping(object):
|
||||
MULTI_TYPES = {1 : OGRGeomType('MultiPoint'),
|
||||
2 : OGRGeomType('MultiLineString'),
|
||||
3 : OGRGeomType('MultiPolygon'),
|
||||
OGRGeomType('Point25D').num : OGRGeomType('MultiPoint25D'),
|
||||
OGRGeomType('LineString25D').num : OGRGeomType('MultiLineString25D'),
|
||||
OGRGeomType('Polygon25D').num : OGRGeomType('MultiPolygon25D'),
|
||||
}
|
||||
|
||||
# Acceptable Django field types and corresponding acceptable OGR
|
||||
@ -282,19 +285,28 @@ class LayerMapping(object):
|
||||
if self.geom_field:
|
||||
raise LayerMapError('LayerMapping does not support more than one GeometryField per model.')
|
||||
|
||||
# Getting the coordinate dimension of the geometry field.
|
||||
coord_dim = model_field.dim
|
||||
|
||||
try:
|
||||
gtype = OGRGeomType(ogr_name)
|
||||
if coord_dim == 3:
|
||||
gtype = OGRGeomType(ogr_name + '25D')
|
||||
else:
|
||||
gtype = OGRGeomType(ogr_name)
|
||||
except OGRException:
|
||||
raise LayerMapError('Invalid mapping for GeometryField "%s".' % field_name)
|
||||
|
||||
# Making sure that the OGR Layer's Geometry is compatible.
|
||||
ltype = self.layer.geom_type
|
||||
if not (gtype == ltype or self.make_multi(ltype, model_field)):
|
||||
raise LayerMapError('Invalid mapping geometry; model has %s, feature has %s.' % (fld_name, gtype))
|
||||
if not (ltype.name.startswith(gtype.name) or self.make_multi(ltype, model_field)):
|
||||
raise LayerMapError('Invalid mapping geometry; model has %s%s, layer is %s.' %
|
||||
(fld_name, (coord_dim == 3 and '(dim=3)') or '', ltype))
|
||||
|
||||
# Setting the `geom_field` attribute w/the name of the model field
|
||||
# that is a Geometry.
|
||||
# that is a Geometry. Also setting the coordinate dimension
|
||||
# attribute.
|
||||
self.geom_field = field_name
|
||||
self.coord_dim = coord_dim
|
||||
fields_val = model_field
|
||||
elif isinstance(model_field, models.ForeignKey):
|
||||
if isinstance(ogr_name, dict):
|
||||
@ -482,6 +494,10 @@ class LayerMapping(object):
|
||||
if necessary (for example if the model field is MultiPolygonField while
|
||||
the mapped shapefile only contains Polygons).
|
||||
"""
|
||||
# Downgrade a 3D geom to a 2D one, if necessary.
|
||||
if self.coord_dim != geom.coord_dim:
|
||||
geom.coord_dim = self.coord_dim
|
||||
|
||||
if self.make_multi(geom.geom_type, model_field):
|
||||
# Constructing a multi-geometry type to contain the single geometry
|
||||
multi_type = self.MULTI_TYPES[geom.geom_type.num]
|
||||
|
@ -68,6 +68,9 @@ class BaseHandler(object):
|
||||
from django.core import exceptions, urlresolvers
|
||||
from django.conf import settings
|
||||
|
||||
# Reset the urlconf for this thread.
|
||||
urlresolvers.set_urlconf(None)
|
||||
|
||||
# Apply request middleware
|
||||
for middleware_method in self._request_middleware:
|
||||
response = middleware_method(request)
|
||||
@ -77,61 +80,69 @@ class BaseHandler(object):
|
||||
# Get urlconf from request object, if available. Otherwise use default.
|
||||
urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF)
|
||||
|
||||
# Set the urlconf for this thread to the one specified above.
|
||||
urlresolvers.set_urlconf(urlconf)
|
||||
|
||||
resolver = urlresolvers.RegexURLResolver(r'^/', urlconf)
|
||||
try:
|
||||
callback, callback_args, callback_kwargs = resolver.resolve(
|
||||
request.path_info)
|
||||
|
||||
# Apply view middleware
|
||||
for middleware_method in self._view_middleware:
|
||||
response = middleware_method(request, callback, callback_args, callback_kwargs)
|
||||
if response:
|
||||
return response
|
||||
|
||||
try:
|
||||
response = callback(request, *callback_args, **callback_kwargs)
|
||||
except Exception, e:
|
||||
# If the view raised an exception, run it through exception
|
||||
# middleware, and if the exception middleware returns a
|
||||
# response, use that. Otherwise, reraise the exception.
|
||||
for middleware_method in self._exception_middleware:
|
||||
response = middleware_method(request, e)
|
||||
callback, callback_args, callback_kwargs = resolver.resolve(
|
||||
request.path_info)
|
||||
|
||||
# Apply view middleware
|
||||
for middleware_method in self._view_middleware:
|
||||
response = middleware_method(request, callback, callback_args, callback_kwargs)
|
||||
if response:
|
||||
return response
|
||||
raise
|
||||
|
||||
# Complain if the view returned None (a common error).
|
||||
if response is None:
|
||||
try:
|
||||
view_name = callback.func_name # If it's a function
|
||||
except AttributeError:
|
||||
view_name = callback.__class__.__name__ + '.__call__' # If it's a class
|
||||
raise ValueError, "The view %s.%s didn't return an HttpResponse object." % (callback.__module__, view_name)
|
||||
response = callback(request, *callback_args, **callback_kwargs)
|
||||
except Exception, e:
|
||||
# If the view raised an exception, run it through exception
|
||||
# middleware, and if the exception middleware returns a
|
||||
# response, use that. Otherwise, reraise the exception.
|
||||
for middleware_method in self._exception_middleware:
|
||||
response = middleware_method(request, e)
|
||||
if response:
|
||||
return response
|
||||
raise
|
||||
|
||||
return response
|
||||
except http.Http404, e:
|
||||
if settings.DEBUG:
|
||||
from django.views import debug
|
||||
return debug.technical_404_response(request, e)
|
||||
else:
|
||||
try:
|
||||
callback, param_dict = resolver.resolve404()
|
||||
return callback(request, **param_dict)
|
||||
except:
|
||||
# Complain if the view returned None (a common error).
|
||||
if response is None:
|
||||
try:
|
||||
return self.handle_uncaught_exception(request, resolver, sys.exc_info())
|
||||
finally:
|
||||
receivers = signals.got_request_exception.send(sender=self.__class__, request=request)
|
||||
except exceptions.PermissionDenied:
|
||||
return http.HttpResponseForbidden('<h1>Permission denied</h1>')
|
||||
except SystemExit:
|
||||
# Allow sys.exit() to actually exit. See tickets #1023 and #4701
|
||||
raise
|
||||
except: # Handle everything else, including SuspiciousOperation, etc.
|
||||
# Get the exception info now, in case another exception is thrown later.
|
||||
exc_info = sys.exc_info()
|
||||
receivers = signals.got_request_exception.send(sender=self.__class__, request=request)
|
||||
return self.handle_uncaught_exception(request, resolver, exc_info)
|
||||
view_name = callback.func_name # If it's a function
|
||||
except AttributeError:
|
||||
view_name = callback.__class__.__name__ + '.__call__' # If it's a class
|
||||
raise ValueError, "The view %s.%s didn't return an HttpResponse object." % (callback.__module__, view_name)
|
||||
|
||||
return response
|
||||
except http.Http404, e:
|
||||
if settings.DEBUG:
|
||||
from django.views import debug
|
||||
return debug.technical_404_response(request, e)
|
||||
else:
|
||||
try:
|
||||
callback, param_dict = resolver.resolve404()
|
||||
return callback(request, **param_dict)
|
||||
except:
|
||||
try:
|
||||
return self.handle_uncaught_exception(request, resolver, sys.exc_info())
|
||||
finally:
|
||||
receivers = signals.got_request_exception.send(sender=self.__class__, request=request)
|
||||
except exceptions.PermissionDenied:
|
||||
return http.HttpResponseForbidden('<h1>Permission denied</h1>')
|
||||
except SystemExit:
|
||||
# Allow sys.exit() to actually exit. See tickets #1023 and #4701
|
||||
raise
|
||||
except: # Handle everything else, including SuspiciousOperation, etc.
|
||||
# Get the exception info now, in case another exception is thrown later.
|
||||
exc_info = sys.exc_info()
|
||||
receivers = signals.got_request_exception.send(sender=self.__class__, request=request)
|
||||
return self.handle_uncaught_exception(request, resolver, exc_info)
|
||||
finally:
|
||||
# Reset URLconf for this thread on the way out for complete
|
||||
# isolation of request.urlconf
|
||||
urlresolvers.set_urlconf(None)
|
||||
|
||||
def handle_uncaught_exception(self, request, resolver, exc_info):
|
||||
"""
|
||||
|
@ -105,6 +105,6 @@ class SMTPConnection(_SMTPConnection):
|
||||
import warnings
|
||||
warnings.warn(
|
||||
'mail.SMTPConnection is deprecated; use mail.get_connection() instead.',
|
||||
DeprecationWarning
|
||||
PendingDeprecationWarning
|
||||
)
|
||||
super(SMTPConnection, self).__init__(*args, **kwds)
|
||||
|
@ -299,7 +299,7 @@ class ManagementUtility(object):
|
||||
|
||||
# subcommand
|
||||
if cword == 1:
|
||||
print ' '.join(filter(lambda x: x.startswith(curr), subcommands))
|
||||
print ' '.join(sorted(filter(lambda x: x.startswith(curr), subcommands)))
|
||||
# subcommand options
|
||||
# special case: the 'help' subcommand has no options
|
||||
elif cwords[0] in subcommands and cwords[0] != 'help':
|
||||
@ -328,7 +328,7 @@ class ManagementUtility(object):
|
||||
options = filter(lambda (x, v): x not in prev_opts, options)
|
||||
|
||||
# filter options by current input
|
||||
options = [(k, v) for k, v in options if k.startswith(curr)]
|
||||
options = sorted([(k, v) for k, v in options if k.startswith(curr)])
|
||||
for option in options:
|
||||
opt_label = option[0]
|
||||
# append '=' to options which require args
|
||||
|
@ -65,7 +65,7 @@ class Command(NoArgsCommand):
|
||||
opts = model._meta
|
||||
if (connection.introspection.table_name_converter(opts.db_table) in tables or
|
||||
(opts.auto_created and
|
||||
connection.introspection.table_name_converter(opts.auto_created._meta.db_table in tables))):
|
||||
connection.introspection.table_name_converter(opts.auto_created._meta.db_table) in tables)):
|
||||
continue
|
||||
sql, references = connection.creation.sql_create_model(model, self.style, seen_models)
|
||||
seen_models.add(model)
|
||||
|
@ -10,6 +10,7 @@ a string) and returns a tuple in this format:
|
||||
import re
|
||||
|
||||
from django.http import Http404
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist
|
||||
from django.utils.datastructures import MultiValueDict
|
||||
from django.utils.encoding import iri_to_uri, force_unicode, smart_str
|
||||
@ -32,6 +33,9 @@ _callable_cache = {} # Maps view and url pattern names to their view functions.
|
||||
# be empty.
|
||||
_prefixes = {}
|
||||
|
||||
# Overridden URLconfs for each thread are stored here.
|
||||
_urlconfs = {}
|
||||
|
||||
class Resolver404(Http404):
|
||||
pass
|
||||
|
||||
@ -300,9 +304,13 @@ class RegexURLResolver(object):
|
||||
"arguments '%s' not found." % (lookup_view_s, args, kwargs))
|
||||
|
||||
def resolve(path, urlconf=None):
|
||||
if urlconf is None:
|
||||
urlconf = get_urlconf()
|
||||
return get_resolver(urlconf).resolve(path)
|
||||
|
||||
def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None, current_app=None):
|
||||
if urlconf is None:
|
||||
urlconf = get_urlconf()
|
||||
resolver = get_resolver(urlconf)
|
||||
args = args or []
|
||||
kwargs = kwargs or {}
|
||||
@ -371,3 +379,25 @@ def get_script_prefix():
|
||||
"""
|
||||
return _prefixes.get(currentThread(), u'/')
|
||||
|
||||
def set_urlconf(urlconf_name):
|
||||
"""
|
||||
Sets the URLconf for the current thread (overriding the default one in
|
||||
settings). Set to None to revert back to the default.
|
||||
"""
|
||||
thread = currentThread()
|
||||
if urlconf_name:
|
||||
_urlconfs[thread] = urlconf_name
|
||||
else:
|
||||
# faster than wrapping in a try/except
|
||||
if thread in _urlconfs:
|
||||
del _urlconfs[thread]
|
||||
|
||||
def get_urlconf(default=None):
|
||||
"""
|
||||
Returns the root URLconf to use for the current thread if it has been
|
||||
changed from the default one.
|
||||
"""
|
||||
thread = currentThread()
|
||||
if thread in _urlconfs:
|
||||
return _urlconfs[thread]
|
||||
return default
|
||||
|
@ -364,6 +364,8 @@ class Model(object):
|
||||
defers = []
|
||||
pk_val = None
|
||||
if self._deferred:
|
||||
from django.db.models.query_utils import deferred_class_factory
|
||||
factory = deferred_class_factory
|
||||
for field in self._meta.fields:
|
||||
if isinstance(self.__class__.__dict__.get(field.attname),
|
||||
DeferredAttribute):
|
||||
@ -374,8 +376,9 @@ class Model(object):
|
||||
# once.
|
||||
obj = self.__class__.__dict__[field.attname]
|
||||
model = obj.model_ref()
|
||||
|
||||
return (model_unpickle, (model, defers), data)
|
||||
else:
|
||||
factory = simple_class_factory
|
||||
return (model_unpickle, (model, defers, factory), data)
|
||||
|
||||
def _get_pk_val(self, meta=None):
|
||||
if not meta:
|
||||
@ -849,12 +852,20 @@ def get_absolute_url(opts, func, self, *args, **kwargs):
|
||||
class Empty(object):
|
||||
pass
|
||||
|
||||
def model_unpickle(model, attrs):
|
||||
def simple_class_factory(model, attrs):
|
||||
"""Used to unpickle Models without deferred fields.
|
||||
|
||||
We need to do this the hard way, rather than just using
|
||||
the default __reduce__ implementation, because of a
|
||||
__deepcopy__ problem in Python 2.4
|
||||
"""
|
||||
return model
|
||||
|
||||
def model_unpickle(model, attrs, factory):
|
||||
"""
|
||||
Used to unpickle Model subclasses with deferred fields.
|
||||
"""
|
||||
from django.db.models.query_utils import deferred_class_factory
|
||||
cls = deferred_class_factory(model, attrs)
|
||||
cls = factory(model, attrs)
|
||||
return cls.__new__(cls)
|
||||
model_unpickle.__safe_for_unpickle__ = True
|
||||
|
||||
|
@ -586,9 +586,13 @@ class ReverseManyRelatedObjectsDescriptor(object):
|
||||
# ReverseManyRelatedObjectsDescriptor instance.
|
||||
def __init__(self, m2m_field):
|
||||
self.field = m2m_field
|
||||
|
||||
def _through(self):
|
||||
# through is provided so that you have easy access to the through
|
||||
# model (Book.authors.through) for inlines, etc.
|
||||
self.through = m2m_field.rel.through
|
||||
# model (Book.authors.through) for inlines, etc. This is done as
|
||||
# a property to ensure that the fully resolved value is returned.
|
||||
return self.field.rel.through
|
||||
through = property(_through)
|
||||
|
||||
def __get__(self, instance, instance_type=None):
|
||||
if instance is None:
|
||||
@ -698,6 +702,10 @@ class ForeignKey(RelatedField, Field):
|
||||
assert isinstance(to, basestring), "%s(%r) is invalid. First parameter to ForeignKey must be either a model, a model name, or the string %r" % (self.__class__.__name__, to, RECURSIVE_RELATIONSHIP_CONSTANT)
|
||||
else:
|
||||
assert not to._meta.abstract, "%s cannot define a relation with abstract class %s" % (self.__class__.__name__, to._meta.object_name)
|
||||
# For backwards compatibility purposes, we need to *try* and set
|
||||
# the to_field during FK construction. It won't be guaranteed to
|
||||
# be correct until contribute_to_class is called. Refs #12190.
|
||||
to_field = to_field or (to._meta.pk and to._meta.pk.name)
|
||||
kwargs['verbose_name'] = kwargs.get('verbose_name', None)
|
||||
|
||||
kwargs['rel'] = rel_class(to, to_field,
|
||||
@ -849,20 +857,13 @@ def create_many_to_many_intermediary_model(field, klass):
|
||||
'db_table': field._get_m2m_db_table(klass._meta),
|
||||
'managed': managed,
|
||||
'auto_created': klass,
|
||||
'app_label': klass._meta.app_label,
|
||||
'unique_together': (from_, to)
|
||||
})
|
||||
# If the models have been split into subpackages, klass.__module__
|
||||
# will be the subpackge, not the models module for the app. (See #12168)
|
||||
# Compose the actual models module name by stripping the trailing parts
|
||||
# of the namespace until we find .models
|
||||
parts = klass.__module__.split('.')
|
||||
while parts[-1] != 'models':
|
||||
parts.pop()
|
||||
module = '.'.join(parts)
|
||||
# Construct and return the new class.
|
||||
return type(name, (models.Model,), {
|
||||
'Meta': meta,
|
||||
'__module__': module,
|
||||
'__module__': klass.__module__,
|
||||
from_: models.ForeignKey(klass, related_name='%s+' % name),
|
||||
to: models.ForeignKey(to_model, related_name='%s+' % name)
|
||||
})
|
||||
|
@ -409,7 +409,7 @@ class DateQuery(Query):
|
||||
self.select = [select]
|
||||
self.select_fields = [None]
|
||||
self.select_related = False # See #7097.
|
||||
self.extra = {}
|
||||
self.set_extra_mask([])
|
||||
self.distinct = True
|
||||
self.order_by = order == 'ASC' and [1] or [-1]
|
||||
|
||||
|
@ -201,7 +201,5 @@ The Django open-source project
|
||||
|
||||
* **Django over time:**
|
||||
:ref:`API stability <misc-api-stability>` |
|
||||
:ref:`Archive of release notes <releases-index>` | `Backwards-incompatible changes`_ |
|
||||
:ref:`Release notes and upgrading instructions <releases-index>` |
|
||||
:ref:`Deprecation Timeline <internals-deprecation>`
|
||||
|
||||
.. _Backwards-incompatible changes: http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges
|
||||
|
@ -200,6 +200,19 @@ Karen Tracey
|
||||
.. _Bauhaus-University Weimar: http://www.uni-weimar.de/
|
||||
.. _pinax: http://pinaxproject.com/
|
||||
|
||||
`James Tauber`_
|
||||
James is the lead developer of Pinax_ and the CEO and founder of
|
||||
Eldarion_. He has been doing open source software since 1993, Python
|
||||
since 1998 and Django since 2006. He serves on the board of the Python
|
||||
Software Foundation and is currently on a leave of absence from a PhD in
|
||||
linguistics.
|
||||
|
||||
James currently lives in Boston, MA, USA but originally hails from
|
||||
Perth, Western Australia where he attended the same high school as
|
||||
Russell Keith-Magee.
|
||||
|
||||
.. _James Tauber: http://jtauber.com/
|
||||
|
||||
Specialists
|
||||
-----------
|
||||
|
||||
|
@ -46,7 +46,7 @@ To enable CSRF protection for your views, follow these steps:
|
||||
``django.views.decorators.csrf.csrf_protect`` on particular views you
|
||||
want to protect (see below).
|
||||
|
||||
2. In any template that uses a POST form, use the ``csrf_token`` tag inside
|
||||
2. In any template that uses a POST form, use the :ttag:`csrf_token` tag inside
|
||||
the ``<form>`` element if the form is for an internal URL, e.g.::
|
||||
|
||||
<form action="" method="POST">{% csrf_token %}
|
||||
@ -123,14 +123,14 @@ as ``CsrfResponseMiddleware``, and it can be used by following these steps:
|
||||
|
||||
``CsrfResponseMiddleware`` needs to process the response before things
|
||||
like compression or setting ofETags happen to the response, so it must
|
||||
come after ``GZipMiddleware``, ``CommonMiddleware`` and
|
||||
come after ``GZipMiddleware``, ``CommonMiddleware`` and
|
||||
``ConditionalGetMiddleware`` in the list. It also must come after
|
||||
``CsrfViewMiddleware``.
|
||||
|
||||
Use of the ``CsrfResponseMiddleware`` is not recommended because of the
|
||||
performance hit it imposes, and because of a potential security problem (see
|
||||
below). It can be used as an interim measure until applications have been
|
||||
updated to use the ``{% csrf_token %}`` tag. It is deprecated and will be
|
||||
updated to use the :ttag:`csrf_token` tag. It is deprecated and will be
|
||||
removed in Django 1.4.
|
||||
|
||||
Django 1.1 and earlier provided a single ``CsrfMiddleware`` class. This is also
|
||||
@ -153,6 +153,8 @@ launch a CSRF attack on your site against that user. The
|
||||
``@csrf_response_exempt`` decorator can be used to fix this, but only if the
|
||||
page doesn't also contain internal forms that require the token.
|
||||
|
||||
.. _ref-csrf-upgrading-notes:
|
||||
|
||||
Upgrading notes
|
||||
---------------
|
||||
|
||||
@ -199,7 +201,7 @@ Note that contrib apps, such as the admin, have been updated to use the
|
||||
``CsrfViewMiddleware`` to your settings. However, if you have supplied
|
||||
customised templates to any of the view functions of contrib apps (whether
|
||||
explicitly via a keyword argument, or by overriding built-in templates), **you
|
||||
MUST update them** to include the ``csrf_token`` template tag as described
|
||||
MUST update them** to include the :ttag:`csrf_token` template tag as described
|
||||
above, or they will stop working. (If you cannot update these templates for
|
||||
some reason, you will be forced to use ``CsrfResponseMiddleware`` for these
|
||||
views to continue working).
|
||||
@ -364,7 +366,7 @@ exactly that.
|
||||
Caching
|
||||
=======
|
||||
|
||||
If the ``csrf_token`` template tag is used by a template (or the ``get_token``
|
||||
If the :ttag:`csrf_token` template tag is used by a template (or the ``get_token``
|
||||
function is called some other way), ``CsrfViewMiddleware`` will add a cookie and
|
||||
a ``Vary: Cookie`` header to the response. Similarly,
|
||||
``CsrfResponseMiddleware`` will send the ``Vary: Cookie`` header if it inserted
|
||||
|
@ -78,11 +78,9 @@ Examples of output::
|
||||
Displaying debug output
|
||||
-----------------------
|
||||
|
||||
.. django-admin-option:: --verbosity <amount>
|
||||
|
||||
Use ``--verbosity`` to specify the amount of notification and debug information
|
||||
Use :djadminopt:`--verbosity` to specify the amount of notification and debug information
|
||||
that ``django-admin.py`` should print to the console. For more details, see the
|
||||
documentation for the :ref:`default options for django-admin.py <django-admin-verbosity>`.
|
||||
documentation for the :djadminopt:`--verbosity` option.
|
||||
|
||||
Available subcommands
|
||||
=====================
|
||||
@ -90,6 +88,8 @@ Available subcommands
|
||||
cleanup
|
||||
-------
|
||||
|
||||
.. django-admin:: cleanup
|
||||
|
||||
.. versionadded:: 1.0
|
||||
|
||||
Can be run as a cronjob or directly to clean out old data from the database
|
||||
@ -98,17 +98,16 @@ Can be run as a cronjob or directly to clean out old data from the database
|
||||
compilemessages
|
||||
---------------
|
||||
|
||||
.. django-admin:: compilemessages
|
||||
|
||||
.. versionchanged:: 1.0
|
||||
Before 1.0 this was the "bin/compile-messages.py" command.
|
||||
|
||||
Compiles .po files created with ``makemessages`` to .mo files for use with
|
||||
the builtin gettext support. See :ref:`topics-i18n`.
|
||||
|
||||
--locale
|
||||
~~~~~~~~
|
||||
|
||||
Use the ``--locale`` or ``-l`` option to specify the locale to process.
|
||||
If not provided all locales are processed.
|
||||
Use the :djadminopt:`--locale`` option to specify the locale to process.
|
||||
If not provided, all locales are processed.
|
||||
|
||||
Example usage::
|
||||
|
||||
@ -117,7 +116,7 @@ Example usage::
|
||||
createcachetable
|
||||
----------------
|
||||
|
||||
.. django-admin:: createcachetable <tablename>
|
||||
.. django-admin:: createcachetable
|
||||
|
||||
Creates a cache table named ``tablename`` for use with the database cache
|
||||
backend. See :ref:`topics-cache` for more information.
|
||||
@ -183,10 +182,10 @@ example, the default settings don't define ``ROOT_URLCONF``, so
|
||||
Note that Django's default settings live in ``django/conf/global_settings.py``,
|
||||
if you're ever curious to see the full list of defaults.
|
||||
|
||||
dumpdata
|
||||
--------
|
||||
dumpdata <appname appname appname.Model ...>
|
||||
--------------------------------------------
|
||||
|
||||
.. django-admin:: dumpdata <appname appname appname.Model ...>
|
||||
.. django-admin:: dumpdata
|
||||
|
||||
Outputs to standard output all data in the database associated with the named
|
||||
application(s).
|
||||
@ -215,18 +214,17 @@ directives::
|
||||
|
||||
django-admin.py dumpdata --exclude=auth --exclude=contenttypes
|
||||
|
||||
|
||||
.. django-admin-option:: --format <fmt>
|
||||
|
||||
By default, ``dumpdata`` will format its output in JSON, but you can use the
|
||||
``--format`` option to specify another format. Currently supported formats
|
||||
are listed in :ref:`serialization-formats`.
|
||||
By default, ``dumpdata`` will format its output in JSON, but you can use the
|
||||
``--format`` option to specify another format. Currently supported formats
|
||||
are listed in :ref:`serialization-formats`.
|
||||
|
||||
.. django-admin-option:: --indent <num>
|
||||
|
||||
By default, ``dumpdata`` will output all data on a single line. This isn't
|
||||
easy for humans to read, so you can use the ``--indent`` option to
|
||||
pretty-print the output with a number of indentation spaces.
|
||||
By default, ``dumpdata`` will output all data on a single line. This isn't
|
||||
easy for humans to read, so you can use the ``--indent`` option to
|
||||
pretty-print the output with a number of indentation spaces.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
@ -239,22 +237,21 @@ model names.
|
||||
flush
|
||||
-----
|
||||
|
||||
.. django-admin: flush
|
||||
.. django-admin:: flush
|
||||
|
||||
Returns the database to the state it was in immediately after syncdb was
|
||||
executed. This means that all data will be removed from the database, any
|
||||
post-synchronization handlers will be re-executed, and the ``initial_data``
|
||||
fixture will be re-installed.
|
||||
|
||||
.. django-admin-option:: --noinput
|
||||
|
||||
Use the ``--noinput`` option to suppress all user prompting, such as "Are
|
||||
you sure?" confirmation messages. This is useful if ``django-admin.py`` is
|
||||
being executed as an unattended, automated script.
|
||||
The :djadminopt:`--noinput` option may be provided to suppress all user
|
||||
prompts.
|
||||
|
||||
inspectdb
|
||||
---------
|
||||
|
||||
.. django-admin:: inspectdb
|
||||
|
||||
Introspects the database tables in the database pointed-to by the
|
||||
``DATABASE_NAME`` setting and outputs a Django model module (a ``models.py``
|
||||
file) to standard output.
|
||||
@ -296,6 +293,8 @@ only works in PostgreSQL and with certain types of MySQL tables.
|
||||
loaddata <fixture fixture ...>
|
||||
------------------------------
|
||||
|
||||
.. django-admin:: loaddata
|
||||
|
||||
Searches for and loads the contents of the named fixture into the database.
|
||||
|
||||
What's a "fixture"?
|
||||
@ -382,6 +381,8 @@ installation will be aborted, and any data installed in the call to
|
||||
makemessages
|
||||
------------
|
||||
|
||||
.. django-admin:: makemessages
|
||||
|
||||
.. versionchanged:: 1.0
|
||||
Before 1.0 this was the ``bin/make-messages.py`` command.
|
||||
|
||||
@ -392,8 +393,7 @@ directory. After making changes to the messages files you need to compile them
|
||||
with ``compilemessages`` for use with the builtin gettext support. See the
|
||||
:ref:`i18n documentation <how-to-create-language-files>` for details.
|
||||
|
||||
--all
|
||||
~~~~~
|
||||
.. django-admin-option:: --all
|
||||
|
||||
Use the ``--all`` or ``-a`` option to update the message files for all
|
||||
available languages.
|
||||
@ -402,8 +402,7 @@ Example usage::
|
||||
|
||||
django-admin.py makemessages --all
|
||||
|
||||
--extension
|
||||
~~~~~~~~~~~
|
||||
.. django-admin-option:: --extension
|
||||
|
||||
Use the ``--extension`` or ``-e`` option to specify a list of file extensions
|
||||
to examine (default: ".html").
|
||||
@ -416,17 +415,13 @@ Separate multiple extensions with commas or use -e or --extension multiple times
|
||||
|
||||
django-admin.py makemessages --locale=de --extension=html,txt --extension xml
|
||||
|
||||
--locale
|
||||
~~~~~~~~
|
||||
|
||||
Use the ``--locale`` or ``-l`` option to specify the locale to process.
|
||||
Use the :djadminopt:`--locale` option to specify the locale to process.
|
||||
|
||||
Example usage::
|
||||
|
||||
django-admin.py makemessages --locale=br_PT
|
||||
|
||||
--domain
|
||||
~~~~~~~~
|
||||
.. django-admin-option:: --domain
|
||||
|
||||
Use the ``--domain`` or ``-d`` option to change the domain of the messages files.
|
||||
Currently supported:
|
||||
@ -434,23 +429,21 @@ Currently supported:
|
||||
* ``django`` for all ``*.py`` and ``*.html`` files (default)
|
||||
* ``djangojs`` for ``*.js`` files
|
||||
|
||||
.. _django-admin-reset:
|
||||
|
||||
reset <appname appname ...>
|
||||
---------------------------
|
||||
|
||||
.. django-admin:: reset
|
||||
|
||||
Executes the equivalent of ``sqlreset`` for the given app name(s).
|
||||
|
||||
--noinput
|
||||
~~~~~~~~~
|
||||
|
||||
Use the ``--noinput`` option to suppress all user prompting, such as
|
||||
"Are you sure?" confirmation messages. This is useful if ``django-admin.py``
|
||||
is being executed as an unattended, automated script.
|
||||
The :djadminopt:`--noinput` option may be provided to suppress all user
|
||||
prompts.
|
||||
|
||||
runfcgi [options]
|
||||
-----------------
|
||||
|
||||
.. django-admin:: runfcgi
|
||||
|
||||
Starts a set of FastCGI processes suitable for use with any Web server that
|
||||
supports the FastCGI protocol. See the :ref:`FastCGI deployment documentation
|
||||
<howto-deployment-fastcgi>` for details. Requires the Python FastCGI module from
|
||||
@ -458,10 +451,10 @@ supports the FastCGI protocol. See the :ref:`FastCGI deployment documentation
|
||||
|
||||
.. _flup: http://www.saddi.com/software/flup/
|
||||
|
||||
runserver
|
||||
---------
|
||||
runserver [port or ipaddr:port]
|
||||
-------------------------------
|
||||
|
||||
.. django-admin:: runserver [port or ipaddr:port]
|
||||
.. django-admin:: runserver
|
||||
|
||||
Starts a lightweight development Web server on the local machine. By default,
|
||||
the server runs on port 8000 on the IP address 127.0.0.1. You can pass in an
|
||||
@ -544,6 +537,8 @@ you want to configure Django to serve static media, read :ref:`howto-static-file
|
||||
shell
|
||||
-----
|
||||
|
||||
.. django-admin:: shell
|
||||
|
||||
Starts the Python interactive interpreter.
|
||||
|
||||
Django will use IPython_, if it's installed. If you have IPython installed and
|
||||
@ -557,11 +552,15 @@ option, like so::
|
||||
sql <appname appname ...>
|
||||
-------------------------
|
||||
|
||||
.. django-admin:: sql
|
||||
|
||||
Prints the CREATE TABLE SQL statements for the given app name(s).
|
||||
|
||||
sqlall <appname appname ...>
|
||||
----------------------------
|
||||
|
||||
.. django-admin:: sqlall
|
||||
|
||||
Prints the CREATE TABLE and initial-data SQL statements for the given app name(s).
|
||||
|
||||
Refer to the description of ``sqlcustom`` for an explanation of how to
|
||||
@ -570,11 +569,15 @@ specify initial data.
|
||||
sqlclear <appname appname ...>
|
||||
------------------------------
|
||||
|
||||
.. django-admin:: sqlclear
|
||||
|
||||
Prints the DROP TABLE SQL statements for the given app name(s).
|
||||
|
||||
sqlcustom <appname appname ...>
|
||||
-------------------------------
|
||||
|
||||
.. django-admin:: sqlcustom
|
||||
|
||||
Prints the custom SQL statements for the given app name(s).
|
||||
|
||||
For each model in each specified app, this command looks for the file
|
||||
@ -594,21 +597,30 @@ Note that the order in which the SQL files are processed is undefined.
|
||||
sqlflush
|
||||
--------
|
||||
|
||||
Prints the SQL statements that would be executed for the `flush`_ command.
|
||||
.. django-admin:: sqlflush
|
||||
|
||||
Prints the SQL statements that would be executed for the :djadmin:`flush`
|
||||
command.
|
||||
|
||||
sqlindexes <appname appname ...>
|
||||
--------------------------------
|
||||
|
||||
.. django-admin:: sqlindexes
|
||||
|
||||
Prints the CREATE INDEX SQL statements for the given app name(s).
|
||||
|
||||
sqlreset <appname appname ...>
|
||||
------------------------------
|
||||
|
||||
.. django-admin:: sqlreset
|
||||
|
||||
Prints the DROP TABLE SQL, then the CREATE TABLE SQL, for the given app name(s).
|
||||
|
||||
sqlsequencereset <appname appname ...>
|
||||
--------------------------------------
|
||||
|
||||
.. django-admin:: sqlsequencereset
|
||||
|
||||
Prints the SQL statements for resetting sequences for the given app name(s).
|
||||
|
||||
Sequences are indexes used by some database engines to track the next available
|
||||
@ -620,12 +632,16 @@ of sync with its automatically incremented field data.
|
||||
startapp <appname>
|
||||
------------------
|
||||
|
||||
.. django-admin:: startapp
|
||||
|
||||
Creates a Django app directory structure for the given app name in the current
|
||||
directory.
|
||||
|
||||
startproject <projectname>
|
||||
--------------------------
|
||||
|
||||
.. django-admin:: startproject
|
||||
|
||||
Creates a Django project directory structure for the given project name in the
|
||||
current directory.
|
||||
|
||||
@ -635,11 +651,11 @@ This command is disabled when the ``--settings`` option to
|
||||
situations, either omit the ``--settings`` option or unset
|
||||
``DJANGO_SETTINGS_MODULE``.
|
||||
|
||||
.. _django-admin-syncdb:
|
||||
|
||||
syncdb
|
||||
------
|
||||
|
||||
.. django-admin:: syncdb
|
||||
|
||||
Creates the database tables for all apps in ``INSTALLED_APPS`` whose tables
|
||||
have not already been created.
|
||||
|
||||
@ -669,29 +685,22 @@ with an appropriate extension (e.g. ``json`` or ``xml``). See the
|
||||
documentation for ``loaddata`` for details on the specification of fixture
|
||||
data files.
|
||||
|
||||
--noinput
|
||||
~~~~~~~~~
|
||||
The :djadminopt:`--noinput` option may be provided to suppress all user
|
||||
prompts.
|
||||
|
||||
Use the ``--noinput`` option to suppress all user prompting, such as
|
||||
"Are you sure?" confirmation messages. This is useful if ``django-admin.py``
|
||||
is being executed as an unattended, automated script.
|
||||
test <app or test identifier>
|
||||
-----------------------------
|
||||
|
||||
test
|
||||
----
|
||||
.. django-admin:: test
|
||||
|
||||
Runs tests for all installed models. See :ref:`topics-testing` for more
|
||||
information.
|
||||
|
||||
--noinput
|
||||
~~~~~~~~~
|
||||
|
||||
Use the ``--noinput`` option to suppress all user prompting, such as
|
||||
"Are you sure?" confirmation messages. This is useful if ``django-admin.py``
|
||||
is being executed as an unattended, automated script.
|
||||
|
||||
testserver <fixture fixture ...>
|
||||
--------------------------------
|
||||
|
||||
.. django-admin:: testserver
|
||||
|
||||
.. versionadded:: 1.0
|
||||
|
||||
Runs a Django development server (as in ``runserver``) using data from the
|
||||
@ -727,8 +736,7 @@ Note that this server does *not* automatically detect changes to your Python
|
||||
source code (as ``runserver`` does). It does, however, detect changes to
|
||||
templates.
|
||||
|
||||
--addrport [port number or ipaddr:port]
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
.. django-admin-option:: --addrport [port number or ipaddr:port]
|
||||
|
||||
Use ``--addrport`` to specify a different port, or IP address and port, from
|
||||
the default of 127.0.0.1:8000. This value follows exactly the same format and
|
||||
@ -752,6 +760,8 @@ To run on 1.2.3.4:7000 with a ``test`` fixture::
|
||||
validate
|
||||
--------
|
||||
|
||||
.. django-admin:: validate
|
||||
|
||||
Validates all installed models (according to the ``INSTALLED_APPS`` setting)
|
||||
and prints validation errors to standard output.
|
||||
|
||||
@ -761,8 +771,7 @@ Default options
|
||||
Although some subcommands may allow their own custom options, every subcommand
|
||||
allows for the following options:
|
||||
|
||||
--pythonpath
|
||||
------------
|
||||
.. django-admin-option:: --pythonpath
|
||||
|
||||
Example usage::
|
||||
|
||||
@ -777,8 +786,7 @@ setting the Python path for you.
|
||||
|
||||
.. _import search path: http://diveintopython.org/getting_to_know_python/everything_is_an_object.html
|
||||
|
||||
--settings
|
||||
----------
|
||||
.. django-admin-option:: --settings
|
||||
|
||||
Example usage::
|
||||
|
||||
@ -792,8 +800,7 @@ variable.
|
||||
Note that this option is unnecessary in ``manage.py``, because it uses
|
||||
``settings.py`` from the current project by default.
|
||||
|
||||
--traceback
|
||||
-----------
|
||||
.. django-admin-option:: --traceback
|
||||
|
||||
Example usage::
|
||||
|
||||
@ -803,10 +810,7 @@ By default, ``django-admin.py`` will show a simple error message whenever an
|
||||
error occurs. If you specify ``--traceback``, ``django-admin.py`` will
|
||||
output a full stack trace whenever an exception is raised.
|
||||
|
||||
.. _django-admin-verbosity:
|
||||
|
||||
--verbosity
|
||||
-----------
|
||||
.. django-admin-option:: --verbosity
|
||||
|
||||
Example usage::
|
||||
|
||||
@ -819,6 +823,23 @@ that ``django-admin.py`` should print to the console.
|
||||
* ``1`` means normal output (default).
|
||||
* ``2`` means verbose output.
|
||||
|
||||
Common options
|
||||
==============
|
||||
|
||||
The following options are not available on every commands, but they are
|
||||
common to a number of commands.
|
||||
|
||||
.. django-admin-option:: --locale
|
||||
|
||||
Use the ``--locale`` or ``-l`` option to specify the locale to process.
|
||||
If not provided all locales are processed.
|
||||
|
||||
.. django-admin-option:: --noinput
|
||||
|
||||
Use the ``--noinput`` option to suppress all user prompting, such as "Are
|
||||
you sure?" confirmation messages. This is useful if ``django-admin.py`` is
|
||||
being executed as an unattended, automated script.
|
||||
|
||||
Extra niceties
|
||||
==============
|
||||
|
||||
@ -844,5 +865,4 @@ distribution. It enables tab-completion of ``django-admin.py`` and
|
||||
with ``sql``.
|
||||
|
||||
|
||||
|
||||
See :ref:`howto-custom-management-commands` for how to add customized actions.
|
||||
|
@ -94,9 +94,8 @@ See the docs for :meth:`~django.db.models.QuerySet.latest` for more.
|
||||
.. versionadded:: 1.1
|
||||
|
||||
Defaults to ``True``, meaning Django will create the appropriate database
|
||||
tables in :ref:`django-admin-syncdb` and remove them as part of a :ref:`reset
|
||||
<django-admin-reset>` management command. That is, Django *manages* the
|
||||
database tables' lifecycles.
|
||||
tables in :djadmin:`syncdb` and remove them as part of a :djadmin:`reset`
|
||||
management command. That is, Django *manages* the database tables' lifecycles.
|
||||
|
||||
If ``False``, no database table creation or deletion operations will be
|
||||
performed for this model. This is useful if the model represents an existing
|
||||
@ -114,7 +113,7 @@ model handling are exactly the same as normal. This includes
|
||||
unmanaged model, then the intermediate table for the many-to-many join
|
||||
will also not be created. However, a the intermediary table between one
|
||||
managed and one unmanaged model *will* be created.
|
||||
|
||||
|
||||
If you need to change this default behavior, create the intermediary
|
||||
table as an explicit model (with ``managed`` set as needed) and use the
|
||||
:attr:`ManyToManyField.through` attribute to make the relation use your
|
||||
|
@ -51,6 +51,18 @@ comment
|
||||
|
||||
Ignore everything between ``{% comment %}`` and ``{% endcomment %}``
|
||||
|
||||
.. templatetag:: csrf_token
|
||||
|
||||
csrf_token
|
||||
~~~~~~~~~~
|
||||
|
||||
.. versionadded:: 1.1.2
|
||||
|
||||
In the Django 1.1.X series, this is a no-op tag that returns an empty string for
|
||||
future compatibility purposes. In Django 1.2 and later, it is used for CSRF
|
||||
protection, as described in the documentation for :ref:`Cross Site Request
|
||||
Forgeries <ref-contrib-csrf>`.
|
||||
|
||||
.. templatetag:: cycle
|
||||
|
||||
csrf_token
|
||||
|
36
docs/releases/1.1.2.txt
Normal file
36
docs/releases/1.1.2.txt
Normal file
@ -0,0 +1,36 @@
|
||||
.. _releases-1.1.2:
|
||||
|
||||
==============================================
|
||||
Django 1.1.2 release notes — UNDER DEVELOPMENT
|
||||
==============================================
|
||||
|
||||
This page documents release notes for the as-yet-unreleased Django
|
||||
1.1.2. As such it is tentative and subject to change. It provides
|
||||
up-to-date information for those who are following the 1.1.X branch.
|
||||
|
||||
This is the second "bugfix" release in the Django 1.1 series,
|
||||
improving the stability and performance of the Django 1.1 codebase.
|
||||
|
||||
Django 1.1.2 maintains backwards compatibility with Django
|
||||
1.1.0, but contain a number of fixes and other
|
||||
improvements. Django 1.1.2 is a recommended upgrade for any
|
||||
development or deployment currently using or targeting Django 1.1.
|
||||
|
||||
For full details on the new features, backwards incompatibilities, and
|
||||
deprecated features in the 1.1 branch, see the :ref:`releases-1.1`.
|
||||
|
||||
One new feature
|
||||
---------------
|
||||
|
||||
Ordinarily, a point release would not include new features, but in the
|
||||
case of Django 1.1.2, we have made an exception to this rule. Django
|
||||
1.2 (the next major release of Django) will contain a feature that
|
||||
will improve protection against Cross-Site Request Forgery (CSRF)
|
||||
attacks. This feature requires the use of a new :ttag:`csrf_token`
|
||||
template tag in all forms that Django renders.
|
||||
|
||||
To make it easier to support both 1.1.X and 1.2.X versions of Django with
|
||||
the same templates, we have decided to introduce the :ttag:`csrf_token` template
|
||||
tag to the 1.1.X branch. In the 1.1.X branch, :ttag:`csrf_token` does nothing -
|
||||
it has no effect on templates or form processing. However, it means that the
|
||||
same template will work with Django 1.2.
|
@ -14,8 +14,10 @@ fixes, and an easy upgrade path from Django 1.0.
|
||||
|
||||
.. _new features: `What's new in Django 1.1`_
|
||||
|
||||
Backwards-incompatible changes
|
||||
==============================
|
||||
.. _backwards-incompatible-changes-1.1:
|
||||
|
||||
Backwards-incompatible changes in 1.1
|
||||
=====================================
|
||||
|
||||
Django has a policy of :ref:`API stability <misc-api-stability>`. This means
|
||||
that, in general, code you develop against Django 1.0 should continue to work
|
||||
@ -150,6 +152,8 @@ Django 1.1 adds a ``permanent`` argument to the
|
||||
backwards-incompatible if you were using the ``redirect_to`` view with a
|
||||
format-string key called 'permanent', which is highly unlikely.
|
||||
|
||||
.. _deprecated-features-1.1:
|
||||
|
||||
Features deprecated in 1.1
|
||||
==========================
|
||||
|
||||
|
157
docs/releases/1.2.txt
Normal file
157
docs/releases/1.2.txt
Normal file
@ -0,0 +1,157 @@
|
||||
.. _releases-1.2:
|
||||
|
||||
============================================
|
||||
Django 1.2 release notes — UNDER DEVELOPMENT
|
||||
============================================
|
||||
|
||||
This page documents release notes for the as-yet-unreleased Django 1.2. As such
|
||||
it is tentative and subject to change. It provides up-to-date information for
|
||||
those who are following trunk.
|
||||
|
||||
Django 1.2 includes a number of nifty `new features`_, lots of bug
|
||||
fixes, and an easy upgrade path from Django 1.1.
|
||||
|
||||
.. _new features: `What's new in Django 1.2`_
|
||||
|
||||
.. _backwards-incompatible-changes-1.2:
|
||||
|
||||
Backwards-incompatible changes in 1.2
|
||||
=====================================
|
||||
|
||||
CSRF Protection
|
||||
---------------
|
||||
|
||||
There have been large changes to the way that CSRF protection works, detailed in
|
||||
:ref:`the CSRF documentaton <ref-contrib-csrf>`. The following are the major
|
||||
changes that developers must be aware of:
|
||||
|
||||
* ``CsrfResponseMiddleware`` and ``CsrfMiddleware`` have been deprecated, and
|
||||
will be removed completely in Django 1.4, in favor of a template tag that
|
||||
should be inserted into forms.
|
||||
|
||||
* All contrib apps use a ``csrf_protect`` decorator to protect the view. This
|
||||
requires the use of the csrf_token template tag in the template, so if you
|
||||
have used custom templates for contrib views, you MUST READ THE :ref:`UPGRADE
|
||||
INSTRUCTIONS <ref-csrf-upgrading-notes>` to fix those templates.
|
||||
|
||||
* ``CsrfViewMiddleware`` is included in :setting:`MIDDLEWARE_CLASSES` by
|
||||
default. This turns on CSRF protection by default, so that views that accept
|
||||
POST requests need to be written to work with the middleware. Instructions
|
||||
on how to do this are found in the CSRF docs.
|
||||
|
||||
* All of the CSRF has moved from contrib to core (with backwards compatible
|
||||
imports in the old locations, which are deprecated).
|
||||
|
||||
``LazyObject``
|
||||
--------------
|
||||
|
||||
``LazyObject`` is an undocumented utility class used for lazily wrapping other
|
||||
objects of unknown type. In Django 1.1 and earlier, it handled introspection in
|
||||
a non-standard way, depending on wrapped objects implementing a public method
|
||||
``get_all_members()``. Since this could easily lead to name clashes, it has been
|
||||
changed to use the standard method, involving ``__members__`` and ``__dir__()``.
|
||||
If you used ``LazyObject`` in your own code, and implemented the
|
||||
``get_all_members()`` method for wrapped objects, you need to make the following
|
||||
changes:
|
||||
|
||||
* If your class does not have special requirements for introspection (i.e. you
|
||||
have not implemented ``__getattr__()`` or other methods that allow for
|
||||
attributes not discoverable by normal mechanisms), you can simply remove the
|
||||
``get_all_members()`` method. The default implementation on ``LazyObject``
|
||||
will do the right thing.
|
||||
|
||||
* If you have more complex requirements for introspection, first rename the
|
||||
``get_all_members()`` method to ``__dir__()``. This is the standard method,
|
||||
from Python 2.6 onwards, for supporting introspection. If you are require
|
||||
support for Python < 2.6, add the following code to the class::
|
||||
|
||||
__members__ = property(lambda self: self.__dir__())
|
||||
|
||||
|
||||
.. _deprecated-features-1.2:
|
||||
|
||||
Features deprecated in 1.2
|
||||
==========================
|
||||
|
||||
CSRF response rewriting middleware
|
||||
----------------------------------
|
||||
|
||||
``CsrfResponseMiddleware``, the middleware that automatically inserted CSRF
|
||||
tokens into POST forms in outgoing pages, has been deprecated in favor of a
|
||||
template tag method (see above), and will be removed completely in Django
|
||||
1.4. ``CsrfMiddleware``, which includes the functionality of
|
||||
``CsrfResponseMiddleware`` and ``CsrfViewMiddleware`` has likewise been
|
||||
deprecated.
|
||||
|
||||
Also, the CSRF module has moved from contrib to core, and the old imports are
|
||||
deprecated, as described in the :ref:`upgrading notes <ref-csrf-upgrading-notes>`.
|
||||
|
||||
``SMTPConnection``
|
||||
------------------
|
||||
|
||||
The ``SMTPConnection`` class has been deprecated in favor of a generic
|
||||
E-mail backend API. Old code that explicitly instantiated an instance
|
||||
of an SMTPConnection::
|
||||
|
||||
from django.core.mail import SMTPConnection
|
||||
connection = SMTPConnection()
|
||||
messages = get_notification_email()
|
||||
connection.send_messages(messages)
|
||||
|
||||
should now call :meth:`~django.core.mail.get_connection()` to
|
||||
instantiate a generic e-mail connection::
|
||||
|
||||
from django.core.mail import get_connection
|
||||
connection = get_connection()
|
||||
messages = get_notification_email()
|
||||
connection.send_messages(messages)
|
||||
|
||||
Depending on the value of the :setting:`EMAIL_BACKEND` setting, this
|
||||
may not return an SMTP connection. If you explicitly require an SMTP
|
||||
connection with which to send e-mail, you can explicitly request an
|
||||
SMTP connection::
|
||||
|
||||
from django.core.mail import get_connection
|
||||
connection = get_connection('django.core.mail.backends.smtp')
|
||||
messages = get_notification_email()
|
||||
connection.send_messages(messages)
|
||||
|
||||
If your call to construct an instance of ``SMTPConnection`` required
|
||||
additional arguments, those arguments can be passed to the
|
||||
:meth:`~django.core.mail.get_connection()` call::
|
||||
|
||||
connection = get_connection('django.core.mail.backends.smtp', hostname='localhost', port=1234)
|
||||
|
||||
What's new in Django 1.2
|
||||
========================
|
||||
|
||||
CSRF support
|
||||
------------
|
||||
|
||||
Django now has much improved protection against :ref:`Cross-Site
|
||||
Request Forgery (CSRF) attacks<ref-contrib-csrf>`. This type of attack
|
||||
occurs when a malicious Web site contains a link, a form button or
|
||||
some javascript that is intended to perform some action on your Web
|
||||
site, using the credentials of a logged-in user who visits the
|
||||
malicious site in their browser. A related type of attack, 'login
|
||||
CSRF', where an attacking site tricks a user's browser into logging
|
||||
into a site with someone else's credentials, is also covered.
|
||||
|
||||
E-mail Backends
|
||||
---------------
|
||||
|
||||
You can now :ref:`configure the way that Django sends e-mail
|
||||
<topic-email-backends>`. Instead of using SMTP to send all e-mail, you
|
||||
can now choose a configurable e-mail backend to send messages. If your
|
||||
hosting provider uses a sandbox or some other non-SMTP technique for
|
||||
sending mail, you can now construct an e-mail backend that will allow
|
||||
Django's standard :ref:`mail sending methods<topics-email>` to use
|
||||
those facilities.
|
||||
|
||||
This also makes it easier to debug mail sending - Django ships with
|
||||
backend implementations that allow you to send e-mail to a
|
||||
:ref:`file<topic-email-file-backend>`, to the
|
||||
:ref:`console<topic-email-console-backend>`, or to
|
||||
:ref:`memory<topic-email-memory-backend>` - you can even configure all
|
||||
e-mail to be :ref:`thrown away<topic-email-dummy-backend>`.
|
||||
|
@ -1,5 +1,6 @@
|
||||
.. _releases-index:
|
||||
|
||||
=============
|
||||
Release notes
|
||||
=============
|
||||
|
||||
@ -7,28 +8,60 @@ Release notes for the official Django releases. Each release note will tell you
|
||||
what's new in each version, and will also describe any backwards-incompatible
|
||||
changes made in that version.
|
||||
|
||||
For those upgrading to a new version of Django, you will need to check
|
||||
all the backwards-incompatible changes and deprecated features for
|
||||
each 'final' release from the one after your current Django version,
|
||||
up to and including the new version.
|
||||
|
||||
Final releases
|
||||
==============
|
||||
|
||||
1.2 release
|
||||
-----------
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
0.95
|
||||
0.96
|
||||
1.0-alpha-1
|
||||
1.0-alpha-2
|
||||
1.0-beta
|
||||
1.0-beta-2
|
||||
1.0
|
||||
1.0.1
|
||||
1.0.2
|
||||
1.1-alpha-1
|
||||
1.1-beta-1
|
||||
1.1-rc-1
|
||||
1.2
|
||||
|
||||
1.1 release
|
||||
-----------
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
1.1.2
|
||||
1.1
|
||||
|
||||
.. seealso::
|
||||
1.0 release
|
||||
-----------
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
The list of `backwards-incompatible changes`_ made in the current
|
||||
development "trunk". If you're running versions of Django newer than an
|
||||
official release, you should keep track of new pieces pointed there. It's
|
||||
also fun reading if you're looking forward to new versions of Django.
|
||||
1.0.2
|
||||
1.0.1
|
||||
1.0
|
||||
|
||||
.. _backwards-incompatible changes: http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges
|
||||
Pre-1.0 releases
|
||||
----------------
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
0.96
|
||||
0.95
|
||||
|
||||
Development releases
|
||||
====================
|
||||
|
||||
These notes are retained for historical purposes. If you are upgrading
|
||||
between formal Django releases, you don't need to worry about these
|
||||
notes.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
1.1-rc-1
|
||||
1.1-beta-1
|
||||
1.1-alpha-1
|
||||
1.0-beta-2
|
||||
1.0-beta
|
||||
1.0-alpha-2
|
||||
1.0-alpha-1
|
||||
|
@ -179,9 +179,9 @@ Local-memory caching
|
||||
If you want the speed advantages of in-memory caching but don't have the
|
||||
capability of running Memcached, consider the local-memory cache backend. This
|
||||
cache is multi-process and thread-safe. To use it, set ``CACHE_BACKEND`` to
|
||||
``"locmem:///"``. For example::
|
||||
``"locmem://"``. For example::
|
||||
|
||||
CACHE_BACKEND = 'locmem:///'
|
||||
CACHE_BACKEND = 'locmem://'
|
||||
|
||||
Note that each process will have its own private cache instance, which means no
|
||||
cross-process caching is possible. This obviously also means the local memory
|
||||
@ -199,7 +199,7 @@ various places but a development/test environment where you don't want to cache
|
||||
and don't want to have to change your code to special-case the latter. To
|
||||
activate dummy caching, set ``CACHE_BACKEND`` like so::
|
||||
|
||||
CACHE_BACKEND = 'dummy:///'
|
||||
CACHE_BACKEND = 'dummy://'
|
||||
|
||||
Using a custom cache backend
|
||||
----------------------------
|
||||
@ -249,7 +249,7 @@ In this example, ``timeout`` is set to ``60``::
|
||||
|
||||
In this example, ``timeout`` is ``30`` and ``max_entries`` is ``400``::
|
||||
|
||||
CACHE_BACKEND = "locmem:///?timeout=30&max_entries=400"
|
||||
CACHE_BACKEND = "locmem://?timeout=30&max_entries=400"
|
||||
|
||||
Invalid arguments are silently ignored, as are invalid values of known
|
||||
arguments.
|
||||
@ -451,11 +451,11 @@ The low-level cache API
|
||||
Sometimes, caching an entire rendered page doesn't gain you very much and is,
|
||||
in fact, inconvenient overkill.
|
||||
|
||||
Perhaps, for instance, your site includes a view whose results depend on
|
||||
Perhaps, for instance, your site includes a view whose results depend on
|
||||
several expensive queries, the results of which change at different intervals.
|
||||
In this case, it would not be ideal to use the full-page caching that the
|
||||
per-site or per-view cache strategies offer, because you wouldn't want to
|
||||
cache the entire result (since some of the data changes often), but you'd still
|
||||
In this case, it would not be ideal to use the full-page caching that the
|
||||
per-site or per-view cache strategies offer, because you wouldn't want to
|
||||
cache the entire result (since some of the data changes often), but you'd still
|
||||
want to cache the results that rarely change.
|
||||
|
||||
For cases like this, Django exposes a simple, low-level cache API. You can use
|
||||
@ -757,10 +757,10 @@ Django comes with a few other pieces of middleware that can help optimize your
|
||||
apps' performance:
|
||||
|
||||
* ``django.middleware.http.ConditionalGetMiddleware`` adds support for
|
||||
modern browsers to conditionally GET responses based on the ``ETag``
|
||||
modern browsers to conditionally GET responses based on the ``ETag``
|
||||
and ``Last-Modified`` headers.
|
||||
|
||||
* ``django.middleware.gzip.GZipMiddleware`` compresses responses for all
|
||||
* ``django.middleware.gzip.GZipMiddleware`` compresses responses for all
|
||||
moderns browsers, saving bandwidth and transfer time.
|
||||
|
||||
Order of MIDDLEWARE_CLASSES
|
||||
|
@ -10,7 +10,7 @@ Sending e-mail
|
||||
Although Python makes sending e-mail relatively easy via the `smtplib
|
||||
library`_, Django provides a couple of light wrappers over it. These wrappers
|
||||
are provided to make sending e-mail extra quick, to make it easy to test
|
||||
email sending during development, and to provide support for platforms that
|
||||
e-mail sending during development, and to provide support for platforms that
|
||||
can't use SMTP.
|
||||
|
||||
The code lives in the ``django.core.mail`` module.
|
||||
@ -64,7 +64,7 @@ are required.
|
||||
* ``auth_password``: The optional password to use to authenticate to the
|
||||
SMTP server. If this isn't provided, Django will use the value of the
|
||||
``EMAIL_HOST_PASSWORD`` setting.
|
||||
* ``connection``: The optional email backend to use to send the mail.
|
||||
* ``connection``: The optional e-mail backend to use to send the mail.
|
||||
If unspecified, an instance of the default backend will be used.
|
||||
See the documentation on :ref:`E-mail backends <topic-email-backends>`
|
||||
for more details.
|
||||
@ -215,8 +215,8 @@ message itself. The :ref:`e-mail backend <topic-email-backends>` is then
|
||||
responsible for sending the e-mail.
|
||||
|
||||
For convenience, :class:`~django.core.mail.EmailMessage` provides a simple
|
||||
``send()`` method for sending a single email. If you need to send multiple
|
||||
messages, the email backend API :ref:`provides an alternative
|
||||
``send()`` method for sending a single e-mail. If you need to send multiple
|
||||
messages, the e-mail backend API :ref:`provides an alternative
|
||||
<topics-sending-multiple-emails>`.
|
||||
|
||||
EmailMessage Objects
|
||||
@ -264,7 +264,7 @@ For example::
|
||||
The class has the following methods:
|
||||
|
||||
* ``send(fail_silently=False)`` sends the message. If a connection was
|
||||
specified when the email was constructed, that connection will be used.
|
||||
specified when the e-mail was constructed, that connection will be used.
|
||||
Otherwise, an instance of the default backend will be instantiated and
|
||||
used. If the keyword argument ``fail_silently`` is ``True``, exceptions
|
||||
raised while sending the message will be quashed.
|
||||
@ -358,9 +358,9 @@ The actual sending of an e-mail is handled by the e-mail backend.
|
||||
|
||||
The e-mail backend class has the following methods:
|
||||
|
||||
* ``open()`` instantiates an long-lived email-sending connection.
|
||||
* ``open()`` instantiates an long-lived e-mail-sending connection.
|
||||
|
||||
* ``close()`` closes the current email-sending connection.
|
||||
* ``close()`` closes the current e-mail-sending connection.
|
||||
|
||||
* ``send_messages(email_messages)`` sends a list of
|
||||
:class:`~django.core.mail.EmailMessage` objects. If the connection is
|
||||
@ -379,11 +379,11 @@ instance of the e-mail backend that you can use.
|
||||
.. function:: get_connection(backend=None, fail_silently=False, *args, **kwargs)
|
||||
|
||||
By default, a call to ``get_connection()`` will return an instance of the
|
||||
email backend specified in :setting:`EMAIL_BACKEND`. If you specify the
|
||||
e-mail backend specified in :setting:`EMAIL_BACKEND`. If you specify the
|
||||
``backend`` argument, an instance of that backend will be instantiated.
|
||||
|
||||
The ``fail_silently`` argument controls how the backend should handle errors.
|
||||
If ``fail_silently`` is True, exceptions during the email sending process
|
||||
If ``fail_silently`` is True, exceptions during the e-mail sending process
|
||||
will be silently ignored.
|
||||
|
||||
All other arguments are passed directly to the constructor of the
|
||||
@ -391,8 +391,8 @@ e-mail backend.
|
||||
|
||||
Django ships with several e-mail sending backends. With the exception of the
|
||||
SMTP backend (which is the default), these backends are only useful during
|
||||
testing and development. If you have special email sending requirements, you
|
||||
can :ref:`write your own email backend <topic-custom-email-backend>`.
|
||||
testing and development. If you have special e-mail sending requirements, you
|
||||
can :ref:`write your own e-mail backend <topic-custom-email-backend>`.
|
||||
|
||||
.. _topic-email-smtp-backend:
|
||||
|
||||
@ -401,7 +401,7 @@ SMTP backend
|
||||
|
||||
This is the default backend. E-mail will be sent through a SMTP server.
|
||||
The server address and authentication credentials are set in the
|
||||
:setting:`EMAIL_HOST`, :setting:`EMAIL_POST`, :setting:`EMAIL_HOST_USER`,
|
||||
:setting:`EMAIL_HOST`, :setting:`EMAIL_PORT`, :setting:`EMAIL_HOST_USER`,
|
||||
:setting:`EMAIL_HOST_PASSWORD` and :setting:`EMAIL_USE_TLS` settings in your
|
||||
settings file.
|
||||
|
||||
@ -414,13 +414,15 @@ want to specify it explicitly, put the following in your settings::
|
||||
|
||||
Prior to version 1.2, Django provided a
|
||||
:class:`~django.core.mail.SMTPConnection` class. This class provided a way
|
||||
to directly control the use of SMTP to send email. This class has been
|
||||
deprecated in favor of the generic email backend API.
|
||||
to directly control the use of SMTP to send e-mail. This class has been
|
||||
deprecated in favor of the generic e-mail backend API.
|
||||
|
||||
For backwards compatibility :class:`~django.core.mail.SMTPConnection` is
|
||||
still available in ``django.core.mail`` as an alias for the SMTP backend.
|
||||
New code should use :meth:`~django.core.mail.get_connection` instead.
|
||||
|
||||
.. _topic-email-console-backend:
|
||||
|
||||
Console backend
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
@ -436,6 +438,8 @@ To specify this backend, put the following in your settings::
|
||||
This backend is not intended for use in production -- it is provided as a
|
||||
convenience that can be used during development.
|
||||
|
||||
.. _topic-email-file-backend:
|
||||
|
||||
File backend
|
||||
~~~~~~~~~~~~
|
||||
|
||||
@ -453,6 +457,8 @@ To specify this backend, put the following in your settings::
|
||||
This backend is not intended for use in production -- it is provided as a
|
||||
convenience that can be used during development.
|
||||
|
||||
.. _topic-email-memory-backend:
|
||||
|
||||
In-memory backend
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
@ -469,6 +475,8 @@ To specify this backend, put the following in your settings::
|
||||
This backend is not intended for use in production -- it is provided as a
|
||||
convenience that can be used during development and testing.
|
||||
|
||||
.. _topic-email-dummy-backend:
|
||||
|
||||
Dummy backend
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
@ -500,15 +508,15 @@ implementation.
|
||||
|
||||
.. _topics-sending-multiple-emails:
|
||||
|
||||
Sending multiple emails
|
||||
-----------------------
|
||||
Sending multiple e-mails
|
||||
------------------------
|
||||
|
||||
Establishing and closing an SMTP connection (or any other network connection,
|
||||
for that matter) is an expensive process. If you have a lot of emails to send,
|
||||
for that matter) is an expensive process. If you have a lot of e-mails to send,
|
||||
it makes sense to reuse an SMTP connection, rather than creating and
|
||||
destroying a connection every time you want to send an email.
|
||||
destroying a connection every time you want to send an e-mail.
|
||||
|
||||
There are two ways you tell an email backend to reuse a connection.
|
||||
There are two ways you tell an e-mail backend to reuse a connection.
|
||||
|
||||
Firstly, you can use the ``send_messages()`` method. ``send_messages()`` takes
|
||||
a list of :class:`~django.core.mail.EmailMessage` instances (or subclasses),
|
||||
@ -516,11 +524,11 @@ and sends them all using a single connection.
|
||||
|
||||
For example, if you have a function called ``get_notification_email()`` that
|
||||
returns a list of :class:`~django.core.mail.EmailMessage` objects representing
|
||||
some periodic e-mail you wish to send out, you could send these emails using
|
||||
some periodic e-mail you wish to send out, you could send these e-mails using
|
||||
a single call to send_messages::
|
||||
|
||||
from django.core import mail
|
||||
connection = mail.get_connection() # Use default email connection
|
||||
connection = mail.get_connection() # Use default e-mail connection
|
||||
messages = get_notification_email()
|
||||
connection.send_messages(messages)
|
||||
|
||||
@ -528,7 +536,7 @@ In this example, the call to ``send_messages()`` opens a connection on the
|
||||
backend, sends the list of messages, and then closes the connection again.
|
||||
|
||||
The second approach is to use the ``open()`` and ``close()`` methods on the
|
||||
email backend to manually control the connection. ``send_messages()`` will not
|
||||
e-mail backend to manually control the connection. ``send_messages()`` will not
|
||||
manually open or close the connection if it is already open, so if you
|
||||
manually open the connection, you can control when it is closed. For example::
|
||||
|
||||
@ -538,10 +546,10 @@ manually open the connection, you can control when it is closed. For example::
|
||||
# Manually open the connection
|
||||
connection.open()
|
||||
|
||||
# Construct an email message that uses the connection
|
||||
# Construct an e-mail message that uses the connection
|
||||
email1 = mail.EmailMessage('Hello', 'Body goes here', 'from@example.com',
|
||||
['to1@example.com'], connection=connection)
|
||||
email1.send() # Send the email
|
||||
email1.send() # Send the e-mail
|
||||
|
||||
# Construct two more messages
|
||||
email2 = mail.EmailMessage('Hello', 'Body goes here', 'from@example.com',
|
||||
@ -549,7 +557,7 @@ manually open the connection, you can control when it is closed. For example::
|
||||
email3 = mail.EmailMessage('Hello', 'Body goes here', 'from@example.com',
|
||||
['to3@example.com'])
|
||||
|
||||
# Send the two emails in a single call -
|
||||
# Send the two e-mails in a single call -
|
||||
connection.send_messages([email2, email3])
|
||||
# The connection was already open so send_messages() doesn't close it.
|
||||
# We need to manually close the connection.
|
||||
@ -566,10 +574,10 @@ people under the right conditions, and that those e-mails will contain the
|
||||
correct content.
|
||||
|
||||
The easiest way to test your project's use of e-mail is to use the ``console``
|
||||
email backend. This backend redirects all email to stdout, allowing you to
|
||||
e-mail backend. This backend redirects all e-mail to stdout, allowing you to
|
||||
inspect the content of mail.
|
||||
|
||||
The ``file`` email backend can also be useful during development -- this backend
|
||||
The ``file`` e-mail backend can also be useful during development -- this backend
|
||||
dumps the contents of every SMTP connection to a file that can be inspected
|
||||
at your leisure.
|
||||
|
||||
@ -596,7 +604,7 @@ SMTPConnection
|
||||
|
||||
.. deprecated:: 1.2
|
||||
|
||||
The ``SMTPConnection`` class has been deprecated in favor of the generic email
|
||||
The ``SMTPConnection`` class has been deprecated in favor of the generic e-mail
|
||||
backend API.
|
||||
|
||||
For backwards compatibility ``SMTPConnection`` is still available in
|
||||
|
@ -40,7 +40,8 @@ algorithm the system follows to determine which Python code to execute:
|
||||
|
||||
1. Django determines the root URLconf module to use. Ordinarily,
|
||||
this is the value of the ``ROOT_URLCONF`` setting, but if the incoming
|
||||
``HttpRequest`` object has an attribute called ``urlconf``, its value
|
||||
``HttpRequest`` object has an attribute called ``urlconf`` (set by
|
||||
middleware :ref:`request processing <request-middleware>`), its value
|
||||
will be used in place of the ``ROOT_URLCONF`` setting.
|
||||
|
||||
2. Django loads that Python module and looks for the variable
|
||||
|
@ -980,19 +980,21 @@ subclass::
|
||||
|
||||
def setUp(self):
|
||||
# Test definitions as before.
|
||||
call_setup_methods()
|
||||
|
||||
def testFluffyAnimals(self):
|
||||
# A test that uses the fixtures.
|
||||
call_some_test_code()
|
||||
|
||||
Here's specifically what will happen:
|
||||
|
||||
* At the start of each test case, before ``setUp()`` is run, Django will
|
||||
flush the database, returning the database to the state it was in
|
||||
directly after ``syncdb`` was called.
|
||||
directly after :djadmin:`syncdb` was called.
|
||||
|
||||
* Then, all the named fixtures are installed. In this example, Django will
|
||||
install any JSON fixture named ``mammals``, followed by any fixture named
|
||||
``birds``. See the :djadmin:`loaddata documentation<loaddata>` for more
|
||||
``birds``. See the :djadmin:`loaddata` documentation for more
|
||||
details on defining and installing fixtures.
|
||||
|
||||
This flush/load procedure is repeated for each test in the test case, so you
|
||||
@ -1028,6 +1030,7 @@ For example::
|
||||
|
||||
def testIndexPageView(self):
|
||||
# Here you'd test your view using ``Client``.
|
||||
call_some_test_code()
|
||||
|
||||
This test case will use the contents of ``myapp.test_urls`` as the
|
||||
URLconf for the duration of the test case.
|
||||
|
@ -1,4 +1,13 @@
|
||||
"""
|
||||
from django.db import models
|
||||
|
||||
class Advertisment(models.Model):
|
||||
customer = models.CharField(max_length=100)
|
||||
publications = models.ManyToManyField("model_package.Publication", null=True, blank=True)
|
||||
|
||||
class Meta:
|
||||
app_label = 'model_package'
|
||||
|
||||
__test__ = {'API_TESTS': """
|
||||
>>> from models.publication import Publication
|
||||
>>> from models.article import Article
|
||||
>>> from django.contrib.auth.views import Site
|
||||
@ -19,7 +28,6 @@
|
||||
>>> a.save()
|
||||
>>> a.publications.add(p)
|
||||
>>> a.sites.add(current_site)
|
||||
>>> a.save()
|
||||
|
||||
>>> a = Article.objects.get(id=1)
|
||||
>>> a
|
||||
@ -29,6 +37,19 @@
|
||||
>>> a.sites.count()
|
||||
1
|
||||
|
||||
"""
|
||||
# Regression for #12248 - Models can exist in the test package, too
|
||||
|
||||
>>> ad = Advertisment(customer="Lawrence Journal-World")
|
||||
>>> ad.save()
|
||||
>>> ad.publications.add(p)
|
||||
|
||||
>>> ad = Advertisment.objects.get(id=1)
|
||||
>>> ad
|
||||
<Advertisment: Advertisment object>
|
||||
|
||||
>>> ad.publications.count()
|
||||
1
|
||||
|
||||
"""}
|
||||
|
||||
|
||||
|
@ -26,6 +26,21 @@ class TwoAlbumFKAndAnE(models.Model):
|
||||
e = models.CharField(max_length=1)
|
||||
|
||||
|
||||
class Author(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
|
||||
|
||||
class Book(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
subtitle = models.CharField(max_length=100)
|
||||
price = models.FloatField()
|
||||
authors = models.ManyToManyField(Author, through='AuthorsBooks')
|
||||
|
||||
|
||||
class AuthorsBooks(models.Model):
|
||||
author = models.ForeignKey(Author)
|
||||
book = models.ForeignKey(Book)
|
||||
|
||||
|
||||
__test__ = {'API_TESTS':"""
|
||||
|
||||
@ -95,4 +110,48 @@ Exception: <class 'regressiontests.admin_validation.models.TwoAlbumFKAndAnE'> ha
|
||||
|
||||
>>> validate_inline(TwoAlbumFKAndAnEInline, None, Album)
|
||||
|
||||
# Regression test for #12203/#12237 - Fail more gracefully when a M2M field that
|
||||
# specifies the 'through' option is included in the 'fields' or the 'fieldsets'
|
||||
# ModelAdmin options.
|
||||
|
||||
>>> class BookAdmin(admin.ModelAdmin):
|
||||
... fields = ['authors']
|
||||
|
||||
>>> validate(BookAdmin, Book)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ImproperlyConfigured: 'BookAdmin.fields' can't include the ManyToManyField field 'authors' because 'authors' manually specifies a 'through' model.
|
||||
|
||||
>>> class FieldsetBookAdmin(admin.ModelAdmin):
|
||||
... fieldsets = (
|
||||
... ('Header 1', {'fields': ('name',)}),
|
||||
... ('Header 2', {'fields': ('authors',)}),
|
||||
... )
|
||||
|
||||
>>> validate(FieldsetBookAdmin, Book)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ImproperlyConfigured: 'FieldsetBookAdmin.fieldsets[1][1]['fields']' can't include the ManyToManyField field 'authors' because 'authors' manually specifies a 'through' model.
|
||||
|
||||
>>> class NestedFieldsetAdmin(admin.ModelAdmin):
|
||||
... fieldsets = (
|
||||
... ('Main', {'fields': ('price', ('name', 'subtitle'))}),
|
||||
... )
|
||||
|
||||
>>> validate(NestedFieldsetAdmin, Book)
|
||||
|
||||
# Regression test for #12209 -- If the explicitly provided through model
|
||||
# is specified as a string, the admin should still be able use
|
||||
# Model.m2m_field.through
|
||||
|
||||
>>> class AuthorsInline(admin.TabularInline):
|
||||
... model = Book.authors.through
|
||||
|
||||
>>> class BookAdmin(admin.ModelAdmin):
|
||||
... inlines = [AuthorsInline]
|
||||
|
||||
# If the through model is still a string (and hasn't been resolved to a model)
|
||||
# the validation will fail.
|
||||
>>> validate(BookAdmin, Book)
|
||||
|
||||
"""}
|
||||
|
1
tests/regressiontests/bash_completion/__init__.py
Normal file
1
tests/regressiontests/bash_completion/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
|
@ -0,0 +1 @@
|
||||
|
@ -0,0 +1 @@
|
||||
|
@ -0,0 +1,14 @@
|
||||
import sys, os
|
||||
from optparse import OptionParser, make_option
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
option_list = BaseCommand.option_list + (
|
||||
make_option("--list", action="store_true", dest="list",
|
||||
help="Print all options"),
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
pass
|
1
tests/regressiontests/bash_completion/models.py
Normal file
1
tests/regressiontests/bash_completion/models.py
Normal file
@ -0,0 +1 @@
|
||||
|
87
tests/regressiontests/bash_completion/tests.py
Normal file
87
tests/regressiontests/bash_completion/tests.py
Normal file
@ -0,0 +1,87 @@
|
||||
"""
|
||||
A series of tests to establish that the command-line bash completion works.
|
||||
"""
|
||||
import os
|
||||
import unittest
|
||||
import sys
|
||||
import StringIO
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.management import ManagementUtility
|
||||
|
||||
class BashCompletionTests(unittest.TestCase):
|
||||
"""
|
||||
Testing the Python level bash completion code.
|
||||
This requires setting up the environment as if we got passed data
|
||||
from bash.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.old_DJANGO_AUTO_COMPLETE = os.environ.get('DJANGO_AUTO_COMPLETE')
|
||||
os.environ['DJANGO_AUTO_COMPLETE'] = '1'
|
||||
self.output = StringIO.StringIO()
|
||||
self.old_stdout = sys.stdout
|
||||
sys.stdout = self.output
|
||||
|
||||
def tearDown(self):
|
||||
sys.stdout = self.old_stdout
|
||||
if self.old_DJANGO_AUTO_COMPLETE:
|
||||
os.environ['DJANGO_AUTO_COMPLETE'] = self.old_DJANGO_AUTO_COMPLETE
|
||||
else:
|
||||
del os.environ['DJANGO_AUTO_COMPLETE']
|
||||
|
||||
def _user_input(self, input_str):
|
||||
os.environ['COMP_WORDS'] = input_str
|
||||
os.environ['COMP_CWORD'] = str(len(input_str.split()) - 1)
|
||||
sys.argv = input_str.split(' ')
|
||||
|
||||
def _run_autocomplete(self):
|
||||
util = ManagementUtility(argv=sys.argv)
|
||||
try:
|
||||
util.autocomplete()
|
||||
except SystemExit:
|
||||
pass
|
||||
return self.output.getvalue().strip().split('\n')
|
||||
|
||||
def test_django_admin_py(self):
|
||||
"django_admin.py will autocomplete option flags"
|
||||
self._user_input('django-admin.py sqlall --v')
|
||||
output = self._run_autocomplete()
|
||||
self.assertEqual(output, ['--verbosity='])
|
||||
|
||||
def test_manage_py(self):
|
||||
"manage.py will autocomplete option flags"
|
||||
self._user_input('manage.py sqlall --v')
|
||||
output = self._run_autocomplete()
|
||||
self.assertEqual(output, ['--verbosity='])
|
||||
|
||||
def test_custom_command(self):
|
||||
"A custom command can autocomplete option flags"
|
||||
self._user_input('django-admin.py test_command --l')
|
||||
output = self._run_autocomplete()
|
||||
self.assertEqual(output, ['--list'])
|
||||
|
||||
def test_subcommands(self):
|
||||
"Subcommands can be autocompleted"
|
||||
self._user_input('django-admin.py sql')
|
||||
output = self._run_autocomplete()
|
||||
self.assertEqual(output, ['sql sqlall sqlclear sqlcustom sqlflush sqlindexes sqlinitialdata sqlreset sqlsequencereset'])
|
||||
|
||||
def test_help(self):
|
||||
"No errors, just an empty list if there are no autocomplete options"
|
||||
self._user_input('django-admin.py help --')
|
||||
output = self._run_autocomplete()
|
||||
self.assertEqual(output, [''])
|
||||
|
||||
def test_runfcgi(self):
|
||||
"Command arguments will be autocompleted"
|
||||
self._user_input('django-admin.py runfcgi h')
|
||||
output = self._run_autocomplete()
|
||||
self.assertEqual(output, ['host='])
|
||||
|
||||
def test_app_completion(self):
|
||||
"Application names will be autocompleted for an AppCommand"
|
||||
self._user_input('django-admin.py sqlall a')
|
||||
output = self._run_autocomplete()
|
||||
app_labels = [name.split('.')[-1] for name in settings.INSTALLED_APPS]
|
||||
self.assertEqual(output, sorted(label for label in app_labels if label.startswith('a')))
|
@ -115,6 +115,23 @@ u'c1'
|
||||
>>> results[0].second_child.name
|
||||
u'c2'
|
||||
|
||||
# Test for #12163 - Pickling error saving session with unsaved model instances.
|
||||
>>> from django.contrib.sessions.backends.db import SessionStore
|
||||
>>> SESSION_KEY = '2b1189a188b44ad18c35e1baac6ceead'
|
||||
>>> item = Item()
|
||||
>>> item._deferred
|
||||
False
|
||||
>>> s = SessionStore(SESSION_KEY)
|
||||
>>> s.clear()
|
||||
>>> s['item'] = item
|
||||
>>> s.save()
|
||||
>>> s = SessionStore(SESSION_KEY)
|
||||
>>> s.modified = True
|
||||
>>> s.save()
|
||||
>>> i2 = s['item']
|
||||
>>> i2._deferred # Item must still be non-deferred
|
||||
False
|
||||
|
||||
# Finally, we need to flush the app cache for the defer module.
|
||||
# Using only/defer creates some artifical entries in the app cache
|
||||
# that messes up later tests. Purge all entries, just to be sure.
|
||||
|
@ -1,4 +1,29 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
##########
|
||||
# Fields #
|
||||
##########
|
||||
|
||||
Each Field class does some sort of validation. Each Field has a clean() method,
|
||||
which either raises django.forms.ValidationError or returns the "clean"
|
||||
data -- usually a Unicode object, but, in some rare cases, a list.
|
||||
|
||||
Each Field's __init__() takes at least these parameters:
|
||||
required -- Boolean that specifies whether the field is required.
|
||||
True by default.
|
||||
widget -- A Widget class, or instance of a Widget class, that should be
|
||||
used for this Field when displaying it. Each Field has a default
|
||||
Widget that it'll use if you don't specify this. In most cases,
|
||||
the default widget is TextInput.
|
||||
label -- A verbose name for this field, for use in displaying this field in
|
||||
a form. By default, Django will use a "pretty" version of the form
|
||||
field name, if the Field is part of a Form.
|
||||
initial -- A value to use in this Field's initial display. This value is
|
||||
*not* used as a fallback if data isn't given.
|
||||
|
||||
Other than that, the Field subclasses have class-specific options for
|
||||
__init__(). For example, CharField has a max_length option.
|
||||
"""
|
||||
import datetime
|
||||
import time
|
||||
import re
|
||||
|
@ -102,4 +102,34 @@ u'<ul class="errorlist"><li>(Hidden field data) This field is required.</li></ul
|
||||
>>> f.as_table()
|
||||
u'<tr><td colspan="2"><ul class="errorlist"><li>(Hidden field data) This field is required.</li></ul><input type="hidden" name="data" id="id_data" /></td></tr>'
|
||||
|
||||
###################################################
|
||||
# Tests for XSS vulnerabilities in error messages #
|
||||
###################################################
|
||||
|
||||
# The forms layer doesn't escape input values directly because error messages
|
||||
# might be presented in non-HTML contexts. Instead, the message is just marked
|
||||
# for escaping by the template engine. So we'll need to construct a little
|
||||
# silly template to trigger the escaping.
|
||||
|
||||
>>> from django.template import Template, Context
|
||||
>>> t = Template('{{ form.errors }}')
|
||||
|
||||
>>> class SomeForm(Form):
|
||||
... field = ChoiceField(choices=[('one', 'One')])
|
||||
>>> f = SomeForm({'field': '<script>'})
|
||||
>>> t.render(Context({'form': f}))
|
||||
u'<ul class="errorlist"><li>field<ul class="errorlist"><li>Select a valid choice. <script> is not one of the available choices.</li></ul></li></ul>'
|
||||
|
||||
>>> class SomeForm(Form):
|
||||
... field = MultipleChoiceField(choices=[('one', 'One')])
|
||||
>>> f = SomeForm({'field': ['<script>']})
|
||||
>>> t.render(Context({'form': f}))
|
||||
u'<ul class="errorlist"><li>field<ul class="errorlist"><li>Select a valid choice. <script> is not one of the available choices.</li></ul></li></ul>'
|
||||
|
||||
>>> from regressiontests.forms.models import ChoiceModel
|
||||
>>> class SomeForm(Form):
|
||||
... field = ModelMultipleChoiceField(ChoiceModel.objects.all())
|
||||
>>> f = SomeForm({'field': ['<script>']})
|
||||
>>> t.render(Context({'form': f}))
|
||||
u'<ul class="errorlist"><li>field<ul class="errorlist"><li>"<script>" is not a valid value for a primary key.</li></ul></li></ul>'
|
||||
"""
|
||||
|
@ -167,4 +167,10 @@ Traceback (most recent call last):
|
||||
...
|
||||
ValueError: Cannot assign "<Child: Child object>": "Child.parent" must be a "Parent" instance.
|
||||
|
||||
# Regression for #12190 -- Should be able to instantiate a FK
|
||||
# outside of a model, and interrogate its related field.
|
||||
>>> cat = models.ForeignKey(Category)
|
||||
>>> cat.rel.get_related_field().name
|
||||
'id'
|
||||
|
||||
"""}
|
||||
|
@ -755,10 +755,20 @@ Bug #6180, #6203 -- dates with limits and/or counts
|
||||
>>> Item.objects.dates('created', 'day')[0]
|
||||
datetime.datetime(2007, 12, 19, 0, 0)
|
||||
|
||||
Bug #7087 -- dates with extra select columns
|
||||
Bug #7087/#12242 -- dates with extra select columns
|
||||
>>> Item.objects.dates('created', 'day').extra(select={'a': 1})
|
||||
[datetime.datetime(2007, 12, 19, 0, 0), datetime.datetime(2007, 12, 20, 0, 0)]
|
||||
|
||||
>>> Item.objects.extra(select={'a': 1}).dates('created', 'day')
|
||||
[datetime.datetime(2007, 12, 19, 0, 0), datetime.datetime(2007, 12, 20, 0, 0)]
|
||||
|
||||
>>> name="one"
|
||||
>>> Item.objects.dates('created', 'day').extra(where=['name=%s'], params=[name])
|
||||
[datetime.datetime(2007, 12, 19, 0, 0)]
|
||||
|
||||
>>> Item.objects.extra(where=['name=%s'], params=[name]).dates('created', 'day')
|
||||
[datetime.datetime(2007, 12, 19, 0, 0)]
|
||||
|
||||
Bug #7155 -- nullable dates
|
||||
>>> Item.objects.dates('modified', 'day')
|
||||
[datetime.datetime(2007, 12, 19, 0, 0)]
|
||||
|
7
tests/regressiontests/urlpatterns_reverse/middleware.py
Normal file
7
tests/regressiontests/urlpatterns_reverse/middleware.py
Normal file
@ -0,0 +1,7 @@
|
||||
from django.core.urlresolvers import set_urlconf
|
||||
|
||||
import urlconf_inner
|
||||
|
||||
class ChangeURLconfMiddleware(object):
|
||||
def process_request(self, request):
|
||||
request.urlconf = urlconf_inner.__name__
|
@ -16,11 +16,16 @@ ImproperlyConfigured: The included urlconf regressiontests.urlpatterns_reverse.n
|
||||
|
||||
import unittest
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse, resolve, NoReverseMatch, Resolver404
|
||||
from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect
|
||||
from django.shortcuts import redirect
|
||||
from django.test import TestCase
|
||||
|
||||
import urlconf_outer
|
||||
import urlconf_inner
|
||||
import middleware
|
||||
|
||||
test_data = (
|
||||
('places', '/places/3/', [3], {}),
|
||||
('places', '/places/3/', ['3'], {}),
|
||||
@ -239,3 +244,35 @@ class NamespaceTests(TestCase):
|
||||
self.assertEquals('/other1/inner/37/42/', reverse('nodefault:urlobject-view', args=[37,42], current_app='other-ns1'))
|
||||
self.assertEquals('/other1/inner/42/37/', reverse('nodefault:urlobject-view', kwargs={'arg1':42, 'arg2':37}, current_app='other-ns1'))
|
||||
|
||||
|
||||
class RequestURLconfTests(TestCase):
|
||||
def setUp(self):
|
||||
self.root_urlconf = settings.ROOT_URLCONF
|
||||
self.middleware_classes = settings.MIDDLEWARE_CLASSES
|
||||
settings.ROOT_URLCONF = urlconf_outer.__name__
|
||||
|
||||
def tearDown(self):
|
||||
settings.ROOT_URLCONF = self.root_urlconf
|
||||
settings.MIDDLEWARE_CLASSES = self.middleware_classes
|
||||
|
||||
def test_urlconf(self):
|
||||
response = self.client.get('/test/me/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.content, 'outer:/test/me/,'
|
||||
'inner:/inner_urlconf/second_test/')
|
||||
response = self.client.get('/inner_urlconf/second_test/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
response = self.client.get('/second_test/')
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
def test_urlconf_overridden(self):
|
||||
settings.MIDDLEWARE_CLASSES += (
|
||||
'%s.ChangeURLconfMiddleware' % middleware.__name__,
|
||||
)
|
||||
response = self.client.get('/test/me/')
|
||||
self.assertEqual(response.status_code, 404)
|
||||
response = self.client.get('/inner_urlconf/second_test/')
|
||||
self.assertEqual(response.status_code, 404)
|
||||
response = self.client.get('/second_test/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.content, 'outer:,inner:/second_test/')
|
||||
|
12
tests/regressiontests/urlpatterns_reverse/urlconf_inner.py
Normal file
12
tests/regressiontests/urlpatterns_reverse/urlconf_inner.py
Normal file
@ -0,0 +1,12 @@
|
||||
from django.conf.urls.defaults import *
|
||||
from django.template import Template, Context
|
||||
from django.http import HttpResponse
|
||||
|
||||
def inner_view(request):
|
||||
content = Template('{% url outer as outer_url %}outer:{{ outer_url }},'
|
||||
'{% url inner as inner_url %}inner:{{ inner_url }}').render(Context())
|
||||
return HttpResponse(content)
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^second_test/$', inner_view, name='inner'),
|
||||
)
|
@ -0,0 +1,9 @@
|
||||
from django.conf.urls.defaults import *
|
||||
|
||||
import urlconf_inner
|
||||
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^test/me/$', urlconf_inner.inner_view, name='outer'),
|
||||
url(r'^inner_urlconf/', include(urlconf_inner.__name__))
|
||||
)
|
Loading…
x
Reference in New Issue
Block a user