From 82b25800cdb8be78e8cfc95cca41e01a418ecc9c Mon Sep 17 00:00:00 2001 From: Zain Memon Date: Fri, 5 Jun 2009 20:58:34 +0000 Subject: [PATCH] [soc2009/admin-ui] Merging trunk r10924 into my branch. git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/admin-ui@10925 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- .../conf/locale/es_AR/LC_MESSAGES/django.mo | Bin 63078 -> 66901 bytes .../conf/locale/es_AR/LC_MESSAGES/django.po | 774 +++++++++++------- django/contrib/gis/db/models/sql/where.py | 4 +- django/contrib/gis/maps/google/gmap.py | 6 +- django/contrib/gis/maps/google/overlays.py | 8 + django/contrib/gis/tests/relatedapp/models.py | 10 + django/contrib/gis/tests/relatedapp/tests.py | 34 +- django/contrib/gis/utils/ogrinspect.py | 56 +- django/db/models/fields/files.py | 133 ++- django/db/models/query.py | 3 +- django/db/models/query_utils.py | 15 +- django/db/models/sql/query.py | 8 +- django/forms/models.py | 2 +- tests/modeltests/model_forms/models.py | 21 +- tests/regressiontests/admin_scripts/tests.py | 2 +- .../delete_regress/__init__.py | 1 + .../regressiontests/delete_regress/models.py | 61 ++ tests/regressiontests/file_storage/models.py | 93 --- tests/regressiontests/file_storage/tests.py | 12 +- tests/regressiontests/model_fields/4x8.png | Bin 0 -> 87 bytes tests/regressiontests/model_fields/8x4.png | Bin 0 -> 87 bytes .../model_fields/imagefield.py | 420 ++++++++++ tests/regressiontests/model_fields/models.py | 107 ++- tests/regressiontests/model_fields/tests.py | 16 +- 24 files changed, 1308 insertions(+), 478 deletions(-) create mode 100644 tests/regressiontests/delete_regress/__init__.py create mode 100644 tests/regressiontests/delete_regress/models.py create mode 100644 tests/regressiontests/model_fields/4x8.png create mode 100644 tests/regressiontests/model_fields/8x4.png create mode 100644 tests/regressiontests/model_fields/imagefield.py diff --git a/django/conf/locale/es_AR/LC_MESSAGES/django.mo b/django/conf/locale/es_AR/LC_MESSAGES/django.mo index e4474b0eb889473d83447037a88aafba80869524..57349e8d9b96cf816704fbfe4527834d7b7b94ba 100644 GIT binary patch delta 23996 zcmb8%cYIXU+V}sRLg>AO&VYm-I!FhB(0lJNBol}v8At)CqXMD`DvVMUsR{}Tj0&PC zh%}{$1uRsRqDZqG3o6g&JNt@q&hL5t`0dxdc&~M>zII7a?{n#gsL$p{2hJ9Ye#YTC z7v(tRu|y@ushP`h3bawwaklnzoTnokXB(EJ+-ZR0)WH!r3?IW*_$`*h$^#vz8Meke zIN8bpEKYeRmck>*d;#YhGSvwDjHs@ne;)_uWS&t?01Zo49tsFhr zT|imXc=uo=_QV+M6E@&DgUM*3QOF@U$yfmI4>xd}DVU$~EYyzXqb3Ss5!{M}a4&Lj z&PV1+)VQZn{k}zwe+~2EE!8u>bDPW%jN%Vfj6iirMy=G3nrJeX!g;72uR=}m8tO#e zMlIkNs^6#PR~G*oXA-}H^D%iS=U+UW??L=#X8 zn1sb}Di+2CsPUe~@CjiF%6qN;Bx)mgw zq9*Qzx>bX$oQP^S0kyyki$8#B_aIipg;*6g59jK1H3E#MW@K)cO@ zs0mJ>+Fd}MB*s#%glcy$Y5{>RWVG^Ls0jw5?&WAR12xfH)IEI$ zHSi{^joVNscLuZYJJfn`+L>_hnmHpg~j9mj{$P#ZXJUP3MO2h{iDFAV5Nizm8A7Kddi zH^Oq*3$>H`Q45@mTF^|?0CQ0%v(n-(quPZ~6YnzjSo{F$#Ezo+olfNZHNg)A^jiIf zl`&TmuNYQG4U}MZN8RJTsD+F+$6*u7e(Z!Buqpn4UGN@`L-ABhzz{aT-+i2aT{5+k z-JK7^)|9hRw<3h?@I%y!3ygEWkma!^HyN8+Q z5UW#u6ZtXV1TK(iN2W!pyRzx1j!&Ql-iS5uebm!^4e!H8#+KoEe zi&zbR!$w$XqWcW>M?LIAF%M3_?&0^J42yL3V1BGS$$d{-pc=MCEv$>hM`B*eV=)F( zF*j!7K%9=zcmj*!NmTnQs15yyI;lJvj+3tU{~j{=a4~AY=P(M_qZY8y;@eOI?ZjO8 z9%|x47XQe~Uz%T|`hRcbpHTDMwsK4+=dTG1kx|2vsB%Tr4&tm_%WQy!i8sSY?1Jjo z9kq}Fs1qE4g>XC;!zostk6QS0)JC4qyjjTayYmi{}LcO zKuxe4HQs*I1jkVu`pi6!YIhaY?xz5m&Sd_=E|`$*c6- z_+wUHWaTBOeyc40BI);C+ChjJ&>c;irSfgiPjS*Tky8@13yQt$sVGHUods^cr>8(4tyJE#SHh??*W zYJe-Kfp1v(XH@&!sB!X4bK4g|UJR!ks^62S`Xw0t{eKl1b=-_v;hU(22hC%s4xgE4 zP!nH7Jxt%D7IqueFJ`*iz96bx5_QYUTe*tW*PPDz>r2;wfPRQHK`o@eH5h{$cs%OJ zGp#(soQGQQV$?XRQ4_95wSO5K;cFO=S5WOr&2Zb5ox%B+CQzAxcGvO%{7nh<=?tpm&)$ZePnLjR&G|^?$3U8o(82y1d z;+O~B4tY@vER8ye%BXe?QSmmYcxTiJ48gKE9xLK(EQxDT3)qRV%;6j)qkD7?byT-e z&q9%h+;}b2(RM;j*vA}!TKIU>2~4!|EY!(7g7Fx{I=C0L(Ceu7zha;bnOqM$PCM*~ zgK;i;@C@oG|HkeZH=AAHB>w1wzu-LVG{^m=^cXg!oOiA}Q9IPF?P~TxZDcU&q`h

KCCFv<&rZ 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 delta 21070 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 BCA$Cs diff --git a/django/conf/locale/es_AR/LC_MESSAGES/django.po b/django/conf/locale/es_AR/LC_MESSAGES/django.po index f92735adc6..d6726ab57b 100644 --- a/django/conf/locale/es_AR/LC_MESSAGES/django.po +++ b/django/conf/locale/es_AR/LC_MESSAGES/django.po @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2008-11-13 18:20-0200\n" -"PO-Revision-Date: 2008-11-13 18:27-0200\n" +"POT-Creation-Date: 2009-05-28 12:30-0300\n" +"PO-Revision-Date: 2009-05-05 20:35-0300\n" "Last-Translator: Ramiro Morales \n" "Language-Team: Django-I18N \n" "MIME-Version: 1.0\n" @@ -100,121 +100,139 @@ msgid "Hebrew" msgstr "hebreo" #: conf/global_settings.py:65 +msgid "Hindi" +msgstr "Hindi" + +#: conf/global_settings.py:66 msgid "Croatian" msgstr "croata" -#: conf/global_settings.py:66 +#: conf/global_settings.py:67 msgid "Icelandic" msgstr "islandés" -#: conf/global_settings.py:67 +#: conf/global_settings.py:68 msgid "Italian" msgstr "italiano" -#: conf/global_settings.py:68 +#: conf/global_settings.py:69 msgid "Japanese" msgstr "japonés" -#: conf/global_settings.py:69 +#: conf/global_settings.py:70 msgid "Georgian" msgstr "georgiano" -#: conf/global_settings.py:70 +#: conf/global_settings.py:71 msgid "Korean" msgstr "koreano" -#: conf/global_settings.py:71 +#: conf/global_settings.py:72 msgid "Khmer" msgstr "jémer" -#: conf/global_settings.py:72 +#: conf/global_settings.py:73 msgid "Kannada" msgstr "canarés" -#: conf/global_settings.py:73 +#: conf/global_settings.py:74 msgid "Latvian" msgstr "letón" -#: conf/global_settings.py:74 +#: conf/global_settings.py:75 msgid "Lithuanian" msgstr "lituano" -#: conf/global_settings.py:75 +#: conf/global_settings.py:76 msgid "Macedonian" msgstr "macedonio" -#: conf/global_settings.py:76 +#: conf/global_settings.py:77 msgid "Dutch" msgstr "holandés" -#: conf/global_settings.py:77 +#: conf/global_settings.py:78 msgid "Norwegian" msgstr "noruego" -#: conf/global_settings.py:78 +#: conf/global_settings.py:79 msgid "Polish" msgstr "polaco" -#: conf/global_settings.py:79 +#: conf/global_settings.py:80 msgid "Portuguese" msgstr "portugués" -#: conf/global_settings.py:80 +#: conf/global_settings.py:81 msgid "Brazilian Portuguese" msgstr "portugués de Brasil" -#: conf/global_settings.py:81 +#: conf/global_settings.py:82 msgid "Romanian" msgstr "rumano" -#: conf/global_settings.py:82 +#: conf/global_settings.py:83 msgid "Russian" msgstr "ruso" -#: conf/global_settings.py:83 +#: conf/global_settings.py:84 msgid "Slovak" msgstr "eslovaco" -#: conf/global_settings.py:84 +#: conf/global_settings.py:85 msgid "Slovenian" msgstr "esloveno" -#: conf/global_settings.py:85 +#: conf/global_settings.py:86 msgid "Serbian" msgstr "serbio" -#: conf/global_settings.py:86 +#: conf/global_settings.py:87 msgid "Swedish" msgstr "sueco" -#: conf/global_settings.py:87 +#: conf/global_settings.py:88 msgid "Tamil" msgstr "tamil" -#: conf/global_settings.py:88 +#: conf/global_settings.py:89 msgid "Telugu" msgstr "telugu" -#: conf/global_settings.py:89 +#: conf/global_settings.py:90 msgid "Thai" msgstr "tailandés" -#: conf/global_settings.py:90 +#: conf/global_settings.py:91 msgid "Turkish" msgstr "turco" -#: conf/global_settings.py:91 +#: conf/global_settings.py:92 msgid "Ukrainian" msgstr "ucraniano" -#: conf/global_settings.py:92 +#: conf/global_settings.py:93 msgid "Simplified Chinese" msgstr "chino simplificado" -#: conf/global_settings.py:93 +#: conf/global_settings.py:94 msgid "Traditional Chinese" msgstr "chino tradicional" +#: contrib/admin/actions.py:60 +#, python-format +msgid "Successfully deleted %(count)d %(items)s." +msgstr "Se eliminaron con éxito %(count)d %(items)s." + +#: contrib/admin/actions.py:67 contrib/admin/options.py:1025 +msgid "Are you sure?" +msgstr "¿Está seguro?" + +#: contrib/admin/actions.py:85 +#, python-format +msgid "Delete selected %(verbose_name_plural)s" +msgstr "Eliminar %(verbose_name_plural)s seleccionados/as" + #: contrib/admin/filterspecs.py:44 #, python-format msgid "" @@ -224,43 +242,47 @@ msgstr "" "

Por %s:

\n" "
    \n" -#: contrib/admin/filterspecs.py:74 contrib/admin/filterspecs.py:91 -#: contrib/admin/filterspecs.py:146 contrib/admin/filterspecs.py:172 +#: contrib/admin/filterspecs.py:75 contrib/admin/filterspecs.py:92 +#: contrib/admin/filterspecs.py:147 contrib/admin/filterspecs.py:173 msgid "All" msgstr "Todos/as" -#: contrib/admin/filterspecs.py:112 +#: contrib/admin/filterspecs.py:113 msgid "Any date" msgstr "Cualquier fecha" -#: contrib/admin/filterspecs.py:113 +#: contrib/admin/filterspecs.py:114 msgid "Today" msgstr "Hoy" -#: contrib/admin/filterspecs.py:116 +#: contrib/admin/filterspecs.py:117 msgid "Past 7 days" msgstr "Últimos 7 días" -#: contrib/admin/filterspecs.py:118 +#: contrib/admin/filterspecs.py:119 msgid "This month" msgstr "Este mes" -#: contrib/admin/filterspecs.py:120 +#: contrib/admin/filterspecs.py:121 msgid "This year" msgstr "Este año" -#: contrib/admin/filterspecs.py:146 forms/widgets.py:390 +#: contrib/admin/filterspecs.py:147 forms/widgets.py:434 msgid "Yes" msgstr "Sí" -#: contrib/admin/filterspecs.py:146 forms/widgets.py:390 +#: contrib/admin/filterspecs.py:147 forms/widgets.py:434 msgid "No" msgstr "No" -#: contrib/admin/filterspecs.py:153 forms/widgets.py:390 +#: contrib/admin/filterspecs.py:154 forms/widgets.py:434 msgid "Unknown" msgstr "Desconocido" +#: contrib/admin/helpers.py:14 +msgid "Action:" +msgstr "Acción:" + #: contrib/admin/models.py:19 msgid "action time" msgstr "hora de la acción" @@ -289,60 +311,61 @@ msgstr "entrada de registro" msgid "log entries" msgstr "entradas de registro" -#: contrib/admin/options.py:60 contrib/admin/options.py:121 +#: contrib/admin/options.py:133 contrib/admin/options.py:147 msgid "None" msgstr "Ninguno" -#: contrib/admin/options.py:338 +#: contrib/admin/options.py:519 #, python-format msgid "Changed %s." msgstr "Modifica %s." -#: contrib/admin/options.py:338 contrib/admin/options.py:348 -#: contrib/comments/templates/comments/preview.html:15 forms/models.py:288 +#: contrib/admin/options.py:519 contrib/admin/options.py:529 +#: contrib/comments/templates/comments/preview.html:16 forms/models.py:388 +#: forms/models.py:587 msgid "and" msgstr "y" -#: contrib/admin/options.py:343 +#: contrib/admin/options.py:524 #, python-format msgid "Added %(name)s \"%(object)s\"." msgstr "Se agregó %(name)s \"%(object)s\"." -#: contrib/admin/options.py:347 +#: contrib/admin/options.py:528 #, python-format msgid "Changed %(list)s for %(name)s \"%(object)s\"." msgstr "Se modificaron %(list)s en %(name)s \"%(object)s\"." -#: contrib/admin/options.py:352 +#: contrib/admin/options.py:533 #, python-format msgid "Deleted %(name)s \"%(object)s\"." msgstr "Se eliminó %(name)s \"%(object)s\"." -#: contrib/admin/options.py:356 +#: contrib/admin/options.py:537 msgid "No fields changed." msgstr "No ha modificado ningún campo." -#: contrib/admin/options.py:417 contrib/auth/admin.py:51 +#: contrib/admin/options.py:598 contrib/auth/admin.py:67 #, python-format msgid "The %(name)s \"%(obj)s\" was added successfully." msgstr "Se agregó con éxito %(name)s \"%(obj)s\"." -#: contrib/admin/options.py:421 contrib/admin/options.py:454 -#: contrib/auth/admin.py:59 +#: contrib/admin/options.py:602 contrib/admin/options.py:635 +#: contrib/auth/admin.py:75 msgid "You may edit it again below." msgstr "Puede modificarlo/a nuevamente abajo." -#: contrib/admin/options.py:431 contrib/admin/options.py:464 +#: contrib/admin/options.py:612 contrib/admin/options.py:645 #, python-format msgid "You may add another %s below." msgstr "Puede agregar otro/a %s abajo." -#: contrib/admin/options.py:452 +#: contrib/admin/options.py:633 #, python-format msgid "The %(name)s \"%(obj)s\" was changed successfully." msgstr "Se modificó con éxito %(name)s \"%(obj)s\"." -#: contrib/admin/options.py:460 +#: contrib/admin/options.py:641 #, python-format msgid "" "The %(name)s \"%(obj)s\" was added successfully. You may edit it again below." @@ -350,40 +373,43 @@ msgstr "" "Se agregó con éxito %(name)s \"%(obj)s\". Puede modificarlo/a nuevamente " "abajo." -#: contrib/admin/options.py:528 +#: contrib/admin/options.py:772 #, python-format msgid "Add %s" msgstr "Agregar %s" -#: contrib/admin/options.py:559 contrib/admin/options.py:673 +#: contrib/admin/options.py:803 contrib/admin/options.py:1003 #, python-format msgid "%(name)s object with primary key %(key)r does not exist." msgstr "No existe un objeto %(name)s con una clave primaria %(key)r." -#: contrib/admin/options.py:606 +#: contrib/admin/options.py:860 #, python-format msgid "Change %s" msgstr "Modificar %s" -#: contrib/admin/options.py:638 +#: contrib/admin/options.py:904 msgid "Database error" msgstr "Error de base de datos" -#: contrib/admin/options.py:688 +#: contrib/admin/options.py:940 +#, python-format +msgid "%(count)s %(name)s was changed successfully." +msgid_plural "%(count)s %(name)s were changed successfully." +msgstr[0] "Se ha modificado con éxito %(count)s %(name)s." +msgstr[1] "Se han modificado con éxito %(count)s %(name)s." + +#: contrib/admin/options.py:1018 #, python-format msgid "The %(name)s \"%(obj)s\" was deleted successfully." msgstr "Se eliminó con éxito %(name)s \"%(obj)s\"." -#: contrib/admin/options.py:695 -msgid "Are you sure?" -msgstr "¿Está seguro?" - -#: contrib/admin/options.py:724 +#: contrib/admin/options.py:1054 #, python-format msgid "Change history: %s" msgstr "Historia de modificaciones: %s" -#: contrib/admin/sites.py:16 contrib/admin/views/decorators.py:14 +#: contrib/admin/sites.py:20 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-" @@ -392,11 +418,11 @@ msgstr "" "Por favor introduzca un nombre de usuario y una contraseña correctos. Note " "que ambos campos son sensibles a mayúsculas/minúsculas." -#: contrib/admin/sites.py:226 contrib/admin/views/decorators.py:40 +#: contrib/admin/sites.py:278 contrib/admin/views/decorators.py:40 msgid "Please log in again, because your session has expired." msgstr "Por favor, identifíquese de nuevo porque su sesión ha caducado." -#: contrib/admin/sites.py:233 contrib/admin/views/decorators.py:47 +#: contrib/admin/sites.py:285 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." @@ -404,64 +430,64 @@ msgstr "" "Parece que su navegador no está configurado para aceptar cookies. Por favor " "actívelas, recargue esta página, e inténtelo de nuevo." -#: contrib/admin/sites.py:249 contrib/admin/sites.py:255 +#: contrib/admin/sites.py:301 contrib/admin/sites.py:307 #: 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:252 contrib/admin/views/decorators.py:62 +#: contrib/admin/sites.py:304 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 electrónico no es su nombre de usuario. Intente " "nuevamente usando '%s'." -#: contrib/admin/sites.py:312 +#: contrib/admin/sites.py:360 msgid "Site administration" msgstr "Administración de sitio" -#: contrib/admin/sites.py:325 contrib/admin/templates/admin/login.html:26 +#: contrib/admin/sites.py:373 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 "Identificarse" -#: contrib/admin/sites.py:372 +#: contrib/admin/sites.py:417 #, python-format msgid "%s administration" msgstr "Administración de %s" -#: contrib/admin/util.py:138 +#: contrib/admin/util.py:168 #, python-format msgid "One or more %(fieldname)s in %(name)s: %(obj)s" msgstr "Uno o más %(fieldname)s en %(name)s: %(obj)s" -#: contrib/admin/util.py:143 +#: contrib/admin/util.py:173 #, python-format msgid "One or more %(fieldname)s in %(name)s:" msgstr "Uno o más %(fieldname)s en %(name)s:" -#: contrib/admin/widgets.py:70 +#: contrib/admin/widgets.py:71 msgid "Date:" msgstr "Fecha:" -#: contrib/admin/widgets.py:70 +#: contrib/admin/widgets.py:71 msgid "Time:" msgstr "Hora:" -#: contrib/admin/widgets.py:94 +#: contrib/admin/widgets.py:95 msgid "Currently:" msgstr "Actualmente" -#: contrib/admin/widgets.py:94 +#: contrib/admin/widgets.py:95 msgid "Change:" msgstr "Modificar:" -#: contrib/admin/widgets.py:123 +#: contrib/admin/widgets.py:124 msgid "Lookup" msgstr "Buscar" -#: contrib/admin/widgets.py:230 +#: contrib/admin/widgets.py:236 msgid "Add Another" msgstr "Agregar otro/a" @@ -478,8 +504,9 @@ msgstr "Lo sentimos, pero no se encuentra la página solicitada." #: contrib/admin/templates/admin/app_index.html:8 #: contrib/admin/templates/admin/base.html:31 #: contrib/admin/templates/admin/change_form.html:17 -#: contrib/admin/templates/admin/change_list.html:8 +#: contrib/admin/templates/admin/change_list.html:25 #: contrib/admin/templates/admin/delete_confirmation.html:6 +#: contrib/admin/templates/admin/delete_selected_confirmation.html:6 #: contrib/admin/templates/admin/invalid_setup.html:4 #: contrib/admin/templates/admin/object_history.html:6 #: contrib/admin/templates/admin/auth/user/change_password.html:10 @@ -515,6 +542,14 @@ msgstr "" "mediante correo electrónico y debería ser solucionado en breve. Gracias por " "su paciencia." +#: contrib/admin/templates/admin/actions.html:4 +msgid "Run the selected action" +msgstr "Ejecutar la acción seleccionada" + +#: contrib/admin/templates/admin/actions.html:4 +msgid "Go" +msgstr "Ejecutar" + #: contrib/admin/templates/admin/app_index.html:10 #: contrib/admin/templates/admin/index.html:19 #, python-format @@ -533,8 +568,8 @@ msgid "Documentation" msgstr "Documentación" #: contrib/admin/templates/admin/base.html:26 -#: contrib/admin/templates/admin/auth/user/change_password.html:13 -#: contrib/admin/templates/admin/auth/user/change_password.html:46 +#: contrib/admin/templates/admin/auth/user/change_password.html:14 +#: contrib/admin/templates/admin/auth/user/change_password.html:47 #: contrib/admin/templates/registration/password_change_done.html:3 #: contrib/admin/templates/registration/password_change_form.html:3 msgid "Change password" @@ -571,23 +606,24 @@ msgid "View on site" msgstr "Ver en el sitio" #: contrib/admin/templates/admin/change_form.html:38 -#: contrib/admin/templates/admin/auth/user/change_password.html:22 +#: contrib/admin/templates/admin/change_list.html:54 +#: contrib/admin/templates/admin/auth/user/change_password.html:23 msgid "Please correct the error below." msgid_plural "Please correct the errors below." msgstr[0] "Por favor, corrija el siguiente error." msgstr[1] "Por favor, corrija los siguientes errores." -#: contrib/admin/templates/admin/change_list.html:16 +#: contrib/admin/templates/admin/change_list.html:46 #, python-format msgid "Add %(name)s" msgstr "Agregar %(name)s" -#: contrib/admin/templates/admin/change_list.html:26 +#: contrib/admin/templates/admin/change_list.html:65 msgid "Filter" msgstr "Filtrar" #: contrib/admin/templates/admin/delete_confirmation.html:10 -#: contrib/admin/templates/admin/submit_line.html:4 forms/formsets.py:246 +#: contrib/admin/templates/admin/submit_line.html:4 forms/formsets.py:275 msgid "Delete" msgstr "Eliminar" @@ -612,9 +648,34 @@ msgstr "" "\"? Se eliminarán los siguientes objetos relacionados:" #: contrib/admin/templates/admin/delete_confirmation.html:28 +#: contrib/admin/templates/admin/delete_selected_confirmation.html:33 msgid "Yes, I'm sure" msgstr "Sí, estoy seguro" +#: contrib/admin/templates/admin/delete_selected_confirmation.html:9 +msgid "Delete multiple objects" +msgstr "Eliminar múltiples objetos" + +#: contrib/admin/templates/admin/delete_selected_confirmation.html:15 +#, python-format +msgid "" +"Deleting the %(object_name)s would result in deleting related objects, but " +"your account doesn't have permission to delete the following types of " +"objects:" +msgstr "" +"El eliminar %(object_name)s provocaría la eliminación de objetos " +"relacionados, pero su cuenta no tiene permiso para eliminar los siguientes " +"tipos de objetos:" + +#: contrib/admin/templates/admin/delete_selected_confirmation.html:22 +#, python-format +msgid "" +"Are you sure you want to delete the selected %(object_name)s objects? All of " +"the following objects and it's related items will be deleted:" +msgstr "" +"¿Está seguro de que quiere eliminar los objetos %(object_name)s? Se " +"eliminarán todos los siguientes objetos relacionados:" + #: contrib/admin/templates/admin/filter.html:2 #, python-format msgid " By %(filter_title)s " @@ -645,6 +706,10 @@ msgstr "Mis acciones" msgid "None available" msgstr "Ninguna disponible" +#: contrib/admin/templates/admin/index.html:72 +msgid "Unknown content" +msgstr "COntenido desconocido" + #: contrib/admin/templates/admin/invalid_setup.html:7 msgid "" "Something's wrong with your database installation. Make sure the appropriate " @@ -677,7 +742,7 @@ msgid "Action" msgstr "Acción" #: contrib/admin/templates/admin/object_history.html:30 -#: utils/translation/trans_real.py:404 +#: utils/translation/trans_real.py:400 msgid "DATETIME_FORMAT" msgstr "j N Y P" @@ -694,7 +759,7 @@ msgid "Show all" msgstr "Mostrar todos/as" #: contrib/admin/templates/admin/search_form.html:8 -msgid "Go" +msgid "Search" msgstr "Buscar" #: contrib/admin/templates/admin/search_form.html:10 @@ -739,24 +804,24 @@ msgid "Username" msgstr "Nombre de usuario:" #: contrib/admin/templates/admin/auth/user/add_form.html:20 -#: contrib/admin/templates/admin/auth/user/change_password.html:33 -#: contrib/auth/forms.py:17 contrib/auth/forms.py:60 contrib/auth/forms.py:184 +#: contrib/admin/templates/admin/auth/user/change_password.html:34 +#: contrib/auth/forms.py:17 contrib/auth/forms.py:60 contrib/auth/forms.py:185 msgid "Password" msgstr "Contraseña:" #: contrib/admin/templates/admin/auth/user/add_form.html:26 -#: contrib/admin/templates/admin/auth/user/change_password.html:39 -#: contrib/auth/forms.py:185 +#: contrib/admin/templates/admin/auth/user/change_password.html:40 +#: contrib/auth/forms.py:186 msgid "Password (again)" msgstr "Contraseña (de nuevo)" #: contrib/admin/templates/admin/auth/user/add_form.html:27 -#: contrib/admin/templates/admin/auth/user/change_password.html:40 +#: contrib/admin/templates/admin/auth/user/change_password.html:41 msgid "Enter the same password as above, for verification." msgstr "" "Para verificación, introduzca la misma contraseña que introdujo arriba." -#: contrib/admin/templates/admin/auth/user/change_password.html:26 +#: contrib/admin/templates/admin/auth/user/change_password.html:27 #, python-format msgid "Enter a new password for the user %(username)s." msgstr "" @@ -925,166 +990,166 @@ msgstr "Dirección de correo electrónico:" msgid "Reset my password" msgstr "Recuperar mi contraseña" -#: contrib/admin/templatetags/admin_list.py:284 +#: contrib/admin/templatetags/admin_list.py:299 msgid "All dates" msgstr "Todas las fechas" -#: contrib/admin/views/main.py:69 +#: contrib/admin/views/main.py:70 #, python-format msgid "Select %s" msgstr "Seleccione %s" -#: contrib/admin/views/main.py:69 +#: contrib/admin/views/main.py:70 #, python-format msgid "Select %s to change" msgstr "Seleccione %s a modificar" -#: contrib/admin/views/template.py:36 contrib/sites/models.py:38 +#: contrib/admin/views/template.py:37 contrib/sites/models.py:38 msgid "site" msgstr "sitio" -#: contrib/admin/views/template.py:38 +#: contrib/admin/views/template.py:39 msgid "template" msgstr "plantilla" -#: contrib/admindocs/views.py:57 contrib/admindocs/views.py:59 -#: contrib/admindocs/views.py:61 +#: contrib/admindocs/views.py:58 contrib/admindocs/views.py:60 +#: contrib/admindocs/views.py:62 msgid "tag:" msgstr "etiqueta:" -#: contrib/admindocs/views.py:90 contrib/admindocs/views.py:92 -#: contrib/admindocs/views.py:94 +#: contrib/admindocs/views.py:91 contrib/admindocs/views.py:93 +#: contrib/admindocs/views.py:95 msgid "filter:" msgstr "filtrar:" -#: contrib/admindocs/views.py:154 contrib/admindocs/views.py:156 -#: contrib/admindocs/views.py:158 +#: contrib/admindocs/views.py:155 contrib/admindocs/views.py:157 +#: contrib/admindocs/views.py:159 msgid "view:" msgstr "ver:" -#: contrib/admindocs/views.py:186 +#: contrib/admindocs/views.py:187 #, python-format msgid "App %r not found" msgstr "Aplicación %r no encontrada" -#: contrib/admindocs/views.py:193 +#: contrib/admindocs/views.py:194 #, python-format msgid "Model %(model_name)r not found in app %(app_label)r" msgstr "Modelo %(model_name)r no encontrado en aplicación %(app_label)r" -#: contrib/admindocs/views.py:205 +#: contrib/admindocs/views.py:206 #, python-format msgid "the related `%(app_label)s.%(data_type)s` object" msgstr "el objeto `%(app_label)s.%(data_type)s` relacionado" -#: contrib/admindocs/views.py:205 contrib/admindocs/views.py:227 -#: contrib/admindocs/views.py:241 contrib/admindocs/views.py:246 +#: contrib/admindocs/views.py:206 contrib/admindocs/views.py:228 +#: contrib/admindocs/views.py:242 contrib/admindocs/views.py:247 msgid "model:" msgstr "modelo:" -#: contrib/admindocs/views.py:236 +#: contrib/admindocs/views.py:237 #, python-format msgid "related `%(app_label)s.%(object_name)s` objects" msgstr "objetos `%(app_label)s.%(object_name)s` relacionados" -#: contrib/admindocs/views.py:241 +#: contrib/admindocs/views.py:242 #, python-format msgid "all %s" msgstr "todos los %s" -#: contrib/admindocs/views.py:246 +#: contrib/admindocs/views.py:247 #, python-format msgid "number of %s" msgstr "número de %s" -#: contrib/admindocs/views.py:251 +#: contrib/admindocs/views.py:252 #, python-format msgid "Fields on %s objects" msgstr "Campos en objetos %s" -#: contrib/admindocs/views.py:314 contrib/admindocs/views.py:325 -#: contrib/admindocs/views.py:327 contrib/admindocs/views.py:333 -#: contrib/admindocs/views.py:334 contrib/admindocs/views.py:336 +#: contrib/admindocs/views.py:315 contrib/admindocs/views.py:326 +#: contrib/admindocs/views.py:328 contrib/admindocs/views.py:334 +#: contrib/admindocs/views.py:335 contrib/admindocs/views.py:337 msgid "Integer" msgstr "Entero" -#: contrib/admindocs/views.py:315 +#: contrib/admindocs/views.py:316 msgid "Boolean (Either True or False)" msgstr "Booleano (Verdadero o Falso)" -#: contrib/admindocs/views.py:316 contrib/admindocs/views.py:335 +#: contrib/admindocs/views.py:317 contrib/admindocs/views.py:336 #, python-format msgid "String (up to %(max_length)s)" msgstr "Cadena (máximo %(max_length)s)" -#: contrib/admindocs/views.py:317 +#: contrib/admindocs/views.py:318 msgid "Comma-separated integers" msgstr "Enteros separados por comas" -#: contrib/admindocs/views.py:318 +#: contrib/admindocs/views.py:319 msgid "Date (without time)" msgstr "Fecha (sin hora)" -#: contrib/admindocs/views.py:319 +#: contrib/admindocs/views.py:320 msgid "Date (with time)" msgstr "Fecha (con hora)" -#: contrib/admindocs/views.py:320 +#: contrib/admindocs/views.py:321 msgid "Decimal number" msgstr "Número decimal" -#: contrib/admindocs/views.py:321 +#: contrib/admindocs/views.py:322 msgid "E-mail address" msgstr "Dirección de correo electrónico" -#: contrib/admindocs/views.py:322 contrib/admindocs/views.py:323 -#: contrib/admindocs/views.py:326 +#: contrib/admindocs/views.py:323 contrib/admindocs/views.py:324 +#: contrib/admindocs/views.py:327 msgid "File path" msgstr "Ruta de archivo" -#: contrib/admindocs/views.py:324 +#: contrib/admindocs/views.py:325 msgid "Floating point number" msgstr "Número de punto flotante" -#: contrib/admindocs/views.py:328 contrib/comments/models.py:58 +#: contrib/admindocs/views.py:329 contrib/comments/models.py:60 msgid "IP address" msgstr "Dirección IP" -#: contrib/admindocs/views.py:330 +#: contrib/admindocs/views.py:331 msgid "Boolean (Either True, False or None)" msgstr "Booleano (Verdadero, Falso o Nulo)" -#: contrib/admindocs/views.py:331 +#: contrib/admindocs/views.py:332 msgid "Relation to parent model" msgstr "Relación con el modelo padre" -#: contrib/admindocs/views.py:332 +#: contrib/admindocs/views.py:333 msgid "Phone number" msgstr "Número de teléfono" -#: contrib/admindocs/views.py:337 +#: contrib/admindocs/views.py:338 msgid "Text" msgstr "Texto" -#: contrib/admindocs/views.py:338 +#: contrib/admindocs/views.py:339 msgid "Time" msgstr "Hora" -#: contrib/admindocs/views.py:339 contrib/comments/forms.py:21 +#: contrib/admindocs/views.py:340 contrib/comments/forms.py:95 #: contrib/comments/templates/comments/moderation_queue.html:37 #: contrib/flatpages/admin.py:8 contrib/flatpages/models.py:7 msgid "URL" msgstr "URL" -#: contrib/admindocs/views.py:340 +#: contrib/admindocs/views.py:341 msgid "U.S. state (two uppercase letters)" msgstr "Estado de los EE.UU. (dos letras mayúsculas)" -#: contrib/admindocs/views.py:341 +#: contrib/admindocs/views.py:342 msgid "XML text" msgstr "Texto XML" -#: contrib/admindocs/views.py:367 +#: contrib/admindocs/views.py:368 #, python-format msgid "%s does not appear to be a urlpattern object" msgstr "%s no parece ser un objeto urlpattern" @@ -1174,21 +1239,21 @@ msgstr "Fechas importantes" msgid "Groups" msgstr "Grupos" -#: contrib/auth/admin.py:64 +#: contrib/auth/admin.py:80 msgid "Add user" msgstr "Agregar usuario" -#: contrib/auth/admin.py:90 +#: contrib/auth/admin.py:106 msgid "Password changed successfully." msgstr "Cambio de contraseña exitoso" -#: contrib/auth/admin.py:96 +#: contrib/auth/admin.py:112 #, python-format msgid "Change password: %s" msgstr "Cambiar contraseña: %s" #: contrib/auth/forms.py:15 contrib/auth/forms.py:48 -#: contrib/auth/models.py:127 +#: contrib/auth/models.py:128 msgid "" "Required. 30 characters or fewer. Alphanumeric characters only (letters, " "digits and underscores)." @@ -1208,8 +1273,8 @@ msgstr "Confirmación de contraseña" msgid "A user with that username already exists." msgstr "Ya existe un usuario con ese nombre." -#: contrib/auth/forms.py:36 contrib/auth/forms.py:154 -#: contrib/auth/forms.py:196 +#: contrib/auth/forms.py:36 contrib/auth/forms.py:155 +#: contrib/auth/forms.py:197 msgid "The two password fields didn't match." msgstr "Los dos campos de contraseñas no coinciden entre si." @@ -1237,24 +1302,24 @@ msgstr "" "Esa dirección de e-mail no está asociada a ninguna cuenta de usuario. ¿Está " "seguro de que ya se ha registrado?" -#: contrib/auth/forms.py:134 +#: contrib/auth/forms.py:135 #, python-format msgid "Password reset on %s" msgstr "Reinicialización de contraseña en %s" -#: contrib/auth/forms.py:142 +#: contrib/auth/forms.py:143 msgid "New password" msgstr "Contraseña nueva" -#: contrib/auth/forms.py:143 +#: contrib/auth/forms.py:144 msgid "New password confirmation" msgstr "Confirmación de contraseña nueva" -#: contrib/auth/forms.py:168 +#: contrib/auth/forms.py:169 msgid "Old password" msgstr "Contraseña antigua" -#: contrib/auth/forms.py:176 +#: contrib/auth/forms.py:177 msgid "Your old password was entered incorrectly. Please enter it again." msgstr "" "La antigua contraseña introducida es incorrecta. Por favor introdúzcala " @@ -1280,31 +1345,31 @@ msgstr "permisos" msgid "group" msgstr "grupo" -#: contrib/auth/models.py:91 contrib/auth/models.py:137 +#: contrib/auth/models.py:91 contrib/auth/models.py:138 msgid "groups" msgstr "grupos" -#: contrib/auth/models.py:127 +#: contrib/auth/models.py:128 msgid "username" msgstr "nombre de usuario" -#: contrib/auth/models.py:128 +#: contrib/auth/models.py:129 msgid "first name" msgstr "nombre" -#: contrib/auth/models.py:129 +#: contrib/auth/models.py:130 msgid "last name" msgstr "apellido" -#: contrib/auth/models.py:130 +#: contrib/auth/models.py:131 msgid "e-mail address" msgstr "dirección de correo electrónico" -#: contrib/auth/models.py:131 +#: contrib/auth/models.py:132 msgid "password" msgstr "contraseña" -#: contrib/auth/models.py:131 +#: contrib/auth/models.py:132 msgid "" "Use '[algo]$[salt]$[hexdigest]' or use the change " "password form." @@ -1312,19 +1377,19 @@ msgstr "" "Use '[algo]$[salt]$[hexdigest]' o use el formulario de " "cambio de contraseña." -#: contrib/auth/models.py:132 +#: contrib/auth/models.py:133 msgid "staff status" msgstr "es staff" -#: contrib/auth/models.py:132 +#: contrib/auth/models.py:133 msgid "Designates whether the user can log into this admin site." msgstr "Indica si el usuario puede ingresar a este sitio de administración." -#: contrib/auth/models.py:133 +#: contrib/auth/models.py:134 msgid "active" msgstr "activo" -#: contrib/auth/models.py:133 +#: contrib/auth/models.py:134 msgid "" "Designates whether this user should be treated as active. Unselect this " "instead of deleting accounts." @@ -1332,11 +1397,11 @@ msgstr "" "Indica si el usuario debe ser tratado como un usuario activo. Desactive este " "campo en lugar de eliminar usuarios." -#: contrib/auth/models.py:134 +#: contrib/auth/models.py:135 msgid "superuser status" msgstr "es superusuario" -#: contrib/auth/models.py:134 +#: contrib/auth/models.py:135 msgid "" "Designates that this user has all permissions without explicitly assigning " "them." @@ -1344,15 +1409,15 @@ msgstr "" "Indica que este usuario posee todos los permisos sin que sea necesario " "asignarle los mismos en forma explícita." -#: contrib/auth/models.py:135 +#: contrib/auth/models.py:136 msgid "last login" msgstr "último ingreso" -#: contrib/auth/models.py:136 +#: contrib/auth/models.py:137 msgid "date joined" msgstr "fecha de creación" -#: contrib/auth/models.py:138 +#: contrib/auth/models.py:139 msgid "" "In addition to the permissions manually assigned, this user will also get " "all permissions granted to each group he/she is in." @@ -1360,27 +1425,28 @@ msgstr "" "Además de los permisos asignados manualmente, este usuario también poseerá " "todos los permisos de los grupos a los que pertenezca." -#: contrib/auth/models.py:139 +#: contrib/auth/models.py:140 msgid "user permissions" msgstr "permisos de usuario" -#: contrib/auth/models.py:143 +#: contrib/auth/models.py:144 contrib/comments/models.py:50 +#: contrib/comments/models.py:168 msgid "user" msgstr "usuario" -#: contrib/auth/models.py:144 +#: contrib/auth/models.py:145 msgid "users" msgstr "usuarios" -#: contrib/auth/models.py:300 +#: contrib/auth/models.py:301 msgid "message" msgstr "mensaje" -#: contrib/auth/views.py:50 +#: contrib/auth/views.py:56 msgid "Logged out" msgstr "Sesión cerrada" -#: contrib/auth/management/commands/createsuperuser.py:23 forms/fields.py:428 +#: contrib/auth/management/commands/createsuperuser.py:23 forms/fields.py:429 msgid "Enter a valid e-mail address." msgstr "Introduzca una dirección de correo electrónico válida" @@ -1392,71 +1458,86 @@ msgstr "Contenido" msgid "Metadata" msgstr "Metadatos" -#: contrib/comments/forms.py:19 +#: contrib/comments/feeds.py:13 +#, python-format +msgid "%(site_name)s comments" +msgstr "comentarios en %(site_name)s" + +#: contrib/comments/feeds.py:23 +#, python-format +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" -#: contrib/comments/forms.py:20 +#: contrib/comments/forms.py:94 msgid "Email address" msgstr "Dirección de correo electrónico" -#: contrib/comments/forms.py:22 +#: contrib/comments/forms.py:96 #: contrib/comments/templates/comments/moderation_queue.html:35 msgid "Comment" msgstr "Comentario" -#: contrib/comments/forms.py:25 -msgid "" -"If you enter anything in this field your comment will be treated as spam" -msgstr "Si introduce algo en este campo su comentario será tratado como spam" - -#: contrib/comments/forms.py:125 +#: contrib/comments/forms.py:173 #, python-format msgid "Watch your mouth! The word %s is not allowed here." msgid_plural "Watch your mouth! The words %s are not allowed here." msgstr[0] "¡Controla tu lenguaje! Aquí no admitimos la palabra %s." msgstr[1] "¡Controla tu lenguaje! Aquí no admitimos las palabras %s." -#: contrib/comments/models.py:23 +#: contrib/comments/forms.py:180 +msgid "" +"If you enter anything in this field your comment will be treated as spam" +msgstr "Si introduce algo en este campo su comentario será tratado como spam" + +#: contrib/comments/models.py:22 contrib/contenttypes/models.py:74 +msgid "content type" +msgstr "tipo de contenido" + +#: contrib/comments/models.py:24 msgid "object ID" msgstr "ID de objeto" -#: contrib/comments/models.py:50 +#: contrib/comments/models.py:52 msgid "user's name" msgstr "nombre de usuario" -#: contrib/comments/models.py:51 +#: contrib/comments/models.py:53 msgid "user's email address" msgstr "dirección de correo electrónico del usuario" -#: contrib/comments/models.py:52 +#: contrib/comments/models.py:54 msgid "user's URL" msgstr "URL del usuario" -#: contrib/comments/models.py:54 +#: contrib/comments/models.py:56 contrib/comments/models.py:76 +#: contrib/comments/models.py:169 msgid "comment" msgstr "comentario" -#: contrib/comments/models.py:57 +#: contrib/comments/models.py:59 msgid "date/time submitted" msgstr "fecha/hora de envío" -#: contrib/comments/models.py:59 +#: contrib/comments/models.py:61 msgid "is public" msgstr "es público" -#: contrib/comments/models.py:60 +#: contrib/comments/models.py:62 msgid "" "Uncheck this box to make the comment effectively disappear from the site." msgstr "" "deseleccione esta caja para lograr que el comentario desaparezca del sitio." -#: contrib/comments/models.py:62 +#: contrib/comments/models.py:64 msgid "is removed" msgstr "se ha eliminado" -#: contrib/comments/models.py:63 +#: contrib/comments/models.py:65 msgid "" "Check this box if the comment is inappropriate. A \"This comment has been " "removed\" message will be displayed instead." @@ -1464,7 +1545,11 @@ msgstr "" "Marque esta caja si el comentario es inapropiado. En su lugar se mostrará un " "mensaje \"Este comentario ha sido eliminado\"." -#: contrib/comments/models.py:115 +#: contrib/comments/models.py:77 +msgid "comments" +msgstr "comentarios" + +#: contrib/comments/models.py:119 msgid "" "This comment was posted by an authenticated user and thus the name is read-" "only." @@ -1472,7 +1557,7 @@ msgstr "" "Este comentario ha sido enviado por un usuario identificado, por lo tanto el " "nombre no puede modificarse.%(text)s" -#: contrib/comments/models.py:124 +#: contrib/comments/models.py:128 msgid "" "This comment was posted by an authenticated user and thus the email is read-" "only." @@ -1480,7 +1565,7 @@ msgstr "" "Este comentario ha sido enviado por un usuario identificado, por lo tanto la " "dirección de correo electrónico no puede modificarse.%(text)s" -#: contrib/comments/models.py:149 +#: contrib/comments/models.py:153 #, python-format msgid "" "Posted by %(user)s at %(date)s\n" @@ -1495,6 +1580,22 @@ msgstr "" "\n" "http://%(domain)s%(url)s" +#: contrib/comments/models.py:170 +msgid "flag" +msgstr "marca" + +#: contrib/comments/models.py:171 +msgid "date" +msgstr "fecha" + +#: contrib/comments/models.py:181 +msgid "comment flag" +msgstr "marca de comentario" + +#: contrib/comments/models.py:182 +msgid "comment flags" +msgstr "marcas de comentario" + #: contrib/comments/templates/comments/approve.html:4 msgid "Approve a comment" msgstr "Aprobar un comentario" @@ -1554,13 +1655,13 @@ msgstr "Marcar" msgid "Thanks for flagging" msgstr "¡Gracias por marcar!" -#: contrib/comments/templates/comments/form.html:16 -#: contrib/comments/templates/comments/preview.html:31 +#: contrib/comments/templates/comments/form.html:17 +#: contrib/comments/templates/comments/preview.html:32 msgid "Post" msgstr "Remitir" -#: contrib/comments/templates/comments/form.html:17 -#: contrib/comments/templates/comments/preview.html:32 +#: contrib/comments/templates/comments/form.html:18 +#: contrib/comments/templates/comments/preview.html:33 msgid "Preview" msgstr "Previsualización" @@ -1606,33 +1707,29 @@ msgid "Thank you for your comment" msgstr "Gracias por dejar su comentario" #: contrib/comments/templates/comments/preview.html:4 -#: contrib/comments/templates/comments/preview.html:12 +#: contrib/comments/templates/comments/preview.html:13 msgid "Preview your comment" msgstr "Ver una copia previa de su comentario" -#: contrib/comments/templates/comments/preview.html:10 +#: contrib/comments/templates/comments/preview.html:11 msgid "Please correct the error below" msgid_plural "Please correct the errors below" msgstr[0] "Por favor, corrija el siguiente error." msgstr[1] "Por favor, corrija los siguientes errores." -#: contrib/comments/templates/comments/preview.html:15 +#: contrib/comments/templates/comments/preview.html:16 msgid "Post your comment" msgstr "Enviar su comentario" -#: contrib/comments/templates/comments/preview.html:15 +#: contrib/comments/templates/comments/preview.html:16 msgid "or make changes" msgstr "o realice modificaciones" -#: contrib/contenttypes/models.py:67 +#: contrib/contenttypes/models.py:70 msgid "python model class name" msgstr "nombre de la clase python del modelo" -#: contrib/contenttypes/models.py:71 -msgid "content type" -msgstr "tipo de contenido" - -#: contrib/contenttypes/models.py:72 +#: contrib/contenttypes/models.py:75 msgid "content types" msgstr "tipos de contenido" @@ -1703,18 +1800,26 @@ msgstr "" "Lamentablemente su formulario ha caducado. Por favor continúe rellenando el " "formulario en esta página." -#: contrib/gis/forms/fields.py:14 +#: contrib/gis/forms/fields.py:17 msgid "No geometry value provided." msgstr "No se ha proporcionado un valor de geometría." -#: contrib/gis/forms/fields.py:15 +#: contrib/gis/forms/fields.py:18 msgid "Invalid geometry value." msgstr "Valor de geometría no válido." -#: contrib/gis/forms/fields.py:16 +#: contrib/gis/forms/fields.py:19 msgid "Invalid geometry type." msgstr "Tipo de geometría no válido." +#: contrib/gis/forms/fields.py:20 +msgid "" +"An error occurred when transforming the geometry to the SRID of the geometry " +"form field." +msgstr "" +"Ha ocurrido un error mientras se transformaba la geometría al SRID del campo " +"de formulario de la misma." + #: contrib/humanize/templatetags/humanize.py:19 msgid "th" msgstr "th" @@ -2037,6 +2142,83 @@ msgstr "Introduzca un RUT chileno válido. EL formato es XX.XXX.XXX-X." msgid "The Chilean RUT is not valid." msgstr "El RUT chileno no es válido." +#: contrib/localflavor/cz/cz_regions.py:8 +msgid "Prague" +msgstr "Praga" + +#: contrib/localflavor/cz/cz_regions.py:9 +msgid "Central Bohemian Region" +msgstr "región Bohemia Central" + +#: contrib/localflavor/cz/cz_regions.py:10 +msgid "South Bohemian Region" +msgstr "región Bohemian Meridional" + +#: contrib/localflavor/cz/cz_regions.py:11 +msgid "Pilsen Region" +msgstr "región Pilsen" + +#: contrib/localflavor/cz/cz_regions.py:12 +msgid "Carlsbad Region" +msgstr "región Karlovy Vary" + +#: contrib/localflavor/cz/cz_regions.py:13 +msgid "Usti Region" +msgstr "región Ústí nad Labem" + +#: contrib/localflavor/cz/cz_regions.py:14 +msgid "Liberec Region" +msgstr "región Liberec" + +#: contrib/localflavor/cz/cz_regions.py:15 +msgid "Hradec Region" +msgstr "región Hradec Králové" + +#: contrib/localflavor/cz/cz_regions.py:16 +msgid "Pardubice Region" +msgstr "región Pardubice" + +#: contrib/localflavor/cz/cz_regions.py:17 +msgid "Vysocina Region" +msgstr "región Vysočina" + +#: contrib/localflavor/cz/cz_regions.py:18 +msgid "South Moravian Region" +msgstr "región Moravia Meridional" + +#: contrib/localflavor/cz/cz_regions.py:19 +msgid "Olomouc Region" +msgstr "región Olomouc" + +#: contrib/localflavor/cz/cz_regions.py:20 +msgid "Zlin Region" +msgstr "región Zlín" + +#: contrib/localflavor/cz/cz_regions.py:21 +msgid "Moravian-Silesian Region" +msgstr "región Moravia-Silesia" + +#: contrib/localflavor/cz/forms.py:27 contrib/localflavor/sk/forms.py:30 +msgid "Enter a postal code in the format XXXXX or XXX XX." +msgstr "Introduzca un código postal en formato XXXXX o XXX XX." + +#: contrib/localflavor/cz/forms.py:47 +msgid "Enter a birth number in the format XXXXXX/XXXX or XXXXXXXXXX." +msgstr "" +"Introduzca un número de nacimiento en formato XXXXXX/XXXX o XXXXXXXXXX." + +#: contrib/localflavor/cz/forms.py:48 +msgid "Invalid optional parameter Gender, valid values are 'f' and 'm'" +msgstr "Parámetro opcional Género inválido, valores válidos son 'f' y 'm'" + +#: contrib/localflavor/cz/forms.py:49 +msgid "Enter a valid birth number." +msgstr "Introduzca un número ide nacimiento válido." + +#: contrib/localflavor/cz/forms.py:106 +msgid "Enter a valid IC number." +msgstr "Introduzca un número IC válido." + #: contrib/localflavor/de/de_states.py:5 msgid "Baden-Wuerttemberg" msgstr "Baden-Wuerttemberg" @@ -2868,19 +3050,19 @@ msgstr "" msgid "Wrong checksum for the Tax Number (NIP)." msgstr "Código de verificación de Número Impostitivo (NIP) inválido." -#: contrib/localflavor/pl/forms.py:111 -msgid "National Business Register Number (REGON) consists of 7 or 9 digits." +#: contrib/localflavor/pl/forms.py:109 +msgid "National Business Register Number (REGON) consists of 9 or 14 digits." msgstr "" -"Los Números Nacionales de Registro de Negocios (REGON) constan de 7 o 9 " +"Los Números Nacionales de Registro de Negocios (REGON) constan de 9 o 14 " "dígitos." -#: contrib/localflavor/pl/forms.py:112 +#: contrib/localflavor/pl/forms.py:110 msgid "Wrong checksum for the National Business Register Number (REGON)." msgstr "" "Código de verificación de Número Nacional de Registro de negocios (REGON) " "inválido." -#: contrib/localflavor/pl/forms.py:155 +#: contrib/localflavor/pl/forms.py:148 msgid "Enter a postal code in the format XX-XXX." msgstr "Introduzca un código postal en formato XX-XXX." @@ -2968,10 +3150,6 @@ msgstr "Los números telefónicos deben respetar el formato XXXX-XXXXXX." msgid "Enter a valid postal code in the format XXXXXX" msgstr "Introduzca un código postal válido en formato XXXXXX" -#: contrib/localflavor/sk/forms.py:30 -msgid "Enter a postal code in the format XXXXX or XXX XX." -msgstr "Introduzca un código postal en formato XXXXX o XXX XX." - #: contrib/localflavor/sk/sk_districts.py:8 msgid "Banska Bystrica" msgstr "Banska Bystrica" @@ -3290,35 +3468,35 @@ msgstr "Zilina" #: contrib/localflavor/sk/sk_regions.py:8 msgid "Banska Bystrica region" -msgstr "region Banska Bystrica" +msgstr "región Banska Bystrica" #: contrib/localflavor/sk/sk_regions.py:9 msgid "Bratislava region" -msgstr "region Bratislava" +msgstr "región Bratislava" #: contrib/localflavor/sk/sk_regions.py:10 msgid "Kosice region" -msgstr "region Kosice" +msgstr "región Kosice" #: contrib/localflavor/sk/sk_regions.py:11 msgid "Nitra region" -msgstr "region Nitra" +msgstr "región Nitra" #: contrib/localflavor/sk/sk_regions.py:12 msgid "Presov region" -msgstr "region Presov" +msgstr "región Presov" #: contrib/localflavor/sk/sk_regions.py:13 msgid "Trencin region" -msgstr "region Trencin" +msgstr "región Trencin" #: contrib/localflavor/sk/sk_regions.py:14 msgid "Trnava region" -msgstr "region Trnava" +msgstr "región Trnava" #: contrib/localflavor/sk/sk_regions.py:15 msgid "Zilina region" -msgstr "region Zilina" +msgstr "región Zilina" #: contrib/localflavor/uk/forms.py:21 msgid "Enter a valid postcode." @@ -3720,57 +3898,61 @@ msgstr "nombre para visualizar" msgid "sites" msgstr "sitios" -#: db/models/fields/__init__.py:348 db/models/fields/__init__.py:683 +#: db/models/fields/__init__.py:356 db/models/fields/__init__.py:710 msgid "This value must be an integer." msgstr "Este valor debe ser un número entero." -#: db/models/fields/__init__.py:379 +#: db/models/fields/__init__.py:388 msgid "This value must be either True or False." msgstr "Este valor debe ser True o False." -#: db/models/fields/__init__.py:412 +#: db/models/fields/__init__.py:427 msgid "This field cannot be null." msgstr "Este campo no puede ser nulo." -#: db/models/fields/__init__.py:428 +#: db/models/fields/__init__.py:443 msgid "Enter only digits separated by commas." msgstr "Introduzca sólo dígitos separados por comas." -#: db/models/fields/__init__.py:459 +#: db/models/fields/__init__.py:474 msgid "Enter a valid date in YYYY-MM-DD format." msgstr "Introduzca una fecha válida en formato AAAA-MM-DD." -#: db/models/fields/__init__.py:468 +#: db/models/fields/__init__.py:483 #, python-format msgid "Invalid date: %s" msgstr "Fecha no válida: %s" -#: db/models/fields/__init__.py:532 db/models/fields/__init__.py:550 +#: db/models/fields/__init__.py:547 db/models/fields/__init__.py:565 msgid "Enter a valid date/time in YYYY-MM-DD HH:MM[:ss[.uuuuuu]] format." msgstr "" "Introduzca un valor de fecha/hora válido en formato AAAA-MM-DD HH:MM[:ss[." "uuuuuu]]." -#: db/models/fields/__init__.py:586 +#: db/models/fields/__init__.py:601 msgid "This value must be a decimal number." msgstr "Este valor debe ser un número decimal." -#: db/models/fields/__init__.py:719 +#: db/models/fields/__init__.py:686 +msgid "This value must be a float." +msgstr "Este valor debe ser un valor en representación de punto flotante." + +#: db/models/fields/__init__.py:746 msgid "This value must be either None, True or False." msgstr "Este valor debe ser None, True o False." -#: db/models/fields/__init__.py:817 db/models/fields/__init__.py:831 +#: db/models/fields/__init__.py:849 db/models/fields/__init__.py:863 msgid "Enter a valid time in HH:MM[:ss[.uuuuuu]] format." msgstr "Introduzca un valor de hora válido en formato HH:MM[:ss[.uuuuuu]]." -#: db/models/fields/related.py:761 +#: db/models/fields/related.py:792 msgid "" "Hold down \"Control\", or \"Command\" on a Mac, to select more than one." msgstr "" "Mantenga presionada \"Control\" (\"Command\" en una Mac) para seleccionar " "más de uno." -#: db/models/fields/related.py:838 +#: db/models/fields/related.py:870 #, python-format msgid "Please enter valid %(self)s IDs. The value %(value)r is invalid." msgid_plural "" @@ -3837,32 +4019,40 @@ msgstr "Asegúrese de que no existan mas de %s lugares decimales." msgid "Ensure that there are no more than %s digits before the decimal point." msgstr "Asegúrese de que no existan mas de %s dígitos antes del punto decimal." -#: forms/fields.py:287 forms/fields.py:849 +#: forms/fields.py:288 forms/fields.py:863 msgid "Enter a valid date." msgstr "Introduzca una fecha válida." -#: forms/fields.py:321 forms/fields.py:850 +#: forms/fields.py:322 forms/fields.py:864 msgid "Enter a valid time." msgstr "Introduzca un valor de hora válido." -#: forms/fields.py:360 +#: forms/fields.py:361 msgid "Enter a valid date/time." msgstr "Introduzca un valor de fecha/hora válido." -#: forms/fields.py:446 +#: forms/fields.py:447 msgid "No file was submitted. Check the encoding type on the form." msgstr "" "No se envió un archivo. Verifique el tipo de codificación en el formulario." -#: forms/fields.py:447 +#: forms/fields.py:448 msgid "No file was submitted." msgstr "No se envió ningún archivo." -#: forms/fields.py:448 +#: forms/fields.py:449 msgid "The submitted file is empty." msgstr "El archivo enviado está vacío." -#: forms/fields.py:477 +#: forms/fields.py:450 +#, python-format +msgid "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr "" +"Asegúrese de que este nombre de archivo tenga como máximo %(max)d caracteres " +"(tiene %(length)d)." + +#: forms/fields.py:483 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." @@ -3870,107 +4060,141 @@ msgstr "" "Envíe una imagen válida. El archivo que ha enviado no era una imagen o se " "trataba de una imagen corrupta." -#: forms/fields.py:538 +#: forms/fields.py:544 msgid "Enter a valid URL." msgstr "Introduzca una URL válida." -#: forms/fields.py:539 +#: forms/fields.py:545 msgid "This URL appears to be a broken link." msgstr "La URL parece ser un enlace roto." -#: forms/fields.py:618 forms/fields.py:696 +#: forms/fields.py:625 forms/fields.py:703 #, python-format msgid "Select a valid choice. %(value)s is not one of the available choices." msgstr "" "Seleccione una opción válida. %(value)s no es una de las opciones " "disponibles." -#: forms/fields.py:697 forms/fields.py:758 forms/models.py:720 +#: forms/fields.py:704 forms/fields.py:765 forms/models.py:991 msgid "Enter a list of values." msgstr "Introduzca una lista de valores." -#: forms/fields.py:878 +#: forms/fields.py:892 msgid "Enter a valid IPv4 address." msgstr "Introduzca una dirección IPv4 válida" -#: forms/fields.py:888 +#: forms/fields.py:902 msgid "" "Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens." msgstr "Introduzca un 'slug' válido consistente en letras, números o guiones." -#: forms/formsets.py:242 forms/formsets.py:244 +#: forms/formsets.py:271 forms/formsets.py:273 msgid "Order" msgstr "Ordenar" -#: forms/models.py:281 forms/models.py:290 +#: forms/models.py:367 +#, python-format +msgid "%(field_name)s must be unique for %(date_field)s %(lookup)s." +msgstr "" +"%(field_name)s debe ser único/a para un %(lookup)s %(date_field)s " +"determinado." + +#: forms/models.py:381 forms/models.py:389 #, python-format msgid "%(model_name)s with this %(field_label)s already exists." msgstr "Ya existe un/a %(model_name)s con este/a %(field_label)s." -#: forms/models.py:587 +#: forms/models.py:581 +#, fuzzy, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "Por favor, corrija el siguiente error." + +#: forms/models.py:585 +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "" + +#: forms/models.py:591 +#, 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 "" + +#: forms/models.py:599 +#, fuzzy +msgid "Please correct the duplicate values below." +msgstr "Por favor, corrija el siguiente error." + +#: forms/models.py:855 msgid "The inline foreign key did not match the parent instance primary key." msgstr "" "La clave foránea del modelo inline no coincide con la de la instancia padre." -#: forms/models.py:650 +#: forms/models.py:918 msgid "Select a valid choice. That choice is not one of the available choices." msgstr "" "Seleccione una opción válida. Esa opción no es una de las opciones " "disponibles." -#: forms/models.py:721 +#: forms/models.py:992 #, python-format msgid "Select a valid choice. %s is not one of the available choices." msgstr "" "Seleccione una opción válida. %s no es una de las opciones disponibles." -#: template/defaultfilters.py:741 +#: forms/models.py:994 +#, 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." + +#: template/defaultfilters.py:767 msgid "yes,no,maybe" msgstr "si,no,talvez" -#: template/defaultfilters.py:772 +#: template/defaultfilters.py:798 #, python-format msgid "%(size)d byte" msgid_plural "%(size)d bytes" msgstr[0] "%(size)d byte" msgstr[1] "%(size)d bytes" -#: template/defaultfilters.py:774 +#: template/defaultfilters.py:800 #, python-format msgid "%.1f KB" msgstr "%.1f KB" -#: template/defaultfilters.py:776 +#: template/defaultfilters.py:802 #, python-format msgid "%.1f MB" msgstr "%.1f MB" -#: template/defaultfilters.py:777 +#: template/defaultfilters.py:803 #, python-format msgid "%.1f GB" msgstr "%.1f GB" -#: utils/dateformat.py:41 +#: utils/dateformat.py:42 msgid "p.m." msgstr "p.m." -#: utils/dateformat.py:42 +#: utils/dateformat.py:43 msgid "a.m." msgstr "a.m." -#: utils/dateformat.py:47 +#: utils/dateformat.py:48 msgid "PM" msgstr "PM" -#: utils/dateformat.py:48 +#: utils/dateformat.py:49 msgid "AM" msgstr "AM" -#: utils/dateformat.py:97 +#: utils/dateformat.py:98 msgid "midnight" msgstr "medianoche" -#: utils/dateformat.py:99 +#: utils/dateformat.py:100 msgid "noon" msgstr "mediodía" @@ -4194,33 +4418,33 @@ msgid_plural "minutes" msgstr[0] "minuto" msgstr[1] "minutos" -#: utils/timesince.py:43 +#: utils/timesince.py:45 msgid "minutes" msgstr "minutos" -#: utils/timesince.py:48 +#: utils/timesince.py:50 #, python-format msgid "%(number)d %(type)s" msgstr "%(number)d %(type)s" -#: utils/timesince.py:54 +#: utils/timesince.py:56 #, python-format msgid ", %(number)d %(type)s" msgstr ", %(number)d %(type)s" -#: utils/translation/trans_real.py:403 +#: utils/translation/trans_real.py:399 msgid "DATE_FORMAT" msgstr "j N Y" -#: utils/translation/trans_real.py:405 +#: utils/translation/trans_real.py:401 msgid "TIME_FORMAT" msgstr "P" -#: utils/translation/trans_real.py:421 +#: utils/translation/trans_real.py:417 msgid "YEAR_MONTH_FORMAT" msgstr "F Y" -#: utils/translation/trans_real.py:422 +#: utils/translation/trans_real.py:418 msgid "MONTH_DAY_FORMAT" msgstr "j \\de F" diff --git a/django/contrib/gis/db/models/sql/where.py b/django/contrib/gis/db/models/sql/where.py index 838889fbad..105cbfbec5 100644 --- a/django/contrib/gis/db/models/sql/where.py +++ b/django/contrib/gis/db/models/sql/where.py @@ -35,7 +35,7 @@ class GeoWhereNode(WhereNode): return super(WhereNode, self).add(data, connector) obj, lookup_type, value = data - alias, col, field = obj.alias, obj.col, obj.field + col, field = obj.col, obj.field if not hasattr(field, "geom_type"): # Not a geographic field, so call `WhereNode.add`. @@ -76,7 +76,7 @@ class GeoWhereNode(WhereNode): # the `get_geo_where_clause` to construct the appropriate # spatial SQL when `make_atom` is called. annotation = GeoAnnotation(field, value, where) - return super(WhereNode, self).add(((alias, col, field.db_type()), lookup_type, annotation, params), connector) + return super(WhereNode, self).add(((obj.alias, col, field.db_type()), lookup_type, annotation, params), connector) def make_atom(self, child, qn): obj, lookup_type, value_annot, params = child diff --git a/django/contrib/gis/maps/google/gmap.py b/django/contrib/gis/maps/google/gmap.py index dd4f67bc52..de8f75c5a0 100644 --- a/django/contrib/gis/maps/google/gmap.py +++ b/django/contrib/gis/maps/google/gmap.py @@ -143,7 +143,7 @@ class GoogleMap(object): @property def icons(self): "Returns a sequence of GIcon objects in this map." - return [marker.icon for marker in self.markers if marker.icon] + return set([marker.icon for marker in self.markers if marker.icon]) class GoogleMapSet(GoogleMap): @@ -221,6 +221,6 @@ class GoogleMapSet(GoogleMap): @property def icons(self): "Returns a sequence of all icons in each map of the set." - icons = [] - for map in self.maps: icons.extend(map.icons) + icons = set() + for map in self.maps: icons |= map.icons return icons diff --git a/django/contrib/gis/maps/google/overlays.py b/django/contrib/gis/maps/google/overlays.py index 4f1247d0a4..c2ebb3c992 100644 --- a/django/contrib/gis/maps/google/overlays.py +++ b/django/contrib/gis/maps/google/overlays.py @@ -231,6 +231,14 @@ class GIcon(object): self.iconanchor = iconanchor self.infowindowanchor = infowindowanchor + def __cmp__(self, other): + return cmp(self.varname, other.varname) + + def __hash__(self): + # XOR with hash of GIcon type so that hash('varname') won't + # equal hash(GIcon('varname')). + return hash(self.__class__) ^ hash(self.varname) + class GMarker(GOverlayBase): """ A Python wrapper for the Google GMarker object. For more information diff --git a/django/contrib/gis/tests/relatedapp/models.py b/django/contrib/gis/tests/relatedapp/models.py index d7dd6bbfd2..1125d7fb85 100644 --- a/django/contrib/gis/tests/relatedapp/models.py +++ b/django/contrib/gis/tests/relatedapp/models.py @@ -32,3 +32,13 @@ class Parcel(models.Model): border2 = models.PolygonField(srid=2276) objects = models.GeoManager() def __unicode__(self): return self.name + +# These use the GeoManager but do not have any geographic fields. +class Author(models.Model): + name = models.CharField(max_length=100) + objects = models.GeoManager() + +class Book(models.Model): + title = models.CharField(max_length=100) + author = models.ForeignKey(Author, related_name='books') + objects = models.GeoManager() diff --git a/django/contrib/gis/tests/relatedapp/tests.py b/django/contrib/gis/tests/relatedapp/tests.py index 77f6c73bb6..8c4f83b15a 100644 --- a/django/contrib/gis/tests/relatedapp/tests.py +++ b/django/contrib/gis/tests/relatedapp/tests.py @@ -1,10 +1,10 @@ import os, unittest from django.contrib.gis.geos import * from django.contrib.gis.db.backend import SpatialBackend -from django.contrib.gis.db.models import F, Extent, Union +from django.contrib.gis.db.models import Count, Extent, F, Union from django.contrib.gis.tests.utils import no_mysql, no_oracle, no_spatialite from django.conf import settings -from models import City, Location, DirectoryEntry, Parcel +from models import City, Location, DirectoryEntry, Parcel, Book, Author cities = (('Aurora', 'TX', -97.516111, 33.058333), ('Roswell', 'NM', -104.528056, 33.387222), @@ -196,8 +196,8 @@ class RelatedGeoModelTest(unittest.TestCase): # ID values do not match their City ID values. loc1 = Location.objects.create(point='POINT (-95.363151 29.763374)') loc2 = Location.objects.create(point='POINT (-96.801611 32.782057)') - dallas = City.objects.create(name='Dallas', location=loc2) - houston = City.objects.create(name='Houston', location=loc1) + dallas = City.objects.create(name='Dallas', state='TX', location=loc2) + houston = City.objects.create(name='Houston', state='TX', location=loc1) # The expected ID values -- notice the last two location IDs # are out of order. We want to make sure that the related @@ -231,6 +231,32 @@ class RelatedGeoModelTest(unittest.TestCase): q = pickle.loads(q_str) self.assertEqual(GeoQuery, q.__class__) + def test12_count(self): + "Testing `Count` aggregate use with the `GeoManager`. See #11087." + # Creating a new City, 'Fort Worth', that uses the same location + # as Dallas. + dallas = City.objects.get(name='Dallas') + ftworth = City.objects.create(name='Fort Worth', state='TX', location=dallas.location) + + # Count annotation should be 2 for the Dallas location now. + loc = Location.objects.annotate(num_cities=Count('city')).get(id=dallas.location.id) + self.assertEqual(2, loc.num_cities) + + # Creating some data for the Book/Author non-geo models that + # use GeoManager. See #11087. + tp = Author.objects.create(name='Trevor Paglen') + Book.objects.create(title='Torture Taxi', author=tp) + Book.objects.create(title='I Could Tell You But Then You Would Have to be Destroyed by Me', author=tp) + Book.objects.create(title='Blank Spots on the Map', author=tp) + wp = Author.objects.create(name='William Patry') + Book.objects.create(title='Patry on Copyright', author=wp) + + # Should only be one author (Trevor Paglen) returned by this query, and + # the annotation should have 3 for the number of books. + qs = Author.objects.annotate(num_books=Count('books')).filter(num_books__gt=1) + self.assertEqual(1, len(qs)) + self.assertEqual(3, qs[0].num_books) + # TODO: Related tests for KML, GML, and distance lookups. def suite(): diff --git a/django/contrib/gis/utils/ogrinspect.py b/django/contrib/gis/utils/ogrinspect.py index c0c3c40a69..145bd221bb 100644 --- a/django/contrib/gis/utils/ogrinspect.py +++ b/django/contrib/gis/utils/ogrinspect.py @@ -12,12 +12,12 @@ from django.contrib.gis.gdal.field import OFTDate, OFTDateTime, OFTInteger, OFTR def mapping(data_source, geom_name='geom', layer_key=0, multi_geom=False): """ - Given a DataSource, generates a dictionary that may be used + Given a DataSource, generates a dictionary that may be used for invoking the LayerMapping utility. Keyword Arguments: `geom_name` => The name of the geometry field to use for the model. - + `layer_key` => The key for specifying which layer in the DataSource to use; defaults to 0 (the first layer). May be an integer index or a string identifier for the layer. @@ -31,7 +31,7 @@ def mapping(data_source, geom_name='geom', layer_key=0, multi_geom=False): pass else: raise TypeError('Data source parameter must be a string or a DataSource object.') - + # Creating the dictionary. _mapping = {} @@ -52,32 +52,32 @@ def ogrinspect(*args, **kwargs): model name this function will generate a GeoDjango model. Usage: - + >>> from django.contrib.gis.utils import ogrinspect >>> ogrinspect('/path/to/shapefile.shp','NewModel') - + ...will print model definition to stout - + or put this in a python script and use to redirect the output to a new model like: - + $ python generate_model.py > myapp/models.py - - # generate_model.py + + # generate_model.py from django.contrib.gis.utils import ogrinspect shp_file = 'data/mapping_hacks/world_borders.shp' model_name = 'WorldBorders' print ogrinspect(shp_file, model_name, multi_geom=True, srid=4326, geom_name='shapes', blank=True) - + Required Arguments `datasource` => string or DataSource object to file pointer - + `model name` => string of name of new model class to create - + Optional Keyword Arguments - `geom_name` => For specifying the model name for the Geometry Field. + `geom_name` => For specifying the model name for the Geometry Field. Otherwise will default to `geom` `layer_key` => The key for specifying which layer in the DataSource to use; @@ -86,24 +86,24 @@ def ogrinspect(*args, **kwargs): `srid` => The SRID to use for the Geometry Field. If it can be determined, the SRID of the datasource is used. - + `multi_geom` => Boolean (default: False) - specify as multigeometry. - + `name_field` => String - specifies a field name to return for the `__unicode__` function (which will be generated if specified). - - `imports` => Boolean (default: True) - set to False to omit the - `from django.contrib.gis.db import models` code from the + + `imports` => Boolean (default: True) - set to False to omit the + `from django.contrib.gis.db import models` code from the autogenerated models thus avoiding duplicated imports when building more than one model by batching ogrinspect() - + `decimal` => Boolean or sequence (default: False). When set to True all generated model fields corresponding to the `OFTReal` type will be `DecimalField` instead of `FloatField`. A sequence of specific field names to generate as `DecimalField` may also be used. `blank` => Boolean or sequence (default: False). When set to True all - generated model fields will have `blank=True`. If the user wants to + generated model fields will have `blank=True`. If the user wants to give specific fields to have blank, then a list/tuple of OGR field names may be used. @@ -111,13 +111,13 @@ def ogrinspect(*args, **kwargs): model fields will have `null=True`. If the user wants to specify give specific fields to have null, then a list/tuple of OGR field names may be used. - + Note: This routine calls the _ogrinspect() helper to do the heavy lifting. """ return '\n'.join(s for s in _ogrinspect(*args, **kwargs)) def _ogrinspect(data_source, model_name, geom_name='geom', layer_key=0, srid=None, - multi_geom=False, name_field=None, imports=True, + multi_geom=False, name_field=None, imports=True, decimal=False, blank=False, null=False): """ Helper routine for `ogrinspect` that generates GeoDjango models corresponding @@ -140,7 +140,7 @@ def _ogrinspect(data_source, model_name, geom_name='geom', layer_key=0, srid=Non # keyword arguments. def process_kwarg(kwarg): if isinstance(kwarg, (list, tuple)): - return [s.lower() for s in kwarg] + return [s.lower() for s in kwarg] elif kwarg: return [s.lower() for s in ogr_fields] else: @@ -164,18 +164,18 @@ def _ogrinspect(data_source, model_name, geom_name='geom', layer_key=0, srid=Non yield '' yield 'class %s(models.Model):' % model_name - + for field_name, width, precision, field_type in izip(ogr_fields, layer.field_widths, layer.field_precisions, layer.field_types): # The model field name. mfield = field_name.lower() if mfield[-1:] == '_': mfield += 'field' - + # Getting the keyword args string. kwargs_str = get_kwargs_str(field_name) if field_type is OFTReal: # By default OFTReals are mapped to `FloatField`, however, they - # may also be mapped to `DecimalField` if specified in the + # may also be mapped to `DecimalField` if specified in the # `decimal` keyword. if field_name.lower() in decimal_fields: yield ' %s = models.DecimalField(max_digits=%d, decimal_places=%d%s)' % (mfield, width, precision, kwargs_str) @@ -192,8 +192,8 @@ def _ogrinspect(data_source, model_name, geom_name='geom', layer_key=0, srid=Non elif field_type is OFTDate: yield ' %s = models.TimeField(%s)' % (mfield, kwargs_str[2:]) else: - raise TypeError('Unknown field type %s in %s' % (fld_type, mfield)) - + raise TypeError('Unknown field type %s in %s' % (field_type, mfield)) + # TODO: Autodetection of multigeometry types (see #7218). gtype = layer.geom_type if multi_geom and gtype.num in (1, 2, 3): diff --git a/django/db/models/fields/files.py b/django/db/models/fields/files.py index 61a903e36c..aab4f3789f 100644 --- a/django/db/models/fields/files.py +++ b/django/db/models/fields/files.py @@ -142,13 +142,13 @@ class FileDescriptor(object): """ The descriptor for the file attribute on the model instance. Returns a FieldFile when accessed so you can do stuff like:: - + >>> instance.file.size - + Assigns a file object on assignment so you can do:: - + >>> instance.file = File(...) - + """ def __init__(self, field): self.field = field @@ -156,9 +156,9 @@ class FileDescriptor(object): def __get__(self, instance=None, owner=None): if instance is None: raise AttributeError( - "The '%s' attribute can only be accessed from %s instances." + "The '%s' attribute can only be accessed from %s instances." % (self.field.name, owner.__name__)) - + # This is slightly complicated, so worth an explanation. # instance.file`needs to ultimately return some instance of `File`, # probably a subclass. Additionally, this returned object needs to have @@ -168,8 +168,8 @@ class FileDescriptor(object): # peek below you can see that we're not. So depending on the current # value of the field we have to dynamically construct some sort of # "thing" to return. - - # The instance dict contains whatever was originally assigned + + # The instance dict contains whatever was originally assigned # in __set__. file = instance.__dict__[self.field.name] @@ -186,14 +186,14 @@ class FileDescriptor(object): # Other types of files may be assigned as well, but they need to have # the FieldFile interface added to the. Thus, we wrap any other type of - # File inside a FieldFile (well, the field's attr_class, which is + # File inside a FieldFile (well, the field's attr_class, which is # usually FieldFile). elif isinstance(file, File) and not isinstance(file, FieldFile): file_copy = self.field.attr_class(instance, self.field, file.name) file_copy.file = file file_copy._committed = False instance.__dict__[self.field.name] = file_copy - + # Finally, because of the (some would say boneheaded) way pickle works, # the underlying FieldFile might not actually itself have an associated # file. So we need to reset the details of the FieldFile in those cases. @@ -201,7 +201,7 @@ class FileDescriptor(object): file.instance = instance file.field = self.field file.storage = self.field.storage - + # That was fun, wasn't it? return instance.__dict__[self.field.name] @@ -212,7 +212,7 @@ class FileField(Field): # The class to wrap instance attributes in. Accessing the file object off # the instance will always return an instance of attr_class. attr_class = FieldFile - + # The descriptor to use for accessing the attribute off of the class. descriptor_class = FileDescriptor @@ -300,40 +300,20 @@ class ImageFileDescriptor(FileDescriptor): assigning the width/height to the width_field/height_field, if appropriate. """ def __set__(self, instance, value): + previous_file = instance.__dict__.get(self.field.name) super(ImageFileDescriptor, self).__set__(instance, value) - - # The rest of this method deals with width/height fields, so we can - # bail early if neither is used. - if not self.field.width_field and not self.field.height_field: - return - - # We need to call the descriptor's __get__ to coerce this assigned - # value into an instance of the right type (an ImageFieldFile, in this - # case). - value = self.__get__(instance) - - if not value: - return - - # Get the image dimensions, making sure to leave the file in the same - # state (opened or closed) that we got it in. However, we *don't* rewind - # the file pointer if the file is already open. This is in keeping with - # most Python standard library file operations that leave it up to the - # user code to reset file pointers after operations that move it. - from django.core.files.images import get_image_dimensions - close = value.closed - value.open() - try: - width, height = get_image_dimensions(value) - finally: - if close: - value.close() - - # Update the width and height fields - if self.field.width_field: - setattr(value.instance, self.field.width_field, width) - if self.field.height_field: - setattr(value.instance, self.field.height_field, height) + + # To prevent recalculating image dimensions when we are instantiating + # an object from the database (bug #11084), only update dimensions if + # the field had a value before this assignment. Since the default + # value for FileField subclasses is an instance of field.attr_class, + # previous_file will only be None when we are called from + # Model.__init__(). The ImageField.update_dimension_fields method + # hooked up to the post_init signal handles the Model.__init__() cases. + # Assignment happening outside of Model.__init__() will trigger the + # update right here. + if previous_file is not None: + self.field.update_dimension_fields(instance, force=True) class ImageFieldFile(ImageFile, FieldFile): def delete(self, save=True): @@ -350,6 +330,69 @@ class ImageField(FileField): self.width_field, self.height_field = width_field, height_field FileField.__init__(self, verbose_name, name, **kwargs) + def contribute_to_class(self, cls, name): + super(ImageField, self).contribute_to_class(cls, name) + # Attach update_dimension_fields so that dimension fields declared + # after their corresponding image field don't stay cleared by + # Model.__init__, see bug #11196. + signals.post_init.connect(self.update_dimension_fields, sender=cls) + + def update_dimension_fields(self, instance, force=False, *args, **kwargs): + """ + Updates field's width and height fields, if defined. + + This method is hooked up to model's post_init signal to update + dimensions after instantiating a model instance. However, dimensions + won't be updated if the dimensions fields are already populated. This + avoids unnecessary recalculation when loading an object from the + database. + + Dimensions can be forced to update with force=True, which is how + ImageFileDescriptor.__set__ calls this method. + """ + # Nothing to update if the field doesn't have have dimension fields. + has_dimension_fields = self.width_field or self.height_field + if not has_dimension_fields: + return + + # getattr will call the ImageFileDescriptor's __get__ method, which + # coerces the assigned value into an instance of self.attr_class + # (ImageFieldFile in this case). + file = getattr(instance, self.attname) + + # Nothing to update if we have no file and not being forced to update. + if not file and not force: + return + + dimension_fields_filled = not( + (self.width_field and not getattr(instance, self.width_field)) + or (self.height_field and not getattr(instance, self.height_field)) + ) + # When both dimension fields have values, we are most likely loading + # data from the database or updating an image field that already had + # an image stored. In the first case, we don't want to update the + # dimension fields because we are already getting their values from the + # database. In the second case, we do want to update the dimensions + # fields and will skip this return because force will be True since we + # were called from ImageFileDescriptor.__set__. + if dimension_fields_filled and not force: + return + + # file should be an instance of ImageFieldFile or should be None. + if file: + width = file.width + height = file.height + else: + # No file, so clear dimensions fields. + width = None + height = None + + # Update the width and height fields. + if self.width_field: + setattr(instance, self.width_field, width) + if self.height_field: + setattr(instance, self.height_field, height) + def formfield(self, **kwargs): defaults = {'form_class': forms.ImageField} defaults.update(kwargs) diff --git a/django/db/models/query.py b/django/db/models/query.py index 6a8d7d5e64..0d35b0ba16 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -355,10 +355,11 @@ class QuerySet(object): # Delete objects in chunks to prevent the list of related objects from # becoming too long. + seen_objs = None while 1: # Collect all the objects to be deleted in this chunk, and all the # objects that are related to the objects that are to be deleted. - seen_objs = CollectedObjects() + seen_objs = CollectedObjects(seen_objs) for object in del_query[:CHUNK_SIZE]: object._collect_sub_objects(seen_objs) diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py index 7a5ad919a1..6a6b69013f 100644 --- a/django/db/models/query_utils.py +++ b/django/db/models/query_utils.py @@ -32,11 +32,21 @@ class CollectedObjects(object): This is used for the database object deletion routines so that we can calculate the 'leaf' objects which should be deleted first. + + previously_seen is an optional argument. It must be a CollectedObjects + instance itself; any previously_seen collected object will be blocked from + being added to this instance. """ - def __init__(self): + def __init__(self, previously_seen=None): self.data = {} self.children = {} + if previously_seen: + self.blocked = previously_seen.blocked + for cls, seen in previously_seen.data.items(): + self.blocked.setdefault(cls, SortedDict()).update(seen) + else: + self.blocked = {} def add(self, model, pk, obj, parent_model, nullable=False): """ @@ -53,6 +63,9 @@ class CollectedObjects(object): Returns True if the item already existed in the structure and False otherwise. """ + if pk in self.blocked.get(model, {}): + return True + d = self.data.setdefault(model, SortedDict()) retval = pk in d d[pk] = obj diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index 394e30ba97..d290d60e63 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -689,7 +689,7 @@ class BaseQuery(object): If 'with_aliases' is true, any column names that are duplicated (without the table names) are given unique aliases. This is needed in - some cases to avoid ambiguitity with nested queries. + some cases to avoid ambiguity with nested queries. """ qn = self.quote_name_unless_alias qn2 = self.connection.ops.quote_name @@ -1303,7 +1303,7 @@ class BaseQuery(object): opts = self.model._meta root_alias = self.tables[0] seen = {None: root_alias} - + # Skip all proxy to the root proxied model proxied_model = get_proxied_model(opts) @@ -1732,7 +1732,7 @@ class BaseQuery(object): raise MultiJoin(pos + 1) if model: # The field lives on a base class of the current model. - # Skip the chain of proxy to the concrete proxied model + # Skip the chain of proxy to the concrete proxied model proxied_model = get_proxied_model(opts) for int_model in opts.get_base_chain(model): @@ -2362,7 +2362,7 @@ class BaseQuery(object): return cursor if result_type == SINGLE: if self.ordering_aliases: - return cursor.fetchone()[:-len(results.ordering_aliases)] + return cursor.fetchone()[:-len(self.ordering_aliases)] return cursor.fetchone() # The MULTI case. diff --git a/django/forms/models.py b/django/forms/models.py index a0b217860d..8e9c0de0e7 100644 --- a/django/forms/models.py +++ b/django/forms/models.py @@ -584,7 +584,7 @@ class BaseModelFormSet(BaseFormSet): else: return ugettext("Please correct the duplicate data for %(field)s, " "which must be unique.") % { - "field": get_text_list(unique_check, _("and")), + "field": get_text_list(unique_check, unicode(_("and"))), } def get_date_error_message(self, date_check): diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py index 95fda273f0..0fd24c18ad 100644 --- a/tests/modeltests/model_forms/models.py +++ b/tests/modeltests/model_forms/models.py @@ -1175,8 +1175,9 @@ True >>> instance.height 16 -# Delete the current file since this is not done by Django. ->>> instance.image.delete() +# Delete the current file since this is not done by Django, but don't save +# because the dimension fields are not null=True. +>>> instance.image.delete(save=False) >>> f = ImageFileForm(data={'description': u'An image'}, files={'image': SimpleUploadedFile('test.png', image_data)}) >>> f.is_valid() @@ -1207,9 +1208,9 @@ True >>> instance.width 16 -# Delete the current image since this is not done by Django. - ->>> instance.image.delete() +# Delete the current file since this is not done by Django, but don't save +# because the dimension fields are not null=True. +>>> instance.image.delete(save=False) # Override the file by uploading a new one. @@ -1224,8 +1225,9 @@ True >>> instance.width 48 -# Delete the current file since this is not done by Django. ->>> instance.image.delete() +# Delete the current file since this is not done by Django, but don't save +# because the dimension fields are not null=True. +>>> instance.image.delete(save=False) >>> instance.delete() >>> f = ImageFileForm(data={'description': u'Changed it'}, files={'image': SimpleUploadedFile('test2.png', image_data2)}) @@ -1239,8 +1241,9 @@ True >>> instance.width 48 -# Delete the current file since this is not done by Django. ->>> instance.image.delete() +# Delete the current file since this is not done by Django, but don't save +# because the dimension fields are not null=True. +>>> instance.image.delete(save=False) >>> instance.delete() # Test the non-required ImageField diff --git a/tests/regressiontests/admin_scripts/tests.py b/tests/regressiontests/admin_scripts/tests.py index 0e28a9749f..e67126e9a7 100644 --- a/tests/regressiontests/admin_scripts/tests.py +++ b/tests/regressiontests/admin_scripts/tests.py @@ -536,7 +536,7 @@ class DjangoAdminSettingsDirectory(AdminScriptTestCase): args = ['startapp','settings_test'] out, err = self.run_django_admin(args,'settings') self.assertNoOutput(err) - self.assertTrue(os.path.exists(os.path.join(test_dir, 'settings_test'))) + self.assert_(os.path.exists(os.path.join(test_dir, 'settings_test'))) shutil.rmtree(os.path.join(test_dir, 'settings_test')) def test_builtin_command(self): diff --git a/tests/regressiontests/delete_regress/__init__.py b/tests/regressiontests/delete_regress/__init__.py new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/tests/regressiontests/delete_regress/__init__.py @@ -0,0 +1 @@ + diff --git a/tests/regressiontests/delete_regress/models.py b/tests/regressiontests/delete_regress/models.py new file mode 100644 index 0000000000..93cadc58fa --- /dev/null +++ b/tests/regressiontests/delete_regress/models.py @@ -0,0 +1,61 @@ +from django.conf import settings +from django.db import models, backend, connection, transaction +from django.db.models import sql, query +from django.test import TransactionTestCase + +class Book(models.Model): + pagecount = models.IntegerField() + +# Can't run this test under SQLite, because you can't +# get two connections to an in-memory database. +if settings.DATABASE_ENGINE != 'sqlite3': + class DeleteLockingTest(TransactionTestCase): + def setUp(self): + # Create a second connection to the database + self.conn2 = backend.DatabaseWrapper({ + 'DATABASE_HOST': settings.DATABASE_HOST, + 'DATABASE_NAME': settings.DATABASE_NAME, + 'DATABASE_OPTIONS': settings.DATABASE_OPTIONS, + 'DATABASE_PASSWORD': settings.DATABASE_PASSWORD, + 'DATABASE_PORT': settings.DATABASE_PORT, + 'DATABASE_USER': settings.DATABASE_USER, + 'TIME_ZONE': settings.TIME_ZONE, + }) + + # Put both DB connections into managed transaction mode + transaction.enter_transaction_management() + transaction.managed(True) + self.conn2._enter_transaction_management(True) + + def tearDown(self): + # Close down the second connection. + transaction.leave_transaction_management() + self.conn2.close() + + def test_concurrent_delete(self): + "Deletes on concurrent transactions don't collide and lock the database. Regression for #9479" + + # Create some dummy data + b1 = Book(id=1, pagecount=100) + b2 = Book(id=2, pagecount=200) + b3 = Book(id=3, pagecount=300) + b1.save() + b2.save() + b3.save() + + transaction.commit() + + self.assertEquals(3, Book.objects.count()) + + # Delete something using connection 2. + cursor2 = self.conn2.cursor() + cursor2.execute('DELETE from delete_regress_book WHERE id=1') + self.conn2._commit(); + + # Now perform a queryset delete that covers the object + # deleted in connection 2. This causes an infinite loop + # under MySQL InnoDB unless we keep track of already + # deleted objects. + Book.objects.filter(pagecount__lt=250).delete() + transaction.commit() + self.assertEquals(1, Book.objects.count()) diff --git a/tests/regressiontests/file_storage/models.py b/tests/regressiontests/file_storage/models.py index 32af5d7588..e69de29bb2 100644 --- a/tests/regressiontests/file_storage/models.py +++ b/tests/regressiontests/file_storage/models.py @@ -1,93 +0,0 @@ -import os -import tempfile -import shutil -from django.db import models -from django.core.files.storage import FileSystemStorage -from django.core.files.base import ContentFile - -# Test for correct behavior of width_field/height_field. -# Of course, we can't run this without PIL. - -try: - # Checking for the existence of Image is enough for CPython, but - # for PyPy, you need to check for the underlying modules - from PIL import Image, _imaging -except ImportError: - Image = None - -# If we have PIL, do these tests -if Image: - temp_storage_dir = tempfile.mkdtemp() - temp_storage = FileSystemStorage(temp_storage_dir) - - class Person(models.Model): - name = models.CharField(max_length=50) - mugshot = models.ImageField(storage=temp_storage, upload_to='tests', - height_field='mug_height', - width_field='mug_width') - mug_height = models.PositiveSmallIntegerField() - mug_width = models.PositiveSmallIntegerField() - - __test__ = {'API_TESTS': """ ->>> from django.core.files import File ->>> image_data = open(os.path.join(os.path.dirname(__file__), "test.png"), 'rb').read() ->>> p = Person(name="Joe") ->>> p.mugshot.save("mug", ContentFile(image_data)) ->>> p.mugshot.width -16 ->>> p.mugshot.height -16 ->>> p.mug_height -16 ->>> p.mug_width -16 - -# Bug #9786: Ensure '==' and '!=' work correctly. ->>> image_data = open(os.path.join(os.path.dirname(__file__), "test1.png"), 'rb').read() ->>> p1 = Person(name="Bob") ->>> p1.mugshot.save("mug", ContentFile(image_data)) ->>> p2 = Person.objects.get(name="Joe") ->>> p.mugshot == p2.mugshot -True ->>> p.mugshot != p2.mugshot -False ->>> p.mugshot != p1.mugshot -True - -Bug #9508: Similarly to the previous test, make sure hash() works as expected -(equal items must hash to the same value). ->>> hash(p.mugshot) == hash(p2.mugshot) -True - -# Bug #8175: correctly delete files that have been removed off the file system. ->>> import os ->>> p2 = Person(name="Fred") ->>> p2.mugshot.save("shot", ContentFile(image_data)) ->>> os.remove(p2.mugshot.path) ->>> p2.delete() - -# Bug #8534: FileField.size should not leave the file open. ->>> p3 = Person(name="Joan") ->>> p3.mugshot.save("shot", ContentFile(image_data)) - -# Get a "clean" model instance ->>> p3 = Person.objects.get(name="Joan") - -# It won't have an opened file. ->>> p3.mugshot.closed -True - -# After asking for the size, the file should still be closed. ->>> _ = p3.mugshot.size ->>> p3.mugshot.closed -True - -# Make sure that wrapping the file in a file still works ->>> p3.mugshot.file.open() ->>> p = Person.objects.create(name="Bob The Builder", mugshot=File(p3.mugshot.file)) ->>> p.save() ->>> p3.mugshot.file.close() - -# Delete all test files ->>> shutil.rmtree(temp_storage_dir) -"""} diff --git a/tests/regressiontests/file_storage/tests.py b/tests/regressiontests/file_storage/tests.py index 6e2b7be8e7..c228764e06 100644 --- a/tests/regressiontests/file_storage/tests.py +++ b/tests/regressiontests/file_storage/tests.py @@ -161,9 +161,9 @@ class FileStoragePathParsing(TestCase): self.storage.save('dotted.path/test', ContentFile("1")) self.storage.save('dotted.path/test', ContentFile("2")) - self.assertFalse(os.path.exists(os.path.join(self.storage_dir, 'dotted_.path'))) - self.assertTrue(os.path.exists(os.path.join(self.storage_dir, 'dotted.path/test'))) - self.assertTrue(os.path.exists(os.path.join(self.storage_dir, 'dotted.path/test_'))) + self.failIf(os.path.exists(os.path.join(self.storage_dir, 'dotted_.path'))) + self.assert_(os.path.exists(os.path.join(self.storage_dir, 'dotted.path/test'))) + self.assert_(os.path.exists(os.path.join(self.storage_dir, 'dotted.path/test_'))) def test_first_character_dot(self): """ @@ -173,13 +173,13 @@ class FileStoragePathParsing(TestCase): self.storage.save('dotted.path/.test', ContentFile("1")) self.storage.save('dotted.path/.test', ContentFile("2")) - self.assertTrue(os.path.exists(os.path.join(self.storage_dir, 'dotted.path/.test'))) + self.assert_(os.path.exists(os.path.join(self.storage_dir, 'dotted.path/.test'))) # Before 2.6, a leading dot was treated as an extension, and so # underscore gets added to beginning instead of end. if sys.version_info < (2, 6): - self.assertTrue(os.path.exists(os.path.join(self.storage_dir, 'dotted.path/_.test'))) + self.assert_(os.path.exists(os.path.join(self.storage_dir, 'dotted.path/_.test'))) else: - self.assertTrue(os.path.exists(os.path.join(self.storage_dir, 'dotted.path/.test_'))) + self.assert_(os.path.exists(os.path.join(self.storage_dir, 'dotted.path/.test_'))) if Image is not None: class DimensionClosingBug(TestCase): diff --git a/tests/regressiontests/model_fields/4x8.png b/tests/regressiontests/model_fields/4x8.png new file mode 100644 index 0000000000000000000000000000000000000000..ffce444d487d9c39a6c060b200e5bf7a1b18c9c0 GIT binary patch literal 87 zcmeAS@N?(olHy`uVBq!ia0vp^EI`b`!2~1&15bhk7>k44ofy`glX(f`2zt6WhHzX@ i{&T*8!H37>F#|)EBO|}3Wal!VB!j1`pUXO@geCyuc@ypc literal 0 HcmV?d00001 diff --git a/tests/regressiontests/model_fields/8x4.png b/tests/regressiontests/model_fields/8x4.png new file mode 100644 index 0000000000000000000000000000000000000000..60e6d69ee1da4a154ab87c29e347e76cdb76169c GIT binary patch literal 87 zcmeAS@N?(olHy`uVBq!ia0vp^96-#%!2~32*1ud1q!^2X+?^QKos)S9