tests.py 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. import warnings
  2. from datetime import datetime
  3. from django.test import TestCase
  4. from .models import Article, IndexErrorArticle, Person
  5. class EarliestOrLatestTests(TestCase):
  6. """Tests for the earliest() and latest() objects methods"""
  7. @classmethod
  8. def setUpClass(cls):
  9. super().setUpClass()
  10. cls._article_get_latest_by = Article._meta.get_latest_by
  11. def tearDown(self):
  12. Article._meta.get_latest_by = self._article_get_latest_by
  13. def test_earliest(self):
  14. # Because no Articles exist yet, earliest() raises ArticleDoesNotExist.
  15. with self.assertRaises(Article.DoesNotExist):
  16. Article.objects.earliest()
  17. a1 = Article.objects.create(
  18. headline="Article 1", pub_date=datetime(2005, 7, 26),
  19. expire_date=datetime(2005, 9, 1)
  20. )
  21. a2 = Article.objects.create(
  22. headline="Article 2", pub_date=datetime(2005, 7, 27),
  23. expire_date=datetime(2005, 7, 28)
  24. )
  25. a3 = Article.objects.create(
  26. headline="Article 3", pub_date=datetime(2005, 7, 28),
  27. expire_date=datetime(2005, 8, 27)
  28. )
  29. a4 = Article.objects.create(
  30. headline="Article 4", pub_date=datetime(2005, 7, 28),
  31. expire_date=datetime(2005, 7, 30)
  32. )
  33. # Get the earliest Article.
  34. self.assertEqual(Article.objects.earliest(), a1)
  35. # Get the earliest Article that matches certain filters.
  36. self.assertEqual(
  37. Article.objects.filter(pub_date__gt=datetime(2005, 7, 26)).earliest(),
  38. a2
  39. )
  40. # Pass a custom field name to earliest() to change the field that's used
  41. # to determine the earliest object.
  42. self.assertEqual(Article.objects.earliest('expire_date'), a2)
  43. self.assertEqual(Article.objects.filter(
  44. pub_date__gt=datetime(2005, 7, 26)).earliest('expire_date'), a2)
  45. # earliest() overrides any other ordering specified on the query.
  46. # Refs #11283.
  47. self.assertEqual(Article.objects.order_by('id').earliest(), a1)
  48. # Error is raised if the user forgot to add a get_latest_by
  49. # in the Model.Meta
  50. Article.objects.model._meta.get_latest_by = None
  51. with self.assertRaisesMessage(
  52. ValueError,
  53. "earliest() and latest() require either fields as positional "
  54. "arguments or 'get_latest_by' in the model's Meta."
  55. ):
  56. Article.objects.earliest()
  57. # Earliest publication date, earliest expire date.
  58. self.assertEqual(
  59. Article.objects.filter(pub_date=datetime(2005, 7, 28)).earliest('pub_date', 'expire_date'),
  60. a4,
  61. )
  62. # Earliest publication date, latest expire date.
  63. self.assertEqual(
  64. Article.objects.filter(pub_date=datetime(2005, 7, 28)).earliest('pub_date', '-expire_date'),
  65. a3,
  66. )
  67. # Meta.get_latest_by may be a tuple.
  68. Article.objects.model._meta.get_latest_by = ('pub_date', 'expire_date')
  69. self.assertEqual(Article.objects.filter(pub_date=datetime(2005, 7, 28)).earliest(), a4)
  70. def test_earliest_fields_and_field_name(self):
  71. msg = 'Cannot use both positional arguments and the field_name keyword argument.'
  72. with self.assertRaisesMessage(ValueError, msg):
  73. Article.objects.earliest('pub_date', field_name='expire_date')
  74. def test_latest(self):
  75. # Because no Articles exist yet, latest() raises ArticleDoesNotExist.
  76. with self.assertRaises(Article.DoesNotExist):
  77. Article.objects.latest()
  78. a1 = Article.objects.create(
  79. headline="Article 1", pub_date=datetime(2005, 7, 26),
  80. expire_date=datetime(2005, 9, 1)
  81. )
  82. a2 = Article.objects.create(
  83. headline="Article 2", pub_date=datetime(2005, 7, 27),
  84. expire_date=datetime(2005, 7, 28)
  85. )
  86. a3 = Article.objects.create(
  87. headline="Article 3", pub_date=datetime(2005, 7, 27),
  88. expire_date=datetime(2005, 8, 27)
  89. )
  90. a4 = Article.objects.create(
  91. headline="Article 4", pub_date=datetime(2005, 7, 28),
  92. expire_date=datetime(2005, 7, 30)
  93. )
  94. # Get the latest Article.
  95. self.assertEqual(Article.objects.latest(), a4)
  96. # Get the latest Article that matches certain filters.
  97. self.assertEqual(
  98. Article.objects.filter(pub_date__lt=datetime(2005, 7, 27)).latest(),
  99. a1
  100. )
  101. # Pass a custom field name to latest() to change the field that's used
  102. # to determine the latest object.
  103. self.assertEqual(Article.objects.latest('expire_date'), a1)
  104. self.assertEqual(
  105. Article.objects.filter(pub_date__gt=datetime(2005, 7, 26)).latest('expire_date'),
  106. a3,
  107. )
  108. # latest() overrides any other ordering specified on the query (#11283).
  109. self.assertEqual(Article.objects.order_by('id').latest(), a4)
  110. # Error is raised if get_latest_by isn't in Model.Meta.
  111. Article.objects.model._meta.get_latest_by = None
  112. with self.assertRaisesMessage(
  113. ValueError,
  114. "earliest() and latest() require either fields as positional "
  115. "arguments or 'get_latest_by' in the model's Meta."
  116. ):
  117. Article.objects.latest()
  118. # Latest publication date, latest expire date.
  119. self.assertEqual(Article.objects.filter(pub_date=datetime(2005, 7, 27)).latest('pub_date', 'expire_date'), a3)
  120. # Latest publication date, earliest expire date.
  121. self.assertEqual(
  122. Article.objects.filter(pub_date=datetime(2005, 7, 27)).latest('pub_date', '-expire_date'),
  123. a2,
  124. )
  125. # Meta.get_latest_by may be a tuple.
  126. Article.objects.model._meta.get_latest_by = ('pub_date', 'expire_date')
  127. self.assertEqual(Article.objects.filter(pub_date=datetime(2005, 7, 27)).latest(), a3)
  128. def test_latest_fields_and_field_name(self):
  129. msg = 'Cannot use both positional arguments and the field_name keyword argument.'
  130. with self.assertRaisesMessage(ValueError, msg):
  131. Article.objects.latest('pub_date', field_name='expire_date')
  132. def test_latest_manual(self):
  133. # You can still use latest() with a model that doesn't have
  134. # "get_latest_by" set -- just pass in the field name manually.
  135. Person.objects.create(name="Ralph", birthday=datetime(1950, 1, 1))
  136. p2 = Person.objects.create(name="Stephanie", birthday=datetime(1960, 2, 3))
  137. msg = (
  138. "earliest() and latest() require either fields as positional arguments "
  139. "or 'get_latest_by' in the model's Meta."
  140. )
  141. with self.assertRaisesMessage(ValueError, msg):
  142. Person.objects.latest()
  143. self.assertEqual(Person.objects.latest("birthday"), p2)
  144. def test_field_name_kwarg_deprecation(self):
  145. Person.objects.create(name='Deprecator', birthday=datetime(1950, 1, 1))
  146. with warnings.catch_warnings(record=True) as warns:
  147. warnings.simplefilter('always')
  148. Person.objects.latest(field_name='birthday')
  149. self.assertEqual(len(warns), 1)
  150. self.assertEqual(
  151. str(warns[0].message),
  152. 'The field_name keyword argument to earliest() and latest() '
  153. 'is deprecated in favor of passing positional arguments.',
  154. )
  155. class TestFirstLast(TestCase):
  156. def test_first(self):
  157. p1 = Person.objects.create(name="Bob", birthday=datetime(1950, 1, 1))
  158. p2 = Person.objects.create(name="Alice", birthday=datetime(1961, 2, 3))
  159. self.assertEqual(Person.objects.first(), p1)
  160. self.assertEqual(Person.objects.order_by('name').first(), p2)
  161. self.assertEqual(Person.objects.filter(birthday__lte=datetime(1955, 1, 1)).first(), p1)
  162. self.assertIsNone(Person.objects.filter(birthday__lte=datetime(1940, 1, 1)).first())
  163. def test_last(self):
  164. p1 = Person.objects.create(name="Alice", birthday=datetime(1950, 1, 1))
  165. p2 = Person.objects.create(name="Bob", birthday=datetime(1960, 2, 3))
  166. # Note: by default PK ordering.
  167. self.assertEqual(Person.objects.last(), p2)
  168. self.assertEqual(Person.objects.order_by('-name').last(), p1)
  169. self.assertEqual(Person.objects.filter(birthday__lte=datetime(1955, 1, 1)).last(), p1)
  170. self.assertIsNone(Person.objects.filter(birthday__lte=datetime(1940, 1, 1)).last())
  171. def test_index_error_not_suppressed(self):
  172. """
  173. #23555 -- Unexpected IndexError exceptions in QuerySet iteration
  174. shouldn't be suppressed.
  175. """
  176. def check():
  177. # We know that we've broken the __iter__ method, so the queryset
  178. # should always raise an exception.
  179. with self.assertRaises(IndexError):
  180. IndexErrorArticle.objects.all()[:10:2]
  181. with self.assertRaises(IndexError):
  182. IndexErrorArticle.objects.all().first()
  183. with self.assertRaises(IndexError):
  184. IndexErrorArticle.objects.all().last()
  185. check()
  186. # And it does not matter if there are any records in the DB.
  187. IndexErrorArticle.objects.create(
  188. headline="Article 1", pub_date=datetime(2005, 7, 26),
  189. expire_date=datetime(2005, 9, 1)
  190. )
  191. check()