Opened 10 hours ago

Last modified 6 hours ago

#37027 new Bug

refresh_from_db() with from_queryset + prefetch does not persist result in instance

Reported by: Hugo Maingonnat Owned by:
Component: Database layer (models, ORM) Version: 5.2
Severity: Normal Keywords: refresh_from_db, prefetch
Cc: MANAS MADESHIYA Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

foo = Foo.objects.create(**data)
# The prefetch query is executed, but does not seem to be persisted into the instance
prefetch = Prefetch('bar_set', queryset=Bar.objects.all())
foo.refresh_from_db(from_queryset=Foo.objects.prefetch_related(
   prefetch
))
# This is empty
print('CACHE:', foo._prefetched_objects_cache)
# Lazy extra query is executed because prefetch was not persisted: this should not happen
list(foo.bar_set.all())

This also does not work if for example to_attr='bar_list' is set in the prefetch, then foo.bar_list is not set.

Note that using select_related() is working.

Change History (2)

comment:1 by Simon Charette, 10 hours ago

There's is little value in using prefetch_related when dealing with a single object as this method is meant to be used to prevent N+1 query issues but in this case N=1 so the number of queries will be the same. In other words, performing foo.bar_set.all() will issue the same query as what a prefetching would have done.

When we added refresh_from_db(from_queryset) support (#28344) we knew it would open the door to a large surface area between Model and QuerySet APIs. Given the original intent was to support for_update and friends I'm not convinced we should add support for prefetch_related and certainly not for Prefetch(to_attr) as it doesn't follow the refresh_from_db(fields) convention.

Support for select_related came for free as it's a normal attribute lookup on the retrieved model instance and there are real benefits to using it (reducing the number of queries). In the case of prefetch_related it requires extra retrieval and invalidation logic of many-to-many relationships and results in the same number of queries. I believe accepting this work would open the door to supporting the full Model X QuerySet intersection which would include cases like annotate members being ignored as well.

Last edited 10 hours ago by Simon Charette (previous) (diff)

comment:2 by MANAS MADESHIYA, 6 hours ago

Cc: MANAS MADESHIYA added
Note: See TracTickets for help on using tickets.
Back to Top