SynchronousOnlyOperation error in custom converter when running on ASGI

Hello, I have a module in my application that implements a model with a hierarchical structure. I am using a custom url converter that parses a series of slugs and returns a list of those objects:

from module.models import Object

class slugGraph:
    regex = r"([-a-zA-Z0-9_]/?)+"

    def to_python(self, value):
        ret = []
        for slug in value.split("/"):
            try:
                ret.append(object.objects.get(slug=slug))
            except Object.DoesNotExist:
                raise ValueError
        return ret

    def to_url(self, value):
        ret = ""
        for x in value:
            ret += x.slug
            ret += "/"
        ret = ret[:-1] # removes last slash
        return ret

This works perfectly well on the development web server, but when I try to deploy the code via ASGI, I get the following error:

SynchronousOnlyOperation - You cannot call this from an async context - use a thread or sync_to_async.

The application errors out on the line:
ret.append(object.objects.get(slug=slug))
Is using converters to return an object like this impossible when deploying using ASGI?
Thank you for your time and I you can help me :slight_smile:

You either need to convert the query to use aget, or wrap the query in a sync_to_async call, or since you’re possibly going to execute this query multiple times, you could make that entire to_python method synchronous.

Side note: If you’re deploying this in an async environment, you really should be doing your development using the Daphne-version of runserver to have it running async as well.

Thank you for a quick reply, that has worked. If i understand correctly I have to now change every call to the custom converter (for example when retrieving a captured variable from the url) asynchronously, correct?

Wonderful, I did not know that, that makes testing a lot easier.

Correct. Your alternatives are to either create two different flavors of the method (sync and async), or to change the callers such that they’re all calling your function from a sync context.

1 Like

Great, thank you for the quick replies again :slight_smile:

I am so sorry, but I am a new to concurrent code in python. How would I implement two flavors of the same method that can be called with the same method name?

You can’t. They need to have two different names. That’s why we have the situations like get and aget.

1 Like