How can I make my view asynchronous?

Hi there,

I would like to introduce asynchronous behavior into my django application.

I’m trying to write an api endpoint which is called from a webhook.
However, processing the request is very I/O intensive and will cause the webhook to fail if it takes more that 10 seconds to complete.

I did a little bit of research and found that using django’s sync_to_async() might solve my problem.
My goal now is to return immediately with the response code 200 and start the heavy processing in a new thread.

Currently it looks like this:

@require_POST
@csrf_exempt
async def hook(request):
    payload = json.loads(request.body)
    event = payload['event_name']

    if event:
        logger.info(f"received event {event}")

        # run database update in a new thread
        loop = asyncio.get_event_loop()
        async_function = sync_to_async(
                fetch.handle_systemhook,
                thread_sensitive=False,)
        loop.create_task(async_function(payload=payload))

        # return 200 immediately to prevent the systemhook from failing
        return HttpResponse()

    else:
        logger.info(f"the received request did not contain the field 'event_name' and will be ignored")
        logger.debug(f"payload: {payload}")
        return HttpResponseBadRequest()

However, when the hook triggers, I get the following error:

Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/asgiref/sync.py", line 486, in thread_handler
    raise exc_info[1]
  File "/usr/local/lib/python3.9/site-packages/django/core/handlers/exception.py", line 42, in inner
    response = await get_response(request)
  File "/usr/local/lib/python3.9/site-packages/django/core/handlers/base.py", line 265, in _get_response_async
    self.check_response(response, callback)
  File "/usr/local/lib/python3.9/site-packages/django/core/handlers/base.py", line 337, in check_response
    raise ValueError(
ValueError: The view submodules.views.hook didn't return an HttpResponse object. It returned an unawaited coroutine instead. You may need to add an 'await' into your view.

I’m not really sure what to make of the error message, as I’m not explicitly returning a coroutine.

Could you tell me what I’m missing?
Is this the best way to achieve what I’m trying to do or should I take a different approach?

django is being run within docker with niginx and gunicorn/uvicorn:

gunicorn -w 3 -k uvicorn.workers.UvicornWorker siteroot.asgi:application --bind 0.0.0.0:8000

Nope, that’s not going to work with Django.

Introducing async into a view does not allow that view to continue executing after the view has exited. Once a view has returned, everything associated with that view ends.

You need to use something like Celery to spawn off any long-running tasks to a background process.

I see, I will try that.

Thank you for your quick response!