-
-
Notifications
You must be signed in to change notification settings - Fork 32k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fixed #33646 -- Added async-compatible interface to QuerySet. #14843
Conversation
8ebf486
to
bfe1019
Compare
Does this feature use native async drivers? Dropping these references, in case you are not aware: psycopg 3 async support and psycopg 3 django driver. |
No - getting to that point will be a lot of work. This is the first phase, where we outline the asynchronous API and mostly make it work via threadpools. The plan is to progressively work down the stack towards native drivers, but that'll probably take quite a while. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
async for
?. Maybe just objects = await queryset
? I don't think making fetching a row an async operation is a good idea (of course, they will actually be fetched in chunks). We rarely need iteration over objects really
If you want to fetch the whole queryset you can just use |
For what is it's worth, I find the |
This discussion is already over, see also Django 4.0 release notes. |
Apart from questions around naming, where is this PR blocked? Is the design sound and simply requires tests, or is it still needing design changes? |
@dralley It's just waiting for an opportunity to review. The PR came in too-hot for the 4.0 feature freeze so we've been dealing with 4.0 things, and will look to target 4.1 for these changes. (It's next on my list of bigger items as it happens.) |
e64b050
to
31c067d
Compare
OK — big thanks to @andrewgodwin for laying this down, and @fcurella for There's more to be done, but it would be good to get a rough set of TODOs A few points: At this stage it's essentially just the API, but are we happy with that? There's a couple of failures on the Windows/SQLite build. Non-matching PKs. I Then, the Given what's there, we'd want to do something like this:
But the context managers aren't async compatible (since they call Q: Do we need an Overall, I like how minimal it is: it gives a nicer layer to work down to fully |
My Django site is fully async (there are no sync views due to a custom ACL framework I’ve written that is async), so I would be required to ‘async_to_sync(qs.explain)()’ which feels odd considering the rest of the API has async equivalents. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@andrewgodwin Many thanks 👍 I really like it, IMO it's the right first step to move async-ORM forward 🎊 🥳 I can check Windows failures, and do a detailed review later.
Do we need an aexplain at all?
Yes we do 🛠️
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left a few comments but nothing major beyond the BaseIterable.__aiter__
and alist
work.
I'd add a mention that this is not true async database I/O per se in the announcement for now but mainly syntactic sugar to avoid having to do sync_to_async
all over the place when interacting with the user-facing part of the ORM.
It's not to diminish the efforts made in this direction in the announcement but just to make it clear that until we land async compiler and backend support we'll still be heavily relying on threading which might not be what users expect when interacting with an async compatible interface to a database.
docs/ref/models/querysets.txt
Outdated
``get()`` | ||
~~~~~~~~~ | ||
|
||
.. method:: get(*args, **kwargs) | ||
|
||
*Asynchronous version*: ``aget`` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'd be great to find a way to have referenceable versions of these definitions instead of raw text references.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, so a quick play and this works:
.. method:: get(*args, **kwargs)
+.. method:: aget(*args, **kwargs)
That generates this with the dev stylesheet:
Both of those anchors are linkable with:
:meth:`get`
:meth:`aget`
… as usual.
I couldn't put the *Asynchronous version*
on the same line; likely with a bit of thought we could do something more sophisticated.
040d5a2
to
9559c9c
Compare
e5924ed
to
a544d97
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@andrewgodwin Thanks 👍 I simplified tests and left comments.
62bcfb9
to
13646fe
Compare
I pushed "small" 😉 edits ✏️ . I'll continue next week. |
0618370
to
41c1101
Compare
63a7fdd
to
541d6d2
Compare
buildbot, test on oracle. |
I rebased, squashed commits, and tried to implement fetching in chunks to reduce the number of @charettes What do you think? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for adjustments @felixxm, they look great. This should make a noticeable difference in performance.
Thanks Simon Charette for reviews. Co-authored-by: Carlton Gibson <[email protected]> Co-authored-by: Mariusz Felisiak <[email protected]>
@charettes Thanks for reviews ⭐ I'm going to prepare another PR to make |
This adds an async-compatible set of methods and special methods to the
QuerySet
class (and, via the generic pass-through, Managers as well).Included are:
a
prefix:aiterator
,aaggregate
,acount
,aget
,acreate
,abulk_create
,abulk_update
,aget_or_create
,aupdate_or_create
,aearliest
,alatest
,afirst
,alast
,ain_bulk
,aupdate
,adelete
,aexists
,acontains
,aexplain
. Most are just wrappers around the underlying sync version, though some have a few performance shortcuts added.BaseIterable
that propagates through to all QuerySets as well as the results ofvalues()
,values_list()
etc. It's not terribly efficient, but we can improve this progressively once we teachcompiler.results_iter
the wonders of async.alist()
utility function for turning async iterables into lists asynchronously, because we do uselist()
quite a bit.As a nice example, this means you can now write this kind of view:
Remaining work: