question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Channels 2 not persisting session data set in `connect`

See original GitHub issue

- Your OS and runtime environment, and browser if applicable Development. Running a Django application from Pycharm.

- The versions of Channels, Daphne, Django, Twisted, and your ASGI backend (channels_redis normally)

channels==2.1.2 channels-redis==2.2.1 daphne==2.2.0 Django==1.11.6

- What you expected to happen vs. what actually happened

I’ve upgraded to Channels 2 specifically for the ability to access and modify the session from within a consumer, but that doesn’t seem to be the case. Basically, I want to identify AnonymousUsers and send them messages (each one, not all of them together)

Here’s my routing.py file:

application = ProtocolTypeRouter({
    # (http->django views is added by default)
    'websocket': AuthMiddlewareStack(
        URLRouter(
            core.routing.websocket_urlpatterns
        )
    ),
})

Here’s my consumers.py file:

class ChatConsumer(WebsocketConsumer):
    def connect(self):
        if self.scope['user'].is_authenticated:
            user_id = str(self.scope['user'].id)
            self.scope['session']['user_identifier']= user_id
            self.group_name = user_id
        else:
            user_id = str(self.scope['user']) + str(uuid.uuid4())
            self.scope['session']['user_identifier'] = user_id
            self.group_name = user_id

        self.scope['session'].save()

        print(f" in consumer: {self.scope['session']['user_identifier']}")

        # Join room group
        async_to_sync(self.channel_layer.group_add)(
            self.group_name,
            self.channel_name
        )

        self.accept()

And in the view where I need to send, I’m trying to get the group_name (user_identifier) from the session in case of an AnonymousUser:

def get_spotify_link(request):
    if request.user.is_authenticated:
        user_identifier = str(request.user.id)
    else:
        user_identifier = request.session['user_identifier']
        print(f"in get_spotify_link: {user_identifier}")

However, I’m intermittently (95% of the time) getting KeyError: 'user_identifier'.

- How you’re running Channels (runserver? daphne/runworker? Nginx/Apache in front?) runserver

  • Console logs and full tracebacks of any errors
Internal Server Error: /get_spotify_link/
Traceback (most recent call last):
  File "/Users/myusername/.virtualenvs/bap_dev/lib/python3.6/site-packages/django/core/handlers/exception.py", line 41, in inner
    response = get_response(request)
  File "/Users/myusername/.virtualenvs/bap_dev/lib/python3.6/site-packages/django/core/handlers/base.py", line 187, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/Users/myusername/.virtualenvs/bap_dev/lib/python3.6/site-packages/django/core/handlers/base.py", line 185, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/myusername/PycharmProjects/artist_notify/core/views.py", line 442, in get_spotify_link
    user_identifier = request.session['user_identifier']
  File "/Users/myusername/.virtualenvs/bap_dev/lib/python3.6/site-packages/django/contrib/sessions/backends/base.py", line 57, in __getitem__
    return self._session[key]
KeyError: 'user_identifier'
[2018/07/08 20:54:03] WebSocket HANDSHAKING / [127.0.0.1:65478]
 in consumer: AnonymousUser8883761c-f673-413e-82e2-413d1cbb17e3
[2018/07/08 20:54:03] WebSocket CONNECT / [127.0.0.1:65478]
Internal Server Error: /get_spotify_link/
Traceback (most recent call last):
  File "/Users/myusername/.virtualenvs/bap_dev/lib/python3.6/site-packages/django/core/handlers/exception.py", line 41, in inner
    response = get_response(request)
  File "/Users/myusername/.virtualenvs/bap_dev/lib/python3.6/site-packages/django/core/handlers/base.py", line 187, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/Users/myusername/.virtualenvs/bap_dev/lib/python3.6/site-packages/django/core/handlers/base.py", line 185, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/myusername/PycharmProjects/artist_notify/core/views.py", line 442, in get_spotify_link
    user_identifier = request.session['user_identifier']
  File "/Users/myusername/.virtualenvs/bap_dev/lib/python3.6/site-packages/django/contrib/sessions/backends/base.py", line 57, in __getitem__
    return self._session[key]
KeyError: 'user_identifier'

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:13 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
MartenBEcommented, Aug 23, 2021

I am also experiencing this. My django right now only has a websocket endpoint (no views) and the session_key changes during every connect:

class GameConsumer(WebsocketConsumer):
    def connect(self):
        self.game_name = self.scope["url_route"]["kwargs"]["game_name"]
        self.session = self.scope["session"]
        self.session.save() # Necessary to generate the session_key
        logger.debug(f"GameConsumer started with session ID: {self.session.session_key}")

This issue should be reopened @andrewgodwin

1reaction
nrempelcommented, Apr 26, 2020

Hi @andrewgodwin I’m also seeing the same problem. I’m not using celery at all.

I’ve tried the default session storage as well as this 3rd party driver: https://github.com/martinrusev/django-redis-sessions

It appears that the session cookie is not being set in the browser when scope["session"].save() is called.


class ChatConsumer(WebsocketConsumer):
    def connect(self):
        self.room_name = self.scope["url_route"]["kwargs"]["room_name"]
        self.room_group_name = "chat_%s" % self.room_name

        LOGGER.info(self.scope["session"].items())
        self.scope["session"]["test"] = "set"
        LOGGER.info(self.scope["session"].items())
        self.scope["session"].save()
        LOGGER.info(self.scope["session"].session_key)

        # Join room group
        async_to_sync(self.channel_layer.group_add)(
            self.room_group_name, self.channel_name
        )

        self.accept()

Then refreshing my page which makes the websocket connection twice results in the following logs:

[0] django_1    | 2020-04-26 17:24:34,710 turnips.consumers INFO     dict_items([])
[0] django_1    | 2020-04-26 17:24:34,711 turnips.consumers INFO     dict_items([('test', 'set')])
[0] django_1    | 2020-04-26 17:24:34,722 turnips.consumers INFO     gfxw8ljci68qajcidptig15ay5txi7d6

[0] django_1    | 2020-04-26 17:24:39,054 turnips.consumers INFO     dict_items([])
[0] django_1    | 2020-04-26 17:24:39,054 turnips.consumers INFO     dict_items([('test', 'set')])
[0] django_1    | 2020-04-26 17:24:39,058 turnips.consumers INFO     bukqwz3tvm60jj8uoyli5ncgppvnoocr

The javascript code to connect is straight from the tutorial. The following middleware is applied:

application = ProtocolTypeRouter(
    {
        # (http->django views is added by default)
        "websocket": SessionMiddlewareStack(
            URLRouter(turnips_routing.websocket_urlpatterns)
        ),
    }
)

No cookies are set in the browser.

Is there something missing here? Happy to open new ticket if this appears to be a different issue.

Thanks.

Edit:

It appears that if I use the session elsewhere first, it works. For instance, I call request.session.set_test_cookie() in another (completely unrelated) django view. Then, as long as the sessionId cookie has been set first, it works fine. But the sessionId key must be set first. This makes sense since the session state is updated in the session store and the cookie can pass the unchanged sessionId to reference the session state.

I thought cookies could be set during the ws handshake but maybe this is not the case or has recently changed. If I’m missing something please let me know.

Solution:

Modify the session once before connection with websockets so that the sessionId is set in the browser. Then session persistence should work. This won’t work with the cookie session backend.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Django-Channels 2 not persisting session data set in `connect`
I've upgraded to Channels 2 specifically for the ability to access and modify the session from within a consumer (and access it in...
Read more >
Sessions — Channels 4.0.0 documentation
SessionMiddleware in Channels supports standard Django sessions, and like all middleware, should be wrapped around the ASGI application that needs the session ......
Read more >
Sessions are not persisting between sessions - Laracasts
Hi I have an issue with my sessions. In my application I save things to the session in between page requests in my...
Read more >
Using Sessions and Session Persistence - Oracle Help Center
Using a Database for Persistent Storage (JDBC persistence). JDBC persistence stores session data in a database table using a schema provided for this...
Read more >
Slack Connect guide: work with external partners
Slack Connect allows you to work with people outside your company in channels and direct messages (DMs). By moving conversations out of emails,...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found