[1.2.X] Migrated m2m_through doctests. Thanks to the anonymous contributor.

Backport of r14419 from trunk.

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.2.X@14420 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Russell Keith-Magee 2010-11-02 02:01:18 +00:00
parent 775165b236
commit 0b0ae709d1
2 changed files with 343 additions and 272 deletions

View File

@ -63,275 +63,3 @@ class Friendship(models.Model):
first = models.ForeignKey(PersonSelfRefM2M, related_name="rel_from_set")
second = models.ForeignKey(PersonSelfRefM2M, related_name="rel_to_set")
date_friended = models.DateTimeField()
__test__ = {'API_TESTS':"""
>>> from datetime import datetime
### Creation and Saving Tests ###
>>> bob = Person.objects.create(name='Bob')
>>> jim = Person.objects.create(name='Jim')
>>> jane = Person.objects.create(name='Jane')
>>> rock = Group.objects.create(name='Rock')
>>> roll = Group.objects.create(name='Roll')
# We start out by making sure that the Group 'rock' has no members.
>>> rock.members.all()
[]
# To make Jim a member of Group Rock, simply create a Membership object.
>>> m1 = Membership.objects.create(person=jim, group=rock)
# We can do the same for Jane and Rock.
>>> m2 = Membership.objects.create(person=jane, group=rock)
# Let's check to make sure that it worked. Jane and Jim should be members of Rock.
>>> rock.members.all()
[<Person: Jane>, <Person: Jim>]
# Now we can add a bunch more Membership objects to test with.
>>> m3 = Membership.objects.create(person=bob, group=roll)
>>> m4 = Membership.objects.create(person=jim, group=roll)
>>> m5 = Membership.objects.create(person=jane, group=roll)
# We can get Jim's Group membership as with any ForeignKey.
>>> jim.group_set.all()
[<Group: Rock>, <Group: Roll>]
# Querying the intermediary model works like normal.
# In this case we get Jane's membership to Rock.
>>> m = Membership.objects.get(person=jane, group=rock)
>>> m
<Membership: Jane is a member of Rock>
# Now we set some date_joined dates for further testing.
>>> m2.invite_reason = "She was just awesome."
>>> m2.date_joined = datetime(2006, 1, 1)
>>> m2.save()
>>> m5.date_joined = datetime(2004, 1, 1)
>>> m5.save()
>>> m3.date_joined = datetime(2004, 1, 1)
>>> m3.save()
# It's not only get that works. Filter works like normal as well.
>>> Membership.objects.filter(person=jim)
[<Membership: Jim is a member of Rock>, <Membership: Jim is a member of Roll>]
### Forward Descriptors Tests ###
# Due to complications with adding via an intermediary model,
# the add method is not provided.
>>> rock.members.add(bob)
Traceback (most recent call last):
...
AttributeError: 'ManyRelatedManager' object has no attribute 'add'
# Create is also disabled as it suffers from the same problems as add.
>>> rock.members.create(name='Anne')
Traceback (most recent call last):
...
AttributeError: Cannot use create() on a ManyToManyField which specifies an intermediary model. Use m2m_through.Membership's Manager instead.
# Remove has similar complications, and is not provided either.
>>> rock.members.remove(jim)
Traceback (most recent call last):
...
AttributeError: 'ManyRelatedManager' object has no attribute 'remove'
# Here we back up the list of all members of Rock.
>>> backup = list(rock.members.all())
# ...and we verify that it has worked.
>>> backup
[<Person: Jane>, <Person: Jim>]
# The clear function should still work.
>>> rock.members.clear()
# Now there will be no members of Rock.
>>> rock.members.all()
[]
# Assignment should not work with models specifying a through model for many of
# the same reasons as adding.
>>> rock.members = backup
Traceback (most recent call last):
...
AttributeError: Cannot set values on a ManyToManyField which specifies an intermediary model. Use m2m_through.Membership's Manager instead.
# Let's re-save those instances that we've cleared.
>>> m1.save()
>>> m2.save()
# Verifying that those instances were re-saved successfully.
>>> rock.members.all()
[<Person: Jane>, <Person: Jim>]
### Reverse Descriptors Tests ###
# Due to complications with adding via an intermediary model,
# the add method is not provided.
>>> bob.group_set.add(rock)
Traceback (most recent call last):
...
AttributeError: 'ManyRelatedManager' object has no attribute 'add'
# Create is also disabled as it suffers from the same problems as add.
>>> bob.group_set.create(name='Funk')
Traceback (most recent call last):
...
AttributeError: Cannot use create() on a ManyToManyField which specifies an intermediary model. Use m2m_through.Membership's Manager instead.
# Remove has similar complications, and is not provided either.
>>> jim.group_set.remove(rock)
Traceback (most recent call last):
...
AttributeError: 'ManyRelatedManager' object has no attribute 'remove'
# Here we back up the list of all of Jim's groups.
>>> backup = list(jim.group_set.all())
>>> backup
[<Group: Rock>, <Group: Roll>]
# The clear function should still work.
>>> jim.group_set.clear()
# Now Jim will be in no groups.
>>> jim.group_set.all()
[]
# Assignment should not work with models specifying a through model for many of
# the same reasons as adding.
>>> jim.group_set = backup
Traceback (most recent call last):
...
AttributeError: Cannot set values on a ManyToManyField which specifies an intermediary model. Use m2m_through.Membership's Manager instead.
# Let's re-save those instances that we've cleared.
>>> m1.save()
>>> m4.save()
# Verifying that those instances were re-saved successfully.
>>> jim.group_set.all()
[<Group: Rock>, <Group: Roll>]
### Custom Tests ###
# Let's see if we can query through our second relationship.
>>> rock.custom_members.all()
[]
# We can query in the opposite direction as well.
>>> bob.custom.all()
[]
# Let's create some membership objects in this custom relationship.
>>> cm1 = CustomMembership.objects.create(person=bob, group=rock)
>>> cm2 = CustomMembership.objects.create(person=jim, group=rock)
# If we get the number of people in Rock, it should be both Bob and Jim.
>>> rock.custom_members.all()
[<Person: Bob>, <Person: Jim>]
# Bob should only be in one custom group.
>>> bob.custom.all()
[<Group: Rock>]
# Let's make sure our new descriptors don't conflict with the FK related_name.
>>> bob.custom_person_related_name.all()
[<CustomMembership: Bob is a member of Rock>]
### SELF-REFERENTIAL TESTS ###
# Let's first create a person who has no friends.
>>> tony = PersonSelfRefM2M.objects.create(name="Tony")
>>> tony.friends.all()
[]
# Now let's create another person for Tony to be friends with.
>>> chris = PersonSelfRefM2M.objects.create(name="Chris")
>>> f = Friendship.objects.create(first=tony, second=chris, date_friended=datetime.now())
# Tony should now show that Chris is his friend.
>>> tony.friends.all()
[<PersonSelfRefM2M: Chris>]
# But we haven't established that Chris is Tony's Friend.
>>> chris.friends.all()
[]
# So let's do that now.
>>> f2 = 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.
>>> chris.friends.all()
[<PersonSelfRefM2M: Tony>]
# Chris gets mad and wants to get rid of all of his friends.
>>> chris.friends.clear()
# Now he should not have any more friends.
>>> chris.friends.all()
[]
# Since this isn't a symmetrical relation, Tony's friend link still exists.
>>> tony.friends.all()
[<PersonSelfRefM2M: Chris>]
### QUERY TESTS ###
# We can query for the related model by using its attribute name (members, in
# this case).
>>> Group.objects.filter(members__name='Bob')
[<Group: Roll>]
# To query through the intermediary model, we specify its model name.
# In this case, membership.
>>> Group.objects.filter(membership__invite_reason="She was just awesome.")
[<Group: Rock>]
# If we want to query in the reverse direction by the related model, use its
# model name (group, in this case).
>>> Person.objects.filter(group__name="Rock")
[<Person: Jane>, <Person: Jim>]
# If the m2m field has specified a related_name, using that will work.
>>> Person.objects.filter(custom__name="Rock")
[<Person: Bob>, <Person: Jim>]
# To query through the intermediary model in the reverse direction, we again
# specify its model name (membership, in this case).
>>> Person.objects.filter(membership__invite_reason="She was just awesome.")
[<Person: Jane>]
# Let's see all of the groups that Jane joined after 1 Jan 2005:
>>> Group.objects.filter(membership__date_joined__gt=datetime(2005, 1, 1), membership__person =jane)
[<Group: Rock>]
# Queries also work in the reverse direction: Now let's see all of the people
# that have joined Rock since 1 Jan 2005:
>>> Person.objects.filter(membership__date_joined__gt=datetime(2005, 1, 1), membership__group=rock)
[<Person: Jane>, <Person: Jim>]
# 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:
>>> Person.objects.filter(membership__date_joined__gt=datetime(2004, 1, 1))
[<Person: Jane>, <Person: Jim>, <Person: Jim>]
# Jim showed up twice, because he joined two groups ('Rock', and 'Roll'):
>>> [(m.person.name, m.group.name) for m in
... Membership.objects.filter(date_joined__gt=datetime(2004, 1, 1))]
[(u'Jane', u'Rock'), (u'Jim', u'Rock'), (u'Jim', u'Roll')]
# QuerySet's distinct() method can correct this problem.
>>> Person.objects.filter(membership__date_joined__gt=datetime(2004, 1, 1)).distinct()
[<Person: Jane>, <Person: Jim>]
"""}

View File

@ -0,0 +1,343 @@
from datetime import datetime
from operator import attrgetter
from django.test import TestCase
from models import Person, Group, Membership, CustomMembership, \
TestNoDefaultsOrNulls, PersonSelfRefM2M, Friendship
class M2mThroughTests(TestCase):
def setUp(self):
self.bob = Person.objects.create(name='Bob')
self.jim = Person.objects.create(name='Jim')
self.jane = Person.objects.create(name='Jane')
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.
m1 = Membership.objects.create(person=self.jim, group=self.rock)
# We can do the same for Jane and Rock.
m2 = 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.
m3 = Membership.objects.create(person=self.bob, group=self.roll)
m4 = Membership.objects.create(person=self.jim, group=self.roll)
m5 = Membership.objects.create(person=self.jane, group=self.roll)
# We can get Jim's Group membership as with any ForeignKey.
self.assertQuerysetEqual(
self.jim.group_set.all(), [
'Rock',
'Roll'
],
attrgetter("name")
)
# Querying the intermediary model works like normal.
self.assertEqual(
repr(Membership.objects.get(person=self.jane, group=self.rock)),
'<Membership: Jane is a member of Rock>'
)
# It's not only get that works. Filter works like normal as well.
self.assertQuerysetEqual(
Membership.objects.filter(person=self.jim), [
'<Membership: Jim is a member of Rock>',
'<Membership: Jim is a member of Roll>'
]
)
self.rock.members.clear()
# Now there will be no members of Rock.
self.assertQuerysetEqual(
self.rock.members.all(),
[]
)
def test_forward_descriptors(self):
# Due to complications with adding via an intermediary model,
# the add method is not provided.
self.assertRaises(AttributeError, lambda: self.rock.members.add(self.bob))
# Create is also disabled as it suffers from the same problems as add.
self.assertRaises(AttributeError, lambda: self.rock.members.create(name='Anne'))
# Remove has similar complications, and is not provided either.
self.assertRaises(AttributeError, lambda: self.rock.members.remove(self.jim))
m1 = Membership.objects.create(person=self.jim, group=self.rock)
m2 = Membership.objects.create(person=self.jane, group=self.rock)
# 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']
)
# The clear function should still work.
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.assertRaises(AttributeError, setattr, self.rock, "members", 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.rock.members.all(),[
'Jane',
'Jim'
],
attrgetter("name")
)
def test_reverse_descriptors(self):
# Due to complications with adding via an intermediary model,
# the add method is not provided.
self.assertRaises(AttributeError, lambda: self.bob.group_set.add(self.rock))
# Create is also disabled as it suffers from the same problems as add.
self.assertRaises(AttributeError, lambda: self.bob.group_set.create(name="funk"))
# Remove has similar complications, and is not provided either.
self.assertRaises(AttributeError, lambda: self.jim.group_set.remove(self.rock))
m1 = Membership.objects.create(person=self.jim, group=self.rock)
m2 = Membership.objects.create(person=self.jim, group=self.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']
)
# The clear function should still work.
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.assertRaises(AttributeError, 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'
],
attrgetter("name")
)
def test_custom_tests(self):
# Let's see if we can query through our second relationship.
self.assertQuerysetEqual(
self.rock.custom_members.all(),
[]
)
# We can query in the opposite direction as well.
self.assertQuerysetEqual(
self.bob.custom.all(),
[]
)
cm1 = CustomMembership.objects.create(person=self.bob, group=self.rock)
cm2 = 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'
],
attrgetter("name")
)
# Bob should only be in one custom group.
self.assertQuerysetEqual(
self.bob.custom.all(),[
'Rock'
],
attrgetter("name")
)
# Let's make sure our new descriptors don't conflict with the FK related_name.
self.assertQuerysetEqual(
self.bob.custom_person_related_name.all(),[
'<CustomMembership: Bob is a member of Rock>'
]
)
def test_self_referential_tests(self):
# Let's first create a person who has no friends.
tony = PersonSelfRefM2M.objects.create(name="Tony")
self.assertQuerysetEqual(
tony.friends.all(),
[]
)
chris = PersonSelfRefM2M.objects.create(name="Chris")
f = Friendship.objects.create(first=tony, second=chris, date_friended=datetime.now())
# Tony should now show that Chris is his friend.
self.assertQuerysetEqual(
tony.friends.all(),[
'Chris'
],
attrgetter("name")
)
# But we haven't established that Chris is Tony's Friend.
self.assertQuerysetEqual(
chris.friends.all(),
[]
)
f2 = 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.
self.assertQuerysetEqual(
chris.friends.all(),[
'Tony'
],
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.
self.assertQuerysetEqual(
tony.friends.all(),[
'Chris'
],
attrgetter("name")
)
def test_query_tests(self):
m1 = 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)
m4 = 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")
)
# 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")
)
# 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'
],
attrgetter("name")
)
cm1 = CustomMembership.objects.create(person=self.bob, group=self.rock)
cm2 = CustomMembership.objects.create(person=self.jim, group=self.rock)
# If the m2m field has specified a related_name, using that will work.
self.assertQuerysetEqual(
Person.objects.filter(custom__name="Rock"),[
'Bob',
'Jim'
],
attrgetter("name")
)
# To query through the intermediary model in the reverse direction, we again
# specify its model name (membership, in this case).
self.assertQuerysetEqual(
Person.objects.filter(membership__invite_reason="She was just awesome."),[
'Jane'
],
attrgetter("name")
)
# Let's see all of the groups that Jane joined after 1 Jan 2005:
self.assertQuerysetEqual(
Group.objects.filter(membership__date_joined__gt=datetime(2005, 1, 1), membership__person=self.jane),[
'Rock'
],
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:
self.assertQuerysetEqual(
Person.objects.filter(membership__date_joined__gt=datetime(2005, 1, 1), membership__group=self.rock),[
'Jane',
'Jim'
],
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'
],
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))],
[(u'Jane', u'Rock'), (u'Jim', u'Rock'), (u'Jim', u'Roll')]
)
# QuerySet's distinct() method can correct this problem.
self.assertQuerysetEqual(
Person.objects.filter(membership__date_joined__gt=datetime(2004, 1, 1)).distinct(),[
'Jane',
'Jim'
],
attrgetter("name")
)