test_dates.py 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792
  1. import datetime
  2. from unittest import mock
  3. from django.core.exceptions import ImproperlyConfigured
  4. from django.test import TestCase, override_settings, skipUnlessDBFeature
  5. from django.test.utils import requires_tz_support
  6. from django.utils import timezone
  7. from .models import Artist, Author, Book, BookSigning, Page
  8. def _make_books(n, base_date):
  9. for i in range(n):
  10. Book.objects.create(
  11. name='Book %d' % i,
  12. slug='book-%d' % i,
  13. pages=100 + i,
  14. pubdate=base_date - datetime.timedelta(days=i))
  15. class TestDataMixin:
  16. @classmethod
  17. def setUpTestData(cls):
  18. cls.artist1 = Artist.objects.create(name='Rene Magritte')
  19. cls.author1 = Author.objects.create(name='Roberto Bolaño', slug='roberto-bolano')
  20. cls.author2 = Author.objects.create(name='Scott Rosenberg', slug='scott-rosenberg')
  21. cls.book1 = Book.objects.create(name='2066', slug='2066', pages=800, pubdate=datetime.date(2008, 10, 1))
  22. cls.book1.authors.add(cls.author1)
  23. cls.book2 = Book.objects.create(
  24. name='Dreaming in Code', slug='dreaming-in-code', pages=300, pubdate=datetime.date(2006, 5, 1)
  25. )
  26. cls.page1 = Page.objects.create(
  27. content='I was once bitten by a moose.', template='generic_views/page_template.html'
  28. )
  29. @override_settings(ROOT_URLCONF='generic_views.urls')
  30. class ArchiveIndexViewTests(TestDataMixin, TestCase):
  31. def test_archive_view(self):
  32. res = self.client.get('/dates/books/')
  33. self.assertEqual(res.status_code, 200)
  34. self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'year', 'DESC')))
  35. self.assertEqual(list(res.context['latest']), list(Book.objects.all()))
  36. self.assertTemplateUsed(res, 'generic_views/book_archive.html')
  37. def test_archive_view_context_object_name(self):
  38. res = self.client.get('/dates/books/context_object_name/')
  39. self.assertEqual(res.status_code, 200)
  40. self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'year', 'DESC')))
  41. self.assertEqual(list(res.context['thingies']), list(Book.objects.all()))
  42. self.assertNotIn('latest', res.context)
  43. self.assertTemplateUsed(res, 'generic_views/book_archive.html')
  44. def test_empty_archive_view(self):
  45. Book.objects.all().delete()
  46. res = self.client.get('/dates/books/')
  47. self.assertEqual(res.status_code, 404)
  48. def test_allow_empty_archive_view(self):
  49. Book.objects.all().delete()
  50. res = self.client.get('/dates/books/allow_empty/')
  51. self.assertEqual(res.status_code, 200)
  52. self.assertEqual(list(res.context['date_list']), [])
  53. self.assertTemplateUsed(res, 'generic_views/book_archive.html')
  54. def test_archive_view_template(self):
  55. res = self.client.get('/dates/books/template_name/')
  56. self.assertEqual(res.status_code, 200)
  57. self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'year', 'DESC')))
  58. self.assertEqual(list(res.context['latest']), list(Book.objects.all()))
  59. self.assertTemplateUsed(res, 'generic_views/list.html')
  60. def test_archive_view_template_suffix(self):
  61. res = self.client.get('/dates/books/template_name_suffix/')
  62. self.assertEqual(res.status_code, 200)
  63. self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'year', 'DESC')))
  64. self.assertEqual(list(res.context['latest']), list(Book.objects.all()))
  65. self.assertTemplateUsed(res, 'generic_views/book_detail.html')
  66. def test_archive_view_invalid(self):
  67. msg = (
  68. 'BookArchive is missing a QuerySet. Define BookArchive.model, '
  69. 'BookArchive.queryset, or override BookArchive.get_queryset().'
  70. )
  71. with self.assertRaisesMessage(ImproperlyConfigured, msg):
  72. self.client.get('/dates/books/invalid/')
  73. def test_archive_view_by_month(self):
  74. res = self.client.get('/dates/books/by_month/')
  75. self.assertEqual(res.status_code, 200)
  76. self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'month', 'DESC')))
  77. def test_paginated_archive_view(self):
  78. _make_books(20, base_date=datetime.date.today())
  79. res = self.client.get('/dates/books/paginated/')
  80. self.assertEqual(res.status_code, 200)
  81. self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'year', 'DESC')))
  82. self.assertEqual(list(res.context['latest']), list(Book.objects.all()[0:10]))
  83. self.assertTemplateUsed(res, 'generic_views/book_archive.html')
  84. res = self.client.get('/dates/books/paginated/?page=2')
  85. self.assertEqual(res.status_code, 200)
  86. self.assertEqual(res.context['page_obj'].number, 2)
  87. self.assertEqual(list(res.context['latest']), list(Book.objects.all()[10:20]))
  88. def test_paginated_archive_view_does_not_load_entire_table(self):
  89. # Regression test for #18087
  90. _make_books(20, base_date=datetime.date.today())
  91. # 1 query for years list + 1 query for books
  92. with self.assertNumQueries(2):
  93. self.client.get('/dates/books/')
  94. # same as above + 1 query to test if books exist + 1 query to count them
  95. with self.assertNumQueries(4):
  96. self.client.get('/dates/books/paginated/')
  97. def test_no_duplicate_query(self):
  98. # Regression test for #18354
  99. with self.assertNumQueries(2):
  100. self.client.get('/dates/books/reverse/')
  101. def test_datetime_archive_view(self):
  102. BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0))
  103. res = self.client.get('/dates/booksignings/')
  104. self.assertEqual(res.status_code, 200)
  105. @requires_tz_support
  106. @skipUnlessDBFeature('has_zoneinfo_database')
  107. @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi')
  108. def test_aware_datetime_archive_view(self):
  109. BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc))
  110. res = self.client.get('/dates/booksignings/')
  111. self.assertEqual(res.status_code, 200)
  112. def test_date_list_order(self):
  113. """date_list should be sorted descending in index"""
  114. _make_books(5, base_date=datetime.date(2011, 12, 25))
  115. res = self.client.get('/dates/books/')
  116. self.assertEqual(res.status_code, 200)
  117. self.assertEqual(list(res.context['date_list']), list(reversed(sorted(res.context['date_list']))))
  118. def test_archive_view_custom_sorting(self):
  119. Book.objects.create(name="Zebras for Dummies", pages=600, pubdate=datetime.date(2007, 5, 1))
  120. res = self.client.get('/dates/books/sortedbyname/')
  121. self.assertEqual(res.status_code, 200)
  122. self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'year', 'DESC')))
  123. self.assertEqual(list(res.context['latest']), list(Book.objects.order_by('name').all()))
  124. self.assertTemplateUsed(res, 'generic_views/book_archive.html')
  125. def test_archive_view_custom_sorting_dec(self):
  126. Book.objects.create(name="Zebras for Dummies", pages=600, pubdate=datetime.date(2007, 5, 1))
  127. res = self.client.get('/dates/books/sortedbynamedec/')
  128. self.assertEqual(res.status_code, 200)
  129. self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'year', 'DESC')))
  130. self.assertEqual(list(res.context['latest']), list(Book.objects.order_by('-name').all()))
  131. self.assertTemplateUsed(res, 'generic_views/book_archive.html')
  132. def test_archive_view_without_date_field(self):
  133. msg = 'BookArchiveWithoutDateField.date_field is required.'
  134. with self.assertRaisesMessage(ImproperlyConfigured, msg):
  135. self.client.get('/dates/books/without_date_field/')
  136. @override_settings(ROOT_URLCONF='generic_views.urls')
  137. class YearArchiveViewTests(TestDataMixin, TestCase):
  138. def test_year_view(self):
  139. res = self.client.get('/dates/books/2008/')
  140. self.assertEqual(res.status_code, 200)
  141. self.assertEqual(list(res.context['date_list']), [datetime.date(2008, 10, 1)])
  142. self.assertEqual(res.context['year'], datetime.date(2008, 1, 1))
  143. self.assertTemplateUsed(res, 'generic_views/book_archive_year.html')
  144. # Since allow_empty=False, next/prev years must be valid (#7164)
  145. self.assertIsNone(res.context['next_year'])
  146. self.assertEqual(res.context['previous_year'], datetime.date(2006, 1, 1))
  147. def test_year_view_make_object_list(self):
  148. res = self.client.get('/dates/books/2006/make_object_list/')
  149. self.assertEqual(res.status_code, 200)
  150. self.assertEqual(list(res.context['date_list']), [datetime.date(2006, 5, 1)])
  151. self.assertEqual(list(res.context['book_list']), list(Book.objects.filter(pubdate__year=2006)))
  152. self.assertEqual(list(res.context['object_list']), list(Book.objects.filter(pubdate__year=2006)))
  153. self.assertTemplateUsed(res, 'generic_views/book_archive_year.html')
  154. def test_year_view_empty(self):
  155. res = self.client.get('/dates/books/1999/')
  156. self.assertEqual(res.status_code, 404)
  157. res = self.client.get('/dates/books/1999/allow_empty/')
  158. self.assertEqual(res.status_code, 200)
  159. self.assertEqual(list(res.context['date_list']), [])
  160. self.assertEqual(list(res.context['book_list']), [])
  161. # Since allow_empty=True, next/prev are allowed to be empty years (#7164)
  162. self.assertEqual(res.context['next_year'], datetime.date(2000, 1, 1))
  163. self.assertEqual(res.context['previous_year'], datetime.date(1998, 1, 1))
  164. def test_year_view_allow_future(self):
  165. # Create a new book in the future
  166. year = datetime.date.today().year + 1
  167. Book.objects.create(name="The New New Testement", pages=600, pubdate=datetime.date(year, 1, 1))
  168. res = self.client.get('/dates/books/%s/' % year)
  169. self.assertEqual(res.status_code, 404)
  170. res = self.client.get('/dates/books/%s/allow_empty/' % year)
  171. self.assertEqual(res.status_code, 200)
  172. self.assertEqual(list(res.context['book_list']), [])
  173. res = self.client.get('/dates/books/%s/allow_future/' % year)
  174. self.assertEqual(res.status_code, 200)
  175. self.assertEqual(list(res.context['date_list']), [datetime.date(year, 1, 1)])
  176. def test_year_view_paginated(self):
  177. res = self.client.get('/dates/books/2006/paginated/')
  178. self.assertEqual(res.status_code, 200)
  179. self.assertEqual(list(res.context['book_list']), list(Book.objects.filter(pubdate__year=2006)))
  180. self.assertEqual(list(res.context['object_list']), list(Book.objects.filter(pubdate__year=2006)))
  181. self.assertTemplateUsed(res, 'generic_views/book_archive_year.html')
  182. def test_year_view_custom_sort_order(self):
  183. # Zebras comes after Dreaming by name, but before on '-pubdate' which is the default sorting
  184. Book.objects.create(name="Zebras for Dummies", pages=600, pubdate=datetime.date(2006, 9, 1))
  185. res = self.client.get('/dates/books/2006/sortedbyname/')
  186. self.assertEqual(res.status_code, 200)
  187. self.assertEqual(list(res.context['date_list']), [datetime.date(2006, 5, 1), datetime.date(2006, 9, 1)])
  188. self.assertEqual(
  189. list(res.context['book_list']),
  190. list(Book.objects.filter(pubdate__year=2006).order_by('name'))
  191. )
  192. self.assertEqual(
  193. list(res.context['object_list']),
  194. list(Book.objects.filter(pubdate__year=2006).order_by('name'))
  195. )
  196. self.assertTemplateUsed(res, 'generic_views/book_archive_year.html')
  197. def test_year_view_two_custom_sort_orders(self):
  198. Book.objects.create(name="Zebras for Dummies", pages=300, pubdate=datetime.date(2006, 9, 1))
  199. Book.objects.create(name="Hunting Hippos", pages=400, pubdate=datetime.date(2006, 3, 1))
  200. res = self.client.get('/dates/books/2006/sortedbypageandnamedec/')
  201. self.assertEqual(res.status_code, 200)
  202. self.assertEqual(
  203. list(res.context['date_list']),
  204. [datetime.date(2006, 3, 1), datetime.date(2006, 5, 1), datetime.date(2006, 9, 1)]
  205. )
  206. self.assertEqual(
  207. list(res.context['book_list']),
  208. list(Book.objects.filter(pubdate__year=2006).order_by('pages', '-name'))
  209. )
  210. self.assertEqual(
  211. list(res.context['object_list']),
  212. list(Book.objects.filter(pubdate__year=2006).order_by('pages', '-name'))
  213. )
  214. self.assertTemplateUsed(res, 'generic_views/book_archive_year.html')
  215. def test_year_view_invalid_pattern(self):
  216. res = self.client.get('/dates/books/no_year/')
  217. self.assertEqual(res.status_code, 404)
  218. def test_no_duplicate_query(self):
  219. # Regression test for #18354
  220. with self.assertNumQueries(4):
  221. self.client.get('/dates/books/2008/reverse/')
  222. def test_datetime_year_view(self):
  223. BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0))
  224. res = self.client.get('/dates/booksignings/2008/')
  225. self.assertEqual(res.status_code, 200)
  226. @skipUnlessDBFeature('has_zoneinfo_database')
  227. @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi')
  228. def test_aware_datetime_year_view(self):
  229. BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc))
  230. res = self.client.get('/dates/booksignings/2008/')
  231. self.assertEqual(res.status_code, 200)
  232. def test_date_list_order(self):
  233. """date_list should be sorted ascending in year view"""
  234. _make_books(10, base_date=datetime.date(2011, 12, 25))
  235. res = self.client.get('/dates/books/2011/')
  236. self.assertEqual(list(res.context['date_list']), list(sorted(res.context['date_list'])))
  237. @mock.patch('django.views.generic.list.MultipleObjectMixin.get_context_data')
  238. def test_get_context_data_receives_extra_context(self, mock):
  239. """
  240. MultipleObjectMixin.get_context_data() receives the context set by
  241. BaseYearArchiveView.get_dated_items(). This behavior is implemented in
  242. BaseDateListView.get().
  243. """
  244. BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0))
  245. with self.assertRaisesMessage(TypeError, 'context must be a dict rather than MagicMock.'):
  246. self.client.get('/dates/booksignings/2008/')
  247. args, kwargs = mock.call_args
  248. # These are context values from get_dated_items().
  249. self.assertEqual(kwargs['year'], datetime.date(2008, 1, 1))
  250. self.assertIsNone(kwargs['previous_year'])
  251. self.assertIsNone(kwargs['next_year'])
  252. def test_get_dated_items_not_implemented(self):
  253. msg = 'A DateView must provide an implementation of get_dated_items()'
  254. with self.assertRaisesMessage(NotImplementedError, msg):
  255. self.client.get('/BaseDateListViewTest/')
  256. @override_settings(ROOT_URLCONF='generic_views.urls')
  257. class MonthArchiveViewTests(TestDataMixin, TestCase):
  258. def test_month_view(self):
  259. res = self.client.get('/dates/books/2008/oct/')
  260. self.assertEqual(res.status_code, 200)
  261. self.assertTemplateUsed(res, 'generic_views/book_archive_month.html')
  262. self.assertEqual(list(res.context['date_list']), [datetime.date(2008, 10, 1)])
  263. self.assertEqual(list(res.context['book_list']),
  264. list(Book.objects.filter(pubdate=datetime.date(2008, 10, 1))))
  265. self.assertEqual(res.context['month'], datetime.date(2008, 10, 1))
  266. # Since allow_empty=False, next/prev months must be valid (#7164)
  267. self.assertIsNone(res.context['next_month'])
  268. self.assertEqual(res.context['previous_month'], datetime.date(2006, 5, 1))
  269. def test_month_view_allow_empty(self):
  270. # allow_empty = False, empty month
  271. res = self.client.get('/dates/books/2000/jan/')
  272. self.assertEqual(res.status_code, 404)
  273. # allow_empty = True, empty month
  274. res = self.client.get('/dates/books/2000/jan/allow_empty/')
  275. self.assertEqual(res.status_code, 200)
  276. self.assertEqual(list(res.context['date_list']), [])
  277. self.assertEqual(list(res.context['book_list']), [])
  278. self.assertEqual(res.context['month'], datetime.date(2000, 1, 1))
  279. # Since allow_empty=True, next/prev are allowed to be empty months (#7164)
  280. self.assertEqual(res.context['next_month'], datetime.date(2000, 2, 1))
  281. self.assertEqual(res.context['previous_month'], datetime.date(1999, 12, 1))
  282. # allow_empty but not allow_future: next_month should be empty (#7164)
  283. url = datetime.date.today().strftime('/dates/books/%Y/%b/allow_empty/').lower()
  284. res = self.client.get(url)
  285. self.assertEqual(res.status_code, 200)
  286. self.assertIsNone(res.context['next_month'])
  287. def test_month_view_allow_future(self):
  288. future = (datetime.date.today() + datetime.timedelta(days=60)).replace(day=1)
  289. urlbit = future.strftime('%Y/%b').lower()
  290. b = Book.objects.create(name="The New New Testement", pages=600, pubdate=future)
  291. # allow_future = False, future month
  292. res = self.client.get('/dates/books/%s/' % urlbit)
  293. self.assertEqual(res.status_code, 404)
  294. # allow_future = True, valid future month
  295. res = self.client.get('/dates/books/%s/allow_future/' % urlbit)
  296. self.assertEqual(res.status_code, 200)
  297. self.assertEqual(res.context['date_list'][0], b.pubdate)
  298. self.assertEqual(list(res.context['book_list']), [b])
  299. self.assertEqual(res.context['month'], future)
  300. # Since allow_future = True but not allow_empty, next/prev are not
  301. # allowed to be empty months (#7164)
  302. self.assertIsNone(res.context['next_month'])
  303. self.assertEqual(res.context['previous_month'], datetime.date(2008, 10, 1))
  304. # allow_future, but not allow_empty, with a current month. So next
  305. # should be in the future (yup, #7164, again)
  306. res = self.client.get('/dates/books/2008/oct/allow_future/')
  307. self.assertEqual(res.status_code, 200)
  308. self.assertEqual(res.context['next_month'], future)
  309. self.assertEqual(res.context['previous_month'], datetime.date(2006, 5, 1))
  310. def test_month_view_paginated(self):
  311. res = self.client.get('/dates/books/2008/oct/paginated/')
  312. self.assertEqual(res.status_code, 200)
  313. self.assertEqual(
  314. list(res.context['book_list']),
  315. list(Book.objects.filter(pubdate__year=2008, pubdate__month=10))
  316. )
  317. self.assertEqual(
  318. list(res.context['object_list']),
  319. list(Book.objects.filter(pubdate__year=2008, pubdate__month=10))
  320. )
  321. self.assertTemplateUsed(res, 'generic_views/book_archive_month.html')
  322. def test_custom_month_format(self):
  323. res = self.client.get('/dates/books/2008/10/')
  324. self.assertEqual(res.status_code, 200)
  325. def test_month_view_invalid_pattern(self):
  326. res = self.client.get('/dates/books/2007/no_month/')
  327. self.assertEqual(res.status_code, 404)
  328. def test_previous_month_without_content(self):
  329. "Content can exist on any day of the previous month. Refs #14711"
  330. self.pubdate_list = [
  331. datetime.date(2010, month, day)
  332. for month, day in ((9, 1), (10, 2), (11, 3))
  333. ]
  334. for pubdate in self.pubdate_list:
  335. name = str(pubdate)
  336. Book.objects.create(name=name, slug=name, pages=100, pubdate=pubdate)
  337. res = self.client.get('/dates/books/2010/nov/allow_empty/')
  338. self.assertEqual(res.status_code, 200)
  339. self.assertEqual(res.context['previous_month'], datetime.date(2010, 10, 1))
  340. # The following test demonstrates the bug
  341. res = self.client.get('/dates/books/2010/nov/')
  342. self.assertEqual(res.status_code, 200)
  343. self.assertEqual(res.context['previous_month'], datetime.date(2010, 10, 1))
  344. # The bug does not occur here because a Book with pubdate of Sep 1 exists
  345. res = self.client.get('/dates/books/2010/oct/')
  346. self.assertEqual(res.status_code, 200)
  347. self.assertEqual(res.context['previous_month'], datetime.date(2010, 9, 1))
  348. def test_datetime_month_view(self):
  349. BookSigning.objects.create(event_date=datetime.datetime(2008, 2, 1, 12, 0))
  350. BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0))
  351. BookSigning.objects.create(event_date=datetime.datetime(2008, 6, 3, 12, 0))
  352. res = self.client.get('/dates/booksignings/2008/apr/')
  353. self.assertEqual(res.status_code, 200)
  354. def test_month_view_get_month_from_request(self):
  355. oct1 = datetime.date(2008, 10, 1)
  356. res = self.client.get('/dates/books/without_month/2008/?month=oct')
  357. self.assertEqual(res.status_code, 200)
  358. self.assertTemplateUsed(res, 'generic_views/book_archive_month.html')
  359. self.assertEqual(list(res.context['date_list']), [oct1])
  360. self.assertEqual(list(res.context['book_list']), list(Book.objects.filter(pubdate=oct1)))
  361. self.assertEqual(res.context['month'], oct1)
  362. def test_month_view_without_month_in_url(self):
  363. res = self.client.get('/dates/books/without_month/2008/')
  364. self.assertEqual(res.status_code, 404)
  365. self.assertEqual(res.context['exception'], 'No month specified')
  366. @skipUnlessDBFeature('has_zoneinfo_database')
  367. @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi')
  368. def test_aware_datetime_month_view(self):
  369. BookSigning.objects.create(event_date=datetime.datetime(2008, 2, 1, 12, 0, tzinfo=timezone.utc))
  370. BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc))
  371. BookSigning.objects.create(event_date=datetime.datetime(2008, 6, 3, 12, 0, tzinfo=timezone.utc))
  372. res = self.client.get('/dates/booksignings/2008/apr/')
  373. self.assertEqual(res.status_code, 200)
  374. def test_date_list_order(self):
  375. """date_list should be sorted ascending in month view"""
  376. _make_books(10, base_date=datetime.date(2011, 12, 25))
  377. res = self.client.get('/dates/books/2011/dec/')
  378. self.assertEqual(list(res.context['date_list']), list(sorted(res.context['date_list'])))
  379. @override_settings(ROOT_URLCONF='generic_views.urls')
  380. class WeekArchiveViewTests(TestDataMixin, TestCase):
  381. def test_week_view(self):
  382. res = self.client.get('/dates/books/2008/week/39/')
  383. self.assertEqual(res.status_code, 200)
  384. self.assertTemplateUsed(res, 'generic_views/book_archive_week.html')
  385. self.assertEqual(res.context['book_list'][0], Book.objects.get(pubdate=datetime.date(2008, 10, 1)))
  386. self.assertEqual(res.context['week'], datetime.date(2008, 9, 28))
  387. # Since allow_empty=False, next/prev weeks must be valid
  388. self.assertIsNone(res.context['next_week'])
  389. self.assertEqual(res.context['previous_week'], datetime.date(2006, 4, 30))
  390. def test_week_view_allow_empty(self):
  391. # allow_empty = False, empty week
  392. res = self.client.get('/dates/books/2008/week/12/')
  393. self.assertEqual(res.status_code, 404)
  394. # allow_empty = True, empty month
  395. res = self.client.get('/dates/books/2008/week/12/allow_empty/')
  396. self.assertEqual(res.status_code, 200)
  397. self.assertEqual(list(res.context['book_list']), [])
  398. self.assertEqual(res.context['week'], datetime.date(2008, 3, 23))
  399. # Since allow_empty=True, next/prev are allowed to be empty weeks
  400. self.assertEqual(res.context['next_week'], datetime.date(2008, 3, 30))
  401. self.assertEqual(res.context['previous_week'], datetime.date(2008, 3, 16))
  402. # allow_empty but not allow_future: next_week should be empty
  403. url = datetime.date.today().strftime('/dates/books/%Y/week/%U/allow_empty/').lower()
  404. res = self.client.get(url)
  405. self.assertEqual(res.status_code, 200)
  406. self.assertIsNone(res.context['next_week'])
  407. def test_week_view_allow_future(self):
  408. # January 7th always falls in week 1, given Python's definition of week numbers
  409. future = datetime.date(datetime.date.today().year + 1, 1, 7)
  410. future_sunday = future - datetime.timedelta(days=(future.weekday() + 1) % 7)
  411. b = Book.objects.create(name="The New New Testement", pages=600, pubdate=future)
  412. res = self.client.get('/dates/books/%s/week/1/' % future.year)
  413. self.assertEqual(res.status_code, 404)
  414. res = self.client.get('/dates/books/%s/week/1/allow_future/' % future.year)
  415. self.assertEqual(res.status_code, 200)
  416. self.assertEqual(list(res.context['book_list']), [b])
  417. self.assertEqual(res.context['week'], future_sunday)
  418. # Since allow_future = True but not allow_empty, next/prev are not
  419. # allowed to be empty weeks
  420. self.assertIsNone(res.context['next_week'])
  421. self.assertEqual(res.context['previous_week'], datetime.date(2008, 9, 28))
  422. # allow_future, but not allow_empty, with a current week. So next
  423. # should be in the future
  424. res = self.client.get('/dates/books/2008/week/39/allow_future/')
  425. self.assertEqual(res.status_code, 200)
  426. self.assertEqual(res.context['next_week'], future_sunday)
  427. self.assertEqual(res.context['previous_week'], datetime.date(2006, 4, 30))
  428. def test_week_view_paginated(self):
  429. week_start = datetime.date(2008, 9, 28)
  430. week_end = week_start + datetime.timedelta(days=7)
  431. res = self.client.get('/dates/books/2008/week/39/')
  432. self.assertEqual(res.status_code, 200)
  433. self.assertEqual(
  434. list(res.context['book_list']),
  435. list(Book.objects.filter(pubdate__gte=week_start, pubdate__lt=week_end))
  436. )
  437. self.assertEqual(
  438. list(res.context['object_list']),
  439. list(Book.objects.filter(pubdate__gte=week_start, pubdate__lt=week_end))
  440. )
  441. self.assertTemplateUsed(res, 'generic_views/book_archive_week.html')
  442. def test_week_view_invalid_pattern(self):
  443. res = self.client.get('/dates/books/2007/week/no_week/')
  444. self.assertEqual(res.status_code, 404)
  445. def test_week_start_Monday(self):
  446. # Regression for #14752
  447. res = self.client.get('/dates/books/2008/week/39/')
  448. self.assertEqual(res.status_code, 200)
  449. self.assertEqual(res.context['week'], datetime.date(2008, 9, 28))
  450. res = self.client.get('/dates/books/2008/week/39/monday/')
  451. self.assertEqual(res.status_code, 200)
  452. self.assertEqual(res.context['week'], datetime.date(2008, 9, 29))
  453. def test_week_iso_format(self):
  454. res = self.client.get('/dates/books/2008/week/40/iso_format/')
  455. self.assertEqual(res.status_code, 200)
  456. self.assertTemplateUsed(res, 'generic_views/book_archive_week.html')
  457. self.assertEqual(
  458. list(res.context['book_list']),
  459. [Book.objects.get(pubdate=datetime.date(2008, 10, 1))],
  460. )
  461. self.assertEqual(res.context['week'], datetime.date(2008, 9, 29))
  462. def test_unknown_week_format(self):
  463. msg = "Unknown week format '%T'. Choices are: %U, %V, %W"
  464. with self.assertRaisesMessage(ValueError, msg):
  465. self.client.get('/dates/books/2008/week/39/unknown_week_format/')
  466. def test_incompatible_iso_week_format_view(self):
  467. msg = (
  468. "ISO week directive '%V' is incompatible with the year directive "
  469. "'%Y'. Use the ISO year '%G' instead."
  470. )
  471. with self.assertRaisesMessage(ValueError, msg):
  472. self.client.get('/dates/books/2008/week/40/invalid_iso_week_year_format/')
  473. def test_datetime_week_view(self):
  474. BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0))
  475. res = self.client.get('/dates/booksignings/2008/week/13/')
  476. self.assertEqual(res.status_code, 200)
  477. @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi')
  478. def test_aware_datetime_week_view(self):
  479. BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc))
  480. res = self.client.get('/dates/booksignings/2008/week/13/')
  481. self.assertEqual(res.status_code, 200)
  482. @override_settings(ROOT_URLCONF='generic_views.urls')
  483. class DayArchiveViewTests(TestDataMixin, TestCase):
  484. def test_day_view(self):
  485. res = self.client.get('/dates/books/2008/oct/01/')
  486. self.assertEqual(res.status_code, 200)
  487. self.assertTemplateUsed(res, 'generic_views/book_archive_day.html')
  488. self.assertEqual(list(res.context['book_list']),
  489. list(Book.objects.filter(pubdate=datetime.date(2008, 10, 1))))
  490. self.assertEqual(res.context['day'], datetime.date(2008, 10, 1))
  491. # Since allow_empty=False, next/prev days must be valid.
  492. self.assertIsNone(res.context['next_day'])
  493. self.assertEqual(res.context['previous_day'], datetime.date(2006, 5, 1))
  494. def test_day_view_allow_empty(self):
  495. # allow_empty = False, empty month
  496. res = self.client.get('/dates/books/2000/jan/1/')
  497. self.assertEqual(res.status_code, 404)
  498. # allow_empty = True, empty month
  499. res = self.client.get('/dates/books/2000/jan/1/allow_empty/')
  500. self.assertEqual(res.status_code, 200)
  501. self.assertEqual(list(res.context['book_list']), [])
  502. self.assertEqual(res.context['day'], datetime.date(2000, 1, 1))
  503. # Since it's allow empty, next/prev are allowed to be empty months (#7164)
  504. self.assertEqual(res.context['next_day'], datetime.date(2000, 1, 2))
  505. self.assertEqual(res.context['previous_day'], datetime.date(1999, 12, 31))
  506. # allow_empty but not allow_future: next_month should be empty (#7164)
  507. url = datetime.date.today().strftime('/dates/books/%Y/%b/%d/allow_empty/').lower()
  508. res = self.client.get(url)
  509. self.assertEqual(res.status_code, 200)
  510. self.assertIsNone(res.context['next_day'])
  511. def test_day_view_allow_future(self):
  512. future = (datetime.date.today() + datetime.timedelta(days=60))
  513. urlbit = future.strftime('%Y/%b/%d').lower()
  514. b = Book.objects.create(name="The New New Testement", pages=600, pubdate=future)
  515. # allow_future = False, future month
  516. res = self.client.get('/dates/books/%s/' % urlbit)
  517. self.assertEqual(res.status_code, 404)
  518. # allow_future = True, valid future month
  519. res = self.client.get('/dates/books/%s/allow_future/' % urlbit)
  520. self.assertEqual(res.status_code, 200)
  521. self.assertEqual(list(res.context['book_list']), [b])
  522. self.assertEqual(res.context['day'], future)
  523. # allow_future but not allow_empty, next/prev must be valid
  524. self.assertIsNone(res.context['next_day'])
  525. self.assertEqual(res.context['previous_day'], datetime.date(2008, 10, 1))
  526. # allow_future, but not allow_empty, with a current month.
  527. res = self.client.get('/dates/books/2008/oct/01/allow_future/')
  528. self.assertEqual(res.status_code, 200)
  529. self.assertEqual(res.context['next_day'], future)
  530. self.assertEqual(res.context['previous_day'], datetime.date(2006, 5, 1))
  531. # allow_future for yesterday, next_day is today (#17192)
  532. today = datetime.date.today()
  533. yesterday = today - datetime.timedelta(days=1)
  534. res = self.client.get('/dates/books/%s/allow_empty_and_future/'
  535. % yesterday.strftime('%Y/%b/%d').lower())
  536. self.assertEqual(res.context['next_day'], today)
  537. def test_day_view_paginated(self):
  538. res = self.client.get('/dates/books/2008/oct/1/')
  539. self.assertEqual(res.status_code, 200)
  540. self.assertEqual(
  541. list(res.context['book_list']),
  542. list(Book.objects.filter(pubdate__year=2008, pubdate__month=10, pubdate__day=1))
  543. )
  544. self.assertEqual(
  545. list(res.context['object_list']),
  546. list(Book.objects.filter(pubdate__year=2008, pubdate__month=10, pubdate__day=1))
  547. )
  548. self.assertTemplateUsed(res, 'generic_views/book_archive_day.html')
  549. def test_next_prev_context(self):
  550. res = self.client.get('/dates/books/2008/oct/01/')
  551. self.assertEqual(res.content, b"Archive for Oct. 1, 2008. Previous day is May 1, 2006\n")
  552. def test_custom_month_format(self):
  553. res = self.client.get('/dates/books/2008/10/01/')
  554. self.assertEqual(res.status_code, 200)
  555. def test_day_view_invalid_pattern(self):
  556. res = self.client.get('/dates/books/2007/oct/no_day/')
  557. self.assertEqual(res.status_code, 404)
  558. def test_today_view(self):
  559. res = self.client.get('/dates/books/today/')
  560. self.assertEqual(res.status_code, 404)
  561. res = self.client.get('/dates/books/today/allow_empty/')
  562. self.assertEqual(res.status_code, 200)
  563. self.assertEqual(res.context['day'], datetime.date.today())
  564. def test_datetime_day_view(self):
  565. BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0))
  566. res = self.client.get('/dates/booksignings/2008/apr/2/')
  567. self.assertEqual(res.status_code, 200)
  568. @requires_tz_support
  569. @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi')
  570. def test_aware_datetime_day_view(self):
  571. bs = BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc))
  572. res = self.client.get('/dates/booksignings/2008/apr/2/')
  573. self.assertEqual(res.status_code, 200)
  574. # 2008-04-02T00:00:00+03:00 (beginning of day) > 2008-04-01T22:00:00+00:00 (book signing event date)
  575. bs.event_date = datetime.datetime(2008, 4, 1, 22, 0, tzinfo=timezone.utc)
  576. bs.save()
  577. res = self.client.get('/dates/booksignings/2008/apr/2/')
  578. self.assertEqual(res.status_code, 200)
  579. # 2008-04-03T00:00:00+03:00 (end of day) > 2008-04-02T22:00:00+00:00 (book signing event date)
  580. bs.event_date = datetime.datetime(2008, 4, 2, 22, 0, tzinfo=timezone.utc)
  581. bs.save()
  582. res = self.client.get('/dates/booksignings/2008/apr/2/')
  583. self.assertEqual(res.status_code, 404)
  584. @override_settings(ROOT_URLCONF='generic_views.urls')
  585. class DateDetailViewTests(TestDataMixin, TestCase):
  586. def test_date_detail_by_pk(self):
  587. res = self.client.get('/dates/books/2008/oct/01/%s/' % self.book1.pk)
  588. self.assertEqual(res.status_code, 200)
  589. self.assertEqual(res.context['object'], self.book1)
  590. self.assertEqual(res.context['book'], self.book1)
  591. self.assertTemplateUsed(res, 'generic_views/book_detail.html')
  592. def test_date_detail_by_slug(self):
  593. res = self.client.get('/dates/books/2006/may/01/byslug/dreaming-in-code/')
  594. self.assertEqual(res.status_code, 200)
  595. self.assertEqual(res.context['book'], Book.objects.get(slug='dreaming-in-code'))
  596. def test_date_detail_custom_month_format(self):
  597. res = self.client.get('/dates/books/2008/10/01/%s/' % self.book1.pk)
  598. self.assertEqual(res.status_code, 200)
  599. self.assertEqual(res.context['book'], self.book1)
  600. def test_date_detail_allow_future(self):
  601. future = (datetime.date.today() + datetime.timedelta(days=60))
  602. urlbit = future.strftime('%Y/%b/%d').lower()
  603. b = Book.objects.create(name="The New New Testement", slug="new-new", pages=600, pubdate=future)
  604. res = self.client.get('/dates/books/%s/new-new/' % urlbit)
  605. self.assertEqual(res.status_code, 404)
  606. res = self.client.get('/dates/books/%s/%s/allow_future/' % (urlbit, b.id))
  607. self.assertEqual(res.status_code, 200)
  608. self.assertEqual(res.context['book'], b)
  609. self.assertTemplateUsed(res, 'generic_views/book_detail.html')
  610. def test_year_out_of_range(self):
  611. urls = [
  612. '/dates/books/9999/',
  613. '/dates/books/9999/12/',
  614. '/dates/books/9999/week/52/',
  615. ]
  616. for url in urls:
  617. with self.subTest(url=url):
  618. res = self.client.get(url)
  619. self.assertEqual(res.status_code, 404)
  620. self.assertEqual(res.context['exception'], 'Date out of range')
  621. def test_invalid_url(self):
  622. msg = (
  623. 'Generic detail view BookDetail must be called with either an '
  624. 'object pk or a slug in the URLconf.'
  625. )
  626. with self.assertRaisesMessage(AttributeError, msg):
  627. self.client.get("/dates/books/2008/oct/01/nopk/")
  628. def test_get_object_custom_queryset(self):
  629. """
  630. Custom querysets are used when provided to
  631. BaseDateDetailView.get_object().
  632. """
  633. res = self.client.get(
  634. '/dates/books/get_object_custom_queryset/2006/may/01/%s/' % self.book2.pk)
  635. self.assertEqual(res.status_code, 200)
  636. self.assertEqual(res.context['object'], self.book2)
  637. self.assertEqual(res.context['book'], self.book2)
  638. self.assertTemplateUsed(res, 'generic_views/book_detail.html')
  639. res = self.client.get(
  640. '/dates/books/get_object_custom_queryset/2008/oct/01/9999999/')
  641. self.assertEqual(res.status_code, 404)
  642. def test_get_object_custom_queryset_numqueries(self):
  643. with self.assertNumQueries(1):
  644. self.client.get('/dates/books/get_object_custom_queryset/2006/may/01/2/')
  645. def test_datetime_date_detail(self):
  646. bs = BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0))
  647. res = self.client.get('/dates/booksignings/2008/apr/2/%d/' % bs.pk)
  648. self.assertEqual(res.status_code, 200)
  649. @requires_tz_support
  650. @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi')
  651. def test_aware_datetime_date_detail(self):
  652. bs = BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc))
  653. res = self.client.get('/dates/booksignings/2008/apr/2/%d/' % bs.pk)
  654. self.assertEqual(res.status_code, 200)
  655. # 2008-04-02T00:00:00+03:00 (beginning of day) > 2008-04-01T22:00:00+00:00 (book signing event date)
  656. bs.event_date = datetime.datetime(2008, 4, 1, 22, 0, tzinfo=timezone.utc)
  657. bs.save()
  658. res = self.client.get('/dates/booksignings/2008/apr/2/%d/' % bs.pk)
  659. self.assertEqual(res.status_code, 200)
  660. # 2008-04-03T00:00:00+03:00 (end of day) > 2008-04-02T22:00:00+00:00 (book signing event date)
  661. bs.event_date = datetime.datetime(2008, 4, 2, 22, 0, tzinfo=timezone.utc)
  662. bs.save()
  663. res = self.client.get('/dates/booksignings/2008/apr/2/%d/' % bs.pk)
  664. self.assertEqual(res.status_code, 404)