Handle stale ContentType models (no longer referring to a model)
This builds on top of a fix in Django 1.6, and has a workaround for Django 1.4 and 1.5. When the base class points to a model that no longer exists, it will be silently dropped in the polymorphic queryset results. This behavior is identical to iterating over results when the derived table doesn't have the object anymore.fix_request_path_info
parent
8527244cb3
commit
8cf313335c
|
|
@ -94,20 +94,34 @@ class PolymorphicModel(models.Model):
|
|||
return super(PolymorphicModel, self).save(*args, **kwargs)
|
||||
|
||||
def get_real_instance_class(self):
|
||||
"""Normally not needed.
|
||||
"""
|
||||
Normally not needed.
|
||||
If a non-polymorphic manager (like base_objects) has been used to
|
||||
retrieve objects, then the real class/type of these objects may be
|
||||
determined using this method."""
|
||||
determined using this method.
|
||||
"""
|
||||
# the following line would be the easiest way to do this, but it produces sql queries
|
||||
#return self.polymorphic_ctype.model_class()
|
||||
# so we use the following version, which uses the CopntentType manager cache
|
||||
return ContentType.objects.get_for_id(self.polymorphic_ctype_id).model_class()
|
||||
# return self.polymorphic_ctype.model_class()
|
||||
# so we use the following version, which uses the CopntentType manager cache.
|
||||
# Note that model_class() can return None for stale content types;
|
||||
# when the content type record still exists but no longer refers to an existing model.
|
||||
try:
|
||||
return ContentType.objects.get_for_id(self.polymorphic_ctype_id).model_class()
|
||||
except AttributeError:
|
||||
# Django <1.6 workaround
|
||||
return None
|
||||
|
||||
def get_real_concrete_instance_class_id(self):
|
||||
return ContentType.objects.get_for_model(self.get_real_instance_class(), for_concrete_model=True).pk
|
||||
model_class = self.get_real_instance_class()
|
||||
if model_class is None:
|
||||
return None
|
||||
return ContentType.objects.get_for_model(model_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()
|
||||
model_class = self.get_real_instance_class()
|
||||
if model_class is None:
|
||||
return None
|
||||
return ContentType.objects.get_for_model(model_class, for_concrete_model=True).model_class()
|
||||
|
||||
def get_real_instance(self):
|
||||
"""Normally not needed.
|
||||
|
|
|
|||
|
|
@ -169,7 +169,10 @@ class PolymorphicQuerySet(QuerySet):
|
|||
real_concrete_class = 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_class_id:
|
||||
if real_concrete_class_id is None:
|
||||
# Dealing with a stale content type
|
||||
continue
|
||||
elif real_concrete_class_id == self_concrete_model_class_id:
|
||||
# Real and base classes share the same concrete ancestor,
|
||||
# upcast it and put it in the results
|
||||
results[base_object.pk] = transmogrify(real_concrete_class, base_object)
|
||||
|
|
|
|||
Loading…
Reference in New Issue