Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #10890: added prev/next_week in the context

of per-week date-based generic views. Thanks ee_lars for the report.
  • Loading branch information...
commit fcb09b5746192fea1b672346c915ee11e4120d7e 1 parent bbb1258
Aymeric Augustin authored May 14, 2012
53  django/views/generic/dates.py
@@ -69,7 +69,7 @@ def get_next_month(self, date):
69 69
             next = date.replace(year=date.year + 1, month=1, day=1)
70 70
         else:
71 71
             next = date.replace(month=date.month + 1, day=1)
72  
-        return _get_next_prev_month(self, next, is_previous=False, use_first_day=True)
  72
+        return _get_next_prev(self, next, is_previous=False, period='month')
73 73
 
74 74
     def get_previous_month(self, date):
75 75
         """
@@ -77,7 +77,7 @@ def get_previous_month(self, date):
77 77
         """
78 78
         # prev must be the last day of the previous month.
79 79
         prev = date.replace(day=1) - datetime.timedelta(days=1)
80  
-        return _get_next_prev_month(self, prev, is_previous=True, use_first_day=True)
  80
+        return _get_next_prev(self, prev, is_previous=True, period='month')
81 81
 
82 82
 
83 83
 class DayMixin(object):
@@ -109,14 +109,14 @@ def get_next_day(self, date):
109 109
         Get the next valid day.
110 110
         """
111 111
         next = date + datetime.timedelta(days=1)
112  
-        return _get_next_prev_month(self, next, is_previous=False, use_first_day=False)
  112
+        return _get_next_prev(self, next, is_previous=False, period='day')
113 113
 
114 114
     def get_previous_day(self, date):
115 115
         """
116 116
         Get the previous valid day.
117 117
         """
118 118
         prev = date - datetime.timedelta(days=1)
119  
-        return _get_next_prev_month(self, prev, is_previous=True, use_first_day=False)
  119
+        return _get_next_prev(self, prev, is_previous=True, period='day')
120 120
 
121 121
 
122 122
 class WeekMixin(object):
@@ -143,6 +143,30 @@ def get_week(self):
143 143
                     raise Http404(_(u"No week specified"))
144 144
         return week
145 145
 
  146
+    def get_next_week(self, date):
  147
+        """
  148
+        Get the next valid week.
  149
+        """
  150
+        # next must be the first day of the next week.
  151
+        next = date + datetime.timedelta(days=7 - self._get_weekday(date))
  152
+        return _get_next_prev(self, next, is_previous=False, period='week')
  153
+
  154
+    def get_previous_week(self, date):
  155
+        """
  156
+        Get the previous valid week.
  157
+        """
  158
+        # prev must be the last day of the previous week.
  159
+        prev = date - datetime.timedelta(days=self._get_weekday(date) + 1)
  160
+        return _get_next_prev(self, prev, is_previous=True, period='week')
  161
+
  162
+    def _get_weekday(self, date):
  163
+        week_format = self.get_week_format()
  164
+        if week_format == '%W':                 # week starts on Monday
  165
+            return date.weekday()
  166
+        elif week_format == '%U':               # week starts on Sunday
  167
+            return (date.weekday() + 1) % 7
  168
+        else:
  169
+            raise ValueError("unknown week format: %s" % week_format)
146 170
 
147 171
 class DateMixin(object):
148 172
     """
@@ -428,7 +452,11 @@ def get_dated_items(self):
428 452
 
429 453
         qs = self.get_dated_queryset(**lookup_kwargs)
430 454
 
431  
-        return (None, qs, {'week': date})
  455
+        return (None, qs, {
  456
+            'week': date,
  457
+            'next_week': self.get_next_week(date),
  458
+            'previous_week': self.get_previous_week(date),
  459
+        })
432 460
 
433 461
 
434 462
 class WeekArchiveView(MultipleObjectTemplateResponseMixin, BaseWeekArchiveView):
@@ -557,7 +585,7 @@ def _date_from_string(year, year_format, month='', month_format='', day='', day_
557 585
         })
558 586
 
559 587
 
560  
-def _get_next_prev_month(generic_view, naive_result, is_previous, use_first_day):
  588
+def _get_next_prev(generic_view, naive_result, is_previous, period):
561 589
     """
562 590
     Helper: Get the next or the previous valid date. The idea is to allow
563 591
     links on month/day views to never be 404s by never providing a date
@@ -620,10 +648,15 @@ def _get_next_prev_month(generic_view, naive_result, is_previous, use_first_day)
620 648
             result = timezone.localtime(result)
621 649
         result = result.date()
622 650
 
623  
-    # For month views, we always want to have a date that's the first of the
624  
-    # month for consistency's sake.
625  
-    if result and use_first_day:
626  
-        result = result.replace(day=1)
  651
+    if result:
  652
+        if period == 'month':
  653
+            # first day of the month
  654
+            result = result.replace(day=1)
  655
+        elif period == 'week':
  656
+            # monday of the week
  657
+            result = result - datetime.timedelta(days=generic_view._get_weekday(result))
  658
+        elif period != 'day':
  659
+            raise ValueError('invalid period: %s' % period)
627 660
 
628 661
     # Check against future dates.
629 662
     if result and (allow_future or result < datetime.date.today()):
35  tests/regressiontests/generic_views/dates.py
@@ -196,7 +196,7 @@ def test_month_view_allow_empty(self):
196 196
         self.assertEqual(list(res.context['book_list']), [])
197 197
         self.assertEqual(res.context['month'], datetime.date(2000, 1, 1))
198 198
 
199  
-        # Since it's allow empty, next/prev are allowed to be empty months (#7164)
  199
+        # Since allow_empty=True, next/prev are allowed to be empty months (#7164)
200 200
         self.assertEqual(res.context['next_month'], datetime.date(2000, 2, 1))
201 201
         self.assertEqual(res.context['previous_month'], datetime.date(1999, 12, 1))
202 202
 
@@ -222,7 +222,7 @@ def test_month_view_allow_future(self):
222 222
         self.assertEqual(list(res.context['book_list']), [b])
223 223
         self.assertEqual(res.context['month'], future)
224 224
 
225  
-        # Since it's allow_future but not allow_empty, next/prev are not
  225
+        # Since allow_future = True but not allow_empty, next/prev are not
226 226
         # allowed to be empty months (#7164)
227 227
         self.assertEqual(res.context['next_month'], None)
228 228
         self.assertEqual(res.context['previous_month'], datetime.date(2008, 10, 1))
@@ -298,17 +298,35 @@ def test_week_view(self):
298 298
         self.assertEqual(res.context['book_list'][0], Book.objects.get(pubdate=datetime.date(2008, 10, 1)))
299 299
         self.assertEqual(res.context['week'], datetime.date(2008, 9, 28))
300 300
 
  301
+        # Since allow_empty=False, next/prev weeks must be valid
  302
+        self.assertEqual(res.context['next_week'], None)
  303
+        self.assertEqual(res.context['previous_week'], datetime.date(2006, 4, 30))
  304
+
301 305
     def test_week_view_allow_empty(self):
  306
+        # allow_empty = False, empty week
302 307
         res = self.client.get('/dates/books/2008/week/12/')
303 308
         self.assertEqual(res.status_code, 404)
304 309
 
  310
+        # allow_empty = True, empty month
305 311
         res = self.client.get('/dates/books/2008/week/12/allow_empty/')
306 312
         self.assertEqual(res.status_code, 200)
307 313
         self.assertEqual(list(res.context['book_list']), [])
  314
+        self.assertEqual(res.context['week'], datetime.date(2008, 3, 23))
  315
+
  316
+        # Since allow_empty=True, next/prev are allowed to be empty weeks
  317
+        self.assertEqual(res.context['next_week'], datetime.date(2008, 3, 30))
  318
+        self.assertEqual(res.context['previous_week'], datetime.date(2008, 3, 16))
  319
+
  320
+        # allow_empty but not allow_future: next_week should be empty
  321
+        url = datetime.date.today().strftime('/dates/books/%Y/week/%U/allow_empty/').lower()
  322
+        res = self.client.get(url)
  323
+        self.assertEqual(res.status_code, 200)
  324
+        self.assertEqual(res.context['next_week'], None)
308 325
 
309 326
     def test_week_view_allow_future(self):
310 327
         # January 7th always falls in week 1, given Python's definition of week numbers
311 328
         future = datetime.date(datetime.date.today().year + 1, 1, 7)
  329
+        future_sunday = future - datetime.timedelta(days=(future.weekday() + 1) % 7)
312 330
         b = Book.objects.create(name="The New New Testement", pages=600, pubdate=future)
313 331
 
314 332
         res = self.client.get('/dates/books/%s/week/1/' % future.year)
@@ -317,6 +335,19 @@ def test_week_view_allow_future(self):
317 335
         res = self.client.get('/dates/books/%s/week/1/allow_future/' % future.year)
318 336
         self.assertEqual(res.status_code, 200)
319 337
         self.assertEqual(list(res.context['book_list']), [b])
  338
+        self.assertEqual(res.context['week'], future_sunday)
  339
+
  340
+        # Since allow_future = True but not allow_empty, next/prev are not
  341
+        # allowed to be empty weeks
  342
+        self.assertEqual(res.context['next_week'], None)
  343
+        self.assertEqual(res.context['previous_week'], datetime.date(2008, 9, 28))
  344
+
  345
+        # allow_future, but not allow_empty, with a current week. So next
  346
+        # should be in the future
  347
+        res = self.client.get('/dates/books/2008/week/39/allow_future/')
  348
+        self.assertEqual(res.status_code, 200)
  349
+        self.assertEqual(res.context['next_week'], future_sunday)
  350
+        self.assertEqual(res.context['previous_week'], datetime.date(2006, 4, 30))
320 351
 
321 352
     def test_week_view_paginated(self):
322 353
         week_start = datetime.date(2008, 9, 28)

0 notes on commit fcb09b5

Please sign in to comment.
Something went wrong with that request. Please try again.