Code

Ticket #18217: 18217-backport.patch

File 18217-backport.patch, 23.5 KB (added by aaugustin, 2 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  
    11import datetime 
     2from django.conf import settings 
    23from django.db import models 
    34from django.core.exceptions import ImproperlyConfigured 
    45from django.http import Http404 
    56from django.utils.encoding import force_unicode 
     7from django.utils.functional import cached_property 
    68from django.utils.translation import ugettext as _ 
    79from django.utils import timezone 
    810from django.views.generic.base import View 
    class DateMixin(object): 
    161163        """ 
    162164        return self.allow_future 
    163165 
     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 
    164211 
    165212class BaseDateListView(MultipleObjectMixin, DateMixin, View): 
    166213    """ 
    class BaseDateListView(MultipleObjectMixin, DateMixin, View): 
    177224 
    178225    def get_dated_items(self): 
    179226        """ 
    180         Obtain the list of dates and itesm 
     227        Obtain the list of dates and items. 
    181228        """ 
    182229        raise NotImplementedError('A DateView must provide an implementation of get_dated_items()') 
    183230 
    class BaseDateListView(MultipleObjectMixin, DateMixin, View): 
    192239        allow_empty = self.get_allow_empty() 
    193240 
    194241        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}) 
    196244 
    197245        if not allow_empty and not qs: 
    198246            raise Http404(_(u"No %(verbose_name_plural)s available") % { 
    class BaseYearArchiveView(YearMixin, BaseDateListView): 
    267315        """ 
    268316        Return (date_list, items, extra_context) for this request. 
    269317        """ 
    270         # Yes, no error checking: the URLpattern ought to validate this; it's 
    271         # an error if it doesn't. 
    272318        year = self.get_year() 
     319 
    273320        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) 
    275331        date_list = self.get_date_list(qs, 'month') 
    276332 
    277333        if self.get_make_object_list(): 
    278             object_list = qs.order_by('-'+date_field) 
     334            object_list = qs.order_by('-' + date_field) 
    279335        else: 
    280336            # We need this to be a queryset since parent classes introspect it 
    281337            # to find information about the model. 
    class BaseMonthArchiveView(YearMixin, MonthMixin, BaseDateListView): 
    314370                                 month, self.get_month_format()) 
    315371 
    316372        # 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)) 
    318378        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, 
    321381        } 
    322382 
    323383        qs = self.get_dated_queryset(**lookup_kwargs) 
    class BaseWeekArchiveView(YearMixin, WeekMixin, BaseDateListView): 
    360420                                 week, week_format) 
    361421 
    362422        # Construct a date-range lookup. 
    363         first_day = date 
    364         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)) 
    365425        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, 
    368428        } 
    369429 
    370430        qs = self.get_dated_queryset(**lookup_kwargs) 
    class BaseDayArchiveView(YearMixin, MonthMixin, DayMixin, BaseDateListView): 
    402462        Do the actual heavy lifting of getting the dated items; this accepts a 
    403463        date object so that TodayArchiveView can be trivial. 
    404464        """ 
    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) 
    410466        qs = self.get_dated_queryset(**lookup_kwargs) 
    411467 
    412468        return (None, qs, { 
    class BaseDateDetailView(YearMixin, MonthMixin, DayMixin, DateMixin, BaseDetailV 
    472528        # Filter down a queryset from self.queryset using the date from the 
    473529        # URL. This'll get passed as the queryset to DetailView.get_object, 
    474530        # 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) 
    479533 
    480534        return super(BaseDetailView, self).get_object(queryset=qs) 
    481535 
    class DateDetailView(SingleObjectTemplateResponseMixin, BaseDateDetailView): 
    488542    template_name_suffix = '_detail' 
    489543 
    490544 
    491 def _date_from_string(year, year_format, month, month_format, day='', day_format='', delim='__'): 
     545def _date_from_string(year, year_format, month='', month_format='', day='', day_format='', delim='__'): 
    492546    """ 
    493547    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. 
    495549    """ 
    496550    format = delim.join((year_format, month_format, day_format)) 
    497551    datestr = delim.join((year, month, day)) 
    def _get_next_prev_month(generic_view, naive_result, is_previous, use_first_day) 
    559613        # Construct a lookup and an ordering depending on whether we're doing 
    560614        # a previous date or a next date lookup. 
    561615        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)} 
    563617            ordering = '-%s' % date_field 
    564618        else: 
    565             lookup = {'%s__gte' % date_field: naive_result} 
     619            lookup = {'%s__gte' % date_field: generic_view._make_date_lookup_arg(naive_result)} 
    566620            ordering = date_field 
    567621 
    568622        qs = generic_view.get_queryset().filter(**lookup).order_by(ordering) 
    def _get_next_prev_month(generic_view, naive_result, is_previous, use_first_day) 
    575629            result = None 
    576630 
    577631    # 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) 
    579635        result = result.date() 
    580636 
    581637    # 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) 
    588644        return result 
    589645    else: 
    590646        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 the 
    596     date field is a DateTimeField, we can't just do filter(df=date) because 
    597     that doesn't take the time into account. So we need to make a range lookup 
    598     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 
    752752        ``QuerySet``'s model that the date-based archive should use to 
    753753        determine the objects on the page. 
    754754 
     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 
    755761    .. attribute:: allow_future 
    756762 
    757763        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 
    44 
    55from django.core.exceptions import ImproperlyConfigured 
    66from django.test import TestCase 
     7from django.test.utils import override_settings 
     8from django.utils import timezone 
     9 
     10from .models import Book, BookSigning 
     11 
     12 
     13import warnings 
     14warnings.filterwarnings( 
     15        'error', r"DateTimeField received a naive datetime", 
     16        RuntimeWarning, r'django\.db\.models\.fields') 
     17 
    718 
    8 from .models import Book 
    919 
    1020 
    1121class ArchiveIndexViewTests(TestCase): 
    class ArchiveIndexViewTests(TestCase): 
    7989        self.assertEqual(list(res.context['latest']), list(Book.objects.all()[10:20])) 
    8090 
    8191 
     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 
    82104class YearArchiveViewTests(TestCase): 
    83105    fixtures = ['generic-views-test-data.json'] 
    84106    urls = 'regressiontests.generic_views.urls' 
    class YearArchiveViewTests(TestCase): 
    132154        res = self.client.get('/dates/books/no_year/') 
    133155        self.assertEqual(res.status_code, 404) 
    134156 
     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 
    135169class MonthArchiveViewTests(TestCase): 
    136170    fixtures = ['generic-views-test-data.json'] 
    137171    urls = 'regressiontests.generic_views.urls' 
    class MonthArchiveViewTests(TestCase): 
    236270        self.assertEqual(res.status_code, 200) 
    237271        self.assertEqual(res.context['previous_month'], datetime.date(2010,9,1)) 
    238272 
     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 
    239288 
    240289class WeekArchiveViewTests(TestCase): 
    241290    fixtures = ['generic-views-test-data.json'] 
    class WeekArchiveViewTests(TestCase): 
    291340        self.assertEqual(res.status_code, 200) 
    292341        self.assertEqual(res.context['week'], datetime.date(2008, 9, 29)) 
    293342 
     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 
    294355class DayArchiveViewTests(TestCase): 
    295356    fixtures = ['generic-views-test-data.json'] 
    296357    urls = 'regressiontests.generic_views.urls' 
    class DayArchiveViewTests(TestCase): 
    379440        self.assertEqual(res.status_code, 200) 
    380441        self.assertEqual(res.context['day'], datetime.date.today()) 
    381442 
     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 
    382463class DateDetailViewTests(TestCase): 
    383464    fixtures = ['generic-views-test-data.json'] 
    384465    urls = 'regressiontests.generic_views.urls' 
    class DateDetailViewTests(TestCase): 
    432513        res = self.client.get( 
    433514            '/dates/books/get_object_custom_queryset/2008/oct/01/1/') 
    434515        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): 
    4242class Page(models.Model): 
    4343    content = models.TextField() 
    4444    template = models.CharField(max_length=300) 
     45 
     46class 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('', 
    108108        views.BookArchive.as_view(queryset=None)), 
    109109    (r'^dates/books/paginated/$', 
    110110        views.BookArchive.as_view(paginate_by=10)), 
     111    (r'^dates/booksignings/$', 
     112        views.BookSigningArchive.as_view()), 
    111113 
    112114    # ListView 
    113115    (r'^list/dict/$', 
    urlpatterns = patterns('', 
    156158        views.BookYearArchive.as_view(make_object_list=True, paginate_by=30)), 
    157159    (r'^dates/books/no_year/$', 
    158160        views.BookYearArchive.as_view()), 
     161    (r'^dates/booksignings/(?P<year>\d{4})/$', 
     162        views.BookSigningYearArchive.as_view()), 
    159163 
    160164    # MonthArchiveView 
    161165    (r'^dates/books/(?P<year>\d{4})/(?P<month>[a-z]{3})/$', 
    urlpatterns = patterns('', 
    170174        views.BookMonthArchive.as_view(paginate_by=30)), 
    171175    (r'^dates/books/(?P<year>\d{4})/no_month/$', 
    172176        views.BookMonthArchive.as_view()), 
     177    (r'^dates/booksignings/(?P<year>\d{4})/(?P<month>[a-z]{3})/$', 
     178        views.BookSigningMonthArchive.as_view()), 
    173179 
    174180    # WeekArchiveView 
    175181    (r'^dates/books/(?P<year>\d{4})/week/(?P<week>\d{1,2})/$', 
    urlpatterns = patterns('', 
    184190        views.BookWeekArchive.as_view()), 
    185191    (r'^dates/books/(?P<year>\d{4})/week/(?P<week>\d{1,2})/monday/$', 
    186192        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()), 
    187195 
    188196    # DayArchiveView 
    189197    (r'^dates/books/(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\d{1,2})/$', 
    urlpatterns = patterns('', 
    198206        views.BookDayArchive.as_view(paginate_by=True)), 
    199207    (r'^dates/books/(?P<year>\d{4})/(?P<month>[a-z]{3})/no_day/$', 
    200208        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()), 
    201211 
    202212    # TodayArchiveView 
    203     (r'dates/books/today/$', 
     213    (r'^dates/books/today/$', 
    204214        views.BookTodayArchive.as_view()), 
    205     (r'dates/books/today/allow_empty/$', 
     215    (r'^dates/books/today/allow_empty/$', 
    206216        views.BookTodayArchive.as_view(allow_empty=True)), 
     217    (r'^dates/booksignings/today/$', 
     218        views.BookSigningTodayArchive.as_view()), 
    207219 
    208220    # DateDetailView 
    209221    (r'^dates/books/(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\d{1,2})/(?P<pk>\d+)/$', 
    urlpatterns = patterns('', 
    221233    (r'^dates/books/get_object_custom_queryset/(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\d{1,2})/(?P<pk>\d+)/$', 
    222234        views.BookDetailGetObjectCustomQueryset.as_view()), 
    223235 
     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 
    224239    # Useful for testing redirects 
    225240    (r'^accounts/login/$',  'django.contrib.auth.views.login') 
    226241) 
  • 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 
    77from django.views import generic 
    88 
    99from .forms import AuthorForm 
    10 from .models import Artist, Author, Book, Page 
     10from .models import Artist, Author, Book, Page, BookSigning 
    1111 
    1212 
    1313class CustomTemplateView(generic.TemplateView): 
    class BookDetailGetObjectCustomQueryset(BookDetail): 
    184184    def get_object(self, queryset=None): 
    185185        return super(BookDetailGetObjectCustomQueryset,self).get_object( 
    186186            queryset=Book.objects.filter(pk=2)) 
     187 
     188class 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 
     203class 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 
     210class BookSigningArchive(BookSigningConfig, generic.ArchiveIndexView): 
     211    pass 
     212 
     213class BookSigningYearArchive(BookSigningConfig, generic.YearArchiveView): 
     214    pass 
     215 
     216class BookSigningMonthArchive(BookSigningConfig, generic.MonthArchiveView): 
     217    pass 
     218 
     219class BookSigningWeekArchive(BookSigningConfig, generic.WeekArchiveView): 
     220    pass 
     221 
     222class BookSigningDayArchive(BookSigningConfig, generic.DayArchiveView): 
     223    pass 
     224 
     225class BookSigningTodayArchive(BookSigningConfig, generic.TodayArchiveView): 
     226    pass 
     227 
     228class BookSigningDetail(BookSigningConfig, generic.DateDetailView): 
     229    context_object_name = 'book'