[Edit: This turned out to be a question about arbitrary Python objects, something I knew in general but temporarily grew stupid in this particular case and forgot. See this question and this answer, also the Python Data model reference which explains that c.x is (to the first approximation) equivalent to c.__dict__['x'] and in particular c.x = y is equivalent to c.__dict__['x'] = y, and therefore one can in general add arbitrary attributes to Python objects unless it's explicitly suppressed.]
It seems that arbitrary attributes can be set on an object of type HttpRequest from django.http (and indeed, this is done by various middleware). For instance, consider the following ipython session:
In [1]: from django.http import HttpRequest
In [2]: request = HttpRequest()
In [3]: request.asdf_foo = 5
In [4]: request.asdf_foo
Out[4]: 5
In [5]: setattr(request, 'asdgadshasdh', 42)
In [6]: request.asdgadshasdh
Out[6]: 42
But I cannot figure out from looking at the source code (current version) how this has been achieved. The HttpRequest class just inherits from object, and doesn't seem to override __setattr__ anwyhere:
class HttpRequest(object):
...
So what's going on, how does it work, how come request.asdf = 42 doesn't raise AttributeError? This doesn't seem to be documented anywhere either.
(My actual original question was whether setting such attributes (e.g. request.user_has_logged_in in middleware is secure and cannot be faked by malicious users, but I guess it is fine, because standard authentication middleware do similar things — right now I'm more surprised by how it works in the first place.)