Ticket #18217: 18217-backport.patch
File 18217-backport.patch, 23.5 KB (added by , 13 years ago) |
---|
-
django/views/generic/dates.py
diff --git a/django/views/generic/dates.py b/django/views/generic/dates.py index 5f5f959..8eb5b0a 100644
a b 1 1 import datetime 2 from django.conf import settings 2 3 from django.db import models 3 4 from django.core.exceptions import ImproperlyConfigured 4 5 from django.http import Http404 5 6 from django.utils.encoding import force_unicode 7 from django.utils.functional import cached_property 6 8 from django.utils.translation import ugettext as _ 7 9 from django.utils import timezone 8 10 from django.views.generic.base import View … … class DateMixin(object): 161 163 """ 162 164 return self.allow_future 163 165 166 # Note: the following three methods only work in subclasses that also 167 # inherit SingleObjectMixin or MultipleObjectMixin. 168 169 @cached_property 170 def uses_datetime_field(self): 171 """ 172 Return `True` if the date field is a `DateTimeField` and `False` 173 if it's a `DateField`. 174 """ 175 model = self.get_queryset().model if self.model is None else self.model 176 field = model._meta.get_field(self.get_date_field()) 177 return isinstance(field, models.DateTimeField) 178 179 def _make_date_lookup_arg(self, value): 180 """ 181 Convert a date into a datetime when the date field is a DateTimeField. 182 183 When time zone support is enabled, `date` is assumed to be in the 184 current time zone, so that displayed items are consistent with the URL. 185 """ 186 if self.uses_datetime_field: 187 value = datetime.datetime.combine(value, datetime.time.min) 188 if settings.USE_TZ: 189 value = timezone.make_aware(value, timezone.get_current_timezone()) 190 return value 191 192 def _make_single_date_lookup(self, date): 193 """ 194 Get the lookup kwargs for filtering on a single date. 195 196 If the date field is a DateTimeField, we can't just filter on 197 date_field=date because that doesn't take the time into account. 198 """ 199 date_field = self.get_date_field() 200 if self.uses_datetime_field: 201 since = self._make_date_lookup_arg(date) 202 until = self._make_date_lookup_arg(date + datetime.timedelta(days=1)) 203 return { 204 '%s__gte' % date_field: since, 205 '%s__lt' % date_field: until, 206 } 207 else: 208 # Skip self._make_date_lookup_arg, it's a no-op in this branch. 209 return {date_field: date} 210 164 211 165 212 class BaseDateListView(MultipleObjectMixin, DateMixin, View): 166 213 """ … … class BaseDateListView(MultipleObjectMixin, DateMixin, View): 177 224 178 225 def get_dated_items(self): 179 226 """ 180 Obtain the list of dates and ite sm227 Obtain the list of dates and items. 181 228 """ 182 229 raise NotImplementedError('A DateView must provide an implementation of get_dated_items()') 183 230 … … class BaseDateListView(MultipleObjectMixin, DateMixin, View): 192 239 allow_empty = self.get_allow_empty() 193 240 194 241 if not allow_future: 195 qs = qs.filter(**{'%s__lte' % date_field: timezone.now()}) 242 now = timezone.now() if self.uses_datetime_field else datetime.date.today() 243 qs = qs.filter(**{'%s__lte' % date_field: now}) 196 244 197 245 if not allow_empty and not qs: 198 246 raise Http404(_(u"No %(verbose_name_plural)s available") % { … … class BaseYearArchiveView(YearMixin, BaseDateListView): 267 315 """ 268 316 Return (date_list, items, extra_context) for this request. 269 317 """ 270 # Yes, no error checking: the URLpattern ought to validate this; it's271 # an error if it doesn't.272 318 year = self.get_year() 319 273 320 date_field = self.get_date_field() 274 qs = self.get_dated_queryset(**{date_field+'__year': year}) 321 date = _date_from_string(year, self.get_year_format()) 322 323 since = self._make_date_lookup_arg(date) 324 until = self._make_date_lookup_arg(datetime.date(date.year + 1, 1, 1)) 325 lookup_kwargs = { 326 '%s__gte' % date_field: since, 327 '%s__lt' % date_field: until, 328 } 329 330 qs = self.get_dated_queryset(**lookup_kwargs) 275 331 date_list = self.get_date_list(qs, 'month') 276 332 277 333 if self.get_make_object_list(): 278 object_list = qs.order_by('-' +date_field)334 object_list = qs.order_by('-' + date_field) 279 335 else: 280 336 # We need this to be a queryset since parent classes introspect it 281 337 # to find information about the model. … … class BaseMonthArchiveView(YearMixin, MonthMixin, BaseDateListView): 314 370 month, self.get_month_format()) 315 371 316 372 # Construct a date-range lookup. 317 first_day, last_day = _month_bounds(date) 373 since = self._make_date_lookup_arg(date) 374 if date.month == 12: 375 until = self._make_date_lookup_arg(datetime.date(date.year + 1, 1, 1)) 376 else: 377 until = self._make_date_lookup_arg(datetime.date(date.year, date.month + 1, 1)) 318 378 lookup_kwargs = { 319 '%s__gte' % date_field: first_day,320 '%s__lt' % date_field: last_day,379 '%s__gte' % date_field: since, 380 '%s__lt' % date_field: until, 321 381 } 322 382 323 383 qs = self.get_dated_queryset(**lookup_kwargs) … … class BaseWeekArchiveView(YearMixin, WeekMixin, BaseDateListView): 360 420 week, week_format) 361 421 362 422 # Construct a date-range lookup. 363 first_day = date364 last_day = date + datetime.timedelta(days=7)423 since = self._make_date_lookup_arg(date) 424 until = self._make_date_lookup_arg(date + datetime.timedelta(days=7)) 365 425 lookup_kwargs = { 366 '%s__gte' % date_field: first_day,367 '%s__lt' % date_field: last_day,426 '%s__gte' % date_field: since, 427 '%s__lt' % date_field: until, 368 428 } 369 429 370 430 qs = self.get_dated_queryset(**lookup_kwargs) … … class BaseDayArchiveView(YearMixin, MonthMixin, DayMixin, BaseDateListView): 402 462 Do the actual heavy lifting of getting the dated items; this accepts a 403 463 date object so that TodayArchiveView can be trivial. 404 464 """ 405 date_field = self.get_date_field() 406 407 field = self.get_queryset().model._meta.get_field(date_field) 408 lookup_kwargs = _date_lookup_for_field(field, date) 409 465 lookup_kwargs = self._make_single_date_lookup(date) 410 466 qs = self.get_dated_queryset(**lookup_kwargs) 411 467 412 468 return (None, qs, { … … class BaseDateDetailView(YearMixin, MonthMixin, DayMixin, DateMixin, BaseDetailV 472 528 # Filter down a queryset from self.queryset using the date from the 473 529 # URL. This'll get passed as the queryset to DetailView.get_object, 474 530 # which'll handle the 404 475 date_field = self.get_date_field() 476 field = qs.model._meta.get_field(date_field) 477 lookup = _date_lookup_for_field(field, date) 478 qs = qs.filter(**lookup) 531 lookup_kwargs = self._make_single_date_lookup(date) 532 qs = qs.filter(**lookup_kwargs) 479 533 480 534 return super(BaseDetailView, self).get_object(queryset=qs) 481 535 … … class DateDetailView(SingleObjectTemplateResponseMixin, BaseDateDetailView): 488 542 template_name_suffix = '_detail' 489 543 490 544 491 def _date_from_string(year, year_format, month , month_format, day='', day_format='', delim='__'):545 def _date_from_string(year, year_format, month='', month_format='', day='', day_format='', delim='__'): 492 546 """ 493 547 Helper: get a datetime.date object given a format string and a year, 494 month, and possibly day; raise a 404 for an invalid date.548 month, and day (only year is mandatory). Raise a 404 for an invalid date. 495 549 """ 496 550 format = delim.join((year_format, month_format, day_format)) 497 551 datestr = delim.join((year, month, day)) … … def _get_next_prev_month(generic_view, naive_result, is_previous, use_first_day) 559 613 # Construct a lookup and an ordering depending on whether we're doing 560 614 # a previous date or a next date lookup. 561 615 if is_previous: 562 lookup = {'%s__lte' % date_field: naive_result}616 lookup = {'%s__lte' % date_field: generic_view._make_date_lookup_arg(naive_result)} 563 617 ordering = '-%s' % date_field 564 618 else: 565 lookup = {'%s__gte' % date_field: naive_result}619 lookup = {'%s__gte' % date_field: generic_view._make_date_lookup_arg(naive_result)} 566 620 ordering = date_field 567 621 568 622 qs = generic_view.get_queryset().filter(**lookup).order_by(ordering) … … def _get_next_prev_month(generic_view, naive_result, is_previous, use_first_day) 575 629 result = None 576 630 577 631 # Convert datetimes to a dates 578 if hasattr(result, 'date'): 632 if result and generic_view.uses_datetime_field: 633 if settings.USE_TZ: 634 result = timezone.localtime(result) 579 635 result = result.date() 580 636 581 637 # For month views, we always want to have a date that's the first of the … … def _get_next_prev_month(generic_view, naive_result, is_previous, use_first_day) 588 644 return result 589 645 else: 590 646 return None 591 592 593 def _date_lookup_for_field(field, date):594 """595 Get the lookup kwargs for looking up a date against a given Field. If the596 date field is a DateTimeField, we can't just do filter(df=date) because597 that doesn't take the time into account. So we need to make a range lookup598 in those cases.599 """600 if isinstance(field, models.DateTimeField):601 date_range = (602 datetime.datetime.combine(date, datetime.time.min),603 datetime.datetime.combine(date, datetime.time.max)604 )605 return {'%s__range' % field.name: date_range}606 else:607 return {field.name: date} -
docs/ref/class-based-views.txt
diff --git a/docs/ref/class-based-views.txt b/docs/ref/class-based-views.txt index 5223aee..0d4bdd6 100644
a b DateMixin 752 752 ``QuerySet``'s model that the date-based archive should use to 753 753 determine the objects on the page. 754 754 755 When :doc:`time zone support </topics/i18n/timezones>` is enabled and 756 ``date_field`` is a ``DateTimeField``, dates are assumed to be in the 757 current time zone. As a consequence, if you have implemented per-user 758 time zone selection, users living in different time zones may view a 759 different set of objects at the same URL. 760 755 761 .. attribute:: allow_future 756 762 757 763 A boolean specifying whether to include "future" objects on this page, -
tests/regressiontests/generic_views/dates.py
diff --git a/tests/regressiontests/generic_views/dates.py b/tests/regressiontests/generic_views/dates.py index 652f66b..1c02056 100644
a b import datetime 4 4 5 5 from django.core.exceptions import ImproperlyConfigured 6 6 from django.test import TestCase 7 from django.test.utils import override_settings 8 from django.utils import timezone 9 10 from .models import Book, BookSigning 11 12 13 import warnings 14 warnings.filterwarnings( 15 'error', r"DateTimeField received a naive datetime", 16 RuntimeWarning, r'django\.db\.models\.fields') 17 7 18 8 from .models import Book9 19 10 20 11 21 class ArchiveIndexViewTests(TestCase): … … class ArchiveIndexViewTests(TestCase): 79 89 self.assertEqual(list(res.context['latest']), list(Book.objects.all()[10:20])) 80 90 81 91 92 def test_datetime_archive_view(self): 93 BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0)) 94 res = self.client.get('/dates/booksignings/') 95 self.assertEqual(res.status_code, 200) 96 97 @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi') 98 def test_aware_datetime_archive_view(self): 99 BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc)) 100 res = self.client.get('/dates/booksignings/') 101 self.assertEqual(res.status_code, 200) 102 103 82 104 class YearArchiveViewTests(TestCase): 83 105 fixtures = ['generic-views-test-data.json'] 84 106 urls = 'regressiontests.generic_views.urls' … … class YearArchiveViewTests(TestCase): 132 154 res = self.client.get('/dates/books/no_year/') 133 155 self.assertEqual(res.status_code, 404) 134 156 157 def test_datetime_year_view(self): 158 BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0)) 159 res = self.client.get('/dates/booksignings/2008/') 160 self.assertEqual(res.status_code, 200) 161 162 @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi') 163 def test_aware_datetime_year_view(self): 164 BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc)) 165 res = self.client.get('/dates/booksignings/2008/') 166 self.assertEqual(res.status_code, 200) 167 168 135 169 class MonthArchiveViewTests(TestCase): 136 170 fixtures = ['generic-views-test-data.json'] 137 171 urls = 'regressiontests.generic_views.urls' … … class MonthArchiveViewTests(TestCase): 236 270 self.assertEqual(res.status_code, 200) 237 271 self.assertEqual(res.context['previous_month'], datetime.date(2010,9,1)) 238 272 273 def test_datetime_month_view(self): 274 BookSigning.objects.create(event_date=datetime.datetime(2008, 2, 1, 12, 0)) 275 BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0)) 276 BookSigning.objects.create(event_date=datetime.datetime(2008, 6, 3, 12, 0)) 277 res = self.client.get('/dates/booksignings/2008/apr/') 278 self.assertEqual(res.status_code, 200) 279 280 @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi') 281 def test_aware_datetime_month_view(self): 282 BookSigning.objects.create(event_date=datetime.datetime(2008, 2, 1, 12, 0, tzinfo=timezone.utc)) 283 BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc)) 284 BookSigning.objects.create(event_date=datetime.datetime(2008, 6, 3, 12, 0, tzinfo=timezone.utc)) 285 res = self.client.get('/dates/booksignings/2008/apr/') 286 self.assertEqual(res.status_code, 200) 287 239 288 240 289 class WeekArchiveViewTests(TestCase): 241 290 fixtures = ['generic-views-test-data.json'] … … class WeekArchiveViewTests(TestCase): 291 340 self.assertEqual(res.status_code, 200) 292 341 self.assertEqual(res.context['week'], datetime.date(2008, 9, 29)) 293 342 343 def test_datetime_week_view(self): 344 BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0)) 345 res = self.client.get('/dates/booksignings/2008/week/13/') 346 self.assertEqual(res.status_code, 200) 347 348 @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi') 349 def test_aware_datetime_week_view(self): 350 BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc)) 351 res = self.client.get('/dates/booksignings/2008/week/13/') 352 self.assertEqual(res.status_code, 200) 353 354 294 355 class DayArchiveViewTests(TestCase): 295 356 fixtures = ['generic-views-test-data.json'] 296 357 urls = 'regressiontests.generic_views.urls' … … class DayArchiveViewTests(TestCase): 379 440 self.assertEqual(res.status_code, 200) 380 441 self.assertEqual(res.context['day'], datetime.date.today()) 381 442 443 def test_datetime_day_view(self): 444 BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0)) 445 res = self.client.get('/dates/booksignings/2008/apr/2/') 446 self.assertEqual(res.status_code, 200) 447 448 @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi') 449 def test_aware_datetime_day_view(self): 450 BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc)) 451 res = self.client.get('/dates/booksignings/2008/apr/2/') 452 self.assertEqual(res.status_code, 200) 453 # 2008-04-02T00:00:00+03:00 (beginning of day) > 2008-04-01T22:00:00+00:00 (book signing event date) 454 BookSigning.objects.filter(pk=1).update(event_date=datetime.datetime(2008, 4, 1, 22, 0, tzinfo=timezone.utc)) 455 res = self.client.get('/dates/booksignings/2008/apr/2/') 456 self.assertEqual(res.status_code, 200) 457 # 2008-04-03T00:00:00+03:00 (end of day) > 2008-04-02T22:00:00+00:00 (book signing event date) 458 BookSigning.objects.filter(pk=1).update(event_date=datetime.datetime(2008, 4, 2, 22, 0, tzinfo=timezone.utc)) 459 res = self.client.get('/dates/booksignings/2008/apr/2/') 460 self.assertEqual(res.status_code, 404) 461 462 382 463 class DateDetailViewTests(TestCase): 383 464 fixtures = ['generic-views-test-data.json'] 384 465 urls = 'regressiontests.generic_views.urls' … … class DateDetailViewTests(TestCase): 432 513 res = self.client.get( 433 514 '/dates/books/get_object_custom_queryset/2008/oct/01/1/') 434 515 self.assertEqual(res.status_code, 404) 516 517 def test_datetime_date_detail(self): 518 BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0)) 519 res = self.client.get('/dates/booksignings/2008/apr/2/1/') 520 self.assertEqual(res.status_code, 200) 521 522 @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi') 523 def test_aware_datetime_date_detail(self): 524 BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc)) 525 res = self.client.get('/dates/booksignings/2008/apr/2/1/') 526 self.assertEqual(res.status_code, 200) 527 # 2008-04-02T00:00:00+03:00 (beginning of day) > 2008-04-01T22:00:00+00:00 (book signing event date) 528 BookSigning.objects.filter(pk=1).update(event_date=datetime.datetime(2008, 4, 1, 22, 0, tzinfo=timezone.utc)) 529 res = self.client.get('/dates/booksignings/2008/apr/2/1/') 530 self.assertEqual(res.status_code, 200) 531 # 2008-04-03T00:00:00+03:00 (end of day) > 2008-04-02T22:00:00+00:00 (book signing event date) 532 BookSigning.objects.filter(pk=1).update(event_date=datetime.datetime(2008, 4, 2, 22, 0, tzinfo=timezone.utc)) 533 res = self.client.get('/dates/booksignings/2008/apr/2/1/') 534 self.assertEqual(res.status_code, 404) -
tests/regressiontests/generic_views/models.py
diff --git a/tests/regressiontests/generic_views/models.py b/tests/regressiontests/generic_views/models.py index 5977258..2355769 100644
a b class Book(models.Model): 42 42 class Page(models.Model): 43 43 content = models.TextField() 44 44 template = models.CharField(max_length=300) 45 46 class BookSigning(models.Model): 47 event_date = models.DateTimeField() -
tests/regressiontests/generic_views/urls.py
diff --git a/tests/regressiontests/generic_views/urls.py b/tests/regressiontests/generic_views/urls.py index 090ec73..9c47ab8 100644
a b urlpatterns = patterns('', 108 108 views.BookArchive.as_view(queryset=None)), 109 109 (r'^dates/books/paginated/$', 110 110 views.BookArchive.as_view(paginate_by=10)), 111 (r'^dates/booksignings/$', 112 views.BookSigningArchive.as_view()), 111 113 112 114 # ListView 113 115 (r'^list/dict/$', … … urlpatterns = patterns('', 156 158 views.BookYearArchive.as_view(make_object_list=True, paginate_by=30)), 157 159 (r'^dates/books/no_year/$', 158 160 views.BookYearArchive.as_view()), 161 (r'^dates/booksignings/(?P<year>\d{4})/$', 162 views.BookSigningYearArchive.as_view()), 159 163 160 164 # MonthArchiveView 161 165 (r'^dates/books/(?P<year>\d{4})/(?P<month>[a-z]{3})/$', … … urlpatterns = patterns('', 170 174 views.BookMonthArchive.as_view(paginate_by=30)), 171 175 (r'^dates/books/(?P<year>\d{4})/no_month/$', 172 176 views.BookMonthArchive.as_view()), 177 (r'^dates/booksignings/(?P<year>\d{4})/(?P<month>[a-z]{3})/$', 178 views.BookSigningMonthArchive.as_view()), 173 179 174 180 # WeekArchiveView 175 181 (r'^dates/books/(?P<year>\d{4})/week/(?P<week>\d{1,2})/$', … … urlpatterns = patterns('', 184 190 views.BookWeekArchive.as_view()), 185 191 (r'^dates/books/(?P<year>\d{4})/week/(?P<week>\d{1,2})/monday/$', 186 192 views.BookWeekArchive.as_view(week_format='%W')), 193 (r'^dates/booksignings/(?P<year>\d{4})/week/(?P<week>\d{1,2})/$', 194 views.BookSigningWeekArchive.as_view()), 187 195 188 196 # DayArchiveView 189 197 (r'^dates/books/(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\d{1,2})/$', … … urlpatterns = patterns('', 198 206 views.BookDayArchive.as_view(paginate_by=True)), 199 207 (r'^dates/books/(?P<year>\d{4})/(?P<month>[a-z]{3})/no_day/$', 200 208 views.BookDayArchive.as_view()), 209 (r'^dates/booksignings/(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\d{1,2})/$', 210 views.BookSigningDayArchive.as_view()), 201 211 202 212 # TodayArchiveView 203 (r' dates/books/today/$',213 (r'^dates/books/today/$', 204 214 views.BookTodayArchive.as_view()), 205 (r' dates/books/today/allow_empty/$',215 (r'^dates/books/today/allow_empty/$', 206 216 views.BookTodayArchive.as_view(allow_empty=True)), 217 (r'^dates/booksignings/today/$', 218 views.BookSigningTodayArchive.as_view()), 207 219 208 220 # DateDetailView 209 221 (r'^dates/books/(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\d{1,2})/(?P<pk>\d+)/$', … … urlpatterns = patterns('', 221 233 (r'^dates/books/get_object_custom_queryset/(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\d{1,2})/(?P<pk>\d+)/$', 222 234 views.BookDetailGetObjectCustomQueryset.as_view()), 223 235 236 (r'^dates/booksignings/(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\d{1,2})/(?P<pk>\d+)/$', 237 views.BookSigningDetail.as_view()), 238 224 239 # Useful for testing redirects 225 240 (r'^accounts/login/$', 'django.contrib.auth.views.login') 226 241 ) -
tests/regressiontests/generic_views/views.py
diff --git a/tests/regressiontests/generic_views/views.py b/tests/regressiontests/generic_views/views.py index 5ff9cf0..a519e10 100644
a b from django.utils.decorators import method_decorator 7 7 from django.views import generic 8 8 9 9 from .forms import AuthorForm 10 from .models import Artist, Author, Book, Page 10 from .models import Artist, Author, Book, Page, BookSigning 11 11 12 12 13 13 class CustomTemplateView(generic.TemplateView): … … class BookDetailGetObjectCustomQueryset(BookDetail): 184 184 def get_object(self, queryset=None): 185 185 return super(BookDetailGetObjectCustomQueryset,self).get_object( 186 186 queryset=Book.objects.filter(pk=2)) 187 188 class CustomContextView(generic.detail.SingleObjectMixin, generic.View): 189 model = Book 190 object = Book(name='dummy') 191 192 def get_object(self): 193 return Book(name="dummy") 194 195 def get_context_data(self, **kwargs): 196 context = {'custom_key': 'custom_value'} 197 context.update(kwargs) 198 return super(CustomContextView, self).get_context_data(**context) 199 200 def get_context_object_name(self, obj): 201 return "test_name" 202 203 class BookSigningConfig(object): 204 model = BookSigning 205 date_field = 'event_date' 206 # use the same templates as for books 207 def get_template_names(self): 208 return ['generic_views/book%s.html' % self.template_name_suffix] 209 210 class BookSigningArchive(BookSigningConfig, generic.ArchiveIndexView): 211 pass 212 213 class BookSigningYearArchive(BookSigningConfig, generic.YearArchiveView): 214 pass 215 216 class BookSigningMonthArchive(BookSigningConfig, generic.MonthArchiveView): 217 pass 218 219 class BookSigningWeekArchive(BookSigningConfig, generic.WeekArchiveView): 220 pass 221 222 class BookSigningDayArchive(BookSigningConfig, generic.DayArchiveView): 223 pass 224 225 class BookSigningTodayArchive(BookSigningConfig, generic.TodayArchiveView): 226 pass 227 228 class BookSigningDetail(BookSigningConfig, generic.DateDetailView): 229 context_object_name = 'book'