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/>
jpellerin@gmail.com
junzhang.jn@gmail.com
Krzysztof Jurewicz <krzysztof.jurewicz@gmail.com>
Xia Kai <http://blog.xiaket.org/>
Antti Kaihola <http://djangopeople.net/akaihola/>
Peter van Kampen
@ -418,6 +419,7 @@ answer newbie questions, and generally made Django that much better:
Christian Metts
michal@plovarna.cz
Justin Michalicek <jmichalicek@gmail.com>
Bojan Mihelac <bmihelac@mihelac.org>
Slawek Mikula <slawek dot mikula at gmail dot com>
Katie Miller <katie@sub50.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>
Fraser Nevett <mail@nevett.org>
Sam Newman <http://www.magpiebrain.com/>
Alasdair Nicol <http://al.sdair.co.uk/>
Ryan Niemeyer <https://profiles.google.com/ryan.niemeyer/about>
Filip Noetzel <http://filip.noetzel.co.uk/>
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>
Jozko Skrablin <jozko.skrablin@gmail.com>
Ben Slavin <benjamin.slavin@gmail.com>
Jonathan Slenders
sloonz <simon.lipp@insa-lyon.fr>
Paul Smith <blinkylights23@gmail.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/>
Ville Säävuori <http://www.unessa.net/>
Mart Sõmermaa <http://mrts.pri.ee/>
Susan Tan <susan.tan.fleckerl@gmail.com>
Christian Tanzer <tanzer@swing.co.at>
Tyler Tarabula <tyler.tarabula@gmail.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.
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.
# The directory where this setting is pointing should contain subdirectories
# named as the locales, containing a formats.py file

View File

@ -23,8 +23,8 @@ ul.actionlist li {
list-style-type: none;
}
ul.actionlist li.changelink {
ul.actionlist li {
overflow: hidden;
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 import six
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):
"""
@ -113,12 +113,20 @@ def get_deleted_objects(objs, opts, user, admin_site, using):
has_admin = obj.__class__ in admin_site._registry
opts = obj._meta
no_edit_link = '%s: %s' % (capfirst(opts.verbose_name),
force_text(obj))
if has_admin:
admin_url = reverse('%s:%s_%s_change'
% (admin_site.name,
opts.app_label,
opts.model_name),
None, (quote(obj._get_pk_val()),))
try:
admin_url = reverse('%s:%s_%s_change'
% (admin_site.name,
opts.app_label,
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,
get_permission_codename('delete', opts))
if not user.has_perm(p):
@ -131,8 +139,7 @@ def get_deleted_objects(objs, opts, user, admin_site, using):
else:
# Don't display link to edit, because it either has no
# admin or is edited inline.
return '%s: %s' % (capfirst(opts.verbose_name),
force_text(obj))
return no_edit_link
to_delete = collector.nested(format_callback)
@ -155,9 +162,6 @@ class NestedObjects(Collector):
if source_attr:
self.add_edge(getattr(obj, source_attr), obj)
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)
try:
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)
if 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(
'<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,
_('Change:'), html
)

View File

@ -64,8 +64,12 @@ def permission_required(perm, login_url=None, raise_exception=False):
is raised.
"""
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)
if user.has_perm(perm):
if user.has_perms(perms):
return True
# In case the 403 handler should be called raise the exception
if raise_exception:

View File

@ -400,11 +400,11 @@ class AbstractUser(AbstractBaseUser, PermissionsMixin):
"Returns the short name for the user."
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.
"""
send_mail(subject, message, from_email, [self.email])
send_mail(subject, message, from_email, [self.email], **kwargs)
class User(AbstractUser):

View File

@ -1,7 +1,12 @@
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.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
@ -49,3 +54,54 @@ class LoginRequiredTestCase(AuthViewsTestCase):
"""
self.testLoginRequired(view_url='/login_required_login_url/',
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'])])
def test_inactive_user_i18n(self):
with self.settings(USE_I18N=True):
with translation.override('pt-br', deactivate=True):
# The user is inactive.
data = {
'username': 'inactive',
'password': 'password',
}
form = AuthenticationForm(None, data)
self.assertFalse(form.is_valid())
self.assertEqual(form.non_field_errors(),
[force_text(form.error_messages['inactive'])])
with self.settings(USE_I18N=True), translation.override('pt-br', deactivate=True):
# The user is inactive.
data = {
'username': 'inactive',
'password': 'password',
}
form = AuthenticationForm(None, data)
self.assertFalse(form.is_valid())
self.assertEqual(form.non_field_errors(),
[force_text(form.error_messages['inactive'])])
def test_custom_login_allowed_policy(self):
# 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)
def test_default_permissions(self):
permission_content_type = ContentType.objects.get_by_natural_key('auth', 'permission')
models.Permission._meta.permissions = [
('my_custom_permission', 'Some permission'),
]
create_permissions(models, [], verbosity=0)
# add/change/delete permission by default + custom permission
self.assertEqual(models.Permission.objects.filter(content_type=
ContentType.objects.get_by_natural_key('auth', 'permission')
self.assertEqual(models.Permission.objects.filter(
content_type=permission_content_type,
).count(), 4)
models.Permission.objects.all().delete()
models.Permission.objects.filter(content_type=permission_content_type).delete()
models.Permission._meta.default_permissions = []
create_permissions(models, [], verbosity=0)
# custom permission only since default permissions is empty
self.assertEqual(models.Permission.objects.filter(content_type=
ContentType.objects.get_by_natural_key('auth', 'permission')
self.assertEqual(models.Permission.objects.filter(
content_type=permission_content_type,
).count(), 1)

View File

@ -1,6 +1,7 @@
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.core import mail
from django.db.models.signals import post_save
from django.test import TestCase
from django.test.utils import override_settings
@ -73,6 +74,29 @@ class UserManagerTestCase(TestCase):
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):
"""
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',
'https://example.com',
'ftp://exampel.com',
'//example.com'):
'//example.com',
'javascript:alert("XSS")'):
nasty_url = '%(url)s?%(next)s=%(bad_url)s' % {
'url': login_url,
@ -467,6 +468,7 @@ class LoginTest(AuthViewsTestCase):
'/view?param=ftp://exampel.com',
'view/?param=//example.com',
'https:///',
'HTTPS:///',
'//testserver/',
'/url%20with%20spaces/'): # see ticket #12534
safe_url = '%(url)s?%(next)s=%(good_url)s' % {
@ -661,7 +663,8 @@ class LogoutTest(AuthViewsTestCase):
for bad_url in ('http://example.com',
'https://example.com',
'ftp://exampel.com',
'//example.com'):
'//example.com',
'javascript:alert("XSS")'):
nasty_url = '%(url)s?%(next)s=%(bad_url)s' % {
'url': logout_url,
'next': REDIRECT_FIELD_NAME,
@ -680,6 +683,7 @@ class LogoutTest(AuthViewsTestCase):
'/view?param=ftp://exampel.com',
'view/?param=//example.com',
'https:///',
'HTTPS:///',
'//testserver/',
'/url%20with%20spaces/'): # see ticket #12534
safe_url = '%(url)s?%(next)s=%(good_url)s' % {

View File

@ -9,6 +9,14 @@ class PostGISIntrospection(DatabaseIntrospection):
# introspection is actually performed.
postgis_types_reverse = {}
ignored_tables = DatabaseIntrospection.ignored_tables + [
'geography_columns',
'geometry_columns',
'raster_columns',
'spatial_ref_sys',
'raster_overviews',
]
def get_postgis_types(self):
"""
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 value
def get_aggregation(self, using):
def get_aggregation(self, using, force_subq=False):
# Remove any aggregates marked for reduction from the subquery
# and move them to the outer AggregateQuery.
connection = connections[using]
@ -84,7 +84,7 @@ class GeoQuery(sql.Query):
if isinstance(aggregate, gis_aggregates.GeoAggregate):
if not getattr(aggregate, 'is_extent', False) or connection.ops.oracle:
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):
"""

View File

@ -40,7 +40,7 @@ class Track(models.Model):
def __str__(self): return self.name
class Truth(models.Model):
val = models.BooleanField()
val = models.BooleanField(default=False)
objects = models.GeoManager()
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',
None)
with self.settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=False):
with translation.override('en'):
self.humanize_tester(test_list, result_list, 'intcomma')
with self.settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=False), \
translation.override('en'):
self.humanize_tester(test_list, result_list, 'intcomma')
def test_intcomma_without_number_grouping(self):
# Regression for #17414
with translation.override('ja'):
with self.settings(USE_L10N=True):
self.humanize_tester([100], ['100'], 'intcomma')
with translation.override('ja'), self.settings(USE_L10N=True):
self.humanize_tester([100], ['100'], 'intcomma')
def test_intword(self):
test_list = ('100', '1000000', '1200000', '1290000',
@ -104,18 +103,18 @@ class HumanizeTests(TransRealMixin, TestCase):
'100', '1000', '10123', '10311', '1000000', None)
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)
with self.settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=True):
with translation.override('de'):
self.humanize_tester(test_list, result_list, 'intcomma')
with self.settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=True), \
translation.override('de'):
self.humanize_tester(test_list, result_list, 'intcomma')
def test_i18n_intword(self):
test_list = ('100', '1000000', '1200000', '1290000',
'1000000000', '2000000000', '6000000000000')
result_list = ('100', '1,0 Million', '1,2 Millionen', '1,3 Millionen',
'1,0 Milliarde', '2,0 Milliarden', '6,0 Billionen')
with self.settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=True):
with translation.override('de'):
self.humanize_tester(test_list, result_list, 'intword')
with self.settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=True), \
translation.override('de'):
self.humanize_tester(test_list, result_list, 'intword')
def test_apnumber(self):
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
try:
with override_settings(TIME_ZONE="America/Chicago", USE_TZ=True):
with translation.override('en'):
self.humanize_tester([dt], ['yesterday'], 'naturalday')
with override_settings(TIME_ZONE="America/Chicago", USE_TZ=True), \
translation.override('en'):
self.humanize_tester([dt], ['yesterday'], 'naturalday')
finally:
humanize.datetime = orig_humanize_datetime

View File

@ -1,5 +1,6 @@
from __future__ import unicode_literals
from django.db import models
def check_test_runner():
"""
@ -24,6 +25,31 @@ def check_test_runner():
]
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():
"""
@ -31,7 +57,8 @@ def run_checks():
messages from all the relevant check functions for this version of Django.
"""
checks = [
check_test_runner()
check_test_runner(),
check_boolean_field_default_value(),
]
# Filter out the ``None`` or empty strings.
return [output for output in checks if output]

View File

@ -172,7 +172,16 @@ class FileSystemStorage(Storage):
directory = os.path.dirname(full_path)
if not os.path.exists(directory):
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:
if e.errno != errno.EEXIST:
raise

View File

@ -26,6 +26,8 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
1700: 'DecimalField',
}
ignored_tables = []
def get_table_list(self, cursor):
"Returns a list of table names in the current database."
cursor.execute("""
@ -35,7 +37,7 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
WHERE c.relkind IN ('r', 'v', '')
AND n.nspname NOT IN ('pg_catalog', 'pg_toast')
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):
"Returns a description of the table, with the DB-API cursor.description interface."

View File

@ -184,10 +184,21 @@ class ModelBase(type):
else:
new_class._meta.concrete_model = new_class
# Do the appropriate setup for any model parents.
o2o_map = dict([(f.rel.to, f) for f in new_class._meta.local_fields
if isinstance(f, OneToOneField)])
# Collect the parent links for multi-table inheritance.
parent_links = {}
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:
original_base = base
if not hasattr(base, '_meta'):
@ -208,8 +219,8 @@ class ModelBase(type):
if not base._meta.abstract:
# Concrete classes...
base = base._meta.concrete_model
if base in o2o_map:
field = o2o_map[base]
if base in parent_links:
field = parent_links[base]
elif not is_proxy:
attr_name = '%s_ptr' % base._meta.model_name
field = OneToOneField(base, name=attr_name,
@ -448,7 +459,9 @@ class Model(six.with_metaclass(ModelBase)):
return '%s object' % self.__class__.__name__
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):
return not self.__eq__(other)

View File

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

View File

@ -167,7 +167,6 @@ class SQLCompiler(object):
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
obj.clear_ordering(True)
obj.bump_prefix()
return obj.get_compiler(connection=self.connection).as_sql()
def get_columns(self, with_aliases=False):
@ -808,13 +807,14 @@ class SQLCompiler(object):
return result
def as_subquery_condition(self, alias, columns, qn):
inner_qn = self.quote_name_unless_alias
qn2 = self.connection.ops.quote_name
if len(columns) == 1:
sql, params = self.as_sql()
return '%s.%s IN (%s)' % (qn(alias), qn2(columns[0]), sql), params
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]))
self.query.where.add(
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
# from other tables.
query = self.query.clone(klass=Query)
query.bump_prefix()
query.extra = {}
query.select = []
query.add_fields([query.get_meta().pk.name])

View File

@ -97,6 +97,7 @@ class Query(object):
LOUTER = 'LEFT OUTER JOIN'
alias_prefix = 'T'
subq_aliases = frozenset([alias_prefix])
query_terms = QUERY_TERMS
aggregates_module = base_aggregates_module
@ -273,6 +274,10 @@ class Query(object):
else:
obj.used_aliases = set()
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)
if hasattr(obj, '_setup_query'):
@ -310,7 +315,7 @@ class Query(object):
# Return value depends on the type of the field being processed.
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.
"""
@ -320,18 +325,26 @@ class Query(object):
# If there is a group by clause, aggregating does not add useful
# information but retrieves only the first row. Aggregate
# 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
query = AggregateQuery(self.model)
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
# and move them to the outer AggregateQuery.
for alias, aggregate in self.aggregate_select.items():
if aggregate.is_summary:
query.aggregate_select[alias] = aggregate
query.aggregate_select[alias] = aggregate.relabeled_clone(relabels)
del obj.aggregate_select[alias]
try:
@ -780,28 +793,22 @@ class Query(object):
data = data._replace(lhs_alias=change_map[lhs])
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
relabels all the aliases. Even tables that previously had no alias will
get an alias after this call (it's mostly used for nested queries and
the outer query will already be using the non-aliased table name).
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.
Changes the alias prefix to the next letter in the alphabet in a way
that the outer query's aliases and this query's aliases will not
conflict. Even tables that previously had no alias will get an alias
after this call.
"""
current = ord(self.alias_prefix)
assert current < ord('Z')
prefix = chr(current + 1)
self.alias_prefix = prefix
self.alias_prefix = chr(ord(self.alias_prefix) + 1)
while self.alias_prefix in self.subq_aliases:
self.alias_prefix = chr(ord(self.alias_prefix) + 1)
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()
for pos, alias in enumerate(self.tables):
if alias in exceptions:
continue
new_alias = '%s%d' % (prefix, pos)
new_alias = '%s%d' % (self.alias_prefix, pos)
change_map[alias] = new_alias
self.tables[pos] = new_alias
self.change_aliases(change_map)
@ -1005,6 +1012,65 @@ class Query(object):
# Add the aggregate to the query
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,
can_reuse=None):
"""
@ -1033,58 +1099,15 @@ class Query(object):
is responsible for unreffing the joins used.
"""
arg, value = filter_expr
parts = arg.split(LOOKUP_SEP)
lookup_type, parts = self.solve_lookup_type(arg)
if not parts:
raise FieldError("Cannot parse keyword query %r" % arg)
# Work out the lookup type and remove it from the end of 'parts',
# if necessary.
lookup_type = 'exact' # Default lookup type
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
value, lookup_type = self.prepare_lookup_value(value, lookup_type, can_reuse)
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():
if alias in (parts[0], LOOKUP_SEP.join(parts)):
clause.add((aggregate, lookup_type, value), AND)
@ -1096,7 +1119,7 @@ class Query(object):
try:
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:
can_reuse.update(join_list)
except MultiJoin as e:
@ -1404,7 +1427,6 @@ class Query(object):
# Generate the inner query.
query = Query(self.model)
query.where.add(query.build_filter(filter_expr), AND)
query.bump_prefix()
query.clear_ordering(True)
# Try to have as simple as possible subquery -> trim leading joins from
# the subquery.

View File

@ -434,7 +434,9 @@ class BoundField(object):
This really is only useful for RadioSelect widgets, so that you can
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
def __len__(self):

View File

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

View File

@ -6,7 +6,7 @@ from importlib import import_module
from inspect import getargspec
from django.conf import settings
from django.template.context import (Context, RequestContext,
from django.template.context import (BaseContext, Context, RequestContext,
ContextPopException)
from django.utils.itercompat import is_iterable
from django.utils.text import (smart_split, unescape_string_literal,
@ -765,6 +765,9 @@ class Variable(object):
current = current[bit]
except (TypeError, AttributeError, KeyError, ValueError):
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)
except (TypeError, AttributeError):
try: # list-index lookup

View File

@ -458,10 +458,11 @@ class VerbatimNode(Node):
return self.content
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.max_expr = max_expr
self.max_width = max_width
self.asvar = asvar
def render(self, context):
try:
@ -480,7 +481,13 @@ class WidthRatioNode(Node):
return '0'
except (ValueError, TypeError):
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):
def __init__(self, var, name, nodelist, extra_context=None):
@ -1353,20 +1360,34 @@ def widthratio(parser, token):
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,
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).
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()
if len(bits) != 4:
raise TemplateSyntaxError("widthratio takes three arguments")
tag, this_value_expr, max_value_expr, max_width = bits
if len(bits) == 4:
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),
parser.compile_filter(max_value_expr),
parser.compile_filter(max_width))
parser.compile_filter(max_width),
asvar=asvar)
@register.tag('with')
def do_with(parser, token):

View File

@ -37,6 +37,7 @@ BOUNDARY = 'BoUnDaRyStRiNg'
MULTIPART_CONTENT = 'multipart/form-data; boundary=%s' % BOUNDARY
CONTENT_TYPE_RE = re.compile('.*; charset=([\w\d-]+);?')
class FakePayload(object):
"""
A wrapper around BytesIO that restricts what can be read since data from
@ -123,6 +124,7 @@ class ClientHandler(BaseHandler):
return response
def store_rendered_templates(store, signal, sender, template, context, **kwargs):
"""
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('context', ContextList()).append(copy(context))
def encode_multipart(boundary, data):
"""
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)
def encode_file(boundary, key, file):
to_bytes = lambda s: force_bytes(s, settings.DEFAULT_CHARSET)
if hasattr(file, 'content_type'):
@ -189,8 +193,8 @@ def encode_file(boundary, key, file):
content_type = 'application/octet-stream'
return [
to_bytes('--%s' % boundary),
to_bytes('Content-Disposition: form-data; name="%s"; filename="%s"' \
% (key, os.path.basename(file.name))),
to_bytes('Content-Disposition: form-data; name="%s"; filename="%s"'
% (key, os.path.basename(file.name))),
to_bytes('Content-Type: %s' % content_type),
b'',
file.read()
@ -274,14 +278,11 @@ class RequestFactory(object):
def get(self, path, data={}, **extra):
"Construct a GET request."
parsed = urlparse(path)
r = {
'PATH_INFO': self._get_path(parsed),
'QUERY_STRING': urlencode(data, doseq=True) or force_str(parsed[4]),
'REQUEST_METHOD': str('GET'),
'QUERY_STRING': urlencode(data, doseq=True),
}
r.update(extra)
return self.request(**r)
return self.generic('GET', path, **r)
def post(self, path, data={}, content_type=MULTIPART_CONTENT,
**extra):
@ -289,32 +290,19 @@ class RequestFactory(object):
post_data = self._encode_data(data, content_type)
parsed = urlparse(path)
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)
return self.generic('POST', path, post_data, content_type, **extra)
def head(self, path, data={}, **extra):
"Construct a HEAD request."
parsed = urlparse(path)
r = {
'PATH_INFO': self._get_path(parsed),
'QUERY_STRING': urlencode(data, doseq=True) or force_str(parsed[4]),
'REQUEST_METHOD': str('HEAD'),
'QUERY_STRING': urlencode(data, doseq=True),
}
r.update(extra)
return self.request(**r)
return self.generic('HEAD', path, **r)
def options(self, path, data='', content_type='application/octet-stream',
**extra):
**extra):
"Construct an OPTIONS request."
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)
def patch(self, path, data='', content_type='application/octet-stream',
**extra):
**extra):
"Construct a PATCH request."
return self.generic('PATCH', path, data, content_type, **extra)
def delete(self, path, data='', content_type='application/octet-stream',
**extra):
**extra):
"Construct a DELETE request."
return self.generic('DELETE', path, data, content_type, **extra)
def generic(self, method, path,
data='', content_type='application/octet-stream', **extra):
"""Constructs an arbitrary HTTP request."""
parsed = urlparse(path)
data = force_bytes(data, settings.DEFAULT_CHARSET)
r = {
'PATH_INFO': self._get_path(parsed),
'QUERY_STRING': force_str(parsed[4]),
'REQUEST_METHOD': str(method),
}
if data:
@ -349,8 +337,12 @@ class RequestFactory(object):
'wsgi.input': FakePayload(data),
})
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)
class Client(RequestFactory):
"""
A class that can act as a client for testing purposes.
@ -392,7 +384,6 @@ class Client(RequestFactory):
return {}
session = property(_session)
def request(self, **request):
"""
The master request method. Composes the environment dictionary
@ -406,7 +397,8 @@ class Client(RequestFactory):
# callback function.
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.
got_request_exception.connect(self.store_exc_info, dispatch_uid="request-exception")
try:
@ -452,7 +444,7 @@ class Client(RequestFactory):
return response
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")
def get(self, path, data={}, follow=False, **extra):
@ -484,12 +476,11 @@ class Client(RequestFactory):
return response
def options(self, path, data='', content_type='application/octet-stream',
follow=False, **extra):
follow=False, **extra):
"""
Request a response from the server using OPTIONS.
"""
response = super(Client, self).options(path,
data=data, content_type=content_type, **extra)
response = super(Client, self).options(path, data=data, content_type=content_type, **extra)
if follow:
response = self._handle_redirects(response, **extra)
return response
@ -499,14 +490,13 @@ class Client(RequestFactory):
"""
Send a resource to the server using PUT.
"""
response = super(Client, self).put(path,
data=data, content_type=content_type, **extra)
response = super(Client, self).put(path, data=data, content_type=content_type, **extra)
if follow:
response = self._handle_redirects(response, **extra)
return response
def patch(self, path, data='', content_type='application/octet-stream',
follow=False, **extra):
follow=False, **extra):
"""
Send a resource to the server using PATCH.
"""
@ -517,12 +507,12 @@ class Client(RequestFactory):
return response
def delete(self, path, data='', content_type='application/octet-stream',
follow=False, **extra):
follow=False, **extra):
"""
Send a DELETE request to the server.
"""
response = super(Client, self).delete(path,
data=data, content_type=content_type, **extra)
response = super(Client, self).delete(
path, data=data, content_type=content_type, **extra)
if follow:
response = self._handle_redirects(response, **extra)
return response

View File

@ -263,17 +263,12 @@ class LazyObject(object):
__dir__ = new_method_proxy(dir)
# Dictionary methods support
@new_method_proxy
def __getitem__(self, key):
return self[key]
__getitem__ = new_method_proxy(operator.getitem)
__setitem__ = new_method_proxy(operator.setitem)
__delitem__ = new_method_proxy(operator.delitem)
@new_method_proxy
def __setitem__(self, key, value):
self[key] = value
@new_method_proxy
def __delitem__(self, key):
del self[key]
__len__ = new_method_proxy(len)
__contains__ = new_method_proxy(operator.contains)
# 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'.
"""
rfcdate = formatdate(epoch_seconds)
return '%s GMT' % rfcdate[:25]
return formatdate(epoch_seconds, usegmt=True)
def parse_http_date(date):
"""
@ -253,11 +252,12 @@ def same_origin(url1, url2):
def is_safe_url(url, host=None):
"""
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.
"""
if not url:
return False
netloc = urllib_parse.urlparse(url)[1]
return not netloc or netloc == host
url_info = urllib_parse.urlparse(url)
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"
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):
from django.template.loader import template_source_loaders
@ -295,13 +295,13 @@ class ExceptionReporter(object):
def get_traceback_html(self):
"Return HTML version of debug 500 HTTP error page."
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)
def get_traceback_text(self):
"Return plain text version of debug 500 HTTP error page."
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)
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
custom field performs whatever input validation and data cleaning is
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
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.
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
and especially scaling in more detail. However, note that this edition
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/

View File

@ -16,7 +16,7 @@ version >= 2.2 and mod_wsgi >= 2.0. For example, you could:
.. note::
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
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

View File

@ -193,7 +193,7 @@ other approaches:
configuration).
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.
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
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
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.

View File

@ -370,8 +370,11 @@ these changes.
* Remove the backward compatible shims introduced to rename the attributes
``ChangeList.root_query_set`` and ``ChangeList.query_set``.
* ``django.conf.urls.shortcut`` and ``django.views.defaults.shortcut`` will be
removed.
* ``django.views.defaults.shortcut`` will be removed, as part of the
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
no longer appears to be actively maintained & does not work on Python 3.
@ -442,11 +445,5 @@ these changes.
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
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
any time leading up to the actual release:
#. If this is a security release, send out pre-notification **one week**
before the release. We maintain a list of who gets these pre-notification
emails at *FIXME WHERE?*. This email should be signed by the key you'll use
for the release, and should include patches for each issue being fixed.
#. If this is a security release, send out pre-notification **one week** before
the release. We maintain a list of who gets these pre-notification emails in
the private ``django-core`` repository. This email should be signed by the
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
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
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 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 --
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
emails will be signed with the same key used to sign Django releases;
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
admin.
.. admonition:: `__unicode__` or `__str__`?
.. admonition:: ``__unicode__`` or ``__str__``?
On Python 3, things are simpler, just use
: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
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
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

View File

@ -104,12 +104,6 @@ TemplateView
Renders a given template, with the context containing parameters captured
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)**
This view inherits methods and attributes from the following views:

View File

@ -138,24 +138,16 @@ YearArchiveView
* ``year``: A :class:`~datetime.date` object
representing the given year.
.. versionchanged:: 1.5
Previously, this returned a string.
* ``next_year``: A :class:`~datetime.date` object
representing the first day of the next year, according to
:attr:`~BaseDateListView.allow_empty` and
:attr:`~DateMixin.allow_future`.
.. versionadded:: 1.5
* ``previous_year``: A :class:`~datetime.date` object
representing the first day of the previous year, according to
:attr:`~BaseDateListView.allow_empty` and
:attr:`~DateMixin.allow_future`.
.. versionadded:: 1.5
**Notes**
* 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``
and ``ordering`` are simply passed to
: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
.. versionadded:: 1.5
A string specifying the name to use for the page parameter.
The view will expect this prameter to be available either as a query
string parameter (via ``request.GET``) or as a kwarg variable specified

View File

@ -7,8 +7,6 @@ ContextMixin
.. class:: django.views.generic.base.ContextMixin
.. versionadded:: 1.5
**Methods**
.. method:: get_context_data(**kwargs)
@ -77,8 +75,6 @@ TemplateResponseMixin
.. attribute:: content_type
.. versionadded:: 1.5
The content type to use for the response. ``content_type`` is passed
as a keyword argument to ``response_class``. Default is ``None`` --
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)
This makes the `export_selected_objects` action globally available as an
action named `"export_selected_objects"`. You can explicitly give the action
This makes the ``export_selected_objects`` action globally available as an
action named "export_selected_objects". You can explicitly give the action
a name -- good if you later want to programmatically :ref:`remove the action
<disabling-admin-actions>` -- by passing a second argument to
:meth:`AdminSite.add_action()`::

View File

@ -156,6 +156,6 @@ Edit this object
Using these bookmarklets requires that you are either logged into the
:mod:`Django admin <django.contrib.admin>` as a
: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
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)
.. versionadded:: 1.5
The ``get_list_filter`` method is given the ``HttpRequest`` and is expected
to return the same kind of sequence type as for the
: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)
.. versionadded:: 1.5
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
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`
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
tags, or fail silently if the ``contrib.messages`` framework is not
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
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
the :setting:`DEFAULT_FROM_EMAIL`.
.. versionchanged:: 1.7
Any ``**kwargs`` are passed to the underlying
:meth:`~django.core.mail.send_mail()` call.
Manager methods
---------------
@ -384,8 +389,6 @@ can be used for notification when a user logs in or out.
.. function:: user_login_failed
.. versionadded:: 1.5
Sent when the user failed to login successfully
``sender``

View File

@ -199,14 +199,18 @@ The ``ContentTypeManager``
Takes either a model class or an instance of a model, and returns the
: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])
Takes a variadic number of model classes, and returns a dictionary
mapping the model classes to the
: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)
@ -232,21 +236,6 @@ lookup::
.. _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
=================

View File

@ -949,10 +949,6 @@ __ http://geohash.org/
*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
`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
:attr:`GEOSGeometry.hexewkb` property instead).
.. versionchanged:: 1.5
Prior to Django 1.5, the Z value of the geometry was dropped.
.. attribute:: GEOSGeometry.hexewkb
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
:attr:`GEOSGeometry.ewkb` property instead.
.. versionchanged:: 1.5
Prior to Django 1.5, the Z value of the geometry was dropped.
.. _ewkb:
.. attribute:: GEOSGeometry.ewkb
@ -426,8 +418,6 @@ geometry that do not make up other.
.. method:: GEOSGeometry.interpolate(distance)
.. method:: GEOSGeometry.interpolate_normalized(distance)
.. versionadded:: 1.5
Given a distance (float), returns the point (or closest point) within the
geometry (:class:`LineString` or :class:`MultiLineString`) at that distance.
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_normalized(point)
.. versionadded:: 1.5
Returns the distance (float) from the origin of the geometry
(: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).

View File

@ -54,20 +54,21 @@ Storage backends
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
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
to prevent manipulation) to persist notifications across requests. Old
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
``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'
.. class:: storage.base.BaseStorage
To write your own storage class, subclass the ``BaseStorage`` class in
``django.contrib.messages.storage.base`` and implement the ``_get`` and
``_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
``{{ site_name }}``.
* The shortcut view (``django.views.defaults.shortcut``) uses the domain
of the current :class:`~django.contrib.sites.models.Site` object when
calculating an object's URL.
* The shortcut view (``django.contrib.contenttypes.views.shortcut``)
uses the domain of the current
: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
: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)
.. versionadded:: 1.5
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.
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
in :ref:`staticfiles-from-cdn`.
.. versionadded:: 1.5
If you'd like to retrieve a static URL without displaying it, you can use a
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
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
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

View File

@ -254,8 +254,6 @@ to flush.
``--no-initial-data``
~~~~~~~~~~~~~~~~~~~~~
.. versionadded:: 1.5
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
.. versionadded:: 1.5
The :djadminopt:`--ignorenonexistent` option can be used to ignore fields that
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
.. versionadded:: 1.5
The ``--interface`` option was added in Django 1.5.
.. versionadded:: 1.6
The ``--no-startup`` option was added in Django 1.6.
@ -1337,8 +1329,6 @@ clearsessions
.. django-admin:: clearsessions
.. versionadded:: 1.5
Can be run as a cron job or directly to clean out expired sessions.
``django.contrib.sitemaps``

View File

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

View File

@ -211,11 +211,6 @@ only the valid fields::
>>> f.cleaned_data
{'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
``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,

View File

@ -573,16 +573,12 @@ For each field, we describe the default widget used if you don't specify
.. attribute:: allow_files
.. versionadded:: 1.5
Optional. Either ``True`` or ``False``. Default is ``True``. Specifies
whether files in the specified location should be included. Either this or
:attr:`allow_folders` must be ``True``.
.. attribute:: allow_folders
.. versionadded:: 1.5
Optional. Either ``True`` or ``False``. Default is ``False``. Specifies
whether folders in the specified location should be included. Either this or
: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``,
``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
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
considering aren't valid, we must remember to remove them from the
``cleaned_data``.
.. 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.
``cleaned_data``. `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
``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``
~~~~~~~~~~
@ -590,25 +585,26 @@ Selector and checkbox widgets
.. code-block:: html
<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 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 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 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>
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
{% for radio in myform.beatles %}
<label>
<label for="{{ radio.id_for_label }}">
{{ radio.choice_label }}
<span class="radio">{{ radio.tag }}</span>
</label>
@ -618,31 +614,41 @@ Selector and checkbox widgets
.. code-block:: html
<label>
John
<span class="radio"><input type="radio" name="beatles" value="john" /></span>
</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>
<label for="id_beatles_0">
John
<span class="radio"><input id="id_beatles_0" name="beatles" type="radio" value="john" /></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.
<label for="id_beatles_1">
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
The outer ``<ul>`` container will now receive the ``id`` attribute defined on
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``
~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -666,6 +672,12 @@ the widget.
Like :class:`RadioSelect`, you can now loop over the individual checkboxes making
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

View File

@ -757,21 +757,16 @@ directory on the filesystem. Has three special arguments, of which the first is
.. attribute:: FilePathField.allow_files
.. versionadded:: 1.5
Optional. Either ``True`` or ``False``. Default is ``True``. Specifies
whether files in the specified location should be included. Either this or
:attr:`~FilePathField.allow_folders` must be ``True``.
.. attribute:: FilePathField.allow_folders
.. versionadded:: 1.5
Optional. Either ``True`` or ``False``. Default is ``False``. Specifies
whether folders in the specified location should be included. Either this
or :attr:`~FilePathField.allow_files` must be ``True``.
Of course, these arguments can be used together.
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`, 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
===================

View File

@ -388,8 +388,6 @@ For more details, see the documentation on :ref:`F() expressions
Specifying which fields to save
-------------------------------
.. versionadded:: 1.5
If ``save()`` is passed a list of field names in keyword argument
``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
@ -494,6 +492,40 @@ using ``__str__()`` like this::
# first_name and last_name will be unicode strings.
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``
--------------------

View File

@ -286,8 +286,6 @@ Django quotes column and table names behind the scenes.
.. attribute:: Options.index_together
.. versionadded:: 1.5
Sets of field names that, taken together, are indexed::
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.
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::
>>> non_prefetched = qs.prefetch_related(None)
@ -1149,13 +1149,11 @@ to ``defer()``::
# Load all fields immediately.
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
:meth:`select_related()` to retrieve related models, you shouldn't defer the
loading of the field that connects from the primary model to the related
one, doing so will result in an error.
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
:meth:`select_related()` to retrieve related models, you shouldn't defer the
loading of the field that connects from the primary model to the related
one, doing so will result in an error.
.. note::
@ -1178,8 +1176,6 @@ to ``defer()``::
reader, is slightly faster and consumes a little less memory in the Python
process.
.. versionchanged:: 1.5
.. note::
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
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
:meth:`select_related` is an error as well.
.. note::
.. note::
When calling :meth:`~django.db.models.Model.save()` for instances with
deferred fields, only the loaded fields will be saved. See
:meth:`~django.db.models.Model.save()` for more details.
When calling :meth:`~django.db.models.Model.save()` for instances with
deferred fields, only the loaded fields will be saved. See
:meth:`~django.db.models.Model.save()` for more details.
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
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
~~~~~
@ -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
(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.
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
@ -1911,7 +1897,7 @@ deletes this can result in significantly reduced memory usage. The amount of
executed queries can be reduced, too.
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
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
: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``
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``
@ -196,8 +192,6 @@ All attributes should be considered read-only, unless stated otherwise below.
.. attribute:: HttpRequest.resolver_match
.. versionadded:: 1.5
An instance of :class:`~django.core.urlresolvers.ResolverMatch` representing
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
@ -824,8 +818,6 @@ types of HTTP responses. Like ``HttpResponse``, these subclasses live in
StreamingHttpResponse objects
=============================
.. versionadded:: 1.5
.. class:: StreamingHttpResponse
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 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
FILE_UPLOAD_PERMISSIONS
@ -1529,6 +1542,8 @@ unpredictable value.
:djadmin:`django-admin.py startproject <startproject>` automatically adds a
randomly-generated ``SECRET_KEY`` to each new project.
Django will refuse to start if :setting:`SECRET_KEY` is not set.
.. warning::
**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
execution vulnerabilities.
.. versionchanged:: 1.5
Django will now refuse to start if :setting:`SECRET_KEY` is not set.
.. setting:: 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`
decorator, for example.
.. versionchanged:: 1.5
This setting now also accepts view function names and
: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.
This setting also accepts view function names and :ref:`named URL patterns
<naming-url-patterns>` which can be used to reduce configuration duplication
since you don't have to define the URL in two places (``settings`` and URLconf).
.. setting:: LOGIN_URL
@ -2113,13 +2120,9 @@ Default: ``'/accounts/login/'``
The URL where requests are redirected for login, especially when using the
:func:`~django.contrib.auth.decorators.login_required` decorator.
.. versionchanged:: 1.5
This setting now also accepts view function names and
: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.
This setting also accepts view function names and :ref:`named URL patterns
<naming-url-patterns>` which can be used to reduce configuration duplication
since you don't have to define the URL in two places (``settings`` and URLconf).
.. 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.
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.
.. setting:: MESSAGE_TAGS

View File

@ -118,8 +118,6 @@ Arguments sent with this signal:
``using``
The database alias being used.
.. versionadded:: 1.5
``update_fields``
The set of fields to update explicitly specified in the ``save()`` method.
``None`` if this argument was not used in the ``save()`` call.
@ -153,8 +151,6 @@ Arguments sent with this signal:
``using``
The database alias being used.
.. versionadded:: 1.5
``update_fields``
The set of fields to update explicitly specified in the ``save()`` method.
``None`` if this argument was not used in the ``save()`` call.
@ -522,14 +518,7 @@ request_finished
.. data:: django.core.signals.request_finished
:module:
Sent when Django finishes processing an HTTP request.
.. 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.
Sent when Django finishes delvering an HTTP response to the client.
.. note::

View File

@ -75,16 +75,10 @@ Methods
The HTTP Status code for the response.
``content_type``
.. versionchanged:: 1.5
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,
:setting:`DEFAULT_CONTENT_TYPE` is used.
The value included in the HTTP ``Content-Type`` header, including the
MIME type specification and the character set encoding. If
``content_type`` is specified, then its value is used. Otherwise,
:setting:`DEFAULT_CONTENT_TYPE` is used.
.. method:: SimpleTemplateResponse.resolve_context(context)
@ -167,13 +161,8 @@ Methods
The HTTP Status code for the response.
``content_type``
.. versionchanged:: 1.5
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
The value included in the HTTP ``Content-Type`` header, including the
MIME type specification and the character set encoding. If
``content_type`` is specified, then its value is used. Otherwise,
: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,
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
----------------------------

View File

@ -1059,22 +1059,14 @@ by the context as to the current application.
.. warning::
Don't forget to put quotes around the function path or pattern name!
.. 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.
Don't forget to put quotes around the function path or pattern name,
otherwise the value will be interpreted as a context variable!
.. templatetag:: verbatim
verbatim
^^^^^^^^
.. versionadded:: 1.5
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
@ -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
(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
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
slightly different call:
.. versionadded:: 1.5
.. code-block:: html+django
{% 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
"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
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::
Python 2 legacy::
my_string = "This is a bytestring"
my_unicode = u"This is an Unicode string"
my_string = "This is a bytestring"
my_unicode = u"This is an Unicode string"
Python 2 with unicode literals or Python 3::
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"
my_unicode = "This is an Unicode string"
See also :doc:`Python 3 compatibility </topics/python3>`.
See also :doc:`Python 3 compatibility </topics/python3>`.
.. 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')
.. versionadded:: 1.5
Returns a text object representing ``s`` -- ``unicode`` on Python 2 and
``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')
.. versionadded:: 1.5
Similar to ``smart_text``, except that lazy instances are resolved to
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')
.. versionadded:: 1.5
Returns a bytestring version of ``s``, encoded as specified in
``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')
.. versionadded:: 1.5
Similar to ``smart_bytes``, except that lazy instances are resolved to
bytestrings, rather than kept as lazy objects.
@ -745,8 +737,6 @@ appropriate entities.
.. class:: SafeBytes
.. versionadded:: 1.5
A ``bytes`` subclass that has been specifically marked as "safe"
(requires no further escaping) for HTML output purposes.
@ -758,8 +748,6 @@ appropriate entities.
.. class:: SafeText
.. versionadded:: 1.5
A ``str`` (in Python 3) or ``unicode`` (in Python 2) subclass
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
on entry with :func:`deactivate()` instead.
.. versionadded:: 1.5
.. function:: localtime(value, timezone=None)
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,
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
example, the following Host header would be accepted by Django when running on
"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
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 django.core.exceptions.SuspiciousOperation
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`_.

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
========================
*February 26, 2013*
Welcome to Django 1.5!
These release notes cover the `new features`_, as well

View File

@ -130,7 +130,7 @@ Minor features
context level.
* 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
relies on ``pubdate``).
@ -190,6 +190,30 @@ Minor features
``Meta`` option allows you to customize (or disable) creation of the default
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
=====================================
@ -237,6 +261,11 @@ Miscellaneous
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.
* 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
==========================

View File

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

View File

@ -359,8 +359,6 @@ the extra database load.
Substituting a custom User model
================================
.. versionadded:: 1.5
Some kinds of projects may have authentication requirements for which Django's
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
@ -684,13 +682,13 @@ auth views.
* :class:`~django.contrib.auth.forms.AuthenticationForm`
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`
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
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`

View File

@ -434,12 +434,10 @@ The login_required decorator
(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 to freely remap your login view within your URLconf
without having to update the setting.
The :setting:`settings.LOGIN_URL <LOGIN_URL>` also accepts view function
names and :ref:`named URL patterns <naming-url-patterns>`. This allows you
to freely remap your login view within your URLconf without having to
update the setting.
.. note::
@ -528,6 +526,11 @@ The permission_required decorator
(HTTP Forbidden) view<http_forbidden_view>` instead of redirecting to the
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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -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>
{% endif %}
.. versionadded:: 1.5
Permission lookup by "if in".
It is possible to also look permissions up by ``{% if in %}`` statements.
For example:

View File

@ -198,6 +198,7 @@ A similar class-based view might look like::
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.views.generic.base import View
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
objects.
.. versionadded:: 1.5
:class:`~django.views.generic.base.ContextMixin`
Every built in view which needs context data, such as for rendering a
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.
- 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 `True` are never 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.
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
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
objects in more than one database. Common setups include master / slave and
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
>>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))
.. versionadded:: 1.5
``.bitand()`` and ``.bitor()``
The ``F()`` objects now support bitwise operations by ``.bitand()`` and
The ``F()`` objects support bitwise operations by ``.bitand()`` and
``.bitor()``, for example::
>>> 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
----------------------

View File

@ -132,7 +132,7 @@ upload 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`
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.**
: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`
The actual handlers for uploaded files. Changing this setting allows
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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. versionchanged:: 1.5
``response`` may also be an :class:`~django.http.StreamingHttpResponse`
object.
Unlike :class:`~django.http.HttpResponse`,
:class:`~django.http.StreamingHttpResponse` does not have a ``content``
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
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
the cache:
@ -302,8 +298,6 @@ You can edit it multiple times.
.. method:: SessionBase.clear_expired
.. versionadded:: 1.5
Removes expired sessions from the session store. This class method is
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
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:

View File

@ -21,7 +21,7 @@ introduce controlled coupling for convenience's sake.
:class:`~django.http.HttpResponse` object with that rendered text.
: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`.
Required arguments
@ -50,10 +50,6 @@ Optional arguments
The MIME type to use for the resulting document. Defaults to the value of
the :setting:`DEFAULT_CONTENT_TYPE` setting.
.. versionchanged:: 1.5
This parameter used to be called ``mimetype``.
``status``
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 :setting:`DEFAULT_CONTENT_TYPE` setting.
.. versionchanged:: 1.5
This parameter used to be called ``mimetype``.
Example
-------
@ -169,7 +160,8 @@ This example is equivalent to::
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
<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