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