hapytex / django-single-session

Allow a user to only have a single active session in Django.
https://pypi.org/project/django-single-session/
BSD 3-Clause "New" or "Revised" License
16 stars 7 forks source link

`UpdateError` with `SINGLE_USER_SESSION=True` #11

Open yozachar opened 1 week ago

yozachar commented 1 week ago

Hi, I see an UpdateError when SINGLE_USER_SESSION=True or not explicitly set.

If SINGLE_USER_SESSION=False this error disappears.

Here is the traceback:

$ python manage.py runserver

Watching for file changes with StatReloader
Performing system checks...

~/project/.venv/Lib/site-packages/allauth/exceptions.py:9: UserWarning: allauth.exceptions is deprecated, use allauth.core.exceptions
  warnings.warn("allauth.exceptions is deprecated, use allauth.core.exceptions")

System check identified no issues (0 silenced).
September 17, 2024 - 15:33:21
Django version 5.1.1, using settings 'core.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

[17/Sep/2024 15:34:41] "GET /auth/login/ HTTP/1.1" 200 13605
[17/Sep/2024 15:34:41] "GET /static/cms/img/login-bg.webp HTTP/1.1" 404 1990
Internal Server Error: /auth/login/
Traceback (most recent call last):
  File "~/project/.venv/Lib/site-packages/django/contrib/sessions/backends/db.py", line 127, in save
    obj.save(
  File "~/project/.venv/Lib/site-packages/django/db/models/base.py", line 891, in save
    self.save_base(
  File "~/project/.venv/Lib/site-packages/django/db/models/base.py", line 997, in save_base
    updated = self._save_table(
              ^^^^^^^^^^^^^^^^^
  File "~/project/.venv/Lib/site-packages/django/db/models/base.py", line 1133, in _save_table
    raise DatabaseError("Forced update did not affect any rows.")
django.db.utils.DatabaseError: Forced update did not affect any rows.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "~/project/.venv/Lib/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "~/project/.venv/Lib/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/project/.venv/Lib/site-packages/django/views/generic/base.py", line 104, in view
    return self.dispatch(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/project/django-cmil-cms/core/cms/mixins.py", line 88, in dispatch
    response = super().dispatch(request, *args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/project/.venv/Lib/site-packages/django/utils/decorators.py", line 48, in _wrapper
    return bound_method(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/project/.venv/Lib/site-packages/allauth/decorators.py", line 12, in wrap
    resp = function(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/project/.venv/Lib/site-packages/django/utils/decorators.py", line 48, in _wrapper
    return bound_method(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/project/.venv/Lib/site-packages/django/views/decorators/debug.py", line 143, in sensitive_post_parameters_wrapper
    return view(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/project/.venv/Lib/site-packages/django/utils/decorators.py", line 48, in _wrapper
    return bound_method(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/project/.venv/Lib/site-packages/django/views/decorators/cache.py", line 80, in _view_wrapper
    response = view_func(request, *args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/project/.venv/Lib/site-packages/allauth/account/views.py", line 89, in dispatch
    return super().dispatch(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/project/.venv/Lib/site-packages/allauth/account/mixins.py", line 40, in dispatch
    response = super().dispatch(request, *args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/project/.venv/Lib/site-packages/django/views/generic/base.py", line 143, in dispatch
    return handler(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/project/.venv/Lib/site-packages/allauth/account/mixins.py", line 69, in post
    response = self.form_valid(form)
               ^^^^^^^^^^^^^^^^^^^^^
  File "~/project/django-cmil-cms/core/cms/views.py", line 118, in form_valid
    return super().form_valid(form)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/project/.venv/Lib/site-packages/allauth/account/views.py", line 102, in form_valid
    return form.login(self.request, redirect_url=redirect_url)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/project/.venv/Lib/site-packages/allauth/account/forms.py", line 198, in login
    ret = flows.login.perform_password_login(request, credentials, login)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/project/.venv/Lib/site-packages/allauth/account/internal/flows/login.py", line 74, in perform_password_login
    return perform_login(request, login)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/project/.venv/Lib/site-packages/allauth/account/internal/flows/login.py", line 83, in perform_login
    return resume_login(request, login)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/project/.venv/Lib/site-packages/allauth/account/internal/flows/login.py", line 95, in resume_login
    adapter.login(request, login.user)
  File "~/project/.venv/Lib/site-packages/allauth/account/adapter.py", line 513, in login
    django_login(request, user)
  File "~/project/.venv/Lib/site-packages/django/contrib/auth/__init__.py", line 152, in login
    user_logged_in.send(sender=user.__class__, request=request, user=user)
  File "~/project/.venv/Lib/site-packages/django/dispatch/dispatcher.py", line 189, in send
    response = receiver(signal=self, sender=sender, **named)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/project/.venv/Lib/site-packages/single_session/signals.py", line 47, in remove_other_sessions
    request.session.save()
  File "~/project/.venv/Lib/site-packages/django/contrib/sessions/backends/db.py", line 136, in save
    raise UpdateError
django.contrib.sessions.backends.base.UpdateError
[17/Sep/2024 15:34:48] "POST /auth/login/ HTTP/1.1" 500 140683

How can I fix it?

KommuSoft commented 1 week ago

Seems like some race condition in the database. Is something else manipulating the session objects? Do you use asynchronous views?

yozachar commented 1 week ago

Thanks for the reply!

Is something else manipulating the session objects?

I'm not accessing request.session anywhere in the code. What else must I look for?

Do you use asynchronous views?

No, none.