diff --git a/CHANGES.rst b/CHANGES.rst index 178dbe8..449608e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,10 @@ CHANGES master (unreleased) ------------------- +* Fix bug in `InheritanceManager` with grandchild classes on Django 1.6+; + `select_subclasses('child', 'child__grandchild')` would only ever get to the + child class. Thanks Keryn Knight for report and proposed fix. + 1.5.0 (2013.08.29) ------------------ diff --git a/model_utils/managers.py b/model_utils/managers.py index 231bffa..abb18b4 100644 --- a/model_utils/managers.py +++ b/model_utils/managers.py @@ -46,9 +46,12 @@ class InheritanceQuerySet(QuerySet): def iterator(self): iter = super(InheritanceQuerySet, self).iterator() if getattr(self, 'subclasses', False): + # sort the subclass names longest first, + # so with 'a' and 'a__b' it goes as deep as possible + subclasses = sorted(self.subclasses, key=len, reverse=True) for obj in iter: sub_obj = None - for s in self.subclasses: + for s in subclasses: sub_obj = self._get_sub_obj_recurse(obj, s) if sub_obj: break diff --git a/model_utils/tests/models.py b/model_utils/tests/models.py index 2a70a4d..f9e587d 100644 --- a/model_utils/tests/models.py +++ b/model_utils/tests/models.py @@ -1,4 +1,7 @@ +from __future__ import unicode_literals + from django.db import models +from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ from model_utils.models import TimeStampedModel, StatusModel, TimeFramedModel @@ -15,6 +18,7 @@ class InheritanceManagerTestRelated(models.Model): +@python_2_unicode_compatible class InheritanceManagerTestParent(models.Model): # FileField is just a handy descriptor-using field. Refs #6. non_related_field_using_descriptor = models.FileField(upload_to="test") @@ -24,11 +28,17 @@ class InheritanceManagerTestParent(models.Model): objects = InheritanceManager() + def __str__(self): + return "%s(%s)" % ( + self.__class__.__name__[len('InheritanceManagerTest'):], + self.pk, + ) + + class InheritanceManagerTestChild1(InheritanceManagerTestParent): non_related_field_using_descriptor_2 = models.FileField(upload_to="test") normal_field_2 = models.TextField() - pass class InheritanceManagerTestGrandChild1(InheritanceManagerTestChild1): diff --git a/model_utils/tests/tests.py b/model_utils/tests/tests.py index e873944..e0ee5e3 100644 --- a/model_utils/tests/tests.py +++ b/model_utils/tests/tests.py @@ -542,8 +542,26 @@ class InheritanceManagerTests(TestCase): self.assertEqual( set( self.get_manager().select_subclasses( - "inheritancemanagertestchild1__" - "inheritancemanagertestgrandchild1" + "inheritancemanagertestchild1__inheritancemanagertestgrandchild1" + ) + ), + children, + ) + + + @skipUnless(django.VERSION >= (1, 6, 0), "test only applies to Django 1.6+") + def test_children_and_grandchildren(self): + children = set([ + self.child1, + InheritanceManagerTestParent(pk=self.child2.pk), + self.grandchild1, + InheritanceManagerTestChild1(pk=self.grandchild1_2.pk), + ]) + self.assertEqual( + set( + self.get_manager().select_subclasses( + "inheritancemanagertestchild1", + "inheritancemanagertestchild1__inheritancemanagertestgrandchild1" ) ), children,