PageRenderTime 32ms CodeModel.GetById 10ms app.highlight 17ms RepoModel.GetById 1ms app.codeStats 0ms

/tests/modeltests/m2m_through/tests.py

https://code.google.com/p/mango-py/
Python | 343 lines | 247 code | 34 blank | 62 comment | 6 complexity | d9e9f0894adb29f803449e803d8c09d9 MD5 | raw file
  1from datetime import datetime
  2from operator import attrgetter
  3
  4from django.test import TestCase
  5
  6from models import Person, Group, Membership, CustomMembership, \
  7    TestNoDefaultsOrNulls, PersonSelfRefM2M, Friendship
  8
  9
 10class M2mThroughTests(TestCase):
 11    def setUp(self):
 12        self.bob = Person.objects.create(name='Bob')
 13        self.jim = Person.objects.create(name='Jim')
 14        self.jane = Person.objects.create(name='Jane')
 15        self.rock = Group.objects.create(name='Rock')
 16        self.roll = Group.objects.create(name='Roll')
 17
 18    def test_m2m_through(self):
 19        # We start out by making sure that the Group 'rock' has no members.
 20        self.assertQuerysetEqual(
 21            self.rock.members.all(),
 22            []
 23        )
 24        # To make Jim a member of Group Rock, simply create a Membership object.
 25        m1 = Membership.objects.create(person=self.jim, group=self.rock)
 26        # We can do the same for Jane and Rock.
 27        m2 = Membership.objects.create(person=self.jane, group=self.rock)
 28        # Let's check to make sure that it worked.  Jane and Jim should be members of Rock.
 29        self.assertQuerysetEqual(
 30            self.rock.members.all(), [
 31                'Jane',
 32                'Jim'
 33            ],
 34            attrgetter("name")
 35        )
 36        # Now we can add a bunch more Membership objects to test with.
 37        m3 = Membership.objects.create(person=self.bob, group=self.roll)
 38        m4 = Membership.objects.create(person=self.jim, group=self.roll)
 39        m5 = Membership.objects.create(person=self.jane, group=self.roll)
 40        # We can get Jim's Group membership as with any ForeignKey.
 41        self.assertQuerysetEqual(
 42            self.jim.group_set.all(), [
 43                'Rock',
 44                'Roll'
 45            ],
 46            attrgetter("name")
 47        )
 48        # Querying the intermediary model works like normal.
 49        self.assertEqual(
 50            repr(Membership.objects.get(person=self.jane, group=self.rock)),
 51            '<Membership: Jane is a member of Rock>'
 52        )
 53        # It's not only get that works. Filter works like normal as well.
 54        self.assertQuerysetEqual(
 55            Membership.objects.filter(person=self.jim), [
 56                '<Membership: Jim is a member of Rock>',
 57                '<Membership: Jim is a member of Roll>'
 58            ]
 59        )
 60        self.rock.members.clear()
 61        # Now there will be no members of Rock.
 62        self.assertQuerysetEqual(
 63            self.rock.members.all(),
 64            []
 65        )
 66
 67
 68
 69    def test_forward_descriptors(self):
 70        # Due to complications with adding via an intermediary model,
 71        # the add method is not provided.
 72        self.assertRaises(AttributeError, lambda: self.rock.members.add(self.bob))
 73        # Create is also disabled as it suffers from the same problems as add.
 74        self.assertRaises(AttributeError, lambda: self.rock.members.create(name='Anne'))
 75        # Remove has similar complications, and is not provided either.
 76        self.assertRaises(AttributeError, lambda: self.rock.members.remove(self.jim))
 77
 78        m1 = Membership.objects.create(person=self.jim, group=self.rock)
 79        m2 = Membership.objects.create(person=self.jane, group=self.rock)
 80
 81        # Here we back up the list of all members of Rock.
 82        backup = list(self.rock.members.all())
 83        # ...and we verify that it has worked.
 84        self.assertEqual(
 85            [p.name for p in backup],
 86            ['Jane', 'Jim']
 87        )
 88        # The clear function should still work.
 89        self.rock.members.clear()
 90        # Now there will be no members of Rock.
 91        self.assertQuerysetEqual(
 92            self.rock.members.all(),
 93            []
 94        )
 95
 96        # Assignment should not work with models specifying a through model for many of
 97        # the same reasons as adding.
 98        self.assertRaises(AttributeError, setattr, self.rock, "members", backup)
 99        # Let's re-save those instances that we've cleared.
100        m1.save()
101        m2.save()
102        # Verifying that those instances were re-saved successfully.
103        self.assertQuerysetEqual(
104            self.rock.members.all(),[
105                'Jane',
106                'Jim'
107            ],
108            attrgetter("name")
109        )
110
111    def test_reverse_descriptors(self):
112        # Due to complications with adding via an intermediary model,
113        # the add method is not provided.
114        self.assertRaises(AttributeError, lambda: self.bob.group_set.add(self.rock))
115        # Create is also disabled as it suffers from the same problems as add.
116        self.assertRaises(AttributeError, lambda: self.bob.group_set.create(name="funk"))
117        # Remove has similar complications, and is not provided either.
118        self.assertRaises(AttributeError, lambda: self.jim.group_set.remove(self.rock))
119
120        m1 = Membership.objects.create(person=self.jim, group=self.rock)
121        m2 = Membership.objects.create(person=self.jim, group=self.roll)
122
123        # Here we back up the list of all of Jim's groups.
124        backup = list(self.jim.group_set.all())
125        self.assertEqual(
126            [g.name for g in backup],
127            ['Rock', 'Roll']
128        )
129        # The clear function should still work.
130        self.jim.group_set.clear()
131        # Now Jim will be in no groups.
132        self.assertQuerysetEqual(
133            self.jim.group_set.all(),
134            []
135        )
136        # Assignment should not work with models specifying a through model for many of
137        # the same reasons as adding.
138        self.assertRaises(AttributeError, setattr, self.jim, "group_set", backup)
139        # Let's re-save those instances that we've cleared.
140
141        m1.save()
142        m2.save()
143        # Verifying that those instances were re-saved successfully.
144        self.assertQuerysetEqual(
145            self.jim.group_set.all(),[
146                'Rock',
147                'Roll'
148            ],
149            attrgetter("name")
150        )
151
152    def test_custom_tests(self):
153        # Let's see if we can query through our second relationship.
154        self.assertQuerysetEqual(
155            self.rock.custom_members.all(),
156            []
157        )
158        # We can query in the opposite direction as well.
159        self.assertQuerysetEqual(
160            self.bob.custom.all(),
161            []
162        )
163
164        cm1 = CustomMembership.objects.create(person=self.bob, group=self.rock)
165        cm2 = CustomMembership.objects.create(person=self.jim, group=self.rock)
166
167        # If we get the number of people in Rock, it should be both Bob and Jim.
168        self.assertQuerysetEqual(
169            self.rock.custom_members.all(),[
170                'Bob',
171                'Jim'
172            ],
173            attrgetter("name")
174        )
175        # Bob should only be in one custom group.
176        self.assertQuerysetEqual(
177            self.bob.custom.all(),[
178                'Rock'
179            ],
180            attrgetter("name")
181        )
182        # Let's make sure our new descriptors don't conflict with the FK related_name.
183        self.assertQuerysetEqual(
184            self.bob.custom_person_related_name.all(),[
185                '<CustomMembership: Bob is a member of Rock>'
186            ]
187        )
188
189    def test_self_referential_tests(self):
190        # Let's first create a person who has no friends.
191        tony = PersonSelfRefM2M.objects.create(name="Tony")
192        self.assertQuerysetEqual(
193            tony.friends.all(),
194            []
195        )
196
197        chris = PersonSelfRefM2M.objects.create(name="Chris")
198        f = Friendship.objects.create(first=tony, second=chris, date_friended=datetime.now())
199
200        # Tony should now show that Chris is his friend.
201        self.assertQuerysetEqual(
202            tony.friends.all(),[
203                'Chris'
204            ],
205            attrgetter("name")
206        )
207        # But we haven't established that Chris is Tony's Friend.
208        self.assertQuerysetEqual(
209            chris.friends.all(),
210            []
211        )
212        f2 = Friendship.objects.create(first=chris, second=tony, date_friended=datetime.now())
213
214        # Having added Chris as a friend, let's make sure that his friend set reflects
215        # that addition.
216        self.assertQuerysetEqual(
217            chris.friends.all(),[
218                'Tony'
219            ],
220            attrgetter("name")
221        )
222
223        # Chris gets mad and wants to get rid of all of his friends.
224        chris.friends.clear()
225        # Now he should not have any more friends.
226        self.assertQuerysetEqual(
227            chris.friends.all(),
228            []
229        )
230        # Since this isn't a symmetrical relation, Tony's friend link still exists.
231        self.assertQuerysetEqual(
232            tony.friends.all(),[
233                'Chris'
234            ],
235            attrgetter("name")
236        )
237
238    def test_query_tests(self):
239        m1 = Membership.objects.create(person=self.jim, group=self.rock)
240        m2 = Membership.objects.create(person=self.jane, group=self.rock)
241        m3 = Membership.objects.create(person=self.bob, group=self.roll)
242        m4 = Membership.objects.create(person=self.jim, group=self.roll)
243        m5 = Membership.objects.create(person=self.jane, group=self.roll)
244
245        m2.invite_reason = "She was just awesome."
246        m2.date_joined = datetime(2006, 1, 1)
247        m2.save()
248        m3.date_joined = datetime(2004, 1, 1)
249        m3.save()
250        m5.date_joined = datetime(2004, 1, 1)
251        m5.save()
252
253        # We can query for the related model by using its attribute name (members, in
254        # this case).
255        self.assertQuerysetEqual(
256            Group.objects.filter(members__name='Bob'),[
257                'Roll'
258            ],
259            attrgetter("name")
260        )
261
262        # To query through the intermediary model, we specify its model name.
263        # In this case, membership.
264        self.assertQuerysetEqual(
265            Group.objects.filter(membership__invite_reason="She was just awesome."),[
266                'Rock'
267            ],
268            attrgetter("name")
269        )
270
271        # If we want to query in the reverse direction by the related model, use its
272        # model name (group, in this case).
273        self.assertQuerysetEqual(
274            Person.objects.filter(group__name="Rock"),[
275                'Jane',
276                'Jim'
277            ],
278            attrgetter("name")
279        )
280
281        cm1 = CustomMembership.objects.create(person=self.bob, group=self.rock)
282        cm2 = CustomMembership.objects.create(person=self.jim, group=self.rock)
283        # If the m2m field has specified a related_name, using that will work.
284        self.assertQuerysetEqual(
285            Person.objects.filter(custom__name="Rock"),[
286                'Bob',
287                'Jim'
288            ],
289            attrgetter("name")
290        )
291
292        # To query through the intermediary model in the reverse direction, we again
293        # specify its model name (membership, in this case).
294        self.assertQuerysetEqual(
295            Person.objects.filter(membership__invite_reason="She was just awesome."),[
296                'Jane'
297            ],
298            attrgetter("name")
299        )
300
301        # Let's see all of the groups that Jane joined after 1 Jan 2005:
302        self.assertQuerysetEqual(
303            Group.objects.filter(membership__date_joined__gt=datetime(2005, 1, 1), membership__person=self.jane),[
304                'Rock'
305            ],
306            attrgetter("name")
307        )
308
309        # Queries also work in the reverse direction: Now let's see all of the people
310        # that have joined Rock since 1 Jan 2005:
311        self.assertQuerysetEqual(
312            Person.objects.filter(membership__date_joined__gt=datetime(2005, 1, 1), membership__group=self.rock),[
313                'Jane',
314                'Jim'
315            ],
316            attrgetter("name")
317        )
318
319        # Conceivably, queries through membership could return correct, but non-unique
320        # querysets.  To demonstrate this, we query for all people who have joined a
321        # group after 2004:
322        self.assertQuerysetEqual(
323            Person.objects.filter(membership__date_joined__gt=datetime(2004, 1, 1)),[
324                'Jane',
325                'Jim',
326                'Jim'
327            ],
328            attrgetter("name")
329        )
330
331        # Jim showed up twice, because he joined two groups ('Rock', and 'Roll'):
332        self.assertEqual(
333            [(m.person.name, m.group.name) for m in Membership.objects.filter(date_joined__gt=datetime(2004, 1, 1))],
334            [(u'Jane', u'Rock'), (u'Jim', u'Rock'), (u'Jim', u'Roll')]
335        )
336        # QuerySet's distinct() method can correct this problem.
337        self.assertQuerysetEqual(
338            Person.objects.filter(membership__date_joined__gt=datetime(2004, 1, 1)).distinct(),[
339                'Jane',
340                'Jim'
341            ],
342            attrgetter("name")
343        )