Improve prefetching of relationships #921
Replies: 4 comments 18 replies
-
And while we are at this, we need to think how to support prefetching related fields in |
Beta Was this translation helpful? Give feedback.
-
I have run into this issue as well that defining It is an interesting idea though to move the prefetching configuration to the serializer. A proof of concept would be needed also in terms of what API would fit. I would see above issue improving of |
Beta Was this translation helpful? Give feedback.
-
My two cents: I started playing with this a while back and also came to the conclusion that all the prefetching logic needs to be defined on the serializer, else includes are either left out in the dark, or require a tonne of repetition to ensure they're supported across all views. My approach would've been to define a
Here's an example of what such a thing would look like:
|
Beta Was this translation helpful? Give feedback.
-
In issue #337 @sliverc specifically mentions an existing N+1 query problem for any reverse relationships (eg. one to one in reverse or one to many) and how that's not handled by In our use of the framework, we make our own base classes and have amended Since it is using the serializers The core of the solution is examining the relationships for models for each serializer: # snip ... looping through the base view serializer and any serializers that the user asked for with "include" ...
level_model = getattr(level_serializer_class.Meta, "model")
info = model_meta.get_field_info(level_model)
relations_set = {
x for x in info.relations.keys() if not x.endswith("_set")
}
for field in relations_set:
field_info = info.relations[field]
if field_info.reverse or field_info.to_many:
included_recursive_prefetch.add(f"{level_agg}{field}")
#... snip
# And then resume the work of `AutoPrefetchMixin`
included_resources = get_included_resources(
self.request, self.serializer_class
) + list(included_recursive_prefetch)
qs = super().get_queryset(*args, **kwargs)
# ... Full gist here: https://gist.github.com/dsschneidermann/ddf9c9e4a782c2f6769d503ff0a0a42e An additional optimization missing from here would be restricting the queries to do As it is, it can be used as a drop-in replacement for |
Beta Was this translation helpful? Give feedback.
-
Refs: #337, #600
Currently, to improve performance one can specify
select_for_includes
andprefetch_for_includes
in viewsets. This ensures that the required fields are loaded in the minimum amount of queries when?include
ed.I haven't seen anything talking about how to reuse defined fields in other views.
Example:
CommentViewSet
has these attributes defined:EntrySerializer
allows one to includecomments
, but since we haven't defined something like the above forEntryViewSet
, nothing will be pre-fetched.We can go and define it like so:
As we see, we kind of repeat ourselves.
We also should note that
writer
was aselect_related
field, which is nowprefetch_related
field.Actual discussion
Now since serializers themselves are the ones defining all the includes, they should be the ones defining what to prefetch and what to not, and how, without having to repeat the same fields many times across different viewsets
It also should be smart enough to understand that a field can be selected in a viewset, and prefetch in another viewset, depending on the "parent" field.
Beta Was this translation helpful? Give feedback.
All reactions