Help with an Async View in Django 3.1

I’m using the latest pre-release version of Django 3.1 with wsgi. I have a view that calls a method to create a PDF. I want the view to return JSON with just the path to the PDF that will be created so that I can poll for it later and download it once it exists.

I’ve tried this:

async def create_pdf(request, slug):
    m = Material.objects.get(slug=slug)
    path_to_pdf = 'some/path/to/pdf.pdf'

    m.create_pdf(path_to_pdf) # Don't wait for this to finish

    data = {
        'pdf': path_to_pdf
    }

    return JsonResponse(data)

I get the following error:

The view materials.views.create_pdf didn’t return an HttpResponse object. It returned an unawaited coroutine instead. You may need to add an ‘await’ into your view.

I tried adding await before the call to m.create_pdf (although I don’t want to wait for that to finish), but I still got the same error.

I’d appreciate any help. Thanks.

I’m really hoping someone else more familiar with the environment chimes in here - and that means just about anyone. What follows is about 75% conjecture. (I’m only posting this because I’d like to learn more about this as well and am hoping a discussion ensues.)

I don’t think you’re going to be able to “not-wait” if you’re running in a wsgi environment. My understanding is that the view itself would run in a one-off event loop, but that that loop needs to end before the view returns results.

(That’s how I interpret the second paragraph of Async Views)

Also, since the ORM is not currently async-capable, you might have issues with the create_pdf method not being async-friendly.

I believe that to achieve your real goal, you’d need to run this in an asgi container.

1 Like

Hi @natdunn.

At first glance, an async view isn’t really what you want here. Rather you want to be off-loading the create_pdf task to some kind of queuing system. I’m a big fan of Django-Q — it’s nice and lightweight, and most of what you need most of the time.

The async def doesn’t do anything to let you pass a task off to the background by itself. And an await just says, more or less, to the interpreter, “You can go an do something else for a while because I’ll be waiting for I/O for a moment”. Importantly, it doesn’t let your view move on to the part where it returns a response early. So you still need an out of band processing channel somehow (basically a queue).

The move to async opens to the door maybe to that sort of thing appearing in Django one day, but it’s not there yet. As I say, Django-Q is super. I’d point you that way.

I hope that helps.

3 Likes

Thanks @carltongibson and @KenWhitesell! I guess this is the crux of it:

The async def doesn’t do anything to let you pass a task off to the background by itself.

I’ll check out Django-Q. Thanks for the tip!