Skip to content

Commit e1b41bb

Browse files
k4anubhavbckohan
authored andcommitted
Support select_related and prefetch_related for inherited models and custom queryset for child models
1 parent c541020 commit e1b41bb

File tree

1 file changed

+48
-3
lines changed

1 file changed

+48
-3
lines changed

src/polymorphic/query.py

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ def __init__(self, *args, **kwargs):
125125
# to that queryset as well).
126126
self.polymorphic_deferred_loading = (set(), True)
127127

128+
self._polymorphic_select_related = {}
129+
self._polymorphic_prefetch_related = {}
130+
self._polymorphic_custom_queryset = {}
131+
128132
def _clone(self, *args, **kwargs):
129133
# Django's _clone only copies its own variables, so we need to copy ours here
130134
new = super()._clone(*args, **kwargs)
@@ -133,6 +137,9 @@ def _clone(self, *args, **kwargs):
133137
copy.copy(self.polymorphic_deferred_loading[0]),
134138
self.polymorphic_deferred_loading[1],
135139
)
140+
new._polymorphic_select_related = copy.copy(self._polymorphic_select_related)
141+
new._polymorphic_prefetch_related = copy.copy(self._polymorphic_prefetch_related)
142+
new._polymorphic_custom_queryset = copy.copy(self._polymorphic_custom_queryset)
136143
return new
137144

138145
def as_manager(cls):
@@ -413,12 +420,31 @@ class self.model, but as a class derived from self.model. We want to re-fetch
413420
# TODO: defer(), only(): support for these would be around here
414421
for real_concrete_class, idlist in idlist_per_model.items():
415422
indices = indexlist_per_model[real_concrete_class]
416-
real_objects = real_concrete_class._base_objects.db_manager(self.db).filter(
417-
**{(f"{pk_name}__in"): idlist}
423+
424+
if self._polymorphic_custom_queryset.get(real_concrete_class):
425+
real_objects = self._polymorphic_custom_queryset[real_concrete_class]
426+
else:
427+
real_objects = real_concrete_class._base_objects.db_manager(self.db)
428+
429+
real_objects = real_objects.db_manager(self.db).filter(
430+
**{("%s__in" % pk_name): idlist}
418431
)
419-
# copy select related configuration to new qs
432+
433+
# copy select_related() fields from base objects to real objects
420434
real_objects.query.select_related = self.query.select_related
421435

436+
# polymorphic select_related() fields if any
437+
if real_concrete_class in self._polymorphic_select_related:
438+
real_objects = real_objects.select_related(
439+
*self._polymorphic_select_related[real_concrete_class]
440+
)
441+
442+
# polymorphic prefetch related configuration to new qs
443+
if real_concrete_class in self._polymorphic_prefetch_related:
444+
real_objects = real_objects.prefetch_related(
445+
*self._polymorphic_prefetch_related[real_concrete_class]
446+
)
447+
422448
# Copy deferred fields configuration to the new queryset
423449
deferred_loading_fields = []
424450
existing_fields = self.polymorphic_deferred_loading[0]
@@ -531,3 +557,22 @@ def get_real_instances(self, base_result_objects=None):
531557
return olist
532558
clist = PolymorphicQuerySet._p_list_class(olist)
533559
return clist
560+
561+
def select_polymorphic_related(self, polymorphic_subclass, *fields):
562+
if self.query.select_related is True:
563+
raise ValueError(
564+
"select_polymorphic_related() cannot be used together with select_related=True"
565+
)
566+
clone = self._clone()
567+
clone._polymorphic_select_related[polymorphic_subclass] = fields
568+
return clone
569+
570+
def prefetch_polymorphic_related(self, polymorphic_subclass, *lookups):
571+
clone = self._clone()
572+
clone._polymorphic_prefetch_related[polymorphic_subclass] = lookups
573+
return clone
574+
575+
def custom_queryset(self, polymorphic_subclass, queryset):
576+
clone = self._clone()
577+
clone._polymorphic_custom_queryset[polymorphic_subclass] = queryset
578+
return clone

0 commit comments

Comments
 (0)