Browse Source

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

Conflicts:
	docs/ref/django-admin.txt
Andrew Godwin 11 years ago
parent
commit
b6a957f0ba
100 changed files with 1125 additions and 604 deletions
  1. 5 0
      AUTHORS
  2. 5 0
      django/conf/global_settings.py
  3. 2 2
      django/contrib/admin/static/admin/css/dashboard.css
  4. 15 11
      django/contrib/admin/util.py
  5. 2 2
      django/contrib/admin/widgets.py
  6. 5 1
      django/contrib/auth/decorators.py
  7. 2 2
      django/contrib/auth/models.py
  8. 57 1
      django/contrib/auth/tests/test_decorators.py
  9. 10 11
      django/contrib/auth/tests/test_forms.py
  10. 6 5
      django/contrib/auth/tests/test_management.py
  11. 25 1
      django/contrib/auth/tests/test_models.py
  12. 6 2
      django/contrib/auth/tests/test_views.py
  13. 8 0
      django/contrib/gis/db/backends/postgis/introspection.py
  14. 2 2
      django/contrib/gis/db/models/sql/query.py
  15. 1 1
      django/contrib/gis/tests/geoapp/models.py
  16. 14 15
      django/contrib/humanize/tests.py
  17. 28 1
      django/core/checks/compatibility/django_1_6_0.py
  18. 10 1
      django/core/files/storage.py
  19. 3 1
      django/db/backends/postgresql_psycopg2/introspection.py
  20. 19 6
      django/db/models/base.py
  21. 2 3
      django/db/models/query.py
  22. 2 3
      django/db/models/sql/compiler.py
  23. 92 70
      django/db/models/sql/query.py
  24. 3 1
      django/forms/forms.py
  25. 8 7
      django/forms/widgets.py
  26. 4 1
      django/template/base.py
  27. 28 7
      django/template/defaulttags.py
  28. 29 39
      django/test/client.py
  29. 5 10
      django/utils/functional.py
  30. 5 5
      django/utils/http.py
  31. 3 3
      django/views/debug.py
  32. 2 2
      docs/howto/custom-model-fields.txt
  33. 2 1
      docs/howto/deployment/index.txt
  34. 1 1
      docs/howto/deployment/wsgi/apache-auth.txt
  35. 1 1
      docs/howto/deployment/wsgi/modwsgi.txt
  36. 1 1
      docs/internals/contributing/writing-code/working-with-git.txt
  37. 5 8
      docs/internals/deprecation.txt
  38. 5 4
      docs/internals/howto-release-django.txt
  39. 5 1
      docs/internals/security.txt
  40. 1 1
      docs/intro/tutorial01.txt
  41. 0 9
      docs/intro/tutorial02.txt
  42. 0 6
      docs/ref/class-based-views/base.txt
  43. 0 8
      docs/ref/class-based-views/generic-date-based.txt
  44. 0 5
      docs/ref/class-based-views/mixins-date-based.txt
  45. 0 2
      docs/ref/class-based-views/mixins-multiple-object.txt
  46. 0 4
      docs/ref/class-based-views/mixins-simple.txt
  47. 2 2
      docs/ref/contrib/admin/actions.txt
  48. 1 1
      docs/ref/contrib/admin/admindocs.txt
  49. 0 8
      docs/ref/contrib/admin/index.txt
  50. 6 3
      docs/ref/contrib/auth.txt
  51. 6 17
      docs/ref/contrib/contenttypes.txt
  52. 0 4
      docs/ref/contrib/gis/geoquerysets.txt
  53. 0 12
      docs/ref/contrib/gis/geos.txt
  54. 7 4
      docs/ref/contrib/messages.txt
  55. 4 3
      docs/ref/contrib/sites.txt
  56. 0 4
      docs/ref/contrib/staticfiles.txt
  57. 0 4
      docs/ref/databases.txt
  58. 0 10
      docs/ref/django-admin.txt
  59. 0 4
      docs/ref/files/file.txt
  60. 0 5
      docs/ref/forms/api.txt
  61. 0 9
      docs/ref/forms/fields.txt
  62. 2 8
      docs/ref/forms/validation.txt
  63. 41 29
      docs/ref/forms/widgets.txt
  64. 0 11
      docs/ref/models/fields.txt
  65. 34 2
      docs/ref/models/instances.txt
  66. 0 2
      docs/ref/models/options.txt
  67. 13 27
      docs/ref/models/querysets.txt
  68. 0 8
      docs/ref/request-response.txt
  69. 24 19
      docs/ref/settings.txt
  70. 1 12
      docs/ref/signals.txt
  71. 6 17
      docs/ref/template-response.txt
  72. 0 5
      docs/ref/templates/api.txt
  73. 10 12
      docs/ref/templates/builtins.txt
  74. 14 16
      docs/ref/unicode.txt
  75. 0 14
      docs/ref/utils.txt
  76. 11 0
      docs/releases/1.3.3.txt
  77. 37 0
      docs/releases/1.3.4.txt
  78. 60 0
      docs/releases/1.3.5.txt
  79. 78 0
      docs/releases/1.3.6.txt
  80. 13 0
      docs/releases/1.3.7.txt
  81. 5 4
      docs/releases/1.4.2.txt
  82. 60 0
      docs/releases/1.4.3.txt
  83. 88 0
      docs/releases/1.4.4.txt
  84. 13 0
      docs/releases/1.4.5.txt
  85. 31 0
      docs/releases/1.4.6.txt
  86. 62 0
      docs/releases/1.5.2.txt
  87. 2 0
      docs/releases/1.5.txt
  88. 30 1
      docs/releases/1.7.txt
  89. 10 0
      docs/releases/index.txt
  90. 2 4
      docs/topics/auth/customizing.txt
  91. 9 10
      docs/topics/auth/default.txt
  92. 1 0
      docs/topics/class-based-views/intro.txt
  93. 0 2
      docs/topics/class-based-views/mixins.txt
  94. 2 2
      docs/topics/db/managers.txt
  95. 0 5
      docs/topics/db/multi-db.txt
  96. 1 10
      docs/topics/db/queries.txt
  97. 6 1
      docs/topics/http/file-uploads.txt
  98. 0 5
      docs/topics/http/middleware.txt
  99. 1 9
      docs/topics/http/sessions.txt
  100. 3 11
      docs/topics/http/shortcuts.txt

+ 5 - 0
AUTHORS

@@ -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>

+ 5 - 0
django/conf/global_settings.py

@@ -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

+ 2 - 2
django/contrib/admin/static/admin/css/dashboard.css

@@ -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;
-}
+}

+ 15 - 11
django/contrib/admin/util.py

@@ -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)

+ 2 - 2
django/contrib/admin/widgets.py

@@ -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
             )

+ 5 - 1
django/contrib/auth/decorators.py

@@ -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:

+ 2 - 2
django/contrib/auth/models.py

@@ -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):

+ 57 - 1
django/contrib/auth/tests/test_decorators.py

@@ -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)

+ 10 - 11
django/contrib/auth/tests/test_forms.py

@@ -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.

+ 6 - 5
django/contrib/auth/tests/test_management.py

@@ -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)

+ 25 - 1
django/contrib/auth/tests/test_models.py

@@ -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

+ 6 - 2
django/contrib/auth/tests/test_views.py

@@ -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' % {

+ 8 - 0
django/contrib/gis/db/backends/postgis/introspection.py

@@ -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

+ 2 - 2
django/contrib/gis/db/models/sql/query.py

@@ -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):
         """

+ 1 - 1
django/contrib/gis/tests/geoapp/models.py

@@ -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:

+ 14 - 15
django/contrib/humanize/tests.py

@@ -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
 

+ 28 - 1
django/core/checks/compatibility/django_1_6_0.py

@@ -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]

+ 10 - 1
django/core/files/storage.py

@@ -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

+ 3 - 1
django/db/backends/postgresql_psycopg2/introspection.py

@@ -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."

+ 19 - 6
django/db/models/base.py

@@ -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)

+ 2 - 3
django/db/models/query.py

@@ -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):
         """

+ 2 - 3
django/db/models/sql/compiler.py

@@ -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])

+ 92 - 70
django/db/models/sql/query.py

@@ -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.

+ 3 - 1
django/forms/forms.py

@@ -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):

+ 8 - 7
django/forms/widgets.py

@@ -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'

+ 4 - 1
django/template/base.py

@@ -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

+ 28 - 7
django/template/defaulttags.py

@@ -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):

+ 29 - 39
django/test/client.py

@@ -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

+ 5 - 10
django/utils/functional.py

@@ -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

+ 5 - 5
django/utils/http.py

@@ -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'])

+ 3 - 3
django/views/debug.py

@@ -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):

+ 2 - 2
docs/howto/custom-model-fields.txt

@@ -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

+ 2 - 1
docs/howto/deployment/index.txt

@@ -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/

+ 1 - 1
docs/howto/deployment/wsgi/apache-auth.txt

@@ -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

+ 1 - 1
docs/howto/deployment/wsgi/modwsgi.txt

@@ -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

+ 1 - 1
docs/internals/contributing/writing-code/working-with-git.txt

@@ -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.

+ 5 - 8
docs/internals/deprecation.txt

@@ -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).

+ 5 - 4
docs/internals/howto-release-django.txt

@@ -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.

+ 5 - 1
docs/internals/security.txt

@@ -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.

+ 1 - 1
docs/intro/tutorial01.txt

@@ -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

+ 0 - 9
docs/intro/tutorial02.txt

@@ -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

+ 0 - 6
docs/ref/class-based-views/base.txt

@@ -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:

+ 0 - 8
docs/ref/class-based-views/generic-date-based.txt

@@ -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``.

+ 0 - 5
docs/ref/class-based-views/mixins-date-based.txt

@@ -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.

+ 0 - 2
docs/ref/class-based-views/mixins-multiple-object.txt

@@ -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

+ 0 - 4
docs/ref/class-based-views/mixins-simple.txt

@@ -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`.

+ 2 - 2
docs/ref/contrib/admin/actions.txt

@@ -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()`::

+ 1 - 1
docs/ref/contrib/admin/admindocs.txt

@@ -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`.

+ 0 - 8
docs/ref/contrib/admin/index.txt

@@ -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

+ 6 - 3
docs/ref/contrib/auth.txt

@@ -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``

+ 6 - 17
docs/ref/contrib/contenttypes.txt

@@ -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
 =================
 

+ 0 - 4
docs/ref/contrib/gis/geoquerysets.txt

@@ -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.
 

+ 0 - 12
docs/ref/contrib/gis/geos.txt

@@ -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).

+ 7 - 4
docs/ref/contrib/messages.txt

@@ -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.

+ 4 - 3
docs/ref/contrib/sites.txt

@@ -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

+ 0 - 4
docs/ref/contrib/staticfiles.txt

@@ -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:
 

+ 0 - 4
docs/ref/databases.txt

@@ -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

+ 0 - 10
docs/ref/django-admin.txt

@@ -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``

+ 0 - 4
docs/ref/files/file.txt

@@ -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

+ 0 - 5
docs/ref/forms/api.txt

@@ -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,

+ 0 - 9
docs/ref/forms/fields.txt

@@ -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

+ 2 - 8
docs/ref/forms/validation.txt

@@ -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.

+ 41 - 29
docs/ref/forms/widgets.txt

@@ -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>
+
+        <label for="id_beatles_1">
+            Paul
+            <span class="radio"><input id="id_beatles_1" name="beatles" type="radio" value="paul" /></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_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

+ 0 - 11
docs/ref/models/fields.txt

@@ -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
 ===================
 

+ 34 - 2
docs/ref/models/instances.txt

@@ -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``
 --------------------
 

+ 0 - 2
docs/ref/models/options.txt

@@ -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 = [

+ 13 - 27
docs/ref/models/querysets.txt

@@ -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.

+ 0 - 8
docs/ref/request-response.txt

@@ -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

+ 24 - 19
docs/ref/settings.txt

@@ -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

+ 1 - 12
docs/ref/signals.txt

@@ -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::
 

+ 6 - 17
docs/ref/template-response.txt

@@ -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.
 

+ 0 - 5
docs/ref/templates/api.txt

@@ -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
 ----------------------------
 

+ 10 - 12
docs/ref/templates/builtins.txt

@@ -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 %}

+ 14 - 16
docs/ref/unicode.txt

@@ -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::
 

+ 0 - 14
docs/ref/utils.txt

@@ -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 - 0
docs/releases/1.3.3.txt

@@ -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 - 0
docs/releases/1.3.4.txt

@@ -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 - 0
docs/releases/1.3.5.txt

@@ -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 - 0
docs/releases/1.3.6.txt

@@ -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 - 0
docs/releases/1.3.7.txt

@@ -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``.

+ 5 - 4
docs/releases/1.4.2.txt

@@ -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 - 0
docs/releases/1.4.3.txt

@@ -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 - 0
docs/releases/1.4.4.txt

@@ -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 - 0
docs/releases/1.4.5.txt

@@ -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 - 0
docs/releases/1.4.6.txt

@@ -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 - 0
docs/releases/1.5.2.txt

@@ -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).

+ 2 - 0
docs/releases/1.5.txt

@@ -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

+ 30 - 1
docs/releases/1.7.txt

@@ -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
 ==========================
 

+ 10 - 0
docs/releases/index.txt

@@ -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

+ 2 - 4
docs/topics/auth/customizing.txt

@@ -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`
 

+ 9 - 10
docs/topics/auth/default.txt

@@ -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:
 

+ 1 - 0
docs/topics/class-based-views/intro.txt

@@ -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
 

+ 0 - 2
docs/topics/class-based-views/mixins.txt

@@ -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

+ 2 - 2
docs/topics/db/managers.txt

@@ -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::
 

+ 0 - 5
docs/topics/db/multi-db.txt

@@ -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:

+ 1 - 10
docs/topics/db/queries.txt

@@ -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
 ----------------------
 

+ 6 - 1
docs/topics/http/file-uploads.txt

@@ -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

+ 0 - 5
docs/topics/http/middleware.txt

@@ -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

+ 1 - 9
docs/topics/http/sessions.txt

@@ -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:
 

+ 3 - 11
docs/topics/http/shortcuts.txt

@@ -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 changed in this diff