Ticket #6552: 6552.diff

File 6552.diff, 9.2 KB (added by lukeplant, 6 years ago)

make the context processor return objects lazily

  • django/core/context_processors.py

    diff -r 6490bd644de2 django/core/context_processors.py
    a b  
    88"""
    99
    1010from django.conf import settings
     11from django.template.utils import ContextLazyObject
    1112
    1213def auth(request):
    1314    """
     
    1718    If there is no 'user' attribute in the request, uses AnonymousUser (from
    1819    django.contrib.auth).
    1920    """
    20     if hasattr(request, 'user'):
    21         user = request.user
    22     else:
    23         from django.contrib.auth.models import AnonymousUser
    24         user = AnonymousUser()
     21    # If we access request.user, request.session is accessed, which results in
     22    # 'Vary: Cookie' being sent in every request that uses this context
     23    # processor, which can easily be every request on a site if
     24    # TEMPLATE_CONTEXT_PROCESSORS has this context processor added.  This kills
     25    # the ability to cache.  So, we carefully ensure these attributes are lazy.
     26    # We don't use django.utils.functional.lazy(), because that requires knowing
     27    # the class of the object we want to proxy, which could break with custom
     28    # auth backends.  LazyObject is a less complete but more flexible solution
     29    # that is good enough for wrapper 'User'. Also, lazy doesn't seem to work
     30    # fully with PermWrapper, so we have modified that to allow laziness.
     31    def get_user():
     32        if hasattr(request, 'user'):
     33            return request.user
     34        else:
     35            from django.contrib.auth.models import AnonymousUser
     36            return AnonymousUser()
     37
     38    def messages():
     39        for m in get_user().get_and_delete_messages():
     40            yield m
     41
    2542    return {
    26         'user': user,
    27         'messages': user.get_and_delete_messages(),
    28         'perms': PermWrapper(user),
     43        'user': ContextLazyObject(get_user),
     44        'messages': messages(),
     45        'perms':  PermWrapper(get_user),
    2946    }
    3047
    3148def debug(request):
     
    7592
    7693class PermWrapper(object):
    7794    def __init__(self, user):
    78         self.user = user
     95        self._user = user
     96
     97    def user(self):
     98        # Allow callable for laziness
     99        if callable(self._user):
     100            self._user = self._user()
     101        return self._user
     102    user = property(user)
    79103
    80104    def __getitem__(self, module_name):
    81105        return PermLookupDict(self.user, module_name)
    82        
     106
    83107    def __iter__(self):
    84108        # I am large, I contain multitudes.
    85109        raise TypeError("PermWrapper is not iterable.")
  • new file django/template/utils.py

    diff -r 6490bd644de2 django/template/utils.py
    - +  
     1from django.utils.functional import LazyObject
     2
     3class ContextLazyObject(LazyObject):
     4    """
     5    A lazy object initialised from any function, useful for lazily
     6    adding things to the Context.
     7
     8    Works best for compound objects of unknown type where you are only
     9    interested in the attributes, and won't need things like str(obj).  For
     10    simple objects of known type, use django.utils.functional.lazy
     11
     12    For sequences, use a generator instead.
     13    """
     14    def __init__(self, func):
     15        """
     16        Pass in a callable that returns the actual value to be used
     17        """
     18        self.__dict__['_setupfunc'] = func
     19        # For some reason, we have to inline LazyObject.__init__ here to avoid
     20        # recursion
     21        self._wrapped = None
     22
     23    def _setup(self):
     24        self._wrapped = self._setupfunc()
  • new file tests/regressiontests/context_processors/fixtures/context-processors-users.xml

    diff -r 6490bd644de2 tests/regressiontests/context_processors/fixtures/context-processors-users.xml
    - +  
     1<?xml version="1.0" encoding="utf-8"?>
     2<django-objects version="1.0">
     3    <object pk="100" model="auth.user">
     4        <field type="CharField" name="username">super</field>
     5        <field type="CharField" name="first_name">Super</field>
     6        <field type="CharField" name="last_name">User</field>
     7        <field type="CharField" name="email">super@example.com</field>
     8        <field type="CharField" name="password">sha1$995a3$6011485ea3834267d719b4c801409b8b1ddd0158</field>
     9        <field type="BooleanField" name="is_staff">True</field>
     10        <field type="BooleanField" name="is_active">True</field>
     11        <field type="BooleanField" name="is_superuser">True</field>
     12        <field type="DateTimeField" name="last_login">2007-05-30 13:20:10</field>
     13        <field type="DateTimeField" name="date_joined">2007-05-30 13:20:10</field>
     14        <field to="auth.group" name="groups" rel="ManyToManyRel"></field>
     15        <field to="auth.permission" name="user_permissions" rel="ManyToManyRel"></field>
     16    </object>
     17</django-objects>
  • new file tests/regressiontests/context_processors/templates/context_processors/auth_attrs_access.html

    diff -r 6490bd644de2 tests/regressiontests/context_processors/templates/context_processors/auth_attrs_access.html
    - +  
     1{{ user.id }}
  • new file tests/regressiontests/context_processors/templates/context_processors/auth_attrs_perms.html

    diff -r 6490bd644de2 tests/regressiontests/context_processors/templates/context_processors/auth_attrs_perms.html
    - +  
     1{% if perms.auth %}Has auth permissions{% endif %}
  • new file tests/regressiontests/context_processors/templates/context_processors/auth_attrs_test_access.html

    diff -r 6490bd644de2 tests/regressiontests/context_processors/templates/context_processors/auth_attrs_test_access.html
    - +  
     1{% if session_accessed %}Session accessed{% else %}Session not accessed{% endif %}
  • tests/regressiontests/context_processors/tests.py

    diff -r 6490bd644de2 tests/regressiontests/context_processors/tests.py
    a b  
    3636        self.assertContains(response, url)
    3737        response = self.client.post(url, {'path': '/blah/'})
    3838        self.assertContains(response, url)
     39
     40class AuthContextProcessorTests(TestCase):
     41    """
     42    Tests for the ``django.core.context_processors.auth`` processor
     43    """
     44    urls = 'regressiontests.context_processors.urls'
     45    fixtures = ['context-processors-users.xml']
     46
     47    def test_session_not_accessed(self):
     48        """
     49        Tests that the session is not accessed simply by including
     50        the auth context processor
     51        """
     52        response = self.client.get('/auth_processor_no_attr_access/')
     53        self.assertContains(response, "Session not accessed")
     54
     55    def test_session_is_accessed(self):
     56        """
     57        Tests that the session is accessed if the auth context processor
     58        is used and relevant attributes accessed.
     59        """
     60        response = self.client.get('/auth_processor_attr_access/')
     61        self.assertContains(response, "Session accessed")
     62
     63    def test_perms_attrs(self):
     64        self.client.login(username='super', password='secret')
     65        response = self.client.get('/auth_processor_perms/')
     66        self.assertContains(response, "Has auth permissions")
  • tests/regressiontests/context_processors/urls.py

    diff -r 6490bd644de2 tests/regressiontests/context_processors/urls.py
    a b  
    55
    66urlpatterns = patterns('',
    77    (r'^request_attrs/$', views.request_processor),
     8    (r'^auth_processor_no_attr_access/$', views.auth_processor_no_attr_access),
     9    (r'^auth_processor_attr_access/$', views.auth_processor_attr_access),
     10    (r'^auth_processor_perms/$', views.auth_processor_perms),
    811)
  • tests/regressiontests/context_processors/views.py

    diff -r 6490bd644de2 tests/regressiontests/context_processors/views.py
    a b  
    66def request_processor(request):
    77    return render_to_response('context_processors/request_attrs.html',
    88        RequestContext(request, {}, processors=[context_processors.request]))
     9
     10def auth_processor_no_attr_access(request):
     11    r1 = render_to_response('context_processors/auth_attrs_no_access.html',
     12        RequestContext(request, {}, processors=[context_processors.auth]))
     13    # *After* rendering, we check whether the session was accessed
     14    return render_to_response('context_processors/auth_attrs_test_access.html',
     15        {'session_accessed':request.session.accessed})
     16
     17def auth_processor_attr_access(request):
     18    r1 = render_to_response('context_processors/auth_attrs_access.html',
     19        RequestContext(request, {}, processors=[context_processors.auth]))
     20    return render_to_response('context_processors/auth_attrs_test_access.html',
     21        {'session_accessed':request.session.accessed})
     22
     23def auth_processor_perms(request):
     24    return render_to_response('context_processors/auth_attrs_perms.html',
     25        RequestContext(request, {}, processors=[context_processors.auth]))
Back to Top