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