I had a django 2 app and i used django channels for socket connection.
i just update django to version 3. and now daphne show this error when i try to make a socket connection. i had not any problem with django 2.
[Failure instance: Traceback: <class 'django.core.exceptions.SynchronousOnlyOperation'>: You cannot call this from an async context - use a thread or sync_to_async.
/home/ubuntu/pl_env/lib/python3.6/site-packages/autobahn/websocket/protocol.py:2844:processHandshake
/home/ubuntu/pl_env/lib/python3.6/site-packages/txaio/tx.py:429:as_future
/home/ubuntu/pl_env/lib/python3.6/site-packages/twisted/internet/defer.py:151:maybeDeferred
/home/ubuntu/pl_env/lib/python3.6/site-packages/daphne/ws_protocol.py:83:onConnect
--- <exception caught here> ---
/home/ubuntu/pl_env/lib/python3.6/site-packages/twisted/internet/defer.py:151:maybeDeferred
/home/ubuntu/pl_env/lib/python3.6/site-packages/daphne/server.py:201:create_application
/home/ubuntu/pl_env/lib/python3.6/site-packages/channels/routing.py:54:__call__
/home/ubuntu/pl_env/lib/python3.6/site-packages/channels/security/websocket.py:37:__call__
/home/ubuntu/petroline_django/orders/token_auth.py:25:__call__
/home/ubuntu/pl_env/lib/python3.6/site-packages/django/db/models/manager.py:82:manager_method
/home/ubuntu/pl_env/lib/python3.6/site-packages/django/db/models/query.py:411:get
/home/ubuntu/pl_env/lib/python3.6/site-packages/django/db/models/query.py:258:__len__
/home/ubuntu/pl_env/lib/python3.6/site-packages/django/db/models/query.py:1261:_fetch_all
/home/ubuntu/pl_env/lib/python3.6/site-packages/django/db/models/query.py:57:__iter__
/home/ubuntu/pl_env/lib/python3.6/site-packages/django/db/models/sql/compiler.py:1142:execute_sql
/home/ubuntu/pl_env/lib/python3.6/site-packages/django/utils/asyncio.py:24:inner
it says the problem is in token_auth.py, line 25. this line is token = Token.objects.get(key=token_key)
this is my token_auth.py that handles token authentication.
from channels.auth import AuthMiddlewareStack
from django.contrib.auth.models import AnonymousUser
from django.db import close_old_connections
from rest_framework.authtoken.models import Token
class TokenAuthMiddleware:
"""
Token authorization middleware for Django Channels 2
see:
https://channels.readthedocs.io/en/latest/topics/authentication.html#custom-authentication
"""
def __init__(self, inner):
self.inner = inner
def __call__(self, scope):
headers = dict(scope['headers'])
if b'authorization' in headers:
try:
token_name, token_key = headers[b'authorization'].decode().split()
if token_name == 'Token':
# Close old database connections to prevent usage of timed out connections
close_old_connections()
token = Token.objects.get(key=token_key)
scope['user'] = token.user
except Token.DoesNotExist:
scope['user'] = AnonymousUser()
return self.inner(scope)
TokenAuthMiddlewareStack = lambda inner: TokenAuthMiddleware(AuthMiddlewareStack(inner))
AlexMercer :
Thanks to @ivissani answers, i fixed my TokenAuthMiddleware with some of SessionMiddleware codes. \n\nI have opened an issue for django channels about updating docs.\n\n@database_sync_to_async\ndef get_user(token_key):\n try:\n return Token.objects.get(key=token_key).user\n except Token.DoesNotExist:\n return AnonymousUser()\n\n\nclass TokenAuthMiddleware:\n \"\"\"\n Token authorization middleware for Django Channels 2\n see:\n https://channels.readthedocs.io/en/latest/topics/authentication.html#custom-authentication\n \"\"\"\n\n def __init__(self, inner):\n self.inner = inner\n\n def __call__(self, scope):\n return TokenAuthMiddlewareInstance(scope, self)\n\n\nclass TokenAuthMiddlewareInstance:\n def __init__(self, scope, middleware):\n self.middleware = middleware\n self.scope = dict(scope)\n self.inner = self.middleware.inner\n\n async def __call__(self, receive, send):\n headers = dict(self.scope['headers'])\n if b'authorization' in headers:\n token_name, token_key = headers[b'authorization'].decode().split()\n if token_name == 'Token':\n self.scope['user'] = await get_user(token_key)\n inner = self.inner(self.scope)\n return await inner(receive, send) \n\n\nTokenAuthMiddlewareStack = lambda inner: TokenAuthMiddleware(AuthMiddlewareStack(inner))\n",
2020-01-08T20:26:01
Mathieu Barbot :
Fixed by using @database_sync_to_async decorator:\n\n(see https://github.com/MathieuB1/KOREK-backend/commit/ff6a4b542cda583a1d5abbf200a5d57ef328cae0#diff-95e545fb374a9ed7e8af8c31087a3f29)\n\n\n\nimport jwt, re\nimport traceback\nfrom channels.auth import AuthMiddlewareStack\nfrom channels.db import database_sync_to_async\nfrom django.contrib.auth.models import AnonymousUser\nfrom django.conf import LazySettings\nfrom jwt import InvalidSignatureError, ExpiredSignatureError, DecodeError\nfrom django.contrib.auth.models import User\nfrom django.contrib.sessions.models import Session\n\nsettings = LazySettings()\n\nfrom django.db import close_old_connections\n\n@database_sync_to_async\ndef close_connections():\n close_old_connections()\n\n@database_sync_to_async\ndef get_user(user_jwt):\n try:\n return User.objects.get(id=user_jwt)\n except User.DoesNotExist:\n return AnonymousUser()\n\n\nclass TokenAuthMiddleware:\n \"\"\"\n Token authorization middleware for Django Channels 2\n \"\"\"\n def __init__(self, inner):\n self.inner = inner\n\n def __call__(self, scope):\n # Close old database connections to prevent usage of timed out connections\n close_connections()\n\n # Login with JWT\n try:\n if scope['subprotocols'][0] != 'None':\n\n token = scope['subprotocols'][0]\n\n try:\n user_jwt = jwt.decode(\n token,\n settings.SECRET_KEY,\n )\n scope['user'] = get_user(user_jwt['user_id'])\n return self.inner(scope)\n\n except (InvalidSignatureError, KeyError, ExpiredSignatureError, DecodeError):\n traceback.print_exc()\n pass\n except Exception as e:\n traceback.print_exc()\n else:\n raise\n",
2020-03-06T18:09:53