drec4s
drec4s

Reputation: 8077

Django - Session management

I wanted to prevent the same user account to have multiple active sessions within my app, and followed the answer from this question.

I implement this into my models.py:

from django.conf import settings
from django.db import models
from django.contrib.sessions.models import Session

class UserSession(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    session = models.OneToOneField(Session, on_delete=models.CASCADE)


from django.contrib.auth import user_logged_in
from django.dispatch.dispatcher import receiver

@receiver(user_logged_in)
def remove_other_sessions(sender, user, request, **kwargs):
    # remove other sessions
    Session.objects.filter(usersession__user=user).delete()

    # save current session
    request.session.save()

    # create a link from the user to the current session (for later removal)
    UserSession.objects.get_or_create(
        user=user,
        session=Session.objects.get(pk=request.session.session_key)
    )

It works mostly fine, however, I noticed a weird behavior.

If I login with one account, and then open an incognito window and login with the same credentials, the first one is logged out (which is what I wanted).

However, if I login with an account, and then close the browser, the next time I want to login I get an error at the line:

# save current session
request.session.save()

with the following traceback (without much info..):

UpdateError at /

No exception message supplied

The console log shows that the error is coming from

django.contrib.sessions.backends.base.UpdateError

After this, if I refresh the page, I can then login successfully.

Any insights of what might be happening?

Upvotes: 0

Views: 906

Answers (2)

j0hnnny
j0hnnny

Reputation: 1

The answer of @GwynBleidD is good! But heres a small bug:

if request.session.session_key:
        old_sessions = old_sessions.exclude(session_key=request.session.session_key)

should be write as:

if request.session.session_key:
        old_sessions = old_sessions.exclude(session_key=request.session.session_key).delete()
else:
    old_sessions.delete()
or there will be an error when the second time you try to login on a same computer.

Upvotes: 0

GwynBleidD
GwynBleidD

Reputation: 20539

You're receiving that error because there is no longer session to update in the database, you've removed all active sessions 2 lines above that. To fix that, simply omit currently active session when cleaning them up:

@receiver(user_logged_in)
def remove_other_sessions(sender, user, request, **kwargs):
    # remove other sessions
    old_sessions = Session.objects.filter(usersession__user=user)
    if request.session.session_key:
        old_sessions = old_sessions.exclude(session_key=request.session.session_key)
    old_sessions.delete()

    # save current session
    request.session.save()

    # create a link from the user to the current session (for later removal)
    UserSession.objects.get_or_create(
        user=user,
        session=Session.objects.get(pk=request.session.session_key)
    )

Upvotes: 1

Related Questions