mirror of
https://github.com/django/django.git
synced 2025-07-04 01:39:20 +00:00
gis: Merged revisions 7574-7583,7585-7586,7590-7602,7614-7615,7619-7625,7629,7632-7636 via svnmerge from trunk.
git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@7642 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
4ec80c4333
commit
842dae0ed5
6
AUTHORS
6
AUTHORS
@ -57,6 +57,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
David Ascher <http://ascher.ca/>
|
||||
Jökull Sólberg Auðunsson <jokullsolberg@gmail.com>
|
||||
Arthur <avandorp@gmail.com>
|
||||
av0000@mail.ru
|
||||
David Avsajanishvili <avsd05@gmail.com>
|
||||
axiak@mit.edu
|
||||
Niran Babalola <niran@niran.org>
|
||||
@ -78,6 +79,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
brut.alll@gmail.com
|
||||
btoll@bestweb.net
|
||||
Jonathan Buchanan <jonathan.buchanan@gmail.com>
|
||||
Keith Bussell <kbussell@gmail.com>
|
||||
Juan Manuel Caicedo <juan.manuel.caicedo@gmail.com>
|
||||
Trevor Caira <trevor@caira.com>
|
||||
Ricardo Javier Cárdenes Medina <ricardo.cardenes@gmail.com>
|
||||
@ -142,6 +144,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
Bill Fenner <fenner@gmail.com>
|
||||
Stefane Fermgier <sf@fermigier.com>
|
||||
Afonso Fernández Nogueira <fonzzo.django@gmail.com>
|
||||
J. Pablo Fernandez <pupeno@pupeno.com>
|
||||
Matthew Flanagan <http://wadofstuff.blogspot.com>
|
||||
Eric Floehr <eric@intellovations.com>
|
||||
Vincent Foley <vfoleybourgon@yahoo.ca>
|
||||
@ -359,13 +362,14 @@ answer newbie questions, and generally made Django that much better:
|
||||
Makoto Tsuyuki <mtsuyuki@gmail.com>
|
||||
tt@gurgle.no
|
||||
David Tulig <david.tulig@gmail.com>
|
||||
Amit Upadhyay
|
||||
Amit Upadhyay <http://www.amitu.com/blog/>
|
||||
Geert Vanderkelen
|
||||
I.S. van Oostveen <v.oostveen@idca.nl>
|
||||
viestards.lists@gmail.com
|
||||
George Vilches <gav@thataddress.com>
|
||||
Vlado <vlado@labath.org>
|
||||
Milton Waddams
|
||||
Chris Wagner <cw264701@ohio.edu>
|
||||
wam-djangobug@wamber.net
|
||||
Wang Chun <wangchun@exoweb.net>
|
||||
Filip Wasilewski <filip.wasilewski@gmail.com>
|
||||
|
@ -11,9 +11,10 @@ gettext_noop = lambda s: s
|
||||
|
||||
DEBUG = False
|
||||
TEMPLATE_DEBUG = False
|
||||
# True if BaseHandler.get_response() should propagate raw exceptions
|
||||
# rather than catching them. This is useful under some testing siutations,
|
||||
# and should never be used on a live site.
|
||||
|
||||
# Whether the framework should propagate raw exceptions rather than catching
|
||||
# them. This is useful under some testing siutations and should never be used
|
||||
# on a live site.
|
||||
DEBUG_PROPAGATE_EXCEPTIONS = False
|
||||
|
||||
# Whether to use the "Etag" header. This saves bandwidth but slows down performance.
|
||||
@ -289,7 +290,7 @@ SESSION_COOKIE_DOMAIN = None # A string like ".lawren
|
||||
SESSION_COOKIE_SECURE = False # Whether the session cookie should be secure (https:// only).
|
||||
SESSION_COOKIE_PATH = '/' # The path of the session cookie.
|
||||
SESSION_SAVE_EVERY_REQUEST = False # Whether to save the session data on every request.
|
||||
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # Whether sessions expire when a user closes his browser.
|
||||
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # Whether a user's session cookie expires when the Web browser is closed.
|
||||
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # The module to store session data
|
||||
SESSION_FILE_PATH = None # Directory to store session files if using the file session module. If None, the backend will use a sensible default.
|
||||
|
||||
|
Binary file not shown.
@ -5,201 +5,200 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2008-05-26 10:38+0200\n"
|
||||
"POT-Creation-Date: 2008-06-13 16:14+0200\n"
|
||||
"PO-Revision-Date: 2008-03-30 01:04+0100\n"
|
||||
"Last-Translator: Django Spanish Group <django-i18n@googlegroups.com>\n"
|
||||
"Language-Team: Spanish <django-i18n@googlegroups.com>\n"
|
||||
"Last-Translator: Django Spanish Translation Team <django-cat@googlegroups.com>\n"
|
||||
"Language-Team: Django Spanish Translation Team <django-cat@googlegroups.com>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: conf/global_settings.py:43
|
||||
#: conf/global_settings.py:44
|
||||
msgid "Arabic"
|
||||
msgstr "Árabe"
|
||||
|
||||
#: conf/global_settings.py:44
|
||||
#: conf/global_settings.py:45
|
||||
msgid "Bengali"
|
||||
msgstr "Bengalí"
|
||||
|
||||
#: conf/global_settings.py:45
|
||||
#: conf/global_settings.py:46
|
||||
msgid "Bulgarian"
|
||||
msgstr "Búlgaro"
|
||||
|
||||
#: conf/global_settings.py:46
|
||||
#: conf/global_settings.py:47
|
||||
msgid "Catalan"
|
||||
msgstr "Catalán"
|
||||
|
||||
#: conf/global_settings.py:47
|
||||
#: conf/global_settings.py:48
|
||||
msgid "Czech"
|
||||
msgstr "Checo"
|
||||
|
||||
#: conf/global_settings.py:48
|
||||
#: conf/global_settings.py:49
|
||||
msgid "Welsh"
|
||||
msgstr "Galés"
|
||||
|
||||
#: conf/global_settings.py:49
|
||||
#: conf/global_settings.py:50
|
||||
msgid "Danish"
|
||||
msgstr "Danés"
|
||||
|
||||
#: conf/global_settings.py:50
|
||||
#: conf/global_settings.py:51
|
||||
msgid "German"
|
||||
msgstr "Alemán"
|
||||
|
||||
#: conf/global_settings.py:51
|
||||
#: conf/global_settings.py:52
|
||||
msgid "Greek"
|
||||
msgstr "Griego"
|
||||
|
||||
#: conf/global_settings.py:52
|
||||
#: conf/global_settings.py:53
|
||||
msgid "English"
|
||||
msgstr "Inglés"
|
||||
|
||||
#: conf/global_settings.py:53
|
||||
#: conf/global_settings.py:54
|
||||
msgid "Spanish"
|
||||
msgstr "Español"
|
||||
|
||||
#: conf/global_settings.py:54
|
||||
#: conf/global_settings.py:55
|
||||
msgid "Argentinean Spanish"
|
||||
msgstr "Español Argentino"
|
||||
|
||||
#: conf/global_settings.py:55
|
||||
#: conf/global_settings.py:56
|
||||
msgid "Basque"
|
||||
msgstr "Vasco"
|
||||
|
||||
#: conf/global_settings.py:56
|
||||
#: conf/global_settings.py:57
|
||||
msgid "Persian"
|
||||
msgstr "Persa"
|
||||
|
||||
#: conf/global_settings.py:57
|
||||
#: conf/global_settings.py:58
|
||||
msgid "Finnish"
|
||||
msgstr "Finés"
|
||||
|
||||
#: conf/global_settings.py:58
|
||||
#: conf/global_settings.py:59
|
||||
msgid "French"
|
||||
msgstr "Francés"
|
||||
|
||||
#: conf/global_settings.py:59
|
||||
#: conf/global_settings.py:60
|
||||
msgid "Irish"
|
||||
msgstr "Irlandés"
|
||||
|
||||
#: conf/global_settings.py:60
|
||||
#: conf/global_settings.py:61
|
||||
msgid "Galician"
|
||||
msgstr "Gallego"
|
||||
|
||||
#: conf/global_settings.py:61
|
||||
#: conf/global_settings.py:62
|
||||
msgid "Hungarian"
|
||||
msgstr "Húngaro"
|
||||
|
||||
#: conf/global_settings.py:62
|
||||
#: conf/global_settings.py:63
|
||||
msgid "Hebrew"
|
||||
msgstr "Hebreo"
|
||||
|
||||
#: conf/global_settings.py:63
|
||||
#: conf/global_settings.py:64
|
||||
msgid "Croatian"
|
||||
msgstr "Croata"
|
||||
|
||||
#: conf/global_settings.py:64
|
||||
#: conf/global_settings.py:65
|
||||
msgid "Icelandic"
|
||||
msgstr "Islandés"
|
||||
|
||||
#: conf/global_settings.py:65
|
||||
#: conf/global_settings.py:66
|
||||
msgid "Italian"
|
||||
msgstr "Italiano"
|
||||
|
||||
#: conf/global_settings.py:66
|
||||
#: conf/global_settings.py:67
|
||||
msgid "Japanese"
|
||||
msgstr "Japonés"
|
||||
|
||||
#: conf/global_settings.py:67
|
||||
#: conf/global_settings.py:68
|
||||
msgid "Georgian"
|
||||
msgstr "Georgiano"
|
||||
|
||||
#: conf/global_settings.py:68
|
||||
#: conf/global_settings.py:69
|
||||
msgid "Korean"
|
||||
msgstr "Koreano"
|
||||
|
||||
#: conf/global_settings.py:69
|
||||
#: conf/global_settings.py:70
|
||||
msgid "Khmer"
|
||||
msgstr "Khmer"
|
||||
|
||||
#: conf/global_settings.py:70
|
||||
#: conf/global_settings.py:71
|
||||
msgid "Kannada"
|
||||
msgstr "Kannada"
|
||||
|
||||
#: conf/global_settings.py:71
|
||||
#: conf/global_settings.py:72
|
||||
msgid "Latvian"
|
||||
msgstr "Latvio"
|
||||
|
||||
#: conf/global_settings.py:72
|
||||
#: conf/global_settings.py:73
|
||||
msgid "Macedonian"
|
||||
msgstr "Macedonio"
|
||||
|
||||
#: conf/global_settings.py:73
|
||||
msgid "Dutch"
|
||||
msgstr "Alemán"
|
||||
|
||||
#: conf/global_settings.py:74
|
||||
msgid "Dutch"
|
||||
msgstr "Holandés"
|
||||
|
||||
#: conf/global_settings.py:75
|
||||
msgid "Norwegian"
|
||||
msgstr "Noruego"
|
||||
|
||||
#: conf/global_settings.py:75
|
||||
#: conf/global_settings.py:76
|
||||
msgid "Polish"
|
||||
msgstr "Polaco"
|
||||
|
||||
#: conf/global_settings.py:76
|
||||
#: conf/global_settings.py:77
|
||||
msgid "Portugese"
|
||||
msgstr "Portugés"
|
||||
|
||||
#: conf/global_settings.py:77
|
||||
#, fuzzy
|
||||
msgid "Brazilian Portuguese"
|
||||
msgstr "Portugés"
|
||||
|
||||
#: conf/global_settings.py:78
|
||||
msgid "Brazilian Portuguese"
|
||||
msgstr "Portugés Brasileño"
|
||||
|
||||
#: conf/global_settings.py:79
|
||||
msgid "Romanian"
|
||||
msgstr "Rumano"
|
||||
|
||||
#: conf/global_settings.py:79
|
||||
#: conf/global_settings.py:80
|
||||
msgid "Russian"
|
||||
msgstr "Ruso"
|
||||
|
||||
#: conf/global_settings.py:80
|
||||
#: conf/global_settings.py:81
|
||||
msgid "Slovak"
|
||||
msgstr "Eslovaco"
|
||||
|
||||
#: conf/global_settings.py:81
|
||||
#: conf/global_settings.py:82
|
||||
msgid "Slovenian"
|
||||
msgstr "Esloveno"
|
||||
|
||||
#: conf/global_settings.py:82
|
||||
#: conf/global_settings.py:83
|
||||
msgid "Serbian"
|
||||
msgstr "Serbio"
|
||||
|
||||
#: conf/global_settings.py:83
|
||||
#: conf/global_settings.py:84
|
||||
msgid "Swedish"
|
||||
msgstr "Sueco"
|
||||
|
||||
#: conf/global_settings.py:84
|
||||
#: conf/global_settings.py:85
|
||||
msgid "Tamil"
|
||||
msgstr "Tamil"
|
||||
|
||||
#: conf/global_settings.py:85
|
||||
#: conf/global_settings.py:86
|
||||
msgid "Telugu"
|
||||
msgstr "Telugu"
|
||||
|
||||
#: conf/global_settings.py:86
|
||||
#: conf/global_settings.py:87
|
||||
msgid "Turkish"
|
||||
msgstr "Turco"
|
||||
|
||||
#: conf/global_settings.py:87
|
||||
#: conf/global_settings.py:88
|
||||
msgid "Ukrainian"
|
||||
msgstr "Ucraniano"
|
||||
|
||||
#: conf/global_settings.py:88
|
||||
#: conf/global_settings.py:89
|
||||
msgid "Simplified Chinese"
|
||||
msgstr "Chino simplificado"
|
||||
|
||||
#: conf/global_settings.py:89
|
||||
#: conf/global_settings.py:90
|
||||
msgid "Traditional Chinese"
|
||||
msgstr "Chino tradicional"
|
||||
|
||||
@ -329,7 +328,7 @@ msgstr ""
|
||||
|
||||
#: contrib/admin/templates/admin/base.html:26
|
||||
msgid "Welcome,"
|
||||
msgstr "Bienvenido,"
|
||||
msgstr "Bienvenido/a,"
|
||||
|
||||
#: contrib/admin/templates/admin/base.html:28
|
||||
#: contrib/admin/templates/admin_doc/bookmarklets.html:3
|
||||
@ -357,7 +356,7 @@ msgstr "Administración de Django"
|
||||
|
||||
#: contrib/admin/templates/admin/change_form.html:14
|
||||
#: contrib/admin/templates/admin/index.html:28
|
||||
msgid "Add"
|
||||
msgid "Añadir"
|
||||
msgstr "Agregar"
|
||||
|
||||
#: contrib/admin/templates/admin/change_form.html:20
|
||||
@ -387,7 +386,7 @@ msgstr "Orden:"
|
||||
#: contrib/admin/templates/admin/change_list.html:11
|
||||
#, python-format
|
||||
msgid "Add %(name)s"
|
||||
msgstr "Agregar %(name)s"
|
||||
msgstr "Añadir %(name)s"
|
||||
|
||||
#: contrib/admin/templates/admin/delete_confirmation.html:8
|
||||
#: contrib/admin/templates/admin/submit_line.html:3
|
||||
@ -463,7 +462,7 @@ msgid ""
|
||||
"database tables have been created, and make sure the database is readable by "
|
||||
"the appropriate user."
|
||||
msgstr ""
|
||||
"Algo va mal con la instalación de la base de datos. Asegúrate que las tablas "
|
||||
"Algo va mal con la instalación de la base de datos. Asegúrese que las tablas "
|
||||
"necesarias han sido creadas, y que la base de datos puede ser leída por el "
|
||||
"usuario apropiado."
|
||||
|
||||
@ -476,12 +475,12 @@ msgstr "Usuario:"
|
||||
#: contrib/admin/templates/admin/login.html:20
|
||||
#: contrib/comments/templates/comments/form.html:8
|
||||
msgid "Password:"
|
||||
msgstr "Clave:"
|
||||
msgstr "Contraseña:"
|
||||
|
||||
#: contrib/admin/templates/admin/login.html:25
|
||||
#: contrib/admin/views/decorators.py:31
|
||||
msgid "Log in"
|
||||
msgstr "Identificarse"
|
||||
msgstr "Iniciar sesión"
|
||||
|
||||
#: contrib/admin/templates/admin/object_history.html:17
|
||||
msgid "Date/time"
|
||||
@ -509,11 +508,11 @@ msgstr ""
|
||||
|
||||
#: contrib/admin/templates/admin/pagination.html:10
|
||||
msgid "Show all"
|
||||
msgstr "Mostrarlo todo"
|
||||
msgstr "Mostrar todo"
|
||||
|
||||
#: contrib/admin/templates/admin/search_form.html:8
|
||||
msgid "Go"
|
||||
msgstr "Buscar"
|
||||
msgstr "Ir"
|
||||
|
||||
#: contrib/admin/templates/admin/search_form.html:10
|
||||
#, python-format
|
||||
@ -568,7 +567,7 @@ msgstr "Contraseña (de nuevo)"
|
||||
#: contrib/admin/templates/admin/auth/user/add_form.html:24
|
||||
#: contrib/admin/templates/admin/auth/user/change_password.html:39
|
||||
msgid "Enter the same password as above, for verification."
|
||||
msgstr "Introduzca la misma contraseña que arriba, para verificación"
|
||||
msgstr "Introduzca la misma contraseña que arriba, para verificación."
|
||||
|
||||
#: contrib/admin/templates/admin/auth/user/change_password.html:27
|
||||
#, python-format
|
||||
@ -599,22 +598,22 @@ msgstr ""
|
||||
"<p class=\"help\">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 <a href=\"password/\">change "
|
||||
"password form</a>."
|
||||
@ -1178,32 +1177,31 @@ msgstr ""
|
||||
"Use'[algo]$[sal]$[hash hexadecimal]' o use <a href=\"password/\">el "
|
||||
"formulario para cambiar la contraseña</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."
|
||||
|
@ -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")
|
||||
|
@ -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)
|
0
django/contrib/auth/management/commands/__init__.py
Normal file
0
django/contrib/auth/management/commands/__init__.py
Normal file
123
django/contrib/auth/management/commands/createsuperuser.py
Normal file
123
django/contrib/auth/management/commands/createsuperuser.py
Normal file
@ -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."
|
@ -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
|
||||
|
@ -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'!'
|
||||
"""
|
@ -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):
|
||||
|
@ -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)
|
@ -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):
|
||||
|
@ -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,
|
||||
|
@ -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__':
|
||||
|
@ -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]
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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:
|
||||
|
@ -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.
|
||||
"""
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
|
@ -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):
|
||||
|
@ -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 ()
|
||||
|
||||
|
@ -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
|
||||
|
@ -467,7 +467,7 @@ class FilterExpression(object):
|
||||
>>> len(fe.filters)
|
||||
2
|
||||
>>> fe.var
|
||||
'variable'
|
||||
<Variable: 'variable'>
|
||||
|
||||
This class should never be instantiated outside of the
|
||||
get_filters_from_token helper function.
|
||||
@ -598,15 +598,15 @@ class Variable(object):
|
||||
a hard-coded string (if it begins and ends with single or double quote
|
||||
marks)::
|
||||
|
||||
>>> c = {'article': {'section':'News'}}
|
||||
>>> c = {'article': {'section':u'News'}}
|
||||
>>> Variable('article.section').resolve(c)
|
||||
u'News'
|
||||
>>> Variable('article').resolve(c)
|
||||
{'section': 'News'}
|
||||
{'section': u'News'}
|
||||
>>> class AClass: pass
|
||||
>>> c = AClass()
|
||||
>>> c.article = AClass()
|
||||
>>> c.article.section = 'News'
|
||||
>>> c.article.section = u'News'
|
||||
>>> Variable('article.section').resolve(c)
|
||||
u'News'
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import urllib
|
||||
import sys
|
||||
import os
|
||||
from cStringIO import StringIO
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import authenticate, login
|
||||
@ -67,7 +68,7 @@ def encode_multipart(boundary, data):
|
||||
if isinstance(value, file):
|
||||
lines.extend([
|
||||
'--' + boundary,
|
||||
'Content-Disposition: form-data; name="%s"; filename="%s"' % (to_str(key), to_str(value.name)),
|
||||
'Content-Disposition: form-data; name="%s"; filename="%s"' % (to_str(key), to_str(os.path.basename(value.name))),
|
||||
'Content-Type: application/octet-stream',
|
||||
'',
|
||||
value.read()
|
||||
@ -178,10 +179,15 @@ class Client:
|
||||
if e.args != ('500.html',):
|
||||
raise
|
||||
|
||||
# Look for a signalled exception and reraise it
|
||||
# Look for a signalled exception, clear the current context
|
||||
# exception data, then re-raise the signalled exception.
|
||||
# Also make sure that the signalled exception is cleared from
|
||||
# the local cache!
|
||||
if self.exc_info:
|
||||
raise self.exc_info[1], None, self.exc_info[2]
|
||||
|
||||
exc_info = self.exc_info
|
||||
self.exc_info = None
|
||||
raise exc_info[1], None, exc_info[2]
|
||||
|
||||
# Save the client and request that stimulated the response
|
||||
response.client = self
|
||||
response.request = request
|
||||
|
@ -128,6 +128,18 @@ class TestCase(unittest.TestCase):
|
||||
self.failUnless(real_count != 0,
|
||||
"Couldn't find '%s' in response" % text)
|
||||
|
||||
def assertNotContains(self, response, text, status_code=200):
|
||||
"""
|
||||
Asserts that a response indicates that a page was retrieved
|
||||
successfully, (i.e., the HTTP status code was as expected), and that
|
||||
``text`` doesn't occurs in the content of the response.
|
||||
"""
|
||||
self.assertEqual(response.status_code, status_code,
|
||||
"Couldn't retrieve page: Response code was %d (expected %d)'" %
|
||||
(response.status_code, status_code))
|
||||
self.assertEqual(response.content.count(text), 0,
|
||||
"Response should not contain '%s'" % text)
|
||||
|
||||
def assertFormError(self, response, form, field, errors):
|
||||
"""
|
||||
Asserts that a form used to render the response has a specific field
|
||||
|
@ -118,7 +118,7 @@ def get_valid_filename(s):
|
||||
spaces are converted to underscores; and all non-filename-safe characters
|
||||
are removed.
|
||||
>>> get_valid_filename("john's portrait in 2004.jpg")
|
||||
'johns_portrait_in_2004.jpg'
|
||||
u'johns_portrait_in_2004.jpg'
|
||||
"""
|
||||
s = force_unicode(s).strip().replace(' ', '_')
|
||||
return re.sub(r'[^-A-Za-z0-9_.]', '', s)
|
||||
@ -127,15 +127,15 @@ get_valid_filename = allow_lazy(get_valid_filename, unicode)
|
||||
def get_text_list(list_, last_word=ugettext_lazy(u'or')):
|
||||
"""
|
||||
>>> get_text_list(['a', 'b', 'c', 'd'])
|
||||
'a, b, c or d'
|
||||
u'a, b, c or d'
|
||||
>>> get_text_list(['a', 'b', 'c'], 'and')
|
||||
'a, b and c'
|
||||
u'a, b and c'
|
||||
>>> get_text_list(['a', 'b'], 'and')
|
||||
'a and b'
|
||||
u'a and b'
|
||||
>>> get_text_list(['a'])
|
||||
'a'
|
||||
u'a'
|
||||
>>> get_text_list([])
|
||||
''
|
||||
u''
|
||||
"""
|
||||
if len(list_) == 0: return u''
|
||||
if len(list_) == 1: return force_unicode(list_[0])
|
||||
@ -198,14 +198,18 @@ javascript_quote = allow_lazy(javascript_quote, unicode)
|
||||
|
||||
smart_split_re = re.compile('("(?:[^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'(?:[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'|[^\\s]+)')
|
||||
def smart_split(text):
|
||||
"""
|
||||
r"""
|
||||
Generator that splits a string by spaces, leaving quoted phrases together.
|
||||
Supports both single and double quotes, and supports escaping quotes with
|
||||
backslashes. In the output, strings will keep their initial and trailing
|
||||
quote marks.
|
||||
|
||||
>>> list(smart_split('This is "a person\'s" test.'))
|
||||
['This', 'is', '"a person\'s"', 'test.']
|
||||
>>> list(smart_split(r'This is "a person\'s" test.'))
|
||||
[u'This', u'is', u'"a person\\\'s"', u'test.']
|
||||
>>> list(smart_split(r"Another 'person\'s' test."))
|
||||
[u'Another', u"'person's'", u'test.']
|
||||
>>> list(smart_split(r'A "\"funky\" style" test.'))
|
||||
[u'A', u'""funky" style"', u'test.']
|
||||
"""
|
||||
text = force_unicode(text)
|
||||
for bit in smart_split_re.finditer(text):
|
||||
|
@ -263,14 +263,25 @@ Creating superusers
|
||||
-------------------
|
||||
|
||||
``manage.py syncdb`` prompts you to create a superuser the first time you run
|
||||
it after adding ``'django.contrib.auth'`` to your ``INSTALLED_APPS``. But if
|
||||
you need to create a superuser after that via the command line, you can use the
|
||||
``create_superuser.py`` utility. Just run this command::
|
||||
it after adding ``'django.contrib.auth'`` to your ``INSTALLED_APPS``. If you need
|
||||
to create a superuser at a later date, you can use a command line utility.
|
||||
|
||||
**New in Django development version.**::
|
||||
|
||||
manage.py createsuperuser --username=joe --email=joe@example.com
|
||||
|
||||
You will be prompted for a password. After you enter one, the user will be
|
||||
created immediately. If you leave off the ``--username`` or the ``--email``
|
||||
options, it will prompt you for those values.
|
||||
|
||||
If you're using an older release of Django, the old way of creating a superuser
|
||||
on the command line still works::
|
||||
|
||||
python /path/to/django/contrib/auth/create_superuser.py
|
||||
|
||||
Make sure to substitute ``/path/to/`` with the path to the Django codebase on
|
||||
your filesystem.
|
||||
...where ``/path/to`` is the path to the Django codebase on your filesystem. The
|
||||
``manage.py`` command is preferred because it figures out the correct path and
|
||||
environment for you.
|
||||
|
||||
Storing additional information about users
|
||||
------------------------------------------
|
||||
|
@ -148,15 +148,17 @@ Once you've claimed a ticket, you have a responsibility to work on that ticket
|
||||
in a reasonably timely fashion. If you don't have time to work on it, either
|
||||
unclaim it or don't claim it in the first place!
|
||||
|
||||
Core Django developers go through the list of claimed tickets from time to
|
||||
Ticket triagers go through the list of claimed tickets from time to
|
||||
time, checking whether any progress has been made. If there's no sign of
|
||||
progress on a particular claimed ticket for a week or two after it's been
|
||||
claimed, we will unclaim it for you so that it's no longer monopolized and
|
||||
progress on a particular claimed ticket for a week or two, a triager may ask
|
||||
you to relinquish the ticket claim so that it's no longer monopolized and
|
||||
somebody else can claim it.
|
||||
|
||||
If you've claimed a ticket and it's taking a long time (days or weeks) to code,
|
||||
keep everybody updated by posting comments on the ticket. That way, we'll know
|
||||
not to unclaim it. More communication is better than less communication!
|
||||
keep everybody updated by posting comments on the ticket. If you don't provide
|
||||
regular updates, and you don't respond to a request for a progress report,
|
||||
your claim on the ticket may be revoked. As always, more communication is
|
||||
better than less communication!
|
||||
|
||||
Which tickets should be claimed?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -177,10 +179,10 @@ Patch style
|
||||
English than in code. Indentation is the most common example; it's hard to
|
||||
read patches when the only difference in code is that it's indented.
|
||||
|
||||
* When creating patches, always run ``svn diff`` from the top-level
|
||||
``trunk`` directory -- i.e., the one that contains ``django``, ``docs``,
|
||||
``tests``, ``AUTHORS``, etc. This makes it easy for other people to apply
|
||||
your patches.
|
||||
* When creating patches, always run ``svn diff`` from the top-level
|
||||
``trunk`` directory -- i.e., the one that contains ``django``, ``docs``,
|
||||
``tests``, ``AUTHORS``, etc. This makes it easy for other people to apply
|
||||
your patches.
|
||||
|
||||
* Attach patches to a ticket in the `ticket tracker`_, using the "attach file"
|
||||
button. Please *don't* put the patch in the ticket description or comment
|
||||
@ -234,22 +236,28 @@ Since a picture is worth a thousand words, let's start there:
|
||||
:width: 590
|
||||
:alt: Django's ticket workflow
|
||||
|
||||
We've got two roles here:
|
||||
We've got two official roles here:
|
||||
|
||||
* Core developers: people with commit access who make the decisions and
|
||||
write the bulk of the code.
|
||||
|
||||
* Ticket triagers: community members who keep track of tickets, making
|
||||
sure the tickets are always categorized correctly.
|
||||
* Core developers: people with commit access who make the big decisions
|
||||
and write the bulk of the code.
|
||||
|
||||
* Ticket triagers: trusted community members with a proven history of
|
||||
working with the Django community. As a result of this history, they
|
||||
have been entrusted by the core developers to make some of the smaller
|
||||
decisions about tickets.
|
||||
|
||||
Second, note the five triage stages:
|
||||
|
||||
1. A ticket starts as "Unreviewed", meaning that a triager has yet to
|
||||
examine the ticket and move it along.
|
||||
1. A ticket starts as "Unreviewed", meaning that nobody has examined
|
||||
the ticket.
|
||||
|
||||
2. "Design decision needed" means "this concept requires a design
|
||||
decision," which should be discussed either in the ticket comments or on
|
||||
django-developers.
|
||||
`django-developers`_. The "Design decision needed" step will generally
|
||||
only be used for feature requests. It can also be used for issues
|
||||
that *might* be bugs, depending on opinion or interpretation. Obvious
|
||||
bugs (such as crashes, incorrect query results, or non-compliance with a
|
||||
standard) skip this step and move straight to "Accepted".
|
||||
|
||||
3. Once a ticket is ruled to be approved for fixing, it's moved into the
|
||||
"Accepted" stage. This stage is where all the real work gets done.
|
||||
@ -269,7 +277,7 @@ ticket has or needs in order to be "ready for checkin":
|
||||
|
||||
"Has patch"
|
||||
This means the ticket has an associated patch_. These will be
|
||||
reviewed to see if the patch is "good".
|
||||
reviewed by the triage team to see if the patch is "good".
|
||||
|
||||
"Needs documentation"
|
||||
This flag is used for tickets with patches that need associated
|
||||
@ -292,7 +300,11 @@ A ticket can be resolved in a number of ways:
|
||||
Django and the issue is fixed.
|
||||
|
||||
"invalid"
|
||||
Used if the ticket is found to be incorrect or a user error.
|
||||
Used if the ticket is found to be incorrect. This means that the
|
||||
issue in the ticket is actually the result of a user error, or
|
||||
describes a problem with something other than Django, or isn't
|
||||
a bug report or feature request at all (for example, some new users
|
||||
submit support queries as tickets).
|
||||
|
||||
"wontfix"
|
||||
Used when a core developer decides that this request is not
|
||||
@ -305,7 +317,8 @@ A ticket can be resolved in a number of ways:
|
||||
tickets, we keep all the discussion in one place, which helps everyone.
|
||||
|
||||
"worksforme"
|
||||
Used when the triage team is unable to replicate the original bug.
|
||||
Used when the the ticket doesn't contain enough detail to replicate
|
||||
the original bug.
|
||||
|
||||
If you believe that the ticket was closed in error -- because you're
|
||||
still having the issue, or it's popped up somewhere else, or the triagers have
|
||||
@ -316,6 +329,55 @@ reopen tickets that have been marked as "wontfix" by core developers.
|
||||
.. _good patch: `Patch style`_
|
||||
.. _patch: `Submitting patches`_
|
||||
|
||||
Triage by the general community
|
||||
-------------------------------
|
||||
|
||||
Although the Core Developers and Ticket Triagers make the big decisions in
|
||||
the ticket triage process, there is also a lot that general community
|
||||
members can do to help the triage process. In particular, you can help out by:
|
||||
|
||||
* Closing "Unreviewed" tickets as "invalid", "worksforme", or "duplicate".
|
||||
|
||||
* Promoting "Unreviewed" tickets to "Design Decision Required" if there
|
||||
is a design decision that needs to be made, or "Accepted" if they are
|
||||
an obvious bug.
|
||||
|
||||
* Correcting the "Needs Tests", "Needs documentation", or "Has Patch" flags
|
||||
for tickets where they are incorrectly set.
|
||||
|
||||
* Checking that old tickets are still valid. If a ticket hasn't seen
|
||||
any activity in a long time, it's possible that the problem has been
|
||||
fixed, but the ticket hasn't been closed.
|
||||
|
||||
* Contact the owners of tickets that have been claimed, but have not seen
|
||||
any recent activity. If the owner doesn't respond after a week or so,
|
||||
remove the owner's claim on the ticket.
|
||||
|
||||
* Identifying trends and themes in the tickets. If there a lot of bug reports
|
||||
about a particular part of Django, it possibly indicates that we need
|
||||
to consider refactoring that part of the code. If a trend is emerging,
|
||||
you should raise it for discussion (referencing the relevant tickets)
|
||||
on `django-developers`_.
|
||||
|
||||
However, we do ask that as a general community member working in the
|
||||
ticket database:
|
||||
|
||||
* Please **don't** close tickets as "wontfix". The core developers will
|
||||
make the final determination of the fate of a ticket, usually after
|
||||
consultation with the community.
|
||||
|
||||
* Please **don't** promote tickets to "Ready for checkin" unless they are
|
||||
*trivial* changes - for example, spelling mistakes or
|
||||
broken links in documentation.
|
||||
|
||||
* Please **don't** reverse a decision that has been made by a core
|
||||
developer. If you disagree with a discussion that has been made,
|
||||
please post a message to `django-developers`_.
|
||||
|
||||
* Please be conservative in your actions. If you're unsure if you should
|
||||
be making a change, don't make the change - leave a comment with your
|
||||
concerns on the ticket, or post a message to `django-developers`_.
|
||||
|
||||
Submitting and maintaining translations
|
||||
=======================================
|
||||
|
||||
|
@ -1373,6 +1373,17 @@ SQL equivalent::
|
||||
|
||||
SELECT ... WHERE id IN (1, 3, 4);
|
||||
|
||||
You can also use a queryset to dynamically evaluate the list of values
|
||||
instead of providing a list of literal values. The queryset must be
|
||||
reduced to a list of individual values using the ``values()`` method,
|
||||
and then converted into a query using the ``query`` attribute::
|
||||
|
||||
Entry.objects.filter(blog__in=Blog.objects.filter(name__contains='Cheddar').values('pk').query)
|
||||
|
||||
This queryset will be evaluated as subselect statement::
|
||||
|
||||
SELET ... WHERE blog.id IN (SELECT id FROM ... WHERE NAME LIKE '%Cheddar%')
|
||||
|
||||
startswith
|
||||
~~~~~~~~~~
|
||||
|
||||
@ -2026,6 +2037,37 @@ Each "reverse" operation described in this section has an immediate effect on
|
||||
the database. Every addition, creation and deletion is immediately and
|
||||
automatically saved to the database.
|
||||
|
||||
One-to-one relationships
|
||||
------------------------
|
||||
|
||||
One-to-one relationships are very similar to Many-to-one relationships.
|
||||
If you define a OneToOneField on your model, instances of that model will have
|
||||
access to the related object via a simple attribute of the model.
|
||||
|
||||
For example::
|
||||
|
||||
class EntryDetail(models.Model):
|
||||
entry = models.OneToOneField(Entry)
|
||||
details = models.TextField()
|
||||
|
||||
ed = EntryDetail.objects.get(id=2)
|
||||
ed.entry # Returns the related Entry object.
|
||||
|
||||
The difference comes in reverse queries. The related model in a One-to-one
|
||||
relationship also has access to a ``Manager`` object; however, that ``Manager``
|
||||
represents a single object, rather than a collection of objects::
|
||||
|
||||
e = Entry.objects.get(id=2)
|
||||
e.entrydetail # returns the related EntryDetail object
|
||||
|
||||
If no object has been assigned to this relationship, Django will raise
|
||||
a ``DoesNotExist`` exception.
|
||||
|
||||
Instances can be assigned to the reverse relationship in the same way as
|
||||
you would assign the forward relationship::
|
||||
|
||||
e.entrydetail = ed
|
||||
|
||||
Many-to-many relationships
|
||||
--------------------------
|
||||
|
||||
@ -2053,12 +2095,6 @@ above example, if the ``ManyToManyField`` in ``Entry`` had specified
|
||||
``related_name='entries'``, then each ``Author`` instance would have an
|
||||
``entries`` attribute instead of ``entry_set``.
|
||||
|
||||
One-to-one relationships
|
||||
------------------------
|
||||
|
||||
The semantics of one-to-one relationships will be changing soon, so we don't
|
||||
recommend you use them.
|
||||
|
||||
How are the backward relationships possible?
|
||||
--------------------------------------------
|
||||
|
||||
|
@ -93,6 +93,31 @@ backend. See the `cache documentation`_ for more information.
|
||||
|
||||
.. _cache documentation: ../cache/
|
||||
|
||||
createsuperuser
|
||||
---------------
|
||||
|
||||
**New in Django development version**
|
||||
|
||||
Creates a superuser account (a user who has all permissions). This is
|
||||
useful if you need to create an initial superuser account but did not
|
||||
do so during ``syncdb``, or if you need to programmatically generate
|
||||
superuser accounts for your site(s).
|
||||
|
||||
When run interactively, this command will prompt for a password for
|
||||
the new superuser account. When run non-interactively, no password
|
||||
will be set, and the superuser account will not be able to log in until
|
||||
a password has been manually set for it.
|
||||
|
||||
The username and e-mail address for the new account can be supplied by
|
||||
using the ``--username`` and ``--email`` arguments on the command
|
||||
line. If either of those is not supplied, ``createsuperuser`` will prompt for
|
||||
it when running interactively.
|
||||
|
||||
This command is only available if Django's `authentication system`_
|
||||
(``django.contrib.auth``) is installed.
|
||||
|
||||
.. _authentication system: ../authentication/
|
||||
|
||||
dbshell
|
||||
-------
|
||||
|
||||
@ -139,6 +164,22 @@ dumped.
|
||||
|
||||
.. _custom manager: ../model-api/#custom-managers
|
||||
|
||||
--exclude
|
||||
~~~~~~~~~
|
||||
|
||||
**New in Django development version**
|
||||
|
||||
Exclude a specific application from the applications whose contents is
|
||||
output. For example, to specifically exclude the `auth` application from
|
||||
the output, you would call::
|
||||
|
||||
django-admin.py dumpdata --exclude=auth
|
||||
|
||||
If you want to exclude multiple applications, use multiple ``--exclude``
|
||||
directives::
|
||||
|
||||
django-admin.py dumpdata --exclude=auth --exclude=contenttype
|
||||
|
||||
--format
|
||||
~~~~~~~~
|
||||
|
||||
@ -313,9 +354,9 @@ The ``dumpdata`` command can be used to generate input for ``loaddata``.
|
||||
Use ``--verbosity`` to specify the amount of notification and debug information
|
||||
that ``django-admin.py`` should print to the console.
|
||||
|
||||
* ``0`` means no input.
|
||||
* ``1`` means normal input (default).
|
||||
* ``2`` means verbose input.
|
||||
* ``0`` means no output.
|
||||
* ``1`` means normal output (default).
|
||||
* ``2`` means verbose output.
|
||||
|
||||
Example usage::
|
||||
|
||||
@ -556,9 +597,9 @@ data files.
|
||||
Use ``--verbosity`` to specify the amount of notification and debug information
|
||||
that ``django-admin.py`` should print to the console.
|
||||
|
||||
* ``0`` means no input.
|
||||
* ``1`` means normal input (default).
|
||||
* ``2`` means verbose input.
|
||||
* ``0`` means no output.
|
||||
* ``1`` means normal output (default).
|
||||
* ``2`` means verbose output.
|
||||
|
||||
Example usage::
|
||||
|
||||
@ -592,9 +633,9 @@ is being executed as an unattended, automated script.
|
||||
Use ``--verbosity`` to specify the amount of notification and debug information
|
||||
that ``django-admin.py`` should print to the console.
|
||||
|
||||
* ``0`` means no input.
|
||||
* ``1`` means normal input (default).
|
||||
* ``2`` means verbose input.
|
||||
* ``0`` means no output.
|
||||
* ``1`` means normal output (default).
|
||||
* ``2`` means verbose output.
|
||||
|
||||
Example usage::
|
||||
|
||||
@ -668,9 +709,9 @@ To run on 1.2.3.4:7000 with a ``test`` fixture::
|
||||
Use ``--verbosity`` to specify the amount of notification and debug information
|
||||
that ``django-admin.py`` should print to the console.
|
||||
|
||||
* ``0`` means no input.
|
||||
* ``1`` means normal input (default).
|
||||
* ``2`` means verbose input.
|
||||
* ``0`` means no output.
|
||||
* ``1`` means normal output (default).
|
||||
* ``2`` means verbose output.
|
||||
|
||||
Example usage::
|
||||
|
||||
|
95
docs/faq.txt
95
docs/faq.txt
@ -226,15 +226,16 @@ When will you release Django 1.0?
|
||||
|
||||
Short answer: When we're comfortable with Django's APIs, have added all
|
||||
features that we feel are necessary to earn a "1.0" status, and are ready to
|
||||
begin maintaining backwards compatibility.
|
||||
begin maintaining backwards compatibility.
|
||||
|
||||
The merging of Django's `magic-removal branch`_ went a long way toward Django
|
||||
1.0.
|
||||
The merging of Django's `Queryset Refactor branch`_ went a long way toward Django
|
||||
1.0. Merging the `Newforms Admin branch` will be another important step.
|
||||
|
||||
Of course, you should note that `quite a few production sites`_ use Django in
|
||||
its current status. Don't let the lack of a 1.0 turn you off.
|
||||
|
||||
.. _magic-removal branch: http://code.djangoproject.com/wiki/RemovingTheMagic
|
||||
.. _Queryset Refactor branch: http://code.djangoproject.com/wiki/QuerysetRefactorBranch
|
||||
.. _Newforms Admin branch: http://code.djangoproject.com/wiki/NewformsAdminBranch
|
||||
.. _quite a few production sites: http://code.djangoproject.com/wiki/DjangoPoweredSites
|
||||
|
||||
How can I download the Django documentation to read it offline?
|
||||
@ -259,7 +260,9 @@ Where can I find Django developers for hire?
|
||||
Consult our `developers for hire page`_ for a list of Django developers who
|
||||
would be happy to help you.
|
||||
|
||||
You might also be interested in posting a job to http://www.gypsyjobs.com/ .
|
||||
You might also be interested in posting a job to http://djangogigs.com/ .
|
||||
If you want to find Django-capable people in your local area, try
|
||||
http://djangopeople.net/ .
|
||||
|
||||
.. _developers for hire page: http://code.djangoproject.com/wiki/DevelopersForHire
|
||||
|
||||
@ -643,6 +646,81 @@ You can also use the Python API. See `creating users`_ for full info.
|
||||
|
||||
.. _creating users: ../authentication/#creating-users
|
||||
|
||||
Getting help
|
||||
============
|
||||
|
||||
How do I do X? Why doesn't Y work? Where can I go to get help?
|
||||
--------------------------------------------------------------
|
||||
|
||||
If this FAQ doesn't contain an answer to your question, you might want to
|
||||
try the `django-users mailing list`_. Feel free to ask any question related
|
||||
to installing, using, or debugging Django.
|
||||
|
||||
If you prefer IRC, the `#django IRC channel`_ on the Freenode IRC network is an
|
||||
active community of helpful individuals who may be able to solve your problem.
|
||||
|
||||
.. _`django-users mailing list`: http://groups.google.com/group/django-users
|
||||
.. _`#django IRC channel`: irc://irc.freenode.net/django
|
||||
|
||||
Why hasn't my message appeared on django-users?
|
||||
-----------------------------------------------
|
||||
|
||||
django-users_ has a lot of subscribers. This is good for the community, as
|
||||
it means many people are available to contribute answers to questions.
|
||||
Unfortunately, it also means that django-users_ is an attractive target for
|
||||
spammers.
|
||||
|
||||
In order to combat the spam problem, when you join the django-users_ mailing
|
||||
list, we manually moderate the first message you send to the list. This means
|
||||
that spammers get caught, but it also means that your first question to the
|
||||
list might take a little longer to get answered. We apologize for any
|
||||
inconvenience that this policy may cause.
|
||||
|
||||
.. _django-users: http://groups.google.com/group/django-users
|
||||
|
||||
Nobody on django-users answered my question! What should I do?
|
||||
--------------------------------------------------------------
|
||||
|
||||
Try making your question more specific, or provide a better example of your
|
||||
problem.
|
||||
|
||||
As with most open-source mailing lists, the folks on django-users_ are
|
||||
volunteers. If nobody has answered your question, it may be because nobody
|
||||
knows the answer, it may be because nobody can understand the question, or it
|
||||
may be that everybody that can help is busy. One thing you might try is to ask
|
||||
the question on IRC -- visit the `#django IRC channel`_ on the Freenode IRC
|
||||
network.
|
||||
|
||||
You might notice we have a second mailing list, called django-developers_ --
|
||||
but please don't e-mail support questions to this mailing list. This list is
|
||||
for discussion of the development of Django itself. Asking a tech support
|
||||
question there is considered quite impolite.
|
||||
|
||||
.. _django-developers: http://groups.google.com/group/django-developers
|
||||
|
||||
I think I've found a bug! What should I do?
|
||||
-------------------------------------------
|
||||
|
||||
Detailed instructions on how to handle a potential bug can be found in our
|
||||
`Guide to contributing to Django`_.
|
||||
|
||||
.. _`Guide to contributing to Django`: ../contributing/#reporting-bugs
|
||||
|
||||
I think I've found a security problem! What should I do?
|
||||
--------------------------------------------------------
|
||||
|
||||
If you think you've found a security problem with Django, please send a message
|
||||
to security@djangoproject.com. This is a private list only open to long-time,
|
||||
highly trusted Django developers, and its archives are not publicly readable.
|
||||
|
||||
Due to the sensitive nature of security issues, we ask that if you think you
|
||||
have found a security problem, *please* don't send a message to one of the
|
||||
public mailing lists. Django has a `policy for handling security issues`_;
|
||||
while a defect is outstanding, we would like to minimize any damage that
|
||||
could be inflicted through public knowledge of that defect.
|
||||
|
||||
.. _`policy for handling security issues`: ../contributing/#reporting-security-issues
|
||||
|
||||
Contributing code
|
||||
=================
|
||||
|
||||
@ -652,7 +730,7 @@ How can I get started contributing code to Django?
|
||||
Thanks for asking! We've written an entire document devoted to this question.
|
||||
It's titled `Contributing to Django`_.
|
||||
|
||||
.. _Contributing to Django: ../contributing/
|
||||
.. _`Contributing to Django`: ../contributing/
|
||||
|
||||
I submitted a bug fix in the ticket system several weeks ago. Why are you ignoring my patch?
|
||||
--------------------------------------------------------------------------------------------
|
||||
@ -664,6 +742,11 @@ ignored" and "a ticket has not been attended to yet." Django's ticket system
|
||||
contains hundreds of open tickets, of various degrees of impact on end-user
|
||||
functionality, and Django's developers have to review and prioritize.
|
||||
|
||||
On top of that: the people who work on Django are all volunteers. As a result,
|
||||
the amount of time that we have to work on the framework is limited and will
|
||||
vary from week to week depending on our spare time. If we're busy, we may not
|
||||
be able to spend as much time on Django as we might want.
|
||||
|
||||
Besides, if your feature request stands no chance of inclusion in Django, we
|
||||
won't ignore it -- we'll just close the ticket. So if your ticket is still
|
||||
open, it doesn't mean we're ignoring you; it just means we haven't had time to
|
||||
|
@ -143,11 +143,14 @@ Further resources
|
||||
|
||||
* PDFlib_ is another PDF-generation library that has Python bindings. To
|
||||
use it with Django, just use the same concepts explained in this article.
|
||||
* `Pisa HTML2PDF`_ is yet another PDF-generation library. Pisa ships with
|
||||
an example of how to integrate Pisa with Django.
|
||||
* HTMLdoc_ is a command-line script that can convert HTML to PDF. It
|
||||
doesn't have a Python interface, but you can escape out to the shell
|
||||
using ``system`` or ``popen`` and retrieve the output in Python.
|
||||
* `forge_fdf in Python`_ is a library that fills in PDF forms.
|
||||
|
||||
.. _PDFlib: http://www.pdflib.org/
|
||||
.. _`Pisa HTML2PDF`: http://www.htmltopdf.org/
|
||||
.. _HTMLdoc: http://www.htmldoc.org/
|
||||
.. _forge_fdf in Python: http://www.accesspdf.com/article.php/20050421092951834
|
||||
|
@ -63,6 +63,41 @@ be serialized.
|
||||
doesn't specify all the fields that are required by a model, the deserializer
|
||||
will not be able to save deserialized instances.
|
||||
|
||||
Inherited Models
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
If you have a model that is defined using an `abstract base class`_, you don't
|
||||
have to do anything special to serialize that model. Just call the serializer
|
||||
on the object (or objects) that you want to serialize, and the output will be
|
||||
a complete representation of the serialized object.
|
||||
|
||||
However, if you have a model that uses `multi-table inheritance`_, you also
|
||||
need to serialize all of the base classes for the model. This is because only
|
||||
the fields that are locally defined on the model will be serialized. For
|
||||
example, consider the following models::
|
||||
|
||||
class Place(models.Model):
|
||||
name = models.CharField(max_length=50)
|
||||
|
||||
class Restaurant(Place):
|
||||
serves_hot_dogs = models.BooleanField()
|
||||
|
||||
If you only serialize the Restaurant model::
|
||||
|
||||
data = serializers.serialize('xml', Restaurant.objects.all())
|
||||
|
||||
the fields on the serialized output will only contain the `serves_hot_dogs`
|
||||
attribute. The `name` attribute of the base class will be ignored.
|
||||
|
||||
In order to fully serialize your Restaurant instances, you will need to
|
||||
serialize the Place models as well::
|
||||
|
||||
all_objects = list(Restaurant.objects.all()) + list(Place.objects.all())
|
||||
data = serializers.serialize('xml', all_objects)
|
||||
|
||||
.. _abstract base class: http://www.djangoproject.com/documentation/model-api/#abstract-base-classes
|
||||
.. _multi-table inheritance: http://www.djangoproject.com/documentation/model-api/#multi-table-inheritance
|
||||
|
||||
Deserializing data
|
||||
------------------
|
||||
|
||||
|
@ -80,19 +80,24 @@ attribute, which is a dictionary-like object. You can read it and write to it.
|
||||
It implements the following standard dictionary methods:
|
||||
|
||||
* ``__getitem__(key)``
|
||||
|
||||
Example: ``fav_color = request.session['fav_color']``
|
||||
|
||||
* ``__setitem__(key, value)``
|
||||
|
||||
Example: ``request.session['fav_color'] = 'blue'``
|
||||
|
||||
* ``__delitem__(key)``
|
||||
|
||||
Example: ``del request.session['fav_color']``. This raises ``KeyError``
|
||||
if the given ``key`` isn't already in the session.
|
||||
|
||||
* ``__contains__(key)``
|
||||
|
||||
Example: ``'fav_color' in request.session``
|
||||
|
||||
* ``get(key, default=None)``
|
||||
|
||||
Example: ``fav_color = request.session.get('fav_color', 'red')``
|
||||
|
||||
* ``keys()``
|
||||
@ -101,23 +106,70 @@ It implements the following standard dictionary methods:
|
||||
|
||||
* ``setdefault()`` (**New in Django development version**)
|
||||
|
||||
It also has these three methods:
|
||||
It also has these methods:
|
||||
|
||||
* ``set_test_cookie()``
|
||||
|
||||
Sets a test cookie to determine whether the user's browser supports
|
||||
cookies. Due to the way cookies work, you won't be able to test this
|
||||
until the user's next page request. See "Setting test cookies" below for
|
||||
more information.
|
||||
|
||||
* ``test_cookie_worked()``
|
||||
|
||||
Returns either ``True`` or ``False``, depending on whether the user's
|
||||
browser accepted the test cookie. Due to the way cookies work, you'll
|
||||
have to call ``set_test_cookie()`` on a previous, separate page request.
|
||||
See "Setting test cookies" below for more information.
|
||||
|
||||
* ``delete_test_cookie()``
|
||||
|
||||
Deletes the test cookie. Use this to clean up after yourself.
|
||||
|
||||
* ``set_expiry(value)``
|
||||
|
||||
**New in Django development version**
|
||||
|
||||
Sets the expiration time for the session. You can pass a number of
|
||||
different values:
|
||||
|
||||
* If ``value`` is an integer, the session will expire after that
|
||||
many seconds of inactivity. For example, calling
|
||||
``request.session.set_expiry(300)`` would make the session expire
|
||||
in 5 minutes.
|
||||
|
||||
* If ``value`` is a ``datetime`` or ``timedelta`` object, the
|
||||
session will expire at that specific date/time.
|
||||
|
||||
* If ``value`` is ``0``, the user's session cookie will expire
|
||||
when the user's Web browser is closed.
|
||||
|
||||
* If ``value`` is ``None``, the session reverts to using the global
|
||||
session expiry policy.
|
||||
|
||||
* ``get_expiry_age()``
|
||||
|
||||
**New in Django development version**
|
||||
|
||||
Returns the number of seconds until this session expires. For sessions
|
||||
with no custom expiration (or those set to expire at browser close), this
|
||||
will equal ``settings.SESSION_COOKIE_AGE``.
|
||||
|
||||
* ``get_expiry_date()``
|
||||
|
||||
**New in Django development version**
|
||||
|
||||
Returns the date this session will expire. For sessions with no custom
|
||||
expiration (or those set to expire at browser close), this will equal the
|
||||
date ``settings.SESSION_COOKIE_AGE`` seconds from now.
|
||||
|
||||
* ``get_expire_at_browser_close()``
|
||||
|
||||
**New in Django development version**
|
||||
|
||||
Returns either ``True`` or ``False``, depending on whether the user's
|
||||
session cookie will expire when the user's Web browser is closed.
|
||||
|
||||
You can edit ``request.session`` at any point in your view. You can edit it
|
||||
multiple times.
|
||||
|
||||
@ -278,6 +330,12 @@ browser-length cookies -- cookies that expire as soon as the user closes his or
|
||||
her browser. Use this if you want people to have to log in every time they open
|
||||
a browser.
|
||||
|
||||
**New in Django development version**
|
||||
|
||||
This setting is a global default and can be overwritten at a per-session level
|
||||
by explicitly calling ``request.session.set_expiry()`` as described above in
|
||||
`using sessions in views`_.
|
||||
|
||||
Clearing the session table
|
||||
==========================
|
||||
|
||||
|
@ -394,6 +394,8 @@ site with ``DEBUG`` turned on.
|
||||
DEBUG_PROPAGATE_EXCEPTIONS
|
||||
--------------------------
|
||||
|
||||
**New in Django development version**
|
||||
|
||||
Default: ``False``
|
||||
|
||||
If set to True, Django's normal exception handling of view functions
|
||||
|
@ -822,6 +822,10 @@ useful for testing Web applications:
|
||||
that ``text`` appears in the content of the response. If ``count`` is
|
||||
provided, ``text`` must occur exactly ``count`` times in the response.
|
||||
|
||||
``assertNotContains(response, text, status_code=200)``
|
||||
Asserts that a ``Response`` instance produced the given ``status_code`` and
|
||||
that ``text`` does not appears in the content of the response.
|
||||
|
||||
``assertFormError(response, form, field, errors)``
|
||||
Asserts that a field on a form raises the provided list of errors when
|
||||
rendered on the form.
|
||||
@ -837,6 +841,12 @@ useful for testing Web applications:
|
||||
``errors`` is an error string, or a list of error strings, that are
|
||||
expected as a result of form validation.
|
||||
|
||||
``assertTemplateUsed(response, template_name)``
|
||||
Asserts that the template with the given name was used in rendering the
|
||||
response.
|
||||
|
||||
The name is a string such as ``'admin/index.html'``.
|
||||
|
||||
``assertTemplateNotUsed(response, template_name)``
|
||||
Asserts that the template with the given name was *not* used in rendering
|
||||
the response.
|
||||
@ -846,12 +856,6 @@ useful for testing Web applications:
|
||||
it redirected to ``expected_url`` (including any GET data), and the subsequent
|
||||
page was received with ``target_status_code``.
|
||||
|
||||
``assertTemplateUsed(response, template_name)``
|
||||
Asserts that the template with the given name was used in rendering the
|
||||
response.
|
||||
|
||||
The name is a string such as ``'admin/index.html'``.
|
||||
|
||||
E-mail services
|
||||
---------------
|
||||
|
||||
|
@ -401,8 +401,9 @@ True
|
||||
|
||||
# The 'select' argument to extra() supports names with dashes in them, as long
|
||||
# as you use values().
|
||||
>>> Article.objects.filter(pub_date__year=2008).extra(select={'dashed-value': '1'}).values('headline', 'dashed-value')
|
||||
[{'headline': u'Article 11', 'dashed-value': 1}, {'headline': u'Article 12', 'dashed-value': 1}]
|
||||
>>> dicts = Article.objects.filter(pub_date__year=2008).extra(select={'dashed-value': '1'}).values('headline', 'dashed-value')
|
||||
>>> [sorted(d.items()) for d in dicts]
|
||||
[[('dashed-value', 1), ('headline', u'Article 11')], [('dashed-value', 1), ('headline', u'Article 12')]]
|
||||
|
||||
# If you use 'select' with extra() and names containing dashes on a query
|
||||
# that's *not* a values() query, those extra 'select' values will silently be
|
||||
|
@ -39,6 +39,14 @@ __test__ = {'API_TESTS':"""
|
||||
|
||||
# Create an Article.
|
||||
>>> a1 = Article(id=None, headline='Django lets you build Web apps easily')
|
||||
|
||||
# You can't associate it with a Publication until it's been saved.
|
||||
>>> a1.publications.add(p1)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: 'Article' instance needs to have a primary key value before a many-to-many relationship can be used.
|
||||
|
||||
# Save it!
|
||||
>>> a1.save()
|
||||
|
||||
# Associate the Article with a Publication.
|
||||
|
@ -175,6 +175,12 @@ False
|
||||
>>> Article.objects.filter(reporter__in=[r,r2]).distinct()
|
||||
[<Article: John's second story>, <Article: Paul's story>, <Article: This is a test>]
|
||||
|
||||
# You can also use a queryset instead of a literal list of instances.
|
||||
# The queryset must be reduced to a list of values using values(),
|
||||
# then converted into a query
|
||||
>>> Article.objects.filter(reporter__in=Reporter.objects.filter(first_name='John').values('pk').query).distinct()
|
||||
[<Article: John's second story>, <Article: This is a test>]
|
||||
|
||||
# You need two underscores between "reporter" and "id" -- not one.
|
||||
>>> Article.objects.filter(reporter_id__exact=1)
|
||||
Traceback (most recent call last):
|
||||
|
@ -147,8 +147,13 @@ Test constructor for Restaurant.
|
||||
>>> c.save()
|
||||
>>> ir = ItalianRestaurant(name='Ristorante Miron', address='1234 W. Ash', serves_hot_dogs=False, serves_pizza=False, serves_gnocchi=True, rating=4, chef=c)
|
||||
>>> ir.save()
|
||||
>>> ItalianRestaurant.objects.filter(address='1234 W. Ash')
|
||||
[<ItalianRestaurant: Ristorante Miron the italian restaurant>]
|
||||
|
||||
>>> ir.address = '1234 W. Elm'
|
||||
>>> ir.save()
|
||||
>>> ItalianRestaurant.objects.filter(address='1234 W. Elm')
|
||||
[<ItalianRestaurant: Ristorante Miron the italian restaurant>]
|
||||
|
||||
# Make sure Restaurant and ItalianRestaurant have the right fields in the right
|
||||
# order.
|
||||
|
@ -80,11 +80,8 @@ DoesNotExist: Restaurant matching query does not exist.
|
||||
>>> r.place
|
||||
<Place: Ace Hardware the place>
|
||||
|
||||
# Set the place back again, using assignment in the reverse direction. Need to
|
||||
# reload restaurant object first, because the reverse set can't update the
|
||||
# existing restaurant instance
|
||||
# Set the place back again, using assignment in the reverse direction.
|
||||
>>> p1.restaurant = r
|
||||
>>> r.save()
|
||||
>>> p1.restaurant
|
||||
<Restaurant: Demon Dogs the restaurant>
|
||||
|
||||
|
@ -110,8 +110,9 @@ __test__ = {'API_TESTS':"""
|
||||
>>> Article.objects.filter(Q(headline__startswith='Hello') | Q(headline__contains='bye')).count()
|
||||
3
|
||||
|
||||
>>> list(Article.objects.filter(Q(headline__startswith='Hello'), Q(headline__contains='bye')).values())
|
||||
[{'headline': u'Hello and goodbye', 'pub_date': datetime.datetime(2005, 11, 29, 0, 0), 'id': 3}]
|
||||
>>> dicts = list(Article.objects.filter(Q(headline__startswith='Hello'), Q(headline__contains='bye')).values())
|
||||
>>> [sorted(d.items()) for d in dicts]
|
||||
[[('headline', u'Hello and goodbye'), ('id', 3), ('pub_date', datetime.datetime(2005, 11, 29, 0, 0))]]
|
||||
|
||||
>>> Article.objects.filter(Q(headline__startswith='Hello')).in_bulk([1,2])
|
||||
{1: <Article: Hello>}
|
||||
|
@ -34,5 +34,23 @@
|
||||
"email": "testclient@example.com",
|
||||
"date_joined": "2006-12-17 07:03:31"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "3",
|
||||
"model": "auth.user",
|
||||
"fields": {
|
||||
"username": "staff",
|
||||
"first_name": "Staff",
|
||||
"last_name": "Member",
|
||||
"is_active": true,
|
||||
"is_superuser": false,
|
||||
"is_staff": true,
|
||||
"last_login": "2006-12-17 07:03:31",
|
||||
"groups": [],
|
||||
"user_permissions": [],
|
||||
"password": "sha1$6efc0$f93efe9fd7542f25a7be94871ea45aa95de57161",
|
||||
"email": "testclient@example.com",
|
||||
"date_joined": "2006-12-17 07:03:31"
|
||||
}
|
||||
}
|
||||
]
|
@ -63,5 +63,12 @@ a manager method.
|
||||
>>> DataPoint.objects.values('value').distinct()
|
||||
[{'value': u'thing'}]
|
||||
|
||||
We do not support update on already sliced query sets.
|
||||
|
||||
>>> DataPoint.objects.all()[:2].update(another_value='another thing')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
AssertionError: Cannot update a query once a slice has been taken.
|
||||
|
||||
"""
|
||||
}
|
||||
|
@ -226,15 +226,17 @@ u'some <b>html</b> with alert("You smell") disallowed tags'
|
||||
>>> striptags(u'some <b>html</b> with <script>alert("You smell")</script> disallowed <img /> 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
|
||||
|
@ -0,0 +1 @@
|
||||
This data shouldn't load, as it's of an unknown file format.
|
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<django-objcts version="1.0">
|
||||
<objct pk="2" model="fixtures.article">
|
||||
<field type="CharField" name="headline">Poker on TV is great!</field>
|
||||
<field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field>
|
||||
</objct>
|
||||
</django-objcts>
|
@ -71,4 +71,27 @@ __test__ = {'API_TESTS':"""
|
||||
>>> Absolute.load_count
|
||||
1
|
||||
|
||||
###############################################
|
||||
# Test for ticket #4371 -- fixture loading fails silently in testcases
|
||||
# Validate that error conditions are caught correctly
|
||||
|
||||
# redirect stderr for the next few tests...
|
||||
>>> import sys
|
||||
>>> savestderr = sys.stderr
|
||||
>>> sys.stderr = sys.stdout
|
||||
|
||||
# Loading data of an unknown format should fail
|
||||
>>> management.call_command('loaddata', 'bad_fixture1.unkn', verbosity=0)
|
||||
Problem installing fixture 'bad_fixture1': unkn is not a known serialization format.
|
||||
|
||||
# Loading a fixture file with invalid data using explicit filename
|
||||
>>> management.call_command('loaddata', 'bad_fixture2.xml', verbosity=0)
|
||||
No fixture data found for 'bad_fixture2'. (File format may be invalid.)
|
||||
|
||||
# Loading a fixture file with invalid data without file extension
|
||||
>>> management.call_command('loaddata', 'bad_fixture2', verbosity=0)
|
||||
No fixture data found for 'bad_fixture2'. (File format may be invalid.)
|
||||
|
||||
>>> sys.stderr = savestderr
|
||||
|
||||
"""}
|
||||
|
@ -1,3 +1,7 @@
|
||||
"""
|
||||
Regression tests for a few FK bugs: #1578, #6886
|
||||
"""
|
||||
|
||||
from django.db import models
|
||||
|
||||
# If ticket #1578 ever slips back in, these models will not be able to be
|
||||
@ -25,10 +29,48 @@ class Child(models.Model):
|
||||
|
||||
|
||||
__test__ = {'API_TESTS':"""
|
||||
>>> Third.AddManipulator().save(dict(id='3', name='An example', another=None))
|
||||
>>> Third.objects.create(id='3', name='An example')
|
||||
<Third: Third object>
|
||||
>>> parent = Parent(name = 'fred')
|
||||
>>> parent.save()
|
||||
>>> Child.AddManipulator().save(dict(name='bam-bam', parent=parent.id))
|
||||
>>> Child.objects.create(name='bam-bam', parent=parent)
|
||||
<Child: Child object>
|
||||
|
||||
#
|
||||
# Tests of ForeignKey assignment and the related-object cache (see #6886)
|
||||
#
|
||||
>>> p = Parent.objects.create(name="Parent")
|
||||
>>> c = Child.objects.create(name="Child", parent=p)
|
||||
|
||||
# Look up the object again so that we get a "fresh" object
|
||||
>>> c = Child.objects.get(name="Child")
|
||||
>>> p = c.parent
|
||||
|
||||
# Accessing the related object again returns the exactly same object
|
||||
>>> c.parent is p
|
||||
True
|
||||
|
||||
# But if we kill the cache, we get a new object
|
||||
>>> del c._parent_cache
|
||||
>>> c.parent is p
|
||||
False
|
||||
|
||||
# Assigning a new object results in that object getting cached immediately
|
||||
>>> p2 = Parent.objects.create(name="Parent 2")
|
||||
>>> c.parent = p2
|
||||
>>> c.parent is p2
|
||||
True
|
||||
|
||||
# Assigning None fails: Child.parent is null=False
|
||||
>>> c.parent = None
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: Cannot assign None: "Child.parent" does not allow null values.
|
||||
|
||||
# You also can't assign an object of the wrong type here
|
||||
>>> c.parent = First(id=1, second=1)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: Cannot assign "<First: First object>": "Child.parent" must be a "Parent" instance.
|
||||
|
||||
"""}
|
||||
|
120
tests/regressiontests/model_inheritance_regress/models.py
Normal file
120
tests/regressiontests/model_inheritance_regress/models.py
Normal file
@ -0,0 +1,120 @@
|
||||
"""
|
||||
Regression tests for Model inheritance behaviour.
|
||||
"""
|
||||
|
||||
from django.db import models
|
||||
|
||||
class Place(models.Model):
|
||||
name = models.CharField(max_length=50)
|
||||
address = models.CharField(max_length=80)
|
||||
|
||||
class Meta:
|
||||
ordering = ('name',)
|
||||
|
||||
def __unicode__(self):
|
||||
return u"%s the place" % self.name
|
||||
|
||||
class Restaurant(Place):
|
||||
serves_hot_dogs = models.BooleanField()
|
||||
serves_pizza = models.BooleanField()
|
||||
|
||||
def __unicode__(self):
|
||||
return u"%s the restaurant" % self.name
|
||||
|
||||
class ItalianRestaurant(Restaurant):
|
||||
serves_gnocchi = models.BooleanField()
|
||||
|
||||
def __unicode__(self):
|
||||
return u"%s the italian restaurant" % self.name
|
||||
|
||||
class ParkingLot(Place):
|
||||
# An explicit link to the parent (we can control the attribute name).
|
||||
parent = models.OneToOneField(Place, primary_key=True, parent_link=True)
|
||||
capacity = models.IntegerField()
|
||||
|
||||
def __unicode__(self):
|
||||
return u"%s the parking lot" % self.name
|
||||
|
||||
__test__ = {'API_TESTS':"""
|
||||
# Regression for #7350, #7202
|
||||
# Check that when you create a Parent object with a specific reference to an existent
|
||||
# child instance, saving the Parent doesn't duplicate the child.
|
||||
# This behaviour is only activated during a raw save - it is mostly relevant to
|
||||
# deserialization, but any sort of CORBA style 'narrow()' API would require a
|
||||
# similar approach.
|
||||
|
||||
# Create a child-parent-grandparent chain
|
||||
>>> place1 = Place(name="Guido's House of Pasta", address='944 W. Fullerton')
|
||||
>>> place1.save_base(raw=True)
|
||||
>>> restaurant = Restaurant(place_ptr=place1, serves_hot_dogs=True, serves_pizza=False)
|
||||
>>> restaurant.save_base(raw=True)
|
||||
>>> italian_restaurant = ItalianRestaurant(restaurant_ptr=restaurant, serves_gnocchi=True)
|
||||
>>> italian_restaurant.save_base(raw=True)
|
||||
|
||||
# Create a child-parent chain with an explicit parent link
|
||||
>>> place2 = Place(name='Main St', address='111 Main St')
|
||||
>>> place2.save_base(raw=True)
|
||||
>>> park = ParkingLot(parent=place2, capacity=100)
|
||||
>>> park.save_base(raw=True)
|
||||
|
||||
# Check that no extra parent objects have been created.
|
||||
>>> Place.objects.all()
|
||||
[<Place: Guido's House of Pasta the place>, <Place: Main St the place>]
|
||||
|
||||
>>> dicts = Restaurant.objects.values('name','serves_hot_dogs')
|
||||
>>> [sorted(d.items()) for d in dicts]
|
||||
[[('name', u"Guido's House of Pasta"), ('serves_hot_dogs', True)]]
|
||||
|
||||
>>> dicts = ItalianRestaurant.objects.values('name','serves_hot_dogs','serves_gnocchi')
|
||||
>>> [sorted(d.items()) for d in dicts]
|
||||
[[('name', u"Guido's House of Pasta"), ('serves_gnocchi', True), ('serves_hot_dogs', True)]]
|
||||
|
||||
>>> dicts = ParkingLot.objects.values('name','capacity')
|
||||
>>> [sorted(d.items()) for d in dicts]
|
||||
[[('capacity', 100), ('name', u'Main St')]]
|
||||
|
||||
# You can also update objects when using a raw save.
|
||||
>>> place1.name = "Guido's All New House of Pasta"
|
||||
>>> place1.save_base(raw=True)
|
||||
|
||||
>>> restaurant.serves_hot_dogs = False
|
||||
>>> restaurant.save_base(raw=True)
|
||||
|
||||
>>> italian_restaurant.serves_gnocchi = False
|
||||
>>> italian_restaurant.save_base(raw=True)
|
||||
|
||||
>>> place2.name='Derelict lot'
|
||||
>>> place2.save_base(raw=True)
|
||||
|
||||
>>> park.capacity = 50
|
||||
>>> park.save_base(raw=True)
|
||||
|
||||
# No extra parent objects after an update, either.
|
||||
>>> Place.objects.all()
|
||||
[<Place: Derelict lot the place>, <Place: Guido's All New House of Pasta the place>]
|
||||
|
||||
>>> dicts = Restaurant.objects.values('name','serves_hot_dogs')
|
||||
>>> [sorted(d.items()) for d in dicts]
|
||||
[[('name', u"Guido's All New House of Pasta"), ('serves_hot_dogs', False)]]
|
||||
|
||||
>>> dicts = ItalianRestaurant.objects.values('name','serves_hot_dogs','serves_gnocchi')
|
||||
>>> [sorted(d.items()) for d in dicts]
|
||||
[[('name', u"Guido's All New House of Pasta"), ('serves_gnocchi', False), ('serves_hot_dogs', False)]]
|
||||
|
||||
>>> dicts = ParkingLot.objects.values('name','capacity')
|
||||
>>> [sorted(d.items()) for d in dicts]
|
||||
[[('capacity', 50), ('name', u'Derelict lot')]]
|
||||
|
||||
# If you try to raw_save a parent attribute onto a child object,
|
||||
# the attribute will be ignored.
|
||||
|
||||
>>> italian_restaurant.name = "Lorenzo's Pasta Hut"
|
||||
>>> italian_restaurant.save_base(raw=True)
|
||||
|
||||
# Note that the name has not changed
|
||||
# - name is an attribute of Place, not ItalianRestaurant
|
||||
>>> dicts = ItalianRestaurant.objects.values('name','serves_hot_dogs','serves_gnocchi')
|
||||
>>> [sorted(d.items()) for d in dicts]
|
||||
[[('name', u"Guido's All New House of Pasta"), ('serves_gnocchi', False), ('serves_hot_dogs', False)]]
|
||||
|
||||
"""}
|
0
tests/regressiontests/null_fk/__init__.py
Normal file
0
tests/regressiontests/null_fk/__init__.py
Normal file
55
tests/regressiontests/null_fk/models.py
Normal file
55
tests/regressiontests/null_fk/models.py
Normal file
@ -0,0 +1,55 @@
|
||||
"""
|
||||
Regression tests for proper working of ForeignKey(null=True). Tests these bugs:
|
||||
|
||||
* #7369: FK non-null after null relationship on select_related() generates an invalid query
|
||||
|
||||
"""
|
||||
|
||||
from django.db import models
|
||||
|
||||
class SystemInfo(models.Model):
|
||||
system_name = models.CharField(max_length=32)
|
||||
|
||||
class Forum(models.Model):
|
||||
system_info = models.ForeignKey(SystemInfo)
|
||||
forum_name = models.CharField(max_length=32)
|
||||
|
||||
class Post(models.Model):
|
||||
forum = models.ForeignKey(Forum, null=True)
|
||||
title = models.CharField(max_length=32)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.title
|
||||
|
||||
class Comment(models.Model):
|
||||
post = models.ForeignKey(Post, null=True)
|
||||
comment_text = models.CharField(max_length=250)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.comment_text
|
||||
|
||||
__test__ = {'API_TESTS':"""
|
||||
|
||||
>>> s = SystemInfo.objects.create(system_name='First forum')
|
||||
>>> f = Forum.objects.create(system_info=s, forum_name='First forum')
|
||||
>>> p = Post.objects.create(forum=f, title='First Post')
|
||||
>>> c1 = Comment.objects.create(post=p, comment_text='My first comment')
|
||||
>>> c2 = Comment.objects.create(comment_text='My second comment')
|
||||
|
||||
# Starting from comment, make sure that a .select_related(...) with a specified
|
||||
# set of fields will properly LEFT JOIN multiple levels of NULLs (and the things
|
||||
# that come after the NULLs, or else data that should exist won't).
|
||||
>>> c = Comment.objects.select_related().get(id=1)
|
||||
>>> c.post
|
||||
<Post: First Post>
|
||||
>>> c = Comment.objects.select_related().get(id=2)
|
||||
>>> print c.post
|
||||
None
|
||||
|
||||
>>> comments = Comment.objects.select_related('post__forum__system_info').all()
|
||||
>>> [(c.id, c.post.id) for c in comments]
|
||||
[(1, 1), (2, None)]
|
||||
>>> [(c.comment_text, c.post.title) for c in comments]
|
||||
[(u'My first comment', u'First Post'), (u'My second comment', None)]
|
||||
|
||||
"""}
|
@ -50,4 +50,42 @@ __test__ = {'API_TESTS':"""
|
||||
<Restaurant: Demon Dogs the restaurant>
|
||||
>>> p1.bar
|
||||
<Bar: Demon Dogs the bar>
|
||||
|
||||
#
|
||||
# Regression test for #6886 (the related-object cache)
|
||||
#
|
||||
|
||||
# Look up the objects again so that we get "fresh" objects
|
||||
>>> p = Place.objects.get(name="Demon Dogs")
|
||||
>>> r = p.restaurant
|
||||
|
||||
# Accessing the related object again returns the exactly same object
|
||||
>>> p.restaurant is r
|
||||
True
|
||||
|
||||
# But if we kill the cache, we get a new object
|
||||
>>> del p._restaurant_cache
|
||||
>>> p.restaurant is r
|
||||
False
|
||||
|
||||
# Reassigning the Restaurant object results in an immediate cache update
|
||||
# We can't use a new Restaurant because that'll violate one-to-one, but
|
||||
# with a new *instance* the is test below will fail if #6886 regresses.
|
||||
>>> r2 = Restaurant.objects.get(pk=r.pk)
|
||||
>>> p.restaurant = r2
|
||||
>>> p.restaurant is r2
|
||||
True
|
||||
|
||||
# Assigning None fails: Place.restaurant is null=False
|
||||
>>> p.restaurant = None
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: Cannot assign None: "Place.restaurant" does not allow null values.
|
||||
|
||||
# You also can't assign an object of the wrong type here
|
||||
>>> p.restaurant = p
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: Cannot assign "<Place: Demon Dogs the place>": "Place.restaurant" must be a "Restaurant" instance.
|
||||
|
||||
"""}
|
||||
|
@ -503,8 +503,15 @@ True
|
||||
|
||||
# Despite having some extra aliases in the query, we can still omit them in a
|
||||
# values() query.
|
||||
>>> qs.values('id', 'rank').order_by('id')
|
||||
[{'id': 1, 'rank': 2}, {'id': 2, 'rank': 1}, {'id': 3, 'rank': 3}]
|
||||
>>> dicts = qs.values('id', 'rank').order_by('id')
|
||||
>>> [sorted(d.items()) for d in dicts]
|
||||
[[('id', 1), ('rank', 2)], [('id', 2), ('rank', 1)], [('id', 3), ('rank', 3)]]
|
||||
|
||||
Bug #7256
|
||||
# An empty values() call includes all aliases, including those from an extra()
|
||||
>>> dicts = qs.values().order_by('id')
|
||||
>>> [sorted(d.items()) for d in dicts]
|
||||
[[('author_id', 2), ('good', 0), ('id', 1), ('rank', 2)], [('author_id', 3), ('good', 0), ('id', 2), ('rank', 1)], [('author_id', 1), ('good', 1), ('id', 3), ('rank', 3)]]
|
||||
|
||||
Bugs #2874, #3002
|
||||
>>> qs = Item.objects.select_related().order_by('note__note', 'name')
|
||||
|
@ -223,3 +223,23 @@ class ModifyingSaveData(models.Model):
|
||||
"A save method that modifies the data in the object"
|
||||
self.data = 666
|
||||
super(ModifyingSaveData, self).save(raw)
|
||||
|
||||
# Tests for serialization of models using inheritance.
|
||||
# Regression for #7202, #7350
|
||||
class AbstractBaseModel(models.Model):
|
||||
parent_data = models.IntegerField()
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
class InheritAbstractModel(AbstractBaseModel):
|
||||
child_data = models.IntegerField()
|
||||
|
||||
class BaseModel(models.Model):
|
||||
parent_data = models.IntegerField()
|
||||
|
||||
class InheritBaseModel(BaseModel):
|
||||
child_data = models.IntegerField()
|
||||
|
||||
class ExplicitInheritBaseModel(BaseModel):
|
||||
parent = models.OneToOneField(BaseModel)
|
||||
child_data = models.IntegerField()
|
||||
|
@ -32,7 +32,7 @@ def data_create(pk, klass, data):
|
||||
instance = klass(id=pk)
|
||||
instance.data = data
|
||||
models.Model.save_base(instance, raw=True)
|
||||
return instance
|
||||
return [instance]
|
||||
|
||||
def generic_create(pk, klass, data):
|
||||
instance = klass(id=pk)
|
||||
@ -40,32 +40,45 @@ def generic_create(pk, klass, data):
|
||||
models.Model.save_base(instance, raw=True)
|
||||
for tag in data[1:]:
|
||||
instance.tags.create(data=tag)
|
||||
return instance
|
||||
return [instance]
|
||||
|
||||
def fk_create(pk, klass, data):
|
||||
instance = klass(id=pk)
|
||||
setattr(instance, 'data_id', data)
|
||||
models.Model.save_base(instance, raw=True)
|
||||
return instance
|
||||
return [instance]
|
||||
|
||||
def m2m_create(pk, klass, data):
|
||||
instance = klass(id=pk)
|
||||
models.Model.save_base(instance, raw=True)
|
||||
instance.data = data
|
||||
return instance
|
||||
return [instance]
|
||||
|
||||
def o2o_create(pk, klass, data):
|
||||
instance = klass()
|
||||
instance.data_id = data
|
||||
models.Model.save_base(instance, raw=True)
|
||||
return instance
|
||||
return [instance]
|
||||
|
||||
def pk_create(pk, klass, data):
|
||||
instance = klass()
|
||||
instance.data = data
|
||||
models.Model.save_base(instance, raw=True)
|
||||
return instance
|
||||
return [instance]
|
||||
|
||||
def inherited_create(pk, klass, data):
|
||||
instance = klass(id=pk,**data)
|
||||
# This isn't a raw save because:
|
||||
# 1) we're testing inheritance, not field behaviour, so none
|
||||
# of the field values need to be protected.
|
||||
# 2) saving the child class and having the parent created
|
||||
# automatically is easier than manually creating both.
|
||||
models.Model.save(instance)
|
||||
created = [instance]
|
||||
for klass,field in instance._meta.parents.items():
|
||||
created.append(klass.objects.get(id=pk))
|
||||
return created
|
||||
|
||||
# A set of functions that can be used to compare
|
||||
# test data objects of various kinds
|
||||
def data_compare(testcase, pk, klass, data):
|
||||
@ -94,6 +107,11 @@ def pk_compare(testcase, pk, klass, data):
|
||||
instance = klass.objects.get(data=data)
|
||||
testcase.assertEqual(data, instance.data)
|
||||
|
||||
def inherited_compare(testcase, pk, klass, data):
|
||||
instance = klass.objects.get(id=pk)
|
||||
for key,value in data.items():
|
||||
testcase.assertEqual(value, getattr(instance,key))
|
||||
|
||||
# Define some data types. Each data type is
|
||||
# actually a pair of functions; one to create
|
||||
# and one to compare objects of that type
|
||||
@ -103,6 +121,7 @@ fk_obj = (fk_create, fk_compare)
|
||||
m2m_obj = (m2m_create, m2m_compare)
|
||||
o2o_obj = (o2o_create, o2o_compare)
|
||||
pk_obj = (pk_create, pk_compare)
|
||||
inherited_obj = (inherited_create, inherited_compare)
|
||||
|
||||
test_data = [
|
||||
# Format: (data type, PK value, Model Class, data)
|
||||
@ -255,6 +274,10 @@ The end."""),
|
||||
|
||||
(data_obj, 800, AutoNowDateTimeData, datetime.datetime(2006,6,16,10,42,37)),
|
||||
(data_obj, 810, ModifyingSaveData, 42),
|
||||
|
||||
(inherited_obj, 900, InheritAbstractModel, {'child_data':37,'parent_data':42}),
|
||||
(inherited_obj, 910, ExplicitInheritBaseModel, {'child_data':37,'parent_data':42}),
|
||||
(inherited_obj, 920, InheritBaseModel, {'child_data':37,'parent_data':42}),
|
||||
]
|
||||
|
||||
# Because Oracle treats the empty string as NULL, Oracle is expected to fail
|
||||
@ -277,13 +300,19 @@ def serializerTest(format, self):
|
||||
|
||||
# Create all the objects defined in the test data
|
||||
objects = []
|
||||
instance_count = {}
|
||||
transaction.enter_transaction_management()
|
||||
transaction.managed(True)
|
||||
for (func, pk, klass, datum) in test_data:
|
||||
objects.append(func[0](pk, klass, datum))
|
||||
objects.extend(func[0](pk, klass, datum))
|
||||
instance_count[klass] = 0
|
||||
transaction.commit()
|
||||
transaction.leave_transaction_management()
|
||||
|
||||
# Get a count of the number of objects created for each class
|
||||
for klass in instance_count:
|
||||
instance_count[klass] = klass.objects.count()
|
||||
|
||||
# Add the generic tagged objects to the object list
|
||||
objects.extend(Tag.objects.all())
|
||||
|
||||
@ -304,6 +333,11 @@ def serializerTest(format, self):
|
||||
for (func, pk, klass, datum) in test_data:
|
||||
func[1](self, pk, klass, datum)
|
||||
|
||||
# Assert that the number of objects deserialized is the
|
||||
# same as the number that was serialized.
|
||||
for klass, count in instance_count.items():
|
||||
self.assertEquals(count, klass.objects.count())
|
||||
|
||||
def fieldsTest(format, self):
|
||||
# Clear the database first
|
||||
management.call_command('flush', verbosity=0, interactive=False)
|
||||
|
@ -16,5 +16,41 @@
|
||||
"email": "testclient@example.com",
|
||||
"date_joined": "2006-12-17 07:03:31"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "2",
|
||||
"model": "auth.user",
|
||||
"fields": {
|
||||
"username": "inactive",
|
||||
"first_name": "Inactive",
|
||||
"last_name": "User",
|
||||
"is_active": false,
|
||||
"is_superuser": false,
|
||||
"is_staff": false,
|
||||
"last_login": "2006-12-17 07:03:31",
|
||||
"groups": [],
|
||||
"user_permissions": [],
|
||||
"password": "sha1$6efc0$f93efe9fd7542f25a7be94871ea45aa95de57161",
|
||||
"email": "testclient@example.com",
|
||||
"date_joined": "2006-12-17 07:03:31"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": "3",
|
||||
"model": "auth.user",
|
||||
"fields": {
|
||||
"username": "staff",
|
||||
"first_name": "Staff",
|
||||
"last_name": "Member",
|
||||
"is_active": true,
|
||||
"is_superuser": false,
|
||||
"is_staff": true,
|
||||
"last_login": "2006-12-17 07:03:31",
|
||||
"groups": [],
|
||||
"user_permissions": [],
|
||||
"password": "sha1$6efc0$f93efe9fd7542f25a7be94871ea45aa95de57161",
|
||||
"email": "testclient@example.com",
|
||||
"date_joined": "2006-12-17 07:03:31"
|
||||
}
|
||||
}
|
||||
]
|
@ -4,6 +4,7 @@ Regression tests for the Test Client, especially the customized assertions.
|
||||
"""
|
||||
from django.test import Client, TestCase
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.exceptions import SuspiciousOperation
|
||||
import os
|
||||
|
||||
class AssertContainsTests(TestCase):
|
||||
@ -11,12 +12,18 @@ class AssertContainsTests(TestCase):
|
||||
"Responses can be inspected for content, including counting repeated substrings"
|
||||
response = self.client.get('/test_client_regress/no_template_view/')
|
||||
|
||||
self.assertNotContains(response, 'never')
|
||||
self.assertContains(response, 'never', 0)
|
||||
self.assertContains(response, 'once')
|
||||
self.assertContains(response, 'once', 1)
|
||||
self.assertContains(response, 'twice')
|
||||
self.assertContains(response, 'twice', 2)
|
||||
|
||||
try:
|
||||
self.assertNotContains(response, 'once')
|
||||
except AssertionError, e:
|
||||
self.assertEquals(str(e), "Response should not contain 'once'")
|
||||
|
||||
try:
|
||||
self.assertContains(response, 'never', 1)
|
||||
except AssertionError, e:
|
||||
@ -288,4 +295,26 @@ class URLEscapingTests(TestCase):
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.content, 'Hi, Arthur')
|
||||
|
||||
class ExceptionTests(TestCase):
|
||||
fixtures = ['testdata.json']
|
||||
|
||||
def test_exception_cleared(self):
|
||||
"#5836 - A stale user exception isn't re-raised by the test client."
|
||||
|
||||
login = self.client.login(username='testclient',password='password')
|
||||
self.failUnless(login, 'Could not log in')
|
||||
try:
|
||||
response = self.client.get("/test_client_regress/staff_only/")
|
||||
self.fail("General users should not be able to visit this page")
|
||||
except SuspiciousOperation:
|
||||
pass
|
||||
|
||||
# At this point, an exception has been raised, and should be cleared.
|
||||
|
||||
# This next operation should be successful; if it isn't we have a problem.
|
||||
login = self.client.login(username='staff', password='password')
|
||||
self.failUnless(login, 'Could not log in')
|
||||
try:
|
||||
self.client.get("/test_client_regress/staff_only/")
|
||||
except SuspiciousOperation:
|
||||
self.fail("Staff should be able to visit this page")
|
||||
|
@ -4,6 +4,7 @@ import views
|
||||
urlpatterns = patterns('',
|
||||
(r'^no_template_view/$', views.no_template_view),
|
||||
(r'^file_upload/$', views.file_upload_view),
|
||||
(r'^staff_only/$', views.staff_only_view),
|
||||
(r'^get_view/$', views.get_view),
|
||||
url(r'^arg_view/(?P<name>.+)/$', views.view_with_argument, name='arg_view'),
|
||||
(r'^login_protected_redirect_view/$', views.login_protected_redirect_view)
|
||||
|
@ -1,5 +1,8 @@
|
||||
import os
|
||||
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseServerError
|
||||
from django.core.exceptions import SuspiciousOperation
|
||||
|
||||
def no_template_view(request):
|
||||
"A simple view that expects a GET request, and returns a rendered template"
|
||||
@ -13,10 +16,21 @@ def file_upload_view(request):
|
||||
form_data = request.POST.copy()
|
||||
form_data.update(request.FILES)
|
||||
if isinstance(form_data['file_field'], dict) and isinstance(form_data['name'], unicode):
|
||||
# If a file is posted, the dummy client should only post the file name,
|
||||
# not the full path.
|
||||
if os.path.dirname(form_data['file_field']['filename']) != '':
|
||||
return HttpResponseServerError()
|
||||
return HttpResponse('')
|
||||
else:
|
||||
return HttpResponseServerError()
|
||||
|
||||
def staff_only_view(request):
|
||||
"A view that can only be visited by staff. Non staff members get an exception"
|
||||
if request.user.is_staff:
|
||||
return HttpResponse('')
|
||||
else:
|
||||
raise SuspiciousOperation()
|
||||
|
||||
def get_view(request):
|
||||
"A simple login protected view"
|
||||
return HttpResponse("Hello world")
|
||||
|
@ -118,7 +118,6 @@ def django_tests(verbosity, interactive, test_labels):
|
||||
get_apps()
|
||||
|
||||
# Load all the test model apps.
|
||||
test_models = []
|
||||
for model_dir, model_name in get_test_models():
|
||||
model_label = '.'.join([model_dir, model_name])
|
||||
try:
|
||||
@ -142,7 +141,13 @@ def django_tests(verbosity, interactive, test_labels):
|
||||
model_label = '.'.join([model_dir, model_name])
|
||||
if not test_labels or model_name in test_labels:
|
||||
extra_tests.append(InvalidModelTestCase(model_label))
|
||||
|
||||
try:
|
||||
# Invalid models are not working apps, so we cannot pass them into
|
||||
# the test runner with the other test_labels
|
||||
test_labels.remove(model_name)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Run the test suite, including the extra validation tests.
|
||||
from django.test.simple import run_tests
|
||||
failures = run_tests(test_labels, verbosity=verbosity, interactive=interactive, extra_tests=extra_tests)
|
||||
|
Loading…
x
Reference in New Issue
Block a user