mirror of
https://github.com/django/django.git
synced 2025-08-03 10:34:04 +00:00
Fixed #33646 -- Added async-compatible interface to QuerySet.
Thanks Simon Charette for reviews. Co-authored-by: Carlton Gibson <carlton.gibson@noumenal.es> Co-authored-by: Mariusz Felisiak <felisiak.mariusz@gmail.com>
This commit is contained in:
parent
27aa7035f5
commit
58b27e0dbb
9 changed files with 748 additions and 23 deletions
|
@ -849,6 +849,102 @@ being evaluated and therefore populate the cache::
|
|||
Simply printing the queryset will not populate the cache. This is because
|
||||
the call to ``__repr__()`` only returns a slice of the entire queryset.
|
||||
|
||||
.. _async-queries:
|
||||
|
||||
Asynchronous queries
|
||||
====================
|
||||
|
||||
.. versionadded:: 4.1
|
||||
|
||||
If you are writing asynchronous views or code, you cannot use the ORM for
|
||||
queries in quite the way we have described above, as you cannot call *blocking*
|
||||
synchronous code from asynchronous code - it will block up the event loop
|
||||
(or, more likely, Django will notice and raise a ``SynchronousOnlyOperation``
|
||||
to stop that from happening).
|
||||
|
||||
Fortunately, you can do many queries using Django's asynchronous query APIs.
|
||||
Every method that might block - such as ``get()`` or ``delete()`` - has an
|
||||
asynchronous variant (``aget()`` or ``adelete()``), and when you iterate over
|
||||
results, you can use asynchronous iteration (``async for``) instead.
|
||||
|
||||
Query iteration
|
||||
---------------
|
||||
|
||||
.. versionadded:: 4.1
|
||||
|
||||
The default way of iterating over a query - with ``for`` - will result in a
|
||||
blocking database query behind the scenes as Django loads the results at
|
||||
iteration time. To fix this, you can swap to ``async for``::
|
||||
|
||||
async for entry in Authors.objects.filter(name__startswith="A"):
|
||||
...
|
||||
|
||||
Be aware that you also can't do other things that might iterate over the
|
||||
queryset, such as wrapping ``list()`` around it to force its evaluation (you
|
||||
can use ``async for`` in a comprehension, if you want it).
|
||||
|
||||
Because ``QuerySet`` methods like ``filter()`` and ``exclude()`` do not
|
||||
actually run the query - they set up the queryset to run when it's iterated
|
||||
over - you can use those freely in asynchronous code. For a guide to which
|
||||
methods can keep being used like this, and which have asynchronous versions,
|
||||
read the next section.
|
||||
|
||||
``QuerySet`` and manager methods
|
||||
--------------------------------
|
||||
|
||||
.. versionadded:: 4.1
|
||||
|
||||
Some methods on managers and querysets - like ``get()`` and ``first()`` - force
|
||||
execution of the queryset and are blocking. Some, like ``filter()`` and
|
||||
``exclude()``, don't force execution and so are safe to run from asynchronous
|
||||
code. But how are you supposed to tell the difference?
|
||||
|
||||
While you could poke around and see if there is an ``a``-prefixed version of
|
||||
the method (for example, we have ``aget()`` but not ``afilter()``), there is a
|
||||
more logical way - look up what kind of method it is in the
|
||||
:doc:`QuerySet reference </ref/models/querysets>`.
|
||||
|
||||
In there, you'll find the methods on QuerySets grouped into two sections:
|
||||
|
||||
* *Methods that return new querysets*: These are the non-blocking ones,
|
||||
and don't have asynchronous versions. You're free to use these in any
|
||||
situation, though read the notes on ``defer()`` and ``only()`` before you use
|
||||
them.
|
||||
|
||||
* *Methods that do not return querysets*: These are the blocking ones, and
|
||||
have asynchronous versions - the asynchronous name for each is noted in its
|
||||
documentation, though our standard pattern is to add an ``a`` prefix.
|
||||
|
||||
Using this distinction, you can work out when you need to use asynchronous
|
||||
versions, and when you don't. For example, here's a valid asynchronous query::
|
||||
|
||||
user = await User.objects.filter(username=my_input).afirst()
|
||||
|
||||
``filter()`` returns a queryset, and so it's fine to keep chaining it inside an
|
||||
asynchronous environment, whereas ``first()`` evaluates and returns a model
|
||||
instance - thus, we change to ``afirst()``, and use ``await`` at the front of
|
||||
the whole expression in order to call it in an asynchronous-friendly way.
|
||||
|
||||
.. note::
|
||||
|
||||
If you forget to put the ``await`` part in, you may see errors like
|
||||
*"coroutine object has no attribute x"* or *"<coroutine …>"* strings in
|
||||
place of your model instances. If you ever see these, you are missing an
|
||||
``await`` somewhere to turn that coroutine into a real value.
|
||||
|
||||
Transactions
|
||||
------------
|
||||
|
||||
.. versionadded:: 4.1
|
||||
|
||||
Transactions are **not** currently supported with asynchronous queries and
|
||||
updates. You will find that trying to use one raises
|
||||
``SynchronousOnlyOperation``.
|
||||
|
||||
If you wish to use a transaction, we suggest you write your ORM code inside a
|
||||
separate, synchronous function and then call that using sync_to_async - see
|
||||
:doc:/topics/async` for more.
|
||||
|
||||
.. _querying-jsonfield:
|
||||
|
||||
Querying ``JSONField``
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue