29π
If you return a QuerySet from your all_unexpired_sessions_for_user
function, you could limit your database hits to two:
def all_unexpired_sessions_for_user(user):
user_sessions = []
all_sessions = Session.objects.filter(expire_date__gte=datetime.datetime.now())
for session in all_sessions:
session_data = session.get_decoded()
if user.pk == session_data.get('_auth_user_id'):
user_sessions.append(session.pk)
return Session.objects.filter(pk__in=user_sessions)
def delete_all_unexpired_sessions_for_user(user, session_to_omit=None):
session_list = all_unexpired_sessions_for_user(user)
if session_to_omit is not None:
session_list.exclude(session_key=session_to_omit.session_key)
session_list.delete()
This gives you a total of two hits to the database. Once to loop over all of the Session
objects, and once to delete all of the sessions. Unfortunately, I donβt know of a more direct way to filter through the sessions themselves.
8π
Another version of a function using list comprehension that will just straight up delete every unexpired session of a user:
from django.utils import timezone
from django.contrib.sessions.models import Session
def delete_all_unexpired_sessions_for_user(user):
unexpired_sessions = Session.objects.filter(expire_date__gte=timezone.now())
[
session.delete() for session in unexpired_sessions
if str(user.pk) == session.get_decoded().get('_auth_user_id')
]
- [Django]-Django self-recursive foreignkey filter query for all childs
- [Django]-Django β how to make translation work?
- [Django]-How to debug gunicorn failure issues? (Worker failed to boot)
2π
The most efficient way is to store the session id of the user during login. You can access the session ID using request.session._session_key and store it in a separate model which has reference to the user. Now when you want to remove all the sessions of the user, just query this model which will return all the active sessions for the user in question. Now you need to delete only these sessions from the session table. Much better than having to look up all the sessions to filter out just sessions for a particular user.
- [Django]-Generating PDFs from SVG input
- [Django]-Change the width of form elements created with ModelForm in Django
- [Django]-Django gives Bad Request (400) when DEBUG = False
1π
It might be helpful to use:
- django-password-session in order to invalidate userβs sessions after a password is changed. Since Django 1.7 this feature implemented out of the box.
- django-admin clearsessions to remove expired cookies
- [Django]-Comparing querysets in django TestCase
- [Django]-Django apps aren't loaded yet when using asgi
- [Django]-Is there something similar to 'rake routes' in django?
0π
This is not a direct answer, but it solves your problem and reduces DB hits to zero. With recent versions of Django you can use the cookie based session backend:
https://docs.djangoproject.com/en/dev/topics/http/sessions/#cookie-session-backend
- [Django]-How to install virtualenv without using sudo?
- [Django]-How to show a PDF file in a Django view?
- [Django]-How to create a custom decorator in Django?
0π
We were in a similar situation where we had an SSO app that used diffrent kind of authentication/authorization solutions like OAuth, Token for CSR apps, and Cookie-Session for SSR apps. During logout we had to clear all session and tokens from all the apps to logout user realtime.
If you watch closely the source code of Session model in django you will find out all rows have a session_key.
The main idea is to find the userβs session_key in login then store it somewhere (it is better to be the userβs model itself or a model that has a FK to it), then restore and delete session rows that have this key during logout.
Example:
# in model.py a regular User model
from django.contrib.postgres.fields import ArrayField
class User(AbstractUser):
# other fields
# This could be a JsonField to store other data of logedin user
# like IP or Agent to have more control on users logout
session_keys = ArrayField(models.CharField(max_length=255), default=list)
# in views.py a simple login view
def login(request):
form = LoginForm(request.POST or None, request=request)
if form.is_valid():
form.save()
return redirect(request.GET.get('next'))
context = {
'form': form,
'next': request.GET.get('next'),
}
return render(request, 'register.html', context)
# in forms.py a form that check regular password and user name checks
class LoginForm(forms.Form):
username = forms.CharField(required=True)
password = forms.CharField(required=True)
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super().__init__(*args, **kwargs)
def clean(self):
# some check
def save(self):
# create a session for user
# I had multiple backend if you have one session backend
# there is no need to provide it
login(self.request, self.user, backend='django.contrib.auth.backends.ModelBackend')
# if everything be ok after creating session, login
# function will add created session instance to request
# object as a property and we can find its key
# (it is little complicated then what I said...)
self.user.session_keys.append(self.request.session.session_key)
self.user.save()
# then again in views.py
from django.contrib.sessions.models import Session
def logout(request):
user = self.request.user
Session.objects.filter(session_key__in=user.session_keys).delete()
user.session_keys = []
user.save()
return render(request, 'logout.html')
This solution is for django 3 but for other versions may session behave diffrent
- [Django]-Best practice for Django project working directory structure
- [Django]-Celery: When should you choose Redis as a message broker over RabbitMQ?
- [Django]-How to add clickable links to a field in Django admin?