Opened 4 years ago

Closed 3 years ago

#24975 closed New feature (fixed)

Add the ability to prefetch_related() from a model instance

Reported by: Eduardo Klein Owned by: nobody
Component: Database layer (models, ORM) Version: master
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Tim Graham)

The current prefetch_related works with a queryset, so if I have, in an ecommerce site, an order with articles, I can do orders = Order.objects.prefetch_related("articles").

If I do now orders[0].articles.all(), it will not query the database again.

Suppose I have an "order" and I want to prefetch the articles (I will need them several times later). If I want to prefetch them, afaik, I would need to store them in a variable in order to reuse the same articles, since every time I do order.articles.all() it queries the db again as expected.

It's a bit inconvenient (and maybe illogical) that I can prefetch objects from a queryset (a "list" of objects), but not from the object itself.

I worked around this issue, by doing the following:

articles = order.articles.all()
order._prefetched_objects_cache = {"articles": articles}

I propose to have somethins like order.prefetch("articles") that would do exactly what I did (but of course take care of verifying if _prefetched_objects_cache already exists and any other precautions needed.

Change History (7)

comment:1 Changed 4 years ago by Tim Graham

Component: UncategorizedDatabase layer (models, ORM)
Description: modified (diff)
Version: 1.8master

comment:2 Changed 4 years ago by Tim Graham

Triage Stage: UnreviewedAccepted

Conceptually, I don't see a request not to support something like this, although I'd be curious to know why assigning the query results to an intermediate variable isn't an acceptable solution. It would be helpful if you could post to the DevelopersMailingList in order to confirm what the API should look like and make sure there's no opposition to the idea in general.

comment:3 Changed 4 years ago by Tim Graham

While a bit verbose, the following pattern also seems to work: order = Order.objects.prefetch_related('articles').get(pk=order.pk) (I guess you may not want to reload the entire object in some cases).

comment:4 Changed 4 years ago by Tim Graham

Summary: Prefetch_related from object directlyAdd the ability to prefetch_related() from a model instance

comment:5 Changed 4 years ago by Adam (Chainz) Johnson

You can already do the following to do the prefetch for a list of objects:

from django.db.models.query import prefetch_related_objects
prefetch_related_objects([order], ['articles'])

I'm looking at making this public and documented in #25279. Then this case for prefetching for a single existing model object is just a special case of invoking prefetch_related_objects with a list of one item.

comment:6 Changed 4 years ago by Eduardo Klein

That seems to work very well. Thanks for addressing this issue.

As far as I understand, this would prefetch

order.articles.all()

May be the docs could also specify how to prefetch other manager / queryset (filters).

comment:7 Changed 3 years ago by Aymeric Augustin

Resolution: fixed
Status: newclosed

Closing as a duplicate of #25279, which was fixed in 1.10, per comment 5.

Note: See TracTickets for help on using tickets.
Back to Top