Optimization - don't do extra queries for proxy models

fix_request_path_info
Jedediah Smith 2012-11-27 17:23:39 -05:00 committed by Diederik van der Boor
parent 74389bb23d
commit 58c4f6f697
2 changed files with 33 additions and 4 deletions

View File

@ -103,6 +103,12 @@ class PolymorphicModel(models.Model):
# so we use the following version, which uses the CopntentType manager cache
return ContentType.objects.get_for_id(self.polymorphic_ctype_id).model_class()
def get_real_concrete_instance_class_id(self):
return ContentType.objects.get_for_model(self.get_real_instance_class(), for_concrete_model=True).pk
def get_real_concrete_instance_class(self):
return ContentType.objects.get_for_model(self.get_real_instance_class(), for_concrete_model=True).model_class()
def get_real_instance(self):
"""Normally not needed.
If a non-polymorphic manager (like base_objects) has been used to

View File

@ -17,6 +17,15 @@ from django.db.models.query import CHUNK_SIZE # this is 100 for Dj
Polymorphic_QuerySet_objects_per_request = CHUNK_SIZE
def transmogrify(cls, obj):
"""
Clone an object as a different class, by instantiating that class and copying the __dict__
"""
new = cls()
for k,v in obj.__dict__.items():
new.__dict__[k] = v
return new
###################################################################################
### PolymorphicQuerySet
@ -136,6 +145,8 @@ class PolymorphicQuerySet(QuerySet):
# - store objects that already have the correct class into "results"
base_result_objects_by_id = {}
self_model_content_type_id = ContentType.objects.get_for_model(self.model, for_concrete_model=False).pk
self_concrete_model_content_type_id = ContentType.objects.get_for_model(self.model, for_concrete_model=True).pk
for base_object in base_result_objects:
ordered_id_list.append(base_object.pk)
@ -146,14 +157,21 @@ class PolymorphicQuerySet(QuerySet):
base_result_objects_by_id[base_object.pk] = base_object
# this object is not a derived object and already the real instance => store it right away
if (base_object.polymorphic_ctype_id == self_model_content_type_id):
# Real class is exactly the same as base class, go straight to results
results[base_object.pk] = base_object
# this object is derived and its real instance needs to be retrieved
# => store it's id into the bin for this model type
else:
idlist_per_model[base_object.get_real_instance_class()].append(base_object.pk)
modelclass = base_object.get_real_instance_class()
real_concrete_class_id = base_object.get_real_concrete_instance_class_id()
if real_concrete_class_id == self_concrete_model_content_type_id:
# Real and base classes share the same concrete ancestor,
# upcast it and put it in the results
results[base_object.pk] = transmogrify(modelclass, base_object)
else:
modelclass = ContentType.objects.get_for_id(real_concrete_class_id).model_class()
idlist_per_model[modelclass].append(base_object.pk)
# django's automatic ".pk" field does not always work correctly for
# custom fields in derived objects (unclear yet who to put the blame on).
@ -175,6 +193,11 @@ class PolymorphicQuerySet(QuerySet):
for o in qs:
o_pk = getattr(o, pk_name)
real_class = o.get_real_instance_class()
# If the real class is a proxy, upcast it
if real_class != modelclass:
o = transmogrify(real_class, o)
if self.query.aggregates:
for anno_field_name in self.query.aggregates.keys():