1
0
mirror of https://github.com/django/django.git synced 2025-07-06 18:59:13 +00:00

queryset-refactor: Merged from trunk up to [6752].

git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@6753 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Malcolm Tredinnick 2007-11-29 20:38:41 +00:00
parent b43a018032
commit dfe05d94b8
47 changed files with 3797 additions and 2231 deletions

View File

@ -71,6 +71,7 @@ answer newbie questions, and generally made Django that much better:
Jonathan Buchanan <jonathan.buchanan@gmail.com> Jonathan Buchanan <jonathan.buchanan@gmail.com>
Trevor Caira <trevor@caira.com> Trevor Caira <trevor@caira.com>
Ricardo Javier Cárdenes Medina <ricardo.cardenes@gmail.com> Ricardo Javier Cárdenes Medina <ricardo.cardenes@gmail.com>
Graham Carlyle <graham.carlyle@maplecroft.net>
Antonio Cavedoni <http://cavedoni.com/> Antonio Cavedoni <http://cavedoni.com/>
C8E C8E
cedric@terramater.net cedric@terramater.net
@ -100,6 +101,7 @@ answer newbie questions, and generally made Django that much better:
Alex Dedul Alex Dedul
deric@monowerks.com deric@monowerks.com
Max Derkachev <mderk@yandex.ru> Max Derkachev <mderk@yandex.ru>
Rajesh Dhawan <rajesh.dhawan@gmail.com>
Sander Dijkhuis <sander.dijkhuis@gmail.com> Sander Dijkhuis <sander.dijkhuis@gmail.com>
Jordan Dimov <s3x3y1@gmail.com> Jordan Dimov <s3x3y1@gmail.com>
dne@mayonnaise.net dne@mayonnaise.net
@ -187,6 +189,7 @@ answer newbie questions, and generally made Django that much better:
krzysiek.pawlik@silvermedia.pl krzysiek.pawlik@silvermedia.pl
Joseph Kocherhans Joseph Kocherhans
konrad@gwu.edu konrad@gwu.edu
knox <christobzr@gmail.com>
kurtiss@meetro.com kurtiss@meetro.com
lakin.wecker@gmail.com lakin.wecker@gmail.com
Nick Lane <nick.lane.au@gmail.com> Nick Lane <nick.lane.au@gmail.com>
@ -273,6 +276,7 @@ answer newbie questions, and generally made Django that much better:
Vinay Sajip <vinay_sajip@yahoo.co.uk> Vinay Sajip <vinay_sajip@yahoo.co.uk>
David Schein David Schein
scott@staplefish.com scott@staplefish.com
Ilya Semenov <semenov@inetss.com>
serbaut@gmail.com serbaut@gmail.com
John Shaffer <jshaffer2112@gmail.com> John Shaffer <jshaffer2112@gmail.com>
Pete Shinners <pete@shinners.org> Pete Shinners <pete@shinners.org>

File diff suppressed because it is too large Load Diff

View File

@ -7,12 +7,12 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2005-12-09 11:51+0100\n" "POT-Creation-Date: 2007-11-29 10:58-0600\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: contrib/admin/media/js/SelectFilter2.js:33 #: contrib/admin/media/js/SelectFilter2.js:33
@ -45,64 +45,73 @@ msgstr ""
msgid "Clear all" msgid "Clear all"
msgstr "" msgstr ""
#: contrib/admin/media/js/dateparse.js:26
#: contrib/admin/media/js/calendar.js:24 #: contrib/admin/media/js/calendar.js:24
#: contrib/admin/media/js/dateparse.js:32
msgid "" msgid ""
"January February March April May June July August September October November " "January February March April May June July August September October November "
"December" "December"
msgstr "" msgstr ""
#: contrib/admin/media/js/dateparse.js:27
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday"
msgstr ""
#: contrib/admin/media/js/calendar.js:25 #: contrib/admin/media/js/calendar.js:25
msgid "S M T W T F S" msgid "S M T W T F S"
msgstr "" msgstr ""
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:45 #: contrib/admin/media/js/dateparse.js:33
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:80 msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday"
msgstr ""
#: contrib/admin/media/js/admin/CollapsedFieldsets.js:34
#: contrib/admin/media/js/admin/CollapsedFieldsets.js:72
msgid "Show"
msgstr ""
#: contrib/admin/media/js/admin/CollapsedFieldsets.js:63
msgid "Hide"
msgstr ""
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:47
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:81
msgid "Now" msgid "Now"
msgstr "" msgstr ""
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:48 #: contrib/admin/media/js/admin/DateTimeShortcuts.js:51
msgid "Clock" msgid "Clock"
msgstr "" msgstr ""
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:77 #: contrib/admin/media/js/admin/DateTimeShortcuts.js:78
msgid "Choose a time" msgid "Choose a time"
msgstr "" msgstr ""
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:81 #: contrib/admin/media/js/admin/DateTimeShortcuts.js:82
msgid "Midnight" msgid "Midnight"
msgstr "" msgstr ""
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:82 #: contrib/admin/media/js/admin/DateTimeShortcuts.js:83
msgid "6 a.m." msgid "6 a.m."
msgstr "" msgstr ""
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:83 #: contrib/admin/media/js/admin/DateTimeShortcuts.js:84
msgid "Noon" msgid "Noon"
msgstr "" msgstr ""
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:87 #: contrib/admin/media/js/admin/DateTimeShortcuts.js:88
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:168 #: contrib/admin/media/js/admin/DateTimeShortcuts.js:183
msgid "Cancel" msgid "Cancel"
msgstr "" msgstr ""
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:111 #: contrib/admin/media/js/admin/DateTimeShortcuts.js:128
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:162 #: contrib/admin/media/js/admin/DateTimeShortcuts.js:177
msgid "Today" msgid "Today"
msgstr "" msgstr ""
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:114 #: contrib/admin/media/js/admin/DateTimeShortcuts.js:132
msgid "Calendar" msgid "Calendar"
msgstr "" msgstr ""
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:160 #: contrib/admin/media/js/admin/DateTimeShortcuts.js:175
msgid "Yesterday" msgid "Yesterday"
msgstr "" msgstr ""
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:164 #: contrib/admin/media/js/admin/DateTimeShortcuts.js:179
msgid "Tomorrow" msgid "Tomorrow"
msgstr "" msgstr ""

View File

@ -13,7 +13,7 @@
{% for library in filter_libraries %} {% for library in filter_libraries %}
<div class="module"> <div class="module">
<h2>{% if library.grouper %}{{ library.grouper }}{% else %}Built-in filters{% endif %}</h2> <h2>{% if library.grouper %}{{ library.grouper }}{% else %}Built-in filters{% endif %}</h2>
{% if library.grouper %}<p class="small quiet">To use these filters, put <code>{% templatetag openblock %} load {{ library.grouper }} {% templatetag closeblock %}</code> in your template before using the filter.</p><hr>{% endif %} {% if library.grouper %}<p class="small quiet">To use these filters, put <code>{% templatetag openblock %} load {{ library.grouper }} {% templatetag closeblock %}</code> in your template before using the filter.</p><hr />{% endif %}
{% for filter in library.list|dictsort:"name" %} {% for filter in library.list|dictsort:"name" %}
<h3 id="{{ filter.name }}">{{ filter.name }}</h3> <h3 id="{{ filter.name }}">{{ filter.name }}</h3>
<p>{{ filter.title }}</p> <p>{{ filter.title }}</p>

View File

@ -13,7 +13,7 @@
{% for library in tag_libraries %} {% for library in tag_libraries %}
<div class="module"> <div class="module">
<h2>{% if library.grouper %}{{ library.grouper }}{% else %}Built-in tags{% endif %}</h2> <h2>{% if library.grouper %}{{ library.grouper }}{% else %}Built-in tags{% endif %}</h2>
{% if library.grouper %}<p class="small quiet">To use these tags, put <code>{% templatetag openblock %} load {{ library.grouper }} {% templatetag closeblock %}</code> in your template before using the tag.</p><hr>{% endif %} {% if library.grouper %}<p class="small quiet">To use these tags, put <code>{% templatetag openblock %} load {{ library.grouper }} {% templatetag closeblock %}</code> in your template before using the tag.</p><hr />{% endif %}
{% for tag in library.list|dictsort:"name" %} {% for tag in library.list|dictsort:"name" %}
<h3 id="{{ tag.name }}">{{ tag.name }}</h3> <h3 id="{{ tag.name }}">{{ tag.name }}</h3>
<h4>{{ tag.title }}</h4> <h4>{{ tag.title }}</h4>

View File

@ -29,10 +29,10 @@
{% for view in site_views.list|dictsort:"url" %} {% for view in site_views.list|dictsort:"url" %}
{% ifchanged %} {% ifchanged %}
<h3><a href="{{ view.module }}.{{ view.name }}/"/>{{ view.url|escape }}</a></h3> <h3><a href="{{ view.module }}.{{ view.name }}/">{{ view.url|escape }}</a></h3>
<p class="small quiet">View function: {{ view.module }}.{{ view.name }}</p> <p class="small quiet">View function: {{ view.module }}.{{ view.name }}</p>
<p>{{ view.title }}</p> <p>{{ view.title }}</p>
<hr> <hr />
{% endifchanged %} {% endifchanged %}
{% endfor %} {% endfor %}
</div> </div>

View File

@ -102,7 +102,7 @@ class Comment(models.Model):
date_hierarchy = 'submit_date' date_hierarchy = 'submit_date'
search_fields = ('comment', 'user__username') search_fields = ('comment', 'user__username')
def __repr__(self): def __unicode__(self):
return "%s: %s..." % (self.user.username, self.comment[:100]) return "%s: %s..." % (self.user.username, self.comment[:100])
def get_absolute_url(self): def get_absolute_url(self):
@ -190,7 +190,7 @@ class FreeComment(models.Model):
date_hierarchy = 'submit_date' date_hierarchy = 'submit_date'
search_fields = ('comment', 'person_name') search_fields = ('comment', 'person_name')
def __repr__(self): def __unicode__(self):
return "%s: %s..." % (self.person_name, self.comment[:100]) return "%s: %s..." % (self.person_name, self.comment[:100])
def get_absolute_url(self): def get_absolute_url(self):
@ -244,7 +244,7 @@ class KarmaScore(models.Model):
verbose_name_plural = _('karma scores') verbose_name_plural = _('karma scores')
unique_together = (('user', 'comment'),) unique_together = (('user', 'comment'),)
def __repr__(self): def __unicode__(self):
return _("%(score)d rating by %(user)s") % {'score': self.score, 'user': self.user} return _("%(score)d rating by %(user)s") % {'score': self.score, 'user': self.user}
class UserFlagManager(models.Manager): class UserFlagManager(models.Manager):
@ -275,7 +275,7 @@ class UserFlag(models.Model):
verbose_name_plural = _('user flags') verbose_name_plural = _('user flags')
unique_together = (('user', 'comment'),) unique_together = (('user', 'comment'),)
def __repr__(self): def __unicode__(self):
return _("Flag by %r") % self.user return _("Flag by %r") % self.user
class ModeratorDeletion(models.Model): class ModeratorDeletion(models.Model):
@ -287,5 +287,5 @@ class ModeratorDeletion(models.Model):
verbose_name_plural = _('moderator deletions') verbose_name_plural = _('moderator deletions')
unique_together = (('user', 'comment'),) unique_together = (('user', 'comment'),)
def __repr__(self): def __unicode__(self):
return _("Moderator deletion by %r") % self.user return _("Moderator deletion by %r") % self.user

View File

@ -0,0 +1,13 @@
# coding: utf-8
r"""
>>> from django.contrib.comments.models import Comment
>>> from django.contrib.auth.models import User
>>> u = User.objects.create_user('commenttestuser', 'commenttest@example.com', 'testpw')
>>> c = Comment(user=u, comment=u'\xe2')
>>> c
<Comment: commenttestuser: â...>
>>> print c
commenttestuser: â...
"""

View File

@ -0,0 +1 @@
""" models.py (even empty) currently required by the runtests.py to enable unit tests """

View File

@ -0,0 +1,12 @@
"""
This is a urlconf to be loaded by tests.py. Add any urls needed
for tests only.
"""
from django.conf.urls.defaults import *
from django.contrib.formtools.tests import *
urlpatterns = patterns('',
(r'^test1/', TestFormPreview(TestForm)),
)

View File

@ -0,0 +1,93 @@
from django import newforms as forms
from django.contrib.formtools import preview
from django import http
from django.conf import settings
from django.test import TestCase
from django.test.client import Client
success_string = "Done was called!"
test_data = {'field1': u'foo',
'field1_': u'asdf'}
class TestFormPreview(preview.FormPreview):
def done(self, request, cleaned_data):
return http.HttpResponse(success_string)
class TestForm(forms.Form):
field1 = forms.CharField()
field1_ = forms.CharField()
class PreviewTests(TestCase):
def setUp(self):
settings.ROOT_URLCONF = 'django.contrib.formtools.test_urls'
# Create a FormPreview instance to share between tests
self.preview = preview.FormPreview(TestForm)
input_template = '<input type="hidden" name="%s" value="%s" />'
self.input = input_template % (self.preview.unused_name('stage'), "%d")
def test_unused_name(self):
"""
Verifies name mangling to get uniue field name.
"""
self.assertEqual(self.preview.unused_name('field1'), 'field1__')
def test_form_get(self):
"""
Test contrib.formtools.preview form retrieval.
Use the client library to see if we can sucessfully retrieve
the form (mostly testing the setup ROOT_URLCONF
process). Verify that an additional hidden input field
is created to manage the stage.
"""
response = self.client.get('/test1/')
stage = self.input % 1
self.assertContains(response, stage, 1)
def test_form_preview(self):
"""
Test contrib.formtools.preview form preview rendering.
Use the client library to POST to the form to see if a preview
is returned. If we do get a form back check that the hidden
value is correctly managing the state of the form.
"""
# Pass strings for form submittal and add stage variable to
# show we previously saw first stage of the form.
test_data.update({'stage': 1})
response = self.client.post('/test1/', test_data)
# Check to confirm stage is set to 2 in output form.
stage = self.input % 2
self.assertContains(response, stage, 1)
def test_form_submit(self):
"""
Test contrib.formtools.preview form submittal.
Use the client library to POST to the form with stage set to 3
to see if our forms done() method is called. Check first
without the security hash, verify failure, retry with security
hash and verify sucess.
"""
# Pass strings for form submittal and add stage variable to
# show we previously saw first stage of the form.
test_data.update({'stage': 2})
response = self.client.post('/test1/', test_data)
self.failIfEqual(response.content, success_string)
hash = self.preview.security_hash(None, TestForm(test_data))
test_data.update({'hash': hash})
response = self.client.post('/test1/', test_data)
self.assertEqual(response.content, success_string)
if __name__ == '__main__':
unittest.main()

View File

@ -33,6 +33,8 @@ PROVINCES_NORMALIZED = {
'british columbia': 'BC', 'british columbia': 'BC',
'mb': 'MB', 'mb': 'MB',
'manitoba': 'MB', 'manitoba': 'MB',
'nb': 'NB',
'new brunswick': 'NB',
'nf': 'NF', 'nf': 'NF',
'newfoundland': 'NF', 'newfoundland': 'NF',
'newfoundland and labrador': 'NF', 'newfoundland and labrador': 'NF',

View File

@ -471,5 +471,5 @@ def method_get_order(ordered_obj, self):
# HELPER FUNCTIONS (CURRIED MODEL FUNCTIONS) # # HELPER FUNCTIONS (CURRIED MODEL FUNCTIONS) #
############################################## ##############################################
def get_absolute_url(opts, func, self): def get_absolute_url(opts, func, self, *args, **kwargs):
return settings.ABSOLUTE_URL_OVERRIDES.get('%s.%s' % (opts.app_label, opts.module_name), func)(self) return settings.ABSOLUTE_URL_OVERRIDES.get('%s.%s' % (opts.app_label, opts.module_name), func)(self, *args, **kwargs)

View File

@ -399,7 +399,7 @@ class Field(object):
"Returns a django.newforms.Field instance for this database Field." "Returns a django.newforms.Field instance for this database Field."
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text} defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
if self.choices: if self.choices:
defaults['widget'] = forms.Select(choices=self.get_choices()) defaults['widget'] = forms.Select(choices=self.get_choices(include_blank=self.blank or not (self.has_default() or 'initial' in kwargs)))
if self.has_default(): if self.has_default():
defaults['initial'] = self.get_default() defaults['initial'] = self.get_default()
defaults.update(kwargs) defaults.update(kwargs)

View File

@ -28,10 +28,10 @@ class Creator(object):
def __get__(self, obj, type=None): def __get__(self, obj, type=None):
if obj is None: if obj is None:
raise AttributeError('Can only be accessed via an instance.') raise AttributeError('Can only be accessed via an instance.')
return self.value return obj.__dict__[self.field.name]
def __set__(self, obj, value): def __set__(self, obj, value):
self.value = self.field.to_python(value) obj.__dict__[self.field.name] = self.field.to_python(value)
def make_contrib(func=None): def make_contrib(func=None):
""" """

View File

@ -1,14 +1,18 @@
from django.conf import settings from django.conf import settings
from django.core.cache import cache from django.core.cache import cache
from django.utils.cache import get_cache_key, learn_cache_key, patch_response_headers from django.utils.cache import get_cache_key, learn_cache_key, patch_response_headers, get_max_age
class CacheMiddleware(object): class CacheMiddleware(object):
""" """
Cache middleware. If this is enabled, each Django-powered page will be Cache middleware. If this is enabled, each Django-powered page will be
cached for CACHE_MIDDLEWARE_SECONDS seconds. Cache is based on URLs. cached (based on URLs).
Only parameter-less GET or HEAD-requests with status code 200 are cached. Only parameter-less GET or HEAD-requests with status code 200 are cached.
The number of seconds each page is stored for is set by the
"max-age" section of the response's "Cache-Control" header, falling back to
the CACHE_MIDDLEWARE_SECONDS setting if the section was not found.
If CACHE_MIDDLEWARE_ANONYMOUS_ONLY is set to True, only anonymous requests If CACHE_MIDDLEWARE_ANONYMOUS_ONLY is set to True, only anonymous requests
(i.e., those not made by a logged-in user) will be cached. This is a (i.e., those not made by a logged-in user) will be cached. This is a
simple and effective way of avoiding the caching of the Django admin (and simple and effective way of avoiding the caching of the Django admin (and
@ -78,7 +82,16 @@ class CacheMiddleware(object):
return response return response
if not response.status_code == 200: if not response.status_code == 200:
return response return response
patch_response_headers(response, self.cache_timeout) # Try to get the timeout from the "max-age" section of the "Cache-
cache_key = learn_cache_key(request, response, self.cache_timeout, self.key_prefix) # Control" header before reverting to using the default cache_timeout
cache.set(cache_key, response, self.cache_timeout) # length.
timeout = get_max_age(response)
if timeout == None:
timeout = self.cache_timeout
elif timeout == 0:
# max-age was set to 0, don't bother caching.
return response
patch_response_headers(response, timeout)
cache_key = learn_cache_key(request, response, timeout, self.key_prefix)
cache.set(cache_key, response, timeout)
return response return response

View File

@ -17,7 +17,7 @@ except NameError:
from sets import Set as set from sets import Set as set
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import StrAndUnicode, smart_unicode from django.utils.encoding import StrAndUnicode, smart_unicode, smart_str
from util import ErrorList, ValidationError from util import ErrorList, ValidationError
from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, DateTimeInput from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, DateTimeInput
@ -235,7 +235,7 @@ class DecimalField(Field):
super(DecimalField, self).clean(value) super(DecimalField, self).clean(value)
if not self.required and value in EMPTY_VALUES: if not self.required and value in EMPTY_VALUES:
return None return None
value = str(value).strip() value = smart_str(value).strip()
try: try:
value = Decimal(value) value = Decimal(value)
except DecimalException: except DecimalException:
@ -536,11 +536,12 @@ class BooleanField(Field):
widget = CheckboxInput widget = CheckboxInput
def clean(self, value): def clean(self, value):
"Returns a Python boolean object." """Returns a Python boolean object."""
super(BooleanField, self).clean(value) super(BooleanField, self).clean(value)
# Explicitly check for the string '0', which is what as hidden field # Explicitly check for the string 'False', which is what a hidden field
# will submit for False. # will submit for False (since bool("True") == True we don't need to
if value == '0': # handle that explicitly).
if value == 'False':
return False return False
return bool(value) return bool(value)

View File

@ -25,8 +25,8 @@ def stringfilter(func):
if args: if args:
args = list(args) args = list(args)
args[0] = force_unicode(args[0]) args[0] = force_unicode(args[0])
if isinstance(args[0], SafeData) and getattr(func, 'is_safe', False): if isinstance(args[0], SafeData) and getattr(func, 'is_safe', False):
return mark_safe(func(*args, **kwargs)) return mark_safe(func(*args, **kwargs))
return func(*args, **kwargs) return func(*args, **kwargs)
# Include a reference to the real function (used to check original # Include a reference to the real function (used to check original
@ -91,7 +91,7 @@ def floatformat(text, arg=-1):
""" """
try: try:
f = float(text) f = float(text)
except ValueError: except (ValueError, TypeError):
return u'' return u''
try: try:
d = int(arg) d = int(arg)

View File

@ -74,6 +74,21 @@ def patch_cache_control(response, **kwargs):
cc = ', '.join([dictvalue(el) for el in cc.items()]) cc = ', '.join([dictvalue(el) for el in cc.items()])
response['Cache-Control'] = cc response['Cache-Control'] = cc
def get_max_age(response):
"""
Returns the max-age from the response Cache-Control header as an integer
(or ``None`` if it wasn't found or wasn't an integer.
"""
if not response.has_header('Cache-Control'):
return
cc = dict([_to_tuple(el) for el in
cc_delim_re.split(response['Cache-Control'])])
if 'max-age' in cc:
try:
return int(cc['max-age'])
except (ValueError, TypeError):
pass
def patch_response_headers(response, cache_timeout=None): def patch_response_headers(response, cache_timeout=None):
""" """
Adds some useful headers to the given HttpResponse object: Adds some useful headers to the given HttpResponse object:
@ -180,3 +195,10 @@ def learn_cache_key(request, response, cache_timeout=None, key_prefix=None):
# for the request.path # for the request.path
cache.set(cache_key, [], cache_timeout) cache.set(cache_key, [], cache_timeout)
return _generate_cache_key(request, [], key_prefix) return _generate_cache_key(request, [], key_prefix)
def _to_tuple(s):
t = s.split('=',1)
if len(t) == 2:
return t[0].lower(), t[1]
return t[0].lower(), True

View File

@ -60,7 +60,10 @@ class SortedDict(dict):
if isinstance(data, dict): if isinstance(data, dict):
self.keyOrder = data.keys() self.keyOrder = data.keys()
else: else:
self.keyOrder = [key for key, value in data] self.keyOrder = []
for key, value in data:
if key not in self.keyOrder:
self.keyOrder.append(key)
def __deepcopy__(self, memo): def __deepcopy__(self, memo):
from copy import deepcopy from copy import deepcopy

View File

@ -54,6 +54,12 @@ class LocalTimezone(tzinfo):
def _isdst(self, dt): def _isdst(self, dt):
tt = (dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.weekday(), 0, -1) tt = (dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.weekday(), 0, -1)
stamp = time.mktime(tt) try:
stamp = time.mktime(tt)
except OverflowError:
# 32 bit systems can't handle dates after Jan 2038, so we fake it
# in that case (since we only care about the DST flag here).
tt = (2037,) + tt[1:]
stamp = time.mktime(tt)
tt = time.localtime(stamp) tt = time.localtime(stamp)
return tt.tm_isdst > 0 return tt.tm_isdst > 0

View File

@ -120,8 +120,12 @@ def javascript_catalog(request, domain='djangojs', packages=None):
p = __import__(package, {}, {}, ['']) p = __import__(package, {}, {}, [''])
path = os.path.join(os.path.dirname(p.__file__), 'locale') path = os.path.join(os.path.dirname(p.__file__), 'locale')
paths.append(path) paths.append(path)
catalog = gettext_module.translation(domain, path, ['en']) try:
t.update(catalog._catalog) catalog = gettext_module.translation(domain, path, ['en'])
t.update(catalog._catalog)
except IOError:
# 'en' catalog was missing. This is harmless.
pass
# next load the settings.LANGUAGE_CODE translations if it isn't english # next load the settings.LANGUAGE_CODE translations if it isn't english
if default_locale != 'en': if default_locale != 'en':
for path in paths: for path in paths:

View File

@ -33,6 +33,7 @@ def serve(request, path, document_root=None, show_indexes=False):
# Clean up given path to only allow serving files below document_root. # Clean up given path to only allow serving files below document_root.
path = posixpath.normpath(urllib.unquote(path)) path = posixpath.normpath(urllib.unquote(path))
path = path.lstrip('/')
newpath = '' newpath = ''
for part in path.split('/'): for part in path.split('/'):
if not part: if not part:

View File

@ -170,7 +170,7 @@ The ``User`` model has a custom manager that has the following helper functions:
If no password is provided, ``set_unusable_password()`` will be called. If no password is provided, ``set_unusable_password()`` will be called.
See _`Creating users` for example usage. See `Creating users`_ for example usage.
* ``make_random_password(length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789')`` * ``make_random_password(length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789')``
Returns a random password with the given length and given string of Returns a random password with the given length and given string of

View File

@ -263,6 +263,18 @@ See the `middleware documentation`_ for more on middleware.
.. _`middleware documentation`: ../middleware/ .. _`middleware documentation`: ../middleware/
**New in Django development version**
If a view sets its own cache expiry time (i.e. it has a ``max-age`` section in
its ``Cache-Control`` header) then the page will be cached until the expiry
time, rather than ``CACHE_MIDDLEWARE_SECONDS``. Using the decorators in
``django.views.decorators.cache`` you can easily set a view's expiry time
(using the ``cache_control`` decorator) or disable caching for a view (using
the ``never_cache`` decorator). See the `using other headers`__ section for
more on these decorators.
__ `Controlling cache: Using other headers`_
The per-view cache The per-view cache
================== ==================
@ -566,7 +578,7 @@ the value of the ``CACHE_MIDDLEWARE_SETTINGS`` setting. If you use a custom
precedence, and the header values will be merged correctly.) precedence, and the header values will be merged correctly.)
If you want to use headers to disable caching altogether, If you want to use headers to disable caching altogether,
``django.views.decorators.never_cache`` is a view decorator that adds ``django.views.decorators.cache.never_cache`` is a view decorator that adds
headers to ensure the response won't be cached by browsers or other caches. Example:: headers to ensure the response won't be cached by browsers or other caches. Example::
from django.views.decorators.cache import never_cache from django.views.decorators.cache import never_cache

View File

@ -184,9 +184,9 @@ is being executed as an unattended, automated script.
Use ``--verbosity`` to specify the amount of notification and debug information Use ``--verbosity`` to specify the amount of notification and debug information
that ``django-admin.py`` should print to the console. that ``django-admin.py`` should print to the console.
* ``0`` means no input. * ``0`` means no output.
* ``1`` means normal input (default). * ``1`` means normal output (default).
* ``2`` means verbose input. * ``2`` means verbose output.
Example usage:: Example usage::
@ -651,7 +651,7 @@ To run the test server on port 7000 with ``fixture1`` and ``fixture2``::
that it doesn't matter whether the options come before or after the fixture that it doesn't matter whether the options come before or after the fixture
arguments.) arguments.)
To run on 1.2.3.4:7000 with a `test` fixture:: To run on 1.2.3.4:7000 with a ``test`` fixture::
django-admin.py testserver --addrport 1.2.3.4:7000 test django-admin.py testserver --addrport 1.2.3.4:7000 test

View File

@ -187,10 +187,10 @@ latest bug fixes and improvements, follow these instructions:
"Where are my ``site-packages`` stored?" section above.) "Where are my ``site-packages`` stored?" section above.)
Alternatively, you can define your ``PYTHONPATH`` environment variable Alternatively, you can define your ``PYTHONPATH`` environment variable
so that it includes the ``django`` subdirectory of ``django-trunk``. so that it includes the ``django-trunk`` directory. This is perhaps the
This is perhaps the most convenient solution on Windows systems, which most convenient solution on Windows systems, which don't support symbolic
don't support symbolic links. (Environment variables can be defined on links. (Environment variables can be defined on Windows systems `from the
Windows systems `from the Control Panel`_.) Control Panel`_.)
.. admonition:: What about Apache and mod_python? .. admonition:: What about Apache and mod_python?
@ -204,11 +204,18 @@ latest bug fixes and improvements, follow these instructions:
.. _How to use Django with mod_python: ../modpython/ .. _How to use Django with mod_python: ../modpython/
4. Copy the file ``django-trunk/django/bin/django-admin.py`` to somewhere on 4. On Unix-like systems, create a symbolic link to the file
your system path, such as ``/usr/local/bin`` (Unix) or ``C:\Python24\Scripts`` ``django-trunk/django/bin/django-admin.py`` in a directory on your system
(Windows). This step simply lets you type ``django-admin.py`` from within path, such as ``/usr/local/bin``. For example::
any directory, rather than having to qualify the command with the full path
to the file. ln -s `pwd`/django-trunk/django/bin/django-admin.py /usr/local/bin
This simply lets you type ``django-admin.py`` from within any directory,
rather than having to qualify the command with the full path to the file.
On Windows systems, the same result can be achieved by copying the file
``django-trunk/django/bin/django-admin.py`` to somewhere on your system
path, for example ``C:\Python24\Scripts``.
You *don't* have to run ``python setup.py install``, because you've already You *don't* have to run ``python setup.py install``, because you've already
carried out the equivalent actions in steps 3 and 4. carried out the equivalent actions in steps 3 and 4.

View File

@ -50,7 +50,7 @@ The above ``Person`` model would create a database table like this::
Some technical notes: Some technical notes:
* The name of the table, ``myapp_person``, is automatically derived from * The name of the table, ``myapp_person``, is automatically derived from
some model metadata but can be overridden. See _`Table names` below. some model metadata but can be overridden. See `Table names`_ below.
* An ``id`` field is added automatically, but this behavior can be * An ``id`` field is added automatically, but this behavior can be
overriden. See `Automatic primary key fields`_ below. overriden. See `Automatic primary key fields`_ below.
* The ``CREATE TABLE`` SQL in this example is formatted using PostgreSQL * The ``CREATE TABLE`` SQL in this example is formatted using PostgreSQL
@ -1664,7 +1664,7 @@ Adding extra Manager methods
Adding extra ``Manager`` methods is the preferred way to add "table-level" Adding extra ``Manager`` methods is the preferred way to add "table-level"
functionality to your models. (For "row-level" functionality -- i.e., functions functionality to your models. (For "row-level" functionality -- i.e., functions
that act on a single instance of a model object -- use _`Model methods`, not that act on a single instance of a model object -- use `Model methods`_, not
custom ``Manager`` methods.) custom ``Manager`` methods.)
A custom ``Manager`` method can return anything you want. It doesn't have to A custom ``Manager`` method can return anything you want. It doesn't have to

View File

@ -753,6 +753,30 @@ For example::
</ul> </ul>
</form> </form>
Highlighting required fields in templates
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You may wish to show a visitor which fields are required. Here is the above
example modified to insert an asterix after the label of each required field::
<form method="post" action="">
<dl>
{% for field in form %}
<dt>{{ field.label_tag }}{{ field.label }}{% if field.field.required %}*{% endif %}</dt>
<dd>{{ field }}</dd>
{% if field.help_text %}<dd>{{ field.help_text }}</dd>{% endif %}
{% if field.errors %}<dd class="myerrors">{{ field.errors }}</dd>{% endif %}
{% endfor %}
</dl>
<input type="submit" />
</form>
The ``{% if field.field.required %}*{% endif %}`` fragment is the relevant
addition here. It adds the asterix only if the field is required. Note that we
check ``field.field.required`` and not ``field.required``. In the template,
``field`` is a ``newforms.forms.BoundField`` instance, which holds the actual
``Field`` instance in its ``field`` attribute.
Binding uploaded files to a form Binding uploaded files to a form
-------------------------------- --------------------------------
@ -1849,7 +1873,11 @@ In addition, each generated form field has attributes set as follows:
* If the model field has ``choices`` set, then the form field's ``widget`` * If the model field has ``choices`` set, then the form field's ``widget``
will be set to ``Select``, with choices coming from the model field's will be set to ``Select``, with choices coming from the model field's
``choices``. ``choices``. The choices will normally include the blank choice which is
selected by default. If the field is required, this forces the user to
make a selection. The blank choice will not be included if the model
field has ``blank=False`` and an explicit ``default`` value (the
``default`` value will be initially selected instead).
Finally, note that you can override the form field used for a given model Finally, note that you can override the form field used for a given model
field. See "Overriding the default field types" below. field. See "Overriding the default field types" below.
@ -2095,10 +2123,14 @@ instance instead of a model class::
# Instantiate the form. # Instantiate the form.
>>> f = AuthorForm() >>> f = AuthorForm()
When a form created by ``form_for_instance()`` is created, the initial When a form created by ``form_for_instance()`` is created, the initial data
data values for the form fields are drawn from the instance. However, values for the form fields are drawn from the instance. However, this data is
this data is not bound to the form. You will need to bind data to the not bound to the form. You will need to bind data to the form before the form
form before the form can be saved. can be saved.
Unlike ``form_for_model()``, a choice field in form created by
``form_for_instance()`` will not include the blank choice if the respective
model field has ``blank=False``. The initial choice is drawn from the instance.
When you call ``save()`` on a form created by ``form_for_instance()``, When you call ``save()`` on a form created by ``form_for_instance()``,
the database instance will be updated. As in ``form_for_model()``, ``save()`` the database instance will be updated. As in ``form_for_model()``, ``save()``

View File

@ -44,7 +44,7 @@ to this::
DATABASE_ENGINE = "mysql_old" DATABASE_ENGINE = "mysql_old"
However, we strongly encourage MySQL users to upgrade to a more recent However, we strongly encourage MySQL users to upgrade to a more recent
version of `MySQLdb` as soon as possible, The "mysql_old" backend is version of ``MySQLdb`` as soon as possible, The "mysql_old" backend is
provided only to ease this transition, and is considered deprecated; provided only to ease this transition, and is considered deprecated;
aside from any necessary security fixes, it will not be actively aside from any necessary security fixes, it will not be actively
maintained, and it will be removed in a future release of Django. maintained, and it will be removed in a future release of Django.

View File

@ -201,6 +201,8 @@ the feed.
An example makes this clear. Here's the code for these beat-specific feeds:: An example makes this clear. Here's the code for these beat-specific feeds::
from django.contrib.syndication import FeedDoesNotExist
class BeatFeed(Feed): class BeatFeed(Feed):
def get_object(self, bits): def get_object(self, bits):
# In case of "/rss/beats/0613/foo/bar/baz/", or other such clutter, # In case of "/rss/beats/0613/foo/bar/baz/", or other such clutter,
@ -213,6 +215,8 @@ An example makes this clear. Here's the code for these beat-specific feeds::
return "Chicagocrime.org: Crimes for beat %s" % obj.beat return "Chicagocrime.org: Crimes for beat %s" % obj.beat
def link(self, obj): def link(self, obj):
if not obj:
raise FeedDoesNotExist
return obj.get_absolute_url() return obj.get_absolute_url()
def description(self, obj): def description(self, obj):
@ -246,11 +250,18 @@ request to the URL ``/rss/beats/0613/``:
each of ``title``, ``link`` and ``description``, Django follows this each of ``title``, ``link`` and ``description``, Django follows this
algorithm: algorithm:
* First, it tries to call a method, passing the ``obj`` argument, where * First, it tries to call a method, passing the ``obj`` argument,
``obj`` is the object returned by ``get_object()``. where ``obj`` is the object returned by ``get_object()``.
* Failing that, it tries to call a method with no arguments. * Failing that, it tries to call a method with no arguments.
* Failing that, it uses the class attribute. * Failing that, it uses the class attribute.
Inside the ``link()`` method, we handle the possibility that ``obj``
might be ``None``, which can occur when the URL isn't fully specified. In
some cases, you might want to do something else in this case, which would
mean you'd need to check for ``obj`` existing in other methods as well
(the ``link()`` method is called very early in the feed generation
process, so is a good place to bail out early).
* Finally, note that ``items()`` in this example also takes the ``obj`` * Finally, note that ``items()`` in this example also takes the ``obj``
argument. The algorithm for ``items`` is the same as described in the argument. The algorithm for ``items`` is the same as described in the
previous step -- first, it tries ``items(obj)``, then ``items()``, then previous step -- first, it tries ``items(obj)``, then ``items()``, then

View File

@ -1183,7 +1183,7 @@ on the object being edited -- so they're a perfect case for using a small
template that is filled with details from the current object. (In the admin's template that is filled with details from the current object. (In the admin's
case, this is the ``submit_row`` tag.) case, this is the ``submit_row`` tag.)
These sorts of tags are called `inclusion tags`. These sorts of tags are called "inclusion tags".
Writing inclusion tags is probably best demonstrated by example. Let's write a Writing inclusion tags is probably best demonstrated by example. Let's write a
tag that outputs a list of choices for a given ``Poll`` object, such as was tag that outputs a list of choices for a given ``Poll`` object, such as was

View File

@ -47,7 +47,7 @@ will create a ``mysite`` directory in your current directory.
denied" when you try to run ``django-admin.py startproject``. This denied" when you try to run ``django-admin.py startproject``. This
is because, on Unix-based systems like OS X, a file must be marked is because, on Unix-based systems like OS X, a file must be marked
as "executable" before it can be run as a program. To do this, open as "executable" before it can be run as a program. To do this, open
Terminal.app and navigate (using the `cd` command) to the directory Terminal.app and navigate (using the ``cd`` command) to the directory
where ``django-admin.py`` is installed, then run the command where ``django-admin.py`` is installed, then run the command
``chmod +x django-admin.py``. ``chmod +x django-admin.py``.

View File

@ -237,7 +237,7 @@ include
------- -------
A function that takes a full Python import path to another URLconf that should A function that takes a full Python import path to another URLconf that should
be "included" in this place. See _`Including other URLconfs` below. be "included" in this place. See `Including other URLconfs`_ below.
Notes on capturing text in URLs Notes on capturing text in URLs
=============================== ===============================

View File

@ -103,4 +103,14 @@ TypeError: Invalid lookup type: 'lt'
>>> obj = list(serializers.deserialize("json", stream))[0] >>> obj = list(serializers.deserialize("json", stream))[0]
>>> obj.object == m >>> obj.object == m
True True
# Test retrieving custom field data
>>> m.delete()
>>> m1 = MyModel(name="1", data=Small(1, 2))
>>> m1.save()
>>> m2 = MyModel(name="2", data=Small(2, 3))
>>> m2.save()
>>> for m in MyModel.objects.all(): print unicode(m.data)
12
23
"""} """}

View File

@ -30,6 +30,23 @@ ARTICLE_STATUS = (
(3, 'Live'), (3, 'Live'),
) )
STEERING_TYPE = (
('left', 'Left steering wheel'),
('right', 'Right steering wheel'),
)
FUEL_TYPE = (
('gas', 'Gasoline'),
('diesel', 'Diesel'),
('other', 'Other'),
)
TRANSMISSION_TYPE = (
('at', 'Automatic'),
('mt', 'Manual'),
('cvt', 'CVT'),
)
class Category(models.Model): class Category(models.Model):
name = models.CharField(max_length=20) name = models.CharField(max_length=20)
slug = models.SlugField(max_length=20) slug = models.SlugField(max_length=20)
@ -70,6 +87,12 @@ class PhoneNumber(models.Model):
def __unicode__(self): def __unicode__(self):
return self.phone return self.phone
class Car(models.Model):
name = models.CharField(max_length=50)
steering = models.CharField(max_length=5, choices=STEERING_TYPE, default='left')
fuel = models.CharField(max_length=10, choices=FUEL_TYPE)
transmission = models.CharField(max_length=3, choices=TRANSMISSION_TYPE, blank=True, help_text='Leave empty if not applicable.')
__test__ = {'API_TESTS': """ __test__ = {'API_TESTS': """
>>> from django.newforms import form_for_model, form_for_instance, save_instance, BaseForm, Form, CharField >>> from django.newforms import form_for_model, form_for_instance, save_instance, BaseForm, Form, CharField
>>> import datetime >>> import datetime
@ -592,4 +615,54 @@ ValidationError: [u'Select a valid choice. 4 is not one of the available choices
True True
>>> f.cleaned_data >>> f.cleaned_data
{'phone': u'312-555-1212', 'description': u'Assistance'} {'phone': u'312-555-1212', 'description': u'Assistance'}
# form_for_* blank choices ####################################################
Show the form for a new Car. Note that steering field doesn't include the blank choice,
because the field is obligatory and has an explicit default.
>>> CarForm = form_for_model(Car)
>>> f = CarForm(auto_id=False)
>>> print f
<tr><th>Name:</th><td><input type="text" name="name" maxlength="50" /></td></tr>
<tr><th>Steering:</th><td><select name="steering">
<option value="left" selected="selected">Left steering wheel</option>
<option value="right">Right steering wheel</option>
</select></td></tr>
<tr><th>Fuel:</th><td><select name="fuel">
<option value="" selected="selected">---------</option>
<option value="gas">Gasoline</option>
<option value="diesel">Diesel</option>
<option value="other">Other</option>
</select></td></tr>
<tr><th>Transmission:</th><td><select name="transmission">
<option value="" selected="selected">---------</option>
<option value="at">Automatic</option>
<option value="mt">Manual</option>
<option value="cvt">CVT</option>
</select><br />Leave empty if not applicable.</td></tr>
Create a Car, and display the form for modifying it. Note that now the fuel
selector doesn't include the blank choice as well, since the field is
obligatory and can not be changed to be blank.
>>> honda = Car(name='Honda Accord Wagon', steering='right', fuel='gas', transmission='at')
>>> honda.save()
>>> HondaForm = form_for_instance(honda)
>>> f = HondaForm(auto_id=False)
>>> print f
<tr><th>Name:</th><td><input type="text" name="name" value="Honda Accord Wagon" maxlength="50" /></td></tr>
<tr><th>Steering:</th><td><select name="steering">
<option value="left">Left steering wheel</option>
<option value="right" selected="selected">Right steering wheel</option>
</select></td></tr>
<tr><th>Fuel:</th><td><select name="fuel">
<option value="gas" selected="selected">Gasoline</option>
<option value="diesel">Diesel</option>
<option value="other">Other</option>
</select></td></tr>
<tr><th>Transmission:</th><td><select name="transmission">
<option value="">---------</option>
<option value="at" selected="selected">Automatic</option>
<option value="mt">Manual</option>
<option value="cvt">CVT</option>
</select><br />Leave empty if not applicable.</td></tr>
"""} """}

View File

@ -66,6 +66,9 @@ u'1979 189 CET'
>>> format(my_birthday, r'jS o\f F') >>> format(my_birthday, r'jS o\f F')
u'8th of July' u'8th of July'
>>> format(the_future, r'Y')
u'2100'
""" """
from django.utils import dateformat, translation from django.utils import dateformat, translation
@ -84,3 +87,4 @@ except AttributeError:
my_birthday = datetime.datetime(1979, 7, 8, 22, 00) my_birthday = datetime.datetime(1979, 7, 8, 22, 00)
summertime = datetime.datetime(2005, 10, 30, 1, 00) summertime = datetime.datetime(2005, 10, 30, 1, 00)
wintertime = datetime.datetime(2005, 10, 30, 4, 00) wintertime = datetime.datetime(2005, 10, 30, 4, 00)
the_future = datetime.datetime(2100, 10, 25, 0, 00)

View File

@ -37,6 +37,8 @@ u''
u'13.1031' u'13.1031'
>>> floatformat(u'foo', u'bar') >>> floatformat(u'foo', u'bar')
u'' u''
>>> floatformat(None)
u''
>>> addslashes(u'"double quotes" and \'single quotes\'') >>> addslashes(u'"double quotes" and \'single quotes\'')
u'\\"double quotes\\" and \\\'single quotes\\\'' u'\\"double quotes\\" and \\\'single quotes\\\''

View File

@ -323,6 +323,10 @@ Decimal("3.14")
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValidationError: [u'Enter a number.'] ValidationError: [u'Enter a number.']
>>> f.clean(u'łąść')
Traceback (most recent call last):
...
ValidationError: [u'Enter a number.']
>>> f.clean('1.0 ') >>> f.clean('1.0 ')
Decimal("1.0") Decimal("1.0")
>>> f.clean(' 1.0') >>> f.clean(' 1.0')
@ -914,6 +918,11 @@ False
>>> f.clean('Django rocks') >>> f.clean('Django rocks')
True True
>>> f.clean('True')
True
>>> f.clean('False')
False
>>> f = BooleanField(required=False) >>> f = BooleanField(required=False)
>>> f.clean('') >>> f.clean('')
False False
@ -930,6 +939,11 @@ False
>>> f.clean('Django rocks') >>> f.clean('Django rocks')
True True
A form's BooleanField with a hidden widget will output the string 'False', so
that should clean to the boolean value False:
>>> f.clean('False')
False
# ChoiceField ################################################################# # ChoiceField #################################################################
>>> f = ChoiceField(choices=[('1', '1'), ('2', '2')]) >>> f = ChoiceField(choices=[('1', '1'), ('2', '2')])

View File

@ -147,6 +147,10 @@ u'BC'
u'NS' u'NS'
>>> f.clean(' manitoba ') >>> f.clean(' manitoba ')
u'MB' u'MB'
>>> f.clean(' new brunswick ')
u'NB'
>>> f.clean('NB')
u'NB'
>>> f.clean('T2S 2H7') >>> f.clean('T2S 2H7')
Traceback (most recent call last): Traceback (most recent call last):
... ...

View File

@ -129,6 +129,13 @@ u'<input type="hidden" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u
>>> w.render('email', '', attrs={'class': 'special'}) >>> w.render('email', '', attrs={'class': 'special'})
u'<input type="hidden" class="special" name="email" />' u'<input type="hidden" class="special" name="email" />'
Boolean values are rendered to their string forms ("True" and "False").
>>> w = HiddenInput()
>>> w.render('get_spam', False)
u'<input type="hidden" name="get_spam" value="False" />'
>>> w.render('get_spam', True)
u'<input type="hidden" name="get_spam" value="True" />'
# MultipleHiddenInput Widget ################################################## # MultipleHiddenInput Widget ##################################################
>>> w = MultipleHiddenInput() >>> w = MultipleHiddenInput()

View File

@ -12,6 +12,15 @@ from datetime import datetime, timedelta
from django.utils.tzinfo import LocalTimezone from django.utils.tzinfo import LocalTimezone
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
# These two classes are used to test auto-escaping of __unicode__ output.
class UnsafeClass:
def __unicode__(self):
return u'you & me'
class SafeClass:
def __unicode__(self):
return mark_safe(u'you &gt; me')
# RESULT SYNTAX -- # RESULT SYNTAX --
# 'template_name': ('template contents', 'context dict', # 'template_name': ('template contents', 'context dict',
# 'expected string output' or Exception class) # 'expected string output' or Exception class)
@ -227,4 +236,11 @@ def get_filter_tests():
'chaining12': ('{% autoescape off %}{{ a|cut:"b"|safe }}{% endautoescape %}', {"a": "a < b"}, "a < "), 'chaining12': ('{% autoescape off %}{{ a|cut:"b"|safe }}{% endautoescape %}', {"a": "a < b"}, "a < "),
'chaining13': ('{{ a|safe|force_escape }}', {"a": "a < b"}, "a &lt; b"), 'chaining13': ('{{ a|safe|force_escape }}', {"a": "a < b"}, "a &lt; b"),
'chaining14': ('{% autoescape off %}{{ a|safe|force_escape }}{% endautoescape %}', {"a": "a < b"}, "a &lt; b"), 'chaining14': ('{% autoescape off %}{{ a|safe|force_escape }}{% endautoescape %}', {"a": "a < b"}, "a &lt; b"),
# Filters decorated with stringfilter still respect is_safe.
'autoescape-stringfilter01': (r'{{ unsafe|capfirst }}', {'unsafe': UnsafeClass()}, 'You &amp; me'),
'autoescape-stringfilter02': (r'{% autoescape off %}{{ unsafe|capfirst }}{% endautoescape %}', {'unsafe': UnsafeClass()}, 'You & me'),
'autoescape-stringfilter03': (r'{{ safe|capfirst }}', {'safe': SafeClass()}, 'You &gt; me'),
'autoescape-stringfilter04': (r'{% autoescape off %}{{ safe|capfirst }}{% endautoescape %}', {'safe': SafeClass()}, 'You &gt; me'),
} }

View File

@ -899,7 +899,12 @@ class Templates(unittest.TestCase):
# Literal string arguments to filters, if used in the result, are # Literal string arguments to filters, if used in the result, are
# safe. # safe.
'basic-syntax08': (r'{% autoescape on %}{{ var|default_if_none:" endquote\" hah" }}{% endautoescape %}', {"var": None}, ' endquote" hah'), 'autoescape-tag08': (r'{% autoescape on %}{{ var|default_if_none:" endquote\" hah" }}{% endautoescape %}', {"var": None}, ' endquote" hah'),
# Objects which return safe strings as their __unicode__ method
# won't get double-escaped.
'autoescape-tag09': (r'{{ unsafe }}', {'unsafe': filters.UnsafeClass()}, 'you &amp; me'),
'autoescape-tag10': (r'{{ safe }}', {'safe': filters.SafeClass()}, 'you &gt; me'),
# The "safe" and "escape" filters cannot work due to internal # The "safe" and "escape" filters cannot work due to internal
# implementation details (fortunately, the (no)autoescape block # implementation details (fortunately, the (no)autoescape block

View File

@ -0,0 +1,51 @@
"""
>>> from django.utils.datastructures import SortedDict
>>> d = SortedDict()
>>> d[7] = 'seven'
>>> d[1] = 'one'
>>> d[9] = 'nine'
>>> d.keys()
[7, 1, 9]
>>> d.values()
['seven', 'one', 'nine']
>>> d.items()
[(7, 'seven'), (1, 'one'), (9, 'nine')]
# Overwriting an item keeps it's place.
>>> d[1] = 'ONE'
>>> d.values()
['seven', 'ONE', 'nine']
# New items go to the end.
>>> d[0] = 'nil'
>>> d.keys()
[7, 1, 9, 0]
# Deleting an item, then inserting the same key again will place it at the end.
>>> del d[7]
>>> d.keys()
[1, 9, 0]
>>> d[7] = 'lucky number 7'
>>> d.keys()
[1, 9, 0, 7]
# Changing the keys won't do anything, it's only a copy of the keys dict.
>>> k = d.keys()
>>> k.remove(9)
>>> d.keys()
[1, 9, 0, 7]
# Initialising a SortedDict with two keys will just take the first one. A real
# dict will actually take the second value so we will too, but we'll keep the
# ordering from the first key found.
>>> tuples = ((2, 'two'), (1, 'one'), (2, 'second-two'))
>>> d = SortedDict(tuples)
>>> d.keys()
[2, 1]
>>> real_dict = dict(tuples)
>>> real_dict.values()
['one', 'second-two']
>>> d.values()
['second-two', 'one']
"""

View File

@ -6,7 +6,14 @@ from unittest import TestCase
from django.utils import html, checksums from django.utils import html, checksums
from timesince import timesince_tests import timesince
import datastructures
# Extra tests
__test__ = {
'timesince': timesince,
'datastructures': datastructures,
}
class TestUtilsHtml(TestCase): class TestUtilsHtml(TestCase):
@ -142,10 +149,6 @@ class TestUtilsChecksums(TestCase):
for value, output in items: for value, output in items:
self.check_output(f, value, output) self.check_output(f, value, output)
__test__ = {
'timesince_tests': timesince_tests,
}
if __name__ == "__main__": if __name__ == "__main__":
import doctest import doctest
doctest.testmod() doctest.testmod()

View File

@ -1,4 +1,4 @@
timesince_tests = """ """
>>> from datetime import datetime, timedelta >>> from datetime import datetime, timedelta
>>> from django.utils.timesince import timesince >>> from django.utils.timesince import timesince

View File

@ -12,4 +12,12 @@ class StaticTests(TestCase):
for filename in media_files: for filename in media_files:
response = self.client.get('/views/site_media/%s' % filename) response = self.client.get('/views/site_media/%s' % filename)
file = open(path.join(media_dir, filename)) file = open(path.join(media_dir, filename))
self.assertEquals(file.read(), response.content) self.assertEquals(file.read(), response.content)
def test_copes_with_empty_path_component(self):
file_name = 'file.txt'
response = self.client.get('/views/site_media//%s' % file_name)
file = open(path.join(media_dir, file_name))
self.assertEquals(file.read(), response.content)