Question


Regarding AuthenticationMiddleware, why did Django 1.3 handle the user property at class level, and why was it changed to an instance property in 1.4?


This is from 1.3

class LazyUser(object):
    def __get__(self, request, obj_type=None):
        if not hasattr(request, '_cached_user'):
            from django.contrib.auth import get_user
            request._cached_user = get_user(request)
        return request._cached_user


class AuthenticationMiddleware(object):
    def process_request(self, request):
        request.__class__.user = LazyUser()
        return None


And this from 1.4

def get_user(request):
    if not hasattr(request, '_cached_user'):
        request._cached_user = auth.get_user(request)
    return request._cached_user


class AuthenticationMiddleware(object):
    def process_request(self, request):
        assert hasattr(request, 'session'), "The Django authentication middleware requires session middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.contrib.sessions.middleware.SessionMiddleware'."

        request.user = SimpleLazyObject(lambda: get_user(request))
        # SimpleLazyObject inherits from LazyObject


In 1.3, I understood that process_request assigned LazyUser() to the class level user attribute, which to me basically means two things:

  • The HttpRequest class (request.__class__) stored its user attribute between two requests, so future request objects had access to it.
  • Whenever a view function tried to access request.user, the LazyUser object's __get__ method was triggered and returned a user object, either from the request's cache or from auth storage.

Am I correct about it?

Also, I have noticed in 1.4 these two major changes:

  • SimpleLazyObject is assigned to request, not to its class (so, it is an instance property).
  • LazyObject and SimpleLazyObject don't define (customize) their own __get__ method.

This way, when is get_user triggered?
How does AuthenticationMiddleware now store request.user between two requests, and override the statelessness of Http?

Was it helpful?

Solution

No. The two versions are equivalent. The 1.4 version is simpler, though.

How it used to work

The LazyUser object implemented the descriptor protocol.

Descriptors are a bit convoluted to understand, but the general idea here is that although user was a class attribute, accessing request.user woud call request.__class__.user.__get__(request), and therefore let the __get__ method access the request parameters, which it would use to return the user bound to the request.

How it works now

The user property uses the SimpeLazyObject construct to achieve the same goal: avoid loading all the user machinery if it's not going to be needed.

As to when get_user is called:

When you access an attribute on request.user, for example request.user.attr1, then request.user.__getattr__('attr1') will be called, which will in turn call request.user._setup(), which will eventually call get_user.

How Django creates statefulness

Through the use of Sessions. Have a look at django's session middleware.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top