tests.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. from __future__ import unicode_literals
  2. from django.core import management
  3. from django.contrib.auth.models import User
  4. from django.test import TestCase
  5. from django.utils.six import StringIO
  6. from .models import (Person, Group, Membership, UserMembership, Car, Driver,
  7. CarDriver)
  8. class M2MThroughTestCase(TestCase):
  9. def test_everything(self):
  10. bob = Person.objects.create(name="Bob")
  11. jim = Person.objects.create(name="Jim")
  12. rock = Group.objects.create(name="Rock")
  13. roll = Group.objects.create(name="Roll")
  14. frank = User.objects.create_user("frank", "frank@example.com", "password")
  15. jane = User.objects.create_user("jane", "jane@example.com", "password")
  16. Membership.objects.create(person=bob, group=rock)
  17. Membership.objects.create(person=bob, group=roll)
  18. Membership.objects.create(person=jim, group=rock)
  19. self.assertQuerysetEqual(
  20. bob.group_set.all(), [
  21. "<Group: Rock>",
  22. "<Group: Roll>",
  23. ],
  24. ordered=False
  25. )
  26. self.assertQuerysetEqual(
  27. roll.members.all(), [
  28. "<Person: Bob>",
  29. ]
  30. )
  31. self.assertRaises(AttributeError, setattr, bob, "group_set", [])
  32. self.assertRaises(AttributeError, setattr, roll, "members", [])
  33. self.assertRaises(AttributeError, rock.members.create, name="Anne")
  34. self.assertRaises(AttributeError, bob.group_set.create, name="Funk")
  35. UserMembership.objects.create(user=frank, group=rock)
  36. UserMembership.objects.create(user=frank, group=roll)
  37. UserMembership.objects.create(user=jane, group=rock)
  38. self.assertQuerysetEqual(
  39. frank.group_set.all(), [
  40. "<Group: Rock>",
  41. "<Group: Roll>",
  42. ],
  43. ordered=False
  44. )
  45. self.assertQuerysetEqual(
  46. roll.user_members.all(), [
  47. "<User: frank>",
  48. ]
  49. )
  50. def test_serialization(self):
  51. "m2m-through models aren't serialized as m2m fields. Refs #8134"
  52. p = Person.objects.create(name="Bob")
  53. g = Group.objects.create(name="Roll")
  54. m = Membership.objects.create(person=p, group=g)
  55. pks = {"p_pk": p.pk, "g_pk": g.pk, "m_pk": m.pk}
  56. out = StringIO()
  57. management.call_command("dumpdata", "m2m_through_regress", format="json", stdout=out)
  58. self.assertJSONEqual(out.getvalue().strip(), """[{"pk": %(m_pk)s, "model": "m2m_through_regress.membership", "fields": {"person": %(p_pk)s, "price": 100, "group": %(g_pk)s}}, {"pk": %(p_pk)s, "model": "m2m_through_regress.person", "fields": {"name": "Bob"}}, {"pk": %(g_pk)s, "model": "m2m_through_regress.group", "fields": {"name": "Roll"}}]""" % pks)
  59. out = StringIO()
  60. management.call_command("dumpdata", "m2m_through_regress", format="xml",
  61. indent=2, stdout=out)
  62. self.assertXMLEqual(out.getvalue().strip(), """
  63. <?xml version="1.0" encoding="utf-8"?>
  64. <django-objects version="1.0">
  65. <object pk="%(m_pk)s" model="m2m_through_regress.membership">
  66. <field to="m2m_through_regress.person" name="person" rel="ManyToOneRel">%(p_pk)s</field>
  67. <field to="m2m_through_regress.group" name="group" rel="ManyToOneRel">%(g_pk)s</field>
  68. <field type="IntegerField" name="price">100</field>
  69. </object>
  70. <object pk="%(p_pk)s" model="m2m_through_regress.person">
  71. <field type="CharField" name="name">Bob</field>
  72. </object>
  73. <object pk="%(g_pk)s" model="m2m_through_regress.group">
  74. <field type="CharField" name="name">Roll</field>
  75. </object>
  76. </django-objects>
  77. """.strip() % pks)
  78. def test_join_trimming(self):
  79. "Check that we don't involve too many copies of the intermediate table when doing a join. Refs #8046, #8254"
  80. bob = Person.objects.create(name="Bob")
  81. jim = Person.objects.create(name="Jim")
  82. rock = Group.objects.create(name="Rock")
  83. roll = Group.objects.create(name="Roll")
  84. Membership.objects.create(person=bob, group=rock)
  85. Membership.objects.create(person=jim, group=rock, price=50)
  86. Membership.objects.create(person=bob, group=roll, price=50)
  87. self.assertQuerysetEqual(
  88. rock.members.filter(membership__price=50), [
  89. "<Person: Jim>",
  90. ]
  91. )
  92. self.assertQuerysetEqual(
  93. bob.group_set.filter(membership__price=50), [
  94. "<Group: Roll>",
  95. ]
  96. )
  97. class ToFieldThroughTests(TestCase):
  98. def setUp(self):
  99. self.car = Car.objects.create(make="Toyota")
  100. self.driver = Driver.objects.create(name="Ryan Briscoe")
  101. CarDriver.objects.create(car=self.car, driver=self.driver)
  102. # We are testing if wrong objects get deleted due to using wrong
  103. # field value in m2m queries. So, it is essential that the pk
  104. # numberings do not match.
  105. # Create one intentionally unused driver to mix up the autonumbering
  106. self.unused_driver = Driver.objects.create(name="Barney Gumble")
  107. # And two intentionally unused cars.
  108. self.unused_car1 = Car.objects.create(make="Trabant")
  109. self.unused_car2 = Car.objects.create(make="Wartburg")
  110. def test_to_field(self):
  111. self.assertQuerysetEqual(
  112. self.car.drivers.all(),
  113. ["<Driver: Ryan Briscoe>"]
  114. )
  115. def test_to_field_reverse(self):
  116. self.assertQuerysetEqual(
  117. self.driver.car_set.all(),
  118. ["<Car: Toyota>"]
  119. )
  120. def test_to_field_clear_reverse(self):
  121. self.driver.car_set.clear()
  122. self.assertQuerysetEqual(
  123. self.driver.car_set.all(),[])
  124. def test_to_field_clear(self):
  125. self.car.drivers.clear()
  126. self.assertQuerysetEqual(
  127. self.car.drivers.all(),[])
  128. # Low level tests for _add_items and _remove_items. We test these methods
  129. # because .add/.remove aren't available for m2m fields with through, but
  130. # through is the only way to set to_field currently. We do want to make
  131. # sure these methods are ready if the ability to use .add or .remove with
  132. # to_field relations is added some day.
  133. def test_add(self):
  134. self.assertQuerysetEqual(
  135. self.car.drivers.all(),
  136. ["<Driver: Ryan Briscoe>"]
  137. )
  138. # Yikes - barney is going to drive...
  139. self.car.drivers._add_items('car', 'driver', self.unused_driver)
  140. self.assertQuerysetEqual(
  141. self.car.drivers.all(),
  142. ["<Driver: Barney Gumble>", "<Driver: Ryan Briscoe>"]
  143. )
  144. def test_add_null(self):
  145. nullcar = Car.objects.create(make=None)
  146. with self.assertRaises(ValueError):
  147. nullcar.drivers._add_items('car', 'driver', self.unused_driver)
  148. def test_add_related_null(self):
  149. nulldriver = Driver.objects.create(name=None)
  150. with self.assertRaises(ValueError):
  151. self.car.drivers._add_items('car', 'driver', nulldriver)
  152. def test_add_reverse(self):
  153. car2 = Car.objects.create(make="Honda")
  154. self.assertQuerysetEqual(
  155. self.driver.car_set.all(),
  156. ["<Car: Toyota>"]
  157. )
  158. self.driver.car_set._add_items('driver', 'car', car2)
  159. self.assertQuerysetEqual(
  160. self.driver.car_set.all(),
  161. ["<Car: Toyota>", "<Car: Honda>"],
  162. ordered=False
  163. )
  164. def test_add_null_reverse(self):
  165. nullcar = Car.objects.create(make=None)
  166. with self.assertRaises(ValueError):
  167. self.driver.car_set._add_items('driver', 'car', nullcar)
  168. def test_add_null_reverse_related(self):
  169. nulldriver = Driver.objects.create(name=None)
  170. with self.assertRaises(ValueError):
  171. nulldriver.car_set._add_items('driver', 'car', self.car)
  172. def test_remove(self):
  173. self.assertQuerysetEqual(
  174. self.car.drivers.all(),
  175. ["<Driver: Ryan Briscoe>"]
  176. )
  177. self.car.drivers._remove_items('car', 'driver', self.driver)
  178. self.assertQuerysetEqual(
  179. self.car.drivers.all(),[])
  180. def test_remove_reverse(self):
  181. self.assertQuerysetEqual(
  182. self.driver.car_set.all(),
  183. ["<Car: Toyota>"]
  184. )
  185. self.driver.car_set._remove_items('driver', 'car', self.car)
  186. self.assertQuerysetEqual(
  187. self.driver.car_set.all(),[])
  188. class ThroughLoadDataTestCase(TestCase):
  189. fixtures = ["m2m_through"]
  190. def test_sequence_creation(self):
  191. "Check that sequences on an m2m_through are created for the through model, not a phantom auto-generated m2m table. Refs #11107"
  192. out = StringIO()
  193. management.call_command("dumpdata", "m2m_through_regress", format="json", stdout=out)
  194. self.assertJSONEqual(out.getvalue().strip(), """[{"pk": 1, "model": "m2m_through_regress.usermembership", "fields": {"price": 100, "group": 1, "user": 1}}, {"pk": 1, "model": "m2m_through_regress.person", "fields": {"name": "Guido"}}, {"pk": 1, "model": "m2m_through_regress.group", "fields": {"name": "Python Core Group"}}]""")