Skip to content

Commit

Permalink
Merge pull request #89 from kezabelle/bugfix/named_onetoone
Browse files Browse the repository at this point in the history
Allow InheritanceManager to work with manually specified OneToOnes
  • Loading branch information
carljm committed Oct 26, 2013
2 parents 06d3b7a + dd469a0 commit 3b5c99b
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 6 deletions.
6 changes: 3 additions & 3 deletions model_utils/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ def _get_subclasses_recurse(self, model, levels=None):
if levels or levels is None:
for subclass in self._get_subclasses_recurse(
rel.field.model, levels=levels):
subclasses.append(rel.var_name + LOOKUP_SEP + subclass)
subclasses.append(rel.var_name)
subclasses.append(rel.get_accessor_name() + LOOKUP_SEP + subclass)
subclasses.append(rel.get_accessor_name())
return subclasses


Expand All @@ -130,7 +130,7 @@ def _get_ancestors_path(self, model, levels=None):
if levels:
levels -= 1
while parent is not None:
ancestry.insert(0, parent.related.var_name)
ancestry.insert(0, parent.related.get_accessor_name())
if levels or levels is None:
parent = parent.related.parent_model._meta.get_ancestor_link(
self.model)
Expand Down
9 changes: 9 additions & 0 deletions model_utils/tests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,20 +44,29 @@ class InheritanceManagerTestChild1(InheritanceManagerTestParent):
objects = InheritanceManager()



class InheritanceManagerTestGrandChild1(InheritanceManagerTestChild1):
text_field = models.TextField()



class InheritanceManagerTestGrandChild1_2(InheritanceManagerTestChild1):
text_field = models.TextField()



class InheritanceManagerTestChild2(InheritanceManagerTestParent):
non_related_field_using_descriptor_2 = models.FileField(upload_to="test")
normal_field_2 = models.TextField()



class InheritanceManagerTestChild3(InheritanceManagerTestParent):
parent_ptr = models.OneToOneField(
InheritanceManagerTestParent, related_name='manual_onetoone',
parent_link=True)


class TimeStamp(TimeStampedModel):
pass

Expand Down
105 changes: 102 additions & 3 deletions model_utils/tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
TimeFrameManagerAdded, Dude, SplitFieldAbstractParent, Car, Spot,
ModelTracked, ModelTrackedFK, ModelTrackedNotDefault, ModelTrackedMultiple, InheritedModelTracked,
Tracked, TrackedFK, TrackedNotDefault, TrackedNonFieldAttr, TrackedMultiple,
InheritedTracked, StatusFieldDefaultFilled, StatusFieldDefaultNotFilled)
InheritedTracked, StatusFieldDefaultFilled, StatusFieldDefaultNotFilled,
InheritanceManagerTestChild3)


class GetExcerptTests(TestCase):
Expand Down Expand Up @@ -683,6 +684,76 @@ def test_version_determining_only_child_depth(self):
self.assertEqual(1, self.get_manager().all()._get_maximum_depth())


@skipUnless(django.VERSION < (1, 6, 0), "test only applies to Django < 1.6")
def test_manually_specifying_parent_fk_only_children(self):
"""
given a Model which inherits from another Model, but also declares
the OneToOne link manually using `related_name` and `parent_link`,
ensure that the relation names and subclasses are obtained correctly.
"""
child3 = InheritanceManagerTestChild3.objects.create()
results = InheritanceManagerTestParent.objects.all().select_subclasses()

expected_objs = [self.child1, self.child2,
InheritanceManagerTestChild1(pk=self.grandchild1.pk),
InheritanceManagerTestChild1(pk=self.grandchild1_2.pk),
child3]
self.assertEqual(list(results), expected_objs)

expected_related_names = [
'inheritancemanagertestchild1',
'inheritancemanagertestchild2',
'manual_onetoone', # this was set via parent_link & related_name
]
self.assertEqual(set(results.subclasses),
set(expected_related_names))


@skipUnless(django.VERSION >= (1, 6, 0), "test only applies to Django 1.6+")
def test_manually_specifying_parent_fk_including_grandchildren(self):
"""
given a Model which inherits from another Model, but also declares
the OneToOne link manually using `related_name` and `parent_link`,
ensure that the relation names and subclasses are obtained correctly.
"""
child3 = InheritanceManagerTestChild3.objects.create()
results = InheritanceManagerTestParent.objects.all().select_subclasses()

expected_objs = [self.child1, self.child2, self.grandchild1,
self.grandchild1_2, child3]
self.assertEqual(list(results), expected_objs)

expected_related_names = [
'inheritancemanagertestchild1__inheritancemanagertestgrandchild1',
'inheritancemanagertestchild1__inheritancemanagertestgrandchild1_2',
'inheritancemanagertestchild1',
'inheritancemanagertestchild2',
'manual_onetoone', # this was set via parent_link & related_name
]
self.assertEqual(set(results.subclasses),
set(expected_related_names))


def test_manually_specifying_parent_fk_single_subclass(self):
"""
Using a string related_name when the relation is manually defined
instead of implicit should still work in the same way.
"""
related_name = 'manual_onetoone'
child3 = InheritanceManagerTestChild3.objects.create()
results = InheritanceManagerTestParent.objects.all().select_subclasses(related_name)

expected_objs = [InheritanceManagerTestParent(pk=self.child1.pk),
InheritanceManagerTestParent(pk=self.child2.pk),
InheritanceManagerTestParent(pk=self.grandchild1.pk),
InheritanceManagerTestParent(pk=self.grandchild1_2.pk),
child3]
self.assertEqual(list(results), expected_objs)
expected_related_names = [related_name]
self.assertEqual(set(results.subclasses),
set(expected_related_names))


class InheritanceManagerUsingModelsTests(TestCase):

def setUp(self):
Expand Down Expand Up @@ -731,6 +802,7 @@ def test_selecting_all_subclasses_specifically_grandchildren(self):
objs = InheritanceManagerTestParent.objects.select_subclasses().order_by('pk')
objsmodels = InheritanceManagerTestParent.objects.select_subclasses(
InheritanceManagerTestChild1, InheritanceManagerTestChild2,
InheritanceManagerTestChild3,
InheritanceManagerTestGrandChild1,
InheritanceManagerTestGrandChild1_2).order_by('pk')
self.assertEqual(set(objs.subclasses), set(objsmodels.subclasses))
Expand All @@ -751,11 +823,15 @@ def test_selecting_all_subclasses_specifically_children(self):
objs = InheritanceManagerTestParent.objects.select_subclasses().order_by('pk')

if django.VERSION >= (1, 6, 0):
models = (InheritanceManagerTestChild1, InheritanceManagerTestChild2,
models = (InheritanceManagerTestChild1,
InheritanceManagerTestChild2,
InheritanceManagerTestChild3,
InheritanceManagerTestGrandChild1,
InheritanceManagerTestGrandChild1_2)
else:
models = (InheritanceManagerTestChild1, InheritanceManagerTestChild2)
models = (InheritanceManagerTestChild1,
InheritanceManagerTestChild2,
InheritanceManagerTestChild3)

objsmodels = InheritanceManagerTestParent.objects.select_subclasses(
*models).order_by('pk')
Expand Down Expand Up @@ -873,6 +949,29 @@ def test_child_doesnt_accidentally_get_parent(self):
], list(objs))


def test_manually_specifying_parent_fk_only_specific_child(self):
"""
given a Model which inherits from another Model, but also declares
the OneToOne link manually using `related_name` and `parent_link`,
ensure that the relation names and subclasses are obtained correctly.
"""
child3 = InheritanceManagerTestChild3.objects.create()
results = InheritanceManagerTestParent.objects.all().select_subclasses(
InheritanceManagerTestChild3)

expected_objs = [InheritanceManagerTestParent(pk=self.parent1.pk),
InheritanceManagerTestParent(pk=self.child1.pk),
InheritanceManagerTestParent(pk=self.child2.pk),
InheritanceManagerTestParent(pk=self.grandchild1.pk),
InheritanceManagerTestParent(pk=self.grandchild1_2.pk),
child3]
self.assertEqual(list(results), expected_objs)

expected_related_names = ['manual_onetoone']
self.assertEqual(set(results.subclasses),
set(expected_related_names))



class InheritanceManagerRelatedTests(InheritanceManagerTests):
def setUp(self):
Expand Down

0 comments on commit 3b5c99b

Please sign in to comment.