Skip to content

Commit 9321221

Browse files
Support select_related and prefetch_related for inherited models and custom queryset for child models (#1)
* add support for select_related for inherited models * clone polymorphic related * added prefetch_polymorphic_related * added support for custom queryset for child objects * fix support for custom queryset for child objects * add cloning to added methods * fix polymorphic select_related for query if already defined on base query --------- Co-authored-by: Anubhav Kushwaha <[email protected]>
1 parent bd5faf0 commit 9321221

File tree

1 file changed

+46
-2
lines changed

1 file changed

+46
-2
lines changed

polymorphic/query.py

+46-2
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ def __init__(self, *args, **kwargs):
112112
# to that queryset as well).
113113
self.polymorphic_deferred_loading = (set(), True)
114114

115+
self._polymorphic_select_related = {}
116+
self._polymorphic_prefetch_related = {}
117+
self._polymorphic_custom_queryset = {}
118+
115119
def _clone(self, *args, **kwargs):
116120
# Django's _clone only copies its own variables, so we need to copy ours here
117121
new = super()._clone(*args, **kwargs)
@@ -120,6 +124,9 @@ def _clone(self, *args, **kwargs):
120124
copy.copy(self.polymorphic_deferred_loading[0]),
121125
self.polymorphic_deferred_loading[1],
122126
)
127+
new._polymorphic_select_related = copy.copy(self._polymorphic_select_related)
128+
new._polymorphic_prefetch_related = copy.copy(self._polymorphic_prefetch_related)
129+
new._polymorphic_custom_queryset = copy.copy(self._polymorphic_custom_queryset)
123130
return new
124131

125132
def as_manager(cls):
@@ -417,12 +424,30 @@ class self.model, but as a class derived from self.model. We want to re-fetch
417424
# TODO: defer(), only(): support for these would be around here
418425
for real_concrete_class, idlist in idlist_per_model.items():
419426
indices = indexlist_per_model[real_concrete_class]
420-
real_objects = real_concrete_class._base_objects.db_manager(self.db).filter(
427+
if self._polymorphic_custom_queryset.get(real_concrete_class):
428+
real_objects = self._polymorphic_custom_queryset[real_concrete_class]
429+
else:
430+
real_objects = real_concrete_class._base_objects.db_manager(self.db)
431+
432+
real_objects = real_objects.filter(
421433
**{("%s__in" % pk_name): idlist}
422434
)
423-
# copy select related configuration to new qs
435+
436+
# copy select_related() fields from base objects to real objects
424437
real_objects.query.select_related = self.query.select_related
425438

439+
# polymorphic select_related() fields if any
440+
if real_concrete_class in self._polymorphic_select_related:
441+
real_objects = real_objects.select_related(
442+
*self._polymorphic_select_related[real_concrete_class]
443+
)
444+
445+
# polymorphic prefetch related configuration to new qs
446+
if real_concrete_class in self._polymorphic_prefetch_related:
447+
real_objects = real_objects.prefetch_related(
448+
*self._polymorphic_prefetch_related[real_concrete_class]
449+
)
450+
426451
# Copy deferred fields configuration to the new queryset
427452
deferred_loading_fields = []
428453
existing_fields = self.polymorphic_deferred_loading[0]
@@ -535,3 +560,22 @@ def get_real_instances(self, base_result_objects=None):
535560
return olist
536561
clist = PolymorphicQuerySet._p_list_class(olist)
537562
return clist
563+
564+
def select_polymorphic_related(self, polymorphic_subclass, *fields):
565+
if self.query.select_related is True:
566+
raise ValueError(
567+
"select_polymorphic_related() cannot be used together with select_related=True"
568+
)
569+
clone = self._clone()
570+
clone._polymorphic_select_related[polymorphic_subclass] = fields
571+
return clone
572+
573+
def prefetch_polymorphic_related(self, polymorphic_subclass, *lookups):
574+
clone = self._clone()
575+
clone._polymorphic_prefetch_related[polymorphic_subclass] = lookups
576+
return clone
577+
578+
def custom_queryset(self, polymorphic_subclass, queryset):
579+
clone = self._clone()
580+
clone._polymorphic_custom_queryset[polymorphic_subclass] = queryset
581+
return clone

0 commit comments

Comments
 (0)