tests.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. from math import ceil
  2. from django.db import IntegrityError, connection, models
  3. from django.db.models.sql.constants import GET_ITERATOR_CHUNK_SIZE
  4. from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
  5. from .models import (
  6. MR, A, Avatar, Base, Child, HiddenUser, HiddenUserProfile, M, M2MFrom,
  7. M2MTo, MRNull, Parent, R, RChild, S, T, User, create_a, get_default_r,
  8. )
  9. class OnDeleteTests(TestCase):
  10. def setUp(self):
  11. self.DEFAULT = get_default_r()
  12. def test_auto(self):
  13. a = create_a('auto')
  14. a.auto.delete()
  15. self.assertFalse(A.objects.filter(name='auto').exists())
  16. def test_auto_nullable(self):
  17. a = create_a('auto_nullable')
  18. a.auto_nullable.delete()
  19. self.assertFalse(A.objects.filter(name='auto_nullable').exists())
  20. def test_setvalue(self):
  21. a = create_a('setvalue')
  22. a.setvalue.delete()
  23. a = A.objects.get(pk=a.pk)
  24. self.assertEqual(self.DEFAULT, a.setvalue.pk)
  25. def test_setnull(self):
  26. a = create_a('setnull')
  27. a.setnull.delete()
  28. a = A.objects.get(pk=a.pk)
  29. self.assertIsNone(a.setnull)
  30. def test_setdefault(self):
  31. a = create_a('setdefault')
  32. a.setdefault.delete()
  33. a = A.objects.get(pk=a.pk)
  34. self.assertEqual(self.DEFAULT, a.setdefault.pk)
  35. def test_setdefault_none(self):
  36. a = create_a('setdefault_none')
  37. a.setdefault_none.delete()
  38. a = A.objects.get(pk=a.pk)
  39. self.assertIsNone(a.setdefault_none)
  40. def test_cascade(self):
  41. a = create_a('cascade')
  42. a.cascade.delete()
  43. self.assertFalse(A.objects.filter(name='cascade').exists())
  44. def test_cascade_nullable(self):
  45. a = create_a('cascade_nullable')
  46. a.cascade_nullable.delete()
  47. self.assertFalse(A.objects.filter(name='cascade_nullable').exists())
  48. def test_protect(self):
  49. a = create_a('protect')
  50. with self.assertRaises(IntegrityError):
  51. a.protect.delete()
  52. def test_do_nothing(self):
  53. # Testing DO_NOTHING is a bit harder: It would raise IntegrityError for a normal model,
  54. # so we connect to pre_delete and set the fk to a known value.
  55. replacement_r = R.objects.create()
  56. def check_do_nothing(sender, **kwargs):
  57. obj = kwargs['instance']
  58. obj.donothing_set.update(donothing=replacement_r)
  59. models.signals.pre_delete.connect(check_do_nothing)
  60. a = create_a('do_nothing')
  61. a.donothing.delete()
  62. a = A.objects.get(pk=a.pk)
  63. self.assertEqual(replacement_r, a.donothing)
  64. models.signals.pre_delete.disconnect(check_do_nothing)
  65. def test_do_nothing_qscount(self):
  66. """
  67. A models.DO_NOTHING relation doesn't trigger a query.
  68. """
  69. b = Base.objects.create()
  70. with self.assertNumQueries(1):
  71. # RelToBase should not be queried.
  72. b.delete()
  73. self.assertEqual(Base.objects.count(), 0)
  74. def test_inheritance_cascade_up(self):
  75. child = RChild.objects.create()
  76. child.delete()
  77. self.assertFalse(R.objects.filter(pk=child.pk).exists())
  78. def test_inheritance_cascade_down(self):
  79. child = RChild.objects.create()
  80. parent = child.r_ptr
  81. parent.delete()
  82. self.assertFalse(RChild.objects.filter(pk=child.pk).exists())
  83. def test_cascade_from_child(self):
  84. a = create_a('child')
  85. a.child.delete()
  86. self.assertFalse(A.objects.filter(name='child').exists())
  87. self.assertFalse(R.objects.filter(pk=a.child_id).exists())
  88. def test_cascade_from_parent(self):
  89. a = create_a('child')
  90. R.objects.get(pk=a.child_id).delete()
  91. self.assertFalse(A.objects.filter(name='child').exists())
  92. self.assertFalse(RChild.objects.filter(pk=a.child_id).exists())
  93. def test_setnull_from_child(self):
  94. a = create_a('child_setnull')
  95. a.child_setnull.delete()
  96. self.assertFalse(R.objects.filter(pk=a.child_setnull_id).exists())
  97. a = A.objects.get(pk=a.pk)
  98. self.assertIsNone(a.child_setnull)
  99. def test_setnull_from_parent(self):
  100. a = create_a('child_setnull')
  101. R.objects.get(pk=a.child_setnull_id).delete()
  102. self.assertFalse(RChild.objects.filter(pk=a.child_setnull_id).exists())
  103. a = A.objects.get(pk=a.pk)
  104. self.assertIsNone(a.child_setnull)
  105. def test_o2o_setnull(self):
  106. a = create_a('o2o_setnull')
  107. a.o2o_setnull.delete()
  108. a = A.objects.get(pk=a.pk)
  109. self.assertIsNone(a.o2o_setnull)
  110. class DeletionTests(TestCase):
  111. def test_m2m(self):
  112. m = M.objects.create()
  113. r = R.objects.create()
  114. MR.objects.create(m=m, r=r)
  115. r.delete()
  116. self.assertFalse(MR.objects.exists())
  117. r = R.objects.create()
  118. MR.objects.create(m=m, r=r)
  119. m.delete()
  120. self.assertFalse(MR.objects.exists())
  121. m = M.objects.create()
  122. r = R.objects.create()
  123. m.m2m.add(r)
  124. r.delete()
  125. through = M._meta.get_field('m2m').remote_field.through
  126. self.assertFalse(through.objects.exists())
  127. r = R.objects.create()
  128. m.m2m.add(r)
  129. m.delete()
  130. self.assertFalse(through.objects.exists())
  131. m = M.objects.create()
  132. r = R.objects.create()
  133. MRNull.objects.create(m=m, r=r)
  134. r.delete()
  135. self.assertFalse(not MRNull.objects.exists())
  136. self.assertFalse(m.m2m_through_null.exists())
  137. def test_bulk(self):
  138. s = S.objects.create(r=R.objects.create())
  139. for i in range(2 * GET_ITERATOR_CHUNK_SIZE):
  140. T.objects.create(s=s)
  141. # 1 (select related `T` instances)
  142. # + 1 (select related `U` instances)
  143. # + 2 (delete `T` instances in batches)
  144. # + 1 (delete `s`)
  145. self.assertNumQueries(5, s.delete)
  146. self.assertFalse(S.objects.exists())
  147. def test_instance_update(self):
  148. deleted = []
  149. related_setnull_sets = []
  150. def pre_delete(sender, **kwargs):
  151. obj = kwargs['instance']
  152. deleted.append(obj)
  153. if isinstance(obj, R):
  154. related_setnull_sets.append([a.pk for a in obj.setnull_set.all()])
  155. models.signals.pre_delete.connect(pre_delete)
  156. a = create_a('update_setnull')
  157. a.setnull.delete()
  158. a = create_a('update_cascade')
  159. a.cascade.delete()
  160. for obj in deleted:
  161. self.assertIsNone(obj.pk)
  162. for pk_list in related_setnull_sets:
  163. for a in A.objects.filter(id__in=pk_list):
  164. self.assertIsNone(a.setnull)
  165. models.signals.pre_delete.disconnect(pre_delete)
  166. def test_deletion_order(self):
  167. pre_delete_order = []
  168. post_delete_order = []
  169. def log_post_delete(sender, **kwargs):
  170. pre_delete_order.append((sender, kwargs['instance'].pk))
  171. def log_pre_delete(sender, **kwargs):
  172. post_delete_order.append((sender, kwargs['instance'].pk))
  173. models.signals.post_delete.connect(log_post_delete)
  174. models.signals.pre_delete.connect(log_pre_delete)
  175. r = R.objects.create(pk=1)
  176. s1 = S.objects.create(pk=1, r=r)
  177. s2 = S.objects.create(pk=2, r=r)
  178. T.objects.create(pk=1, s=s1)
  179. T.objects.create(pk=2, s=s2)
  180. RChild.objects.create(r_ptr=r)
  181. r.delete()
  182. self.assertEqual(
  183. pre_delete_order, [(T, 2), (T, 1), (RChild, 1), (S, 2), (S, 1), (R, 1)]
  184. )
  185. self.assertEqual(
  186. post_delete_order, [(T, 1), (T, 2), (RChild, 1), (S, 1), (S, 2), (R, 1)]
  187. )
  188. models.signals.post_delete.disconnect(log_post_delete)
  189. models.signals.pre_delete.disconnect(log_pre_delete)
  190. def test_relational_post_delete_signals_happen_before_parent_object(self):
  191. deletions = []
  192. def log_post_delete(instance, **kwargs):
  193. self.assertTrue(R.objects.filter(pk=instance.r_id))
  194. self.assertIs(type(instance), S)
  195. deletions.append(instance.id)
  196. r = R.objects.create(pk=1)
  197. S.objects.create(pk=1, r=r)
  198. models.signals.post_delete.connect(log_post_delete, sender=S)
  199. try:
  200. r.delete()
  201. finally:
  202. models.signals.post_delete.disconnect(log_post_delete)
  203. self.assertEqual(len(deletions), 1)
  204. self.assertEqual(deletions[0], 1)
  205. @skipUnlessDBFeature("can_defer_constraint_checks")
  206. def test_can_defer_constraint_checks(self):
  207. u = User.objects.create(
  208. avatar=Avatar.objects.create()
  209. )
  210. a = Avatar.objects.get(pk=u.avatar_id)
  211. # 1 query to find the users for the avatar.
  212. # 1 query to delete the user
  213. # 1 query to delete the avatar
  214. # The important thing is that when we can defer constraint checks there
  215. # is no need to do an UPDATE on User.avatar to null it out.
  216. # Attach a signal to make sure we will not do fast_deletes.
  217. calls = []
  218. def noop(*args, **kwargs):
  219. calls.append('')
  220. models.signals.post_delete.connect(noop, sender=User)
  221. self.assertNumQueries(3, a.delete)
  222. self.assertFalse(User.objects.exists())
  223. self.assertFalse(Avatar.objects.exists())
  224. self.assertEqual(len(calls), 1)
  225. models.signals.post_delete.disconnect(noop, sender=User)
  226. @skipIfDBFeature("can_defer_constraint_checks")
  227. def test_cannot_defer_constraint_checks(self):
  228. u = User.objects.create(
  229. avatar=Avatar.objects.create()
  230. )
  231. # Attach a signal to make sure we will not do fast_deletes.
  232. calls = []
  233. def noop(*args, **kwargs):
  234. calls.append('')
  235. models.signals.post_delete.connect(noop, sender=User)
  236. a = Avatar.objects.get(pk=u.avatar_id)
  237. # The below doesn't make sense... Why do we need to null out
  238. # user.avatar if we are going to delete the user immediately after it,
  239. # and there are no more cascades.
  240. # 1 query to find the users for the avatar.
  241. # 1 query to delete the user
  242. # 1 query to null out user.avatar, because we can't defer the constraint
  243. # 1 query to delete the avatar
  244. self.assertNumQueries(4, a.delete)
  245. self.assertFalse(User.objects.exists())
  246. self.assertFalse(Avatar.objects.exists())
  247. self.assertEqual(len(calls), 1)
  248. models.signals.post_delete.disconnect(noop, sender=User)
  249. def test_hidden_related(self):
  250. r = R.objects.create()
  251. h = HiddenUser.objects.create(r=r)
  252. HiddenUserProfile.objects.create(user=h)
  253. r.delete()
  254. self.assertEqual(HiddenUserProfile.objects.count(), 0)
  255. def test_large_delete(self):
  256. TEST_SIZE = 2000
  257. objs = [Avatar() for i in range(0, TEST_SIZE)]
  258. Avatar.objects.bulk_create(objs)
  259. # Calculate the number of queries needed.
  260. batch_size = connection.ops.bulk_batch_size(['pk'], objs)
  261. # The related fetches are done in batches.
  262. batches = int(ceil(float(len(objs)) / batch_size))
  263. # One query for Avatar.objects.all() and then one related fast delete for
  264. # each batch.
  265. fetches_to_mem = 1 + batches
  266. # The Avatar objects are going to be deleted in batches of GET_ITERATOR_CHUNK_SIZE
  267. queries = fetches_to_mem + TEST_SIZE // GET_ITERATOR_CHUNK_SIZE
  268. self.assertNumQueries(queries, Avatar.objects.all().delete)
  269. self.assertFalse(Avatar.objects.exists())
  270. def test_large_delete_related(self):
  271. TEST_SIZE = 2000
  272. s = S.objects.create(r=R.objects.create())
  273. for i in range(TEST_SIZE):
  274. T.objects.create(s=s)
  275. batch_size = max(connection.ops.bulk_batch_size(['pk'], range(TEST_SIZE)), 1)
  276. # TEST_SIZE // batch_size (select related `T` instances)
  277. # + 1 (select related `U` instances)
  278. # + TEST_SIZE // GET_ITERATOR_CHUNK_SIZE (delete `T` instances in batches)
  279. # + 1 (delete `s`)
  280. expected_num_queries = (ceil(TEST_SIZE // batch_size) +
  281. ceil(TEST_SIZE // GET_ITERATOR_CHUNK_SIZE) + 2)
  282. self.assertNumQueries(expected_num_queries, s.delete)
  283. self.assertFalse(S.objects.exists())
  284. self.assertFalse(T.objects.exists())
  285. def test_delete_with_keeping_parents(self):
  286. child = RChild.objects.create()
  287. parent_id = child.r_ptr_id
  288. child.delete(keep_parents=True)
  289. self.assertFalse(RChild.objects.filter(id=child.id).exists())
  290. self.assertTrue(R.objects.filter(id=parent_id).exists())
  291. def test_delete_with_keeping_parents_relationships(self):
  292. child = RChild.objects.create()
  293. parent_id = child.r_ptr_id
  294. parent_referent_id = S.objects.create(r=child.r_ptr).pk
  295. child.delete(keep_parents=True)
  296. self.assertFalse(RChild.objects.filter(id=child.id).exists())
  297. self.assertTrue(R.objects.filter(id=parent_id).exists())
  298. self.assertTrue(S.objects.filter(pk=parent_referent_id).exists())
  299. def test_queryset_delete_returns_num_rows(self):
  300. """
  301. QuerySet.delete() should return the number of deleted rows and a
  302. dictionary with the number of deletions for each object type.
  303. """
  304. Avatar.objects.bulk_create([Avatar(desc='a'), Avatar(desc='b'), Avatar(desc='c')])
  305. avatars_count = Avatar.objects.count()
  306. deleted, rows_count = Avatar.objects.all().delete()
  307. self.assertEqual(deleted, avatars_count)
  308. # more complex example with multiple object types
  309. r = R.objects.create()
  310. h1 = HiddenUser.objects.create(r=r)
  311. HiddenUser.objects.create(r=r)
  312. HiddenUserProfile.objects.create(user=h1)
  313. existed_objs = {
  314. R._meta.label: R.objects.count(),
  315. HiddenUser._meta.label: HiddenUser.objects.count(),
  316. A._meta.label: A.objects.count(),
  317. MR._meta.label: MR.objects.count(),
  318. HiddenUserProfile._meta.label: HiddenUserProfile.objects.count(),
  319. }
  320. deleted, deleted_objs = R.objects.all().delete()
  321. for k, v in existed_objs.items():
  322. self.assertEqual(deleted_objs[k], v)
  323. def test_model_delete_returns_num_rows(self):
  324. """
  325. Model.delete() should return the number of deleted rows and a
  326. dictionary with the number of deletions for each object type.
  327. """
  328. r = R.objects.create()
  329. h1 = HiddenUser.objects.create(r=r)
  330. h2 = HiddenUser.objects.create(r=r)
  331. HiddenUser.objects.create(r=r)
  332. HiddenUserProfile.objects.create(user=h1)
  333. HiddenUserProfile.objects.create(user=h2)
  334. m1 = M.objects.create()
  335. m2 = M.objects.create()
  336. MR.objects.create(r=r, m=m1)
  337. r.m_set.add(m1)
  338. r.m_set.add(m2)
  339. r.save()
  340. existed_objs = {
  341. R._meta.label: R.objects.count(),
  342. HiddenUser._meta.label: HiddenUser.objects.count(),
  343. A._meta.label: A.objects.count(),
  344. MR._meta.label: MR.objects.count(),
  345. HiddenUserProfile._meta.label: HiddenUserProfile.objects.count(),
  346. M.m2m.through._meta.label: M.m2m.through.objects.count(),
  347. }
  348. deleted, deleted_objs = r.delete()
  349. self.assertEqual(deleted, sum(existed_objs.values()))
  350. for k, v in existed_objs.items():
  351. self.assertEqual(deleted_objs[k], v)
  352. def test_proxied_model_duplicate_queries(self):
  353. """
  354. #25685 - Deleting instances of a model with existing proxy
  355. classes should not issue multiple queries during cascade
  356. deletion of referring models.
  357. """
  358. avatar = Avatar.objects.create()
  359. # One query for the Avatar table and a second for the User one.
  360. with self.assertNumQueries(2):
  361. avatar.delete()
  362. class FastDeleteTests(TestCase):
  363. def test_fast_delete_fk(self):
  364. u = User.objects.create(
  365. avatar=Avatar.objects.create()
  366. )
  367. a = Avatar.objects.get(pk=u.avatar_id)
  368. # 1 query to fast-delete the user
  369. # 1 query to delete the avatar
  370. self.assertNumQueries(2, a.delete)
  371. self.assertFalse(User.objects.exists())
  372. self.assertFalse(Avatar.objects.exists())
  373. def test_fast_delete_m2m(self):
  374. t = M2MTo.objects.create()
  375. f = M2MFrom.objects.create()
  376. f.m2m.add(t)
  377. # 1 to delete f, 1 to fast-delete m2m for f
  378. self.assertNumQueries(2, f.delete)
  379. def test_fast_delete_revm2m(self):
  380. t = M2MTo.objects.create()
  381. f = M2MFrom.objects.create()
  382. f.m2m.add(t)
  383. # 1 to delete t, 1 to fast-delete t's m_set
  384. self.assertNumQueries(2, f.delete)
  385. def test_fast_delete_qs(self):
  386. u1 = User.objects.create()
  387. u2 = User.objects.create()
  388. self.assertNumQueries(1, User.objects.filter(pk=u1.pk).delete)
  389. self.assertEqual(User.objects.count(), 1)
  390. self.assertTrue(User.objects.filter(pk=u2.pk).exists())
  391. def test_fast_delete_joined_qs(self):
  392. a = Avatar.objects.create(desc='a')
  393. User.objects.create(avatar=a)
  394. u2 = User.objects.create()
  395. expected_queries = 1 if connection.features.update_can_self_select else 2
  396. self.assertNumQueries(expected_queries,
  397. User.objects.filter(avatar__desc='a').delete)
  398. self.assertEqual(User.objects.count(), 1)
  399. self.assertTrue(User.objects.filter(pk=u2.pk).exists())
  400. def test_fast_delete_inheritance(self):
  401. c = Child.objects.create()
  402. p = Parent.objects.create()
  403. # 1 for self, 1 for parent
  404. self.assertNumQueries(2, c.delete)
  405. self.assertFalse(Child.objects.exists())
  406. self.assertEqual(Parent.objects.count(), 1)
  407. self.assertEqual(Parent.objects.filter(pk=p.pk).count(), 1)
  408. # 1 for self delete, 1 for fast delete of empty "child" qs.
  409. self.assertNumQueries(2, p.delete)
  410. self.assertFalse(Parent.objects.exists())
  411. # 1 for self delete, 1 for fast delete of empty "child" qs.
  412. c = Child.objects.create()
  413. p = c.parent_ptr
  414. self.assertNumQueries(2, p.delete)
  415. self.assertFalse(Parent.objects.exists())
  416. self.assertFalse(Child.objects.exists())
  417. def test_fast_delete_large_batch(self):
  418. User.objects.bulk_create(User() for i in range(0, 2000))
  419. # No problems here - we aren't going to cascade, so we will fast
  420. # delete the objects in a single query.
  421. self.assertNumQueries(1, User.objects.all().delete)
  422. a = Avatar.objects.create(desc='a')
  423. User.objects.bulk_create(User(avatar=a) for i in range(0, 2000))
  424. # We don't hit parameter amount limits for a, so just one query for
  425. # that + fast delete of the related objs.
  426. self.assertNumQueries(2, a.delete)
  427. self.assertEqual(User.objects.count(), 0)
  428. def test_fast_delete_empty_no_update_can_self_select(self):
  429. """
  430. #25932 - Fast deleting on backends that don't have the
  431. `no_update_can_self_select` feature should work even if the specified
  432. filter doesn't match any row.
  433. """
  434. with self.assertNumQueries(1):
  435. self.assertEqual(
  436. User.objects.filter(avatar__desc='missing').delete(),
  437. (0, {'delete.User': 0})
  438. )