In Dango v4.2.3 I have to use some async views (long-running access to 3rd party APIs) and also have django.contrib.auth.middleware.AuthenticationMiddleware
in my MIDDLEWARE
setting. Now I hit the SynchronousOnlyOperation
exception. As far as I know, this will be solved with Django v5. Is there in the meantime some 3rd party middleware I could use or some other workaround?
You need to give more detail. Sync middleware are adapted as needed when running under ASGI, so you’re doing something more than just using the authentication middleware.
Ah, ok … I didn’t know that it should work. Do you haven an idea what could be wrong with my setup, or how I investigate it further?
This is how I define the view:
class DicomExplorerFormView(LoginRequiredMixin, View):
async def get(self, request: HttpRequest) -> HttpResponse:
....
Here is the full stack trace:
Environment:
Request Method: GET
Request URL: http://adit.krz.uni-heidelberg.de:8000/dicom-explorer/
Django Version: 4.2.3
Python Version: 3.11.4
Installed Applications:
['daphne',
'whitenoise.runserver_nostatic',
'adit.accounts.apps.AccountsConfig',
'registration',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.humanize',
'django.contrib.sites',
'revproxy',
'loginas',
'crispy_forms',
'crispy_bootstrap5',
'django_htmx',
'django_tables2',
'rest_framework',
'adrf',
'adit.core.apps.CoreConfig',
'adit.api.apps.ApiConfig',
'adit.selective_transfer.apps.SelectiveTransferConfig',
'adit.batch_query.apps.BatchQueryConfig',
'adit.batch_transfer.apps.BatchTransferConfig',
'adit.dicom_explorer.apps.DicomExplorerConfig',
'adit.token_authentication.apps.TokenAuthenticationConfig',
'adit.dicom_web.apps.DicomWebConfig',
'channels',
'debug_toolbar',
'debug_permissions',
'django_extensions']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.contrib.sites.middleware.CurrentSiteMiddleware',
'django_htmx.middleware.HtmxMiddleware',
'adit.core.middlewares.MaintenanceMiddleware',
'adit.core.middlewares.TimezoneMiddleware',
'debug_toolbar.middleware.DebugToolbarMiddleware']
Traceback (most recent call last):
File "/opt/pysetup/.venv/lib/python3.11/site-packages/django/core/handlers/exception.py", line 42, in inner
response = await get_response(request)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/pysetup/.venv/lib/python3.11/site-packages/django/core/handlers/base.py", line 253, in _get_response_async
response = await wrapped_callback(
File "/opt/pysetup/.venv/lib/python3.11/site-packages/django/views/generic/base.py", line 104, in view
return self.dispatch(request, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/pysetup/.venv/lib/python3.11/site-packages/django/contrib/auth/mixins.py", line 71, in dispatch
if not request.user.is_authenticated:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/pysetup/.venv/lib/python3.11/site-packages/django/utils/functional.py", line 266, in inner
self._setup()
^^^^^^^^^^^^^
File "/opt/pysetup/.venv/lib/python3.11/site-packages/django/utils/functional.py", line 419, in _setup
self._wrapped = self._setupfunc()
^^^^^^^^^^^^^^^^^
File "/opt/pysetup/.venv/lib/python3.11/site-packages/django/contrib/auth/middleware.py", line 25, in <lambda>
request.user = SimpleLazyObject(lambda: get_user(request))
^^^^^^^^^^^^^^^^^
File "/opt/pysetup/.venv/lib/python3.11/site-packages/django/contrib/auth/middleware.py", line 11, in get_user
request._cached_user = auth.get_user(request)
^^^^^^^^^^^^^^^^^^^^^^
File "/opt/pysetup/.venv/lib/python3.11/site-packages/django/contrib/auth/__init__.py", line 198, in get_user
user = backend.get_user(user_id)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/pysetup/.venv/lib/python3.11/site-packages/django/contrib/auth/backends.py", line 161, in get_user
user = UserModel._default_manager.get(pk=user_id)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/pysetup/.venv/lib/python3.11/site-packages/django/db/models/manager.py", line 87, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/pysetup/.venv/lib/python3.11/site-packages/django/db/models/query.py", line 633, in get
num = len(clone)
^^^^^^^^^^
File "/opt/pysetup/.venv/lib/python3.11/site-packages/django/db/models/query.py", line 380, in __len__
self._fetch_all()
^^^^^^^^^^^^^^^^^
File "/opt/pysetup/.venv/lib/python3.11/site-packages/django/db/models/query.py", line 1881, in _fetch_all
self._result_cache = list(self._iterable_class(self))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/pysetup/.venv/lib/python3.11/site-packages/django/db/models/query.py", line 91, in __iter__
results = compiler.execute_sql(
File "/opt/pysetup/.venv/lib/python3.11/site-packages/django/db/models/sql/compiler.py", line 1560, in execute_sql
cursor = self.connection.cursor()
^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/pysetup/.venv/lib/python3.11/site-packages/django/utils/asyncio.py", line 24, in inner
raise SynchronousOnlyOperation(message)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Exception Type: SynchronousOnlyOperation at /dicom-explorer/
Exception Value: You cannot call this from an async context - use a thread or sync_to_async.
EDIT:
I also tried to remove all nonessential middlewares (see below), but still got the exception.
MIDDLEWARE = [
"django.contrib.sessions.middleware.SessionMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
]
Hi @medihack — you’re still not showing the actual code that’s raising the issue…
I’m going to assume — but guessing — it’s this issue that you’re hitting:
https://code.djangoproject.com/ticket/31920
Unwrapping request.user
in an async view…
So is there a workaround? — You can use one of the examples from the report (Cases 4 and 5 from the description there.)
Something like…
user = await sync_to_async(bool)(request.user)
… should get you off the ground.
Hi @carltongibson - you are absolutely right. You know what I do without seeing it (I really hope my wife doesn’t have this skill, too ). I simply rendered my template like usual (return render(request, "dicom_explorer/query_form.html" {"form": form})
, which indeed can not work as I access the user in there. But even if I don’t access the user in there but have Django debug toolbar enabled, it would raise the error as debug toolbar seems to access the user object. That is why I thought it was the middleware, because of the lazy evaluation in there. Wrapping the whole render in sync_to_async
works fine. So, thanks a lot for the help!
By the way, big fan of yours and the Django podcast!
Great!
Glad you enjoy it. Thanks