summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorLuke Plant <L.Plant.98@cantab.net>2009-10-14 18:14:19 +0000
committerLuke Plant <L.Plant.98@cantab.net>2009-10-14 18:14:19 +0000
commite2b83db9ef5bc5be3d42b75f7a4e37631a5bf165 (patch)
tree4d791366cde91537e006e5a81c5af5d8bd14a0bd /django
parentec9b6f2616a8794b9530558291001aa12e845125 (diff)
[1.1.X] Fixed #6552, #12031 - Make django.core.context_processors.auth lazy to avoid "Vary: Cookie"
Thanks to olau@iola.dk, Suor for the report Backport of r11623 from trunk git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.1.X@11624 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django')
-rw-r--r--django/contrib/auth/tests/remote_user.py10
-rw-r--r--django/core/context_processors.py50
2 files changed, 46 insertions, 14 deletions
diff --git a/django/contrib/auth/tests/remote_user.py b/django/contrib/auth/tests/remote_user.py
index 842d589a54..6115edcfd0 100644
--- a/django/contrib/auth/tests/remote_user.py
+++ b/django/contrib/auth/tests/remote_user.py
@@ -2,7 +2,7 @@ from datetime import datetime
from django.conf import settings
from django.contrib.auth.backends import RemoteUserBackend
-from django.contrib.auth.models import AnonymousUser, User
+from django.contrib.auth.models import User
from django.test import TestCase
@@ -30,15 +30,15 @@ class RemoteUserTest(TestCase):
num_users = User.objects.count()
response = self.client.get('/remote_user/')
- self.assert_(isinstance(response.context['user'], AnonymousUser))
+ self.assert_(response.context['user'].is_anonymous())
self.assertEqual(User.objects.count(), num_users)
response = self.client.get('/remote_user/', REMOTE_USER=None)
- self.assert_(isinstance(response.context['user'], AnonymousUser))
+ self.assert_(response.context['user'].is_anonymous())
self.assertEqual(User.objects.count(), num_users)
response = self.client.get('/remote_user/', REMOTE_USER='')
- self.assert_(isinstance(response.context['user'], AnonymousUser))
+ self.assert_(response.context['user'].is_anonymous())
self.assertEqual(User.objects.count(), num_users)
def test_unknown_user(self):
@@ -115,7 +115,7 @@ class RemoteUserNoCreateTest(RemoteUserTest):
def test_unknown_user(self):
num_users = User.objects.count()
response = self.client.get('/remote_user/', REMOTE_USER='newuser')
- self.assert_(isinstance(response.context['user'], AnonymousUser))
+ self.assert_(response.context['user'].is_anonymous())
self.assertEqual(User.objects.count(), num_users)
diff --git a/django/core/context_processors.py b/django/core/context_processors.py
index cb07125ce7..707068d3be 100644
--- a/django/core/context_processors.py
+++ b/django/core/context_processors.py
@@ -8,6 +8,27 @@ RequestContext.
"""
from django.conf import settings
+from django.utils.functional import lazy, memoize, LazyObject
+
+class ContextLazyObject(LazyObject):
+ """
+ A lazy object initialised from any function, useful for lazily
+ adding things to the Context.
+
+ Designed for compound objects of unknown type. For simple objects of known
+ type, use django.utils.functional.lazy.
+ """
+ def __init__(self, func):
+ """
+ Pass in a callable that returns the actual value to be used
+ """
+ self.__dict__['_setupfunc'] = func
+ # For some reason, we have to inline LazyObject.__init__ here to avoid
+ # recursion
+ self._wrapped = None
+
+ def _setup(self):
+ self._wrapped = self._setupfunc()
def auth(request):
"""
@@ -17,15 +38,26 @@ def auth(request):
If there is no 'user' attribute in the request, uses AnonymousUser (from
django.contrib.auth).
"""
- if hasattr(request, 'user'):
- user = request.user
- else:
- from django.contrib.auth.models import AnonymousUser
- user = AnonymousUser()
+ # If we access request.user, request.session is accessed, which results in
+ # 'Vary: Cookie' being sent in every request that uses this context
+ # processor, which can easily be every request on a site if
+ # TEMPLATE_CONTEXT_PROCESSORS has this context processor added. This kills
+ # the ability to cache. So, we carefully ensure these attributes are lazy.
+ # We don't use django.utils.functional.lazy() for User, because that
+ # requires knowing the class of the object we want to proxy, which could
+ # break with custom auth backends. LazyObject is a less complete but more
+ # flexible solution that is a good enough wrapper for 'User'.
+ def get_user():
+ if hasattr(request, 'user'):
+ return request.user
+ else:
+ from django.contrib.auth.models import AnonymousUser
+ return AnonymousUser()
+
return {
- 'user': user,
- 'messages': user.get_and_delete_messages(),
- 'perms': PermWrapper(user),
+ 'user': ContextLazyObject(get_user),
+ 'messages': lazy(memoize(lambda: get_user().get_and_delete_messages(), {}, 0), list)(),
+ 'perms': lazy(lambda: PermWrapper(get_user()), PermWrapper)(),
}
def debug(request):
@@ -79,7 +111,7 @@ class PermWrapper(object):
def __getitem__(self, module_name):
return PermLookupDict(self.user, module_name)
-
+
def __iter__(self):
# I am large, I contain multitudes.
raise TypeError("PermWrapper is not iterable.")