Async aget() and related objects...

I have done some extensive searches and read the documentation. Unfortunately I could not solve the following. I probably missed something.

async def generate_quotes(request, id):
   
    try:
        interrogation = await Interrogation.objects.aget(id=id)
    except Interrogation.DoesNotExist:
        return JsonResponse("Interrogation does not exist")
   print(interrogation.address.postcode) #where address is a onetoone related object with related name address

i get a keyerror for address
and
django.core.exceptions.SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async.
I am stuck on how to proceed. I have tried sync to async in all manner of ways with that one print statement and still nothing. I cant seem to get to the address object through its related interrogation object query result.

Any help would be greatly appreciate !

Thanks

The issue here appears to be the implicit query executed by the reference to .address.postcode in the print statement. How did you try to use sync_to_async with the print? I would expect it to be something like:
await sync_to_async(print)(interrogation.address.postcode)
but that’s really just a guess.

I think the print is a red herring. I experimented by outsourcing to a sync_to_async function and think its to do with caching the related objects… When you do a .get() u can do something like interrogation.address.postcode. and it “FETCHES” and caches the address object into memory but when doing this asynchronously … the only object that is being cached in my async code currently is the interrogation object… making a call then to related objects would require a further query which I think i proved in my experiment. Surely there must be a “prefetch_related” or fetch_related for an async .aget() .

i dont see how the django dev team didnt allow for traversing through related objects after aget() is called… There must be a simple solution.

Yes, you can use select_related before the get. See QuerySet API reference | Django documentation | Django

Ah briliant! I dont know why i always put it after the filter or gets usually .
What bout calling a model property method with async…

this wont work :

await sync_to_async(quote.update_quote_dict)```

If update_quote_dict is a function, then the statement would be:
await sync_to_async(quote.update_quote_dict)()

Yes that works if I remove @property from the model function. So async doesn’t work on model properties?

Also , thank you so much Ken for your assistance.

The sync_to_async function requires that it be passed a callable. A method decorated with the @property decorator is not a callable. To understand why requires understanding what decorators do in general, and what the @property decorator specifically does.

fair enough.
would you call a template render as sync_to_async then?

return await sync_to_async(render)(request, 'main/search.html',context)

This works but is it cannon?

or is there an async version like arender() ?

Wow. I’ve never done that. Seriously cool. :+1:

I don’t think I’ve even thought of doing that.

(I’m a stickler for ensuring that queries don’t get executed in templates, and I’ve never written a standard async view. I only use async with Channels, so this also isn’t something I’ve even needed to consider.)

I don’t see why not.

Not that I can find…

1 Like

Thank you again.
Its a shame/surprise that since 4.0 there has not really been any tutorials, demos or in-depth guides in regards to async integration… even the documentation does not have a dedicated caegory for it :frowning:

<opinion>
The implied need for async views tends to be vastly overstated. You can find any number of people here and in other sites thinking it’s the magic potion to improve performance, when that most emphatically is not the case.

Yes, there are some specific use-cases where async will improve throughput for an aggregate set of operations within a view, or allow for greater scalability within a single-server environment. Aside from that, you’ll actually see a decrease in performance.

But it’s also still evolving - it’s not even a “complete” implementation throughout the entire stack. I’m not sure that there’s much value in recommending it yet, unless there’s clear and convincing evidence that it’s going to provide tangible improvements that justify the additional complexity.
</opinion>

1 Like

I am leaning towards your sentiment. I have only converted those views/functions that move or process large amounts of data. For example, I created a report view which processes 5000 entries and then has to render a template presenting it the way an excel workbook would but pure html. My understanding is that I am averting any hanging for other requests to the server by doing it this way while it generates a report. I hope my understanding is correct :slight_smile:

As the experienced programmer that you are, I am curious if you do views.py for views strictly and refer to code.py for the processing of functions and data to return to the view to render? Or do you do all in the view. I do a lot in the view and rely on fat models for many of the functions. I need to decide if I take the plunge into separating code from views.

Actually, that’s not accurate. Each request to the server is served in a separate worker process, whether that process is handling a sync or an async view. One long-running request is only going to tie up that process - and that’s going to happen regardless.

What an async view allows you to do is to initiate multiple long-running non-blocking “tasks” concurrently, where those “tasks” are requests for “non-local” data. For example, say you were requesting data from an API from 4 different web sites. In a synchronous view, the first request would need to return before the second request could be made. An async view would allow you to start all 4 at the same time, and then continue when all 4 have returned data.

You could also do this with database queries, assuming you don’t need results from one before issuing the next query.

Now, as more of Django moves toward a full async implementation, you would expect to see scalability improvements for a given installation. But in the general case, we’re not there yet.

We have no hard rules for this. We do have the general principle that we like to keep file sizes below 2500 lines. We also make judgement calls regarding code location on a case-by-case basis, usually taking into account the size and scope of the project. For example, we have a number of smaller projects (~ 1000 lines total) that are “thin model / fat view”, only because they’re so small that it benefits us to have all the logic in one file. But as the project gets larger, we recognize the value of refactoring out code to other locations.

1 Like

I guess ill chalk this up to me future proofing for async :crazy_face:
I have about 1000 lines of code in each view.py across 10 apps and the models are also fat

I’ll certainly be refactoring !

Thanks again for your thoughts and input!