#37218: Fetch modes aren't async-compatible
-------------------------------------+-------------------------------------
Reporter: Jacob | Owner: Jacob Walls
Walls |
Type: Bug | Status: assigned
Component: Database | Version: 6.1
layer (models, ORM) |
Severity: Normal | Keywords: fetch modes, async
Triage Stage: | Has patch: 0
Unreviewed |
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 0
UI/UX: 0 |
-------------------------------------+-------------------------------------
See [
https://dryorm.xterm.info/fetch-mode-async-failure fiddle]:
{{{#!py
from asgiref.sync import async_to_sync
from django.contrib.auth.models import User
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=100)
user = models.ForeignKey(User, models.CASCADE)
@async_to_sync
async def async_view():
qs = Person.objects.fetch_mode(models.FETCH_PEERS)
async for el in qs:
print(el.user)
def run():
admin = User.objects.create(username="admin")
instance = Person.objects.create(name='John Doe', user=admin)
instance2 = Person.objects.create(name='Jane Doe', user=admin)
async_view()
}}}
{{{#!py
File "/app/app/models.py", line 15, in async_view
print(el.user)
^^^^^^^
File "/django-pr/django/db/models/fields/related_descriptors.py", line
266, in __get__
instance._state.fetch_mode.fetch(self, instance)
File "/django-pr/django/db/models/fetch_modes.py", line 38, in fetch
fetcher.fetch_many(instances)
File "/django-pr/django/db/models/fields/related_descriptors.py", line
291, in fetch_many
prefetch_related_objects(missing_instances,
self.field.name)
File "/django-pr/django/db/models/query.py", line 2701, in
prefetch_related_objects
obj_list, additional_lookups = prefetch_one_level(
^^^^^^^^^^^^^^^^^^^
File "/django-pr/django/db/models/query.py", line 2876, in
prefetch_one_level
all_related_objects = list(rel_qs)
^^^^^^^^^^^^
File "/django-pr/django/db/models/query.py", line 434, in __iter__
self._fetch_all()
File "/django-pr/django/db/models/query.py", line 2239, in _fetch_all
self._result_cache = list(self._iterable_class(self))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/django-pr/django/db/models/query.py", line 98, in __iter__
results = compiler.execute_sql(
^^^^^^^^^^^^^^^^^^^^^
File "/django-pr/django/db/models/sql/compiler.py", line 1622, in
execute_sql
cursor = self.connection.cursor()
^^^^^^^^^^^^^^^^^^^^^^^^
File "/django-pr/django/utils/asyncio.py", line 24, in inner
raise SynchronousOnlyOperation(message)
django.core.exceptions.SynchronousOnlyOperation: You cannot call this from
an async context - use a thread or sync_to_async.
}}}
We probably need to add `AFETCH_PEERS` and `AFETCH_ONE` fetch modes, and
duplicate the `fetch_{one,many}()` methods into `afetch_{one,many}()` on
the descriptors.
I can hustle this in before the release candidate if it seems sane.
--
Ticket URL: <
https://code.djangoproject.com/ticket/37218>
Django <
https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.