Para instalar bookmarklets, arrastre el enlace a su barra\n"
"de favoritos, o pulse con el botón derecho el enlace y añádalo a sus "
"favoritos.\n"
-"Ahora puede escoger el bookmarklet desde cualquier página en el sitio.\n"
-"Observer que algunos de estos bookmarklets precisan que esté viendo\n"
-"el sitio desde un computador señalado como \"interno\" (hable\n"
-"con su administrador de sistemas si no está seguro de si el suyo lo es)."
+"Ahora puede escoger el bookmarklet desde cualquier página del sitio.\n"
+"Observe que algunos de estos bookmarklets precisan que esté viendo\n"
+"el sitio desde un ordenador señalado como \"interno\" (hable\n"
+"con su administrador de sistemas si no está seguro si el suyo lo es)."
"p>\n"
#: contrib/admin/templates/admin_doc/bookmarklets.html:18
msgid "Documentation for this page"
-msgstr "Documentación de esta página"
+msgstr "Documentación para esta página"
#: contrib/admin/templates/admin_doc/bookmarklets.html:19
msgid ""
"Jumps you from any page to the documentation for the view that generates "
"that page."
msgstr ""
-"Le lleva desde cualquier página a la documentación de la vista que la genera."
+"Lleva desde cualquier página a la documentación de la vista que la genera."
#: contrib/admin/templates/admin_doc/bookmarklets.html:21
msgid "Show object ID"
@@ -625,8 +624,8 @@ msgid ""
"Shows the content-type and unique ID for pages that represent a single "
"object."
msgstr ""
-"Muestra el tipo de contenido e ID unívoco de las páginas que representan un "
-"único objeto."
+"Muestra el tipo de contenido e ID único de las páginas que representan un "
+"simple objeto."
#: contrib/admin/templates/admin_doc/bookmarklets.html:24
msgid "Edit this object (current window)"
@@ -635,7 +634,7 @@ msgstr "Editar este objeto (ventana actual)"
#: contrib/admin/templates/admin_doc/bookmarklets.html:25
msgid "Jumps to the admin page for pages that represent a single object."
msgstr ""
-"Le lleva a la página de administración de páginas que representan un único "
+"Lleva a la página de administración de páginas que representan un único "
"objeto."
#: contrib/admin/templates/admin_doc/bookmarklets.html:27
@@ -649,85 +648,85 @@ msgstr ""
#: contrib/admin/templates/registration/logged_out.html:8
msgid "Thanks for spending some quality time with the Web site today."
-msgstr "Gracias por el tiempo que ha dedicado al sitio web hoy."
+msgstr "Gracias por el tiempo que ha dedicado hoy al sitio web."
#: contrib/admin/templates/registration/logged_out.html:10
msgid "Log in again"
-msgstr "Identificarse de nuevo"
+msgstr "Iniciar sesión de nuevo"
#: contrib/admin/templates/registration/password_change_done.html:3
#: contrib/admin/templates/registration/password_change_form.html:3
#: contrib/admin/templates/registration/password_change_form.html:5
#: contrib/admin/templates/registration/password_change_form.html:9
msgid "Password change"
-msgstr "Cambio de clave"
+msgstr "Cambio de contraseña"
#: contrib/admin/templates/registration/password_change_done.html:5
#: contrib/admin/templates/registration/password_change_done.html:9
msgid "Password change successful"
-msgstr "Cambio de clave exitoso"
+msgstr "Cambio de contraseña exitoso"
#: contrib/admin/templates/registration/password_change_done.html:11
msgid "Your password was changed."
-msgstr "Su clave ha sido cambiada."
+msgstr "Su contraseña ha sido cambiada."
#: contrib/admin/templates/registration/password_change_form.html:11
msgid ""
"Please enter your old password, for security's sake, and then enter your new "
"password twice so we can verify you typed it in correctly."
msgstr ""
-"Por favor, introduzca su clave antigua, por seguridad, y después introduzca "
-"la nueva clave dos veces para verificar que la ha escrito correctamente."
+"Por favor, introduzca su contraseña antigua, por seguridad, y después introduzca "
+"la nueva contraseña dos veces para verificar que la ha escrito correctamente."
#: contrib/admin/templates/registration/password_change_form.html:16
msgid "Old password:"
-msgstr "Clave antigua:"
+msgstr "Contraseña antigua:"
#: contrib/admin/templates/registration/password_change_form.html:18
msgid "New password:"
-msgstr "Clave nueva:"
+msgstr "Contraseña nueva:"
#: contrib/admin/templates/registration/password_change_form.html:20
msgid "Confirm password:"
-msgstr "Confirme clave:"
+msgstr "Confirme contraseña:"
#: contrib/admin/templates/registration/password_change_form.html:22
msgid "Change my password"
-msgstr "Cambiar mi clave"
+msgstr "Cambiar mi contraseña"
#: contrib/admin/templates/registration/password_reset_done.html:4
#: contrib/admin/templates/registration/password_reset_form.html:4
#: contrib/admin/templates/registration/password_reset_form.html:6
#: contrib/admin/templates/registration/password_reset_form.html:10
msgid "Password reset"
-msgstr "Recuperar clave"
+msgstr "Restablecer contraseña"
#: contrib/admin/templates/registration/password_reset_done.html:6
#: contrib/admin/templates/registration/password_reset_done.html:10
msgid "Password reset successful"
-msgstr "Recuperación de clave exitosa"
+msgstr "Restablecimiento de contraseña exitoso"
#: contrib/admin/templates/registration/password_reset_done.html:12
msgid ""
"We've e-mailed a new password to the e-mail address you submitted. You "
"should be receiving it shortly."
msgstr ""
-"Le hemos enviado una clave nueva a la dirección que ha suministrado. Debería "
+"Le hemos enviado una contraseña nueva a la dirección que ha suministrado. Debería "
"recibirla en breve."
#: contrib/admin/templates/registration/password_reset_email.html:2
msgid "You're receiving this e-mail because you requested a password reset"
-msgstr "Está recibiendo este mensaje debido a que solicitó recuperar la clave"
+msgstr "Está recibiendo este mensaje debido a que solicitó restablecer la contraseña"
#: contrib/admin/templates/registration/password_reset_email.html:3
#, python-format
msgid "for your user account at %(site_name)s"
-msgstr "de su cuenta de usuario en %(site_name)s."
+msgstr "para su cuenta de usuario en %(site_name)s."
#: contrib/admin/templates/registration/password_reset_email.html:5
#, python-format
msgid "Your new password is: %(new_password)s"
-msgstr "Su nueva clave es: %(new_password)s"
+msgstr "Su nueva contraseña es: %(new_password)s"
#: contrib/admin/templates/registration/password_reset_email.html:7
msgid "Feel free to change this password by going to this page:"
@@ -751,7 +750,7 @@ msgid ""
"Forgotten your password? Enter your e-mail address below, and we'll reset "
"your password and e-mail the new one to you."
msgstr ""
-"¿Ha olvidado su clave? Introduzca su dirección de correo electrónico, y "
+"¿Ha olvidado su contraseña? Introduzca su dirección de correo electrónico, y "
"crearemos una nueva que le enviaremos por correo."
#: contrib/admin/templates/registration/password_reset_form.html:16
@@ -760,7 +759,7 @@ msgstr "Dirección de correo electrónico:"
#: contrib/admin/templates/registration/password_reset_form.html:16
msgid "Reset my password"
-msgstr "Recuperar mi clave"
+msgstr "Restablecer mi contraseña"
#: contrib/admin/templates/widget/date_time.html:3
msgid "Date:"
@@ -798,7 +797,7 @@ msgstr "Añadir usuario"
#: contrib/admin/views/auth.py:58
msgid "Password changed successfully."
-msgstr "La clave se ha cambiado exitosamente."
+msgstr "La contraseña se ha cambiado con éxito."
#: contrib/admin/views/auth.py:65
#, python-format
@@ -810,7 +809,7 @@ msgid ""
"Please enter a correct username and password. Note that both fields are case-"
"sensitive."
msgstr ""
-"Por favor, introduzca un correcto nombre de usuario y contraseña. Note que "
+"Por favor, introduzca un nombre de usuario correcto y una contraseña. Note que "
"ambos campos son sensibles a mayúsculas/minúsculas."
#: contrib/admin/views/decorators.py:69
@@ -818,7 +817,7 @@ msgid ""
"Please log in again, because your session has expired. Don't worry: Your "
"submission has been saved."
msgstr ""
-"Por favor, identifíquese de nuevo, porque su sesión ha caducado. No se "
+"Por favor, inicie sesión de nuevo, ya que su sesión ha caducado. No se "
"preocupe: se ha guardado su envío."
#: contrib/admin/views/decorators.py:76
@@ -827,7 +826,7 @@ msgid ""
"cookies, reload this page, and try again."
msgstr ""
"Parece que su navegador no está configurado para aceptar cookies. Actívelas "
-"por favor, recargue esta página, e inténtelo de nuevo."
+", recargue esta página, e inténtelo de nuevo."
#: contrib/admin/views/decorators.py:89
#, python-format
@@ -937,7 +936,7 @@ msgstr "Ruta de fichero"
#: contrib/admin/views/doc.py:303
msgid "Floating point number"
-msgstr "Número decimal"
+msgstr "Número en coma flotante"
#: contrib/admin/views/doc.py:307 contrib/comments/models.py:89
msgid "IP address"
@@ -987,17 +986,17 @@ msgstr "Sitio administrativo"
#: contrib/admin/views/main.py:280 contrib/admin/views/main.py:365
#, python-format
msgid "You may add another %s below."
-msgstr "Puede agregar otro %s abajo."
+msgstr "Puede añadir otro %s abajo."
#: contrib/admin/views/main.py:298
#, python-format
msgid "Add %s"
-msgstr "Agregar %s"
+msgstr "Añadir %s"
#: contrib/admin/views/main.py:344
#, python-format
msgid "Added %s."
-msgstr "Agregado %s."
+msgstr "Añadido %s."
#: contrib/admin/views/main.py:344 contrib/admin/views/main.py:346
#: contrib/admin/views/main.py:348 core/validators.py:283
@@ -1029,7 +1028,7 @@ msgstr "Se modificó con éxito el %(name)s \"%(obj)s\"."
msgid ""
"The %(name)s \"%(obj)s\" was added successfully. You may edit it again below."
msgstr ""
-"Se agregó con éxito el %(name)s \"%(obj)s. Puede editarlo de nuevo abajo."
+"Se añadió con éxito el %(name)s \"%(obj)s. Puede editarlo de nuevo abajo."
#: contrib/admin/views/main.py:400
#, python-format
@@ -1087,7 +1086,7 @@ msgid ""
"Your Web browser doesn't appear to have cookies enabled. Cookies are "
"required for logging in."
msgstr ""
-"Tu navegador de internet parece no tener las cookies habilitadas. Las "
+"Su navegador no parece tener las cookies habilitadas. Las "
"cookies se necesitan para poder ingresar."
#: contrib/auth/forms.py:62
@@ -1105,17 +1104,17 @@ msgstr ""
#: contrib/auth/forms.py:107
#, python-format
msgid "Password reset on %s"
-msgstr "Clave restablecida en %s"
+msgstr "Contraseña restablecida en %s"
#: contrib/auth/forms.py:117
msgid "The two 'new password' fields didn't match."
msgstr ""
-"Las contraseñas introducidas en los campos 'nueva contraseña' no coinciden."
+"Los dos campos 'nueva contraseña' no coinciden."
#: contrib/auth/forms.py:124
msgid "Your old password was entered incorrectly. Please enter it again."
msgstr ""
-"Tu contraseña antigua es incorrecta. Por favor, vuelve a introducirla "
+"Tu contraseña antigua es incorrecta. Por favor, vuelva a introducirla "
"correctamente."
#: contrib/auth/models.py:73 contrib/auth/models.py:93
@@ -1138,15 +1137,15 @@ msgstr "permisos"
msgid "group"
msgstr "grupo"
-#: contrib/auth/models.py:98 contrib/auth/models.py:141
+#: contrib/auth/models.py:98 contrib/auth/models.py:148
msgid "groups"
msgstr "grupos"
-#: contrib/auth/models.py:131
+#: contrib/auth/models.py:138
msgid "username"
msgstr "nombre de usuario"
-#: contrib/auth/models.py:131
+#: contrib/auth/models.py:138
msgid ""
"Required. 30 characters or fewer. Alphanumeric characters only (letters, "
"digits and underscores)."
@@ -1154,23 +1153,23 @@ msgstr ""
"Requerido. 30 caracteres o menos. Sólo caracteres alfanuméricos (letras, "
"dígitos y guiones bajos)."
-#: contrib/auth/models.py:132
+#: contrib/auth/models.py:139
msgid "first name"
-msgstr "nombre"
+msgstr "nombre propio"
-#: contrib/auth/models.py:133
+#: contrib/auth/models.py:140
msgid "last name"
msgstr "apellidos"
-#: contrib/auth/models.py:134
+#: contrib/auth/models.py:141
msgid "e-mail address"
-msgstr "dirección de correo"
+msgstr "dirección de correo electrónico"
-#: contrib/auth/models.py:135
+#: contrib/auth/models.py:142
msgid "password"
-msgstr "clave"
+msgstr "contraseña"
-#: contrib/auth/models.py:135
+#: contrib/auth/models.py:142
msgid ""
"Use '[algo]$[salt]$[hexdigest]' or use the change "
"password form."
@@ -1178,32 +1177,31 @@ msgstr ""
"Use'[algo]$[sal]$[hash hexadecimal]' o use el "
"formulario para cambiar la contraseña."
-#: contrib/auth/models.py:136
+#: contrib/auth/models.py:143
msgid "staff status"
msgstr "es staff"
-#: contrib/auth/models.py:136
+#: contrib/auth/models.py:143
msgid "Designates whether the user can log into this admin site."
msgstr "Indica si el usuario puede entrar en este sitio de administración."
-#: contrib/auth/models.py:137
+#: contrib/auth/models.py:144
msgid "active"
msgstr "activo"
-#: contrib/auth/models.py:137
-#, fuzzy
+#: contrib/auth/models.py:144
msgid ""
"Designates whether this user should be treated as active. Unselect this "
"instead of deleting accounts."
msgstr ""
-"Indica si el usuario puede entrar en este sitio de administración. Desmarque "
-"esto en lugar de borrar la cuenta."
+"Indica si el usuario puede ser tratado como activo. Desmarque "
+"esta opción en lugar de borrar la cuenta."
-#: contrib/auth/models.py:138
+#: contrib/auth/models.py:145
msgid "superuser status"
msgstr "es superusuario"
-#: contrib/auth/models.py:138
+#: contrib/auth/models.py:145
msgid ""
"Designates that this user has all permissions without explicitly assigning "
"them."
@@ -1211,15 +1209,15 @@ msgstr ""
"Indica que este usuario tiene todos los permisos sin asignárselos "
"explícitamente."
-#: contrib/auth/models.py:139
+#: contrib/auth/models.py:146
msgid "last login"
-msgstr "Último registro"
+msgstr "Último inicio de sesión"
-#: contrib/auth/models.py:140
+#: contrib/auth/models.py:147
msgid "date joined"
-msgstr "fecha de creación"
+msgstr "fecha de alta"
-#: contrib/auth/models.py:142
+#: contrib/auth/models.py:149
msgid ""
"In addition to the permissions manually assigned, this user will also get "
"all permissions granted to each group he/she is in."
@@ -1227,35 +1225,35 @@ msgstr ""
"Además de los permisos asignados manualmente, este usuario también tendrá "
"todos los permisos de los grupos en los que esté."
-#: contrib/auth/models.py:143
+#: contrib/auth/models.py:150
msgid "user permissions"
-msgstr "permisos"
+msgstr "permisos de usuario"
-#: contrib/auth/models.py:147
+#: contrib/auth/models.py:154
msgid "user"
msgstr "usuario"
-#: contrib/auth/models.py:148
+#: contrib/auth/models.py:155
msgid "users"
msgstr "usuarios"
-#: contrib/auth/models.py:154
+#: contrib/auth/models.py:161
msgid "Personal info"
msgstr "Información personal"
-#: contrib/auth/models.py:155
+#: contrib/auth/models.py:162
msgid "Permissions"
msgstr "Permisos"
-#: contrib/auth/models.py:156
+#: contrib/auth/models.py:163
msgid "Important dates"
msgstr "Fechas importantes"
-#: contrib/auth/models.py:157
+#: contrib/auth/models.py:164
msgid "Groups"
msgstr "Grupos"
-#: contrib/auth/models.py:316
+#: contrib/auth/models.py:323
msgid "message"
msgstr "mensaje"
@@ -1278,39 +1276,39 @@ msgstr "comentario"
#: contrib/comments/models.py:74
msgid "rating #1"
-msgstr "calificación 1"
+msgstr "puntuación 1"
#: contrib/comments/models.py:75
msgid "rating #2"
-msgstr "calificación 2"
+msgstr "puntuación 2"
#: contrib/comments/models.py:76
msgid "rating #3"
-msgstr "calificación 3"
+msgstr "puntuación 3"
#: contrib/comments/models.py:77
msgid "rating #4"
-msgstr "calificación 4"
+msgstr "puntuación 4"
#: contrib/comments/models.py:78
msgid "rating #5"
-msgstr "calificación 5"
+msgstr "puntuación 5"
#: contrib/comments/models.py:79
msgid "rating #6"
-msgstr "calificación 6"
+msgstr "puntuación 6"
#: contrib/comments/models.py:80
msgid "rating #7"
-msgstr "calificación 7"
+msgstr "puntuación 7"
#: contrib/comments/models.py:81
msgid "rating #8"
-msgstr "calificación 8"
+msgstr "puntuación 8"
#: contrib/comments/models.py:86
msgid "is valid rating"
-msgstr "es calificación válida"
+msgstr "puntuación válida"
#: contrib/comments/models.py:87 contrib/comments/models.py:179
msgid "date/time submitted"
@@ -1329,8 +1327,8 @@ msgid ""
"Check this box if the comment is inappropriate. A \"This comment has been "
"removed\" message will be displayed instead."
msgstr ""
-"Marque esta caja si el comentario es inapropiado. En su lugar se mostrará "
-"\"Este comentario ha sido eliminado\"."
+"Marque esta opción si el comentario es inapropiado. En su lugar se mostrará "
+"el mensaje \"Este comentario ha sido eliminado\"."
#: contrib/comments/models.py:96
msgid "comments"
@@ -1426,15 +1424,15 @@ msgstr "Marca de %r"
#: contrib/comments/models.py:300
msgid "deletion date"
-msgstr "fecha de eliminación"
+msgstr "fecha de borrado"
#: contrib/comments/models.py:303
msgid "moderator deletion"
-msgstr "eliminación de moderador"
+msgstr "borrado del moderador"
#: contrib/comments/models.py:304
msgid "moderator deletions"
-msgstr "eliminaciones de moderador"
+msgstr "eliminaciones del moderador"
#: contrib/comments/models.py:308
#, python-format
@@ -1447,7 +1445,7 @@ msgstr "¿Has olvidado tu contraseña?"
#: contrib/comments/templates/comments/form.html:12
msgid "Ratings"
-msgstr "Calificaciones"
+msgstr "Puntuaciones"
#: contrib/comments/templates/comments/form.html:12
#: contrib/comments/templates/comments/form.html:23
@@ -1461,7 +1459,7 @@ msgstr "Opcional"
#: contrib/comments/templates/comments/form.html:23
msgid "Post a photo"
-msgstr "Postea una fotografía"
+msgstr "Publica una fotografía"
#: contrib/comments/templates/comments/form.html:28
#: contrib/comments/templates/comments/freeform.html:5
@@ -1519,7 +1517,7 @@ msgstr ""
#: contrib/comments/views/comments.py:190
#: contrib/comments/views/comments.py:283
msgid "Only POSTs are allowed"
-msgstr "Sólo se admite POST"
+msgstr "Sólo se admiten POSTs"
#: contrib/comments/views/comments.py:194
#: contrib/comments/views/comments.py:287
@@ -1530,7 +1528,7 @@ msgstr "No se proporcionó uno o más de los siguientes campos requeridos"
#: contrib/comments/views/comments.py:289
msgid "Somebody tampered with the comment form (security violation)"
msgstr ""
-"Alguien está jugando con el formulario de comentarios (violación de "
+"Alguien realizó manipulaciones con el formulario de comentarios (violación de "
"seguridad)"
#: contrib/comments/views/comments.py:208
@@ -1557,11 +1555,11 @@ msgstr "ID de comentario no válido"
#: contrib/comments/views/karma.py:27
msgid "No voting for yourself"
-msgstr "No puedes votarte tú mismo"
+msgstr "No puedes votarte a ti mismo"
#: contrib/contenttypes/models.py:67
msgid "python model class name"
-msgstr "nombre de módulo python"
+msgstr "nombre de la clase modelo de python"
#: contrib/contenttypes/models.py:71
msgid "content type"
@@ -1588,7 +1586,7 @@ msgstr "contenido"
#: contrib/flatpages/models.py:12
msgid "enable comments"
-msgstr "admitir comentarios"
+msgstr "habilitar comentarios"
#: contrib/flatpages/models.py:13
msgid "template name"
@@ -1624,19 +1622,19 @@ msgstr "Opciones avanzadas"
#: contrib/humanize/templatetags/humanize.py:19
msgid "th"
-msgstr "th"
+msgstr "º"
#: contrib/humanize/templatetags/humanize.py:19
msgid "st"
-msgstr "st"
+msgstr "º"
#: contrib/humanize/templatetags/humanize.py:19
msgid "nd"
-msgstr "nd"
+msgstr "º"
#: contrib/humanize/templatetags/humanize.py:19
msgid "rd"
-msgstr "rd"
+msgstr "º"
#: contrib/humanize/templatetags/humanize.py:51
#, python-format
@@ -1649,15 +1647,15 @@ msgstr[1] "%(value).1f millión"
#, python-format
msgid "%(value).1f billion"
msgid_plural "%(value).1f billion"
-msgstr[0] "%(value).1f billión"
-msgstr[1] "%(value).1f billión"
+msgstr[0] "%(value).1f billón"
+msgstr[1] "%(value).1f billón"
#: contrib/humanize/templatetags/humanize.py:57
#, python-format
msgid "%(value).1f trillion"
msgid_plural "%(value).1f trillion"
-msgstr[0] "%(value).1f trillión"
-msgstr[1] "%(value).1f trillión"
+msgstr[0] "%(value).1f trillón"
+msgstr[1] "%(value).1f trillón"
#: contrib/humanize/templatetags/humanize.py:73
msgid "one"
@@ -3507,7 +3505,7 @@ msgid ""
"This should be an absolute path, excluding the domain name. Example: '/"
"events/search/'."
msgstr ""
-"Esta ruta debería ser absoluta, excluyendo el nombre de dominio. Ejeplo: '/"
+"Esta ruta debería ser absoluta, excluyendo el nombre de dominio. Ejemplo: '/"
"events/search/'."
#: contrib/redirects/models.py:9
@@ -3575,7 +3573,7 @@ msgid ""
"This value must contain only letters, numbers, underscores, dashes or "
"slashes."
msgstr ""
-"Este valor debe contener letras, números, guiones bajos o barras solamente."
+"Este valor debe contener sólo letras, números, guiones bajos y barras."
#: core/validators.py:80
msgid "This value must contain only letters, numbers, underscores or hyphens."
@@ -3830,7 +3828,7 @@ msgstr "Este campo no es válido."
#: core/validators.py:536
#, python-format
msgid "Could not retrieve anything from %s."
-msgstr "No pude obtener nada de %s."
+msgstr "No se pudo obtener nada de %s."
#: core/validators.py:539
#, python-format
@@ -3932,25 +3930,25 @@ msgstr "Introduzca un nombre de fichero válido"
#: db/models/fields/__init__.py:981
msgid "This value must be either None, True or False."
-msgstr "Este valor debe ser Verdadero o Falso."
+msgstr "Este valor debe ser Verdadero, Falso o Ninguno."
#: db/models/fields/related.py:94
#, python-format
msgid "Please enter a valid %s."
msgstr "Por favor, introduzca un %s válido."
-#: db/models/fields/related.py:721
+#: db/models/fields/related.py:746
msgid "Separate multiple IDs with commas."
msgstr "Separe múltiples IDs con comas."
-#: db/models/fields/related.py:723
+#: db/models/fields/related.py:748
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 uno."
+"más de una opción."
-#: db/models/fields/related.py:770
+#: db/models/fields/related.py:795
#, python-format
msgid "Please enter valid %(self)s IDs. The value %(value)r is invalid."
msgid_plural ""
@@ -4405,16 +4403,5 @@ msgstr "Se actualizó con éxito el %(verbose_name)s."
#: views/generic/create_update.py:184
#, python-format
msgid "The %(verbose_name)s was deleted."
-msgstr "El %(verbose_name)s ha sido eliminado."
+msgstr "El %(verbose_name)s ha sido borrado."
-#~ msgid "Brazilian"
-#~ msgstr "Brasileño"
-
-#~ msgid "Gaeilge"
-#~ msgstr "Gaeilge"
-
-#~ msgid ""
-#~ "Enter a postcode. A space is required between the two postcode parts."
-#~ msgstr ""
-#~ "Introduzca un código postal. Se necesita un espacio entre las dos partes "
-#~ "del código."
diff --git a/django/contrib/auth/create_superuser.py b/django/contrib/auth/create_superuser.py
index 7b6cefd268..7b58678b78 100644
--- a/django/contrib/auth/create_superuser.py
+++ b/django/contrib/auth/create_superuser.py
@@ -1,94 +1,8 @@
"""
-Helper function for creating superusers in the authentication system.
-
-If run from the command line, this module lets you create a superuser
-interactively.
+Create a superuser from the command line. Deprecated; use manage.py
+createsuperuser instead.
"""
-from django.core import validators
-from django.contrib.auth.models import User
-import getpass
-import os
-import sys
-import re
-
-RE_VALID_USERNAME = re.compile('\w+$')
-
-def createsuperuser(username=None, email=None, password=None):
- """
- Helper function for creating a superuser from the command line. All
- arguments are optional and will be prompted-for if invalid or not given.
- """
- try:
- import pwd
- except ImportError:
- default_username = ''
- else:
- # Determine the current system user's username, to use as a default.
- default_username = pwd.getpwuid(os.getuid())[0].replace(' ', '').lower()
-
- # Determine whether the default username is taken, so we don't display
- # it as an option.
- if default_username:
- try:
- User.objects.get(username=default_username)
- except User.DoesNotExist:
- pass
- else:
- default_username = ''
-
- try:
- while 1:
- if not username:
- input_msg = 'Username'
- if default_username:
- input_msg += ' (Leave blank to use %r)' % default_username
- username = raw_input(input_msg + ': ')
- if default_username and username == '':
- username = default_username
- if not RE_VALID_USERNAME.match(username):
- sys.stderr.write("Error: That username is invalid. Use only letters, digits and underscores.\n")
- username = None
- continue
- try:
- User.objects.get(username=username)
- except User.DoesNotExist:
- break
- else:
- sys.stderr.write("Error: That username is already taken.\n")
- username = None
- while 1:
- if not email:
- email = raw_input('E-mail address: ')
- try:
- validators.isValidEmail(email, None)
- except validators.ValidationError:
- sys.stderr.write("Error: That e-mail address is invalid.\n")
- email = None
- else:
- break
- while 1:
- if not password:
- password = getpass.getpass()
- password2 = getpass.getpass('Password (again): ')
- if password != password2:
- sys.stderr.write("Error: Your passwords didn't match.\n")
- password = None
- continue
- if password.strip() == '':
- sys.stderr.write("Error: Blank passwords aren't allowed.\n")
- password = None
- continue
- break
- except KeyboardInterrupt:
- sys.stderr.write("\nOperation cancelled.\n")
- sys.exit(1)
- u = User.objects.create_user(username, email, password)
- u.is_staff = True
- u.is_active = True
- u.is_superuser = True
- u.save()
- print "Superuser created successfully."
-
if __name__ == "__main__":
- createsuperuser()
+ from django.core.management import call_command
+ call_command("createsuperuser")
diff --git a/django/contrib/auth/management.py b/django/contrib/auth/management/__init__.py
similarity index 78%
rename from django/contrib/auth/management.py
rename to django/contrib/auth/management/__init__.py
index 2b4cb8bd19..8394bee5cd 100644
--- a/django/contrib/auth/management.py
+++ b/django/contrib/auth/management/__init__.py
@@ -32,7 +32,7 @@ def create_permissions(app, created_models, verbosity):
def create_superuser(app, created_models, verbosity, **kwargs):
from django.contrib.auth.models import User
- from django.contrib.auth.create_superuser import createsuperuser as do_create
+ from django.core.management import call_command
if User in created_models and kwargs.get('interactive', True):
msg = "\nYou just installed Django's auth system, which means you don't have " \
"any superusers defined.\nWould you like to create one now? (yes/no): "
@@ -42,8 +42,10 @@ def create_superuser(app, created_models, verbosity, **kwargs):
confirm = raw_input('Please enter either "yes" or "no": ')
continue
if confirm == 'yes':
- do_create()
+ call_command("createsuperuser", interactive=True)
break
-dispatcher.connect(create_permissions, signal=signals.post_syncdb)
-dispatcher.connect(create_superuser, sender=auth_app, signal=signals.post_syncdb)
+if 'create_permissions' not in [i.__name__ for i in dispatcher.getAllReceivers(signal=signals.post_syncdb)]:
+ dispatcher.connect(create_permissions, signal=signals.post_syncdb)
+if 'create_superuser' not in [i.__name__ for i in dispatcher.getAllReceivers(signal=signals.post_syncdb, sender=auth_app)]:
+ dispatcher.connect(create_superuser, sender=auth_app, signal=signals.post_syncdb)
\ No newline at end of file
diff --git a/django/contrib/auth/management/commands/__init__.py b/django/contrib/auth/management/commands/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/django/contrib/auth/management/commands/createsuperuser.py b/django/contrib/auth/management/commands/createsuperuser.py
new file mode 100644
index 0000000000..4299762c74
--- /dev/null
+++ b/django/contrib/auth/management/commands/createsuperuser.py
@@ -0,0 +1,123 @@
+"""
+Management utility to create superusers.
+"""
+
+import getpass
+import os
+import re
+import sys
+from optparse import make_option
+from django.contrib.auth.models import User, UNUSABLE_PASSWORD
+from django.core import validators
+from django.core.management.base import BaseCommand, CommandError
+
+RE_VALID_USERNAME = re.compile('\w+$')
+
+class Command(BaseCommand):
+ option_list = BaseCommand.option_list + (
+ make_option('--username', dest='username', default=None,
+ help='Specifies the username for the superuser.'),
+ make_option('--email', dest='email', default=None,
+ help='Specifies the email address for the superuser.'),
+ make_option('--noinput', action='store_false', dest='interactive', default=True,
+ help='Tells Django to NOT prompt the user for input of any kind. ' \
+ 'You must use --username and --email with --noinput, and ' \
+ 'superusers created with --noinput will not be able to log in ' \
+ 'until they\'re given a valid password.'),
+ )
+ help = 'Used to create a superuser.'
+
+ def handle(self, *args, **options):
+ username = options.get('username', None)
+ email = options.get('email', None)
+ interactive = options.get('interactive')
+
+ # Do quick and dirty validation if --noinput
+ if not interactive:
+ if not username or not email:
+ raise CommandError("You must use --username and --email with --noinput.")
+ if not RE_VALID_USERNAME.match(username):
+ raise CommandError("Invalid username. Use only letters, digits, and underscores")
+ try:
+ validators.isValidEmail(email, None)
+ except validators.ValidationError:
+ raise CommandError("Invalid email address.")
+
+ password = ''
+
+ # Try to determine the current system user's username to use as a default.
+ try:
+ import pwd
+ except ImportError:
+ default_username = ''
+ else:
+ default_username = pwd.getpwuid(os.getuid())[0].replace(' ', '').lower()
+
+ # Determine whether the default username is taken, so we don't display
+ # it as an option.
+ if default_username:
+ try:
+ User.objects.get(username=default_username)
+ except User.DoesNotExist:
+ pass
+ else:
+ default_username = ''
+
+ # Prompt for username/email/password. Enclose this whole thing in a
+ # try/except to trap for a keyboard interrupt and exit gracefully.
+ if interactive:
+ try:
+
+ # Get a username
+ while 1:
+ if not username:
+ input_msg = 'Username'
+ if default_username:
+ input_msg += ' (Leave blank to use %r)' % default_username
+ username = raw_input(input_msg + ': ')
+ if default_username and username == '':
+ username = default_username
+ if not RE_VALID_USERNAME.match(username):
+ sys.stderr.write("Error: That username is invalid. Use only letters, digits and underscores.\n")
+ username = None
+ continue
+ try:
+ User.objects.get(username=username)
+ except User.DoesNotExist:
+ break
+ else:
+ sys.stderr.write("Error: That username is already taken.\n")
+ username = None
+
+ # Get an email
+ while 1:
+ if not email:
+ email = raw_input('E-mail address: ')
+ try:
+ validators.isValidEmail(email, None)
+ except validators.ValidationError:
+ sys.stderr.write("Error: That e-mail address is invalid.\n")
+ email = None
+ else:
+ break
+
+ # Get a password
+ while 1:
+ if not password:
+ password = getpass.getpass()
+ password2 = getpass.getpass('Password (again): ')
+ if password != password2:
+ sys.stderr.write("Error: Your passwords didn't match.\n")
+ password = None
+ continue
+ if password.strip() == '':
+ sys.stderr.write("Error: Blank passwords aren't allowed.\n")
+ password = None
+ continue
+ break
+ except KeyboardInterrupt:
+ sys.stderr.write("\nOperation cancelled.\n")
+ sys.exit(1)
+
+ User.objects.create_superuser(username, email, password)
+ print "Superuser created successfully."
diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py
index c8670655d4..f74d1d7761 100644
--- a/django/contrib/auth/models.py
+++ b/django/contrib/auth/models.py
@@ -116,6 +116,13 @@ class UserManager(models.Manager):
user.save()
return user
+ def create_superuser(self, username, email, password):
+ u = self.create_user(username, email, password)
+ u.is_staff = True
+ u.is_active = True
+ u.is_superuser = True
+ u.save()
+
def make_random_password(self, length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'):
"Generates a random password with the given length and given allowed_chars"
# Note that default value of allowed_chars does not have "I" or letters
diff --git a/django/contrib/auth/tests.py b/django/contrib/auth/tests.py
index d369ac524c..81094ca85f 100644
--- a/django/contrib/auth/tests.py
+++ b/django/contrib/auth/tests.py
@@ -35,4 +35,21 @@ False
[]
>>> a.user_permissions.all()
[]
+
+#
+# Tests for createsuperuser management command.
+# It's nearly impossible to test the interactive mode -- a command test helper
+# would be needed (and *awesome*) -- so just test the non-interactive mode.
+# This covers most of the important validation, but not all.
+#
+>>> from django.core.management import call_command
+
+>>> call_command("createsuperuser", noinput=True, username="joe", email="joe@somewhere.org")
+Superuser created successfully.
+
+>>> u = User.objects.get(username="joe")
+>>> u.email
+u'joe@somewhere.org'
+>>> u.password
+u'!'
"""
\ No newline at end of file
diff --git a/django/contrib/sessions/backends/base.py b/django/contrib/sessions/backends/base.py
index b8726fd2bd..1063760915 100644
--- a/django/contrib/sessions/backends/base.py
+++ b/django/contrib/sessions/backends/base.py
@@ -4,6 +4,7 @@ import os
import random
import sys
import time
+from datetime import datetime, timedelta
from django.conf import settings
from django.core.exceptions import SuspiciousOperation
@@ -128,6 +129,62 @@ class SessionBase(object):
_session = property(_get_session)
+ def get_expiry_age(self):
+ """Get the number of seconds until the session expires."""
+ expiry = self.get('_session_expiry')
+ if not expiry: # Checks both None and 0 cases
+ return settings.SESSION_COOKIE_AGE
+ if not isinstance(expiry, datetime):
+ return expiry
+ delta = expiry - datetime.now()
+ return delta.days * 86400 + delta.seconds
+
+ def get_expiry_date(self):
+ """Get session the expiry date (as a datetime object)."""
+ expiry = self.get('_session_expiry')
+ if isinstance(expiry, datetime):
+ return expiry
+ if not expiry: # Checks both None and 0 cases
+ expiry = settings.SESSION_COOKIE_AGE
+ return datetime.now() + timedelta(seconds=expiry)
+
+ def set_expiry(self, value):
+ """
+ Sets a custom expiration for the session. ``value`` can be an integer, a
+ Python ``datetime`` or ``timedelta`` object or ``None``.
+
+ If ``value`` is an integer, the session will expire after that many
+ seconds of inactivity. If set to ``0`` then the session will expire on
+ browser close.
+
+ If ``value`` is a ``datetime`` or ``timedelta`` object, the session
+ will expire at that specific future time.
+
+ If ``value`` is ``None``, the session uses the global session expiry
+ policy.
+ """
+ if value is None:
+ # Remove any custom expiration for this session.
+ try:
+ del self['_session_expiry']
+ except KeyError:
+ pass
+ return
+ if isinstance(value, timedelta):
+ value = datetime.now() + value
+ self['_session_expiry'] = value
+
+ def get_expire_at_browser_close(self):
+ """
+ Returns ``True`` if the session is set to expire when the browser
+ closes, and ``False`` if there's an expiry date. Use
+ ``get_expiry_date()`` or ``get_expiry_age()`` to find the actual expiry
+ date/age, if there is one.
+ """
+ if self.get('_session_expiry') is None:
+ return settings.SESSION_EXPIRE_AT_BROWSER_CLOSE
+ return self.get('_session_expiry') == 0
+
# Methods that child classes must implement.
def exists(self, session_key):
diff --git a/django/contrib/sessions/backends/cache.py b/django/contrib/sessions/backends/cache.py
index c3e641e691..7626163a13 100644
--- a/django/contrib/sessions/backends/cache.py
+++ b/django/contrib/sessions/backends/cache.py
@@ -4,23 +4,23 @@ from django.core.cache import cache
class SessionStore(SessionBase):
"""
- A cache-based session store.
+ A cache-based session store.
"""
def __init__(self, session_key=None):
self._cache = cache
super(SessionStore, self).__init__(session_key)
-
+
def load(self):
session_data = self._cache.get(self.session_key)
return session_data or {}
def save(self):
- self._cache.set(self.session_key, self._session, settings.SESSION_COOKIE_AGE)
+ self._cache.set(self.session_key, self._session, self.get_expiry_age())
def exists(self, session_key):
if self._cache.get(session_key):
return True
return False
-
+
def delete(self, session_key):
self._cache.delete(session_key)
\ No newline at end of file
diff --git a/django/contrib/sessions/backends/db.py b/django/contrib/sessions/backends/db.py
index 0f79d9ee1a..b1c1097865 100644
--- a/django/contrib/sessions/backends/db.py
+++ b/django/contrib/sessions/backends/db.py
@@ -41,7 +41,7 @@ class SessionStore(SessionBase):
Session.objects.create(
session_key = self.session_key,
session_data = self.encode(self._session),
- expire_date = datetime.datetime.now() + datetime.timedelta(seconds=settings.SESSION_COOKIE_AGE)
+ expire_date = self.get_expiry_date()
)
def delete(self, session_key):
diff --git a/django/contrib/sessions/middleware.py b/django/contrib/sessions/middleware.py
index 2af2312e76..a7b376dde0 100644
--- a/django/contrib/sessions/middleware.py
+++ b/django/contrib/sessions/middleware.py
@@ -26,14 +26,14 @@ class SessionMiddleware(object):
if accessed:
patch_vary_headers(response, ('Cookie',))
if modified or settings.SESSION_SAVE_EVERY_REQUEST:
- if settings.SESSION_EXPIRE_AT_BROWSER_CLOSE:
+ if request.session.get_expire_at_browser_close():
max_age = None
expires = None
else:
- max_age = settings.SESSION_COOKIE_AGE
- expires_time = time.time() + settings.SESSION_COOKIE_AGE
+ max_age = request.session.get_expiry_age()
+ expires_time = time.time() + max_age
expires = cookie_date(expires_time)
- # Save the seesion data and refresh the client cookie.
+ # Save the session data and refresh the client cookie.
request.session.save()
response.set_cookie(settings.SESSION_COOKIE_NAME,
request.session.session_key, max_age=max_age,
diff --git a/django/contrib/sessions/tests.py b/django/contrib/sessions/tests.py
index b2c664ce7b..0f162b211f 100644
--- a/django/contrib/sessions/tests.py
+++ b/django/contrib/sessions/tests.py
@@ -88,6 +88,100 @@ False
>>> s.pop('some key', 'does not exist')
'does not exist'
+
+#########################
+# Custom session expiry #
+#########################
+
+>>> from django.conf import settings
+>>> from datetime import datetime, timedelta
+
+>>> td10 = timedelta(seconds=10)
+
+# A normal session has a max age equal to settings
+>>> s.get_expiry_age() == settings.SESSION_COOKIE_AGE
+True
+
+# So does a custom session with an idle expiration time of 0 (but it'll expire
+# at browser close)
+>>> s.set_expiry(0)
+>>> s.get_expiry_age() == settings.SESSION_COOKIE_AGE
+True
+
+# Custom session idle expiration time
+>>> s.set_expiry(10)
+>>> delta = s.get_expiry_date() - datetime.now()
+>>> delta.seconds in (9, 10)
+True
+>>> age = s.get_expiry_age()
+>>> age in (9, 10)
+True
+
+# Custom session fixed expiry date (timedelta)
+>>> s.set_expiry(td10)
+>>> delta = s.get_expiry_date() - datetime.now()
+>>> delta.seconds in (9, 10)
+True
+>>> age = s.get_expiry_age()
+>>> age in (9, 10)
+True
+
+# Custom session fixed expiry date (fixed datetime)
+>>> s.set_expiry(datetime.now() + td10)
+>>> delta = s.get_expiry_date() - datetime.now()
+>>> delta.seconds in (9, 10)
+True
+>>> age = s.get_expiry_age()
+>>> age in (9, 10)
+True
+
+# Set back to default session age
+>>> s.set_expiry(None)
+>>> s.get_expiry_age() == settings.SESSION_COOKIE_AGE
+True
+
+# Allow to set back to default session age even if no alternate has been set
+>>> s.set_expiry(None)
+
+
+# We're changing the setting then reverting back to the original setting at the
+# end of these tests.
+>>> original_expire_at_browser_close = settings.SESSION_EXPIRE_AT_BROWSER_CLOSE
+>>> settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = False
+
+# Custom session age
+>>> s.set_expiry(10)
+>>> s.get_expire_at_browser_close()
+False
+
+# Custom expire-at-browser-close
+>>> s.set_expiry(0)
+>>> s.get_expire_at_browser_close()
+True
+
+# Default session age
+>>> s.set_expiry(None)
+>>> s.get_expire_at_browser_close()
+False
+
+>>> settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = True
+
+# Custom session age
+>>> s.set_expiry(10)
+>>> s.get_expire_at_browser_close()
+False
+
+# Custom expire-at-browser-close
+>>> s.set_expiry(0)
+>>> s.get_expire_at_browser_close()
+True
+
+# Default session age
+>>> s.set_expiry(None)
+>>> s.get_expire_at_browser_close()
+True
+
+>>> settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = original_expire_at_browser_close
"""
if __name__ == '__main__':
diff --git a/django/core/management/commands/dumpdata.py b/django/core/management/commands/dumpdata.py
index 2642ae925e..2559d57104 100644
--- a/django/core/management/commands/dumpdata.py
+++ b/django/core/management/commands/dumpdata.py
@@ -9,6 +9,8 @@ class Command(BaseCommand):
help='Specifies the output serialization format for fixtures.'),
make_option('--indent', default=None, dest='indent', type='int',
help='Specifies the indent level to use when pretty-printing output'),
+ make_option('-e', '--exclude', dest='exclude',action='append', default=[],
+ help='App to exclude (use multiple --exclude to exclude multiple apps).'),
)
help = 'Output the contents of the database as a fixture of the given format.'
args = '[appname ...]'
@@ -16,12 +18,15 @@ class Command(BaseCommand):
def handle(self, *app_labels, **options):
from django.db.models import get_app, get_apps, get_models
- format = options.get('format', 'json')
- indent = options.get('indent', None)
+ format = options.get('format','json')
+ indent = options.get('indent',None)
+ exclude = options.get('exclude',[])
show_traceback = options.get('traceback', False)
+ excluded_apps = [get_app(app_label) for app_label in exclude]
+
if len(app_labels) == 0:
- app_list = get_apps()
+ app_list = [app for app in get_apps() if app not in excluded_apps]
else:
app_list = [get_app(app_label) for app_label in app_labels]
diff --git a/django/core/management/commands/loaddata.py b/django/core/management/commands/loaddata.py
index d06b131d6f..193bb26ccf 100644
--- a/django/core/management/commands/loaddata.py
+++ b/django/core/management/commands/loaddata.py
@@ -32,6 +32,7 @@ class Command(BaseCommand):
# Keep a count of the installed objects and fixtures
fixture_count = 0
object_count = 0
+ objects_per_fixture = []
models = set()
humanize = lambda dirname: dirname and "'%s'" % dirname or 'absolute path'
@@ -60,11 +61,16 @@ class Command(BaseCommand):
else:
formats = []
- if verbosity >= 2:
- if formats:
+ if formats:
+ if verbosity > 1:
print "Loading '%s' fixtures..." % fixture_name
- else:
- print "Skipping fixture '%s': %s is not a known serialization format" % (fixture_name, format)
+ else:
+ sys.stderr.write(
+ self.style.ERROR("Problem installing fixture '%s': %s is not a known serialization format." %
+ (fixture_name, format)))
+ transaction.rollback()
+ transaction.leave_transaction_management()
+ return
if os.path.isabs(fixture_name):
fixture_dirs = [fixture_name]
@@ -93,6 +99,7 @@ class Command(BaseCommand):
return
else:
fixture_count += 1
+ objects_per_fixture.append(0)
if verbosity > 0:
print "Installing %s fixture '%s' from %s." % \
(format, fixture_name, humanize(fixture_dir))
@@ -100,6 +107,7 @@ class Command(BaseCommand):
objects = serializers.deserialize(format, fixture)
for obj in objects:
object_count += 1
+ objects_per_fixture[-1] += 1
models.add(obj.object.__class__)
obj.save()
label_found = True
@@ -117,10 +125,23 @@ class Command(BaseCommand):
return
fixture.close()
except:
- if verbosity >= 2:
+ if verbosity > 1:
print "No %s fixture '%s' in %s." % \
(format, fixture_name, humanize(fixture_dir))
+
+ # If any of the fixtures we loaded contain 0 objects, assume that an
+ # error was encountered during fixture loading.
+ if 0 in objects_per_fixture:
+ sys.stderr.write(
+ self.style.ERROR("No fixture data found for '%s'. (File format may be invalid.)" %
+ (fixture_name)))
+ transaction.rollback()
+ transaction.leave_transaction_management()
+ return
+
+ # If we found even one object in a fixture, we need to reset the
+ # database sequences.
if object_count > 0:
sequence_sql = connection.ops.sequence_reset_sql(self.style, models)
if sequence_sql:
@@ -128,12 +149,12 @@ class Command(BaseCommand):
print "Resetting sequences"
for line in sequence_sql:
cursor.execute(line)
-
+
transaction.commit()
transaction.leave_transaction_management()
if object_count == 0:
- if verbosity >= 2:
+ if verbosity > 1:
print "No fixtures found."
else:
if verbosity > 0:
diff --git a/django/core/management/commands/syncdb.py b/django/core/management/commands/syncdb.py
index ae92c053ca..dc0a72a2a6 100644
--- a/django/core/management/commands/syncdb.py
+++ b/django/core/management/commands/syncdb.py
@@ -34,7 +34,16 @@ class Command(NoArgsCommand):
try:
__import__(app_name + '.management', {}, {}, [''])
except ImportError, exc:
- if not exc.args[0].startswith('No module named management'):
+ # This is slightly hackish. We want to ignore ImportErrors
+ # if the "management" module itself is missing -- but we don't
+ # want to ignore the exception if the management module exists
+ # but raises an ImportError for some reason. The only way we
+ # can do this is to check the text of the exception. Note that
+ # we're a bit broad in how we check the text, because different
+ # Python implementations may not use the same text. CPython
+ # uses the text "No module named management".
+ msg = exc.args[0]
+ if not msg.startswith('No module named management') or 'management' not in msg:
raise
cursor = connection.cursor()
diff --git a/django/core/management/sql.py b/django/core/management/sql.py
index e7a54acb6f..dfe97cdd0c 100644
--- a/django/core/management/sql.py
+++ b/django/core/management/sql.py
@@ -454,7 +454,7 @@ def custom_sql_for_model(model, style):
fp = open(sql_file, 'U')
for statement in statements.split(fp.read().decode(settings.FILE_CHARSET)):
# Remove any comments from the file
- statement = re.sub(ur"--.*[\n\Z]", "", statement)
+ statement = re.sub(ur"--.*([\n\Z]|$)", "", statement)
if statement.strip():
output.append(statement + u";")
fp.close()
diff --git a/django/core/serializers/base.py b/django/core/serializers/base.py
index a79497ecec..e22a35815b 100644
--- a/django/core/serializers/base.py
+++ b/django/core/serializers/base.py
@@ -38,7 +38,7 @@ class Serializer(object):
self.start_serialization()
for obj in queryset:
self.start_object(obj)
- for field in obj._meta.fields:
+ for field in obj._meta.local_fields:
if field.serialize:
if field.rel is None:
if self.selected_fields is None or field.attname in self.selected_fields:
diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py
index 063c0ae411..b076e290dd 100644
--- a/django/db/backends/__init__.py
+++ b/django/db/backends/__init__.py
@@ -202,8 +202,8 @@ class BaseDatabaseOperations(object):
def query_class(self, DefaultQueryClass):
"""
- Given the default QuerySet class, returns a custom QuerySet class
- to use for this backend. Returns None if a custom QuerySet isn't used.
+ Given the default Query class, returns a custom Query class
+ to use for this backend. Returns None if a custom Query isn't used.
See also BaseDatabaseFeatures.uses_custom_query_class, which regulates
whether this method is called at all.
"""
diff --git a/django/db/models/base.py b/django/db/models/base.py
index 0ee225675a..5dd11a9d83 100644
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -290,12 +290,17 @@ class Model(object):
meta = cls._meta
signal = False
- for parent, field in meta.parents.items():
- self.save_base(raw, parent)
- setattr(self, field.attname, self._get_pk_val(parent._meta))
+ # If we are in a raw save, save the object exactly as presented.
+ # That means that we don't try to be smart about saving attributes
+ # that might have come from the parent class - we just save the
+ # attributes we have been given to the class we have been given.
+ if not raw:
+ for parent, field in meta.parents.items():
+ self.save_base(raw, parent)
+ setattr(self, field.attname, self._get_pk_val(parent._meta))
non_pks = [f for f in meta.local_fields if not f.primary_key]
-
+
# First, try an UPDATE. If that doesn't update anything, do an INSERT.
pk_val = self._get_pk_val(meta)
# Note: the comparison with '' is required for compatibility with
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index 3253ea18e6..c9deac428f 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -182,14 +182,29 @@ class SingleRelatedObjectDescriptor(object):
def __set__(self, instance, value):
if instance is None:
raise AttributeError, "%s must be accessed via instance" % self.related.opts.object_name
+
+ # The similarity of the code below to the code in
+ # ReverseSingleRelatedObjectDescriptor is annoying, but there's a bunch
+ # of small differences that would make a common base class convoluted.
+
+ # If null=True, we can assign null here, but otherwise the value needs
+ # to be an instance of the related class.
+ if value is None and self.related.field.null == False:
+ raise ValueError('Cannot assign None: "%s.%s" does not allow null values.' %
+ (instance._meta.object_name, self.related.get_accessor_name()))
+ elif value is not None and not isinstance(value, self.related.model):
+ raise ValueError('Cannot assign "%r": "%s.%s" must be a "%s" instance.' %
+ (value, instance._meta.object_name,
+ self.related.get_accessor_name(), self.related.opts.object_name))
+
# Set the value of the related field
setattr(value, self.related.field.rel.get_related_field().attname, instance)
- # Clear the cache, if it exists
- try:
- delattr(value, self.related.field.get_cache_name())
- except AttributeError:
- pass
+ # Since we already know what the related object is, seed the related
+ # object caches now, too. This avoids another db hit if you get the
+ # object you just set.
+ setattr(instance, self.cache_name, value)
+ setattr(value, self.related.field.get_cache_name(), instance)
class ReverseSingleRelatedObjectDescriptor(object):
# This class provides the functionality that makes the related-object
@@ -225,6 +240,17 @@ class ReverseSingleRelatedObjectDescriptor(object):
def __set__(self, instance, value):
if instance is None:
raise AttributeError, "%s must be accessed via instance" % self._field.name
+
+ # If null=True, we can assign null here, but otherwise the value needs
+ # to be an instance of the related class.
+ if value is None and self.field.null == False:
+ raise ValueError('Cannot assign None: "%s.%s" does not allow null values.' %
+ (instance._meta.object_name, self.field.name))
+ elif value is not None and not isinstance(value, self.field.rel.to):
+ raise ValueError('Cannot assign "%r": "%s.%s" must be a "%s" instance.' %
+ (value, instance._meta.object_name,
+ self.field.name, self.field.rel.to._meta.object_name))
+
# Set the value of the related field
try:
val = getattr(value, self.field.rel.get_related_field().attname)
@@ -232,11 +258,10 @@ class ReverseSingleRelatedObjectDescriptor(object):
val = None
setattr(instance, self.field.attname, val)
- # Clear the cache, if it exists
- try:
- delattr(instance, self.field.get_cache_name())
- except AttributeError:
- pass
+ # Since we already know what the related object is, seed the related
+ # object cache now, too. This avoids another db hit if you get the
+ # object you just set.
+ setattr(instance, self.field.get_cache_name(), value)
class ForeignRelatedObjectsDescriptor(object):
# This class provides the functionality that makes the related-object
@@ -326,7 +351,7 @@ def create_many_related_manager(superclass):
self.target_col_name = target_col_name
self._pk_val = self.instance._get_pk_val()
if self._pk_val is None:
- raise ValueError("%r instance needs to have a primary key value before a many-to-many relationship can be used." % model)
+ raise ValueError("%r instance needs to have a primary key value before a many-to-many relationship can be used." % instance.__class__.__name__)
def get_query_set(self):
return superclass.get_query_set(self).filter(**(self.core_filters))
diff --git a/django/db/models/options.py b/django/db/models/options.py
index 5802ead081..bc1aec62c1 100644
--- a/django/db/models/options.py
+++ b/django/db/models/options.py
@@ -56,8 +56,12 @@ class Options(object):
# Next, apply any overridden values from 'class Meta'.
if self.meta:
meta_attrs = self.meta.__dict__.copy()
- del meta_attrs['__module__']
- del meta_attrs['__doc__']
+ for name in self.meta.__dict__:
+ # Ignore any private attributes that Django doesn't care about.
+ # NOTE: We can't modify a dictionary's contents while looping
+ # over it, so we loop over the *original* dictionary instead.
+ if name.startswith('_'):
+ del meta_attrs[name]
for attr_name in DEFAULT_NAMES:
if attr_name in meta_attrs:
setattr(self, attr_name, meta_attrs.pop(attr_name))
@@ -98,7 +102,7 @@ class Options(object):
# field.
field = self.parents.value_for_index(0)
field.primary_key = True
- self.pk = field
+ self.setup_pk(field)
else:
auto = AutoField(verbose_name='ID', primary_key=True,
auto_created=True)
diff --git a/django/db/models/query.py b/django/db/models/query.py
index 6b341ba9ab..fb6d116a6e 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -292,6 +292,8 @@ class QuerySet(object):
Updates all elements in the current QuerySet, setting all the given
fields to the appropriate values.
"""
+ assert self.query.can_filter(), \
+ "Cannot update a query once a slice has been taken."
query = self.query.clone(sql.UpdateQuery)
query.add_update_values(kwargs)
query.execute_sql(None)
@@ -306,6 +308,8 @@ class QuerySet(object):
code (it requires too much poking around at model internals to be
useful at that level).
"""
+ assert self.query.can_filter(), \
+ "Cannot update a query once a slice has been taken."
query = self.query.clone(sql.UpdateQuery)
query.add_update_fields(values)
query.execute_sql(None)
@@ -509,7 +513,9 @@ class ValuesQuerySet(QuerySet):
# names of the model fields to select.
def iterator(self):
- self.query.trim_extra_select(self.extra_names)
+ if (not self.extra_names and
+ len(self.field_names) != len(self.model._meta.fields)):
+ self.query.trim_extra_select(self.extra_names)
names = self.query.extra_select.keys() + self.field_names
for row in self.query.results_iter():
yield dict(zip(names, row))
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index a6957bab7b..3044882a86 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -851,7 +851,7 @@ class Query(object):
return alias
def fill_related_selections(self, opts=None, root_alias=None, cur_depth=1,
- used=None, requested=None, restricted=None):
+ used=None, requested=None, restricted=None, nullable=None):
"""
Fill in the information needed for a select_related query. The current
depth is measured as the number of connections away from the root model
@@ -883,6 +883,10 @@ class Query(object):
(not restricted and f.null) or f.rel.parent_link):
continue
table = f.rel.to._meta.db_table
+ if nullable or f.null:
+ promote = True
+ else:
+ promote = False
if model:
int_opts = opts
alias = root_alias
@@ -891,12 +895,12 @@ class Query(object):
int_opts = int_model._meta
alias = self.join((alias, int_opts.db_table, lhs_col,
int_opts.pk.column), exclusions=used,
- promote=f.null)
+ promote=promote)
else:
alias = root_alias
alias = self.join((alias, table, f.column,
f.rel.get_related_field().column), exclusions=used,
- promote=f.null)
+ promote=promote)
used.add(alias)
self.related_select_cols.extend([(alias, f2.column)
for f2 in f.rel.to._meta.fields])
@@ -905,8 +909,12 @@ class Query(object):
next = requested.get(f.name, {})
else:
next = False
+ if f.null is not None:
+ new_nullable = f.null
+ else:
+ new_nullable = None
self.fill_related_selections(f.rel.to._meta, alias, cur_depth + 1,
- used, next, restricted)
+ used, next, restricted, new_nullable)
def add_filter(self, filter_expr, connector=AND, negate=False, trim=False,
can_reuse=None):
diff --git a/django/db/models/sql/subqueries.py b/django/db/models/sql/subqueries.py
index 7385cd00e1..28436abede 100644
--- a/django/db/models/sql/subqueries.py
+++ b/django/db/models/sql/subqueries.py
@@ -2,10 +2,9 @@
Query subclasses which provide extra functionality beyond simple data retrieval.
"""
-from django.contrib.contenttypes import generic
from django.core.exceptions import FieldError
from django.db.models.sql.constants import *
-from django.db.models.sql.datastructures import RawValue, Date
+from django.db.models.sql.datastructures import Date
from django.db.models.sql.query import Query
from django.db.models.sql.where import AND
@@ -43,6 +42,7 @@ class DeleteQuery(Query):
More than one physical query may be executed if there are a
lot of values in pk_list.
"""
+ from django.contrib.contenttypes import generic
cls = self.model
for related in cls._meta.get_all_related_many_to_many_objects():
if not isinstance(related.field, generic.GenericRelation):
@@ -382,4 +382,3 @@ class CountQuery(Query):
def get_ordering(self):
return ()
-
diff --git a/django/db/models/sql/where.py b/django/db/models/sql/where.py
index 3e8bfed087..14e54487a3 100644
--- a/django/db/models/sql/where.py
+++ b/django/db/models/sql/where.py
@@ -18,7 +18,7 @@ class WhereNode(tree.Node):
Used to represent the SQL where-clause.
The class is tied to the Query class that created it (in order to create
- the corret SQL).
+ the correct SQL).
The children in this tree are usually either Q-like objects or lists of
[table_alias, field_name, field_class, lookup_type, value]. However, a
diff --git a/django/template/__init__.py b/django/template/__init__.py
index e60ff64ebf..5c4ab3052a 100644
--- a/django/template/__init__.py
+++ b/django/template/__init__.py
@@ -467,7 +467,7 @@ class FilterExpression(object):
>>> len(fe.filters)
2
>>> fe.var
- 'variable'
+ tags')
u'some html with alert("You smell") disallowed tags'
->>> dictsort([{'age': 23, 'name': 'Barbara-Ann'},
-... {'age': 63, 'name': 'Ra Ra Rasputin'},
-... {'name': 'Jonny B Goode', 'age': 18}], 'age')
-[{'age': 18, 'name': 'Jonny B Goode'}, {'age': 23, 'name': 'Barbara-Ann'}, {'age': 63, 'name': 'Ra Ra Rasputin'}]
+>>> sorted_dicts = dictsort([{'age': 23, 'name': 'Barbara-Ann'},
+... {'age': 63, 'name': 'Ra Ra Rasputin'},
+... {'name': 'Jonny B Goode', 'age': 18}], 'age')
+>>> [sorted(dict.items()) for dict in sorted_dicts]
+[[('age', 18), ('name', 'Jonny B Goode')], [('age', 23), ('name', 'Barbara-Ann')], [('age', 63), ('name', 'Ra Ra Rasputin')]]
->>> dictsortreversed([{'age': 23, 'name': 'Barbara-Ann'},
-... {'age': 63, 'name': 'Ra Ra Rasputin'},
-... {'name': 'Jonny B Goode', 'age': 18}], 'age')
-[{'age': 63, 'name': 'Ra Ra Rasputin'}, {'age': 23, 'name': 'Barbara-Ann'}, {'age': 18, 'name': 'Jonny B Goode'}]
+>>> sorted_dicts = dictsortreversed([{'age': 23, 'name': 'Barbara-Ann'},
+... {'age': 63, 'name': 'Ra Ra Rasputin'},
+... {'name': 'Jonny B Goode', 'age': 18}], 'age')
+>>> [sorted(dict.items()) for dict in sorted_dicts]
+[[('age', 63), ('name', 'Ra Ra Rasputin')], [('age', 23), ('name', 'Barbara-Ann')], [('age', 18), ('name', 'Jonny B Goode')]]
>>> first([0,1,2])
0
diff --git a/tests/regressiontests/fixtures_regress/fixtures/bad_fixture1.unkn b/tests/regressiontests/fixtures_regress/fixtures/bad_fixture1.unkn
new file mode 100644
index 0000000000..a8b0a0c56c
--- /dev/null
+++ b/tests/regressiontests/fixtures_regress/fixtures/bad_fixture1.unkn
@@ -0,0 +1 @@
+This data shouldn't load, as it's of an unknown file format.
\ No newline at end of file
diff --git a/tests/regressiontests/fixtures_regress/fixtures/bad_fixture2.xml b/tests/regressiontests/fixtures_regress/fixtures/bad_fixture2.xml
new file mode 100644
index 0000000000..87b809fbc6
--- /dev/null
+++ b/tests/regressiontests/fixtures_regress/fixtures/bad_fixture2.xml
@@ -0,0 +1,7 @@
+
+