tests.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. """
  2. Spanning tests for all the operations that F() expressions can perform.
  3. """
  4. import datetime
  5. from django.conf import settings
  6. from django.db import models, connection
  7. from django.db.models import F
  8. from django.test import TestCase, Approximate, skipUnlessDBFeature
  9. from regressiontests.expressions_regress.models import Number, Experiment
  10. class ExpressionsRegressTests(TestCase):
  11. def setUp(self):
  12. Number(integer=-1).save()
  13. Number(integer=42).save()
  14. Number(integer=1337).save()
  15. self.assertEqual(Number.objects.update(float=F('integer')), 3)
  16. def test_fill_with_value_from_same_object(self):
  17. """
  18. We can fill a value in all objects with an other value of the
  19. same object.
  20. """
  21. self.assertQuerysetEqual(
  22. Number.objects.all(),
  23. [
  24. '<Number: -1, -1.000>',
  25. '<Number: 42, 42.000>',
  26. '<Number: 1337, 1337.000>'
  27. ]
  28. )
  29. def test_increment_value(self):
  30. """
  31. We can increment a value of all objects in a query set.
  32. """
  33. self.assertEqual(
  34. Number.objects.filter(integer__gt=0)
  35. .update(integer=F('integer') + 1),
  36. 2)
  37. self.assertQuerysetEqual(
  38. Number.objects.all(),
  39. [
  40. '<Number: -1, -1.000>',
  41. '<Number: 43, 42.000>',
  42. '<Number: 1338, 1337.000>'
  43. ]
  44. )
  45. def test_filter_not_equals_other_field(self):
  46. """
  47. We can filter for objects, where a value is not equals the value
  48. of an other field.
  49. """
  50. self.assertEqual(
  51. Number.objects.filter(integer__gt=0)
  52. .update(integer=F('integer') + 1),
  53. 2)
  54. self.assertQuerysetEqual(
  55. Number.objects.exclude(float=F('integer')),
  56. [
  57. '<Number: 43, 42.000>',
  58. '<Number: 1338, 1337.000>'
  59. ]
  60. )
  61. def test_complex_expressions(self):
  62. """
  63. Complex expressions of different connection types are possible.
  64. """
  65. n = Number.objects.create(integer=10, float=123.45)
  66. self.assertEqual(Number.objects.filter(pk=n.pk)
  67. .update(float=F('integer') + F('float') * 2),
  68. 1)
  69. self.assertEqual(Number.objects.get(pk=n.pk).integer, 10)
  70. self.assertEqual(Number.objects.get(pk=n.pk).float, Approximate(256.900, places=3))
  71. class ExpressionOperatorTests(TestCase):
  72. def setUp(self):
  73. self.n = Number.objects.create(integer=42, float=15.5)
  74. def test_lefthand_addition(self):
  75. # LH Addition of floats and integers
  76. Number.objects.filter(pk=self.n.pk).update(
  77. integer=F('integer') + 15,
  78. float=F('float') + 42.7
  79. )
  80. self.assertEqual(Number.objects.get(pk=self.n.pk).integer, 57)
  81. self.assertEqual(Number.objects.get(pk=self.n.pk).float, Approximate(58.200, places=3))
  82. def test_lefthand_subtraction(self):
  83. # LH Subtraction of floats and integers
  84. Number.objects.filter(pk=self.n.pk).update(integer=F('integer') - 15,
  85. float=F('float') - 42.7)
  86. self.assertEqual(Number.objects.get(pk=self.n.pk).integer, 27)
  87. self.assertEqual(Number.objects.get(pk=self.n.pk).float, Approximate(-27.200, places=3))
  88. def test_lefthand_multiplication(self):
  89. # Multiplication of floats and integers
  90. Number.objects.filter(pk=self.n.pk).update(integer=F('integer') * 15,
  91. float=F('float') * 42.7)
  92. self.assertEqual(Number.objects.get(pk=self.n.pk).integer, 630)
  93. self.assertEqual(Number.objects.get(pk=self.n.pk).float, Approximate(661.850, places=3))
  94. def test_lefthand_division(self):
  95. # LH Division of floats and integers
  96. Number.objects.filter(pk=self.n.pk).update(integer=F('integer') / 2,
  97. float=F('float') / 42.7)
  98. self.assertEqual(Number.objects.get(pk=self.n.pk).integer, 21)
  99. self.assertEqual(Number.objects.get(pk=self.n.pk).float, Approximate(0.363, places=3))
  100. def test_lefthand_modulo(self):
  101. # LH Modulo arithmetic on integers
  102. Number.objects.filter(pk=self.n.pk).update(integer=F('integer') % 20)
  103. self.assertEqual(Number.objects.get(pk=self.n.pk).integer, 2)
  104. self.assertEqual(Number.objects.get(pk=self.n.pk).float, Approximate(15.500, places=3))
  105. def test_lefthand_bitwise_and(self):
  106. # LH Bitwise ands on integers
  107. Number.objects.filter(pk=self.n.pk).update(integer=F('integer') & 56)
  108. self.assertEqual(Number.objects.get(pk=self.n.pk).integer, 40)
  109. self.assertEqual(Number.objects.get(pk=self.n.pk).float, Approximate(15.500, places=3))
  110. @skipUnlessDBFeature('supports_bitwise_or')
  111. def test_lefthand_bitwise_or(self):
  112. # LH Bitwise or on integers
  113. Number.objects.filter(pk=self.n.pk).update(integer=F('integer') | 48)
  114. self.assertEqual(Number.objects.get(pk=self.n.pk).integer, 58)
  115. self.assertEqual(Number.objects.get(pk=self.n.pk).float, Approximate(15.500, places=3))
  116. def test_right_hand_addition(self):
  117. # Right hand operators
  118. Number.objects.filter(pk=self.n.pk).update(integer=15 + F('integer'),
  119. float=42.7 + F('float'))
  120. # RH Addition of floats and integers
  121. self.assertEqual(Number.objects.get(pk=self.n.pk).integer, 57)
  122. self.assertEqual(Number.objects.get(pk=self.n.pk).float, Approximate(58.200, places=3))
  123. def test_right_hand_subtraction(self):
  124. Number.objects.filter(pk=self.n.pk).update(integer=15 - F('integer'),
  125. float=42.7 - F('float'))
  126. # RH Subtraction of floats and integers
  127. self.assertEqual(Number.objects.get(pk=self.n.pk).integer, -27)
  128. self.assertEqual(Number.objects.get(pk=self.n.pk).float, Approximate(27.200, places=3))
  129. def test_right_hand_multiplication(self):
  130. # RH Multiplication of floats and integers
  131. Number.objects.filter(pk=self.n.pk).update(integer=15 * F('integer'),
  132. float=42.7 * F('float'))
  133. self.assertEqual(Number.objects.get(pk=self.n.pk).integer, 630)
  134. self.assertEqual(Number.objects.get(pk=self.n.pk).float, Approximate(661.850, places=3))
  135. def test_right_hand_division(self):
  136. # RH Division of floats and integers
  137. Number.objects.filter(pk=self.n.pk).update(integer=640 / F('integer'),
  138. float=42.7 / F('float'))
  139. self.assertEqual(Number.objects.get(pk=self.n.pk).integer, 15)
  140. self.assertEqual(Number.objects.get(pk=self.n.pk).float, Approximate(2.755, places=3))
  141. def test_right_hand_modulo(self):
  142. # RH Modulo arithmetic on integers
  143. Number.objects.filter(pk=self.n.pk).update(integer=69 % F('integer'))
  144. self.assertEqual(Number.objects.get(pk=self.n.pk).integer, 27)
  145. self.assertEqual(Number.objects.get(pk=self.n.pk).float, Approximate(15.500, places=3))
  146. def test_right_hand_bitwise_and(self):
  147. # RH Bitwise ands on integers
  148. Number.objects.filter(pk=self.n.pk).update(integer=15 & F('integer'))
  149. self.assertEqual(Number.objects.get(pk=self.n.pk).integer, 10)
  150. self.assertEqual(Number.objects.get(pk=self.n.pk).float, Approximate(15.500, places=3))
  151. @skipUnlessDBFeature('supports_bitwise_or')
  152. def test_right_hand_bitwise_or(self):
  153. # RH Bitwise or on integers
  154. Number.objects.filter(pk=self.n.pk).update(integer=15 | F('integer'))
  155. self.assertEqual(Number.objects.get(pk=self.n.pk).integer, 47)
  156. self.assertEqual(Number.objects.get(pk=self.n.pk).float, Approximate(15.500, places=3))
  157. class FTimeDeltaTests(TestCase):
  158. def setUp(self):
  159. sday = datetime.date(2010, 6, 25)
  160. stime = datetime.datetime(2010, 6, 25, 12, 15, 30, 747000)
  161. midnight = datetime.time(0)
  162. delta0 = datetime.timedelta(0)
  163. delta1 = datetime.timedelta(microseconds=253000)
  164. delta2 = datetime.timedelta(seconds=44)
  165. delta3 = datetime.timedelta(hours=21, minutes=8)
  166. delta4 = datetime.timedelta(days=10)
  167. # Test data is set so that deltas and delays will be
  168. # strictly increasing.
  169. self.deltas = []
  170. self.delays = []
  171. self.days_long = []
  172. # e0: started same day as assigned, zero duration
  173. end = stime+delta0
  174. e0 = Experiment.objects.create(name='e0', assigned=sday, start=stime,
  175. end=end, completed=end.date())
  176. self.deltas.append(delta0)
  177. self.delays.append(e0.start-
  178. datetime.datetime.combine(e0.assigned, midnight))
  179. self.days_long.append(e0.completed-e0.assigned)
  180. # e1: started one day after assigned, tiny duration, data
  181. # set so that end time has no fractional seconds, which
  182. # tests an edge case on sqlite. This Experiment is only
  183. # included in the test data when the DB supports microsecond
  184. # precision.
  185. if connection.features.supports_microsecond_precision:
  186. delay = datetime.timedelta(1)
  187. end = stime + delay + delta1
  188. e1 = Experiment.objects.create(name='e1', assigned=sday,
  189. start=stime+delay, end=end, completed=end.date())
  190. self.deltas.append(delta1)
  191. self.delays.append(e1.start-
  192. datetime.datetime.combine(e1.assigned, midnight))
  193. self.days_long.append(e1.completed-e1.assigned)
  194. # e2: started three days after assigned, small duration
  195. end = stime+delta2
  196. e2 = Experiment.objects.create(name='e2',
  197. assigned=sday-datetime.timedelta(3), start=stime, end=end,
  198. completed=end.date())
  199. self.deltas.append(delta2)
  200. self.delays.append(e2.start-
  201. datetime.datetime.combine(e2.assigned, midnight))
  202. self.days_long.append(e2.completed-e2.assigned)
  203. # e3: started four days after assigned, medium duration
  204. delay = datetime.timedelta(4)
  205. end = stime + delay + delta3
  206. e3 = Experiment.objects.create(name='e3',
  207. assigned=sday, start=stime+delay, end=end, completed=end.date())
  208. self.deltas.append(delta3)
  209. self.delays.append(e3.start-
  210. datetime.datetime.combine(e3.assigned, midnight))
  211. self.days_long.append(e3.completed-e3.assigned)
  212. # e4: started 10 days after assignment, long duration
  213. end = stime + delta4
  214. e4 = Experiment.objects.create(name='e4',
  215. assigned=sday-datetime.timedelta(10), start=stime, end=end,
  216. completed=end.date())
  217. self.deltas.append(delta4)
  218. self.delays.append(e4.start-
  219. datetime.datetime.combine(e4.assigned, midnight))
  220. self.days_long.append(e4.completed-e4.assigned)
  221. self.expnames = [e.name for e in Experiment.objects.all()]
  222. def test_delta_add(self):
  223. for i in range(len(self.deltas)):
  224. delta = self.deltas[i]
  225. test_set = [e.name for e in
  226. Experiment.objects.filter(end__lt=F('start')+delta)]
  227. self.assertEqual(test_set, self.expnames[:i])
  228. test_set = [e.name for e in
  229. Experiment.objects.filter(end__lte=F('start')+delta)]
  230. self.assertEqual(test_set, self.expnames[:i+1])
  231. def test_delta_subtract(self):
  232. for i in range(len(self.deltas)):
  233. delta = self.deltas[i]
  234. test_set = [e.name for e in
  235. Experiment.objects.filter(start__gt=F('end')-delta)]
  236. self.assertEqual(test_set, self.expnames[:i])
  237. test_set = [e.name for e in
  238. Experiment.objects.filter(start__gte=F('end')-delta)]
  239. self.assertEqual(test_set, self.expnames[:i+1])
  240. def test_exclude(self):
  241. for i in range(len(self.deltas)):
  242. delta = self.deltas[i]
  243. test_set = [e.name for e in
  244. Experiment.objects.exclude(end__lt=F('start')+delta)]
  245. self.assertEqual(test_set, self.expnames[i:])
  246. test_set = [e.name for e in
  247. Experiment.objects.exclude(end__lte=F('start')+delta)]
  248. self.assertEqual(test_set, self.expnames[i+1:])
  249. def test_date_comparison(self):
  250. for i in range(len(self.days_long)):
  251. days = self.days_long[i]
  252. test_set = [e.name for e in
  253. Experiment.objects.filter(completed__lt=F('assigned')+days)]
  254. self.assertEqual(test_set, self.expnames[:i])
  255. test_set = [e.name for e in
  256. Experiment.objects.filter(completed__lte=F('assigned')+days)]
  257. self.assertEqual(test_set, self.expnames[:i+1])
  258. @skipUnlessDBFeature("supports_mixed_date_datetime_comparisons")
  259. def test_mixed_comparisons1(self):
  260. for i in range(len(self.delays)):
  261. delay = self.delays[i]
  262. if not connection.features.supports_microsecond_precision:
  263. delay = datetime.timedelta(delay.days, delay.seconds)
  264. test_set = [e.name for e in
  265. Experiment.objects.filter(assigned__gt=F('start')-delay)]
  266. self.assertEqual(test_set, self.expnames[:i])
  267. test_set = [e.name for e in
  268. Experiment.objects.filter(assigned__gte=F('start')-delay)]
  269. self.assertEqual(test_set, self.expnames[:i+1])
  270. def test_mixed_comparisons2(self):
  271. delays = [datetime.timedelta(delay.days) for delay in self.delays]
  272. for i in range(len(delays)):
  273. delay = delays[i]
  274. test_set = [e.name for e in
  275. Experiment.objects.filter(start__lt=F('assigned')+delay)]
  276. self.assertEqual(test_set, self.expnames[:i])
  277. test_set = [e.name for e in
  278. Experiment.objects.filter(start__lte=F('assigned')+delay+
  279. datetime.timedelta(1))]
  280. self.assertEqual(test_set, self.expnames[:i+1])
  281. def test_delta_update(self):
  282. for i in range(len(self.deltas)):
  283. delta = self.deltas[i]
  284. exps = Experiment.objects.all()
  285. expected_durations = [e.duration() for e in exps]
  286. expected_starts = [e.start+delta for e in exps]
  287. expected_ends = [e.end+delta for e in exps]
  288. Experiment.objects.update(start=F('start')+delta, end=F('end')+delta)
  289. exps = Experiment.objects.all()
  290. new_starts = [e.start for e in exps]
  291. new_ends = [e.end for e in exps]
  292. new_durations = [e.duration() for e in exps]
  293. self.assertEqual(expected_starts, new_starts)
  294. self.assertEqual(expected_ends, new_ends)
  295. self.assertEqual(expected_durations, new_durations)
  296. def test_delta_invalid_op_mult(self):
  297. raised = False
  298. try:
  299. r = repr(Experiment.objects.filter(end__lt=F('start')*self.deltas[0]))
  300. except TypeError:
  301. raised = True
  302. self.assertTrue(raised, "TypeError not raised on attempt to multiply datetime by timedelta.")
  303. def test_delta_invalid_op_div(self):
  304. raised = False
  305. try:
  306. r = repr(Experiment.objects.filter(end__lt=F('start')/self.deltas[0]))
  307. except TypeError:
  308. raised = True
  309. self.assertTrue(raised, "TypeError not raised on attempt to divide datetime by timedelta.")
  310. def test_delta_invalid_op_mod(self):
  311. raised = False
  312. try:
  313. r = repr(Experiment.objects.filter(end__lt=F('start')%self.deltas[0]))
  314. except TypeError:
  315. raised = True
  316. self.assertTrue(raised, "TypeError not raised on attempt to modulo divide datetime by timedelta.")
  317. def test_delta_invalid_op_and(self):
  318. raised = False
  319. try:
  320. r = repr(Experiment.objects.filter(end__lt=F('start')&self.deltas[0]))
  321. except TypeError:
  322. raised = True
  323. self.assertTrue(raised, "TypeError not raised on attempt to binary and a datetime with a timedelta.")
  324. def test_delta_invalid_op_or(self):
  325. raised = False
  326. try:
  327. r = repr(Experiment.objects.filter(end__lt=F('start')|self.deltas[0]))
  328. except TypeError:
  329. raised = True
  330. self.assertTrue(raised, "TypeError not raised on attempt to binary or a datetime with a timedelta.")