Merge remote-tracking branch 'core/master' into schema-alteration

Conflicts:
	docs/ref/django-admin.txt
This commit is contained in:
Andrew Godwin 2013-08-19 18:30:48 +01:00
commit b6a957f0ba
167 changed files with 1861 additions and 1064 deletions

View File

@ -317,6 +317,7 @@ answer newbie questions, and generally made Django that much better:
Michael Josephson <http://www.sdjournal.com/> Michael Josephson <http://www.sdjournal.com/>
jpellerin@gmail.com jpellerin@gmail.com
junzhang.jn@gmail.com junzhang.jn@gmail.com
Krzysztof Jurewicz <krzysztof.jurewicz@gmail.com>
Xia Kai <http://blog.xiaket.org/> Xia Kai <http://blog.xiaket.org/>
Antti Kaihola <http://djangopeople.net/akaihola/> Antti Kaihola <http://djangopeople.net/akaihola/>
Peter van Kampen Peter van Kampen
@ -418,6 +419,7 @@ answer newbie questions, and generally made Django that much better:
Christian Metts Christian Metts
michal@plovarna.cz michal@plovarna.cz
Justin Michalicek <jmichalicek@gmail.com> Justin Michalicek <jmichalicek@gmail.com>
Bojan Mihelac <bmihelac@mihelac.org>
Slawek Mikula <slawek dot mikula at gmail dot com> Slawek Mikula <slawek dot mikula at gmail dot com>
Katie Miller <katie@sub50.com> Katie Miller <katie@sub50.com>
Shawn Milochik <shawn@milochik.com> Shawn Milochik <shawn@milochik.com>
@ -440,6 +442,7 @@ answer newbie questions, and generally made Django that much better:
Gopal Narayanan <gopastro@gmail.com> Gopal Narayanan <gopastro@gmail.com>
Fraser Nevett <mail@nevett.org> Fraser Nevett <mail@nevett.org>
Sam Newman <http://www.magpiebrain.com/> Sam Newman <http://www.magpiebrain.com/>
Alasdair Nicol <http://al.sdair.co.uk/>
Ryan Niemeyer <https://profiles.google.com/ryan.niemeyer/about> Ryan Niemeyer <https://profiles.google.com/ryan.niemeyer/about>
Filip Noetzel <http://filip.noetzel.co.uk/> Filip Noetzel <http://filip.noetzel.co.uk/>
Afonso Fernández Nogueira <fonzzo.django@gmail.com> Afonso Fernández Nogueira <fonzzo.django@gmail.com>
@ -537,6 +540,7 @@ answer newbie questions, and generally made Django that much better:
Brenton Simpson <http://theillustratedlife.com> Brenton Simpson <http://theillustratedlife.com>
Jozko Skrablin <jozko.skrablin@gmail.com> Jozko Skrablin <jozko.skrablin@gmail.com>
Ben Slavin <benjamin.slavin@gmail.com> Ben Slavin <benjamin.slavin@gmail.com>
Jonathan Slenders
sloonz <simon.lipp@insa-lyon.fr> sloonz <simon.lipp@insa-lyon.fr>
Paul Smith <blinkylights23@gmail.com> Paul Smith <blinkylights23@gmail.com>
Steven L. Smith (fvox13) <steven@stevenlsmith.com> Steven L. Smith (fvox13) <steven@stevenlsmith.com>
@ -573,6 +577,7 @@ answer newbie questions, and generally made Django that much better:
Aaron Swartz <http://www.aaronsw.com/> Aaron Swartz <http://www.aaronsw.com/>
Ville Säävuori <http://www.unessa.net/> Ville Säävuori <http://www.unessa.net/>
Mart Sõmermaa <http://mrts.pri.ee/> Mart Sõmermaa <http://mrts.pri.ee/>
Susan Tan <susan.tan.fleckerl@gmail.com>
Christian Tanzer <tanzer@swing.co.at> Christian Tanzer <tanzer@swing.co.at>
Tyler Tarabula <tyler.tarabula@gmail.com> Tyler Tarabula <tyler.tarabula@gmail.com>
Tyson Tate <tyson@fallingbullets.com> Tyson Tate <tyson@fallingbullets.com>

View File

@ -313,6 +313,11 @@ FILE_UPLOAD_TEMP_DIR = None
# you'd pass directly to os.chmod; see http://docs.python.org/lib/os-file-dir.html. # you'd pass directly to os.chmod; see http://docs.python.org/lib/os-file-dir.html.
FILE_UPLOAD_PERMISSIONS = None FILE_UPLOAD_PERMISSIONS = None
# The numeric mode to assign to newly-created directories, when uploading files.
# The value should be a mode as you'd pass to os.chmod;
# see http://docs.python.org/lib/os-file-dir.html.
FILE_UPLOAD_DIRECTORY_PERMISSIONS = None
# Python module path where user will place custom format definition. # Python module path where user will place custom format definition.
# The directory where this setting is pointing should contain subdirectories # The directory where this setting is pointing should contain subdirectories
# named as the locales, containing a formats.py file # named as the locales, containing a formats.py file

View File

@ -23,8 +23,8 @@ ul.actionlist li {
list-style-type: none; list-style-type: none;
} }
ul.actionlist li.changelink { ul.actionlist li {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
-o-text-overflow: ellipsis; -o-text-overflow: ellipsis;
} }

View File

@ -16,7 +16,7 @@ from django.utils import timezone
from django.utils.encoding import force_str, force_text, smart_text from django.utils.encoding import force_str, force_text, smart_text
from django.utils import six from django.utils import six
from django.utils.translation import ungettext from django.utils.translation import ungettext
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse, NoReverseMatch
def lookup_needs_distinct(opts, lookup_path): def lookup_needs_distinct(opts, lookup_path):
""" """
@ -113,12 +113,20 @@ def get_deleted_objects(objs, opts, user, admin_site, using):
has_admin = obj.__class__ in admin_site._registry has_admin = obj.__class__ in admin_site._registry
opts = obj._meta opts = obj._meta
no_edit_link = '%s: %s' % (capfirst(opts.verbose_name),
force_text(obj))
if has_admin: if has_admin:
admin_url = reverse('%s:%s_%s_change' try:
% (admin_site.name, admin_url = reverse('%s:%s_%s_change'
opts.app_label, % (admin_site.name,
opts.model_name), opts.app_label,
None, (quote(obj._get_pk_val()),)) opts.model_name),
None, (quote(obj._get_pk_val()),))
except NoReverseMatch:
# Change url doesn't exist -- don't display link to edit
return no_edit_link
p = '%s.%s' % (opts.app_label, p = '%s.%s' % (opts.app_label,
get_permission_codename('delete', opts)) get_permission_codename('delete', opts))
if not user.has_perm(p): if not user.has_perm(p):
@ -131,8 +139,7 @@ def get_deleted_objects(objs, opts, user, admin_site, using):
else: else:
# Don't display link to edit, because it either has no # Don't display link to edit, because it either has no
# admin or is edited inline. # admin or is edited inline.
return '%s: %s' % (capfirst(opts.verbose_name), return no_edit_link
force_text(obj))
to_delete = collector.nested(format_callback) to_delete = collector.nested(format_callback)
@ -155,9 +162,6 @@ class NestedObjects(Collector):
if source_attr: if source_attr:
self.add_edge(getattr(obj, source_attr), obj) self.add_edge(getattr(obj, source_attr), obj)
else: else:
if obj._meta.proxy:
# Take concrete model's instance to avoid mismatch in edges
obj = obj._meta.concrete_model(pk=obj.pk)
self.add_edge(None, obj) self.add_edge(None, obj)
try: try:
return super(NestedObjects, self).collect(objs, source_attr=source_attr, **kwargs) return super(NestedObjects, self).collect(objs, source_attr=source_attr, **kwargs)

View File

@ -305,9 +305,9 @@ class AdminURLFieldWidget(forms.URLInput):
html = super(AdminURLFieldWidget, self).render(name, value, attrs) html = super(AdminURLFieldWidget, self).render(name, value, attrs)
if value: if value:
value = force_text(self._format_value(value)) value = force_text(self._format_value(value))
final_attrs = {'href': mark_safe(smart_urlquote(value))} final_attrs = {'href': smart_urlquote(value)}
html = format_html( html = format_html(
'<p class="url">{0} <a {1}>{2}</a><br />{3} {4}</p>', '<p class="url">{0} <a{1}>{2}</a><br />{3} {4}</p>',
_('Currently:'), flatatt(final_attrs), value, _('Currently:'), flatatt(final_attrs), value,
_('Change:'), html _('Change:'), html
) )

View File

@ -64,8 +64,12 @@ def permission_required(perm, login_url=None, raise_exception=False):
is raised. is raised.
""" """
def check_perms(user): def check_perms(user):
if not isinstance(perm, (list, tuple)):
perms = (perm, )
else:
perms = perm
# First check if the user has the permission (even anon users) # First check if the user has the permission (even anon users)
if user.has_perm(perm): if user.has_perms(perms):
return True return True
# In case the 403 handler should be called raise the exception # In case the 403 handler should be called raise the exception
if raise_exception: if raise_exception:

View File

@ -400,11 +400,11 @@ class AbstractUser(AbstractBaseUser, PermissionsMixin):
"Returns the short name for the user." "Returns the short name for the user."
return self.first_name return self.first_name
def email_user(self, subject, message, from_email=None): def email_user(self, subject, message, from_email=None, **kwargs):
""" """
Sends an email to this User. Sends an email to this User.
""" """
send_mail(subject, message, from_email, [self.email]) send_mail(subject, message, from_email, [self.email], **kwargs)
class User(AbstractUser): class User(AbstractUser):

View File

@ -1,7 +1,12 @@
from django.conf import settings from django.conf import settings
from django.contrib.auth.decorators import login_required from django.contrib.auth import models
from django.contrib.auth.decorators import login_required, permission_required
from django.contrib.auth.tests.test_views import AuthViewsTestCase from django.contrib.auth.tests.test_views import AuthViewsTestCase
from django.contrib.auth.tests.utils import skipIfCustomUser from django.contrib.auth.tests.utils import skipIfCustomUser
from django.core.exceptions import PermissionDenied
from django.http import HttpResponse
from django.test import TestCase
from django.test.client import RequestFactory
@skipIfCustomUser @skipIfCustomUser
@ -49,3 +54,54 @@ class LoginRequiredTestCase(AuthViewsTestCase):
""" """
self.testLoginRequired(view_url='/login_required_login_url/', self.testLoginRequired(view_url='/login_required_login_url/',
login_url='/somewhere/') login_url='/somewhere/')
class PermissionsRequiredDecoratorTest(TestCase):
"""
Tests for the permission_required decorator
"""
def setUp(self):
self.user = models.User.objects.create(username='joe', password='qwerty')
self.factory = RequestFactory()
# Add permissions auth.add_customuser and auth.change_customuser
perms = models.Permission.objects.filter(codename__in=('add_customuser', 'change_customuser'))
self.user.user_permissions.add(*perms)
def test_many_permissions_pass(self):
@permission_required(['auth.add_customuser', 'auth.change_customuser'])
def a_view(request):
return HttpResponse()
request = self.factory.get('/rand')
request.user = self.user
resp = a_view(request)
self.assertEqual(resp.status_code, 200)
def test_single_permission_pass(self):
@permission_required('auth.add_customuser')
def a_view(request):
return HttpResponse()
request = self.factory.get('/rand')
request.user = self.user
resp = a_view(request)
self.assertEqual(resp.status_code, 200)
def test_permissioned_denied_redirect(self):
@permission_required(['auth.add_customuser', 'auth.change_customuser', 'non-existant-permission'])
def a_view(request):
return HttpResponse()
request = self.factory.get('/rand')
request.user = self.user
resp = a_view(request)
self.assertEqual(resp.status_code, 302)
def test_permissioned_denied_exception_raised(self):
@permission_required(['auth.add_customuser', 'auth.change_customuser', 'non-existant-permission'], raise_exception=True)
def a_view(request):
return HttpResponse()
request = self.factory.get('/rand')
request.user = self.user
self.assertRaises(PermissionDenied, a_view, request)

View File

@ -121,17 +121,16 @@ class AuthenticationFormTest(TestCase):
[force_text(form.error_messages['inactive'])]) [force_text(form.error_messages['inactive'])])
def test_inactive_user_i18n(self): def test_inactive_user_i18n(self):
with self.settings(USE_I18N=True): with self.settings(USE_I18N=True), translation.override('pt-br', deactivate=True):
with translation.override('pt-br', deactivate=True): # The user is inactive.
# The user is inactive. data = {
data = { 'username': 'inactive',
'username': 'inactive', 'password': 'password',
'password': 'password', }
} form = AuthenticationForm(None, data)
form = AuthenticationForm(None, data) self.assertFalse(form.is_valid())
self.assertFalse(form.is_valid()) self.assertEqual(form.non_field_errors(),
self.assertEqual(form.non_field_errors(), [force_text(form.error_messages['inactive'])])
[force_text(form.error_messages['inactive'])])
def test_custom_login_allowed_policy(self): def test_custom_login_allowed_policy(self):
# The user is inactive, but our custom form policy allows him to log in. # The user is inactive, but our custom form policy allows him to log in.

View File

@ -239,21 +239,22 @@ class PermissionTestCase(TestCase):
create_permissions(models, [], verbosity=0) create_permissions(models, [], verbosity=0)
def test_default_permissions(self): def test_default_permissions(self):
permission_content_type = ContentType.objects.get_by_natural_key('auth', 'permission')
models.Permission._meta.permissions = [ models.Permission._meta.permissions = [
('my_custom_permission', 'Some permission'), ('my_custom_permission', 'Some permission'),
] ]
create_permissions(models, [], verbosity=0) create_permissions(models, [], verbosity=0)
# add/change/delete permission by default + custom permission # add/change/delete permission by default + custom permission
self.assertEqual(models.Permission.objects.filter(content_type= self.assertEqual(models.Permission.objects.filter(
ContentType.objects.get_by_natural_key('auth', 'permission') content_type=permission_content_type,
).count(), 4) ).count(), 4)
models.Permission.objects.all().delete() models.Permission.objects.filter(content_type=permission_content_type).delete()
models.Permission._meta.default_permissions = [] models.Permission._meta.default_permissions = []
create_permissions(models, [], verbosity=0) create_permissions(models, [], verbosity=0)
# custom permission only since default permissions is empty # custom permission only since default permissions is empty
self.assertEqual(models.Permission.objects.filter(content_type= self.assertEqual(models.Permission.objects.filter(
ContentType.objects.get_by_natural_key('auth', 'permission') content_type=permission_content_type,
).count(), 1) ).count(), 1)

View File

@ -1,6 +1,7 @@
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group, User, UserManager from django.contrib.auth.models import AbstractUser, Group, User, UserManager
from django.contrib.auth.tests.utils import skipIfCustomUser from django.contrib.auth.tests.utils import skipIfCustomUser
from django.core import mail
from django.db.models.signals import post_save from django.db.models.signals import post_save
from django.test import TestCase from django.test import TestCase
from django.test.utils import override_settings from django.test.utils import override_settings
@ -73,6 +74,29 @@ class UserManagerTestCase(TestCase):
User.objects.create_user, username='') User.objects.create_user, username='')
class AbstractUserTestCase(TestCase):
def test_email_user(self):
# valid send_mail parameters
kwargs = {
"fail_silently": False,
"auth_user": None,
"auth_password": None,
"connection": None,
"html_message": None,
}
abstract_user = AbstractUser(email='foo@bar.com')
abstract_user.email_user(subject="Subject here",
message="This is a message", from_email="from@domain.com", **kwargs)
# Test that one message has been sent.
self.assertEqual(len(mail.outbox), 1)
# Verify that test email contains the correct attributes:
message = mail.outbox[0]
self.assertEqual(message.subject, "Subject here")
self.assertEqual(message.body, "This is a message")
self.assertEqual(message.from_email, "from@domain.com")
self.assertEqual(message.to, [abstract_user.email])
class IsActiveTestCase(TestCase): class IsActiveTestCase(TestCase):
""" """
Tests the behavior of the guaranteed is_active attribute Tests the behavior of the guaranteed is_active attribute

View File

@ -446,7 +446,8 @@ class LoginTest(AuthViewsTestCase):
for bad_url in ('http://example.com', for bad_url in ('http://example.com',
'https://example.com', 'https://example.com',
'ftp://exampel.com', 'ftp://exampel.com',
'//example.com'): '//example.com',
'javascript:alert("XSS")'):
nasty_url = '%(url)s?%(next)s=%(bad_url)s' % { nasty_url = '%(url)s?%(next)s=%(bad_url)s' % {
'url': login_url, 'url': login_url,
@ -467,6 +468,7 @@ class LoginTest(AuthViewsTestCase):
'/view?param=ftp://exampel.com', '/view?param=ftp://exampel.com',
'view/?param=//example.com', 'view/?param=//example.com',
'https:///', 'https:///',
'HTTPS:///',
'//testserver/', '//testserver/',
'/url%20with%20spaces/'): # see ticket #12534 '/url%20with%20spaces/'): # see ticket #12534
safe_url = '%(url)s?%(next)s=%(good_url)s' % { safe_url = '%(url)s?%(next)s=%(good_url)s' % {
@ -661,7 +663,8 @@ class LogoutTest(AuthViewsTestCase):
for bad_url in ('http://example.com', for bad_url in ('http://example.com',
'https://example.com', 'https://example.com',
'ftp://exampel.com', 'ftp://exampel.com',
'//example.com'): '//example.com',
'javascript:alert("XSS")'):
nasty_url = '%(url)s?%(next)s=%(bad_url)s' % { nasty_url = '%(url)s?%(next)s=%(bad_url)s' % {
'url': logout_url, 'url': logout_url,
'next': REDIRECT_FIELD_NAME, 'next': REDIRECT_FIELD_NAME,
@ -680,6 +683,7 @@ class LogoutTest(AuthViewsTestCase):
'/view?param=ftp://exampel.com', '/view?param=ftp://exampel.com',
'view/?param=//example.com', 'view/?param=//example.com',
'https:///', 'https:///',
'HTTPS:///',
'//testserver/', '//testserver/',
'/url%20with%20spaces/'): # see ticket #12534 '/url%20with%20spaces/'): # see ticket #12534
safe_url = '%(url)s?%(next)s=%(good_url)s' % { safe_url = '%(url)s?%(next)s=%(good_url)s' % {

View File

@ -9,6 +9,14 @@ class PostGISIntrospection(DatabaseIntrospection):
# introspection is actually performed. # introspection is actually performed.
postgis_types_reverse = {} postgis_types_reverse = {}
ignored_tables = DatabaseIntrospection.ignored_tables + [
'geography_columns',
'geometry_columns',
'raster_columns',
'spatial_ref_sys',
'raster_overviews',
]
def get_postgis_types(self): def get_postgis_types(self):
""" """
Returns a dictionary with keys that are the PostgreSQL object Returns a dictionary with keys that are the PostgreSQL object

View File

@ -76,7 +76,7 @@ class GeoQuery(sql.Query):
return super(GeoQuery, self).convert_values(value, field, connection) return super(GeoQuery, self).convert_values(value, field, connection)
return value return value
def get_aggregation(self, using): def get_aggregation(self, using, force_subq=False):
# Remove any aggregates marked for reduction from the subquery # Remove any aggregates marked for reduction from the subquery
# and move them to the outer AggregateQuery. # and move them to the outer AggregateQuery.
connection = connections[using] connection = connections[using]
@ -84,7 +84,7 @@ class GeoQuery(sql.Query):
if isinstance(aggregate, gis_aggregates.GeoAggregate): if isinstance(aggregate, gis_aggregates.GeoAggregate):
if not getattr(aggregate, 'is_extent', False) or connection.ops.oracle: if not getattr(aggregate, 'is_extent', False) or connection.ops.oracle:
self.extra_select_fields[alias] = GeomField() self.extra_select_fields[alias] = GeomField()
return super(GeoQuery, self).get_aggregation(using) return super(GeoQuery, self).get_aggregation(using, force_subq)
def resolve_aggregate(self, value, aggregate, connection): def resolve_aggregate(self, value, aggregate, connection):
""" """

View File

@ -40,7 +40,7 @@ class Track(models.Model):
def __str__(self): return self.name def __str__(self): return self.name
class Truth(models.Model): class Truth(models.Model):
val = models.BooleanField() val = models.BooleanField(default=False)
objects = models.GeoManager() objects = models.GeoManager()
if not spatialite: if not spatialite:

View File

@ -77,15 +77,14 @@ class HumanizeTests(TransRealMixin, TestCase):
'100', '1,000', '10,123', '10,311', '1,000,000', '1,234,567.1234567', '1,234,567.1234567', '100', '1,000', '10,123', '10,311', '1,000,000', '1,234,567.1234567', '1,234,567.1234567',
None) None)
with self.settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=False): with self.settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=False), \
with translation.override('en'): translation.override('en'):
self.humanize_tester(test_list, result_list, 'intcomma') self.humanize_tester(test_list, result_list, 'intcomma')
def test_intcomma_without_number_grouping(self): def test_intcomma_without_number_grouping(self):
# Regression for #17414 # Regression for #17414
with translation.override('ja'): with translation.override('ja'), self.settings(USE_L10N=True):
with self.settings(USE_L10N=True): self.humanize_tester([100], ['100'], 'intcomma')
self.humanize_tester([100], ['100'], 'intcomma')
def test_intword(self): def test_intword(self):
test_list = ('100', '1000000', '1200000', '1290000', test_list = ('100', '1000000', '1200000', '1290000',
@ -104,18 +103,18 @@ class HumanizeTests(TransRealMixin, TestCase):
'100', '1000', '10123', '10311', '1000000', None) '100', '1000', '10123', '10311', '1000000', None)
result_list = ('100', '1.000', '10.123', '10.311', '1.000.000', '1.234.567,25', result_list = ('100', '1.000', '10.123', '10.311', '1.000.000', '1.234.567,25',
'100', '1.000', '10.123', '10.311', '1.000.000', None) '100', '1.000', '10.123', '10.311', '1.000.000', None)
with self.settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=True): with self.settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=True), \
with translation.override('de'): translation.override('de'):
self.humanize_tester(test_list, result_list, 'intcomma') self.humanize_tester(test_list, result_list, 'intcomma')
def test_i18n_intword(self): def test_i18n_intword(self):
test_list = ('100', '1000000', '1200000', '1290000', test_list = ('100', '1000000', '1200000', '1290000',
'1000000000', '2000000000', '6000000000000') '1000000000', '2000000000', '6000000000000')
result_list = ('100', '1,0 Million', '1,2 Millionen', '1,3 Millionen', result_list = ('100', '1,0 Million', '1,2 Millionen', '1,3 Millionen',
'1,0 Milliarde', '2,0 Milliarden', '6,0 Billionen') '1,0 Milliarde', '2,0 Milliarden', '6,0 Billionen')
with self.settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=True): with self.settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=True), \
with translation.override('de'): translation.override('de'):
self.humanize_tester(test_list, result_list, 'intword') self.humanize_tester(test_list, result_list, 'intword')
def test_apnumber(self): def test_apnumber(self):
test_list = [str(x) for x in range(1, 11)] test_list = [str(x) for x in range(1, 11)]
@ -162,9 +161,9 @@ class HumanizeTests(TransRealMixin, TestCase):
orig_humanize_datetime, humanize.datetime = humanize.datetime, MockDateTime orig_humanize_datetime, humanize.datetime = humanize.datetime, MockDateTime
try: try:
with override_settings(TIME_ZONE="America/Chicago", USE_TZ=True): with override_settings(TIME_ZONE="America/Chicago", USE_TZ=True), \
with translation.override('en'): translation.override('en'):
self.humanize_tester([dt], ['yesterday'], 'naturalday') self.humanize_tester([dt], ['yesterday'], 'naturalday')
finally: finally:
humanize.datetime = orig_humanize_datetime humanize.datetime = orig_humanize_datetime

View File

@ -1,5 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import models
def check_test_runner(): def check_test_runner():
""" """
@ -24,6 +25,31 @@ def check_test_runner():
] ]
return ' '.join(message) return ' '.join(message)
def check_boolean_field_default_value():
"""
Checks if there are any BooleanFields without a default value, &
warns the user that the default has changed from False to Null.
"""
fields = []
for cls in models.get_models():
opts = cls._meta
for f in opts.local_fields:
if isinstance(f, models.BooleanField) and not f.has_default():
fields.append(
'%s.%s: "%s"' % (opts.app_label, opts.object_name, f.name)
)
if fields:
fieldnames = ", ".join(fields)
message = [
"You have not set a default value for one or more BooleanFields:",
"%s." % fieldnames,
"In Django 1.6 the default value of BooleanField was changed from",
"False to Null when Field.default isn't defined. See",
"https://docs.djangoproject.com/en/1.6/ref/models/fields/#booleanfield"
"for more information."
]
return ' '.join(message)
def run_checks(): def run_checks():
""" """
@ -31,7 +57,8 @@ def run_checks():
messages from all the relevant check functions for this version of Django. messages from all the relevant check functions for this version of Django.
""" """
checks = [ checks = [
check_test_runner() check_test_runner(),
check_boolean_field_default_value(),
] ]
# Filter out the ``None`` or empty strings. # Filter out the ``None`` or empty strings.
return [output for output in checks if output] return [output for output in checks if output]

View File

@ -172,7 +172,16 @@ class FileSystemStorage(Storage):
directory = os.path.dirname(full_path) directory = os.path.dirname(full_path)
if not os.path.exists(directory): if not os.path.exists(directory):
try: try:
os.makedirs(directory) if settings.FILE_UPLOAD_DIRECTORY_PERMISSIONS is not None:
# os.makedirs applies the global umask, so we reset it,
# for consistency with FILE_UPLOAD_PERMISSIONS behavior.
old_umask = os.umask(0)
try:
os.makedirs(directory, settings.FILE_UPLOAD_DIRECTORY_PERMISSIONS)
finally:
os.umask(old_umask)
else:
os.makedirs(directory)
except OSError as e: except OSError as e:
if e.errno != errno.EEXIST: if e.errno != errno.EEXIST:
raise raise

View File

@ -26,6 +26,8 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
1700: 'DecimalField', 1700: 'DecimalField',
} }
ignored_tables = []
def get_table_list(self, cursor): def get_table_list(self, cursor):
"Returns a list of table names in the current database." "Returns a list of table names in the current database."
cursor.execute(""" cursor.execute("""
@ -35,7 +37,7 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
WHERE c.relkind IN ('r', 'v', '') WHERE c.relkind IN ('r', 'v', '')
AND n.nspname NOT IN ('pg_catalog', 'pg_toast') AND n.nspname NOT IN ('pg_catalog', 'pg_toast')
AND pg_catalog.pg_table_is_visible(c.oid)""") AND pg_catalog.pg_table_is_visible(c.oid)""")
return [row[0] for row in cursor.fetchall()] return [row[0] for row in cursor.fetchall() if row[0] not in self.ignored_tables]
def get_table_description(self, cursor, table_name): def get_table_description(self, cursor, table_name):
"Returns a description of the table, with the DB-API cursor.description interface." "Returns a description of the table, with the DB-API cursor.description interface."

View File

@ -184,10 +184,21 @@ class ModelBase(type):
else: else:
new_class._meta.concrete_model = new_class new_class._meta.concrete_model = new_class
# Do the appropriate setup for any model parents. # Collect the parent links for multi-table inheritance.
o2o_map = dict([(f.rel.to, f) for f in new_class._meta.local_fields parent_links = {}
if isinstance(f, OneToOneField)]) for base in reversed([new_class] + parents):
# Conceptually equivalent to `if base is Model`.
if not hasattr(base, '_meta'):
continue
# Skip concrete parent classes.
if base != new_class and not base._meta.abstract:
continue
# Locate OneToOneField instances.
for field in base._meta.local_fields:
if isinstance(field, OneToOneField):
parent_links[field.rel.to] = field
# Do the appropriate setup for any model parents.
for base in parents: for base in parents:
original_base = base original_base = base
if not hasattr(base, '_meta'): if not hasattr(base, '_meta'):
@ -208,8 +219,8 @@ class ModelBase(type):
if not base._meta.abstract: if not base._meta.abstract:
# Concrete classes... # Concrete classes...
base = base._meta.concrete_model base = base._meta.concrete_model
if base in o2o_map: if base in parent_links:
field = o2o_map[base] field = parent_links[base]
elif not is_proxy: elif not is_proxy:
attr_name = '%s_ptr' % base._meta.model_name attr_name = '%s_ptr' % base._meta.model_name
field = OneToOneField(base, name=attr_name, field = OneToOneField(base, name=attr_name,
@ -448,7 +459,9 @@ class Model(six.with_metaclass(ModelBase)):
return '%s object' % self.__class__.__name__ return '%s object' % self.__class__.__name__
def __eq__(self, other): def __eq__(self, other):
return isinstance(other, self.__class__) and self._get_pk_val() == other._get_pk_val() return (isinstance(other, Model) and
self._meta.concrete_model == other._meta.concrete_model and
self._get_pk_val() == other._get_pk_val())
def __ne__(self, other): def __ne__(self, other):
return not self.__eq__(other) return not self.__eq__(other)

View File

@ -313,14 +313,13 @@ class QuerySet(object):
kwargs[arg.default_alias] = arg kwargs[arg.default_alias] = arg
query = self.query.clone() query = self.query.clone()
force_subq = query.low_mark != 0 or query.high_mark is not None
aggregate_names = [] aggregate_names = []
for (alias, aggregate_expr) in kwargs.items(): for (alias, aggregate_expr) in kwargs.items():
query.add_aggregate(aggregate_expr, self.model, alias, query.add_aggregate(aggregate_expr, self.model, alias,
is_summary=True) is_summary=True)
aggregate_names.append(alias) aggregate_names.append(alias)
return query.get_aggregation(using=self.db, force_subq=force_subq)
return query.get_aggregation(using=self.db)
def count(self): def count(self):
""" """

View File

@ -167,7 +167,6 @@ class SQLCompiler(object):
if obj.low_mark == 0 and obj.high_mark is None: if obj.low_mark == 0 and obj.high_mark is None:
# If there is no slicing in use, then we can safely drop all ordering # If there is no slicing in use, then we can safely drop all ordering
obj.clear_ordering(True) obj.clear_ordering(True)
obj.bump_prefix()
return obj.get_compiler(connection=self.connection).as_sql() return obj.get_compiler(connection=self.connection).as_sql()
def get_columns(self, with_aliases=False): def get_columns(self, with_aliases=False):
@ -808,13 +807,14 @@ class SQLCompiler(object):
return result return result
def as_subquery_condition(self, alias, columns, qn): def as_subquery_condition(self, alias, columns, qn):
inner_qn = self.quote_name_unless_alias
qn2 = self.connection.ops.quote_name qn2 = self.connection.ops.quote_name
if len(columns) == 1: if len(columns) == 1:
sql, params = self.as_sql() sql, params = self.as_sql()
return '%s.%s IN (%s)' % (qn(alias), qn2(columns[0]), sql), params return '%s.%s IN (%s)' % (qn(alias), qn2(columns[0]), sql), params
for index, select_col in enumerate(self.query.select): for index, select_col in enumerate(self.query.select):
lhs = '%s.%s' % (qn(select_col.col[0]), qn2(select_col.col[1])) lhs = '%s.%s' % (inner_qn(select_col.col[0]), qn2(select_col.col[1]))
rhs = '%s.%s' % (qn(alias), qn2(columns[index])) rhs = '%s.%s' % (qn(alias), qn2(columns[index]))
self.query.where.add( self.query.where.add(
QueryWrapper('%s = %s' % (lhs, rhs), []), 'AND') QueryWrapper('%s = %s' % (lhs, rhs), []), 'AND')
@ -1010,7 +1010,6 @@ class SQLUpdateCompiler(SQLCompiler):
# We need to use a sub-select in the where clause to filter on things # We need to use a sub-select in the where clause to filter on things
# from other tables. # from other tables.
query = self.query.clone(klass=Query) query = self.query.clone(klass=Query)
query.bump_prefix()
query.extra = {} query.extra = {}
query.select = [] query.select = []
query.add_fields([query.get_meta().pk.name]) query.add_fields([query.get_meta().pk.name])

View File

@ -97,6 +97,7 @@ class Query(object):
LOUTER = 'LEFT OUTER JOIN' LOUTER = 'LEFT OUTER JOIN'
alias_prefix = 'T' alias_prefix = 'T'
subq_aliases = frozenset([alias_prefix])
query_terms = QUERY_TERMS query_terms = QUERY_TERMS
aggregates_module = base_aggregates_module aggregates_module = base_aggregates_module
@ -273,6 +274,10 @@ class Query(object):
else: else:
obj.used_aliases = set() obj.used_aliases = set()
obj.filter_is_sticky = False obj.filter_is_sticky = False
if 'alias_prefix' in self.__dict__:
obj.alias_prefix = self.alias_prefix
if 'subq_aliases' in self.__dict__:
obj.subq_aliases = self.subq_aliases.copy()
obj.__dict__.update(kwargs) obj.__dict__.update(kwargs)
if hasattr(obj, '_setup_query'): if hasattr(obj, '_setup_query'):
@ -310,7 +315,7 @@ class Query(object):
# Return value depends on the type of the field being processed. # Return value depends on the type of the field being processed.
return self.convert_values(value, aggregate.field, connection) return self.convert_values(value, aggregate.field, connection)
def get_aggregation(self, using): def get_aggregation(self, using, force_subq=False):
""" """
Returns the dictionary with the values of the existing aggregations. Returns the dictionary with the values of the existing aggregations.
""" """
@ -320,18 +325,26 @@ class Query(object):
# If there is a group by clause, aggregating does not add useful # If there is a group by clause, aggregating does not add useful
# information but retrieves only the first row. Aggregate # information but retrieves only the first row. Aggregate
# over the subquery instead. # over the subquery instead.
if self.group_by is not None: if self.group_by is not None or force_subq:
from django.db.models.sql.subqueries import AggregateQuery from django.db.models.sql.subqueries import AggregateQuery
query = AggregateQuery(self.model) query = AggregateQuery(self.model)
obj = self.clone() obj = self.clone()
if not force_subq:
# In forced subq case the ordering and limits will likely
# affect the results.
obj.clear_ordering(True)
obj.clear_limits()
obj.select_for_update = False
obj.select_related = False
obj.related_select_cols = []
relabels = dict((t, 'subquery') for t in self.tables)
# Remove any aggregates marked for reduction from the subquery # Remove any aggregates marked for reduction from the subquery
# and move them to the outer AggregateQuery. # and move them to the outer AggregateQuery.
for alias, aggregate in self.aggregate_select.items(): for alias, aggregate in self.aggregate_select.items():
if aggregate.is_summary: if aggregate.is_summary:
query.aggregate_select[alias] = aggregate query.aggregate_select[alias] = aggregate.relabeled_clone(relabels)
del obj.aggregate_select[alias] del obj.aggregate_select[alias]
try: try:
@ -780,28 +793,22 @@ class Query(object):
data = data._replace(lhs_alias=change_map[lhs]) data = data._replace(lhs_alias=change_map[lhs])
self.alias_map[alias] = data self.alias_map[alias] = data
def bump_prefix(self, exceptions=()): def bump_prefix(self, outer_query):
""" """
Changes the alias prefix to the next letter in the alphabet and Changes the alias prefix to the next letter in the alphabet in a way
relabels all the aliases. Even tables that previously had no alias will that the outer query's aliases and this query's aliases will not
get an alias after this call (it's mostly used for nested queries and conflict. Even tables that previously had no alias will get an alias
the outer query will already be using the non-aliased table name). after this call.
Subclasses who create their own prefix should override this method to
produce a similar result (a new prefix and relabelled aliases).
The 'exceptions' parameter is a container that holds alias names which
should not be changed.
""" """
current = ord(self.alias_prefix) self.alias_prefix = chr(ord(self.alias_prefix) + 1)
assert current < ord('Z') while self.alias_prefix in self.subq_aliases:
prefix = chr(current + 1) self.alias_prefix = chr(ord(self.alias_prefix) + 1)
self.alias_prefix = prefix assert self.alias_prefix < 'Z'
self.subq_aliases = self.subq_aliases.union([self.alias_prefix])
outer_query.subq_aliases = outer_query.subq_aliases.union(self.subq_aliases)
change_map = OrderedDict() change_map = OrderedDict()
for pos, alias in enumerate(self.tables): for pos, alias in enumerate(self.tables):
if alias in exceptions: new_alias = '%s%d' % (self.alias_prefix, pos)
continue
new_alias = '%s%d' % (prefix, pos)
change_map[alias] = new_alias change_map[alias] = new_alias
self.tables[pos] = new_alias self.tables[pos] = new_alias
self.change_aliases(change_map) self.change_aliases(change_map)
@ -1005,6 +1012,65 @@ class Query(object):
# Add the aggregate to the query # Add the aggregate to the query
aggregate.add_to_query(self, alias, col=col, source=source, is_summary=is_summary) aggregate.add_to_query(self, alias, col=col, source=source, is_summary=is_summary)
def prepare_lookup_value(self, value, lookup_type, can_reuse):
# Interpret '__exact=None' as the sql 'is NULL'; otherwise, reject all
# uses of None as a query value.
if value is None:
if lookup_type != 'exact':
raise ValueError("Cannot use None as a query value")
lookup_type = 'isnull'
value = True
elif callable(value):
value = value()
elif isinstance(value, ExpressionNode):
# If value is a query expression, evaluate it
value = SQLEvaluator(value, self, reuse=can_reuse)
if hasattr(value, 'query') and hasattr(value.query, 'bump_prefix'):
value = value._clone()
value.query.bump_prefix(self)
if hasattr(value, 'bump_prefix'):
value = value.clone()
value.bump_prefix(self)
# For Oracle '' is equivalent to null. The check needs to be done
# at this stage because join promotion can't be done at compiler
# stage. Using DEFAULT_DB_ALIAS isn't nice, but it is the best we
# can do here. Similar thing is done in is_nullable(), too.
if (connections[DEFAULT_DB_ALIAS].features.interprets_empty_strings_as_nulls and
lookup_type == 'exact' and value == ''):
value = True
lookup_type = 'isnull'
return value, lookup_type
def solve_lookup_type(self, lookup):
"""
Solve the lookup type from the lookup (eg: 'foobar__id__icontains')
"""
lookup_type = 'exact' # Default lookup type
lookup_parts = lookup.split(LOOKUP_SEP)
num_parts = len(lookup_parts)
if (len(lookup_parts) > 1 and lookup_parts[-1] in self.query_terms
and lookup not in self.aggregates):
# Traverse the lookup query to distinguish related fields from
# lookup types.
lookup_model = self.model
for counter, field_name in enumerate(lookup_parts):
try:
lookup_field = lookup_model._meta.get_field(field_name)
except FieldDoesNotExist:
# Not a field. Bail out.
lookup_type = lookup_parts.pop()
break
# Unless we're at the end of the list of lookups, let's attempt
# to continue traversing relations.
if (counter + 1) < num_parts:
try:
lookup_model = lookup_field.rel.to
except AttributeError:
# Not a related field. Bail out.
lookup_type = lookup_parts.pop()
break
return lookup_type, lookup_parts
def build_filter(self, filter_expr, branch_negated=False, current_negated=False, def build_filter(self, filter_expr, branch_negated=False, current_negated=False,
can_reuse=None): can_reuse=None):
""" """
@ -1033,58 +1099,15 @@ class Query(object):
is responsible for unreffing the joins used. is responsible for unreffing the joins used.
""" """
arg, value = filter_expr arg, value = filter_expr
parts = arg.split(LOOKUP_SEP) lookup_type, parts = self.solve_lookup_type(arg)
if not parts: if not parts:
raise FieldError("Cannot parse keyword query %r" % arg) raise FieldError("Cannot parse keyword query %r" % arg)
# Work out the lookup type and remove it from the end of 'parts', # Work out the lookup type and remove it from the end of 'parts',
# if necessary. # if necessary.
lookup_type = 'exact' # Default lookup type value, lookup_type = self.prepare_lookup_value(value, lookup_type, can_reuse)
num_parts = len(parts)
if (len(parts) > 1 and parts[-1] in self.query_terms
and arg not in self.aggregates):
# Traverse the lookup query to distinguish related fields from
# lookup types.
lookup_model = self.model
for counter, field_name in enumerate(parts):
try:
lookup_field = lookup_model._meta.get_field(field_name)
except FieldDoesNotExist:
# Not a field. Bail out.
lookup_type = parts.pop()
break
# Unless we're at the end of the list of lookups, let's attempt
# to continue traversing relations.
if (counter + 1) < num_parts:
try:
lookup_model = lookup_field.rel.to
except AttributeError:
# Not a related field. Bail out.
lookup_type = parts.pop()
break
clause = self.where_class() clause = self.where_class()
# Interpret '__exact=None' as the sql 'is NULL'; otherwise, reject all
# uses of None as a query value.
if value is None:
if lookup_type != 'exact':
raise ValueError("Cannot use None as a query value")
lookup_type = 'isnull'
value = True
elif callable(value):
value = value()
elif isinstance(value, ExpressionNode):
# If value is a query expression, evaluate it
value = SQLEvaluator(value, self, reuse=can_reuse)
# For Oracle '' is equivalent to null. The check needs to be done
# at this stage because join promotion can't be done at compiler
# stage. Using DEFAULT_DB_ALIAS isn't nice, but it is the best we
# can do here. Similar thing is done in is_nullable(), too.
if (connections[DEFAULT_DB_ALIAS].features.interprets_empty_strings_as_nulls and
lookup_type == 'exact' and value == ''):
value = True
lookup_type = 'isnull'
for alias, aggregate in self.aggregates.items(): for alias, aggregate in self.aggregates.items():
if alias in (parts[0], LOOKUP_SEP.join(parts)): if alias in (parts[0], LOOKUP_SEP.join(parts)):
clause.add((aggregate, lookup_type, value), AND) clause.add((aggregate, lookup_type, value), AND)
@ -1096,7 +1119,7 @@ class Query(object):
try: try:
field, sources, opts, join_list, path = self.setup_joins( field, sources, opts, join_list, path = self.setup_joins(
parts, opts, alias, can_reuse, allow_many,) parts, opts, alias, can_reuse, allow_many,)
if can_reuse is not None: if can_reuse is not None:
can_reuse.update(join_list) can_reuse.update(join_list)
except MultiJoin as e: except MultiJoin as e:
@ -1404,7 +1427,6 @@ class Query(object):
# Generate the inner query. # Generate the inner query.
query = Query(self.model) query = Query(self.model)
query.where.add(query.build_filter(filter_expr), AND) query.where.add(query.build_filter(filter_expr), AND)
query.bump_prefix()
query.clear_ordering(True) query.clear_ordering(True)
# Try to have as simple as possible subquery -> trim leading joins from # Try to have as simple as possible subquery -> trim leading joins from
# the subquery. # the subquery.

View File

@ -434,7 +434,9 @@ class BoundField(object):
This really is only useful for RadioSelect widgets, so that you can This really is only useful for RadioSelect widgets, so that you can
iterate over individual radio buttons in a template. iterate over individual radio buttons in a template.
""" """
for subwidget in self.field.widget.subwidgets(self.html_name, self.value()): id_ = self.field.widget.attrs.get('id') or self.auto_id
attrs = {'id': id_} if id_ else {}
for subwidget in self.field.widget.subwidgets(self.html_name, self.value(), attrs):
yield subwidget yield subwidget
def __len__(self): def __len__(self):

View File

@ -601,16 +601,15 @@ class ChoiceInput(SubWidget):
self.choice_value = force_text(choice[0]) self.choice_value = force_text(choice[0])
self.choice_label = force_text(choice[1]) self.choice_label = force_text(choice[1])
self.index = index self.index = index
if 'id' in self.attrs:
self.attrs['id'] += "_%d" % self.index
def __str__(self): def __str__(self):
return self.render() return self.render()
def render(self, name=None, value=None, attrs=None, choices=()): def render(self, name=None, value=None, attrs=None, choices=()):
name = name or self.name if self.id_for_label:
value = value or self.value label_for = format_html(' for="{0}"', self.id_for_label)
attrs = attrs or self.attrs
if 'id' in self.attrs:
label_for = format_html(' for="{0}_{1}"', self.attrs['id'], self.index)
else: else:
label_for = '' label_for = ''
return format_html('<label{0}>{1} {2}</label>', label_for, self.tag(), self.choice_label) return format_html('<label{0}>{1} {2}</label>', label_for, self.tag(), self.choice_label)
@ -619,13 +618,15 @@ class ChoiceInput(SubWidget):
return self.value == self.choice_value return self.value == self.choice_value
def tag(self): def tag(self):
if 'id' in self.attrs:
self.attrs['id'] = '%s_%s' % (self.attrs['id'], self.index)
final_attrs = dict(self.attrs, type=self.input_type, name=self.name, value=self.choice_value) final_attrs = dict(self.attrs, type=self.input_type, name=self.name, value=self.choice_value)
if self.is_checked(): if self.is_checked():
final_attrs['checked'] = 'checked' final_attrs['checked'] = 'checked'
return format_html('<input{0} />', flatatt(final_attrs)) return format_html('<input{0} />', flatatt(final_attrs))
@property
def id_for_label(self):
return self.attrs.get('id', '')
class RadioChoiceInput(ChoiceInput): class RadioChoiceInput(ChoiceInput):
input_type = 'radio' input_type = 'radio'

View File

@ -6,7 +6,7 @@ from importlib import import_module
from inspect import getargspec from inspect import getargspec
from django.conf import settings from django.conf import settings
from django.template.context import (Context, RequestContext, from django.template.context import (BaseContext, Context, RequestContext,
ContextPopException) ContextPopException)
from django.utils.itercompat import is_iterable from django.utils.itercompat import is_iterable
from django.utils.text import (smart_split, unescape_string_literal, from django.utils.text import (smart_split, unescape_string_literal,
@ -765,6 +765,9 @@ class Variable(object):
current = current[bit] current = current[bit]
except (TypeError, AttributeError, KeyError, ValueError): except (TypeError, AttributeError, KeyError, ValueError):
try: # attribute lookup try: # attribute lookup
# Don't return class attributes if the class is the context:
if isinstance(current, BaseContext) and getattr(type(current), bit):
raise AttributeError
current = getattr(current, bit) current = getattr(current, bit)
except (TypeError, AttributeError): except (TypeError, AttributeError):
try: # list-index lookup try: # list-index lookup

View File

@ -458,10 +458,11 @@ class VerbatimNode(Node):
return self.content return self.content
class WidthRatioNode(Node): class WidthRatioNode(Node):
def __init__(self, val_expr, max_expr, max_width): def __init__(self, val_expr, max_expr, max_width, asvar=None):
self.val_expr = val_expr self.val_expr = val_expr
self.max_expr = max_expr self.max_expr = max_expr
self.max_width = max_width self.max_width = max_width
self.asvar = asvar
def render(self, context): def render(self, context):
try: try:
@ -480,7 +481,13 @@ class WidthRatioNode(Node):
return '0' return '0'
except (ValueError, TypeError): except (ValueError, TypeError):
return '' return ''
return str(int(round(ratio))) result = str(int(round(ratio)))
if self.asvar:
context[self.asvar] = result
return ''
else:
return result
class WithNode(Node): class WithNode(Node):
def __init__(self, var, name, nodelist, extra_context=None): def __init__(self, var, name, nodelist, extra_context=None):
@ -1353,20 +1360,34 @@ def widthratio(parser, token):
For example:: For example::
<img src='bar.gif' height='10' width='{% widthratio this_value max_value max_width %}' /> <img src="bar.png" alt="Bar"
height="10" width="{% widthratio this_value max_value max_width %}" />
If ``this_value`` is 175, ``max_value`` is 200, and ``max_width`` is 100, If ``this_value`` is 175, ``max_value`` is 200, and ``max_width`` is 100,
the image in the above example will be 88 pixels wide the image in the above example will be 88 pixels wide
(because 175/200 = .875; .875 * 100 = 87.5 which is rounded up to 88). (because 175/200 = .875; .875 * 100 = 87.5 which is rounded up to 88).
In some cases you might want to capture the result of widthratio in a
variable. It can be useful for instance in a blocktrans like this::
{% widthratio this_value max_value max_width as width %}
{% blocktrans %}The width is: {{ width }}{% endblocktrans %}
""" """
bits = token.split_contents() bits = token.split_contents()
if len(bits) != 4: if len(bits) == 4:
raise TemplateSyntaxError("widthratio takes three arguments") tag, this_value_expr, max_value_expr, max_width = bits
tag, this_value_expr, max_value_expr, max_width = bits asvar = None
elif len(bits) == 6:
tag, this_value_expr, max_value_expr, max_width, as_, asvar = bits
if as_ != 'as':
raise TemplateSyntaxError("Invalid syntax in widthratio tag. Expecting 'as' keyword")
else:
raise TemplateSyntaxError("widthratio takes at least three arguments")
return WidthRatioNode(parser.compile_filter(this_value_expr), return WidthRatioNode(parser.compile_filter(this_value_expr),
parser.compile_filter(max_value_expr), parser.compile_filter(max_value_expr),
parser.compile_filter(max_width)) parser.compile_filter(max_width),
asvar=asvar)
@register.tag('with') @register.tag('with')
def do_with(parser, token): def do_with(parser, token):

View File

@ -37,6 +37,7 @@ BOUNDARY = 'BoUnDaRyStRiNg'
MULTIPART_CONTENT = 'multipart/form-data; boundary=%s' % BOUNDARY MULTIPART_CONTENT = 'multipart/form-data; boundary=%s' % BOUNDARY
CONTENT_TYPE_RE = re.compile('.*; charset=([\w\d-]+);?') CONTENT_TYPE_RE = re.compile('.*; charset=([\w\d-]+);?')
class FakePayload(object): class FakePayload(object):
""" """
A wrapper around BytesIO that restricts what can be read since data from A wrapper around BytesIO that restricts what can be read since data from
@ -123,6 +124,7 @@ class ClientHandler(BaseHandler):
return response return response
def store_rendered_templates(store, signal, sender, template, context, **kwargs): def store_rendered_templates(store, signal, sender, template, context, **kwargs):
""" """
Stores templates and contexts that are rendered. Stores templates and contexts that are rendered.
@ -133,6 +135,7 @@ def store_rendered_templates(store, signal, sender, template, context, **kwargs)
store.setdefault('templates', []).append(template) store.setdefault('templates', []).append(template)
store.setdefault('context', ContextList()).append(copy(context)) store.setdefault('context', ContextList()).append(copy(context))
def encode_multipart(boundary, data): def encode_multipart(boundary, data):
""" """
Encodes multipart POST data from a dictionary of form values. Encodes multipart POST data from a dictionary of form values.
@ -178,6 +181,7 @@ def encode_multipart(boundary, data):
]) ])
return b'\r\n'.join(lines) return b'\r\n'.join(lines)
def encode_file(boundary, key, file): def encode_file(boundary, key, file):
to_bytes = lambda s: force_bytes(s, settings.DEFAULT_CHARSET) to_bytes = lambda s: force_bytes(s, settings.DEFAULT_CHARSET)
if hasattr(file, 'content_type'): if hasattr(file, 'content_type'):
@ -189,8 +193,8 @@ def encode_file(boundary, key, file):
content_type = 'application/octet-stream' content_type = 'application/octet-stream'
return [ return [
to_bytes('--%s' % boundary), to_bytes('--%s' % boundary),
to_bytes('Content-Disposition: form-data; name="%s"; filename="%s"' \ to_bytes('Content-Disposition: form-data; name="%s"; filename="%s"'
% (key, os.path.basename(file.name))), % (key, os.path.basename(file.name))),
to_bytes('Content-Type: %s' % content_type), to_bytes('Content-Type: %s' % content_type),
b'', b'',
file.read() file.read()
@ -274,14 +278,11 @@ class RequestFactory(object):
def get(self, path, data={}, **extra): def get(self, path, data={}, **extra):
"Construct a GET request." "Construct a GET request."
parsed = urlparse(path)
r = { r = {
'PATH_INFO': self._get_path(parsed), 'QUERY_STRING': urlencode(data, doseq=True),
'QUERY_STRING': urlencode(data, doseq=True) or force_str(parsed[4]),
'REQUEST_METHOD': str('GET'),
} }
r.update(extra) r.update(extra)
return self.request(**r) return self.generic('GET', path, **r)
def post(self, path, data={}, content_type=MULTIPART_CONTENT, def post(self, path, data={}, content_type=MULTIPART_CONTENT,
**extra): **extra):
@ -289,32 +290,19 @@ class RequestFactory(object):
post_data = self._encode_data(data, content_type) post_data = self._encode_data(data, content_type)
parsed = urlparse(path) return self.generic('POST', path, post_data, content_type, **extra)
r = {
'CONTENT_LENGTH': len(post_data),
'CONTENT_TYPE': content_type,
'PATH_INFO': self._get_path(parsed),
'QUERY_STRING': force_str(parsed[4]),
'REQUEST_METHOD': str('POST'),
'wsgi.input': FakePayload(post_data),
}
r.update(extra)
return self.request(**r)
def head(self, path, data={}, **extra): def head(self, path, data={}, **extra):
"Construct a HEAD request." "Construct a HEAD request."
parsed = urlparse(path)
r = { r = {
'PATH_INFO': self._get_path(parsed), 'QUERY_STRING': urlencode(data, doseq=True),
'QUERY_STRING': urlencode(data, doseq=True) or force_str(parsed[4]),
'REQUEST_METHOD': str('HEAD'),
} }
r.update(extra) r.update(extra)
return self.request(**r) return self.generic('HEAD', path, **r)
def options(self, path, data='', content_type='application/octet-stream', def options(self, path, data='', content_type='application/octet-stream',
**extra): **extra):
"Construct an OPTIONS request." "Construct an OPTIONS request."
return self.generic('OPTIONS', path, data, content_type, **extra) return self.generic('OPTIONS', path, data, content_type, **extra)
@ -324,22 +312,22 @@ class RequestFactory(object):
return self.generic('PUT', path, data, content_type, **extra) return self.generic('PUT', path, data, content_type, **extra)
def patch(self, path, data='', content_type='application/octet-stream', def patch(self, path, data='', content_type='application/octet-stream',
**extra): **extra):
"Construct a PATCH request." "Construct a PATCH request."
return self.generic('PATCH', path, data, content_type, **extra) return self.generic('PATCH', path, data, content_type, **extra)
def delete(self, path, data='', content_type='application/octet-stream', def delete(self, path, data='', content_type='application/octet-stream',
**extra): **extra):
"Construct a DELETE request." "Construct a DELETE request."
return self.generic('DELETE', path, data, content_type, **extra) return self.generic('DELETE', path, data, content_type, **extra)
def generic(self, method, path, def generic(self, method, path,
data='', content_type='application/octet-stream', **extra): data='', content_type='application/octet-stream', **extra):
"""Constructs an arbitrary HTTP request."""
parsed = urlparse(path) parsed = urlparse(path)
data = force_bytes(data, settings.DEFAULT_CHARSET) data = force_bytes(data, settings.DEFAULT_CHARSET)
r = { r = {
'PATH_INFO': self._get_path(parsed), 'PATH_INFO': self._get_path(parsed),
'QUERY_STRING': force_str(parsed[4]),
'REQUEST_METHOD': str(method), 'REQUEST_METHOD': str(method),
} }
if data: if data:
@ -349,8 +337,12 @@ class RequestFactory(object):
'wsgi.input': FakePayload(data), 'wsgi.input': FakePayload(data),
}) })
r.update(extra) r.update(extra)
# If QUERY_STRING is absent or empty, we want to extract it from the URL.
if not r.get('QUERY_STRING'):
r['QUERY_STRING'] = force_str(parsed[4])
return self.request(**r) return self.request(**r)
class Client(RequestFactory): class Client(RequestFactory):
""" """
A class that can act as a client for testing purposes. A class that can act as a client for testing purposes.
@ -392,7 +384,6 @@ class Client(RequestFactory):
return {} return {}
session = property(_session) session = property(_session)
def request(self, **request): def request(self, **request):
""" """
The master request method. Composes the environment dictionary The master request method. Composes the environment dictionary
@ -406,7 +397,8 @@ class Client(RequestFactory):
# callback function. # callback function.
data = {} data = {}
on_template_render = curry(store_rendered_templates, data) on_template_render = curry(store_rendered_templates, data)
signals.template_rendered.connect(on_template_render, dispatch_uid="template-render") signal_uid = "template-render-%s" % id(request)
signals.template_rendered.connect(on_template_render, dispatch_uid=signal_uid)
# Capture exceptions created by the handler. # Capture exceptions created by the handler.
got_request_exception.connect(self.store_exc_info, dispatch_uid="request-exception") got_request_exception.connect(self.store_exc_info, dispatch_uid="request-exception")
try: try:
@ -452,7 +444,7 @@ class Client(RequestFactory):
return response return response
finally: finally:
signals.template_rendered.disconnect(dispatch_uid="template-render") signals.template_rendered.disconnect(dispatch_uid=signal_uid)
got_request_exception.disconnect(dispatch_uid="request-exception") got_request_exception.disconnect(dispatch_uid="request-exception")
def get(self, path, data={}, follow=False, **extra): def get(self, path, data={}, follow=False, **extra):
@ -484,12 +476,11 @@ class Client(RequestFactory):
return response return response
def options(self, path, data='', content_type='application/octet-stream', def options(self, path, data='', content_type='application/octet-stream',
follow=False, **extra): follow=False, **extra):
""" """
Request a response from the server using OPTIONS. Request a response from the server using OPTIONS.
""" """
response = super(Client, self).options(path, response = super(Client, self).options(path, data=data, content_type=content_type, **extra)
data=data, content_type=content_type, **extra)
if follow: if follow:
response = self._handle_redirects(response, **extra) response = self._handle_redirects(response, **extra)
return response return response
@ -499,14 +490,13 @@ class Client(RequestFactory):
""" """
Send a resource to the server using PUT. Send a resource to the server using PUT.
""" """
response = super(Client, self).put(path, response = super(Client, self).put(path, data=data, content_type=content_type, **extra)
data=data, content_type=content_type, **extra)
if follow: if follow:
response = self._handle_redirects(response, **extra) response = self._handle_redirects(response, **extra)
return response return response
def patch(self, path, data='', content_type='application/octet-stream', def patch(self, path, data='', content_type='application/octet-stream',
follow=False, **extra): follow=False, **extra):
""" """
Send a resource to the server using PATCH. Send a resource to the server using PATCH.
""" """
@ -517,12 +507,12 @@ class Client(RequestFactory):
return response return response
def delete(self, path, data='', content_type='application/octet-stream', def delete(self, path, data='', content_type='application/octet-stream',
follow=False, **extra): follow=False, **extra):
""" """
Send a DELETE request to the server. Send a DELETE request to the server.
""" """
response = super(Client, self).delete(path, response = super(Client, self).delete(
data=data, content_type=content_type, **extra) path, data=data, content_type=content_type, **extra)
if follow: if follow:
response = self._handle_redirects(response, **extra) response = self._handle_redirects(response, **extra)
return response return response

View File

@ -263,17 +263,12 @@ class LazyObject(object):
__dir__ = new_method_proxy(dir) __dir__ = new_method_proxy(dir)
# Dictionary methods support # Dictionary methods support
@new_method_proxy __getitem__ = new_method_proxy(operator.getitem)
def __getitem__(self, key): __setitem__ = new_method_proxy(operator.setitem)
return self[key] __delitem__ = new_method_proxy(operator.delitem)
@new_method_proxy __len__ = new_method_proxy(len)
def __setitem__(self, key, value): __contains__ = new_method_proxy(operator.contains)
self[key] = value
@new_method_proxy
def __delitem__(self, key):
del self[key]
# Workaround for http://bugs.python.org/issue12370 # Workaround for http://bugs.python.org/issue12370

View File

@ -109,8 +109,7 @@ def http_date(epoch_seconds=None):
Outputs a string in the format 'Wdy, DD Mon YYYY HH:MM:SS GMT'. Outputs a string in the format 'Wdy, DD Mon YYYY HH:MM:SS GMT'.
""" """
rfcdate = formatdate(epoch_seconds) return formatdate(epoch_seconds, usegmt=True)
return '%s GMT' % rfcdate[:25]
def parse_http_date(date): def parse_http_date(date):
""" """
@ -253,11 +252,12 @@ def same_origin(url1, url2):
def is_safe_url(url, host=None): def is_safe_url(url, host=None):
""" """
Return ``True`` if the url is a safe redirection (i.e. it doesn't point to Return ``True`` if the url is a safe redirection (i.e. it doesn't point to
a different host). a different host and uses a safe scheme).
Always returns ``False`` on an empty url. Always returns ``False`` on an empty url.
""" """
if not url: if not url:
return False return False
netloc = urllib_parse.urlparse(url)[1] url_info = urllib_parse.urlparse(url)
return not netloc or netloc == host return (not url_info.netloc or url_info.netloc == host) and \
(not url_info.scheme or url_info.scheme in ['http', 'https'])

View File

@ -227,7 +227,7 @@ class ExceptionReporter(object):
return "File exists" return "File exists"
def get_traceback_data(self): def get_traceback_data(self):
"Return a Context instance containing traceback information." """Return a dictionary containing traceback information."""
if self.exc_type and issubclass(self.exc_type, TemplateDoesNotExist): if self.exc_type and issubclass(self.exc_type, TemplateDoesNotExist):
from django.template.loader import template_source_loaders from django.template.loader import template_source_loaders
@ -295,13 +295,13 @@ class ExceptionReporter(object):
def get_traceback_html(self): def get_traceback_html(self):
"Return HTML version of debug 500 HTTP error page." "Return HTML version of debug 500 HTTP error page."
t = Template(TECHNICAL_500_TEMPLATE, name='Technical 500 template') t = Template(TECHNICAL_500_TEMPLATE, name='Technical 500 template')
c = Context(self.get_traceback_data()) c = Context(self.get_traceback_data(), use_l10n=False)
return t.render(c) return t.render(c)
def get_traceback_text(self): def get_traceback_text(self):
"Return plain text version of debug 500 HTTP error page." "Return plain text version of debug 500 HTTP error page."
t = Template(TECHNICAL_500_TEXT_TEMPLATE, name='Technical 500 template') t = Template(TECHNICAL_500_TEXT_TEMPLATE, name='Technical 500 template')
c = Context(self.get_traceback_data(), autoescape=False) c = Context(self.get_traceback_data(), autoescape=False, use_l10n=False)
return t.render(c) return t.render(c)
def get_template_exception_info(self): def get_template_exception_info(self):

View File

@ -295,9 +295,9 @@ validation process will break.
Therefore, you must ensure that the form field used to represent your Therefore, you must ensure that the form field used to represent your
custom field performs whatever input validation and data cleaning is custom field performs whatever input validation and data cleaning is
necessary to convert user-provided form input into a necessary to convert user-provided form input into a
`to_python()`-compatible model field value. This may require writing a ``to_python()``-compatible model field value. This may require writing a
custom form field, and/or implementing the :meth:`.formfield` method on custom form field, and/or implementing the :meth:`.formfield` method on
your field to return a form field class whose `to_python()` returns the your field to return a form field class whose ``to_python()`` returns the
correct datatype. correct datatype.
Documenting your custom field Documenting your custom field

View File

@ -28,6 +28,7 @@ the easiest, fastest, and most stable deployment choice.
* `Chapter 12 of the Django Book (second edition)`_ discusses deployment * `Chapter 12 of the Django Book (second edition)`_ discusses deployment
and especially scaling in more detail. However, note that this edition and especially scaling in more detail. However, note that this edition
was written against Django version 1.1 and has not been updated since was written against Django version 1.1 and has not been updated since
`mod_python` was first deprecated, then completely removed in Django 1.5. ``mod_python`` was first deprecated, then completely removed in
Django 1.5.
.. _chapter 12 of the django book (second edition): http://djangobook.com/en/2.0/chapter12/ .. _chapter 12 of the django book (second edition): http://djangobook.com/en/2.0/chapter12/

View File

@ -16,7 +16,7 @@ version >= 2.2 and mod_wsgi >= 2.0. For example, you could:
.. note:: .. note::
If you have installed a :ref:`custom User model <auth-custom-user>` and If you have installed a :ref:`custom User model <auth-custom-user>` and
want to use this default auth handler, it must support an `is_active` want to use this default auth handler, it must support an ``is_active``
attribute. If you want to use group based authorization, your custom user attribute. If you want to use group based authorization, your custom user
must have a relation named 'groups', referring to a related object that has must have a relation named 'groups', referring to a related object that has
a 'name' field. You can also specify your own custom mod_wsgi a 'name' field. You can also specify your own custom mod_wsgi

View File

@ -193,7 +193,7 @@ other approaches:
configuration). configuration).
2. Use an ``Alias`` directive, as demonstrated above, to alias the appropriate 2. Use an ``Alias`` directive, as demonstrated above, to alias the appropriate
URL (probably :setting:`STATIC_URL` + `admin/`) to the actual location of URL (probably :setting:`STATIC_URL` + ``admin/``) to the actual location of
the admin files. the admin files.
3. Copy the admin static files so that they live within your Apache 3. Copy the admin static files so that they live within your Apache

View File

@ -157,7 +157,7 @@ using interactive rebase::
The HEAD~2 above is shorthand for two latest commits. The above command The HEAD~2 above is shorthand for two latest commits. The above command
will open an editor showing the two commits, prefixed with the word "pick". will open an editor showing the two commits, prefixed with the word "pick".
Change the second line to "squash" instead. This will keep the Change "pick" on the second line to "squash" instead. This will keep the
first commit, and squash the second commit into the first one. Save and quit first commit, and squash the second commit into the first one. Save and quit
the editor. A second editor window should open, so you can reword the the editor. A second editor window should open, so you can reword the
commit message for the commit now that it includes both your steps. commit message for the commit now that it includes both your steps.

View File

@ -370,8 +370,11 @@ these changes.
* Remove the backward compatible shims introduced to rename the attributes * Remove the backward compatible shims introduced to rename the attributes
``ChangeList.root_query_set`` and ``ChangeList.query_set``. ``ChangeList.root_query_set`` and ``ChangeList.query_set``.
* ``django.conf.urls.shortcut`` and ``django.views.defaults.shortcut`` will be * ``django.views.defaults.shortcut`` will be removed, as part of the
removed. goal of removing all ``django.contrib`` references from the core
Django codebase. Instead use
``django.contrib.contenttypes.views.shortcut``. ``django.conf.urls.shortcut``
will also be removed.
* Support for the Python Imaging Library (PIL) module will be removed, as it * Support for the Python Imaging Library (PIL) module will be removed, as it
no longer appears to be actively maintained & does not work on Python 3. no longer appears to be actively maintained & does not work on Python 3.
@ -442,11 +445,5 @@ these changes.
2.0 2.0
--- ---
* ``django.views.defaults.shortcut()``. This function has been moved
to ``django.contrib.contenttypes.views.shortcut()`` as part of the
goal of removing all ``django.contrib`` references from the core
Django codebase. The old shortcut will be removed in the 2.0
release.
* ``ssi`` and ``url`` template tags will be removed from the ``future`` template * ``ssi`` and ``url`` template tags will be removed from the ``future`` template
tag library (used during the 1.3/1.4 deprecation period). tag library (used during the 1.3/1.4 deprecation period).

View File

@ -83,10 +83,11 @@ A few items need to be taken care of before even beginning the release process.
This stuff starts about a week before the release; most of it can be done This stuff starts about a week before the release; most of it can be done
any time leading up to the actual release: any time leading up to the actual release:
#. If this is a security release, send out pre-notification **one week** #. If this is a security release, send out pre-notification **one week** before
before the release. We maintain a list of who gets these pre-notification the release. We maintain a list of who gets these pre-notification emails in
emails at *FIXME WHERE?*. This email should be signed by the key you'll use the private ``django-core`` repository. This email should be signed by the
for the release, and should include patches for each issue being fixed. key you'll use for the release, and should include patches for each issue
being fixed.
#. As the release approaches, watch Trac to make sure no release blockers #. As the release approaches, watch Trac to make sure no release blockers
are left for the upcoming release. are left for the upcoming release.

View File

@ -108,8 +108,12 @@ On the day of disclosure, we will take the following steps:
relevant patches and new releases, and crediting the reporter of relevant patches and new releases, and crediting the reporter of
the issue (if the reporter wishes to be publicly identified). the issue (if the reporter wishes to be publicly identified).
4. Post a notice to the `django-announce`_ mailing list that links to the blog
post.
.. _the Python Package Index: http://pypi.python.org/pypi .. _the Python Package Index: http://pypi.python.org/pypi
.. _the official Django development blog: https://www.djangoproject.com/weblog/ .. _the official Django development blog: https://www.djangoproject.com/weblog/
.. _django-announce: http://groups.google.com/group/django-announce
If a reported issue is believed to be particularly time-sensitive -- If a reported issue is believed to be particularly time-sensitive --
due to a known exploit in the wild, for example -- the time between due to a known exploit in the wild, for example -- the time between
@ -214,4 +218,4 @@ If you are added to the notification list, security-related emails
will be sent to you by Django's release manager, and all notification will be sent to you by Django's release manager, and all notification
emails will be signed with the same key used to sign Django releases; emails will be signed with the same key used to sign Django releases;
that key has the ID ``0x3684C0C08C8B2AE1``, and is available from most that key has the ID ``0x3684C0C08C8B2AE1``, and is available from most
commonly-used keyservers. commonly-used keyservers.

View File

@ -600,7 +600,7 @@ for your own sanity when dealing with the interactive prompt, but also because
objects' representations are used throughout Django's automatically-generated objects' representations are used throughout Django's automatically-generated
admin. admin.
.. admonition:: `__unicode__` or `__str__`? .. admonition:: ``__unicode__`` or ``__str__``?
On Python 3, things are simpler, just use On Python 3, things are simpler, just use
:meth:`~django.db.models.Model.__str__` and forget about :meth:`~django.db.models.Model.__str__` and forget about

View File

@ -385,15 +385,6 @@ search terms, Django will search the ``question`` field. You can use as many
fields as you'd like -- although because it uses a ``LIKE`` query behind the fields as you'd like -- although because it uses a ``LIKE`` query behind the
scenes, keep it reasonable, to keep your database happy. scenes, keep it reasonable, to keep your database happy.
Finally, because ``Poll`` objects have dates, it'd be convenient to be able to
drill down by date. Add this line::
date_hierarchy = 'pub_date'
That adds hierarchical navigation, by date, to the top of the change list page.
At top level, it displays all available years. Then it drills down to months
and, ultimately, days.
Now's also a good time to note that change lists give you free pagination. The Now's also a good time to note that change lists give you free pagination. The
default is to display 100 items per page. Change-list pagination, search boxes, default is to display 100 items per page. Change-list pagination, search boxes,
filters, date-hierarchies and column-header-ordering all work together like you filters, date-hierarchies and column-header-ordering all work together like you

View File

@ -104,12 +104,6 @@ TemplateView
Renders a given template, with the context containing parameters captured Renders a given template, with the context containing parameters captured
in the URL. in the URL.
.. versionchanged:: 1.5
The context used to be populated with a ``{{ params }}`` dictionary of
the parameters captured in the URL. Now those parameters are first-level
context variables.
**Ancestors (MRO)** **Ancestors (MRO)**
This view inherits methods and attributes from the following views: This view inherits methods and attributes from the following views:

View File

@ -138,24 +138,16 @@ YearArchiveView
* ``year``: A :class:`~datetime.date` object * ``year``: A :class:`~datetime.date` object
representing the given year. representing the given year.
.. versionchanged:: 1.5
Previously, this returned a string.
* ``next_year``: A :class:`~datetime.date` object * ``next_year``: A :class:`~datetime.date` object
representing the first day of the next year, according to representing the first day of the next year, according to
:attr:`~BaseDateListView.allow_empty` and :attr:`~BaseDateListView.allow_empty` and
:attr:`~DateMixin.allow_future`. :attr:`~DateMixin.allow_future`.
.. versionadded:: 1.5
* ``previous_year``: A :class:`~datetime.date` object * ``previous_year``: A :class:`~datetime.date` object
representing the first day of the previous year, according to representing the first day of the previous year, according to
:attr:`~BaseDateListView.allow_empty` and :attr:`~BaseDateListView.allow_empty` and
:attr:`~DateMixin.allow_future`. :attr:`~DateMixin.allow_future`.
.. versionadded:: 1.5
**Notes** **Notes**
* Uses a default ``template_name_suffix`` of ``_archive_year``. * Uses a default ``template_name_suffix`` of ``_archive_year``.

View File

@ -328,8 +328,3 @@ BaseDateListView
:meth:`~BaseDateListView.get_date_list_period` is used. ``date_type`` :meth:`~BaseDateListView.get_date_list_period` is used. ``date_type``
and ``ordering`` are simply passed to and ``ordering`` are simply passed to
:meth:`QuerySet.dates()<django.db.models.query.QuerySet.dates>`. :meth:`QuerySet.dates()<django.db.models.query.QuerySet.dates>`.
.. versionchanged:: 1.5
The ``ordering`` parameter was added, and the default order was
changed to ascending.

View File

@ -90,8 +90,6 @@ MultipleObjectMixin
.. attribute:: page_kwarg .. attribute:: page_kwarg
.. versionadded:: 1.5
A string specifying the name to use for the page parameter. A string specifying the name to use for the page parameter.
The view will expect this prameter to be available either as a query The view will expect this prameter to be available either as a query
string parameter (via ``request.GET``) or as a kwarg variable specified string parameter (via ``request.GET``) or as a kwarg variable specified

View File

@ -7,8 +7,6 @@ ContextMixin
.. class:: django.views.generic.base.ContextMixin .. class:: django.views.generic.base.ContextMixin
.. versionadded:: 1.5
**Methods** **Methods**
.. method:: get_context_data(**kwargs) .. method:: get_context_data(**kwargs)
@ -77,8 +75,6 @@ TemplateResponseMixin
.. attribute:: content_type .. attribute:: content_type
.. versionadded:: 1.5
The content type to use for the response. ``content_type`` is passed The content type to use for the response. ``content_type`` is passed
as a keyword argument to ``response_class``. Default is ``None`` -- as a keyword argument to ``response_class``. Default is ``None`` --
meaning that Django uses :setting:`DEFAULT_CONTENT_TYPE`. meaning that Django uses :setting:`DEFAULT_CONTENT_TYPE`.

View File

@ -269,8 +269,8 @@ Making actions available site-wide
admin.site.add_action(export_selected_objects) admin.site.add_action(export_selected_objects)
This makes the `export_selected_objects` action globally available as an This makes the ``export_selected_objects`` action globally available as an
action named `"export_selected_objects"`. You can explicitly give the action action named "export_selected_objects". You can explicitly give the action
a name -- good if you later want to programmatically :ref:`remove the action a name -- good if you later want to programmatically :ref:`remove the action
<disabling-admin-actions>` -- by passing a second argument to <disabling-admin-actions>` -- by passing a second argument to
:meth:`AdminSite.add_action()`:: :meth:`AdminSite.add_action()`::

View File

@ -156,6 +156,6 @@ Edit this object
Using these bookmarklets requires that you are either logged into the Using these bookmarklets requires that you are either logged into the
:mod:`Django admin <django.contrib.admin>` as a :mod:`Django admin <django.contrib.admin>` as a
:class:`~django.contrib.auth.models.User` with :class:`~django.contrib.auth.models.User` with
:attr:`~django.contrib.auth.models.User.is_staff` set to `True`, or that the :attr:`~django.contrib.auth.models.User.is_staff` set to ``True``, or that the
``XViewMiddleware`` is installed and you are accessing the site from an IP ``XViewMiddleware`` is installed and you are accessing the site from an IP
address listed in :setting:`INTERNAL_IPS`. address listed in :setting:`INTERNAL_IPS`.

View File

@ -1235,8 +1235,6 @@ templates used by the :class:`ModelAdmin` views:
.. method:: ModelAdmin.get_list_filter(self, request) .. method:: ModelAdmin.get_list_filter(self, request)
.. versionadded:: 1.5
The ``get_list_filter`` method is given the ``HttpRequest`` and is expected The ``get_list_filter`` method is given the ``HttpRequest`` and is expected
to return the same kind of sequence type as for the to return the same kind of sequence type as for the
:attr:`~ModelAdmin.list_filter` attribute. :attr:`~ModelAdmin.list_filter` attribute.
@ -1251,8 +1249,6 @@ templates used by the :class:`ModelAdmin` views:
.. method:: ModelAdmin.get_inline_instances(self, request, obj=None) .. method:: ModelAdmin.get_inline_instances(self, request, obj=None)
.. versionadded:: 1.5
The ``get_inline_instances`` method is given the ``HttpRequest`` and the The ``get_inline_instances`` method is given the ``HttpRequest`` and the
``obj`` being edited (or ``None`` on an add form) and is expected to return ``obj`` being edited (or ``None`` on an add form) and is expected to return
a ``list`` or ``tuple`` of :class:`~django.contrib.admin.InlineModelAdmin` a ``list`` or ``tuple`` of :class:`~django.contrib.admin.InlineModelAdmin`
@ -1506,10 +1502,6 @@ templates used by the :class:`ModelAdmin` views:
Sends a message to the user using the :mod:`django.contrib.messages` Sends a message to the user using the :mod:`django.contrib.messages`
backend. See the :ref:`custom ModelAdmin example <custom-admin-action>`. backend. See the :ref:`custom ModelAdmin example <custom-admin-action>`.
.. versionchanged:: 1.5
Keyword arguments were added in Django 1.5.
Keyword arguments allow you to change the message level, add extra CSS Keyword arguments allow you to change the message level, add extra CSS
tags, or fail silently if the ``contrib.messages`` framework is not tags, or fail silently if the ``contrib.messages`` framework is not
installed. These keyword arguments match those for installed. These keyword arguments match those for

View File

@ -215,11 +215,16 @@ Methods
(the Django app label). If the user is inactive, this method will (the Django app label). If the user is inactive, this method will
always return ``False``. always return ``False``.
.. method:: email_user(subject, message, from_email=None) .. method:: email_user(subject, message, from_email=None, **kwargs)
Sends an email to the user. If ``from_email`` is ``None``, Django uses Sends an email to the user. If ``from_email`` is ``None``, Django uses
the :setting:`DEFAULT_FROM_EMAIL`. the :setting:`DEFAULT_FROM_EMAIL`.
.. versionchanged:: 1.7
Any ``**kwargs`` are passed to the underlying
:meth:`~django.core.mail.send_mail()` call.
Manager methods Manager methods
--------------- ---------------
@ -384,8 +389,6 @@ can be used for notification when a user logs in or out.
.. function:: user_login_failed .. function:: user_login_failed
.. versionadded:: 1.5
Sent when the user failed to login successfully Sent when the user failed to login successfully
``sender`` ``sender``

View File

@ -199,14 +199,18 @@ The ``ContentTypeManager``
Takes either a model class or an instance of a model, and returns the Takes either a model class or an instance of a model, and returns the
:class:`~django.contrib.contenttypes.models.ContentType` instance :class:`~django.contrib.contenttypes.models.ContentType` instance
representing that model. representing that model. ``for_concrete_model=False`` allows fetching
the :class:`~django.contrib.contenttypes.models.ContentType` of a proxy
model.
.. method:: get_for_models(*models[, for_concrete_models=True]) .. method:: get_for_models(*models[, for_concrete_models=True])
Takes a variadic number of model classes, and returns a dictionary Takes a variadic number of model classes, and returns a dictionary
mapping the model classes to the mapping the model classes to the
:class:`~django.contrib.contenttypes.models.ContentType` instances :class:`~django.contrib.contenttypes.models.ContentType` instances
representing them. representing them. ``for_concrete_models=False`` allows fetching the
:class:`~django.contrib.contenttypes.models.ContentType` of proxy
models.
.. method:: get_by_natural_key(app_label, model) .. method:: get_by_natural_key(app_label, model)
@ -232,21 +236,6 @@ lookup::
.. _generic-relations: .. _generic-relations:
.. versionadded:: 1.5
Prior to Django 1.5,
:meth:`~django.contrib.contenttypes.models.ContentTypeManager.get_for_model` and
:meth:`~django.contrib.contenttypes.models.ContentTypeManager.get_for_models`
always returned the :class:`~django.contrib.contenttypes.models.ContentType`
associated with the concrete model of the specified one(s). That means there
was no way to retrieve the
:class:`~django.contrib.contenttypes.models.ContentType` of a proxy model
using those methods. As of Django 1.5 you can now pass a boolean flag
``for_concrete_model`` and ``for_concrete_models`` respectively to specify
wether or not you want to retrieve the
:class:`~django.contrib.contenttypes.models.ContentType` for the concrete or
direct model.
Generic relations Generic relations
================= =================

View File

@ -949,10 +949,6 @@ __ http://geohash.org/
*Availability*: PostGIS, SpatiaLite *Availability*: PostGIS, SpatiaLite
.. versionchanged:: 1.5
``geojson`` support for Spatialite > 3.0 has been added.
Attaches a ``geojson`` attribute to every model in the queryset that contains the Attaches a ``geojson`` attribute to every model in the queryset that contains the
`GeoJSON`__ representation of the geometry. `GeoJSON`__ representation of the geometry.

View File

@ -276,10 +276,6 @@ that the SRID value is not included in this representation
because it is not a part of the OGC specification (use the because it is not a part of the OGC specification (use the
:attr:`GEOSGeometry.hexewkb` property instead). :attr:`GEOSGeometry.hexewkb` property instead).
.. versionchanged:: 1.5
Prior to Django 1.5, the Z value of the geometry was dropped.
.. attribute:: GEOSGeometry.hexewkb .. attribute:: GEOSGeometry.hexewkb
Returns the EWKB of this Geometry in hexadecimal form. This is an Returns the EWKB of this Geometry in hexadecimal form. This is an
@ -325,10 +321,6 @@ Returns the WKB (Well-Known Binary) representation of this Geometry
as a Python buffer. SRID value is not included, use the as a Python buffer. SRID value is not included, use the
:attr:`GEOSGeometry.ewkb` property instead. :attr:`GEOSGeometry.ewkb` property instead.
.. versionchanged:: 1.5
Prior to Django 1.5, the Z value of the geometry was dropped.
.. _ewkb: .. _ewkb:
.. attribute:: GEOSGeometry.ewkb .. attribute:: GEOSGeometry.ewkb
@ -426,8 +418,6 @@ geometry that do not make up other.
.. method:: GEOSGeometry.interpolate(distance) .. method:: GEOSGeometry.interpolate(distance)
.. method:: GEOSGeometry.interpolate_normalized(distance) .. method:: GEOSGeometry.interpolate_normalized(distance)
.. versionadded:: 1.5
Given a distance (float), returns the point (or closest point) within the Given a distance (float), returns the point (or closest point) within the
geometry (:class:`LineString` or :class:`MultiLineString`) at that distance. geometry (:class:`LineString` or :class:`MultiLineString`) at that distance.
The normalized version takes the distance as a float between 0 (origin) and 1 The normalized version takes the distance as a float between 0 (origin) and 1
@ -443,8 +433,6 @@ geometry and other.
.. method:: GEOSGeometry.project(point) .. method:: GEOSGeometry.project(point)
.. method:: GEOSGeometry.project_normalized(point) .. method:: GEOSGeometry.project_normalized(point)
.. versionadded:: 1.5
Returns the distance (float) from the origin of the geometry Returns the distance (float) from the origin of the geometry
(:class:`LineString` or :class:`MultiLineString`) to the point projected on the (:class:`LineString` or :class:`MultiLineString`) to the point projected on the
geometry (that is to a point of the line the closest to the given point). geometry (that is to a point of the line the closest to the given point).

View File

@ -54,20 +54,21 @@ Storage backends
The messages framework can use different backends to store temporary messages. The messages framework can use different backends to store temporary messages.
Django provides three built-in storage classes: Django provides three built-in storage classes in
:mod:`django.contrib.messages`:
.. class:: django.contrib.messages.storage.session.SessionStorage .. class:: storage.session.SessionStorage
This class stores all messages inside of the request's session. Therefore This class stores all messages inside of the request's session. Therefore
it requires Django's ``contrib.sessions`` application. it requires Django's ``contrib.sessions`` application.
.. class:: django.contrib.messages.storage.cookie.CookieStorage .. class:: storage.cookie.CookieStorage
This class stores the message data in a cookie (signed with a secret hash This class stores the message data in a cookie (signed with a secret hash
to prevent manipulation) to persist notifications across requests. Old to prevent manipulation) to persist notifications across requests. Old
messages are dropped if the cookie data size would exceed 2048 bytes. messages are dropped if the cookie data size would exceed 2048 bytes.
.. class:: django.contrib.messages.storage.fallback.FallbackStorage .. class:: storage.fallback.FallbackStorage
This class first uses ``CookieStorage``, and falls back to using This class first uses ``CookieStorage``, and falls back to using
``SessionStorage`` for the messages that could not fit in a single cookie. ``SessionStorage`` for the messages that could not fit in a single cookie.
@ -83,6 +84,8 @@ path, for example::
MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage' MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage'
.. class:: storage.base.BaseStorage
To write your own storage class, subclass the ``BaseStorage`` class in To write your own storage class, subclass the ``BaseStorage`` class in
``django.contrib.messages.storage.base`` and implement the ``_get`` and ``django.contrib.messages.storage.base`` and implement the ``_get`` and
``_store`` methods. ``_store`` methods.

View File

@ -417,9 +417,10 @@ Here's how Django uses the sites framework:
:class:`~django.contrib.sites.models.Site` name to the template as :class:`~django.contrib.sites.models.Site` name to the template as
``{{ site_name }}``. ``{{ site_name }}``.
* The shortcut view (``django.views.defaults.shortcut``) uses the domain * The shortcut view (``django.contrib.contenttypes.views.shortcut``)
of the current :class:`~django.contrib.sites.models.Site` object when uses the domain of the current
calculating an object's URL. :class:`~django.contrib.sites.models.Site` object when calculating
an object's URL.
* In the admin framework, the "view on site" link uses the current * In the admin framework, the "view on site" link uses the current
:class:`~django.contrib.sites.models.Site` to work out the domain for the :class:`~django.contrib.sites.models.Site` to work out the domain for the

View File

@ -255,8 +255,6 @@ CachedStaticFilesStorage
.. method:: file_hash(name, content=None) .. method:: file_hash(name, content=None)
.. versionadded:: 1.5
The method that is used when creating the hashed name of a file. The method that is used when creating the hashed name of a file.
Needs to return a hash for the given file name and content. Needs to return a hash for the given file name and content.
By default it calculates a MD5 hash from the content's chunks as By default it calculates a MD5 hash from the content's chunks as
@ -290,8 +288,6 @@ The previous example is equal to calling the ``url`` method of an instance of
useful when using a non-local storage backend to deploy files as documented useful when using a non-local storage backend to deploy files as documented
in :ref:`staticfiles-from-cdn`. in :ref:`staticfiles-from-cdn`.
.. versionadded:: 1.5
If you'd like to retrieve a static URL without displaying it, you can use a If you'd like to retrieve a static URL without displaying it, you can use a
slightly different call: slightly different call:

View File

@ -191,10 +191,6 @@ Django supports MySQL 5.0.3 and higher.
`MySQL 5.0`_ adds the ``information_schema`` database, which contains detailed `MySQL 5.0`_ adds the ``information_schema`` database, which contains detailed
data on all database schema. Django's ``inspectdb`` feature uses it. data on all database schema. Django's ``inspectdb`` feature uses it.
.. versionchanged:: 1.5
The minimum version requirement of MySQL 5.0.3 was set in Django 1.5.
Django expects the database to support Unicode (UTF-8 encoding) and delegates to Django expects the database to support Unicode (UTF-8 encoding) and delegates to
it the task of enforcing transactions and referential integrity. It is important it the task of enforcing transactions and referential integrity. It is important
to be aware of the fact that the two latter ones aren't actually enforced by to be aware of the fact that the two latter ones aren't actually enforced by

View File

@ -254,8 +254,6 @@ to flush.
``--no-initial-data`` ``--no-initial-data``
~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~
.. versionadded:: 1.5
Use ``--no-initial-data`` to avoid loading the initial_data fixture. Use ``--no-initial-data`` to avoid loading the initial_data fixture.
@ -332,8 +330,6 @@ onto which the data will be loaded.
.. django-admin-option:: --ignorenonexistent .. django-admin-option:: --ignorenonexistent
.. versionadded:: 1.5
The :djadminopt:`--ignorenonexistent` option can be used to ignore fields that The :djadminopt:`--ignorenonexistent` option can be used to ignore fields that
may have been removed from models since the fixture was originally generated. may have been removed from models since the fixture was originally generated.
@ -903,10 +899,6 @@ behavior you can use the ``--no-startup`` option. e.g.::
django-admin.py shell --plain --no-startup django-admin.py shell --plain --no-startup
.. versionadded:: 1.5
The ``--interface`` option was added in Django 1.5.
.. versionadded:: 1.6 .. versionadded:: 1.6
The ``--no-startup`` option was added in Django 1.6. The ``--no-startup`` option was added in Django 1.6.
@ -1337,8 +1329,6 @@ clearsessions
.. django-admin:: clearsessions .. django-admin:: clearsessions
.. versionadded:: 1.5
Can be run as a cron job or directly to clean out expired sessions. Can be run as a cron job or directly to clean out expired sessions.
``django.contrib.sitemaps`` ``django.contrib.sitemaps``

View File

@ -100,10 +100,6 @@ The ``ContentFile`` Class
f1 = ContentFile("esta sentencia está en español") f1 = ContentFile("esta sentencia está en español")
f2 = ContentFile(b"these are bytes") f2 = ContentFile(b"these are bytes")
.. versionchanged:: 1.5
ContentFile also accepts Unicode strings.
.. currentmodule:: django.core.files.images .. currentmodule:: django.core.files.images
The ``ImageFile`` Class The ``ImageFile`` Class

View File

@ -211,11 +211,6 @@ only the valid fields::
>>> f.cleaned_data >>> f.cleaned_data
{'cc_myself': True, 'message': u'Hi there'} {'cc_myself': True, 'message': u'Hi there'}
.. versionchanged:: 1.5
Until Django 1.5, the ``cleaned_data`` attribute wasn't defined at all when
the ``Form`` didn't validate.
``cleaned_data`` will always *only* contain a key for fields defined in the ``cleaned_data`` will always *only* contain a key for fields defined in the
``Form``, even if you pass extra data when you define the ``Form``. In this ``Form``, even if you pass extra data when you define the ``Form``. In this
example, we pass a bunch of extra fields to the ``ContactForm`` constructor, example, we pass a bunch of extra fields to the ``ContactForm`` constructor,

View File

@ -573,16 +573,12 @@ For each field, we describe the default widget used if you don't specify
.. attribute:: allow_files .. attribute:: allow_files
.. versionadded:: 1.5
Optional. Either ``True`` or ``False``. Default is ``True``. Specifies Optional. Either ``True`` or ``False``. Default is ``True``. Specifies
whether files in the specified location should be included. Either this or whether files in the specified location should be included. Either this or
:attr:`allow_folders` must be ``True``. :attr:`allow_folders` must be ``True``.
.. attribute:: allow_folders .. attribute:: allow_folders
.. versionadded:: 1.5
Optional. Either ``True`` or ``False``. Default is ``False``. Specifies Optional. Either ``True`` or ``False``. Default is ``False``. Specifies
whether folders in the specified location should be included. Either this or whether folders in the specified location should be included. Either this or
:attr:`allow_files` must be ``True``. :attr:`allow_files` must be ``True``.
@ -1065,11 +1061,6 @@ objects (in the case of ``ModelMultipleChoiceField``) into the
* Error message keys: ``required``, ``list``, ``invalid_choice``, * Error message keys: ``required``, ``list``, ``invalid_choice``,
``invalid_pk_value`` ``invalid_pk_value``
.. versionchanged:: 1.5
The empty and normalized values were changed to be consistently
``QuerySets`` instead of ``[]`` and ``QuerySet`` respectively.
.. versionchanged:: 1.6 .. versionchanged:: 1.6
The ``invalid_choice`` message may contain ``%(value)s`` and the The ``invalid_choice`` message may contain ``%(value)s`` and the

View File

@ -450,11 +450,5 @@ entries in ``_errors``.
Secondly, once we have decided that the combined data in the two fields we are Secondly, once we have decided that the combined data in the two fields we are
considering aren't valid, we must remember to remove them from the considering aren't valid, we must remember to remove them from the
``cleaned_data``. ``cleaned_data``. `cleaned_data`` is present even if the form doesn't
validate, but it contains only field values that did validate.
.. versionchanged:: 1.5
Django used to remove the ``cleaned_data`` attribute entirely if there were
any errors in the form. Since version 1.5, ``cleaned_data`` is present even if
the form doesn't validate, but it contains only field values that did
validate.

View File

@ -525,11 +525,6 @@ Selector and checkbox widgets
A callable that takes the value of the CheckBoxInput and returns A callable that takes the value of the CheckBoxInput and returns
``True`` if the checkbox should be checked for that value. ``True`` if the checkbox should be checked for that value.
.. versionchanged:: 1.5
Exceptions from ``check_test`` used to be silenced by its caller,
this is no longer the case, they will propagate upwards.
``Select`` ``Select``
~~~~~~~~~~ ~~~~~~~~~~
@ -590,25 +585,26 @@ Selector and checkbox widgets
.. code-block:: html .. code-block:: html
<div class="myradio"> <div class="myradio">
<label><input type="radio" name="beatles" value="john" /> John</label> <label for="id_beatles_0"><input id="id_beatles_0" name="beatles" type="radio" value="john" /> John</label>
</div> </div>
<div class="myradio"> <div class="myradio">
<label><input type="radio" name="beatles" value="paul" /> Paul</label> <label for="id_beatles_1"><input id="id_beatles_1" name="beatles" type="radio" value="paul" /> Paul</label>
</div> </div>
<div class="myradio"> <div class="myradio">
<label><input type="radio" name="beatles" value="george" /> George</label> <label for="id_beatles_2"><input id="id_beatles_2" name="beatles" type="radio" value="george" /> George</label>
</div> </div>
<div class="myradio"> <div class="myradio">
<label><input type="radio" name="beatles" value="ringo" /> Ringo</label> <label for="id_beatles_3"><input id="id_beatles_3" name="beatles" type="radio" value="ringo" /> Ringo</label>
</div> </div>
That included the ``<label>`` tags. To get more granular, you can use each That included the ``<label>`` tags. To get more granular, you can use each
radio button's ``tag`` and ``choice_label`` attributes. For example, this template... radio button's ``tag``, ``choice_label`` and ``id_for_label`` attributes.
For example, this template...
.. code-block:: html+django .. code-block:: html+django
{% for radio in myform.beatles %} {% for radio in myform.beatles %}
<label> <label for="{{ radio.id_for_label }}">
{{ radio.choice_label }} {{ radio.choice_label }}
<span class="radio">{{ radio.tag }}</span> <span class="radio">{{ radio.tag }}</span>
</label> </label>
@ -618,31 +614,41 @@ Selector and checkbox widgets
.. code-block:: html .. code-block:: html
<label> <label for="id_beatles_0">
John John
<span class="radio"><input type="radio" name="beatles" value="john" /></span> <span class="radio"><input id="id_beatles_0" name="beatles" type="radio" value="john" /></span>
</label> </label>
<label>
Paul
<span class="radio"><input type="radio" name="beatles" value="paul" /></span>
</label>
<label>
George
<span class="radio"><input type="radio" name="beatles" value="george" /></span>
</label>
<label>
Ringo
<span class="radio"><input type="radio" name="beatles" value="ringo" /></span>
</label>
If you decide not to loop over the radio buttons -- e.g., if your template simply includes <label for="id_beatles_1">
``{{ myform.beatles }}`` -- they'll be output in a ``<ul>`` with ``<li>`` tags, as above. Paul
<span class="radio"><input id="id_beatles_1" name="beatles" type="radio" value="paul" /></span>
</label>
<label for="id_beatles_2">
George
<span class="radio"><input id="id_beatles_2" name="beatles" type="radio" value="george" /></span>
</label>
<label for="id_beatles_3">
Ringo
<span class="radio"><input id="id_beatles_3" name="beatles" type="radio" value="ringo" /></span>
</label>
If you decide not to loop over the radio buttons -- e.g., if your template
simply includes ``{{ myform.beatles }}`` -- they'll be output in a ``<ul>``
with ``<li>`` tags, as above.
.. versionchanged:: 1.6 .. versionchanged:: 1.6
The outer ``<ul>`` container will now receive the ``id`` attribute defined on The outer ``<ul>`` container will now receive the ``id`` attribute defined on
the widget. the widget.
.. versionchanged:: 1.7
When looping over the radio buttons, the ``label`` and ``input`` tags include
``for`` and ``id`` attributes, respectively. Each radio button has an
``id_for_label`` attribute to output the element's ID.
``CheckboxSelectMultiple`` ``CheckboxSelectMultiple``
~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -666,6 +672,12 @@ the widget.
Like :class:`RadioSelect`, you can now loop over the individual checkboxes making Like :class:`RadioSelect`, you can now loop over the individual checkboxes making
up the lists. See the documentation of :class:`RadioSelect` for more details. up the lists. See the documentation of :class:`RadioSelect` for more details.
.. versionchanged:: 1.7
When looping over the checkboxes, the ``label`` and ``input`` tags include
``for`` and ``id`` attributes, respectively. Each checkbox has an
``id_for_label`` attribute to output the element's ID.
.. _file-upload-widgets: .. _file-upload-widgets:
File upload widgets File upload widgets

View File

@ -757,21 +757,16 @@ directory on the filesystem. Has three special arguments, of which the first is
.. attribute:: FilePathField.allow_files .. attribute:: FilePathField.allow_files
.. versionadded:: 1.5
Optional. Either ``True`` or ``False``. Default is ``True``. Specifies Optional. Either ``True`` or ``False``. Default is ``True``. Specifies
whether files in the specified location should be included. Either this or whether files in the specified location should be included. Either this or
:attr:`~FilePathField.allow_folders` must be ``True``. :attr:`~FilePathField.allow_folders` must be ``True``.
.. attribute:: FilePathField.allow_folders .. attribute:: FilePathField.allow_folders
.. versionadded:: 1.5
Optional. Either ``True`` or ``False``. Default is ``False``. Specifies Optional. Either ``True`` or ``False``. Default is ``False``. Specifies
whether folders in the specified location should be included. Either this whether folders in the specified location should be included. Either this
or :attr:`~FilePathField.allow_files` must be ``True``. or :attr:`~FilePathField.allow_files` must be ``True``.
Of course, these arguments can be used together. Of course, these arguments can be used together.
The one potential gotcha is that :attr:`~FilePathField.match` applies to the The one potential gotcha is that :attr:`~FilePathField.match` applies to the
@ -980,12 +975,6 @@ Like all :class:`CharField` subclasses, :class:`URLField` takes the optional
:attr:`~CharField.max_length` argument. If you don't specify :attr:`~CharField.max_length` argument. If you don't specify
:attr:`~CharField.max_length`, a default of 200 is used. :attr:`~CharField.max_length`, a default of 200 is used.
.. versionadded:: 1.5
The current value of the field will be displayed as a clickable link above the
input widget.
Relationship fields Relationship fields
=================== ===================

View File

@ -388,8 +388,6 @@ For more details, see the documentation on :ref:`F() expressions
Specifying which fields to save Specifying which fields to save
------------------------------- -------------------------------
.. versionadded:: 1.5
If ``save()`` is passed a list of field names in keyword argument If ``save()`` is passed a list of field names in keyword argument
``update_fields``, only the fields named in that list will be updated. ``update_fields``, only the fields named in that list will be updated.
This may be desirable if you want to update just one or a few fields on This may be desirable if you want to update just one or a few fields on
@ -494,6 +492,40 @@ using ``__str__()`` like this::
# first_name and last_name will be unicode strings. # first_name and last_name will be unicode strings.
return force_bytes('%s %s' % (self.first_name, self.last_name)) return force_bytes('%s %s' % (self.first_name, self.last_name))
``__eq__``
----------
.. method:: Model.__eq__()
The equality method is defined such that instances with the same primary
key value and the same concrete class are considered equal. For proxy
models, concrete class is defined as the model's first non-proxy parent;
for all other models it is simply the model's class.
For example::
form django.db import models
class MyModel(models.Model):
id = models.AutoField(primary_key=True)
class MyProxyModel(MyModel):
class Meta:
proxy = True
class MultitableInherited(MyModel):
pass
MyModel(id=1) == MyModel(id=1)
MyModel(id=1) == MyProxyModel(id=1)
MyModel(id=1) != MultitableInherited(id=1)
MyModel(id=1) != MyModel(id=2)
.. versionchanged:: 1.7
In previous versions only instances of the exact same class and same
primary key value were considered equal.
``get_absolute_url`` ``get_absolute_url``
-------------------- --------------------

View File

@ -286,8 +286,6 @@ Django quotes column and table names behind the scenes.
.. attribute:: Options.index_together .. attribute:: Options.index_together
.. versionadded:: 1.5
Sets of field names that, taken together, are indexed:: Sets of field names that, taken together, are indexed::
index_together = [ index_together = [

View File

@ -913,7 +913,7 @@ needed by ``select_related``), it is able to detect that the ``best_pizza``
objects have already been fetched, and it will skip fetching them again. objects have already been fetched, and it will skip fetching them again.
Chaining ``prefetch_related`` calls will accumulate the lookups that are Chaining ``prefetch_related`` calls will accumulate the lookups that are
prefetched. To clear any ``prefetch_related`` behavior, pass `None` as a prefetched. To clear any ``prefetch_related`` behavior, pass ``None`` as a
parameter:: parameter::
>>> non_prefetched = qs.prefetch_related(None) >>> non_prefetched = qs.prefetch_related(None)
@ -1149,13 +1149,11 @@ to ``defer()``::
# Load all fields immediately. # Load all fields immediately.
my_queryset.defer(None) my_queryset.defer(None)
.. versionchanged:: 1.5 Some fields in a model won't be deferred, even if you ask for them. You can
never defer the loading of the primary key. If you are using
Some fields in a model won't be deferred, even if you ask for them. You can :meth:`select_related()` to retrieve related models, you shouldn't defer the
never defer the loading of the primary key. If you are using loading of the field that connects from the primary model to the related
:meth:`select_related()` to retrieve related models, you shouldn't defer the one, doing so will result in an error.
loading of the field that connects from the primary model to the related
one, doing so will result in an error.
.. note:: .. note::
@ -1178,8 +1176,6 @@ to ``defer()``::
reader, is slightly faster and consumes a little less memory in the Python reader, is slightly faster and consumes a little less memory in the Python
process. process.
.. versionchanged:: 1.5
.. note:: .. note::
When calling :meth:`~django.db.models.Model.save()` for instances with When calling :meth:`~django.db.models.Model.save()` for instances with
@ -1227,16 +1223,14 @@ All of the cautions in the note for the :meth:`defer` documentation apply to
``only()`` as well. Use it cautiously and only after exhausting your other ``only()`` as well. Use it cautiously and only after exhausting your other
options. options.
.. versionchanged:: 1.5 Using :meth:`only` and omitting a field requested using :meth:`select_related`
is an error as well.
Using :meth:`only` and omitting a field requested using .. note::
:meth:`select_related` is an error as well.
.. note:: When calling :meth:`~django.db.models.Model.save()` for instances with
deferred fields, only the loaded fields will be saved. See
When calling :meth:`~django.db.models.Model.save()` for instances with :meth:`~django.db.models.Model.save()` for more details.
deferred fields, only the loaded fields will be saved. See
:meth:`~django.db.models.Model.save()` for more details.
using using
~~~~~ ~~~~~
@ -1567,10 +1561,6 @@ The ``batch_size`` parameter controls how many objects are created in single
query. The default is to create all objects in one batch, except for SQLite query. The default is to create all objects in one batch, except for SQLite
where the default is such that at maximum 999 variables per query is used. where the default is such that at maximum 999 variables per query is used.
.. versionadded:: 1.5
The ``batch_size`` parameter was added in version 1.5.
count count
~~~~~ ~~~~~
@ -1900,10 +1890,6 @@ methods on your models. It does, however, emit the
:data:`~django.db.models.signals.post_delete` signals for all deleted objects :data:`~django.db.models.signals.post_delete` signals for all deleted objects
(including cascaded deletions). (including cascaded deletions).
.. versionadded:: 1.5
Allow fast-path deletion of objects.
Django needs to fetch objects into memory to send signals and handle cascades. Django needs to fetch objects into memory to send signals and handle cascades.
However, if there are no cascades and no signals, then Django may take a However, if there are no cascades and no signals, then Django may take a
fast-path and delete objects without fetching into memory. For large fast-path and delete objects without fetching into memory. For large
@ -1911,7 +1897,7 @@ deletes this can result in significantly reduced memory usage. The amount of
executed queries can be reduced, too. executed queries can be reduced, too.
ForeignKeys which are set to :attr:`~django.db.models.ForeignKey.on_delete` ForeignKeys which are set to :attr:`~django.db.models.ForeignKey.on_delete`
DO_NOTHING do not prevent taking the fast-path in deletion. ``DO_NOTHING`` do not prevent taking the fast-path in deletion.
Note that the queries generated in object deletion is an implementation Note that the queries generated in object deletion is an implementation
detail subject to change. detail subject to change.

View File

@ -93,10 +93,6 @@ All attributes should be considered read-only, unless stated otherwise below.
non-form data posted in the request, access this through the non-form data posted in the request, access this through the
:attr:`HttpRequest.body` attribute instead. :attr:`HttpRequest.body` attribute instead.
.. versionchanged:: 1.5
Before Django 1.5, HttpRequest.POST contained non-form data.
It's possible that a request can come in via POST with an empty ``POST`` It's possible that a request can come in via POST with an empty ``POST``
dictionary -- if, say, a form is requested via the POST HTTP method but dictionary -- if, say, a form is requested via the POST HTTP method but
does not include form data. Therefore, you shouldn't use ``if request.POST`` does not include form data. Therefore, you shouldn't use ``if request.POST``
@ -196,8 +192,6 @@ All attributes should be considered read-only, unless stated otherwise below.
.. attribute:: HttpRequest.resolver_match .. attribute:: HttpRequest.resolver_match
.. versionadded:: 1.5
An instance of :class:`~django.core.urlresolvers.ResolverMatch` representing An instance of :class:`~django.core.urlresolvers.ResolverMatch` representing
the resolved url. This attribute is only set after url resolving took place, the resolved url. This attribute is only set after url resolving took place,
which means it's available in all views but not in middleware methods which which means it's available in all views but not in middleware methods which
@ -824,8 +818,6 @@ types of HTTP responses. Like ``HttpResponse``, these subclasses live in
StreamingHttpResponse objects StreamingHttpResponse objects
============================= =============================
.. versionadded:: 1.5
.. class:: StreamingHttpResponse .. class:: StreamingHttpResponse
The :class:`StreamingHttpResponse` class is used to stream a response from The :class:`StreamingHttpResponse` class is used to stream a response from

View File

@ -1108,6 +1108,19 @@ Default: ``2621440`` (i.e. 2.5 MB).
The maximum size (in bytes) that an upload will be before it gets streamed to The maximum size (in bytes) that an upload will be before it gets streamed to
the file system. See :doc:`/topics/files` for details. the file system. See :doc:`/topics/files` for details.
.. setting:: FILE_UPLOAD_DIRECTORY_PERMISSIONS
FILE_UPLOAD_DIRECTORY_PERMISSIONS
---------------------------------
.. versionadded:: 1.7
Default: ``None``
The numeric mode to apply to directories created in the process of
uploading files. This value mirrors the functionality and caveats of
the :setting:`FILE_UPLOAD_PERMISSIONS` setting.
.. setting:: FILE_UPLOAD_PERMISSIONS .. setting:: FILE_UPLOAD_PERMISSIONS
FILE_UPLOAD_PERMISSIONS FILE_UPLOAD_PERMISSIONS
@ -1529,6 +1542,8 @@ unpredictable value.
:djadmin:`django-admin.py startproject <startproject>` automatically adds a :djadmin:`django-admin.py startproject <startproject>` automatically adds a
randomly-generated ``SECRET_KEY`` to each new project. randomly-generated ``SECRET_KEY`` to each new project.
Django will refuse to start if :setting:`SECRET_KEY` is not set.
.. warning:: .. warning::
**Keep this value secret.** **Keep this value secret.**
@ -1537,10 +1552,6 @@ randomly-generated ``SECRET_KEY`` to each new project.
security protections, and can lead to privilege escalation and remote code security protections, and can lead to privilege escalation and remote code
execution vulnerabilities. execution vulnerabilities.
.. versionchanged:: 1.5
Django will now refuse to start if :setting:`SECRET_KEY` is not set.
.. setting:: SECURE_PROXY_SSL_HEADER .. setting:: SECURE_PROXY_SSL_HEADER
SECURE_PROXY_SSL_HEADER SECURE_PROXY_SSL_HEADER
@ -2095,13 +2106,9 @@ The URL where requests are redirected after login when the
This is used by the :func:`~django.contrib.auth.decorators.login_required` This is used by the :func:`~django.contrib.auth.decorators.login_required`
decorator, for example. decorator, for example.
.. versionchanged:: 1.5 This setting also accepts view function names and :ref:`named URL patterns
<naming-url-patterns>` which can be used to reduce configuration duplication
This setting now also accepts view function names and since you don't have to define the URL in two places (``settings`` and URLconf).
:ref:`named URL patterns <naming-url-patterns>` which can be used to reduce
configuration duplication since you no longer have to define the URL in two
places (``settings`` and URLconf).
For backward compatibility reasons the default remains unchanged.
.. setting:: LOGIN_URL .. setting:: LOGIN_URL
@ -2113,13 +2120,9 @@ Default: ``'/accounts/login/'``
The URL where requests are redirected for login, especially when using the The URL where requests are redirected for login, especially when using the
:func:`~django.contrib.auth.decorators.login_required` decorator. :func:`~django.contrib.auth.decorators.login_required` decorator.
.. versionchanged:: 1.5 This setting also accepts view function names and :ref:`named URL patterns
<naming-url-patterns>` which can be used to reduce configuration duplication
This setting now also accepts view function names and since you don't have to define the URL in two places (``settings`` and URLconf).
:ref:`named URL patterns <naming-url-patterns>` which can be used to reduce
configuration duplication since you no longer have to define the URL in two
places (``settings`` and URLconf).
For backward compatibility reasons the default remains unchanged.
.. setting:: LOGOUT_URL .. setting:: LOGOUT_URL
@ -2249,7 +2252,9 @@ Controls where Django stores message data. Valid values are:
See :ref:`message storage backends <message-storage-backends>` for more details. See :ref:`message storage backends <message-storage-backends>` for more details.
The backends that use cookies -- ``CookieStorage`` and ``FallbackStorage`` -- The backends that use cookies --
:class:`~django.contrib.messages.storage.cookie.CookieStorage` and
:class:`~django.contrib.messages.storage.fallback.FallbackStorage` --
use the value of :setting:`SESSION_COOKIE_DOMAIN` when setting their cookies. use the value of :setting:`SESSION_COOKIE_DOMAIN` when setting their cookies.
.. setting:: MESSAGE_TAGS .. setting:: MESSAGE_TAGS

View File

@ -118,8 +118,6 @@ Arguments sent with this signal:
``using`` ``using``
The database alias being used. The database alias being used.
.. versionadded:: 1.5
``update_fields`` ``update_fields``
The set of fields to update explicitly specified in the ``save()`` method. The set of fields to update explicitly specified in the ``save()`` method.
``None`` if this argument was not used in the ``save()`` call. ``None`` if this argument was not used in the ``save()`` call.
@ -153,8 +151,6 @@ Arguments sent with this signal:
``using`` ``using``
The database alias being used. The database alias being used.
.. versionadded:: 1.5
``update_fields`` ``update_fields``
The set of fields to update explicitly specified in the ``save()`` method. The set of fields to update explicitly specified in the ``save()`` method.
``None`` if this argument was not used in the ``save()`` call. ``None`` if this argument was not used in the ``save()`` call.
@ -522,14 +518,7 @@ request_finished
.. data:: django.core.signals.request_finished .. data:: django.core.signals.request_finished
:module: :module:
Sent when Django finishes processing an HTTP request. Sent when Django finishes delvering an HTTP response to the client.
.. versionchanged:: 1.5
Before Django 1.5, this signal was sent before delivering content to the
client. In order to accommodate :ref:`streaming responses
<httpresponse-streaming>`, it is now sent after the response has been fully
delivered to the client.
.. note:: .. note::

View File

@ -75,16 +75,10 @@ Methods
The HTTP Status code for the response. The HTTP Status code for the response.
``content_type`` ``content_type``
The value included in the HTTP ``Content-Type`` header, including the
.. versionchanged:: 1.5 MIME type specification and the character set encoding. If
``content_type`` is specified, then its value is used. Otherwise,
Historically, this parameter was only called ``mimetype`` (now :setting:`DEFAULT_CONTENT_TYPE` is used.
deprecated), but since this is actually the value included in the HTTP
``Content-Type`` header, it can also include the character set
encoding, which makes it more than just a MIME type specification. If
``content_type`` is specified, then its value is used. Otherwise,
:setting:`DEFAULT_CONTENT_TYPE` is used.
.. method:: SimpleTemplateResponse.resolve_context(context) .. method:: SimpleTemplateResponse.resolve_context(context)
@ -167,13 +161,8 @@ Methods
The HTTP Status code for the response. The HTTP Status code for the response.
``content_type`` ``content_type``
The value included in the HTTP ``Content-Type`` header, including the
.. versionchanged:: 1.5 MIME type specification and the character set encoding. If
Historically, this parameter was only called ``mimetype`` (now
deprecated), but since this is actually the value included in the HTTP
``Content-Type`` header, it can also include the character set
encoding, which makes it more than just a MIME type specification. If
``content_type`` is specified, then its value is used. Otherwise, ``content_type`` is specified, then its value is used. Otherwise,
:setting:`DEFAULT_CONTENT_TYPE` is used. :setting:`DEFAULT_CONTENT_TYPE` is used.

View File

@ -274,11 +274,6 @@ Builtin variables
Every context contains ``True``, ``False`` and ``None``. As you would expect, Every context contains ``True``, ``False`` and ``None``. As you would expect,
these variables resolve to the corresponding Python objects. these variables resolve to the corresponding Python objects.
.. versionadded:: 1.5
Before Django 1.5, these variables weren't a special case, and they
resolved to ``None`` unless you defined them in the context.
Playing with Context objects Playing with Context objects
---------------------------- ----------------------------

View File

@ -1059,22 +1059,14 @@ by the context as to the current application.
.. warning:: .. warning::
Don't forget to put quotes around the function path or pattern name! Don't forget to put quotes around the function path or pattern name,
otherwise the value will be interpreted as a context variable!
.. versionchanged:: 1.5
The first parameter used not to be quoted, which was inconsistent with
other template tags. Since Django 1.5, it is evaluated according to
the usual rules: it can be a quoted string or a variable that will be
looked up in the context.
.. templatetag:: verbatim .. templatetag:: verbatim
verbatim verbatim
^^^^^^^^ ^^^^^^^^
.. versionadded:: 1.5
Stops the template engine from rendering the contents of this block tag. Stops the template engine from rendering the contents of this block tag.
A common use is to allow a Javascript template layer that collides with A common use is to allow a Javascript template layer that collides with
@ -1108,6 +1100,14 @@ If ``this_value`` is 175, ``max_value`` is 200, and ``max_width`` is 100, the
image in the above example will be 88 pixels wide image in the above example will be 88 pixels wide
(because 175/200 = .875; .875 * 100 = 87.5 which is rounded up to 88). (because 175/200 = .875; .875 * 100 = 87.5 which is rounded up to 88).
.. versionchanged:: 1.7
In some cases you might want to capture the result of ``widthratio`` in a
variable. It can be useful, for instance, in a :ttag:`blocktrans` like this::
{% widthratio this_value max_value max_width as width %}
{% blocktrans %}The width is: {{ width }}{% endblocktrans %}
.. templatetag:: with .. templatetag:: with
with with
@ -2403,8 +2403,6 @@ It is also able to consume standard context variables, e.g. assuming a
If you'd like to retrieve a static URL without displaying it, you can use a If you'd like to retrieve a static URL without displaying it, you can use a
slightly different call: slightly different call:
.. versionadded:: 1.5
.. code-block:: html+django .. code-block:: html+django
{% load static %} {% load static %}

View File

@ -45,28 +45,26 @@ rendering or anywhere else -- you have two choices for encoding those strings.
You can use Unicode strings, or you can use normal strings (sometimes called You can use Unicode strings, or you can use normal strings (sometimes called
"bytestrings") that are encoded using UTF-8. "bytestrings") that are encoded using UTF-8.
.. versionchanged:: 1.5 In Python 3, the logic is reversed, that is normal strings are Unicode, and
when you want to specifically create a bytestring, you have to prefix the
string with a 'b'. As we are doing in Django code from version 1.5,
we recommend that you import ``unicode_literals`` from the __future__ library
in your code. Then, when you specifically want to create a bytestring literal,
prefix the string with 'b'.
In Python 3, the logic is reversed, that is normal strings are Unicode, and Python 2 legacy::
when you want to specifically create a bytestring, you have to prefix the
string with a 'b'. As we are doing in Django code from version 1.5,
we recommend that you import ``unicode_literals`` from the __future__ library
in your code. Then, when you specifically want to create a bytestring literal,
prefix the string with 'b'.
Python 2 legacy:: my_string = "This is a bytestring"
my_unicode = u"This is an Unicode string"
my_string = "This is a bytestring" Python 2 with unicode literals or Python 3::
my_unicode = u"This is an Unicode string"
Python 2 with unicode literals or Python 3:: from __future__ import unicode_literals
from __future__ import unicode_literals my_string = b"This is a bytestring"
my_unicode = "This is an Unicode string"
my_string = b"This is a bytestring" See also :doc:`Python 3 compatibility </topics/python3>`.
my_unicode = "This is an Unicode string"
See also :doc:`Python 3 compatibility </topics/python3>`.
.. warning:: .. warning::

View File

@ -204,8 +204,6 @@ The functions defined in this module share the following properties:
.. function:: smart_text(s, encoding='utf-8', strings_only=False, errors='strict') .. function:: smart_text(s, encoding='utf-8', strings_only=False, errors='strict')
.. versionadded:: 1.5
Returns a text object representing ``s`` -- ``unicode`` on Python 2 and Returns a text object representing ``s`` -- ``unicode`` on Python 2 and
``str`` on Python 3. Treats bytestrings using the ``encoding`` codec. ``str`` on Python 3. Treats bytestrings using the ``encoding`` codec.
@ -225,8 +223,6 @@ The functions defined in this module share the following properties:
.. function:: force_text(s, encoding='utf-8', strings_only=False, errors='strict') .. function:: force_text(s, encoding='utf-8', strings_only=False, errors='strict')
.. versionadded:: 1.5
Similar to ``smart_text``, except that lazy instances are resolved to Similar to ``smart_text``, except that lazy instances are resolved to
strings, rather than kept as lazy objects. strings, rather than kept as lazy objects.
@ -239,8 +235,6 @@ The functions defined in this module share the following properties:
.. function:: smart_bytes(s, encoding='utf-8', strings_only=False, errors='strict') .. function:: smart_bytes(s, encoding='utf-8', strings_only=False, errors='strict')
.. versionadded:: 1.5
Returns a bytestring version of ``s``, encoded as specified in Returns a bytestring version of ``s``, encoded as specified in
``encoding``. ``encoding``.
@ -249,8 +243,6 @@ The functions defined in this module share the following properties:
.. function:: force_bytes(s, encoding='utf-8', strings_only=False, errors='strict') .. function:: force_bytes(s, encoding='utf-8', strings_only=False, errors='strict')
.. versionadded:: 1.5
Similar to ``smart_bytes``, except that lazy instances are resolved to Similar to ``smart_bytes``, except that lazy instances are resolved to
bytestrings, rather than kept as lazy objects. bytestrings, rather than kept as lazy objects.
@ -745,8 +737,6 @@ appropriate entities.
.. class:: SafeBytes .. class:: SafeBytes
.. versionadded:: 1.5
A ``bytes`` subclass that has been specifically marked as "safe" A ``bytes`` subclass that has been specifically marked as "safe"
(requires no further escaping) for HTML output purposes. (requires no further escaping) for HTML output purposes.
@ -758,8 +748,6 @@ appropriate entities.
.. class:: SafeText .. class:: SafeText
.. versionadded:: 1.5
A ``str`` (in Python 3) or ``unicode`` (in Python 2) subclass A ``str`` (in Python 3) or ``unicode`` (in Python 2) subclass
that has been specifically marked as "safe" for HTML output purposes. that has been specifically marked as "safe" for HTML output purposes.
@ -977,8 +965,6 @@ For a complete discussion on the usage of the following see the
``None``, the :ref:`current time zone <default-current-time-zone>` is unset ``None``, the :ref:`current time zone <default-current-time-zone>` is unset
on entry with :func:`deactivate()` instead. on entry with :func:`deactivate()` instead.
.. versionadded:: 1.5
.. function:: localtime(value, timezone=None) .. function:: localtime(value, timezone=None)
Converts an aware :class:`~datetime.datetime` to a different time zone, Converts an aware :class:`~datetime.datetime` to a different time zone,

11
docs/releases/1.3.3.txt Normal file
View File

@ -0,0 +1,11 @@
==========================
Django 1.3.3 release notes
==========================
*August 1, 2012*
Following Monday's security release of :doc:`Django 1.3.2 </releases/1.3.2>`,
we began receiving reports that one of the fixes applied was breaking Python
2.4 compatibility for Django 1.3. Since Python 2.4 is a supported Python
version for that release series, this release fixes compatibility with
Python 2.4.

37
docs/releases/1.3.4.txt Normal file
View File

@ -0,0 +1,37 @@
==========================
Django 1.3.4 release notes
==========================
*October 17, 2012*
This is the fourth release in the Django 1.3 series.
Host header poisoning
---------------------
Some parts of Django -- independent of end-user-written applications -- make
use of full URLs, including domain name, which are generated from the HTTP Host
header. Some attacks against this are beyond Django's ability to control, and
require the web server to be properly configured; Django's documentation has
for some time contained notes advising users on such configuration.
Django's own built-in parsing of the Host header is, however, still vulnerable,
as was reported to us recently. The Host header parsing in Django 1.3.3 and
Django 1.4.1 -- specifically, ``django.http.HttpRequest.get_host()`` -- was
incorrectly handling username/password information in the header. Thus, for
example, the following Host header would be accepted by Django when running on
"validsite.com"::
Host: validsite.com:random@evilsite.com
Using this, an attacker can cause parts of Django -- particularly the
password-reset mechanism -- to generate and display arbitrary URLs to users.
To remedy this, the parsing in ``HttpRequest.get_host()`` is being modified;
Host headers which contain potentially dangerous content (such as
username/password pairs) now raise the exception
:exc:`django.core.exceptions.SuspiciousOperation`.
Details of this issue were initially posted online as a `security advisory`_.
.. _security advisory: https://www.djangoproject.com/weblog/2012/oct/17/security/

60
docs/releases/1.3.5.txt Normal file
View File

@ -0,0 +1,60 @@
==========================
Django 1.3.5 release notes
==========================
*December 10, 2012*
Django 1.3.5 addresses two security issues present in previous Django releases
in the 1.3 series.
Please be aware that this security release is slightly different from previous
ones. Both issues addressed here have been dealt with in prior security updates
to Django. In one case, we have received ongoing reports of problems, and in
the other we've chosen to take further steps to tighten up Django's code in
response to independent discovery of potential problems from multiple sources.
Host header poisoning
---------------------
Several earlier Django security releases focused on the issue of poisoning the
HTTP Host header, causing Django to generate URLs pointing to arbitrary,
potentially-malicious domains.
In response to further input received and reports of continuing issues
following the previous release, we're taking additional steps to tighten Host
header validation. Rather than attempt to accommodate all features HTTP
supports here, Django's Host header validation attempts to support a smaller,
but far more common, subset:
* Hostnames must consist of characters [A-Za-z0-9] plus hyphen ('-') or dot
('.').
* IP addresses -- both IPv4 and IPv6 -- are permitted.
* Port, if specified, is numeric.
Any deviation from this will now be rejected, raising the exception
:exc:`django.core.exceptions.SuspiciousOperation`.
Redirect poisoning
------------------
Also following up on a previous issue: in July of this year, we made changes to
Django's HTTP redirect classes, performing additional validation of the scheme
of the URL to redirect to (since, both within Django's own supplied
applications and many third-party applications, accepting a user-supplied
redirect target is a common pattern).
Since then, two independent audits of the code turned up further potential
problems. So, similar to the Host-header issue, we are taking steps to provide
tighter validation in response to reported problems (primarily with third-party
applications, but to a certain extent also within Django itself). This comes in
two parts:
1. A new utility function, ``django.utils.http.is_safe_url``, is added; this
function takes a URL and a hostname, and checks that the URL is either
relative, or if absolute matches the supplied hostname. This function is
intended for use whenever user-supplied redirect targets are accepted, to
ensure that such redirects cannot lead to arbitrary third-party sites.
2. All of Django's own built-in views -- primarily in the authentication system
-- which allow user-supplied redirect targets now use ``is_safe_url`` to
validate the supplied URL.

78
docs/releases/1.3.6.txt Normal file
View File

@ -0,0 +1,78 @@
==========================
Django 1.3.6 release notes
==========================
*February 19, 2013*
Django 1.3.6 fixes four security issues present in previous Django releases in
the 1.3 series.
This is the sixth bugfix/security release in the Django 1.3 series.
Host header poisoning
---------------------
Some parts of Django -- independent of end-user-written applications -- make
use of full URLs, including domain name, which are generated from the HTTP Host
header. Django's documentation has for some time contained notes advising users
on how to configure webservers to ensure that only valid Host headers can reach
the Django application. However, it has been reported to us that even with the
recommended webserver configurations there are still techniques available for
tricking many common webservers into supplying the application with an
incorrect and possibly malicious Host header.
For this reason, Django 1.3.6 adds a new setting, ``ALLOWED_HOSTS``, which
should contain an explicit list of valid host/domain names for this site. A
request with a Host header not matching an entry in this list will raise
``SuspiciousOperation`` if ``request.get_host()`` is called. For full details
see the documentation for the :setting:`ALLOWED_HOSTS` setting.
The default value for this setting in Django 1.3.6 is ``['*']`` (matching any
host), for backwards-compatibility, but we strongly encourage all sites to set
a more restrictive value.
This host validation is disabled when ``DEBUG`` is ``True`` or when running tests.
XML deserialization
-------------------
The XML parser in the Python standard library is vulnerable to a number of
attacks via external entities and entity expansion. Django uses this parser for
deserializing XML-formatted database fixtures. The fixture deserializer is not
intended for use with untrusted data, but in order to err on the side of safety
in Django 1.3.6 the XML deserializer refuses to parse an XML document with a
DTD (DOCTYPE definition), which closes off these attack avenues.
These issues in the Python standard library are CVE-2013-1664 and
CVE-2013-1665. More information available `from the Python security team`_.
Django's XML serializer does not create documents with a DTD, so this should
not cause any issues with the typical round-trip from ``dumpdata`` to
``loaddata``, but if you feed your own XML documents to the ``loaddata``
management command, you will need to ensure they do not contain a DTD.
.. _from the Python security team: http://blog.python.org/2013/02/announcing-defusedxml-fixes-for-xml.html
Formset memory exhaustion
-------------------------
Previous versions of Django did not validate or limit the form-count data
provided by the client in a formset's management form, making it possible to
exhaust a server's available memory by forcing it to create very large numbers
of forms.
In Django 1.3.6, all formsets have a strictly-enforced maximum number of forms
(1000 by default, though it can be set higher via the ``max_num`` formset
factory argument).
Admin history view information leakage
--------------------------------------
In previous versions of Django, an admin user without change permission on a
model could still view the unicode representation of instances via their admin
history log. Django 1.3.6 now limits the admin history log view for an object
to users with change permission for that model.

13
docs/releases/1.3.7.txt Normal file
View File

@ -0,0 +1,13 @@
==========================
Django 1.3.7 release notes
==========================
*February 20, 2013*
Django 1.3.7 corrects a packaging problem with yesterday's :doc:`1.3.6 release
</releases/1.3.6>`.
The release contained stray ``.pyc`` files that caused "bad magic number"
errors when running with some versions of Python. This releases corrects this,
and also fixes a bad documentation link in the project template ``settings.py``
file generated by ``manage.py startproject``.

View File

@ -17,7 +17,7 @@ for some time contained notes advising users on such configuration.
Django's own built-in parsing of the Host header is, however, still vulnerable, Django's own built-in parsing of the Host header is, however, still vulnerable,
as was reported to us recently. The Host header parsing in Django 1.3.3 and as was reported to us recently. The Host header parsing in Django 1.3.3 and
Django 1.4.1 -- specifically, django.http.HttpRequest.get_host() -- was Django 1.4.1 -- specifically, ``django.http.HttpRequest.get_host()`` -- was
incorrectly handling username/password information in the header. Thus, for incorrectly handling username/password information in the header. Thus, for
example, the following Host header would be accepted by Django when running on example, the following Host header would be accepted by Django when running on
"validsite.com":: "validsite.com"::
@ -27,9 +27,10 @@ example, the following Host header would be accepted by Django when running on
Using this, an attacker can cause parts of Django -- particularly the Using this, an attacker can cause parts of Django -- particularly the
password-reset mechanism -- to generate and display arbitrary URLs to users. password-reset mechanism -- to generate and display arbitrary URLs to users.
To remedy this, the parsing in HttpRequest.get_host() is being modified; Host To remedy this, the parsing in ``HttpRequest.get_host()`` is being modified;
headers which contain potentially dangerous content (such as username/password Host headers which contain potentially dangerous content (such as
pairs) now raise the exception django.core.exceptions.SuspiciousOperation username/password pairs) now raise the exception
:exc:`django.core.exceptions.SuspiciousOperation`.
Details of this issue were initially posted online as a `security advisory`_. Details of this issue were initially posted online as a `security advisory`_.

60
docs/releases/1.4.3.txt Normal file
View File

@ -0,0 +1,60 @@
==========================
Django 1.4.3 release notes
==========================
*December 10, 2012*
Django 1.4.3 addresses two security issues present in previous Django releases
in the 1.4 series.
Please be aware that this security release is slightly different from previous
ones. Both issues addressed here have been dealt with in prior security updates
to Django. In one case, we have received ongoing reports of problems, and in
the other we've chosen to take further steps to tighten up Django's code in
response to independent discovery of potential problems from multiple sources.
Host header poisoning
---------------------
Several earlier Django security releases focused on the issue of poisoning the
HTTP Host header, causing Django to generate URLs pointing to arbitrary,
potentially-malicious domains.
In response to further input received and reports of continuing issues
following the previous release, we're taking additional steps to tighten Host
header validation. Rather than attempt to accommodate all features HTTP
supports here, Django's Host header validation attempts to support a smaller,
but far more common, subset:
* Hostnames must consist of characters [A-Za-z0-9] plus hyphen ('-') or dot
('.').
* IP addresses -- both IPv4 and IPv6 -- are permitted.
* Port, if specified, is numeric.
Any deviation from this will now be rejected, raising the exception
:exc:`django.core.exceptions.SuspiciousOperation`.
Redirect poisoning
------------------
Also following up on a previous issue: in July of this year, we made changes to
Django's HTTP redirect classes, performing additional validation of the scheme
of the URL to redirect to (since, both within Django's own supplied
applications and many third-party applications, accepting a user-supplied
redirect target is a common pattern).
Since then, two independent audits of the code turned up further potential
problems. So, similar to the Host-header issue, we are taking steps to provide
tighter validation in response to reported problems (primarily with third-party
applications, but to a certain extent also within Django itself). This comes in
two parts:
1. A new utility function, ``django.utils.http.is_safe_url``, is added; this
function takes a URL and a hostname, and checks that the URL is either
relative, or if absolute matches the supplied hostname. This function is
intended for use whenever user-supplied redirect targets are accepted, to
ensure that such redirects cannot lead to arbitrary third-party sites.
2. All of Django's own built-in views -- primarily in the authentication system
-- which allow user-supplied redirect targets now use ``is_safe_url`` to
validate the supplied URL.

88
docs/releases/1.4.4.txt Normal file
View File

@ -0,0 +1,88 @@
==========================
Django 1.4.4 release notes
==========================
*February 19, 2013*
Django 1.4.4 fixes four security issues present in previous Django releases in
the 1.4 series, as well as several other bugs and numerous documentation
improvements.
This is the fourth bugfix/security release in the Django 1.4 series.
Host header poisoning
---------------------
Some parts of Django -- independent of end-user-written applications -- make
use of full URLs, including domain name, which are generated from the HTTP Host
header. Django's documentation has for some time contained notes advising users
on how to configure webservers to ensure that only valid Host headers can reach
the Django application. However, it has been reported to us that even with the
recommended webserver configurations there are still techniques available for
tricking many common webservers into supplying the application with an
incorrect and possibly malicious Host header.
For this reason, Django 1.4.4 adds a new setting, ``ALLOWED_HOSTS``, containing
an explicit list of valid host/domain names for this site. A request with a
Host header not matching an entry in this list will raise
``SuspiciousOperation`` if ``request.get_host()`` is called. For full details
see the documentation for the :setting:`ALLOWED_HOSTS` setting.
The default value for this setting in Django 1.4.4 is ``['*']`` (matching any
host), for backwards-compatibility, but we strongly encourage all sites to set
a more restrictive value.
This host validation is disabled when ``DEBUG`` is ``True`` or when running tests.
XML deserialization
-------------------
The XML parser in the Python standard library is vulnerable to a number of
attacks via external entities and entity expansion. Django uses this parser for
deserializing XML-formatted database fixtures. This deserializer is not
intended for use with untrusted data, but in order to err on the side of safety
in Django 1.4.4 the XML deserializer refuses to parse an XML document with a
DTD (DOCTYPE definition), which closes off these attack avenues.
These issues in the Python standard library are CVE-2013-1664 and
CVE-2013-1665. More information available `from the Python security team`_.
Django's XML serializer does not create documents with a DTD, so this should
not cause any issues with the typical round-trip from ``dumpdata`` to
``loaddata``, but if you feed your own XML documents to the ``loaddata``
management command, you will need to ensure they do not contain a DTD.
.. _from the Python security team: http://blog.python.org/2013/02/announcing-defusedxml-fixes-for-xml.html
Formset memory exhaustion
-------------------------
Previous versions of Django did not validate or limit the form-count data
provided by the client in a formset's management form, making it possible to
exhaust a server's available memory by forcing it to create very large numbers
of forms.
In Django 1.4.4, all formsets have a strictly-enforced maximum number of forms
(1000 by default, though it can be set higher via the ``max_num`` formset
factory argument).
Admin history view information leakage
--------------------------------------
In previous versions of Django, an admin user without change permission on a
model could still view the unicode representation of instances via their admin
history log. Django 1.4.4 now limits the admin history log view for an object
to users with change permission for that model.
Other bugfixes and changes
==========================
* Prevented transaction state from leaking from one request to the next (#19707).
* Changed a SQL command syntax to be MySQL 4 compatible (#19702).
* Added backwards-compatibility with old unsalted MD5 passwords (#18144).
* Numerous documentation improvements and fixes.

13
docs/releases/1.4.5.txt Normal file
View File

@ -0,0 +1,13 @@
==========================
Django 1.4.5 release notes
==========================
*February 20, 2013*
Django 1.4.5 corrects a packaging problem with yesterday's :doc:`1.4.4 release
</releases/1.4.4>`.
The release contained stray ``.pyc`` files that caused "bad magic number"
errors when running with some versions of Python. This releases corrects this,
and also fixes a bad documentation link in the project template ``settings.py``
file generated by ``manage.py startproject``.

31
docs/releases/1.4.6.txt Normal file
View File

@ -0,0 +1,31 @@
==========================
Django 1.4.6 release notes
==========================
*August 13, 2013*
Django 1.4.6 fixes one security issue present in previous Django releases in
the 1.4 series, as well as one other bug.
This is the sixth bugfix/security release in the Django 1.4 series.
Mitigated possible XSS attack via user-supplied redirect URLs
-------------------------------------------------------------
Django relies on user input in some cases (e.g.
:func:`django.contrib.auth.views.login`, :mod:`django.contrib.comments`, and
:doc:`i18n </topics/i18n/index>`) to redirect the user to an "on success" URL.
The security checks for these redirects (namely
``django.util.http.is_safe_url()``) didn't check if the scheme is ``http(s)``
and as such allowed ``javascript:...`` URLs to be entered. If a developer
relied on ``is_safe_url()`` to provide safe redirect targets and put such a
URL into a link, he could suffer from a XSS attack. This bug doesn't affect
Django currently, since we only put this URL into the ``Location`` response
header and browsers seem to ignore JavaScript there.
Bugfixes
========
* Fixed an obscure bug with the :func:`~django.test.utils.override_settings`
decorator. If you hit an ``AttributeError: 'Settings' object has no attribute
'_original_allowed_hosts'`` exception, it's probably fixed (#20636).

62
docs/releases/1.5.2.txt Normal file
View File

@ -0,0 +1,62 @@
==========================
Django 1.5.2 release notes
==========================
*August 13, 2013*
This is Django 1.5.2, a bugfix and security release for Django 1.5.
Mitigated possible XSS attack via user-supplied redirect URLs
-------------------------------------------------------------
Django relies on user input in some cases (e.g.
:func:`django.contrib.auth.views.login`, :mod:`django.contrib.comments`, and
:doc:`i18n </topics/i18n/index>`) to redirect the user to an "on success" URL.
The security checks for these redirects (namely
``django.util.http.is_safe_url()``) didn't check if the scheme is ``http(s)``
and as such allowed ``javascript:...`` URLs to be entered. If a developer
relied on ``is_safe_url()`` to provide safe redirect targets and put such a
URL into a link, he could suffer from a XSS attack. This bug doesn't affect
Django currently, since we only put this URL into the ``Location`` response
header and browsers seem to ignore JavaScript there.
XSS vulnerability in :mod:`django.contrib.admin`
------------------------------------------------
If a :class:`~django.db.models.URLField` is used in Django 1.5, it displays the
current value of the field and a link to the target on the admin change page.
The display routine of this widget was flawed and allowed for XSS.
Bugfixes
========
* Fixed a crash with :meth:`~django.db.models.query.QuerySet.prefetch_related`
(#19607) as well as some ``pickle`` regressions with ``prefetch_related``
(#20157 and #20257).
* Fixed a regression in :mod:`django.contrib.gis` in the Google Map output on
Python 3 (#20773).
* Made ``DjangoTestSuiteRunner.setup_databases`` properly handle aliases for
the default database (#19940) and prevented ``teardown_databases`` from
attempting to tear down aliases (#20681).
* Fixed the ``django.core.cache.backends.memcached.MemcachedCache`` backend's
``get_many()`` method on Python 3 (#20722).
* Fixed :mod:`django.contrib.humanize` translation syntax errors. Affected
languages: Mexican Spanish, Mongolian, Romanian, Turkish (#20695).
* Added support for wheel packages (#19252).
* The CSRF token now rotates when a user logs in.
* Some Python 3 compatibility fixes including #20212 and #20025.
* Fixed some rare cases where :meth:`~django.db.models.query.QuerySet.get`
exceptions recursed infinitely (#20278).
* :djadmin:`makemessages` no longer crashes with ``UnicodeDecodeError``
(#20354).
* Fixed ``geojson`` detection with Spatialite.
* :meth:`~django.test.SimpleTestCase.assertContains` once again works with
binary content (#20237).
* Fixed :class:`~django.db.models.ManyToManyField` if it has a unicode ``name``
parameter (#20207).
* Ensured that the WSGI request's path is correctly based on the
``SCRIPT_NAME`` environment variable or the :setting:`FORCE_SCRIPT_NAME`
setting, regardless of whether or not either has a trailing slash (#20169).
* Fixed an obscure bug with the :func:`~django.test.utils.override_settings`
decorator. If you hit an ``AttributeError: 'Settings' object has no attribute
'_original_allowed_hosts'`` exception, it's probably fixed (#20636).

View File

@ -2,6 +2,8 @@
Django 1.5 release notes Django 1.5 release notes
======================== ========================
*February 26, 2013*
Welcome to Django 1.5! Welcome to Django 1.5!
These release notes cover the `new features`_, as well These release notes cover the `new features`_, as well

View File

@ -130,7 +130,7 @@ Minor features
context level. context level.
* The :class:`~django.utils.feedgenerator.Atom1Feed` syndication feed's * The :class:`~django.utils.feedgenerator.Atom1Feed` syndication feed's
``updated`` element now utilizes `updateddate` instead of ``pubdate``, ``updated`` element now utilizes ``updateddate`` instead of ``pubdate``,
allowing the ``published`` element to be included in the feed (which allowing the ``published`` element to be included in the feed (which
relies on ``pubdate``). relies on ``pubdate``).
@ -190,6 +190,30 @@ Minor features
``Meta`` option allows you to customize (or disable) creation of the default ``Meta`` option allows you to customize (or disable) creation of the default
add, change, and delete permissions. add, change, and delete permissions.
* The :func:`~django.contrib.auth.decorators.permission_required` decorator can
take a list of permissions as well as a single permission.
* The new :setting:`FILE_UPLOAD_DIRECTORY_PERMISSIONS` setting controls
the file system permissions of directories created during file upload, like
:setting:`FILE_UPLOAD_PERMISSIONS` does for the files themselves.
* Explicit :class:`~django.db.models.OneToOneField` for
:ref:`multi-table-inheritance` are now discovered in abstract classes.
* The ``<label>`` and ``<input>`` tags rendered by
:class:`~django.forms.RadioSelect` and
:class:`~django.forms.CheckboxSelectMultiple` when looping over the radio
buttons or checkboxes now include ``for`` and ``id`` attributes, respectively.
Each radio button or checkbox includes an ``id_for_label`` attribute to
output the element's ID.
* Any ``**kwargs`` passed to
:meth:`~django.contrib.auth.models.User.email_user()` are passed to the
underlying :meth:`~django.core.mail.send_mail()` call.
* The :ttag:`widthratio` template tag now accepts an "as" parameter to capture
the result in a variable.
Backwards incompatible changes in 1.7 Backwards incompatible changes in 1.7
===================================== =====================================
@ -237,6 +261,11 @@ Miscellaneous
removes the ability for visitors to generate spurious HTTP 500 errors by removes the ability for visitors to generate spurious HTTP 500 errors by
requesting static files that don't exist or haven't been collected yet. requesting static files that don't exist or haven't been collected yet.
* The :meth:`django.db.models.Model.__eq__` method is now defined in a
way where instances of a proxy model and its base model are considered
equal when primary keys match. Previously only instances of exact same
class were considered equal on primary key match.
Features deprecated in 1.7 Features deprecated in 1.7
========================== ==========================

View File

@ -36,6 +36,7 @@ Final releases
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1
1.5.2
1.5.1 1.5.1
1.5 1.5
@ -44,6 +45,10 @@ Final releases
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2 1.4.2
1.4.1 1.4.1
1.4 1.4
@ -53,6 +58,11 @@ Final releases
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1
1.3.7
1.3.6
1.3.5
1.3.4
1.3.3
1.3.2 1.3.2
1.3.1 1.3.1
1.3 1.3

View File

@ -359,8 +359,6 @@ the extra database load.
Substituting a custom User model Substituting a custom User model
================================ ================================
.. versionadded:: 1.5
Some kinds of projects may have authentication requirements for which Django's Some kinds of projects may have authentication requirements for which Django's
built-in :class:`~django.contrib.auth.models.User` model is not always built-in :class:`~django.contrib.auth.models.User` model is not always
appropriate. For instance, on some sites it makes more sense to use an email appropriate. For instance, on some sites it makes more sense to use an email
@ -684,13 +682,13 @@ auth views.
* :class:`~django.contrib.auth.forms.AuthenticationForm` * :class:`~django.contrib.auth.forms.AuthenticationForm`
Works with any subclass of :class:`~django.contrib.auth.models.AbstractBaseUser`, Works with any subclass of :class:`~django.contrib.auth.models.AbstractBaseUser`,
and will adapt to use the field defined in `USERNAME_FIELD`. and will adapt to use the field defined in ``USERNAME_FIELD``.
* :class:`~django.contrib.auth.forms.PasswordResetForm` * :class:`~django.contrib.auth.forms.PasswordResetForm`
Assumes that the user model has an integer primary key, has a field named Assumes that the user model has an integer primary key, has a field named
``email`` that can be used to identify the user, and a boolean field ``email`` that can be used to identify the user, and a boolean field
named `is_active` to prevent password resets for inactive users. named ``is_active`` to prevent password resets for inactive users.
* :class:`~django.contrib.auth.forms.SetPasswordForm` * :class:`~django.contrib.auth.forms.SetPasswordForm`

View File

@ -434,12 +434,10 @@ The login_required decorator
(r'^accounts/login/$', 'django.contrib.auth.views.login'), (r'^accounts/login/$', 'django.contrib.auth.views.login'),
.. versionchanged:: 1.5 The :setting:`settings.LOGIN_URL <LOGIN_URL>` also accepts view function
names and :ref:`named URL patterns <naming-url-patterns>`. This allows you
The :setting:`settings.LOGIN_URL <LOGIN_URL>` also accepts to freely remap your login view within your URLconf without having to
view function names and :ref:`named URL patterns <naming-url-patterns>`. update the setting.
This allows you to freely remap your login view within your URLconf
without having to update the setting.
.. note:: .. note::
@ -528,6 +526,11 @@ The permission_required decorator
(HTTP Forbidden) view<http_forbidden_view>` instead of redirecting to the (HTTP Forbidden) view<http_forbidden_view>` instead of redirecting to the
login page. login page.
.. versionchanged:: 1.7
The :func:`~django.contrib.auth.decorators.permission_required`
decorator can take a list of permissions as well as a single permission.
Applying permissions to generic views Applying permissions to generic views
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -1182,10 +1185,6 @@ Thus, you can check permissions in template ``{% if %}`` statements:
<p>You don't have permission to do anything in the foo app.</p> <p>You don't have permission to do anything in the foo app.</p>
{% endif %} {% endif %}
.. versionadded:: 1.5
Permission lookup by "if in".
It is possible to also look permissions up by ``{% if in %}`` statements. It is possible to also look permissions up by ``{% if in %}`` statements.
For example: For example:

View File

@ -198,6 +198,7 @@ A similar class-based view might look like::
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.shortcuts import render from django.shortcuts import render
from django.views.generic.base import View
from .forms import MyForm from .forms import MyForm

View File

@ -55,8 +55,6 @@ interface to working with templates in class-based views.
override this to provide more flexible defaults when dealing with actual override this to provide more flexible defaults when dealing with actual
objects. objects.
.. versionadded:: 1.5
:class:`~django.views.generic.base.ContextMixin` :class:`~django.views.generic.base.ContextMixin`
Every built in view which needs context data, such as for rendering a Every built in view which needs context data, such as for rendering a
template (including ``TemplateResponseMixin`` above), should call template (including ``TemplateResponseMixin`` above), should call

View File

@ -265,8 +265,8 @@ Methods are copied according to the following rules:
- Public methods are copied by default. - Public methods are copied by default.
- Private methods (starting with an underscore) are not copied by default. - Private methods (starting with an underscore) are not copied by default.
- Methods with a `queryset_only` attribute set to `False` are always copied. - Methods with a ``queryset_only`` attribute set to ``False`` are always copied.
- Methods with a `queryset_only` attribute set to `True` are never copied. - Methods with a ``queryset_only`` attribute set to ``True`` are never copied.
For example:: For example::

View File

@ -689,11 +689,6 @@ In addition, some objects are automatically created just after
- three ``Permission`` for each model (including those not stored in that - three ``Permission`` for each model (including those not stored in that
database). database).
.. versionchanged:: 1.5
Previously, ``ContentType`` and ``Permission`` instances were created only
in the default database.
For common setups with multiple databases, it isn't useful to have these For common setups with multiple databases, it isn't useful to have these
objects in more than one database. Common setups include master / slave and objects in more than one database. Common setups include master / slave and
connecting to external databases. Therefore, it's recommended: connecting to external databases. Therefore, it's recommended:

View File

@ -639,20 +639,11 @@ that were modified more than 3 days after they were published::
>>> from datetime import timedelta >>> from datetime import timedelta
>>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3)) >>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))
.. versionadded:: 1.5 The ``F()`` objects support bitwise operations by ``.bitand()`` and
``.bitand()`` and ``.bitor()``
The ``F()`` objects now support bitwise operations by ``.bitand()`` and
``.bitor()``, for example:: ``.bitor()``, for example::
>>> F('somefield').bitand(16) >>> F('somefield').bitand(16)
.. versionchanged:: 1.5
The previously undocumented operators ``&`` and ``|`` no longer produce
bitwise operations, use ``.bitand()`` and ``.bitor()`` instead.
The pk lookup shortcut The pk lookup shortcut
---------------------- ----------------------

View File

@ -132,7 +132,7 @@ upload behavior.
Changing upload handler behavior Changing upload handler behavior
-------------------------------- --------------------------------
Three settings control Django's file upload behavior: There are a few settings which control Django's file upload behavior:
:setting:`FILE_UPLOAD_MAX_MEMORY_SIZE` :setting:`FILE_UPLOAD_MAX_MEMORY_SIZE`
The maximum size, in bytes, for files that will be uploaded into memory. The maximum size, in bytes, for files that will be uploaded into memory.
@ -167,6 +167,11 @@ Three settings control Django's file upload behavior:
**Always prefix the mode with a 0.** **Always prefix the mode with a 0.**
:setting:`FILE_UPLOAD_DIRECTORY_PERMISSIONS`
The numeric mode to apply to directories created in the process of
uploading files. This value mirrors the functionality and caveats of
the :setting:`FILE_UPLOAD_PERMISSIONS` setting.
:setting:`FILE_UPLOAD_HANDLERS` :setting:`FILE_UPLOAD_HANDLERS`
The actual handlers for uploaded files. Changing this setting allows The actual handlers for uploaded files. Changing this setting allows
complete customization -- even replacement -- of Django's upload complete customization -- even replacement -- of Django's upload

View File

@ -204,11 +204,6 @@ reverse order, from the bottom up. This means classes defined at the end of
Dealing with streaming responses Dealing with streaming responses
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. versionchanged:: 1.5
``response`` may also be an :class:`~django.http.StreamingHttpResponse`
object.
Unlike :class:`~django.http.HttpResponse`, Unlike :class:`~django.http.HttpResponse`,
:class:`~django.http.StreamingHttpResponse` does not have a ``content`` :class:`~django.http.StreamingHttpResponse` does not have a ``content``
attribute. As a result, middleware can no longer assume that all responses attribute. As a result, middleware can no longer assume that all responses

View File

@ -70,10 +70,6 @@ If you have multiple caches defined in :setting:`CACHES`, Django will use the
default cache. To use another cache, set :setting:`SESSION_CACHE_ALIAS` to the default cache. To use another cache, set :setting:`SESSION_CACHE_ALIAS` to the
name of that cache. name of that cache.
.. versionchanged:: 1.5
The :setting:`SESSION_CACHE_ALIAS` setting was added.
Once your cache is configured, you've got two choices for how to store data in Once your cache is configured, you've got two choices for how to store data in
the cache: the cache:
@ -302,8 +298,6 @@ You can edit it multiple times.
.. method:: SessionBase.clear_expired .. method:: SessionBase.clear_expired
.. versionadded:: 1.5
Removes expired sessions from the session store. This class method is Removes expired sessions from the session store. This class method is
called by :djadmin:`clearsessions`. called by :djadmin:`clearsessions`.
@ -469,9 +463,7 @@ cookie will be sent on every request.
Similarly, the ``expires`` part of a session cookie is updated each time the Similarly, the ``expires`` part of a session cookie is updated each time the
session cookie is sent. session cookie is sent.
.. versionchanged:: 1.5 The session is not saved if the response's status code is 500.
The session is not saved if the response's status code is 500.
.. _browser-length-vs-persistent-sessions: .. _browser-length-vs-persistent-sessions:

View File

@ -21,7 +21,7 @@ introduce controlled coupling for convenience's sake.
:class:`~django.http.HttpResponse` object with that rendered text. :class:`~django.http.HttpResponse` object with that rendered text.
:func:`render()` is the same as a call to :func:`render()` is the same as a call to
:func:`render_to_response()` with a `context_instance` argument that :func:`render_to_response()` with a ``context_instance`` argument that
forces the use of a :class:`~django.template.RequestContext`. forces the use of a :class:`~django.template.RequestContext`.
Required arguments Required arguments
@ -50,10 +50,6 @@ Optional arguments
The MIME type to use for the resulting document. Defaults to the value of The MIME type to use for the resulting document. Defaults to the value of
the :setting:`DEFAULT_CONTENT_TYPE` setting. the :setting:`DEFAULT_CONTENT_TYPE` setting.
.. versionchanged:: 1.5
This parameter used to be called ``mimetype``.
``status`` ``status``
The status code for the response. Defaults to ``200``. The status code for the response. Defaults to ``200``.
@ -129,11 +125,6 @@ Optional arguments
The MIME type to use for the resulting document. Defaults to the value of The MIME type to use for the resulting document. Defaults to the value of
the :setting:`DEFAULT_CONTENT_TYPE` setting. the :setting:`DEFAULT_CONTENT_TYPE` setting.
.. versionchanged:: 1.5
This parameter used to be called ``mimetype``.
Example Example
------- -------
@ -169,7 +160,8 @@ This example is equivalent to::
The arguments could be: The arguments could be:
* A model: the model's `get_absolute_url()` function will be called. * A model: the model's `:meth:`~django.db.models.Model.get_absolute_url()`
function will be called.
* A view name, possibly with arguments: :func:`urlresolvers.reverse * A view name, possibly with arguments: :func:`urlresolvers.reverse
<django.core.urlresolvers.reverse>` will be used to reverse-resolve the <django.core.urlresolvers.reverse>` will be used to reverse-resolve the

Some files were not shown because too many files have changed in this diff Show More