diff options
| author | Malcolm Tredinnick <malcolm.tredinnick@gmail.com> | 2007-10-08 16:10:39 +0000 |
|---|---|---|
| committer | Malcolm Tredinnick <malcolm.tredinnick@gmail.com> | 2007-10-08 16:10:39 +0000 |
| commit | 7ebf3068c16d29c0288a317dac45166a8bc4c23c (patch) | |
| tree | 537b7f487e515d6870e6a61f41588b3d351256f2 | |
| parent | 94c320d8a982ce30f6dd4e7556f2696229b761c7 (diff) | |
queryset-refactor: Merged changed from trunk up to [6463].
git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@6466 bcc190cf-cafb-0310-a4f2-bffc1f526a37
75 files changed, 789 insertions, 324 deletions
@@ -114,6 +114,7 @@ answer newbie questions, and generally made Django that much better: Enrico <rico.bl@gmail.com> A. Murat Eren <meren@pardus.org.tr> Ludvig Ericson <ludvig.ericson@gmail.com> + eriks@win.tue.nl Dirk Eschler <dirk.eschler@gmx.net> Marc Fargas <telenieko@telenieko.com> Szilveszter Farkas <szilveszter.farkas@gmail.com> @@ -234,6 +235,7 @@ answer newbie questions, and generally made Django that much better: Jay Parlar <parlar@gmail.com> pavithran s <pavithran.s@gmail.com> Barry Pederson <bp@barryp.org> + permonik@mesias.brnonet.cz petr.marhoun@gmail.com pgross@thoughtworks.com phaedo <http://phaedo.cx/> @@ -312,7 +314,8 @@ answer newbie questions, and generally made Django that much better: Vlado <vlado@labath.org> Milton Waddams wam-djangobug@wamber.net - wangchun <yaohua2000@gmail.com> + Wang Chun <wangchun@exoweb.net> + Filip Wasilewski <filip.wasilewski@gmail.com> Filip Wasilewski <filip.wasilewski@gmail.com> Dan Watson <http://theidioteque.net/> Chris Wesseling <Chris.Wesseling@cwi.nl> @@ -321,6 +324,7 @@ answer newbie questions, and generally made Django that much better: Rachel Willmer <http://www.willmer.com/kb/> Gary Wilson <gary.wilson@gmail.com> Jakub Wiśniowski <restless.being@gmail.com> + Maciej Wiśniowski <pigletto@gmail.com> wojtek ye7cakf02@sneakemail.com ymasuda@ethercube.com diff --git a/django/bin/compile-messages.py b/django/bin/compile-messages.py index 2838cb8aa4..8693022644 100755 --- a/django/bin/compile-messages.py +++ b/django/bin/compile-messages.py @@ -14,7 +14,8 @@ def compile_messages(locale=None): basedirs = [os.path.join('conf', 'locale'), 'locale'] if os.environ.get('DJANGO_SETTINGS_MODULE'): from django.conf import settings - basedirs += settings.LOCALE_PATHS + if hasattr(settings, 'LOCALE_PATHS'): + basedirs += settings.LOCALE_PATHS # Gather existing directories. basedirs = set(map(os.path.abspath, filter(os.path.isdir, basedirs))) diff --git a/django/bin/make-messages.py b/django/bin/make-messages.py index 11616c9ca5..44040390ea 100755 --- a/django/bin/make-messages.py +++ b/django/bin/make-messages.py @@ -74,59 +74,62 @@ def make_messages(): if os.path.exists(potfile): os.unlink(potfile) + all_files = [] for (dirpath, dirnames, filenames) in os.walk("."): - for file in filenames: - if domain == 'djangojs' and file.endswith('.js'): - if verbose: sys.stdout.write('processing file %s in %s\n' % (file, dirpath)) + all_files.extend([(dirpath, f) for f in filenames]) + all_files.sort() + for dirpath, file in all_files: + if domain == 'djangojs' and file.endswith('.js'): + if verbose: sys.stdout.write('processing file %s in %s\n' % (file, dirpath)) + src = open(os.path.join(dirpath, file), "rb").read() + src = pythonize_re.sub('\n#', src) + open(os.path.join(dirpath, '%s.py' % file), "wb").write(src) + thefile = '%s.py' % file + cmd = 'xgettext %s -d %s -L Perl --keyword=gettext_noop --keyword=gettext_lazy --keyword=ngettext_lazy:1,2 --from-code UTF-8 -o - "%s"' % ( + os.path.exists(potfile) and '--omit-header' or '', domain, os.path.join(dirpath, thefile)) + (stdin, stdout, stderr) = os.popen3(cmd, 't') + msgs = stdout.read() + errors = stderr.read() + if errors: + print "errors happened while running xgettext on %s" % file + print errors + sys.exit(8) + old = '#: '+os.path.join(dirpath, thefile)[2:] + new = '#: '+os.path.join(dirpath, file)[2:] + msgs = msgs.replace(old, new) + if msgs: + open(potfile, 'ab').write(msgs) + os.unlink(os.path.join(dirpath, thefile)) + elif domain == 'django' and (file.endswith('.py') or file.endswith('.html')): + thefile = file + if file.endswith('.html'): src = open(os.path.join(dirpath, file), "rb").read() - src = pythonize_re.sub('\n#', src) - open(os.path.join(dirpath, '%s.py' % file), "wb").write(src) thefile = '%s.py' % file - cmd = 'xgettext %s -d %s -L Perl --keyword=gettext_noop --keyword=gettext_lazy --keyword=ngettext_lazy:1,2 --from-code UTF-8 -o - "%s"' % ( - os.path.exists(potfile) and '--omit-header' or '', domain, os.path.join(dirpath, thefile)) - (stdin, stdout, stderr) = os.popen3(cmd, 't') - msgs = stdout.read() - errors = stderr.read() - if errors: - print "errors happened while running xgettext on %s" % file - print errors - sys.exit(8) + open(os.path.join(dirpath, thefile), "wb").write(templatize(src)) + if verbose: + sys.stdout.write('processing file %s in %s\n' % (file, dirpath)) + cmd = 'xgettext -d %s -L Python --keyword=gettext_noop --keyword=gettext_lazy --keyword=ngettext_lazy:1,2 --keyword=ugettext_noop --keyword=ugettext_lazy --keyword=ungettext_lazy:1,2 --from-code UTF-8 -o - "%s"' % ( + domain, os.path.join(dirpath, thefile)) + (stdin, stdout, stderr) = os.popen3(cmd, 't') + msgs = stdout.read() + errors = stderr.read() + if errors: + print "errors happened while running xgettext on %s" % file + print errors + sys.exit(8) + if thefile != file: old = '#: '+os.path.join(dirpath, thefile)[2:] new = '#: '+os.path.join(dirpath, file)[2:] msgs = msgs.replace(old, new) - if msgs: - open(potfile, 'ab').write(msgs) + if os.path.exists(potfile): + # Strip the header + msgs = '\n'.join(dropwhile(len, msgs.split('\n'))) + else: + msgs = msgs.replace('charset=CHARSET', 'charset=UTF-8') + if msgs: + open(potfile, 'ab').write(msgs) + if thefile != file: os.unlink(os.path.join(dirpath, thefile)) - elif domain == 'django' and (file.endswith('.py') or file.endswith('.html')): - thefile = file - if file.endswith('.html'): - src = open(os.path.join(dirpath, file), "rb").read() - thefile = '%s.py' % file - open(os.path.join(dirpath, thefile), "wb").write(templatize(src)) - if verbose: - sys.stdout.write('processing file %s in %s\n' % (file, dirpath)) - cmd = 'xgettext -d %s -L Python --keyword=gettext_noop --keyword=gettext_lazy --keyword=ngettext_lazy:1,2 --keyword=ugettext_noop --keyword=ugettext_lazy --keyword=ungettext_lazy:1,2 --from-code UTF-8 -o - "%s"' % ( - domain, os.path.join(dirpath, thefile)) - (stdin, stdout, stderr) = os.popen3(cmd, 't') - msgs = stdout.read() - errors = stderr.read() - if errors: - print "errors happened while running xgettext on %s" % file - print errors - sys.exit(8) - if thefile != file: - old = '#: '+os.path.join(dirpath, thefile)[2:] - new = '#: '+os.path.join(dirpath, file)[2:] - msgs = msgs.replace(old, new) - if os.path.exists(potfile): - # Strip the header - msgs = '\n'.join(dropwhile(len, msgs.split('\n'))) - else: - msgs = msgs.replace('charset=CHARSET', 'charset=UTF-8') - if msgs: - open(potfile, 'ab').write(msgs) - if thefile != file: - os.unlink(os.path.join(dirpath, thefile)) if os.path.exists(potfile): (stdin, stdout, stderr) = os.popen3('msguniq --to-code=utf-8 "%s"' % potfile, 'b') diff --git a/django/conf/locale/de/LC_MESSAGES/django.mo b/django/conf/locale/de/LC_MESSAGES/django.mo Binary files differindex 307976e4d5..2699d25e55 100644 --- a/django/conf/locale/de/LC_MESSAGES/django.mo +++ b/django/conf/locale/de/LC_MESSAGES/django.mo diff --git a/django/conf/locale/de/LC_MESSAGES/django.po b/django/conf/locale/de/LC_MESSAGES/django.po index 6978cf2d44..50fa66d43f 100644 --- a/django/conf/locale/de/LC_MESSAGES/django.po +++ b/django/conf/locale/de/LC_MESSAGES/django.po @@ -1,6 +1,7 @@ -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# Translation of django.po to German +# +# Copyright (C) 2005-2007, +# This file is distributed under the same license as the django package. # msgid "" "" @@ -1629,76 +1630,76 @@ msgstr "Webseite" msgid "flat pages" msgstr "Webseiten" -#: contrib/humanize/templatetags/humanize.py:17 +#: contrib/humanize/templatetags/humanize.py:20 msgid "th" -msgstr "" +msgstr "." -#: contrib/humanize/templatetags/humanize.py:17 +#: contrib/humanize/templatetags/humanize.py:20 msgid "st" -msgstr "" +msgstr "." -#: contrib/humanize/templatetags/humanize.py:17 +#: contrib/humanize/templatetags/humanize.py:20 msgid "nd" -msgstr "" +msgstr "." -#: contrib/humanize/templatetags/humanize.py:17 +#: contrib/humanize/templatetags/humanize.py:20 msgid "rd" -msgstr "" +msgstr "." -#: contrib/humanize/templatetags/humanize.py:47 +#: contrib/humanize/templatetags/humanize.py:50 #, python-format msgid "%(value).1f million" msgid_plural "%(value).1f million" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%(value).1f Million" +msgstr[1] "%(value).1f Millionen" -#: contrib/humanize/templatetags/humanize.py:50 +#: contrib/humanize/templatetags/humanize.py:53 #, python-format msgid "%(value).1f billion" msgid_plural "%(value).1f billion" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%(value).1f Milliarde" +msgstr[1] "%(value).1f Milliarden" -#: contrib/humanize/templatetags/humanize.py:53 +#: contrib/humanize/templatetags/humanize.py:56 #, python-format msgid "%(value).1f trillion" msgid_plural "%(value).1f trillion" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%(value).1f Billion" +msgstr[1] "%(value).1f Billionen" -#: contrib/humanize/templatetags/humanize.py:68 +#: contrib/humanize/templatetags/humanize.py:71 msgid "one" msgstr "ein" -#: contrib/humanize/templatetags/humanize.py:68 +#: contrib/humanize/templatetags/humanize.py:71 msgid "two" msgstr "zwei" -#: contrib/humanize/templatetags/humanize.py:68 +#: contrib/humanize/templatetags/humanize.py:71 msgid "three" msgstr "drei" -#: contrib/humanize/templatetags/humanize.py:68 +#: contrib/humanize/templatetags/humanize.py:71 msgid "four" msgstr "vier" -#: contrib/humanize/templatetags/humanize.py:68 +#: contrib/humanize/templatetags/humanize.py:71 msgid "five" msgstr "fünf" -#: contrib/humanize/templatetags/humanize.py:68 +#: contrib/humanize/templatetags/humanize.py:71 msgid "six" msgstr "sechs" -#: contrib/humanize/templatetags/humanize.py:68 +#: contrib/humanize/templatetags/humanize.py:71 msgid "seven" msgstr "sieben" -#: contrib/humanize/templatetags/humanize.py:68 +#: contrib/humanize/templatetags/humanize.py:71 msgid "eight" msgstr "acht" -#: contrib/humanize/templatetags/humanize.py:68 +#: contrib/humanize/templatetags/humanize.py:71 msgid "nine" msgstr "neun" diff --git a/django/contrib/admin/templates/admin/auth/user/change_password.html b/django/contrib/admin/templates/admin/auth/user/change_password.html index d9c1eae637..28e342da86 100644 --- a/django/contrib/admin/templates/admin/auth/user/change_password.html +++ b/django/contrib/admin/templates/admin/auth/user/change_password.html @@ -6,7 +6,6 @@ {% endblock %} {% block stylesheet %}{% admin_media_prefix %}css/forms.css{% endblock %} {% block bodyclass %}{{ opts.app_label }}-{{ opts.object_name.lower }} change-form{% endblock %} -{% block userlinks %}<a href="../../../../doc/">{% trans 'Documentation' %}</a> / <a href="../../../../password_change/">{% trans 'Change password' %}</a> / <a href="../../../../logout/">{% trans 'Log out' %}</a>{% endblock %} {% block breadcrumbs %}{% if not is_popup %} <div class="breadcrumbs"> <a href="../../../../">{% trans "Home" %}</a> › diff --git a/django/contrib/admin/templates/admin/base.html b/django/contrib/admin/templates/admin/base.html index d3e8c96b91..cdd3561ab9 100644 --- a/django/contrib/admin/templates/admin/base.html +++ b/django/contrib/admin/templates/admin/base.html @@ -22,7 +22,14 @@ {% block branding %}{% endblock %} </div> {% if user.is_authenticated and user.is_staff %} - <div id="user-tools">{% trans 'Welcome,' %} <strong>{% if user.first_name %}{{ user.first_name|escape }}{% else %}{{ user.username }}{% endif %}</strong>. {% block userlinks %}<a href="doc/">{% trans 'Documentation' %}</a> / <a href="password_change/">{% trans 'Change password' %}</a> / <a href="logout/">{% trans 'Log out' %}</a>{% endblock %}</div> + <div id="user-tools"> + {% trans 'Welcome,' %} <strong>{% if user.first_name %}{{ user.first_name|escape }}{% else %}{{ user.username }}{% endif %}</strong>. + {% block userlinks %} + <a href="{% url django.contrib.admin.views.doc.doc_index %}">{% trans 'Documentation' %}</a> + / <a href="{% url django.contrib.auth.views.password_change %}">{% trans 'Change password' %}</a> + / <a href="{% url django.contrib.auth.views.logout %}">{% trans 'Log out' %}</a> + {% endblock %} + </div> {% endif %} {% block nav-global %}{% endblock %} </div> diff --git a/django/contrib/admin/templates/admin/change_form.html b/django/contrib/admin/templates/admin/change_form.html index 7e7b639139..49e709caf6 100644 --- a/django/contrib/admin/templates/admin/change_form.html +++ b/django/contrib/admin/templates/admin/change_form.html @@ -7,7 +7,6 @@ {% block stylesheet %}{% admin_media_prefix %}css/forms.css{% endblock %} {% block coltype %}{% if ordered_objects %}colMS{% else %}colM{% endif %}{% endblock %} {% block bodyclass %}{{ opts.app_label }}-{{ opts.object_name.lower }} change-form{% endblock %} -{% block userlinks %}<a href="../../../doc/">{% trans 'Documentation' %}</a> / <a href="../../../password_change/">{% trans 'Change password' %}</a> / <a href="../../../logout/">{% trans 'Log out' %}</a>{% endblock %} {% block breadcrumbs %}{% if not is_popup %} <div class="breadcrumbs"> <a href="../../../">{% trans "Home" %}</a> › diff --git a/django/contrib/admin/templates/admin/change_list.html b/django/contrib/admin/templates/admin/change_list.html index f50a73c934..611c5ff8fc 100644 --- a/django/contrib/admin/templates/admin/change_list.html +++ b/django/contrib/admin/templates/admin/change_list.html @@ -2,7 +2,6 @@ {% load adminmedia admin_list i18n %} {% block stylesheet %}{% admin_media_prefix %}css/changelists.css{% endblock %} {% block bodyclass %}change-list{% endblock %} -{% block userlinks %}<a href="../../doc/">{% trans 'Documentation' %}</a> / <a href="../../password_change/">{% trans 'Change password' %}</a> / <a href="../../logout/">{% trans 'Log out' %}</a>{% endblock %} {% if not is_popup %}{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans "Home" %}</a> › {{ cl.opts.verbose_name_plural|capfirst|escape }}</div>{% endblock %}{% endif %} {% block coltype %}flex{% endblock %} {% block content %} diff --git a/django/contrib/admin/templates/admin/delete_confirmation.html b/django/contrib/admin/templates/admin/delete_confirmation.html index 3921ab69e3..ac9634a9e9 100644 --- a/django/contrib/admin/templates/admin/delete_confirmation.html +++ b/django/contrib/admin/templates/admin/delete_confirmation.html @@ -1,6 +1,5 @@ {% extends "admin/base_site.html" %} {% load i18n %} -{% block userlinks %}<a href="../../../../doc/">{% trans 'Documentation' %}</a> / <a href="../../../../password_change/">{% trans 'Change password' %}</a> / <a href="../../../../logout/">{% trans 'Log out' %}</a>{% endblock %} {% block breadcrumbs %} <div class="breadcrumbs"> <a href="../../../../">{% trans "Home" %}</a> › diff --git a/django/contrib/admin/templates/admin/object_history.html b/django/contrib/admin/templates/admin/object_history.html index 14a77b8a31..34529f69db 100644 --- a/django/contrib/admin/templates/admin/object_history.html +++ b/django/contrib/admin/templates/admin/object_history.html @@ -1,6 +1,5 @@ {% extends "admin/base_site.html" %} {% load i18n %} -{% block userlinks %}<a href="../../../../doc/">{% trans 'Documentation' %}</a> / <a href="../../../../password_change/">{% trans 'Change password' %}</a> / <a href="../../../../logout/">{% trans 'Log out' %}</a>{% endblock %} {% block breadcrumbs %} <div class="breadcrumbs"><a href="../../../../">{% trans 'Home' %}</a> › <a href="../../">{{ module_name|escape }}</a> › <a href="../">{{ object|escape|truncatewords:"18" }}</a> › {% trans 'History' %}</div> {% endblock %} diff --git a/django/contrib/admin/templates/admin_doc/bookmarklets.html b/django/contrib/admin/templates/admin_doc/bookmarklets.html index fa5942926f..64475299c2 100644 --- a/django/contrib/admin/templates/admin_doc/bookmarklets.html +++ b/django/contrib/admin/templates/admin_doc/bookmarklets.html @@ -1,7 +1,6 @@ {% extends "admin/base_site.html" %} {% block breadcrumbs %}{% load i18n %}<div class="breadcrumbs"><a href="../../">{% trans "Home" %}</a> › <a href="../">{% trans "Documentation" %}</a> › {% trans "Bookmarklets" %}</div>{% endblock %} -{% block userlinks %}<a href="../../password_change/">{% trans 'Change password' %}</a> / <a href="../../logout/">{% trans 'Log out' %}</a>{% endblock %} {% block title %}{% trans "Documentation bookmarklets" %}{% endblock %} {% block content %} diff --git a/django/contrib/admin/templates/admin_doc/index.html b/django/contrib/admin/templates/admin_doc/index.html index 74f9f20916..64766fb46f 100644 --- a/django/contrib/admin/templates/admin_doc/index.html +++ b/django/contrib/admin/templates/admin_doc/index.html @@ -1,7 +1,6 @@ {% extends "admin/base_site.html" %}
{% load i18n %}
{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">Home</a> › Documentation</div>{% endblock %}
-{% block userlinks %}<a href="../password_change/">{% trans 'Change password' %}</a> / <a href="../logout/">{% trans 'Log out' %}</a>{% endblock %}
{% block title %}Documentation{% endblock %}
{% block content %}
diff --git a/django/contrib/admin/templates/admin_doc/missing_docutils.html b/django/contrib/admin/templates/admin_doc/missing_docutils.html index cdeab0cdd8..97c9d4711c 100644 --- a/django/contrib/admin/templates/admin_doc/missing_docutils.html +++ b/django/contrib/admin/templates/admin_doc/missing_docutils.html @@ -1,7 +1,6 @@ {% extends "admin/base_site.html" %} {% load i18n %} {% block breadcrumbs %}<div class="breadcrumbs"><a href="../">Home</a> › Documentation</div>{% endblock %} -{% block userlinks %}<a href="../password_change/">{% trans 'Change password' %}</a> / <a href="../logout/">{% trans 'Log out' %}</a>{% endblock %} {% block title %}Please install docutils{% endblock %} {% block content %} diff --git a/django/contrib/admin/templates/admin_doc/model_detail.html b/django/contrib/admin/templates/admin_doc/model_detail.html index 70133e26dd..e19e31d891 100644 --- a/django/contrib/admin/templates/admin_doc/model_detail.html +++ b/django/contrib/admin/templates/admin_doc/model_detail.html @@ -1,6 +1,5 @@ {% extends "admin/base_site.html" %} {% load i18n %} -{% block userlinks %}<a href="../../../password_change/">{% trans 'Change password' %}</a> / <a href="../../../logout/">{% trans 'Log out' %}</a>{% endblock %} {% block extrahead %} {{ block.super }} <style type="text/css"> diff --git a/django/contrib/admin/templates/admin_doc/model_index.html b/django/contrib/admin/templates/admin_doc/model_index.html index c681da75c9..702fdff329 100644 --- a/django/contrib/admin/templates/admin_doc/model_index.html +++ b/django/contrib/admin/templates/admin_doc/model_index.html @@ -2,7 +2,6 @@ {% load i18n %} {% block coltype %}colSM{% endblock %} {% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> › <a href="../">Documentation</a> › Models</div>{% endblock %} -{% block userlinks %}<a href="../../password_change/">{% trans 'Change password' %}</a> / <a href="../../logout/">{% trans 'Log out' %}</a>{% endblock %} {% block title %}Models{% endblock %} diff --git a/django/contrib/admin/templates/admin_doc/template_detail.html b/django/contrib/admin/templates/admin_doc/template_detail.html index 280ea912d0..a8b02bd189 100644 --- a/django/contrib/admin/templates/admin_doc/template_detail.html +++ b/django/contrib/admin/templates/admin_doc/template_detail.html @@ -1,7 +1,6 @@ {% extends "admin/base_site.html" %} {% load i18n %} {% block breadcrumbs %}<div class="breadcrumbs"><a href="../../../">Home</a> › <a href="../../">Documentation</a> › Templates › {{ name|escape }}</div>{% endblock %} -{% block userlinks %}<a href="../../../password_change/">{% trans 'Change password' %}</a> / <a href="../../../logout/">{% trans 'Log out' %}</a>{% endblock %} {% block title %}Template: {{ name|escape }}{% endblock %} diff --git a/django/contrib/admin/templates/admin_doc/template_filter_index.html b/django/contrib/admin/templates/admin_doc/template_filter_index.html index 72344c16cb..f67f14eb35 100644 --- a/django/contrib/admin/templates/admin_doc/template_filter_index.html +++ b/django/contrib/admin/templates/admin_doc/template_filter_index.html @@ -2,7 +2,6 @@ {% load i18n %} {% block coltype %}colSM{% endblock %} {% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> › <a href="../">Documentation</a> › filters</div>{% endblock %} -{% block userlinks %}<a href="../../password_change/">{% trans 'Change password' %}</a> / <a href="../../logout/">{% trans 'Log out' %}</a>{% endblock %} {% block title %}Template filters{% endblock %} {% block content %} diff --git a/django/contrib/admin/templates/admin_doc/template_tag_index.html b/django/contrib/admin/templates/admin_doc/template_tag_index.html index 287475ab09..9bd66b70e7 100644 --- a/django/contrib/admin/templates/admin_doc/template_tag_index.html +++ b/django/contrib/admin/templates/admin_doc/template_tag_index.html @@ -2,7 +2,6 @@ {% load i18n %} {% block coltype %}colSM{% endblock %} {% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> › <a href="../">Documentation</a> › Tags</div>{% endblock %} -{% block userlinks %}<a href="../../password_change/">{% trans 'Change password' %}</a> / <a href="../../logout/">{% trans 'Log out' %}</a>{% endblock %} {% block title %}Template tags{% endblock %} {% block content %} diff --git a/django/contrib/admin/templates/admin_doc/view_detail.html b/django/contrib/admin/templates/admin_doc/view_detail.html index ba90399358..d7415ab74f 100644 --- a/django/contrib/admin/templates/admin_doc/view_detail.html +++ b/django/contrib/admin/templates/admin_doc/view_detail.html @@ -1,7 +1,6 @@ {% extends "admin/base_site.html" %} {% load i18n %} {% block breadcrumbs %}<div class="breadcrumbs"><a href="../../../">Home</a> › <a href="../../">Documentation</a> › <a href="../">Views</a> › {{ name }}</div>{% endblock %} -{% block userlinks %}<a href="../../../password_change/">{% trans 'Change password' %}</a> / <a href="../../../logout/">{% trans 'Log out' %}</a>{% endblock %} {% block title %}View: {{ name }}{% endblock %} {% block content %} diff --git a/django/contrib/admin/templates/admin_doc/view_index.html b/django/contrib/admin/templates/admin_doc/view_index.html index caab8a2e71..47f99350a3 100644 --- a/django/contrib/admin/templates/admin_doc/view_index.html +++ b/django/contrib/admin/templates/admin_doc/view_index.html @@ -2,7 +2,6 @@ {% load i18n %}
{% block coltype %}colSM{% endblock %}
{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> › <a href="../">Documentation</a> › Views</div>{% endblock %}
-{% block userlinks %}<a href="../../password_change/">{% trans 'Change password' %}</a> / <a href="../../logout/">{% trans 'Log out' %}</a>{% endblock %}
{% block title %}Views{% endblock %}
{% block content %}
diff --git a/django/contrib/admin/templates/registration/password_change_done.html b/django/contrib/admin/templates/registration/password_change_done.html index 252572001d..4498c63a37 100644 --- a/django/contrib/admin/templates/registration/password_change_done.html +++ b/django/contrib/admin/templates/registration/password_change_done.html @@ -1,6 +1,5 @@ {% extends "admin/base_site.html" %} {% load i18n %} -{% block userlinks %}<a href="../../doc/">{% trans 'Documentation' %}</a> / {% trans 'Change password' %} / <a href="../../logout/">{% trans 'Log out' %}</a>{% endblock %} {% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans 'Home' %}</a> › {% trans 'Password change' %}</div>{% endblock %} {% block title %}{% trans 'Password change successful' %}{% endblock %} diff --git a/django/contrib/admin/templates/registration/password_change_form.html b/django/contrib/admin/templates/registration/password_change_form.html index 036d56212c..cd2b1e0c8a 100644 --- a/django/contrib/admin/templates/registration/password_change_form.html +++ b/django/contrib/admin/templates/registration/password_change_form.html @@ -1,6 +1,5 @@ {% extends "admin/base_site.html" %} {% load i18n %} -{% block userlinks %}<a href="../doc/">{% trans 'Documentation' %}</a> / {% trans 'Change password' %} / <a href="../logout/">{% trans 'Log out' %}</a>{% endblock %} {% block breadcrumbs %}<div class="breadcrumbs"><a href="../">{% trans 'Home' %}</a> › {% trans 'Password change' %}</div>{% endblock %} {% block title %}{% trans 'Password change' %}{% endblock %} diff --git a/django/contrib/admin/templatetags/admin_modify.py b/django/contrib/admin/templatetags/admin_modify.py index 5f29782903..9ce6aa77ae 100644 --- a/django/contrib/admin/templatetags/admin_modify.py +++ b/django/contrib/admin/templatetags/admin_modify.py @@ -72,7 +72,7 @@ class FieldWidgetNode(template.Node): default = None def __init__(self, bound_field_var): - self.bound_field_var = bound_field_var + self.bound_field_var = template.Variable(bound_field_var) def get_nodelist(cls, klass): if klass not in cls.nodelists: @@ -96,7 +96,7 @@ class FieldWidgetNode(template.Node): get_nodelist = classmethod(get_nodelist) def render(self, context): - bound_field = template.resolve_variable(self.bound_field_var, context) + bound_field = self.bound_field_var.resolve(context) context.push() context['bound_field'] = bound_field @@ -156,10 +156,10 @@ class StackedBoundRelatedObject(BoundRelatedObject): class EditInlineNode(template.Node): def __init__(self, rel_var): - self.rel_var = rel_var + self.rel_var = template.Variable(rel_var) def render(self, context): - relation = template.resolve_variable(self.rel_var, context) + relation = self.rel_var.resolve(context) context.push() if relation.field.rel.edit_inline == models.TABULAR: bound_related_object_class = TabularBoundRelatedObject diff --git a/django/contrib/auth/backends.py b/django/contrib/auth/backends.py index be6cfede11..f79929e2d2 100644 --- a/django/contrib/auth/backends.py +++ b/django/contrib/auth/backends.py @@ -1,6 +1,11 @@ from django.db import connection from django.contrib.auth.models import User +try: + set +except NameError: + from sets import Set as set # Python 2.3 fallback + class ModelBackend: """ Authenticate against django.contrib.auth.models.User diff --git a/django/contrib/comments/templatetags/comments.py b/django/contrib/comments/templatetags/comments.py index 1d4628978d..959cec4c7f 100644 --- a/django/contrib/comments/templatetags/comments.py +++ b/django/contrib/comments/templatetags/comments.py @@ -19,6 +19,8 @@ class CommentFormNode(template.Node): ratings_optional=False, ratings_required=False, rating_options='', is_public=True): self.content_type = content_type + if obj_id_lookup_var is not None: + obj_id_lookup_var = template.Variable(obj_id_lookup_var) self.obj_id_lookup_var, self.obj_id, self.free = obj_id_lookup_var, obj_id, free self.photos_optional, self.photos_required = photos_optional, photos_required self.ratings_optional, self.ratings_required = ratings_optional, ratings_required @@ -32,7 +34,7 @@ class CommentFormNode(template.Node): context.push() if self.obj_id_lookup_var is not None: try: - self.obj_id = template.resolve_variable(self.obj_id_lookup_var, context) + self.obj_id = self.obj_id_lookup_var.resolve(context) except template.VariableDoesNotExist: return '' # Validate that this object ID is valid for this content-type. @@ -75,6 +77,8 @@ class CommentFormNode(template.Node): class CommentCountNode(template.Node): def __init__(self, package, module, context_var_name, obj_id, var_name, free): self.package, self.module = package, module + if context_var_name is not None: + context_var_name = template.Variable(context_var_name) self.context_var_name, self.obj_id = context_var_name, obj_id self.var_name, self.free = var_name, free @@ -82,7 +86,7 @@ class CommentCountNode(template.Node): from django.conf import settings manager = self.free and FreeComment.objects or Comment.objects if self.context_var_name is not None: - self.obj_id = template.resolve_variable(self.context_var_name, context) + self.obj_id = self.context_var_name.resolve(context) comment_count = manager.filter(object_id__exact=self.obj_id, content_type__app_label__exact=self.package, content_type__model__exact=self.module, site__id__exact=settings.SITE_ID).count() @@ -92,6 +96,8 @@ class CommentCountNode(template.Node): class CommentListNode(template.Node): def __init__(self, package, module, context_var_name, obj_id, var_name, free, ordering, extra_kwargs=None): self.package, self.module = package, module + if context_var_name is not None: + context_var_name = template.Variable(context_var_name) self.context_var_name, self.obj_id = context_var_name, obj_id self.var_name, self.free = var_name, free self.ordering = ordering @@ -102,7 +108,7 @@ class CommentListNode(template.Node): get_list_function = self.free and FreeComment.objects.filter or Comment.objects.get_list_with_karma if self.context_var_name is not None: try: - self.obj_id = template.resolve_variable(self.context_var_name, context) + self.obj_id = self.context_var_name.resolve(context) except template.VariableDoesNotExist: return '' kwargs = { diff --git a/django/contrib/sessions/backends/base.py b/django/contrib/sessions/backends/base.py index 382212bb70..e6be0482de 100644 --- a/django/contrib/sessions/backends/base.py +++ b/django/contrib/sessions/backends/base.py @@ -16,7 +16,6 @@ class SessionBase(object): """ Base class for all Session classes. """ - TEST_COOKIE_NAME = 'testcookie' TEST_COOKIE_VALUE = 'worked' @@ -59,7 +58,7 @@ class SessionBase(object): def delete_test_cookie(self): del self[self.TEST_COOKIE_NAME] - + def encode(self, session_dict): "Returns the given session dictionary pickled and encoded as a string." pickled = pickle.dumps(session_dict, pickle.HIGHEST_PROTOCOL) @@ -77,28 +76,33 @@ class SessionBase(object): # just return an empty dictionary (an empty session). except: return {} - + def _get_new_session_key(self): "Returns session key that isn't being used." # The random module is seeded when this Apache child is created. # Use settings.SECRET_KEY as added salt. + try: + pid = os.getpid() + except AttributeError: + # No getpid() in Jython, for example + pid = 1 while 1: - session_key = md5.new("%s%s%s%s" % (random.randint(0, sys.maxint - 1), - os.getpid(), time.time(), settings.SECRET_KEY)).hexdigest() + session_key = md5.new("%s%s%s%s" % (random.randint(0, sys.maxint - 1), + pid, time.time(), settings.SECRET_KEY)).hexdigest() if not self.exists(session_key): break return session_key - + def _get_session_key(self): if self._session_key: return self._session_key else: self._session_key = self._get_new_session_key() return self._session_key - + def _set_session_key(self, session_key): self._session_key = session_key - + session_key = property(_get_session_key, _set_session_key) def _get_session(self): @@ -114,9 +118,9 @@ class SessionBase(object): return self._session_cache _session = property(_get_session) - + # Methods that child classes must implement. - + def exists(self, session_key): """ Returns True if the given session_key already exists. @@ -140,4 +144,3 @@ class SessionBase(object): Loads the session data and returns a dictionary. """ raise NotImplementedError - diff --git a/django/contrib/sessions/models.py b/django/contrib/sessions/models.py index c086396947..8fc5e17d69 100644 --- a/django/contrib/sessions/models.py +++ b/django/contrib/sessions/models.py @@ -1,5 +1,12 @@ -import base64, md5, random, sys, datetime +import os +import sys +import time +import datetime +import base64 +import md5 +import random import cPickle as pickle + from django.db import models from django.utils.translation import ugettext_lazy as _ from django.conf import settings diff --git a/django/core/handlers/wsgi.py b/django/core/handlers/wsgi.py index 6fe24f5d13..16d1d822e0 100644 --- a/django/core/handlers/wsgi.py +++ b/django/core/handlers/wsgi.py @@ -1,16 +1,16 @@ +from threading import Lock +from pprint import pformat +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + from django.core.handlers.base import BaseHandler from django.core import signals from django.dispatch import dispatcher from django.utils import datastructures from django.utils.encoding import force_unicode from django import http -from pprint import pformat -from shutil import copyfileobj -from threading import Lock -try: - from cStringIO import StringIO -except ImportError: - from StringIO import StringIO # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html STATUS_CODE_TEXT = { @@ -105,7 +105,8 @@ class WSGIRequest(http.HttpRequest): return '%s%s' % (self.path, self.environ.get('QUERY_STRING', '') and ('?' + self.environ.get('QUERY_STRING', '')) or '') def is_secure(self): - return 'HTTPS' in self.environ and self.environ['HTTPS'] == 'on' + return 'wsgi.url_scheme' in self.environ \ + and self.environ['wsgi.url_scheme'] == 'https' def _load_post_and_files(self): # Populates self._post and self._files diff --git a/django/core/management/__init__.py b/django/core/management/__init__.py index a4731652f5..cd90fd6109 100644 --- a/django/core/management/__init__.py +++ b/django/core/management/__init__.py @@ -1,18 +1,104 @@ import django +from django.core.management.base import BaseCommand, CommandError, handle_default_options from optparse import OptionParser import os import sys +from imp import find_module # For backwards compatibility: get_version() used to be in this module. get_version = django.get_version -def load_command_class(name): +# A cache of loaded commands, so that call_command +# doesn't have to reload every time it is called +_commands = None + +def find_commands(management_dir): + """ + Given a path to a management directory, return a list of all the command names + that are available. Returns an empty list if no commands are defined. + """ + command_dir = os.path.join(management_dir,'commands') + try: + return [f[:-3] for f in os.listdir(command_dir) if not f.startswith('_') and f.endswith('.py')] + except OSError: + return [] + +def find_management_module(app_name): """ - Given a command name, returns the Command class instance. Raises - ImportError if it doesn't exist. + Determine the path to the management module for the application named, + without acutally importing the application or the management module. + + Raises ImportError if the management module cannot be found for any reason. + """ + parts = app_name.split('.') + parts.append('management') + parts.reverse() + path = None + while parts: + part = parts.pop() + f,path,descr = find_module(part, path and [path] or None) + return path + +def load_command_class(app_name, name): """ - # Let the ImportError propogate. - return getattr(__import__('django.core.management.commands.%s' % name, {}, {}, ['Command']), 'Command')() + Given a command name and an application name, returns the Command + class instance. All errors raised by the importation process + (ImportError, AttributeError) are allowed to propagate. + """ + return getattr(__import__('%s.management.commands.%s' % (app_name, name), + {}, {}, ['Command']), 'Command')() + +def get_commands(load_user_commands=True, project_directory=None): + """ + Returns a dictionary of commands against the application in which + those commands can be found. This works by looking for a + management.commands package in django.core, and in each installed + application -- if a commands package exists, all commands in that + package are registered. + + Core commands are always included; user-defined commands will also + be included if ``load_user_commands`` is True. If a project directory + is provided, the startproject command will be disabled, and the + startapp command will be modified to use that directory. + + The dictionary is in the format {command_name: app_name}. Key-value + pairs from this dictionary can then be used in calls to + load_command_class(app_name, command_name) + + If a specific version of a command must be loaded (e.g., with the + startapp command), the instantiated module can be placed in the + dictionary in place of the application name. + + The dictionary is cached on the first call, and reused on subsequent + calls. + """ + global _commands + if _commands is None: + _commands = dict([(name, 'django.core') + for name in find_commands(__path__[0])]) + if load_user_commands: + # Get commands from all installed apps + from django.conf import settings + for app_name in settings.INSTALLED_APPS: + try: + path = find_management_module(app_name) + _commands.update(dict([(name, app_name) + for name in find_commands(path)])) + except ImportError: + pass # No management module - ignore this app + + if project_directory: + # Remove the "startproject" command from self.commands, because + # that's a django-admin.py command, not a manage.py command. + del _commands['startproject'] + + # Override the startapp command so that it always uses the + # project_directory, not the current working directory + # (which is default). + from django.core.management.commands.startapp import ProjectCommand + _commands['startapp'] = ProjectCommand(project_directory) + + return _commands def call_command(name, *args, **options): """ @@ -25,8 +111,26 @@ def call_command(name, *args, **options): call_command('shell', plain=True) call_command('sqlall', 'myapp') """ - klass = load_command_class(name) + try: + app_name = get_commands()[name] + if isinstance(app_name, BaseCommand): + # If the command is already loaded, use it directly. + klass = app_name + else: + klass = load_command_class(app_name, name) + except KeyError: + raise CommandError, "Unknown command: %r" % name return klass.execute(*args, **options) + +class LaxOptionParser(OptionParser): + """ + An option parser that doesn't raise any errors on unknown options. + + This is needed because the --settings and --pythonpath options affect + the commands (and thus the options) that are available to the user. + """ + def error(self, msg): + pass class ManagementUtility(object): """ @@ -38,21 +142,9 @@ class ManagementUtility(object): def __init__(self, argv=None): self.argv = argv or sys.argv[:] self.prog_name = os.path.basename(self.argv[0]) - self.commands = self.default_commands() - - def default_commands(self): - """ - Returns a dictionary of instances of all available Command classes. - - This works by looking for and loading all Python modules in the - django.core.management.commands package. - - The dictionary is in the format {name: command_instance}. - """ - command_dir = os.path.join(__path__[0], 'commands') - names = [f[:-3] for f in os.listdir(command_dir) if not f.startswith('_') and f.endswith('.py')] - return dict([(name, load_command_class(name)) for name in names]) - + self.project_directory = None + self.user_commands = False + def main_help_text(self): """ Returns the script's main help text, as a string. @@ -61,7 +153,7 @@ class ManagementUtility(object): usage.append('Django command line tool, version %s' % django.get_version()) usage.append("Type '%s help <subcommand>' for help on a specific subcommand." % self.prog_name) usage.append('Available subcommands:') - commands = self.commands.keys() + commands = get_commands(self.user_commands, self.project_directory).keys() commands.sort() for cmd in commands: usage.append(' %s' % cmd) @@ -74,16 +166,33 @@ class ManagementUtility(object): django-admin.py or manage.py) if it can't be found. """ try: - return self.commands[subcommand] + app_name = get_commands(self.user_commands, self.project_directory)[subcommand] + if isinstance(app_name, BaseCommand): + # If the command is already loaded, use it directly. + klass = app_name + else: + klass = load_command_class(app_name, subcommand) except KeyError: sys.stderr.write("Unknown command: %r\nType '%s help' for usage.\n" % (subcommand, self.prog_name)) sys.exit(1) - + return klass + def execute(self): """ Given the command-line arguments, this figures out which subcommand is being run, creates a parser appropriate to that command, and runs it. """ + # Preprocess options to extract --settings and --pythonpath. These options + # could affect the commands that are available, so they must be processed + # early + parser = LaxOptionParser(version=get_version(), + option_list=BaseCommand.option_list) + try: + options, args = parser.parse_args(self.argv) + handle_default_options(options) + except: + pass # Ignore any option errors at this point. + try: subcommand = self.argv[1] except IndexError: @@ -91,8 +200,8 @@ class ManagementUtility(object): sys.exit(1) if subcommand == 'help': - if len(self.argv) > 2: - self.fetch_command(self.argv[2]).print_help(self.prog_name, self.argv[2]) + if len(args) > 2: + self.fetch_command(args[2]).print_help(self.prog_name, args[2]) else: sys.stderr.write(self.main_help_text() + '\n') sys.exit(1) @@ -116,16 +225,9 @@ class ProjectManagementUtility(ManagementUtility): """ def __init__(self, argv, project_directory): super(ProjectManagementUtility, self).__init__(argv) - - # Remove the "startproject" command from self.commands, because - # that's a django-admin.py command, not a manage.py command. - del self.commands['startproject'] - - # Override the startapp command so that it always uses the - # project_directory, not the current working directory (which is default). - from django.core.management.commands.startapp import ProjectCommand - self.commands['startapp'] = ProjectCommand(project_directory) - + self.project_directory = project_directory + self.user_commands = True + def setup_environ(settings_mod): """ Configure the runtime environment. This can also be used by external @@ -137,7 +239,7 @@ def setup_environ(settings_mod): project_directory, settings_filename = os.path.split(settings_mod.__file__) project_name = os.path.basename(project_directory) settings_name = os.path.splitext(settings_filename)[0] - sys.path.append(os.path.join(project_directory, '..')) + sys.path.append(os.path.join(project_directory, os.pardir)) project_module = __import__(project_name, {}, {}, ['']) sys.path.pop() diff --git a/django/core/management/base.py b/django/core/management/base.py index d883fe23dc..26ecef4ff5 100644 --- a/django/core/management/base.py +++ b/django/core/management/base.py @@ -9,6 +9,17 @@ import os class CommandError(Exception): pass +def handle_default_options(options): + """ + Include any default options that all commands should accept + here so that ManagementUtility can handle them before searching + for user commands. + """ + if options.settings: + os.environ['DJANGO_SETTINGS_MODULE'] = options.settings + if options.pythonpath: + sys.path.insert(0, options.pythonpath) + class BaseCommand(object): # Metadata about this command. option_list = ( @@ -55,10 +66,7 @@ class BaseCommand(object): def run_from_argv(self, argv): parser = self.create_parser(argv[0], argv[1]) options, args = parser.parse_args(argv[2:]) - if options.settings: - os.environ['DJANGO_SETTINGS_MODULE'] = options.settings - if options.pythonpath: - sys.path.insert(0, options.pythonpath) + handle_default_options(options) self.execute(*args, **options.__dict__) def execute(self, *args, **options): diff --git a/django/core/management/commands/startapp.py b/django/core/management/commands/startapp.py index 1da2776cf5..c238e74a08 100644 --- a/django/core/management/commands/startapp.py +++ b/django/core/management/commands/startapp.py @@ -16,7 +16,7 @@ class Command(LabelCommand): directory = os.getcwd() # Determine the project_name a bit naively -- by looking at the name of # the parent directory. - project_dir = os.path.normpath(os.path.join(directory, '..')) + project_dir = os.path.normpath(os.path.join(directory, os.pardir)) parent_dir = os.path.basename(project_dir) project_name = os.path.basename(directory) if app_name == project_name: diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index b0b9ae2135..3d3fa2e99a 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -245,7 +245,7 @@ class Field(object): if self.default is not NOT_PROVIDED: if callable(self.default): return self.default() - return self.default + return force_unicode(self.default, strings_only=True) if not self.empty_strings_allowed or (self.null and settings.DATABASE_ENGINE != 'oracle'): return None return "" diff --git a/django/http/__init__.py b/django/http/__init__.py index 9a47e70592..508c6d2f3e 100644 --- a/django/http/__init__.py +++ b/django/http/__init__.py @@ -47,8 +47,9 @@ class HttpRequest(object): def get_host(self): "Returns the HTTP host using the environment or request headers." # We try three options, in order of decreasing preference. - host = self.META.get('HTTP_X_FORWARDED_HOST', '') - if 'HTTP_HOST' in self.META: + if 'HTTP_X_FORWARDED_HOST' in self.META: + host = self.META['HTTP_X_FORWARDED_HOST'] + elif 'HTTP_HOST' in self.META: host = self.META['HTTP_HOST'] else: # Reconstruct the host using the algorithm from PEP 333. diff --git a/django/middleware/http.py b/django/middleware/http.py index 78e066c67b..71cdf7aa5d 100644 --- a/django/middleware/http.py +++ b/django/middleware/http.py @@ -54,8 +54,7 @@ class SetRemoteAddrFromForwardedFor(object): except KeyError: return None else: - # HTTP_X_FORWARDED_FOR can be a comma-separated list of IPs. - # Take just the last one. - # See http://bob.pythonmac.org/archives/2005/09/23/apache-x-forwarded-for-caveat/ - real_ip = real_ip.split(",")[-1].strip() + # HTTP_X_FORWARDED_FOR can be a comma-separated list of IPs. The + # client's IP will be the first one. + real_ip = real_ip.split(",")[0].strip() request.META['REMOTE_ADDR'] = real_ip diff --git a/django/newforms/widgets.py b/django/newforms/widgets.py index 0e7752499e..2c235513d0 100644 --- a/django/newforms/widgets.py +++ b/django/newforms/widgets.py @@ -7,7 +7,9 @@ try: except NameError: from sets import Set as set # Python 2.3 fallback +import copy from itertools import chain + from django.utils.datastructures import MultiValueDict from django.utils.html import escape from django.utils.translation import ugettext @@ -32,6 +34,12 @@ class Widget(object): else: self.attrs = {} + def __deepcopy__(self, memo): + obj = copy.copy(self) + obj.attrs = self.attrs.copy() + memo[id(self)] = obj + return obj + def render(self, name, value, attrs=None): """ Returns this Widget rendered as HTML, as a Unicode string. @@ -88,7 +96,7 @@ class PasswordInput(Input): input_type = 'password' def __init__(self, attrs=None, render_value=True): - self.attrs = attrs or {} + super(PasswordInput, self).__init__(attrs) self.render_value = render_value def render(self, name, value, attrs=None): @@ -105,8 +113,8 @@ class MultipleHiddenInput(HiddenInput): of values. """ def __init__(self, attrs=None, choices=()): + super(MultipleHiddenInput, self).__init__(attrs) # choices can be any iterable - self.attrs = attrs or {} self.choices = choices def render(self, name, value, attrs=None, choices=()): @@ -145,9 +153,9 @@ class Textarea(Widget): class CheckboxInput(Widget): def __init__(self, attrs=None, check_test=bool): + super(CheckboxInput, self).__init__(attrs) # check_test is a callable that takes a value and returns True # if the checkbox should be checked for that value. - self.attrs = attrs or {} self.check_test = check_test def render(self, name, value, attrs=None): @@ -164,7 +172,7 @@ class CheckboxInput(Widget): class Select(Widget): def __init__(self, attrs=None, choices=()): - self.attrs = attrs or {} + super(Select, self).__init__(attrs) # choices can be any iterable, but we may need to render this widget # multiple times. Thus, collapse it into a list so it can be consumed # more than once. @@ -203,8 +211,8 @@ class NullBooleanSelect(Select): class SelectMultiple(Widget): def __init__(self, attrs=None, choices=()): + super(SelectMultiple, self).__init__(attrs) # choices can be any iterable - self.attrs = attrs or {} self.choices = choices def render(self, name, value, attrs=None, choices=()): diff --git a/django/oldforms/__init__.py b/django/oldforms/__init__.py index 9bb90416c4..5f7b9fc2ad 100644 --- a/django/oldforms/__init__.py +++ b/django/oldforms/__init__.py @@ -500,7 +500,7 @@ class SelectField(FormField): selected_html = u'' if smart_unicode(value) == str_data: selected_html = u' selected="selected"' - output.append(u' <option value="%s"%s>%s</option>' % (escape(value), selected_html, escape(display_name))) + output.append(u' <option value="%s"%s>%s</option>' % (escape(value), selected_html, force_unicode(escape(display_name)))) output.append(u' </select>') return u'\n'.join(output) @@ -612,7 +612,7 @@ class SelectMultipleField(SelectField): selected_html = u'' if smart_unicode(value) in str_data_list: selected_html = u' selected="selected"' - output.append(u' <option value="%s"%s>%s</option>' % (escape(value), selected_html, escape(choice))) + output.append(u' <option value="%s"%s>%s</option>' % (escape(value), selected_html, force_unicode(escape(choice)))) output.append(u' </select>') return u'\n'.join(output) diff --git a/django/template/__init__.py b/django/template/__init__.py index 449e0d0c28..1cfd85be06 100644 --- a/django/template/__init__.py +++ b/django/template/__init__.py @@ -88,8 +88,6 @@ UNKNOWN_SOURCE="<unknown source>" tag_re = re.compile('(%s.*?%s|%s.*?%s|%s.*?%s)' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END), re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END), re.escape(COMMENT_TAG_START), re.escape(COMMENT_TAG_END))) -# matches if the string is valid number -number_re = re.compile(r'[-+]?(\d+|\d*\.\d+)$') # global dictionary of libraries that have been loaded using get_library libraries = {} @@ -564,18 +562,19 @@ class FilterExpression(object): elif constant_arg is not None: args.append((False, constant_arg.replace(r'\"', '"'))) elif var_arg: - args.append((True, var_arg)) + args.append((True, Variable(var_arg))) filter_func = parser.find_filter(filter_name) self.args_check(filter_name,filter_func, args) filters.append( (filter_func,args)) upto = match.end() if upto != len(token): raise TemplateSyntaxError, "Could not parse the remainder: '%s' from '%s'" % (token[upto:], token) - self.var, self.filters = var, filters + self.filters = filters + self.var = Variable(var) def resolve(self, context, ignore_failures=False): try: - obj = resolve_variable(self.var, context) + obj = self.var.resolve(context) except VariableDoesNotExist: if ignore_failures: obj = None @@ -595,7 +594,7 @@ class FilterExpression(object): if not lookup: arg_vals.append(arg) else: - arg_vals.append(resolve_variable(arg, context)) + arg_vals.append(arg.resolve(context)) obj = func(obj, *arg_vals) return obj @@ -637,37 +636,98 @@ class FilterExpression(object): def resolve_variable(path, context): """ Returns the resolved variable, which may contain attribute syntax, within - the given context. The variable may be a hard-coded string (if it begins - and ends with single or double quote marks). + the given context. + + Deprecated; use the Variable class instead. + """ + return Variable(path).resolve(context) - >>> c = {'article': {'section':'News'}} - >>> resolve_variable('article.section', c) - u'News' - >>> resolve_variable('article', c) - {'section': 'News'} - >>> class AClass: pass - >>> c = AClass() - >>> c.article = AClass() - >>> c.article.section = 'News' - >>> resolve_variable('article.section', c) - u'News' +class Variable(object): + """ + A template variable, resolvable against a given context. The variable may be + a hard-coded string (if it begins and ends with single or double quote + marks):: + + >>> c = {'article': {'section':'News'}} + >>> Variable('article.section').resolve(c) + u'News' + >>> Variable('article').resolve(c) + {'section': 'News'} + >>> class AClass: pass + >>> c = AClass() + >>> c.article = AClass() + >>> c.article.section = 'News' + >>> Variable('article.section').resolve(c) + u'News' (The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.') """ - if number_re.match(path): - number_type = '.' in path and float or int - current = number_type(path) - elif path[0] in ('"', "'") and path[0] == path[-1]: - current = path[1:-1] - else: + + def __init__(self, var): + self.var = var + self.literal = None + self.lookups = None + + try: + # First try to treat this variable as a number. + # + # Note that this could cause an OverflowError here that we're not + # catching. Since this should only happen at compile time, that's + # probably OK. + self.literal = float(var) + + # So it's a float... is it an int? If the original value contained a + # dot or an "e" then it was a float, not an int. + if '.' not in var and 'e' not in var.lower(): + self.literal = int(self.literal) + + # "2." is invalid + if var.endswith('.'): + raise ValueError + + except ValueError: + # A ValueError means that the variable isn't a number. + # If it's wrapped with quotes (single or double), then + # we're also dealing with a literal. + if var[0] in "\"'" and var[0] == var[-1]: + self.literal = var[1:-1] + + else: + # Otherwise we'll set self.lookups so that resolve() knows we're + # dealing with a bonafide variable + self.lookups = tuple(var.split(VARIABLE_ATTRIBUTE_SEPARATOR)) + + def resolve(self, context): + """Resolve this variable against a given context.""" + if self.lookups is not None: + # We're dealing with a variable that needs to be resolved + return self._resolve_lookup(context) + else: + # We're dealing with a literal, so it's already been "resolved" + return self.literal + + def __repr__(self): + return "<%s: %r>" % (self.__class__.__name__, self.var) + + def __str__(self): + return self.var + + def _resolve_lookup(self, context): + """ + Performs resolution of a real variable (i.e. not a literal) against the + given context. + + As indicated by the method's name, this method is an implementation + detail and shouldn't be called by external code. Use Variable.resolve() + instead. + """ current = context - bits = path.split(VARIABLE_ATTRIBUTE_SEPARATOR) - while bits: + for bit in self.lookups: try: # dictionary lookup - current = current[bits[0]] + current = current[bit] except (TypeError, AttributeError, KeyError): try: # attribute lookup - current = getattr(current, bits[0]) + current = getattr(current, bit) if callable(current): if getattr(current, 'alters_data', False): current = settings.TEMPLATE_STRING_IF_INVALID @@ -685,27 +745,27 @@ def resolve_variable(path, context): raise except (TypeError, AttributeError): try: # list-index lookup - current = current[int(bits[0])] + current = current[int(bit)] except (IndexError, # list index out of range ValueError, # invalid literal for int() - KeyError, # current is a dict without `int(bits[0])` key + KeyError, # current is a dict without `int(bit)` key TypeError, # unsubscriptable object ): - raise VariableDoesNotExist("Failed lookup for key [%s] in %r", (bits[0], current)) # missing attribute + raise VariableDoesNotExist("Failed lookup for key [%s] in %r", (bit, current)) # missing attribute except Exception, e: if getattr(e, 'silent_variable_failure', False): current = settings.TEMPLATE_STRING_IF_INVALID else: raise - del bits[0] - if isinstance(current, (basestring, Promise)): - try: - current = force_unicode(current) - except UnicodeDecodeError: - # Failing to convert to unicode can happen sometimes (e.g. debug - # tracebacks). So we allow it in this particular instance. - pass - return current + + if isinstance(current, (basestring, Promise)): + try: + current = force_unicode(current) + except UnicodeDecodeError: + # Failing to convert to unicode can happen sometimes (e.g. debug + # tracebacks). So we allow it in this particular instance. + pass + return current class Node(object): def render(self, context): @@ -861,10 +921,10 @@ class Library(object): class SimpleNode(Node): def __init__(self, vars_to_resolve): - self.vars_to_resolve = vars_to_resolve + self.vars_to_resolve = map(Variable, vars_to_resolve) def render(self, context): - resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve] + resolved_vars = [var.resolve(context) for var in self.vars_to_resolve] return func(*resolved_vars) compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, SimpleNode) @@ -883,10 +943,10 @@ class Library(object): class InclusionNode(Node): def __init__(self, vars_to_resolve): - self.vars_to_resolve = vars_to_resolve + self.vars_to_resolve = map(Variable, vars_to_resolve) def render(self, context): - resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve] + resolved_vars = [var.resolve(context) for var in self.vars_to_resolve] if takes_context: args = [context] + resolved_vars else: diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py index 1fd6d02c70..f1fcf5fe90 100644 --- a/django/template/defaultfilters.py +++ b/django/template/defaultfilters.py @@ -1,6 +1,6 @@ "Default variable filters" -from django.template import resolve_variable, Library +from django.template import Variable, Library from django.conf import settings from django.utils.translation import ugettext, ungettext from django.utils.encoding import force_unicode, smart_str, iri_to_uri @@ -297,7 +297,8 @@ def dictsort(value, arg): Takes a list of dicts, returns that list sorted by the property given in the argument. """ - decorated = [(resolve_variable(u'var.' + arg, {u'var' : item}), item) for item in value] + var_resolve = Variable(arg).resolve + decorated = [(var_resolve(item), item) for item in value] decorated.sort() return [item[1] for item in decorated] @@ -306,7 +307,8 @@ def dictsortreversed(value, arg): Takes a list of dicts, returns that list sorted in reverse order by the property given in the argument. """ - decorated = [(resolve_variable(u'var.' + arg, {u'var' : item}), item) for item in value] + var_resolve = Variable(arg).resolve + decorated = [(var_resolve(item), item) for item in value] decorated.sort() decorated.reverse() return [item[1] for item in decorated] diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py index e23295f732..151985bfdd 100644 --- a/django/template/defaulttags.py +++ b/django/template/defaulttags.py @@ -1,6 +1,6 @@ "Default tags used by the template system, available to all templates." -from django.template import Node, NodeList, Template, Context, resolve_variable +from django.template import Node, NodeList, Template, Context, Variable from django.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, SINGLE_BRACE_START, SINGLE_BRACE_END, COMMENT_TAG_START, COMMENT_TAG_END from django.template import get_library, Library, InvalidTemplateLibrary from django.conf import settings @@ -30,7 +30,7 @@ class CycleNode(Node): def render(self, context): self.counter += 1 value = self.cyclevars[self.counter % self.cyclevars_len] - value = resolve_variable(value, context) + value = Variable(value).resolve(context) if self.variable_name: context[self.variable_name] = value return value @@ -57,12 +57,12 @@ class FilterNode(Node): class FirstOfNode(Node): def __init__(self, vars): - self.vars = vars + self.vars = map(Variable, vars) def render(self, context): for var in self.vars: try: - value = resolve_variable(var, context) + value = var.resolve(context) except VariableDoesNotExist: continue if value: @@ -147,7 +147,7 @@ class IfChangedNode(Node): def __init__(self, nodelist, *varlist): self.nodelist = nodelist self._last_seen = None - self._varlist = varlist + self._varlist = map(Variable, varlist) def render(self, context): if 'forloop' in context and context['forloop']['first']: @@ -156,7 +156,7 @@ class IfChangedNode(Node): if self._varlist: # Consider multiple parameters. # This automatically behaves like a OR evaluation of the multiple variables. - compare_to = [resolve_variable(var, context) for var in self._varlist] + compare_to = [var.resolve(context) for var in self._varlist] else: compare_to = self.nodelist.render(context) except VariableDoesNotExist: @@ -175,7 +175,7 @@ class IfChangedNode(Node): class IfEqualNode(Node): def __init__(self, var1, var2, nodelist_true, nodelist_false, negate): - self.var1, self.var2 = var1, var2 + self.var1, self.var2 = Variable(var1), Variable(var2) self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false self.negate = negate @@ -184,11 +184,11 @@ class IfEqualNode(Node): def render(self, context): try: - val1 = resolve_variable(self.var1, context) + val1 = self.var1.resolve(context) except VariableDoesNotExist: val1 = None try: - val2 = resolve_variable(self.var2, context) + val2 = self.var2.resolve(context) except VariableDoesNotExist: val2 = None if (self.negate and val1 != val2) or (not self.negate and val1 == val2): diff --git a/django/template/loader_tags.py b/django/template/loader_tags.py index 19f368711c..652fda11ce 100644 --- a/django/template/loader_tags.py +++ b/django/template/loader_tags.py @@ -1,4 +1,4 @@ -from django.template import TemplateSyntaxError, TemplateDoesNotExist, resolve_variable +from django.template import TemplateSyntaxError, TemplateDoesNotExist, Variable from django.template import Library, Node from django.template.loader import get_template, get_template_from_string, find_template_source from django.conf import settings @@ -99,11 +99,11 @@ class ConstantIncludeNode(Node): class IncludeNode(Node): def __init__(self, template_name): - self.template_name = template_name + self.template_name = Variable(template_name) def render(self, context): try: - template_name = resolve_variable(self.template_name, context) + template_name = self.template_name.resolve(context) t = get_template(template_name) return t.render(context) except TemplateSyntaxError, e: diff --git a/django/templatetags/i18n.py b/django/templatetags/i18n.py index 1e85c6b5d1..d5b0741a61 100644 --- a/django/templatetags/i18n.py +++ b/django/templatetags/i18n.py @@ -1,4 +1,4 @@ -from django.template import Node, resolve_variable +from django.template import Node, Variable from django.template import TemplateSyntaxError, TokenParser, Library from django.template import TOKEN_TEXT, TOKEN_VAR from django.utils import translation @@ -32,11 +32,11 @@ class GetCurrentLanguageBidiNode(Node): class TranslateNode(Node): def __init__(self, value, noop): - self.value = value + self.value = Variable(value) self.noop = noop def render(self, context): - value = resolve_variable(self.value, context) + value = self.value.resolve(context) if self.noop: return value else: diff --git a/django/utils/cache.py b/django/utils/cache.py index 2494d7839e..f192e1115c 100644 --- a/django/utils/cache.py +++ b/django/utils/cache.py @@ -23,7 +23,7 @@ import time from email.Utils import formatdate from django.conf import settings from django.core.cache import cache -from django.utils.encoding import smart_str +from django.utils.encoding import smart_str, iri_to_uri cc_delim_re = re.compile(r'\s*,\s*') @@ -57,6 +57,13 @@ def patch_cache_control(response, **kwargs): cc = dict([dictitem(el) for el in cc]) else: cc = {} + + # If there's already a max-age header but we're being asked to set a new + # max-age, use the minumum of the two ages. In practice this happens when + # a decorator and a piece of middleware both operate on a given view. + if 'max-age' in cc and 'max_age' in kwargs: + kwargs['max_age'] = min(cc['max-age'], kwargs['max_age']) + for (k,v) in kwargs.items(): cc[k.replace('_', '-')] = v cc = ', '.join([dictvalue(el) for el in cc.items()]) @@ -118,7 +125,7 @@ def _generate_cache_key(request, headerlist, key_prefix): value = request.META.get(header, None) if value is not None: ctx.update(value) - return 'views.decorators.cache.cache_page.%s.%s.%s' % (key_prefix, request.path, ctx.hexdigest()) + return 'views.decorators.cache.cache_page.%s.%s.%s' % (key_prefix, iri_to_uri(request.path), ctx.hexdigest()) def get_cache_key(request, key_prefix=None): """ @@ -132,7 +139,7 @@ def get_cache_key(request, key_prefix=None): """ if key_prefix is None: key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX - cache_key = 'views.decorators.cache.cache_header.%s.%s' % (key_prefix, request.path) + cache_key = 'views.decorators.cache.cache_header.%s.%s' % (key_prefix, iri_to_uri(request.path)) headerlist = cache.get(cache_key, None) if headerlist is not None: return _generate_cache_key(request, headerlist, key_prefix) @@ -156,7 +163,7 @@ def learn_cache_key(request, response, cache_timeout=None, key_prefix=None): key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX if cache_timeout is None: cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS - cache_key = 'views.decorators.cache.cache_header.%s.%s' % (key_prefix, request.path) + cache_key = 'views.decorators.cache.cache_header.%s.%s' % (key_prefix, iri_to_uri(request.path)) if response.has_header('Vary'): headerlist = ['HTTP_'+header.upper().replace('-', '_') for header in vary_delim_re.split(response['Vary'])] cache.set(cache_key, headerlist, cache_timeout) diff --git a/django/utils/datastructures.py b/django/utils/datastructures.py index 40e99c3962..2f3c9bb568 100644 --- a/django/utils/datastructures.py +++ b/django/utils/datastructures.py @@ -149,7 +149,7 @@ class MultiValueDict(dict): dict.__init__(self, key_to_list_mapping) def __repr__(self): - return "<MultiValueDict: %s>" % dict.__repr__(self) + return "<%s: %s>" % (self.__class__.__name__, dict.__repr__(self)) def __getitem__(self, key): """ diff --git a/django/utils/encoding.py b/django/utils/encoding.py index 69c3e9c28b..6daae4386d 100644 --- a/django/utils/encoding.py +++ b/django/utils/encoding.py @@ -1,5 +1,6 @@ import types import urllib +import datetime from django.utils.functional import Promise class StrAndUnicode(object): @@ -30,7 +31,7 @@ def force_unicode(s, encoding='utf-8', strings_only=False, errors='strict'): If strings_only is True, don't convert (some) non-string-like objects. """ - if strings_only and isinstance(s, (types.NoneType, int, long)): + if strings_only and isinstance(s, (types.NoneType, int, long, datetime.datetime, datetime.time, float)): return s if not isinstance(s, basestring,): if hasattr(s, '__unicode__'): diff --git a/django/utils/translation/__init__.py b/django/utils/translation/__init__.py index 13fc8a847a..9ca7419e1e 100644 --- a/django/utils/translation/__init__.py +++ b/django/utils/translation/__init__.py @@ -2,6 +2,7 @@ Internationalization support. """ from django.utils.functional import lazy +from django.utils.encoding import force_unicode __all__ = ['gettext', 'gettext_noop', 'gettext_lazy', 'ngettext', 'ngettext_lazy', 'string_concat', 'activate', 'deactivate', @@ -39,7 +40,7 @@ def delayed_loader(*args, **kwargs): g['real_%s' % name] = getattr(trans, name) # Make the originally requested function call on the way out the door. - return g[caller](*args, **kwargs) + return g['real_%s' % caller](*args, **kwargs) g = globals() for name in __all__: @@ -63,14 +64,10 @@ def ugettext(message): def ungettext(singular, plural, number): return real_ungettext(singular, plural, number) -def string_concat(*strings): - return real_string_concat(*strings) - ngettext_lazy = lazy(ngettext, str) gettext_lazy = lazy(gettext, str) ungettext_lazy = lazy(ungettext, unicode) ugettext_lazy = lazy(ugettext, unicode) -string_concat = lazy(string_concat, unicode) def activate(language): return real_activate(language) @@ -108,3 +105,10 @@ def templatize(src): def deactivate_all(): return real_deactivate_all() +def string_concat(*strings): + """ + Lazy variant of string concatenation, needed for translations that are + constructed from multiple parts. + """ + return u''.join([force_unicode(s) for s in strings]) +string_concat = lazy(string_concat, unicode) diff --git a/django/utils/translation/trans_null.py b/django/utils/translation/trans_null.py index e3f89567a5..771a80e99c 100644 --- a/django/utils/translation/trans_null.py +++ b/django/utils/translation/trans_null.py @@ -13,7 +13,6 @@ ngettext_lazy = ngettext def ungettext(singular, plural, number): return force_unicode(ngettext(singular, plural, number)) -string_concat = lambda *strings: u''.join([force_unicode(el) for el in strings]) activate = lambda x: None deactivate = deactivate_all = install = lambda: None get_language = lambda: settings.LANGUAGE_CODE diff --git a/django/utils/translation/trans_real.py b/django/utils/translation/trans_real.py index 5fff1ea63a..250af04e8c 100644 --- a/django/utils/translation/trans_real.py +++ b/django/utils/translation/trans_real.py @@ -516,9 +516,3 @@ def templatize(src): out.write(blankout(t.contents, 'X')) return out.getvalue() -def string_concat(*strings): - """" - Lazy variant of string concatenation, needed for translations that are - constructed from multiple parts. - """ - return u''.join([force_unicode(s) for s in strings]) diff --git a/django/views/static.py b/django/views/static.py index 3ec4ca14a1..e274057617 100644 --- a/django/views/static.py +++ b/django/views/static.py @@ -29,12 +29,12 @@ def serve(request, path, document_root=None, show_indexes=False): newpath = '' for part in path.split('/'): if not part: - # strip empty path components + # Strip empty path components. continue drive, part = os.path.splitdrive(part) head, part = os.path.split(part) if part in (os.curdir, os.pardir): - # strip '.' amd '..' in path + # Strip '.' and '..' in path. continue newpath = os.path.join(newpath, part).replace('\\', '/') if newpath and path != newpath: diff --git a/docs/databases.txt b/docs/databases.txt index 21ff4c7434..213c2d666c 100644 --- a/docs/databases.txt +++ b/docs/databases.txt @@ -163,3 +163,118 @@ storage engine, you have a couple of options. .. _AlterModelOnSyncDB: http://code.djangoproject.com/wiki/AlterModelOnSyncDB + +Oracle Notes +============ + +Django supports `Oracle Database Server`_ versions 9i and higher. Oracle +version 10g or later is required to use Django's ``regex`` and ``iregex`` query +operators. You will also need the `cx_Oracle`_ driver, version 4.3.1 or newer. + +.. _`Oracle Database Server`: http://www.oracle.com/ +.. _`cx_Oracle`: http://cx-oracle.sourceforge.net/ + +To run ``python manage.py syncdb``, you'll need to create an Oracle database +user with CREATE TABLE, CREATE SEQUENCE, CREATE PROCEDURE, and CREATE TRIGGER +privileges. To run Django's test suite, the user also needs +CREATE and DROP DATABASE and CREATE and DROP TABLESPACE privileges. + +Connecting to the Database +-------------------------- + +Your Django settings.py file should look something like this for Oracle:: + + DATABASE_ENGINE = 'oracle' + DATABASE_NAME = 'xe' + DATABASE_USER = 'a_user' + DATABASE_PASSWORD = 'a_password' + DATABASE_HOST = '' + DATABASE_PORT = '' + +If you don't use a ``tnsnames.ora`` file or a similar naming method that +recognizes the SID ("xe" in this example), then fill in both ``DATABASE_HOST`` +and ``DATABASE_PORT`` like so:: + + DATABASE_ENGINE = 'oracle' + DATABASE_NAME = 'xe' + DATABASE_USER = 'a_user' + DATABASE_PASSWORD = 'a_password' + DATABASE_HOST = 'dbprod01ned.mycompany.com' + DATABASE_PORT = '1540' + +You should supply both ``DATABASE_HOST`` and ``DATABASE_PORT``, or leave both +as empty strings. + +Tablespace Options +------------------ + +A common paradigm for optimizing performance in Oracle-based systems is the +use of `tablespaces`_ to organize disk layout. The Oracle backend supports +this use case by adding ``db_tablespace`` options to the ``Meta`` and +``Field`` classes. (When using a backend that lacks support for tablespaces, +these options are ignored.) + +.. _`tablespaces`: http://en.wikipedia.org/wiki/Tablespace + +A tablespace can be specified for the table(s) generated by a model by +supplying the ``db_tablespace`` option inside the model's ``Meta`` class. +Additionally, the ``db_tablespace`` option can be passed to a ``Field`` +constructor to specify an alternate tablespace for the ``Field``'s column +index. If no index would be created for the column, the ``db_tablespace`` +option is ignored. + +:: + + class TablespaceExample(models.Model): + name = models.CharField(maxlength=30, db_index=True, db_tablespace="indexes") + data = models.CharField(maxlength=255, db_index=True) + edges = models.ManyToManyField(to="self", db_tablespace="indexes") + + class Meta: + db_tablespace = "tables" + +In this example, the tables generated by the ``TablespaceExample`` model +(i.e., the model table and the many-to-many table) would be stored in the +``tables`` tablespace. The index for the name field and the indexes on the +many-to-many table would be stored in the ``indexes`` tablespace. The ``data`` +field would also generate an index, but no tablespace for it is specified, so +it would be stored in the model tablespace ``tables`` by default. + +Django does not create the tablespaces for you. Please refer to `Oracle's +documentation`_ for details on creating and managing tablespaces. + +.. _`Oracle's documentation`: http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/statements_7003.htm#SQLRF01403 + +Naming Issues +------------- + +Oracle imposes a name length limit of 30 characters. To accommodate this, the +backend truncates database identifiers to fit, replacing the final four +characters of the truncated name with a repeatable MD5 hash value. + +NULL and Empty Strings +---------------------- + +Django generally prefers to use the empty string ('') rather than NULL, but +Oracle treats both identically. To get around this, the Oracle backend +coerces the ``null=True`` option on fields that permit the empty string as a +value. When fetching from the database, it is assumed that a NULL value in +one of these fields really means the empty string, and the data is silently +converted to reflect this assumption. + +TextField Limitations +--------------------- + +The Oracle backend stores ``TextFields`` as ``NCLOB`` columns. Oracle imposes +some limitations on the usage of such LOB columns in general: + + * LOB columns may not be used as primary keys. + + * LOB columns may not be used in indexes. + + * LOB columns may not be used in a ``SELECT DISTINCT`` list. This means that + attempting to use the ``QuerySet.distinct`` method on a model that + includes ``TextField`` columns will result in an error when run against + Oracle. A workaround to this is to keep ``TextField`` columns out of any + models that you foresee performing ``.distinct`` queries on, and to + include the ``TextField`` in a related model instead. diff --git a/docs/db-api.txt b/docs/db-api.txt index 61cf2d4ffd..adca8b4d5c 100644 --- a/docs/db-api.txt +++ b/docs/db-api.txt @@ -952,7 +952,7 @@ Example:: If you pass ``in_bulk()`` an empty list, you'll get an empty dictionary. ``iterator()`` -~~~~~~~~~~~~ +~~~~~~~~~~~~~~ Evaluates the ``QuerySet`` (by performing the query) and returns an `iterator`_ over the results. A ``QuerySet`` typically reads all of @@ -1371,11 +1371,6 @@ equivalent:: Entry.objects.filter(blog__id=3) # __exact is implied Entry.objects.filter(blog__pk=3) # __pk implies __id__exact -.. note:: - Because of this shortcut, you cannot have a field in your model called - ``pk`` that is not the primary key of the model. It will always be - replaced by the name of the model's primary key in queries. - Lookups that span relationships ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/django-admin.txt b/docs/django-admin.txt index 0f99987bad..f098dfa988 100644 --- a/docs/django-admin.txt +++ b/docs/django-admin.txt @@ -735,3 +735,32 @@ distribution. It enables tab-completion of ``django-admin.py`` and * Press [TAB] to see all available options. * Type ``sql``, then [TAB], to see all available options whose names start with ``sql``. + +Customized actions +================== + +**New in Django development version** + +If you want to add an action of your own to ``manage.py``, you can. +Simply add a ``management/commands`` directory to your application. +Each python module in that directory will be discovered and registered as +a command that can be executed as an action when you run ``manage.py``:: + + /fancy_blog + __init__.py + models.py + /management + __init__.py + /commands + __init__.py + explode.py + views.py + +In this example, ``explode`` command will be made available to any project +that includes the ``fancy_blog`` application in ``settings.INSTALLED_APPS``. + +The ``explode.py`` module has only one requirement -- it must define a class +called ``Command`` that extends ``django.core.management.base.BaseCommand``. + +For more details on how to define your own commands, look at the code for the +existing ``django-admin.py`` commands, in ``/django/core/management/commands``. diff --git a/docs/email.txt b/docs/email.txt index 17c2b2115a..effc5e24cf 100644 --- a/docs/email.txt +++ b/docs/email.txt @@ -100,31 +100,31 @@ mail_admins() ============= ``django.core.mail.mail_admins()`` is a shortcut for sending an e-mail to the -site admins, as defined in the `ADMINS setting`_. Here's the definition:: +site admins, as defined in the `ADMINS`_ setting. Here's the definition:: mail_admins(subject, message, fail_silently=False) ``mail_admins()`` prefixes the subject with the value of the -`EMAIL_SUBJECT_PREFIX setting`_, which is ``"[Django] "`` by default. +`EMAIL_SUBJECT_PREFIX`_ setting, which is ``"[Django] "`` by default. -The "From:" header of the e-mail will be the value of the `SERVER_EMAIL setting`_. +The "From:" header of the e-mail will be the value of the `SERVER_EMAIL`_ setting. This method exists for convenience and readability. -.. _ADMINS setting: ../settings/#admins -.. _EMAIL_SUBJECT_PREFIX setting: ../settings/#email-subject-prefix -.. _SERVER_EMAIL setting: ../settings/#server-email +.. _ADMINS: ../settings/#admins +.. _EMAIL_SUBJECT_PREFIX: ../settings/#email-subject-prefix +.. _SERVER_EMAIL: ../settings/#server-email mail_managers() function ======================== ``django.core.mail.mail_managers()`` is just like ``mail_admins()``, except it -sends an e-mail to the site managers, as defined in the `MANAGERS setting`_. +sends an e-mail to the site managers, as defined in the `MANAGERS`_ setting. Here's the definition:: mail_managers(subject, message, fail_silently=False) -.. _MANAGERS setting: ../settings/#managers +.. _MANAGERS: ../settings/#managers Examples ======== @@ -225,7 +225,7 @@ optional and can be set at any time prior to calling the ``send()`` method. * ``from_email``: The sender's address. Both ``fred@example.com`` and ``Fred <fred@example.com>`` forms are legal. If omitted, the - ``DEFAULT_FROM_EMAIL`` setting is used. + `DEFAULT_FROM_EMAIL`_ setting is used. * ``to``: A list or tuple of recipient addresses. @@ -297,6 +297,8 @@ The class has the following methods: message.attach_file('/images/weather_map.png') +.. _DEFAULT_FROM_EMAIL: ../settings/#default-from-email + Sending alternative content types ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -315,7 +317,7 @@ To send a text and HTML combination, you could write:: subject, from_email, to = 'hello', 'from@example.com', 'to@example.com' text_content = 'This is an important message.' html_content = '<p>This is an <strong>important</strong> message.</p>' - msg = EmailMultiAlternatives(subject, text_content, from_email, to) + msg = EmailMultiAlternatives(subject, text_content, from_email, [to]) msg.attach_alternative(html_content, "text/html") msg.send() diff --git a/docs/generic_views.txt b/docs/generic_views.txt index 87b82f7adf..08ff01c372 100644 --- a/docs/generic_views.txt +++ b/docs/generic_views.txt @@ -800,9 +800,14 @@ specify the page number in the URL in one of two ways: variable. You can iterate over the list provided by ``page_range`` to create a link to every page of results. -These values and lists are is 1-based, not 0-based, so the first page would be +These values and lists are 1-based, not 0-based, so the first page would be represented as page ``1``. +An example of the use of pagination can be found in the `object pagination`_ +example model. + +.. _`object pagination`: ../models/pagination/ + **New in Django development version:** As a special case, you are also permitted to use diff --git a/docs/i18n.txt b/docs/i18n.txt index bf73c88008..2c43e7884e 100644 --- a/docs/i18n.txt +++ b/docs/i18n.txt @@ -456,10 +456,13 @@ otherwise, they'll be tacked together without whitespace! .. admonition:: Mind your charset - When creating a ``.po`` file with your favorite text editor, first edit + When creating a PO file with your favorite text editor, first edit the charset line (search for ``"CHARSET"``) and set it to the charset - you'll be using to edit the content. Generally, utf-8 should work for most - languages, but ``gettext`` should handle any charset you throw at it. + you'll be using to edit the content. Due to the way the ``gettext`` tools + work internally and because we want to allow non-ASCII source strings in + Django's core and your applications, you **must** use UTF-8 as the encoding + for your PO file (this means that everybody will be using the same + encoding, which is important when Django processes the PO files). To reexamine all source code and templates for new translation strings and update all message files for **all** languages, run this:: diff --git a/docs/install.txt b/docs/install.txt index 2de8529d24..95aa82b2e3 100644 --- a/docs/install.txt +++ b/docs/install.txt @@ -66,6 +66,7 @@ installed. * If you're using SQLite, you'll need pysqlite_. Use version 2.0.3 or higher. * If you're using Oracle, you'll need cx_Oracle_, version 4.3.1 or higher. + You will also want to read the database-specific notes for the `Oracle backend`_. If you plan to use Django's ``manage.py syncdb`` command to automatically create database tables for your models, you'll need to @@ -88,6 +89,7 @@ to create a temporary test database. .. _MySQL backend: ../databases/ .. _cx_Oracle: http://cx-oracle.sourceforge.net/ .. _Oracle: http://www.oracle.com/ +.. _Oracle backend: ../databases/#oracle-notes .. _testing framework: ../testing/ Remove any old versions of Django diff --git a/docs/newforms.txt b/docs/newforms.txt index 2c8f67ce32..2005adeec8 100644 --- a/docs/newforms.txt +++ b/docs/newforms.txt @@ -1923,11 +1923,22 @@ of the model fields: .. note:: If you specify ``fields`` when creating a form with ``form_for_model()``, - make sure that the fields that are *not* specified can provide default - values, or are allowed to have a value of ``None``. If a field isn't - specified on a form, the object created from the form can't provide - a value for that attribute, which will prevent the new instance from - being saved. + then the fields that are *not* specified will not be set by the form's + ``save()`` method. Django will prevent any attempt to save an incomplete + model, so if the model does not allow the missing fields to be empty, and + does not provide a default value for the missing fields, any attempt to + ``save()`` a ``form_for_model`` with missing fields will fail. To avoid + this failure, you must use ``save(commit=False)`` and manually set any + extra required fields:: + + instance = form.save(commit=False) + instance.required_field = 'new value' + instance.save() + + See the `section on saving forms`_ for more details on using + ``save(commit=False)``. + +.. _section on saving forms: `The save() method`_ Overriding the default field types ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -2064,9 +2075,9 @@ More coming soon ================ That's all the documentation for now. For more, see the file -http://code.djangoproject.com/browser/django/trunk/tests/regressiontests/forms/tests.py +http://code.djangoproject.com/browser/django/trunk/tests/regressiontests/forms -- the unit tests for ``django.newforms``. This can give you a good idea of -what's possible. +what's possible. (Each submodule there contains separate tests.) If you're really itching to learn and use this library, please be patient. We're working hard on finishing both the code and documentation. diff --git a/docs/request_response.txt b/docs/request_response.txt index bf914fb5ff..7806886841 100644 --- a/docs/request_response.txt +++ b/docs/request_response.txt @@ -190,7 +190,7 @@ necessary because some HTML form elements, notably That means you can't change attributes of ``request.POST`` and ``request.GET`` directly. -``QueryDict`` implements the all standard dictionary methods, because it's a +``QueryDict`` implements all the standard dictionary methods, because it's a subclass of dictionary. Exceptions are outlined here: * ``__getitem__(key)`` -- Returns the value for the given key. If the key diff --git a/docs/sessions.txt b/docs/sessions.txt index 7fc607bb13..96e8d36854 100644 --- a/docs/sessions.txt +++ b/docs/sessions.txt @@ -205,7 +205,7 @@ An API is available to manipulate session data outside of a view:: datetime.datetime(2005, 8, 20, 13, 35, 0) >>> s.save() -If you're using the ``django.contrib.sessions.engine.db`` backend, each +If you're using the ``django.contrib.sessions.backends.db`` backend, each session is just a normal Django model. The ``Session`` model is defined in ``django/contrib/sessions/models.py``. Because it's a normal model, you can access sessions using the normal Django database API:: diff --git a/docs/settings.txt b/docs/settings.txt index e40374a822..7ad1f6441d 100644 --- a/docs/settings.txt +++ b/docs/settings.txt @@ -363,7 +363,7 @@ regular expression which will hide from the DEBUG view anything that contains be able to give backtraces without seeing sensitive (or offensive) settings. Still, note that there are always going to be sections of your debug output that -are inapporpriate for public consumption. File paths, configuration options, and +are inappropriate for public consumption. File paths, configuration options, and the like all give attackers extra information about your server. Never deploy a site with ``DEBUG`` turned on. diff --git a/docs/templates_python.txt b/docs/templates_python.txt index 232f54061f..bd105888ce 100644 --- a/docs/templates_python.txt +++ b/docs/templates_python.txt @@ -316,7 +316,7 @@ optional, third positional argument, ``processors``. In this example, the }, [ip_address_processor]) return t.render(c) -Note:: +.. note:: If you're using Django's ``render_to_response()`` shortcut to populate a template with the contents of a dictionary, your template will be passed a ``Context`` instance by default (not a ``RequestContext``). To use a @@ -928,10 +928,36 @@ current context, available in the ``render`` method:: ``resolve_variable`` will try to resolve ``blog_entry.date_updated`` and then format it accordingly. -.. note:: - The ``resolve_variable()`` function will throw a ``VariableDoesNotExist`` - exception if it cannot resolve the string passed to it in the current - context of the page. +.. admonition:: New in development version: + + Variable resolution has changed in the development version of Django. + ``template.resolve_variable()`` is still available, but has been deprecated + in favor of a new ``template.Variable`` class. Using this class will usually + be more efficient than calling ``template.resolve_variable`` + + To use the ``Variable`` class, simply instantiate it with the name of the + variable to be resolved, and then call ``variable.resolve(context)``. So, + in the development version, the above example would be more correctly + written as: + + .. parsed-literal:: + + class FormatTimeNode(template.Node): + def __init__(self, date_to_be_formatted, format_string): + self.date_to_be_formatted = **Variable(date_to_be_formatted)** + self.format_string = format_string + + def render(self, context): + try: + actual_date = **self.date_to_be_formatted.resolve(context)** + return actual_date.strftime(self.format_string) + except template.VariableDoesNotExist: + return '' + + Changes are highlighted in bold. + +Variable resolution will throw a ``VariableDoesNotExist`` exception if it cannot +resolve the string passed to it in the current context of the page. Shortcut for simple tags ~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/unicode.txt b/docs/unicode.txt index 1ab255970c..a0e2648f57 100644 --- a/docs/unicode.txt +++ b/docs/unicode.txt @@ -110,19 +110,22 @@ Conversion functions The ``django.utils.encoding`` module contains a few functions that are handy for converting back and forth between Unicode and bytestrings. - * ``smart_unicode(s, encoding='utf-8', errors='strict')`` converts its - input to a Unicode string. The ``encoding`` parameter specifies the input - encoding. (For example, Django uses this internally when processing form - input data, which might not be UTF-8 encoded.) The ``errors`` parameter - takes any of the values that are accepted by Python's ``unicode()`` - function for its error handling. + * ``smart_unicode(s, encoding='utf-8', strings_only=False, errors='strict')`` + converts its input to a Unicode string. The ``encoding`` parameter + specifies the input encoding. (For example, Django uses this internally + when processing form input data, which might not be UTF-8 encoded.) The + ``strings_only`` parameter, if set to True, will result in Python + numbers, booleans and ``None`` not being converted to a string (they keep + their original types). The ``errors`` parameter takes any of the values + that are accepted by Python's ``unicode()`` function for its error + handling. If you pass ``smart_unicode()`` an object that has a ``__unicode__`` method, it will use that method to do the conversion. - * ``force_unicode(s, encoding='utf-8', errors='strict')`` is identical to - ``smart_unicode()`` in almost all cases. The difference is when the - first argument is a `lazy translation`_ instance. While + * ``force_unicode(s, encoding='utf-8', strings_only=False, errors='strict')`` + is identical to ``smart_unicode()`` in almost all cases. The difference + is when the first argument is a `lazy translation`_ instance. While ``smart_unicode()`` preserves lazy translations, ``force_unicode()`` forces those objects to a Unicode string (causing the translation to occur). Normally, you'll want to use ``smart_unicode()``. However, @@ -132,11 +135,10 @@ for converting back and forth between Unicode and bytestrings. * ``smart_str(s, encoding='utf-8', strings_only=False, errors='strict')`` is essentially the opposite of ``smart_unicode()``. It forces the first - argument to a bytestring. The ``strings_only`` parameter, if set to True, - will result in Python integers, booleans and ``None`` not being - converted to a string (they keep their original types). This is slightly - different semantics from Python's builtin ``str()`` function, but the - difference is needed in a few places within Django's internals. + argument to a bytestring. The ``strings_only`` parameter has the same + behaviour as for ``smart_unicode()`` and ``force_unicode()``. This is + slightly different semantics from Python's builtin ``str()`` function, + but the difference is needed in a few places within Django's internals. Normally, you'll only need to use ``smart_unicode()``. Call it as early as possible on any input data that might be either Unicode or a bytestring, and diff --git a/tests/modeltests/basic/models.py b/tests/modeltests/basic/models.py index 58770ef2ce..2f4b932fe0 100644 --- a/tests/modeltests/basic/models.py +++ b/tests/modeltests/basic/models.py @@ -152,7 +152,7 @@ TypeError: 'foo' is an invalid keyword argument for this function >>> a6 = Article(pub_date=datetime(2005, 7, 31)) >>> a6.save() >>> a6.headline -'Default headline' +u'Default headline' # For DateTimeFields, Django saves as much precision (in seconds) as you # give it. diff --git a/tests/modeltests/field_defaults/models.py b/tests/modeltests/field_defaults/models.py index 23c4733b8f..1132f1ca41 100644 --- a/tests/modeltests/field_defaults/models.py +++ b/tests/modeltests/field_defaults/models.py @@ -42,7 +42,7 @@ __test__ = {'API_TESTS':""" # Access database columns via Python attributes. >>> a.headline -'Default headline' +u'Default headline' # make sure the two dates are sufficiently close >>> d = now - a.pub_date diff --git a/tests/modeltests/serializers/models.py b/tests/modeltests/serializers/models.py index a0a68d4bcc..a2388223f0 100644 --- a/tests/modeltests/serializers/models.py +++ b/tests/modeltests/serializers/models.py @@ -63,6 +63,9 @@ class Movie(models.Model): def __unicode__(self): return self.title + +class Score(models.Model): + score = models.FloatField() __test__ = {'API_TESTS':""" # Create some data: @@ -83,7 +86,7 @@ __test__ = {'API_TESTS':""" >>> a2 = Article( ... author = joe, ... headline = "Time to reform copyright", -... pub_date = datetime(2006, 6, 16, 13, 00)) +... pub_date = datetime(2006, 6, 16, 13, 00, 11, 345)) >>> a1.save(); a2.save() >>> a1.categories = [sports, op_ed] >>> a2.categories = [music, op_ed] @@ -181,7 +184,7 @@ __test__ = {'API_TESTS':""" # Serializer output can be restricted to a subset of fields >>> print serializers.serialize("json", Article.objects.all(), fields=('headline','pub_date')) -[{"pk": 1, "model": "serializers.article", "fields": {"headline": "Just kidding; I love TV poker", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 2, "model": "serializers.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 3, "model": "serializers.article", "fields": {"headline": "Forward references pose no problem", "pub_date": "2006-06-16 15:00:00"}}] +[{"pk": 1, "model": "serializers.article", "fields": {"headline": "Just kidding; I love TV poker", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 2, "model": "serializers.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:11"}}, {"pk": 3, "model": "serializers.article", "fields": {"headline": "Forward references pose no problem", "pub_date": "2006-06-16 15:00:00"}}] # Every string is serialized as a unicode object, also primary key # which is 'varchar' @@ -207,4 +210,11 @@ u'G\u0119\u015bl\u0105 ja\u017a\u0144' >>> print list(serializers.deserialize('json', serializers.serialize('json', [mv2])))[0].object.id None +# Serialization and deserialization of floats: +>>> sc = Score(score=3.4) +>>> print serializers.serialize("json", [sc]) +[{"pk": null, "model": "serializers.score", "fields": {"score": 3.4}}] +>>> print list(serializers.deserialize('json', serializers.serialize('json', [sc])))[0].object.score +3.4 + """} diff --git a/tests/modeltests/signals/models.py b/tests/modeltests/signals/models.py index d41142135e..1fb73828bb 100644 --- a/tests/modeltests/signals/models.py +++ b/tests/modeltests/signals/models.py @@ -54,7 +54,7 @@ Is updated pre_delete signal, Tom Smith instance.id is not None: True post_delete signal, Tom Smith -instance.id is None: True +instance.id is None: False >>> p2 = Person(first_name='James', last_name='Jones') >>> p2.id = 99999 @@ -73,7 +73,7 @@ Is created pre_delete signal, James Jones instance.id is not None: True post_delete signal, James Jones -instance.id is None: True +instance.id is None: False >>> Person.objects.all() [<Person: James Jones>] diff --git a/tests/modeltests/user_commands/__init__.py b/tests/modeltests/user_commands/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/modeltests/user_commands/__init__.py diff --git a/tests/modeltests/user_commands/management/__init__.py b/tests/modeltests/user_commands/management/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/modeltests/user_commands/management/__init__.py diff --git a/tests/modeltests/user_commands/management/commands/__init__.py b/tests/modeltests/user_commands/management/commands/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/modeltests/user_commands/management/commands/__init__.py diff --git a/tests/modeltests/user_commands/management/commands/dance.py b/tests/modeltests/user_commands/management/commands/dance.py new file mode 100644 index 0000000000..5886cd1d8f --- /dev/null +++ b/tests/modeltests/user_commands/management/commands/dance.py @@ -0,0 +1,9 @@ +from django.core.management.base import BaseCommand + +class Command(BaseCommand): + help = "Dance around like a madman." + args = '' + requires_model_validation = True + + def handle(self, *args, **options): + print "I don't feel like dancing."
\ No newline at end of file diff --git a/tests/modeltests/user_commands/models.py b/tests/modeltests/user_commands/models.py new file mode 100644 index 0000000000..5f96806dac --- /dev/null +++ b/tests/modeltests/user_commands/models.py @@ -0,0 +1,30 @@ +""" +37. User-registered management commands + +The manage.py utility provides a number of useful commands for managing a +Django project. If you want to add a utility command of your own, you can. + +The user-defined command 'dance' is defined in the management/commands +subdirectory of this test application. It is a simple command that responds +with a printed message when invoked. + +For more details on how to define your own manage.py commands, look at the +django.core.management.commands directory. This directory contains the +definitions for the base Django manage.py commands. +""" + +__test__ = {'API_TESTS': """ +>>> from django.core import management + +# Invoke a simple user-defined command +>>> management.call_command('dance') +I don't feel like dancing. + +# Invoke a command that doesn't exist +>>> management.call_command('explode') +Traceback (most recent call last): +... +CommandError: Unknown command: 'explode' + + +"""}
\ No newline at end of file diff --git a/tests/regressiontests/httpwrappers/tests.py b/tests/regressiontests/httpwrappers/tests.py index 9d5ca2006a..5cfae029bb 100644 --- a/tests/regressiontests/httpwrappers/tests.py +++ b/tests/regressiontests/httpwrappers/tests.py @@ -8,7 +8,7 @@ >>> q['foo'] Traceback (most recent call last): ... -MultiValueDictKeyError: "Key 'foo' not found in <MultiValueDict: {}>" +MultiValueDictKeyError: "Key 'foo' not found in <QueryDict: {}>" >>> q['something'] = 'bar' Traceback (most recent call last): @@ -89,7 +89,7 @@ AttributeError: This QueryDict instance is immutable >>> q['foo'] Traceback (most recent call last): ... -MultiValueDictKeyError: "Key 'foo' not found in <MultiValueDict: {}>" +MultiValueDictKeyError: "Key 'foo' not found in <QueryDict: {}>" >>> q['name'] = 'john' @@ -201,7 +201,7 @@ u'bar' >>> q['bar'] Traceback (most recent call last): ... -MultiValueDictKeyError: "Key 'bar' not found in <MultiValueDict: {u'foo': [u'bar']}>" +MultiValueDictKeyError: "Key 'bar' not found in <QueryDict: {u'foo': [u'bar']}>" >>> q['something'] = 'bar' Traceback (most recent call last): diff --git a/tests/regressiontests/i18n/models.py b/tests/regressiontests/i18n/models.py index e69de29bb2..e1eefb8477 100644 --- a/tests/regressiontests/i18n/models.py +++ b/tests/regressiontests/i18n/models.py @@ -0,0 +1,12 @@ +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +class TestModel(models.Model): + text = models.CharField(max_length=10, default=_('Anything')) + +__test__ = {'API_TESTS': ''' +>>> tm = TestModel() +>>> tm.save() +''' +} + diff --git a/tests/regressiontests/i18n/tests.py b/tests/regressiontests/i18n/tests.py index 8a7d2bee3e..99204451e4 100644 --- a/tests/regressiontests/i18n/tests.py +++ b/tests/regressiontests/i18n/tests.py @@ -30,4 +30,12 @@ True >>> s4 = ugettext_lazy('Some other string') >>> s == s4 False + +unicode(string_concat(...)) should not raise a TypeError - #4796 + +>>> import django.utils.translation +>>> reload(django.utils.translation) +<module 'django.utils.translation' from ...> +>>> unicode(django.utils.translation.string_concat("dja", "ngo")) +u'django' """ diff --git a/tests/regressiontests/views/tests/generic/date_based.py b/tests/regressiontests/views/tests/generic/date_based.py index aca93d4579..94ae99a20a 100644 --- a/tests/regressiontests/views/tests/generic/date_based.py +++ b/tests/regressiontests/views/tests/generic/date_based.py @@ -43,9 +43,9 @@ class MonthArchiveTest(TestCase): author.save() # 2004 was a leap year, so it should be weird enough to not cheat - first_second_of_feb = datetime(2004, 2, 1, 0, 0, 0) - first_second_of_mar = datetime(2004, 3, 1, 0, 0, 0) - one_microsecond = timedelta(0, 0, 1) + first_second_of_feb = datetime(2004, 2, 1, 0, 0, 1) + first_second_of_mar = datetime(2004, 3, 1, 0, 0, 1) + two_seconds = timedelta(0, 2, 0) article = Article(title="example", author=author) article.date_created = first_second_of_feb @@ -53,12 +53,12 @@ class MonthArchiveTest(TestCase): response = self.client.get('/views/date_based/archive_month/2004/02/') self.assertEqual(response.status_code, 200) - article.date_created = first_second_of_feb-one_microsecond + article.date_created = first_second_of_feb-two_seconds article.save() response = self.client.get('/views/date_based/archive_month/2004/02/') self.assertEqual(response.status_code, 404) - article.date_created = first_second_of_mar-one_microsecond + article.date_created = first_second_of_mar-two_seconds article.save() response = self.client.get('/views/date_based/archive_month/2004/02/') self.assertEqual(response.status_code, 200) |
