My Django Project heavily relies on sessions, and the Framework does a very good job with this. It manages Session Cookies, the Session Database and so on, mostly done through the SessionMiddleware.
But there a some Views that explicitly do not require a session. And those Views often appear as an entry to the page.
Can I exclude these Views from the SessionMiddleware creating new sessions? If a new (anonymous) user visits such a View and leaves, there is no need for setting a Cookie or creating a database record in the sessions table.
I've gone the "Adapt the Middleware" way, as it looks like a session is created only on an access to the session or - in case of totally new sessions - during handling the response in the SessionMiddleware.
So I created this Middleware, that extends the SessionMiddleware
:
from django.contrib.sessions.middleware import SessionMiddleware
from django.http import HttpRequest
class ConditionalSessionMiddleware(SessionMiddleware):
def process_request(self, request: HttpRequest):
# Default: We need a Session
setattr(request, '_conditional_session_middleware_session_required', True)
return super(ConditionalSessionMiddleware, self).process_request(request)
def process_response(self, request: HttpRequest, response):
# Is still a Session required?
required = getattr(request, '_conditional_session_middleware_session_required', True)
# or is the user not anonymous?
if required or not request.user.is_anonymous:
return super(ConditionalSessionMiddleware, self).process_response(request, response)
else:
return response
To use it, I created a simple function decorator:
from types import MethodType
class NoSessionUsed:
def __init__(self, f):
self.__func = f
def __call__(self, *args, **kwargs):
for a in args:
if isinstance(a, HttpRequest):
setattr(a, '_conditional_session_middleware_session_required', False)
break
return self.__func(*args, **kwargs)
def __get__(self, instance, owner):
if instance:
return MethodType(self, instance)
else:
return self
In the class-based views, I use the decorator on the dispatch
method:
@NoSessionUsed
def dispatch(self, request, *args, **kwargs):
return super(MyClassBasedView, self).dispatch(request, *args, **kwargs)
You can also use it in urls.py, e.g. for Sitemaps and Feeds, where you have no direct access to the Views:
urlpatterns = [
path('rss.xml', NoSessionUsed(RssFeed()), name='rss'),
path('atom.xml', NoSessionUsed(AtomFeed()), name='atom'),
path('sitemap.xml', NoSessionUsed(views.index), {
'sitemaps': SITEMAPS,
'sitemap_url_name': 'sitemaps:sitemap.section'
}, name='sitemap'),
path('sitemaps/sitemap-<section>.xml', NoSessionUsed(views.sitemap), {
'sitemaps': SITEMAPS
}, name='sitemap.section'),
]
To activate the new Middleware, I replaced the SessionMiddleware
in the settings.py MIDDLEWARE
section with the new ConditionalSessionMiddleware
.
Of course there are downsides:
This might not be the perfect solution, but it's quite simple and works for me.