@@ -112,6 +112,10 @@ def __init__(self, *args, **kwargs):
112
112
# to that queryset as well).
113
113
self .polymorphic_deferred_loading = (set (), True )
114
114
115
+ self ._polymorphic_select_related = {}
116
+ self ._polymorphic_prefetch_related = {}
117
+ self ._polymorphic_custom_queryset = {}
118
+
115
119
def _clone (self , * args , ** kwargs ):
116
120
# Django's _clone only copies its own variables, so we need to copy ours here
117
121
new = super ()._clone (* args , ** kwargs )
@@ -120,6 +124,9 @@ def _clone(self, *args, **kwargs):
120
124
copy .copy (self .polymorphic_deferred_loading [0 ]),
121
125
self .polymorphic_deferred_loading [1 ],
122
126
)
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 )
123
130
return new
124
131
125
132
def as_manager (cls ):
@@ -417,12 +424,30 @@ class self.model, but as a class derived from self.model. We want to re-fetch
417
424
# TODO: defer(), only(): support for these would be around here
418
425
for real_concrete_class , idlist in idlist_per_model .items ():
419
426
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 (
421
433
** {("%s__in" % pk_name ): idlist }
422
434
)
423
- # copy select related configuration to new qs
435
+
436
+ # copy select_related() fields from base objects to real objects
424
437
real_objects .query .select_related = self .query .select_related
425
438
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
+
426
451
# Copy deferred fields configuration to the new queryset
427
452
deferred_loading_fields = []
428
453
existing_fields = self .polymorphic_deferred_loading [0 ]
@@ -535,3 +560,22 @@ def get_real_instances(self, base_result_objects=None):
535
560
return olist
536
561
clist = PolymorphicQuerySet ._p_list_class (olist )
537
562
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