tests.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. from datetime import date, datetime, timedelta
  2. from operator import attrgetter
  3. from django.db import IntegrityError
  4. from django.test import TestCase
  5. from .models import (
  6. CustomMembership,
  7. Employee,
  8. Event,
  9. Friendship,
  10. Group,
  11. Ingredient,
  12. Invitation,
  13. Membership,
  14. Person,
  15. PersonChild,
  16. PersonSelfRefM2M,
  17. Recipe,
  18. RecipeIngredient,
  19. Relationship,
  20. SymmetricalFriendship,
  21. )
  22. class M2mThroughTests(TestCase):
  23. @classmethod
  24. def setUpTestData(cls):
  25. cls.bob = Person.objects.create(name="Bob")
  26. cls.jim = Person.objects.create(name="Jim")
  27. cls.jane = Person.objects.create(name="Jane")
  28. cls.rock = Group.objects.create(name="Rock")
  29. cls.roll = Group.objects.create(name="Roll")
  30. def test_reverse_inherited_m2m_with_through_fields_list_hashable(self):
  31. reverse_m2m = Person._meta.get_field("events_invited")
  32. self.assertEqual(reverse_m2m.through_fields, ["event", "invitee"])
  33. inherited_reverse_m2m = PersonChild._meta.get_field("events_invited")
  34. self.assertEqual(inherited_reverse_m2m.through_fields, ["event", "invitee"])
  35. self.assertEqual(hash(reverse_m2m), hash(inherited_reverse_m2m))
  36. def test_retrieve_intermediate_items(self):
  37. Membership.objects.create(person=self.jim, group=self.rock)
  38. Membership.objects.create(person=self.jane, group=self.rock)
  39. expected = ["Jane", "Jim"]
  40. self.assertQuerySetEqual(self.rock.members.all(), expected, attrgetter("name"))
  41. def test_get_on_intermediate_model(self):
  42. Membership.objects.create(person=self.jane, group=self.rock)
  43. queryset = Membership.objects.get(person=self.jane, group=self.rock)
  44. self.assertEqual(repr(queryset), "<Membership: Jane is a member of Rock>")
  45. def test_filter_on_intermediate_model(self):
  46. m1 = Membership.objects.create(person=self.jim, group=self.rock)
  47. m2 = Membership.objects.create(person=self.jane, group=self.rock)
  48. queryset = Membership.objects.filter(group=self.rock)
  49. self.assertSequenceEqual(queryset, [m1, m2])
  50. def test_add_on_m2m_with_intermediate_model(self):
  51. self.rock.members.add(
  52. self.bob, through_defaults={"invite_reason": "He is good."}
  53. )
  54. self.assertSequenceEqual(self.rock.members.all(), [self.bob])
  55. self.assertEqual(self.rock.membership_set.get().invite_reason, "He is good.")
  56. def test_add_on_m2m_with_intermediate_model_callable_through_default(self):
  57. def invite_reason_callable():
  58. return "They were good at %s" % datetime.now()
  59. self.rock.members.add(
  60. self.bob,
  61. self.jane,
  62. through_defaults={"invite_reason": invite_reason_callable},
  63. )
  64. self.assertSequenceEqual(self.rock.members.all(), [self.bob, self.jane])
  65. self.assertEqual(
  66. self.rock.membership_set.filter(
  67. invite_reason__startswith="They were good at ",
  68. ).count(),
  69. 2,
  70. )
  71. # invite_reason_callable() is called once.
  72. self.assertEqual(
  73. self.bob.membership_set.get().invite_reason,
  74. self.jane.membership_set.get().invite_reason,
  75. )
  76. def test_set_on_m2m_with_intermediate_model_callable_through_default(self):
  77. self.rock.members.set(
  78. [self.bob, self.jane],
  79. through_defaults={"invite_reason": lambda: "Why not?"},
  80. )
  81. self.assertSequenceEqual(self.rock.members.all(), [self.bob, self.jane])
  82. self.assertEqual(
  83. self.rock.membership_set.filter(
  84. invite_reason__startswith="Why not?",
  85. ).count(),
  86. 2,
  87. )
  88. def test_add_on_m2m_with_intermediate_model_value_required(self):
  89. self.rock.nodefaultsnonulls.add(
  90. self.jim, through_defaults={"nodefaultnonull": 1}
  91. )
  92. self.assertEqual(self.rock.testnodefaultsornulls_set.get().nodefaultnonull, 1)
  93. def test_add_on_m2m_with_intermediate_model_value_required_fails(self):
  94. with self.assertRaises(IntegrityError):
  95. self.rock.nodefaultsnonulls.add(self.jim)
  96. def test_create_on_m2m_with_intermediate_model(self):
  97. annie = self.rock.members.create(
  98. name="Annie", through_defaults={"invite_reason": "She was just awesome."}
  99. )
  100. self.assertSequenceEqual(self.rock.members.all(), [annie])
  101. self.assertEqual(
  102. self.rock.membership_set.get().invite_reason, "She was just awesome."
  103. )
  104. def test_create_on_m2m_with_intermediate_model_callable_through_default(self):
  105. annie = self.rock.members.create(
  106. name="Annie",
  107. through_defaults={"invite_reason": lambda: "She was just awesome."},
  108. )
  109. self.assertSequenceEqual(self.rock.members.all(), [annie])
  110. self.assertEqual(
  111. self.rock.membership_set.get().invite_reason,
  112. "She was just awesome.",
  113. )
  114. def test_create_on_m2m_with_intermediate_model_value_required(self):
  115. self.rock.nodefaultsnonulls.create(
  116. name="Test", through_defaults={"nodefaultnonull": 1}
  117. )
  118. self.assertEqual(self.rock.testnodefaultsornulls_set.get().nodefaultnonull, 1)
  119. def test_create_on_m2m_with_intermediate_model_value_required_fails(self):
  120. with self.assertRaises(IntegrityError):
  121. self.rock.nodefaultsnonulls.create(name="Test")
  122. def test_get_or_create_on_m2m_with_intermediate_model_value_required(self):
  123. self.rock.nodefaultsnonulls.get_or_create(
  124. name="Test", through_defaults={"nodefaultnonull": 1}
  125. )
  126. self.assertEqual(self.rock.testnodefaultsornulls_set.get().nodefaultnonull, 1)
  127. def test_get_or_create_on_m2m_with_intermediate_model_value_required_fails(self):
  128. with self.assertRaises(IntegrityError):
  129. self.rock.nodefaultsnonulls.get_or_create(name="Test")
  130. def test_update_or_create_on_m2m_with_intermediate_model_value_required(self):
  131. self.rock.nodefaultsnonulls.update_or_create(
  132. name="Test", through_defaults={"nodefaultnonull": 1}
  133. )
  134. self.assertEqual(self.rock.testnodefaultsornulls_set.get().nodefaultnonull, 1)
  135. def test_update_or_create_on_m2m_with_intermediate_model_value_required_fails(self):
  136. with self.assertRaises(IntegrityError):
  137. self.rock.nodefaultsnonulls.update_or_create(name="Test")
  138. def test_remove_on_m2m_with_intermediate_model(self):
  139. Membership.objects.create(person=self.jim, group=self.rock)
  140. self.rock.members.remove(self.jim)
  141. self.assertSequenceEqual(self.rock.members.all(), [])
  142. def test_remove_on_m2m_with_intermediate_model_multiple(self):
  143. Membership.objects.create(person=self.jim, group=self.rock, invite_reason="1")
  144. Membership.objects.create(person=self.jim, group=self.rock, invite_reason="2")
  145. self.assertSequenceEqual(self.rock.members.all(), [self.jim, self.jim])
  146. self.rock.members.remove(self.jim)
  147. self.assertSequenceEqual(self.rock.members.all(), [])
  148. def test_set_on_m2m_with_intermediate_model(self):
  149. members = list(Person.objects.filter(name__in=["Bob", "Jim"]))
  150. self.rock.members.set(members)
  151. self.assertSequenceEqual(self.rock.members.all(), [self.bob, self.jim])
  152. def test_set_on_m2m_with_intermediate_model_value_required(self):
  153. self.rock.nodefaultsnonulls.set(
  154. [self.jim], through_defaults={"nodefaultnonull": 1}
  155. )
  156. self.assertEqual(self.rock.testnodefaultsornulls_set.get().nodefaultnonull, 1)
  157. self.rock.nodefaultsnonulls.set(
  158. [self.jim], through_defaults={"nodefaultnonull": 2}
  159. )
  160. self.assertEqual(self.rock.testnodefaultsornulls_set.get().nodefaultnonull, 1)
  161. self.rock.nodefaultsnonulls.set(
  162. [self.jim], through_defaults={"nodefaultnonull": 2}, clear=True
  163. )
  164. self.assertEqual(self.rock.testnodefaultsornulls_set.get().nodefaultnonull, 2)
  165. def test_set_on_m2m_with_intermediate_model_value_required_fails(self):
  166. with self.assertRaises(IntegrityError):
  167. self.rock.nodefaultsnonulls.set([self.jim])
  168. def test_clear_removes_all_the_m2m_relationships(self):
  169. Membership.objects.create(person=self.jim, group=self.rock)
  170. Membership.objects.create(person=self.jane, group=self.rock)
  171. self.rock.members.clear()
  172. self.assertQuerySetEqual(self.rock.members.all(), [])
  173. def test_retrieve_reverse_intermediate_items(self):
  174. Membership.objects.create(person=self.jim, group=self.rock)
  175. Membership.objects.create(person=self.jim, group=self.roll)
  176. expected = ["Rock", "Roll"]
  177. self.assertQuerySetEqual(self.jim.group_set.all(), expected, attrgetter("name"))
  178. def test_add_on_reverse_m2m_with_intermediate_model(self):
  179. self.bob.group_set.add(self.rock)
  180. self.assertSequenceEqual(self.bob.group_set.all(), [self.rock])
  181. def test_create_on_reverse_m2m_with_intermediate_model(self):
  182. funk = self.bob.group_set.create(name="Funk")
  183. self.assertSequenceEqual(self.bob.group_set.all(), [funk])
  184. def test_remove_on_reverse_m2m_with_intermediate_model(self):
  185. Membership.objects.create(person=self.bob, group=self.rock)
  186. self.bob.group_set.remove(self.rock)
  187. self.assertSequenceEqual(self.bob.group_set.all(), [])
  188. def test_set_on_reverse_m2m_with_intermediate_model(self):
  189. members = list(Group.objects.filter(name__in=["Rock", "Roll"]))
  190. self.bob.group_set.set(members)
  191. self.assertSequenceEqual(self.bob.group_set.all(), [self.rock, self.roll])
  192. def test_clear_on_reverse_removes_all_the_m2m_relationships(self):
  193. Membership.objects.create(person=self.jim, group=self.rock)
  194. Membership.objects.create(person=self.jim, group=self.roll)
  195. self.jim.group_set.clear()
  196. self.assertQuerySetEqual(self.jim.group_set.all(), [])
  197. def test_query_model_by_attribute_name_of_related_model(self):
  198. Membership.objects.create(person=self.jim, group=self.rock)
  199. Membership.objects.create(person=self.jane, group=self.rock)
  200. Membership.objects.create(person=self.bob, group=self.roll)
  201. Membership.objects.create(person=self.jim, group=self.roll)
  202. Membership.objects.create(person=self.jane, group=self.roll)
  203. self.assertQuerySetEqual(
  204. Group.objects.filter(members__name="Bob"), ["Roll"], attrgetter("name")
  205. )
  206. def test_order_by_relational_field_through_model(self):
  207. today = datetime.now()
  208. yesterday = today - timedelta(days=1)
  209. CustomMembership.objects.create(
  210. person=self.jim, group=self.rock, date_joined=yesterday
  211. )
  212. CustomMembership.objects.create(
  213. person=self.bob, group=self.rock, date_joined=today
  214. )
  215. CustomMembership.objects.create(
  216. person=self.jane, group=self.roll, date_joined=yesterday
  217. )
  218. CustomMembership.objects.create(
  219. person=self.jim, group=self.roll, date_joined=today
  220. )
  221. self.assertSequenceEqual(
  222. self.rock.custom_members.order_by("custom_person_related_name"),
  223. [self.jim, self.bob],
  224. )
  225. self.assertSequenceEqual(
  226. self.roll.custom_members.order_by("custom_person_related_name"),
  227. [self.jane, self.jim],
  228. )
  229. def test_query_first_model_by_intermediate_model_attribute(self):
  230. Membership.objects.create(
  231. person=self.jane, group=self.roll, invite_reason="She was just awesome."
  232. )
  233. Membership.objects.create(
  234. person=self.jim, group=self.roll, invite_reason="He is good."
  235. )
  236. Membership.objects.create(person=self.bob, group=self.roll)
  237. qs = Group.objects.filter(membership__invite_reason="She was just awesome.")
  238. self.assertQuerySetEqual(qs, ["Roll"], attrgetter("name"))
  239. def test_query_second_model_by_intermediate_model_attribute(self):
  240. Membership.objects.create(
  241. person=self.jane, group=self.roll, invite_reason="She was just awesome."
  242. )
  243. Membership.objects.create(
  244. person=self.jim, group=self.roll, invite_reason="He is good."
  245. )
  246. Membership.objects.create(person=self.bob, group=self.roll)
  247. qs = Person.objects.filter(membership__invite_reason="She was just awesome.")
  248. self.assertQuerySetEqual(qs, ["Jane"], attrgetter("name"))
  249. def test_query_model_by_related_model_name(self):
  250. Membership.objects.create(person=self.jim, group=self.rock)
  251. Membership.objects.create(person=self.jane, group=self.rock)
  252. Membership.objects.create(person=self.bob, group=self.roll)
  253. Membership.objects.create(person=self.jim, group=self.roll)
  254. Membership.objects.create(person=self.jane, group=self.roll)
  255. self.assertQuerySetEqual(
  256. Person.objects.filter(group__name="Rock"),
  257. ["Jane", "Jim"],
  258. attrgetter("name"),
  259. )
  260. def test_query_model_by_custom_related_name(self):
  261. CustomMembership.objects.create(person=self.bob, group=self.rock)
  262. CustomMembership.objects.create(person=self.jim, group=self.rock)
  263. self.assertQuerySetEqual(
  264. Person.objects.filter(custom__name="Rock"),
  265. ["Bob", "Jim"],
  266. attrgetter("name"),
  267. )
  268. def test_query_model_by_intermediate_can_return_non_unique_queryset(self):
  269. Membership.objects.create(person=self.jim, group=self.rock)
  270. Membership.objects.create(
  271. person=self.jane, group=self.rock, date_joined=datetime(2006, 1, 1)
  272. )
  273. Membership.objects.create(
  274. person=self.bob, group=self.roll, date_joined=datetime(2004, 1, 1)
  275. )
  276. Membership.objects.create(person=self.jim, group=self.roll)
  277. Membership.objects.create(
  278. person=self.jane, group=self.roll, date_joined=datetime(2004, 1, 1)
  279. )
  280. qs = Person.objects.filter(membership__date_joined__gt=datetime(2004, 1, 1))
  281. self.assertQuerySetEqual(qs, ["Jane", "Jim", "Jim"], attrgetter("name"))
  282. def test_custom_related_name_forward_empty_qs(self):
  283. self.assertQuerySetEqual(self.rock.custom_members.all(), [])
  284. def test_custom_related_name_reverse_empty_qs(self):
  285. self.assertQuerySetEqual(self.bob.custom.all(), [])
  286. def test_custom_related_name_forward_non_empty_qs(self):
  287. CustomMembership.objects.create(person=self.bob, group=self.rock)
  288. CustomMembership.objects.create(person=self.jim, group=self.rock)
  289. self.assertQuerySetEqual(
  290. self.rock.custom_members.all(), ["Bob", "Jim"], attrgetter("name")
  291. )
  292. def test_custom_related_name_reverse_non_empty_qs(self):
  293. CustomMembership.objects.create(person=self.bob, group=self.rock)
  294. CustomMembership.objects.create(person=self.jim, group=self.rock)
  295. self.assertQuerySetEqual(self.bob.custom.all(), ["Rock"], attrgetter("name"))
  296. def test_custom_related_name_doesnt_conflict_with_fky_related_name(self):
  297. c = CustomMembership.objects.create(person=self.bob, group=self.rock)
  298. self.assertSequenceEqual(self.bob.custom_person_related_name.all(), [c])
  299. def test_through_fields(self):
  300. """
  301. Relations with intermediary tables with multiple FKs
  302. to the M2M's ``to`` model are possible.
  303. """
  304. event = Event.objects.create(title="Rockwhale 2014")
  305. Invitation.objects.create(event=event, inviter=self.bob, invitee=self.jim)
  306. Invitation.objects.create(event=event, inviter=self.bob, invitee=self.jane)
  307. self.assertQuerySetEqual(
  308. event.invitees.all(), ["Jane", "Jim"], attrgetter("name")
  309. )
  310. class M2mThroughReferentialTests(TestCase):
  311. def test_self_referential_empty_qs(self):
  312. tony = PersonSelfRefM2M.objects.create(name="Tony")
  313. self.assertQuerySetEqual(tony.friends.all(), [])
  314. def test_self_referential_non_symmetrical_first_side(self):
  315. tony = PersonSelfRefM2M.objects.create(name="Tony")
  316. chris = PersonSelfRefM2M.objects.create(name="Chris")
  317. Friendship.objects.create(
  318. first=tony, second=chris, date_friended=datetime.now()
  319. )
  320. self.assertQuerySetEqual(tony.friends.all(), ["Chris"], attrgetter("name"))
  321. def test_self_referential_non_symmetrical_second_side(self):
  322. tony = PersonSelfRefM2M.objects.create(name="Tony")
  323. chris = PersonSelfRefM2M.objects.create(name="Chris")
  324. Friendship.objects.create(
  325. first=tony, second=chris, date_friended=datetime.now()
  326. )
  327. self.assertQuerySetEqual(chris.friends.all(), [])
  328. def test_self_referential_non_symmetrical_clear_first_side(self):
  329. tony = PersonSelfRefM2M.objects.create(name="Tony")
  330. chris = PersonSelfRefM2M.objects.create(name="Chris")
  331. Friendship.objects.create(
  332. first=tony, second=chris, date_friended=datetime.now()
  333. )
  334. chris.friends.clear()
  335. self.assertQuerySetEqual(chris.friends.all(), [])
  336. # Since this isn't a symmetrical relation, Tony's friend link still exists.
  337. self.assertQuerySetEqual(tony.friends.all(), ["Chris"], attrgetter("name"))
  338. def test_self_referential_non_symmetrical_both(self):
  339. tony = PersonSelfRefM2M.objects.create(name="Tony")
  340. chris = PersonSelfRefM2M.objects.create(name="Chris")
  341. Friendship.objects.create(
  342. first=tony, second=chris, date_friended=datetime.now()
  343. )
  344. Friendship.objects.create(
  345. first=chris, second=tony, date_friended=datetime.now()
  346. )
  347. self.assertQuerySetEqual(tony.friends.all(), ["Chris"], attrgetter("name"))
  348. self.assertQuerySetEqual(chris.friends.all(), ["Tony"], attrgetter("name"))
  349. def test_through_fields_self_referential(self):
  350. john = Employee.objects.create(name="john")
  351. peter = Employee.objects.create(name="peter")
  352. mary = Employee.objects.create(name="mary")
  353. harry = Employee.objects.create(name="harry")
  354. Relationship.objects.create(source=john, target=peter, another=None)
  355. Relationship.objects.create(source=john, target=mary, another=None)
  356. Relationship.objects.create(source=john, target=harry, another=peter)
  357. self.assertQuerySetEqual(
  358. john.subordinates.all(), ["peter", "mary", "harry"], attrgetter("name")
  359. )
  360. def test_self_referential_symmetrical(self):
  361. tony = PersonSelfRefM2M.objects.create(name="Tony")
  362. chris = PersonSelfRefM2M.objects.create(name="Chris")
  363. SymmetricalFriendship.objects.create(
  364. first=tony,
  365. second=chris,
  366. date_friended=date.today(),
  367. )
  368. self.assertSequenceEqual(tony.sym_friends.all(), [chris])
  369. # Manually created symmetrical m2m relation doesn't add mirror entry
  370. # automatically.
  371. self.assertSequenceEqual(chris.sym_friends.all(), [])
  372. SymmetricalFriendship.objects.create(
  373. first=chris, second=tony, date_friended=date.today()
  374. )
  375. self.assertSequenceEqual(chris.sym_friends.all(), [tony])
  376. def test_add_on_symmetrical_m2m_with_intermediate_model(self):
  377. tony = PersonSelfRefM2M.objects.create(name="Tony")
  378. chris = PersonSelfRefM2M.objects.create(name="Chris")
  379. date_friended = date(2017, 1, 3)
  380. tony.sym_friends.add(chris, through_defaults={"date_friended": date_friended})
  381. self.assertSequenceEqual(tony.sym_friends.all(), [chris])
  382. self.assertSequenceEqual(chris.sym_friends.all(), [tony])
  383. friendship = tony.symmetricalfriendship_set.get()
  384. self.assertEqual(friendship.date_friended, date_friended)
  385. def test_set_on_symmetrical_m2m_with_intermediate_model(self):
  386. tony = PersonSelfRefM2M.objects.create(name="Tony")
  387. chris = PersonSelfRefM2M.objects.create(name="Chris")
  388. anne = PersonSelfRefM2M.objects.create(name="Anne")
  389. kate = PersonSelfRefM2M.objects.create(name="Kate")
  390. date_friended_add = date(2013, 1, 5)
  391. date_friended_set = date.today()
  392. tony.sym_friends.add(
  393. anne,
  394. chris,
  395. through_defaults={"date_friended": date_friended_add},
  396. )
  397. tony.sym_friends.set(
  398. [anne, kate],
  399. through_defaults={"date_friended": date_friended_set},
  400. )
  401. self.assertSequenceEqual(tony.sym_friends.all(), [anne, kate])
  402. self.assertSequenceEqual(anne.sym_friends.all(), [tony])
  403. self.assertSequenceEqual(kate.sym_friends.all(), [tony])
  404. self.assertEqual(
  405. kate.symmetricalfriendship_set.get().date_friended,
  406. date_friended_set,
  407. )
  408. # Date is preserved.
  409. self.assertEqual(
  410. anne.symmetricalfriendship_set.get().date_friended,
  411. date_friended_add,
  412. )
  413. # Recreate relationship.
  414. tony.sym_friends.set(
  415. [anne],
  416. clear=True,
  417. through_defaults={"date_friended": date_friended_set},
  418. )
  419. self.assertSequenceEqual(tony.sym_friends.all(), [anne])
  420. self.assertSequenceEqual(anne.sym_friends.all(), [tony])
  421. self.assertEqual(
  422. anne.symmetricalfriendship_set.get().date_friended,
  423. date_friended_set,
  424. )
  425. class M2mThroughToFieldsTests(TestCase):
  426. @classmethod
  427. def setUpTestData(cls):
  428. cls.pea = Ingredient.objects.create(iname="pea")
  429. cls.potato = Ingredient.objects.create(iname="potato")
  430. cls.tomato = Ingredient.objects.create(iname="tomato")
  431. cls.curry = Recipe.objects.create(rname="curry")
  432. RecipeIngredient.objects.create(recipe=cls.curry, ingredient=cls.potato)
  433. RecipeIngredient.objects.create(recipe=cls.curry, ingredient=cls.pea)
  434. RecipeIngredient.objects.create(recipe=cls.curry, ingredient=cls.tomato)
  435. def test_retrieval(self):
  436. # Forward retrieval
  437. self.assertSequenceEqual(
  438. self.curry.ingredients.all(), [self.pea, self.potato, self.tomato]
  439. )
  440. # Backward retrieval
  441. self.assertEqual(self.tomato.recipes.get(), self.curry)
  442. def test_choices(self):
  443. field = Recipe._meta.get_field("ingredients")
  444. self.assertEqual(
  445. [choice[0] for choice in field.get_choices(include_blank=False)],
  446. ["pea", "potato", "tomato"],
  447. )
  448. def test_count(self):
  449. self.assertEqual(self.curry.ingredients.count(), 3)
  450. self.assertEqual(self.tomato.recipes.count(), 1)
  451. def test_exists(self):
  452. self.assertTrue(self.curry.ingredients.exists())
  453. self.assertTrue(self.tomato.recipes.exists())