prefetch_related should support foreign keys/one-to-one
|Reported by:||lukeplant||Owned by:||nobody|
|Component:||Database layer (models, ORM)||Version:||1.3|
|Has patch:||yes||Needs documentation:||no|
|Needs tests:||no||Patch needs improvement:||no|
Description (last modified by lukeplant)
Although it is currently billed as providing support for many-to-many or the 'many' side of foreign key relationships, there is nothing in the name or API which stops prefetch_related providing support for foreign key relationships or one-to-one.
Often, select_related will be a better alternative for these relationships, since they can be done with a database join. However:
- It is possible that in practice it is better to do a separate query and join in Python.
- There are cases where it may be difficult to arrange for the relationships to be included via select_related(). For example if you do prefetch_related('many1__single__many2'), then, although prefetch_related will traverse the 'single' relationship to get to 'many2', it could execute many DB queries when it does so.
An ideal solution to this would allow prefetching of things like GenericForeignKey, and potentially other similar 3rd party constructs. For example, someone might have a CrossDatabaseForeignKey, which would not have referential integrity since the related data is in a different database, but provides convenient access on objects, and would benefit from being able to do a prefetch for efficient access. (select_related, being JOIN based, is clearly out of the picture for this use case). This suggests that some API similar to the one created for many-related-managers (i.e. existence of a get_prefetch_query_set() method) should be used.
The primary difference for singly related objects is that we have to avoid simply fetching the attribute from the instance, as this will cause the descriptor __get__() to be called, and the query to be done immediately the inefficient way. We can do this by calling getattr on the class, which will retrieve the descriptor object itself.
For reference, it appears that Ruby on Rails uses something like prefetch_related() for all eager loading of relationships, and doesn't have an equivalent to select_related().
Change History (13)
comment:1 Changed 2 years ago by lukeplant
- Description modified (diff)
- Triage Stage changed from Unreviewed to Accepted