summaryrefslogtreecommitdiff
path: root/tests/m2m_through
diff options
context:
space:
mode:
authorDavide Ceretti <davide.ceretti@hogarthww.com>2014-09-24 21:55:16 +0100
committerLoic Bistuer <loic.bistuer@gmail.com>2014-09-26 01:28:32 +0700
commitd8e157d5ab1648a509b25d5ec571572ae936de79 (patch)
tree3883563bda97f06781134ed2d5ecfa63ef6de3ff /tests/m2m_through
parent74e7f91e6de11bc41ecba5d0b2e1b78d03865d32 (diff)
downloaddjango-d8e157d5ab1648a509b25d5ec571572ae936de79.tar.gz
Refactored m2m_through tests. Refs #18586
Refactored old tests that were rewritten 1:1 from doctests.
Diffstat (limited to 'tests/m2m_through')
-rw-r--r--tests/m2m_through/tests.py565
1 files changed, 291 insertions, 274 deletions
diff --git a/tests/m2m_through/tests.py b/tests/m2m_through/tests.py
index 39621bf958..25ec074d91 100644
--- a/tests/m2m_through/tests.py
+++ b/tests/m2m_through/tests.py
@@ -17,263 +17,307 @@ class M2mThroughTests(TestCase):
self.rock = Group.objects.create(name='Rock')
self.roll = Group.objects.create(name='Roll')
- def test_m2m_through(self):
- # We start out by making sure that the Group 'rock' has no members.
- self.assertQuerysetEqual(
- self.rock.members.all(),
- []
- )
- # To make Jim a member of Group Rock, simply create a Membership object.
+ def test_retrieve_intermediate_items(self):
Membership.objects.create(person=self.jim, group=self.rock)
- # We can do the same for Jane and Rock.
Membership.objects.create(person=self.jane, group=self.rock)
- # Let's check to make sure that it worked. Jane and Jim should be members of Rock.
- self.assertQuerysetEqual(
- self.rock.members.all(), [
- 'Jane',
- 'Jim'
- ],
- attrgetter("name")
- )
- # Now we can add a bunch more Membership objects to test with.
- Membership.objects.create(person=self.bob, group=self.roll)
- Membership.objects.create(person=self.jim, group=self.roll)
- Membership.objects.create(person=self.jane, group=self.roll)
- # We can get Jim's Group membership as with any ForeignKey.
+
+ expected = ['Jane', 'Jim']
self.assertQuerysetEqual(
- self.jim.group_set.all(), [
- 'Rock',
- 'Roll'
- ],
+ self.rock.members.all(),
+ expected,
attrgetter("name")
)
- # Querying the intermediary model works like normal.
+
+ def test_get_on_intermediate_model(self):
+ Membership.objects.create(person=self.jane, group=self.rock)
+
+ queryset = Membership.objects.get(person=self.jane, group=self.rock)
+
self.assertEqual(
- repr(Membership.objects.get(person=self.jane, group=self.rock)),
+ repr(queryset),
'<Membership: Jane is a member of Rock>'
)
- # It's not only get that works. Filter works like normal as well.
+
+ def test_filter_on_intermediate_model(self):
+ Membership.objects.create(person=self.jim, group=self.rock)
+ Membership.objects.create(person=self.jane, group=self.rock)
+
+ queryset = Membership.objects.filter(group=self.rock)
+
+ expected = [
+ '<Membership: Jim is a member of Rock>',
+ '<Membership: Jane is a member of Rock>',
+ ]
+
self.assertQuerysetEqual(
- Membership.objects.filter(person=self.jim), [
- '<Membership: Jim is a member of Rock>',
- '<Membership: Jim is a member of Roll>'
- ]
+ queryset,
+ expected
)
- self.rock.members.clear()
- # Now there will be no members of Rock.
+
+ def test_cannot_use_add_on_m2m_with_intermediary_model(self):
+ msg = 'Cannot use add() on a ManyToManyField which specifies an intermediary model'
+
+ with self.assertRaisesMessage(AttributeError, msg):
+ self.rock.members.add(self.bob)
+
self.assertQuerysetEqual(
self.rock.members.all(),
[]
)
- def test_forward_descriptors(self):
- # Due to complications with adding via an intermediary model,
- # the add method raises an error.
- self.assertRaisesMessage(
- AttributeError,
- 'Cannot use add() on a ManyToManyField which specifies an intermediary model',
- lambda: self.rock.members.add(self.bob)
- )
- # Create is also disabled as it suffers from the same problems as add.
- self.assertRaisesMessage(
- AttributeError,
- 'Cannot use create() on a ManyToManyField which specifies an intermediary model',
- lambda: self.rock.members.create(name='Anne')
+ def test_cannot_use_create_on_m2m_with_intermediary_model(self):
+ msg = 'Cannot use create() on a ManyToManyField which specifies an intermediary model'
+
+ with self.assertRaisesMessage(AttributeError, msg):
+ self.rock.members.create(name='Annie')
+
+ self.assertQuerysetEqual(
+ self.rock.members.all(),
+ []
)
- # Remove has similar complications, and it also raises an error.
- self.assertRaisesMessage(
- AttributeError,
- 'Cannot use remove() on a ManyToManyField which specifies an intermediary model',
- lambda: self.rock.members.remove(self.jim)
+
+ def test_cannot_use_remove_on_m2m_with_intermediary_model(self):
+ Membership.objects.create(person=self.jim, group=self.rock)
+ msg = 'Cannot use remove() on a ManyToManyField which specifies an intermediary model'
+
+ with self.assertRaisesMessage(AttributeError, msg):
+ self.rock.members.remove(self.jim)
+
+ self.assertQuerysetEqual(
+ self.rock.members.all(),
+ ['Jim', ],
+ attrgetter("name")
)
- m1 = Membership.objects.create(person=self.jim, group=self.rock)
- m2 = Membership.objects.create(person=self.jane, group=self.rock)
+ def test_cannot_use_setattr_on_m2m_with_intermediary_model(self):
+ msg = 'Cannot set values on a ManyToManyField which specifies an intermediary model'
+ members = list(Person.objects.filter(name__in=['Bob', 'Jim']))
- # Here we back up the list of all members of Rock.
- backup = list(self.rock.members.all())
- # ...and we verify that it has worked.
- self.assertEqual(
- [p.name for p in backup],
- ['Jane', 'Jim']
+ with self.assertRaisesMessage(AttributeError, msg):
+ setattr(self.rock, 'members', members)
+
+ self.assertQuerysetEqual(
+ self.rock.members.all(),
+ []
)
- # The clear function should still work.
+
+ def test_clear_removes_all_the_m2m_relationships(self):
+ Membership.objects.create(person=self.jim, group=self.rock)
+ Membership.objects.create(person=self.jane, group=self.rock)
+
self.rock.members.clear()
- # Now there will be no members of Rock.
+
self.assertQuerysetEqual(
self.rock.members.all(),
[]
)
- # Assignment should not work with models specifying a through model for
- # many of the same reasons as adding.
- self.assertRaisesMessage(
- AttributeError,
- 'Cannot set values on a ManyToManyField which specifies an intermediary model',
- setattr,
- self.rock,
- "members",
- backup
- )
+ def test_retrieve_reverse_intermediate_items(self):
+ Membership.objects.create(person=self.jim, group=self.rock)
+ Membership.objects.create(person=self.jim, group=self.roll)
- # Let's re-save those instances that we've cleared.
- m1.save()
- m2.save()
- # Verifying that those instances were re-saved successfully.
+ expected = ['Rock', 'Roll']
self.assertQuerysetEqual(
- self.rock.members.all(), [
- 'Jane',
- 'Jim'
- ],
+ self.jim.group_set.all(),
+ expected,
attrgetter("name")
)
- def test_reverse_descriptors(self):
- # Due to complications with adding via an intermediary model,
- # the add method is not provided.
- self.assertRaisesMessage(
- AttributeError,
- 'Cannot use add() on a ManyToManyField which specifies an intermediary model',
- lambda: self.bob.group_set.add(self.rock)
+ def test_cannot_use_add_on_reverse_m2m_with_intermediary_model(self):
+ msg = 'Cannot use add() on a ManyToManyField which specifies an intermediary model'
+
+ with self.assertRaisesMessage(AttributeError, msg):
+ self.bob.group_set.add(self.bob)
+
+ self.assertQuerysetEqual(
+ self.bob.group_set.all(),
+ []
)
- # Create is also disabled as it suffers from the same problems as add.
- self.assertRaisesMessage(
- AttributeError,
- 'Cannot use create() on a ManyToManyField which specifies an intermediary model',
- lambda: self.bob.group_set.create(name="funk")
+ def test_cannot_use_create_on_reverse_m2m_with_intermediary_model(self):
+ msg = 'Cannot use create() on a ManyToManyField which specifies an intermediary model'
+
+ with self.assertRaisesMessage(AttributeError, msg):
+ self.bob.group_set.create(name='Funk')
+
+ self.assertQuerysetEqual(
+ self.bob.group_set.all(),
+ []
)
- # Remove has similar complications, and is not provided either.
- self.assertRaisesMessage(
- AttributeError,
- 'Cannot use remove() on a ManyToManyField which specifies an intermediary model',
- lambda: self.jim.group_set.remove(self.rock)
+ def test_cannot_use_remove_on_reverse_m2m_with_intermediary_model(self):
+ Membership.objects.create(person=self.bob, group=self.rock)
+ msg = 'Cannot use remove() on a ManyToManyField which specifies an intermediary model'
+
+ with self.assertRaisesMessage(AttributeError, msg):
+ self.bob.group_set.remove(self.rock)
+
+ self.assertQuerysetEqual(
+ self.bob.group_set.all(),
+ ['Rock', ],
+ attrgetter('name')
)
- m1 = Membership.objects.create(person=self.jim, group=self.rock)
- m2 = Membership.objects.create(person=self.jim, group=self.roll)
+ def test_cannot_use_setattr_on_reverse_m2m_with_intermediary_model(self):
+ msg = 'Cannot set values on a ManyToManyField which specifies an intermediary model'
+ members = list(Group.objects.filter(name__in=['Rock', 'Roll']))
- # Here we back up the list of all of Jim's groups.
- backup = list(self.jim.group_set.all())
- self.assertEqual(
- [g.name for g in backup],
- ['Rock', 'Roll']
+ with self.assertRaisesMessage(AttributeError, msg):
+ setattr(self.bob, 'group_set', members)
+
+ self.assertQuerysetEqual(
+ self.bob.group_set.all(),
+ []
)
- # The clear function should still work.
+
+ def test_clear_on_reverse_removes_all_the_m2m_relationships(self):
+ Membership.objects.create(person=self.jim, group=self.rock)
+ Membership.objects.create(person=self.jim, group=self.roll)
+
self.jim.group_set.clear()
- # Now Jim will be in no groups.
+
self.assertQuerysetEqual(
self.jim.group_set.all(),
[]
)
- # Assignment should not work with models specifying a through model for
- # many of the same reasons as adding.
- self.assertRaisesMessage(
- AttributeError,
- 'Cannot set values on a ManyToManyField which specifies an intermediary model',
- setattr,
- self.jim,
- "group_set",
- backup
- )
-
- # Let's re-save those instances that we've cleared.
- m1.save()
- m2.save()
- # Verifying that those instances were re-saved successfully.
- self.assertQuerysetEqual(
- self.jim.group_set.all(), [
- 'Rock',
- 'Roll'
- ],
+
+ def test_query_model_by_attribute_name_of_related_model(self):
+ Membership.objects.create(person=self.jim, group=self.rock)
+ Membership.objects.create(person=self.jane, group=self.rock)
+ Membership.objects.create(person=self.bob, group=self.roll)
+ Membership.objects.create(person=self.jim, group=self.roll)
+ Membership.objects.create(person=self.jane, group=self.roll)
+
+ self.assertQuerysetEqual(
+ Group.objects.filter(members__name='Bob'),
+ ['Roll', ],
attrgetter("name")
)
- def test_custom_tests(self):
- # Let's see if we can query through our second relationship.
+ def test_query_first_model_by_intermediate_model_attribute(self):
+ Membership.objects.create(
+ person=self.jane, group=self.roll,
+ invite_reason="She was just awesome."
+ )
+ Membership.objects.create(
+ person=self.jim, group=self.roll,
+ invite_reason="He is good."
+ )
+ Membership.objects.create(person=self.bob, group=self.roll)
+
+ qs = Group.objects.filter(
+ membership__invite_reason="She was just awesome."
+ )
self.assertQuerysetEqual(
- self.rock.custom_members.all(),
- []
+ qs,
+ ['Roll'],
+ attrgetter("name")
+ )
+
+ def test_query_second_model_by_intermediate_model_attribute(self):
+ Membership.objects.create(
+ person=self.jane, group=self.roll,
+ invite_reason="She was just awesome."
+ )
+ Membership.objects.create(
+ person=self.jim, group=self.roll,
+ invite_reason="He is good."
+ )
+ Membership.objects.create(person=self.bob, group=self.roll)
+
+ qs = Person.objects.filter(
+ membership__invite_reason="She was just awesome."
)
- # We can query in the opposite direction as well.
self.assertQuerysetEqual(
- self.bob.custom.all(),
- []
+ qs,
+ ['Jane'],
+ attrgetter("name")
)
+ def test_query_model_by_related_model_name(self):
+ Membership.objects.create(person=self.jim, group=self.rock)
+ Membership.objects.create(person=self.jane, group=self.rock)
+ Membership.objects.create(person=self.bob, group=self.roll)
+ Membership.objects.create(person=self.jim, group=self.roll)
+ Membership.objects.create(person=self.jane, group=self.roll)
+
+ self.assertQuerysetEqual(
+ Person.objects.filter(group__name="Rock"),
+ ['Jane', 'Jim'],
+ attrgetter("name")
+ )
+
+ def test_query_model_by_custom_related_name(self):
CustomMembership.objects.create(person=self.bob, group=self.rock)
CustomMembership.objects.create(person=self.jim, group=self.rock)
- # If we get the number of people in Rock, it should be both Bob and Jim.
self.assertQuerysetEqual(
- self.rock.custom_members.all(), [
- 'Bob',
- 'Jim'
- ],
+ Person.objects.filter(custom__name="Rock"),
+ ['Bob', 'Jim'],
attrgetter("name")
)
- # Bob should only be in one custom group.
+
+ def test_query_model_by_intermediate_can_return_non_unique_queryset(self):
+ Membership.objects.create(person=self.jim, group=self.rock)
+ Membership.objects.create(
+ person=self.jane, group=self.rock,
+ date_joined=datetime(2006, 1, 1)
+ )
+ Membership.objects.create(
+ person=self.bob, group=self.roll,
+ date_joined=datetime(2004, 1, 1))
+ Membership.objects.create(person=self.jim, group=self.roll)
+ Membership.objects.create(
+ person=self.jane, group=self.roll,
+ date_joined=datetime(2004, 1, 1))
+
+ qs = Person.objects.filter(
+ membership__date_joined__gt=datetime(2004, 1, 1)
+ )
self.assertQuerysetEqual(
- self.bob.custom.all(), [
- 'Rock'
- ],
+ qs,
+ ['Jane', 'Jim', 'Jim'],
attrgetter("name")
)
- # Let's make sure our new descriptors don't conflict with the FK related_name.
+
+ def test_custom_related_name_forward_empty_qs(self):
self.assertQuerysetEqual(
- self.bob.custom_person_related_name.all(), [
- '<CustomMembership: Bob is a member of Rock>'
- ]
+ self.rock.custom_members.all(),
+ []
)
- def test_self_referential_tests(self):
- # Let's first create a person who has no friends.
- tony = PersonSelfRefM2M.objects.create(name="Tony")
+ def test_custom_related_name_reverse_empty_qs(self):
self.assertQuerysetEqual(
- tony.friends.all(),
+ self.bob.custom.all(),
[]
)
- chris = PersonSelfRefM2M.objects.create(name="Chris")
- Friendship.objects.create(first=tony, second=chris, date_friended=datetime.now())
+ def test_custom_related_name_forward_non_empty_qs(self):
+ CustomMembership.objects.create(person=self.bob, group=self.rock)
+ CustomMembership.objects.create(person=self.jim, group=self.rock)
- # Tony should now show that Chris is his friend.
self.assertQuerysetEqual(
- tony.friends.all(), [
- 'Chris'
- ],
+ self.rock.custom_members.all(),
+ ['Bob', 'Jim'],
attrgetter("name")
)
- # But we haven't established that Chris is Tony's Friend.
- self.assertQuerysetEqual(
- chris.friends.all(),
- []
- )
- Friendship.objects.create(first=chris, second=tony, date_friended=datetime.now())
- # Having added Chris as a friend, let's make sure that his friend set reflects
- # that addition.
+ def test_custom_related_name_reverse_non_empty_qs(self):
+ CustomMembership.objects.create(person=self.bob, group=self.rock)
+ CustomMembership.objects.create(person=self.jim, group=self.rock)
+
self.assertQuerysetEqual(
- chris.friends.all(), [
- 'Tony'
- ],
+ self.bob.custom.all(),
+ ['Rock'],
attrgetter("name")
)
- # Chris gets mad and wants to get rid of all of his friends.
- chris.friends.clear()
- # Now he should not have any more friends.
- self.assertQuerysetEqual(
- chris.friends.all(),
- []
- )
- # Since this isn't a symmetrical relation, Tony's friend link still exists.
+ def test_custom_related_name_doesnt_conflict_with_fky_related_name(self):
+ CustomMembership.objects.create(person=self.bob, group=self.rock)
+
self.assertQuerysetEqual(
- tony.friends.all(), [
- 'Chris'
- ],
- attrgetter("name")
+ self.bob.custom_person_related_name.all(),
+ ['<CustomMembership: Bob is a member of Rock>']
)
def test_through_fields(self):
@@ -284,128 +328,101 @@ class M2mThroughTests(TestCase):
event = Event.objects.create(title='Rockwhale 2014')
Invitation.objects.create(event=event, inviter=self.bob, invitee=self.jim)
Invitation.objects.create(event=event, inviter=self.bob, invitee=self.jane)
- self.assertQuerysetEqual(event.invitees.all(), [
- 'Jane',
- 'Jim',
- ], attrgetter('name'))
+ self.assertQuerysetEqual(
+ event.invitees.all(),
+ ['Jane', 'Jim'],
+ attrgetter('name')
+ )
- def test_through_fields_self_referential(self):
- john = Employee.objects.create(name='john')
- peter = Employee.objects.create(name='peter')
- mary = Employee.objects.create(name='mary')
- harry = Employee.objects.create(name='harry')
- Relationship.objects.create(source=john, target=peter, another=None)
- Relationship.objects.create(source=john, target=mary, another=None)
- Relationship.objects.create(source=john, target=harry, another=peter)
- self.assertQuerysetEqual(john.subordinates.all(), [
- 'peter',
- 'mary',
- 'harry',
- ], attrgetter('name'))
- def test_query_tests(self):
- Membership.objects.create(person=self.jim, group=self.rock)
- m2 = Membership.objects.create(person=self.jane, group=self.rock)
- m3 = Membership.objects.create(person=self.bob, group=self.roll)
- Membership.objects.create(person=self.jim, group=self.roll)
- m5 = Membership.objects.create(person=self.jane, group=self.roll)
-
- m2.invite_reason = "She was just awesome."
- m2.date_joined = datetime(2006, 1, 1)
- m2.save()
- m3.date_joined = datetime(2004, 1, 1)
- m3.save()
- m5.date_joined = datetime(2004, 1, 1)
- m5.save()
-
- # We can query for the related model by using its attribute name (members, in
- # this case).
- self.assertQuerysetEqual(
- Group.objects.filter(members__name='Bob'), [
- 'Roll'
- ],
- attrgetter("name")
+class M2mThroughReferentialTests(TestCase):
+ def test_self_referential_empty_qs(self):
+ tony = PersonSelfRefM2M.objects.create(name="Tony")
+ self.assertQuerysetEqual(
+ tony.friends.all(),
+ []
)
- # To query through the intermediary model, we specify its model name.
- # In this case, membership.
- self.assertQuerysetEqual(
- Group.objects.filter(membership__invite_reason="She was just awesome."), [
- 'Rock'
- ],
- attrgetter("name")
+ def test_self_referential_non_symmentrical_first_side(self):
+ tony = PersonSelfRefM2M.objects.create(name="Tony")
+ chris = PersonSelfRefM2M.objects.create(name="Chris")
+ Friendship.objects.create(
+ first=tony, second=chris, date_friended=datetime.now()
)
- # If we want to query in the reverse direction by the related model, use its
- # model name (group, in this case).
self.assertQuerysetEqual(
- Person.objects.filter(group__name="Rock"), [
- 'Jane',
- 'Jim'
- ],
+ tony.friends.all(),
+ ['Chris'],
attrgetter("name")
)
- CustomMembership.objects.create(person=self.bob, group=self.rock)
- CustomMembership.objects.create(person=self.jim, group=self.rock)
- # If the m2m field has specified a related_name, using that will work.
+ def test_self_referential_non_symmentrical_second_side(self):
+ tony = PersonSelfRefM2M.objects.create(name="Tony")
+ chris = PersonSelfRefM2M.objects.create(name="Chris")
+ Friendship.objects.create(
+ first=tony, second=chris, date_friended=datetime.now()
+ )
+
self.assertQuerysetEqual(
- Person.objects.filter(custom__name="Rock"), [
- 'Bob',
- 'Jim'
- ],
- attrgetter("name")
+ chris.friends.all(),
+ []
)
- # To query through the intermediary model in the reverse direction, we again
- # specify its model name (membership, in this case).
+ def test_self_referential_non_symmentrical_clear_first_side(self):
+ tony = PersonSelfRefM2M.objects.create(name="Tony")
+ chris = PersonSelfRefM2M.objects.create(name="Chris")
+ Friendship.objects.create(
+ first=tony, second=chris, date_friended=datetime.now()
+ )
+
+ chris.friends.clear()
+
self.assertQuerysetEqual(
- Person.objects.filter(membership__invite_reason="She was just awesome."), [
- 'Jane'
- ],
- attrgetter("name")
+ chris.friends.all(),
+ []
)
- # Let's see all of the groups that Jane joined after 1 Jan 2005:
+ # Since this isn't a symmetrical relation, Tony's friend link still exists.
self.assertQuerysetEqual(
- Group.objects.filter(membership__date_joined__gt=datetime(2005, 1, 1), membership__person=self.jane), [
- 'Rock'
- ],
+ tony.friends.all(),
+ ['Chris'],
attrgetter("name")
)
- # Queries also work in the reverse direction: Now let's see all of the people
- # that have joined Rock since 1 Jan 2005:
+ def test_self_referential_symmentrical(self):
+ tony = PersonSelfRefM2M.objects.create(name="Tony")
+ chris = PersonSelfRefM2M.objects.create(name="Chris")
+ Friendship.objects.create(
+ first=tony, second=chris, date_friended=datetime.now()
+ )
+ Friendship.objects.create(
+ first=chris, second=tony, date_friended=datetime.now()
+ )
+
self.assertQuerysetEqual(
- Person.objects.filter(membership__date_joined__gt=datetime(2005, 1, 1), membership__group=self.rock), [
- 'Jane',
- 'Jim'
- ],
+ tony.friends.all(),
+ ['Chris'],
attrgetter("name")
)
- # Conceivably, queries through membership could return correct, but non-unique
- # querysets. To demonstrate this, we query for all people who have joined a
- # group after 2004:
self.assertQuerysetEqual(
- Person.objects.filter(membership__date_joined__gt=datetime(2004, 1, 1)), [
- 'Jane',
- 'Jim',
- 'Jim'
- ],
+ chris.friends.all(),
+ ['Tony'],
attrgetter("name")
)
- # Jim showed up twice, because he joined two groups ('Rock', and 'Roll'):
- self.assertEqual(
- [(m.person.name, m.group.name) for m in Membership.objects.filter(date_joined__gt=datetime(2004, 1, 1))],
- [('Jane', 'Rock'), ('Jim', 'Rock'), ('Jim', 'Roll')]
- )
- # QuerySet's distinct() method can correct this problem.
+ def test_through_fields_self_referential(self):
+ john = Employee.objects.create(name='john')
+ peter = Employee.objects.create(name='peter')
+ mary = Employee.objects.create(name='mary')
+ harry = Employee.objects.create(name='harry')
+
+ Relationship.objects.create(source=john, target=peter, another=None)
+ Relationship.objects.create(source=john, target=mary, another=None)
+ Relationship.objects.create(source=john, target=harry, another=peter)
+
self.assertQuerysetEqual(
- Person.objects.filter(membership__date_joined__gt=datetime(2004, 1, 1)).distinct(), [
- 'Jane',
- 'Jim'
- ],
- attrgetter("name")
+ john.subordinates.all(),
+ ['peter', 'mary', 'harry'],
+ attrgetter('name')
)