@@ -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