Why do I have to create a new thread for updating a database record? (Django Rest Framework)

I'm using Django Rest Framework for creating an API.

The idea is that the user uploads some data using a POST request. This data is uploaded to a database. Once uploaded, a new Python thread is created, running some operations on said data. Then when operations have finished, the result is written to the original database record.

Using this approach the API remains accessible, while also working on the operations in the background. The user can check the operations status using a GET request.

For some reason, when trying to update the record with the operation results, I got the following error: SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async

It was confusing, since the operation and updating function was already running using a thread.

I would not get the error when substituting the operations with some simple mock-up test results (strings). Not even when using time.sleep() for a longer period than the actual operations would take. So I suspect the problem has something to do with the operations I'm running, even though they do not interact with Django whatsoever.

I fixed the problem by creating new threads specifically for saving the updated data to the database. But I'm still wondering: Why is that necessary?

@api_view(['POST'])
def create_job(request):
    serializer = JobsSerializer(data=request.data)
    if serializer.is_valid():
        job = serializer.save()
        job_thread = threading.Thread(target=trigger_operation, args=(job,)).start()
        return Response(serializer.data, status=status.HTTP_201_CREATED)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


def trigger_operation(db_record):
    db_record.status = Jobs.Status.WORKING
    threading.Thread(target=db_record.save, args=()).start()

    try:
        results = actual_operation(db_record.url)
        status = Jobs.Status.DONE
    except Exception as e:
        results = e
        status = Jobs.Status.FAILED
    finally:
        db_record.status = status
        db_record.results = results
        threading.Thread(target=db_record.save, args=()).start()
Вернуться на верх