Skip to content

Commit 8e700e8

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

1 file changed

Lines changed: 47 additions & 4 deletions

File tree

src/polymorphic/query.py

Lines changed: 47 additions & 4 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,29 @@ 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}
418-
)
419-
# copy select related configuration to new qs
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(**{("%s__in" % pk_name): idlist})
430+
431+
# copy select_related() fields from base objects to real objects
420432
real_objects.query.select_related = self.query.select_related
421433

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

0 commit comments

Comments
 (0)