diff --git a/AUTHORS.rst b/AUTHORS.rst index bd9edba..668453e 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -2,6 +2,7 @@ ad-m Alejandro Varas Alex Orange Andy Freeland +Artis Avotins Bram Boogaard Carl Meyer Curtis Maloney diff --git a/CHANGES.rst b/CHANGES.rst index f8fff6a..5da09fc 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ CHANGES master (unreleased) ------------------- +* Fix `InheritanceQuerySet` raising an `AttributeError` exception + under Django 1.9. + 2.5 (2016-04-18) ---------------- diff --git a/model_utils/managers.py b/model_utils/managers.py index d8ecf4e..4f06d2e 100644 --- a/model_utils/managers.py +++ b/model_utils/managers.py @@ -161,6 +161,12 @@ class InheritanceQuerySetMixin(object): def _get_sub_obj_recurse(self, obj, s): rel, _, s = s.partition(LOOKUP_SEP) + + # Django 1.9: If a primitive type gets passed to this recursive function, + # return None as non-models are not part of inheritance. + if not isinstance(obj, models.Model): + return None + try: node = getattr(obj, rel) except ObjectDoesNotExist: diff --git a/model_utils/tests/tests.py b/model_utils/tests/tests.py index 84e2dd6..14b6329 100644 --- a/model_utils/tests/tests.py +++ b/model_utils/tests/tests.py @@ -768,6 +768,54 @@ class InheritanceManagerTests(TestCase): self.assertEqual(list(queryset), [{'id': self.child1.pk}]) + @skipUnless(django.VERSION >= (1, 9, 0), "test only applies to Django 1.9+") + def test_dj19_values_list_on_select_subclasses(self): + """ + Using `select_subclasses` in conjunction with `values_list()` raised an + exception in `_get_sub_obj_recurse()` because the result of `values_list()` + is either a `tuple` or primitive objects if `flat=True` is specified, + because no type checking was done prior to fetching child nodes. + + Django versions below 1.9 are not affected by this bug. + """ + + # Querysets are cast to lists to force immediate evaluation. + # No exceptions must be thrown. + + # No argument to select_subclasses + objs_1 = list( + self.get_manager(). + select_subclasses(). + values_list('id') + ) + + # String argument to select_subclasses + objs_2 = list( + self.get_manager(). + select_subclasses( + "inheritancemanagertestchild2" + ). + values_list('id') + ) + + # String argument to select_subclasses + objs_3 = list( + self.get_manager(). + select_subclasses( + InheritanceManagerTestChild2 + ). + values_list('id') + ) + + assert all(( + isinstance(objs_1, list), + isinstance(objs_2, list), + isinstance(objs_3, list), + )) + + assert objs_1 == objs_2 == objs_3 + + class InheritanceManagerUsingModelsTests(TestCase): def setUp(self):