Fixed #18217 -- Time zone support in generic views
Introduced a distinct implementation depending on the type of the date field (DateField or DateTimeField), and applied appropriate conversions is the latter case, when time zone support is enabled.
This commit is contained in:
parent
596cb9c7e2
commit
78ba9670af
|
@ -1,8 +1,10 @@
|
|||
import datetime
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.http import Http404
|
||||
from django.utils.encoding import force_unicode
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils import timezone
|
||||
from django.views.generic.base import View
|
||||
|
@ -164,6 +166,51 @@ class DateMixin(object):
|
|||
"""
|
||||
return self.allow_future
|
||||
|
||||
# Note: the following three methods only work in subclasses that also
|
||||
# inherit SingleObjectMixin or MultipleObjectMixin.
|
||||
|
||||
@cached_property
|
||||
def uses_datetime_field(self):
|
||||
"""
|
||||
Return `True` if the date field is a `DateTimeField` and `False`
|
||||
if it's a `DateField`.
|
||||
"""
|
||||
model = self.get_queryset().model if self.model is None else self.model
|
||||
field = model._meta.get_field(self.get_date_field())
|
||||
return isinstance(field, models.DateTimeField)
|
||||
|
||||
def _make_date_lookup_arg(self, value):
|
||||
"""
|
||||
Convert a date into a datetime when the date field is a DateTimeField.
|
||||
|
||||
When time zone support is enabled, `date` is assumed to be in the
|
||||
current time zone, so that displayed items are consistent with the URL.
|
||||
"""
|
||||
if self.uses_datetime_field:
|
||||
value = datetime.datetime.combine(value, datetime.time.min)
|
||||
if settings.USE_TZ:
|
||||
value = timezone.make_aware(value, timezone.get_current_timezone())
|
||||
return value
|
||||
|
||||
def _make_single_date_lookup(self, date):
|
||||
"""
|
||||
Get the lookup kwargs for filtering on a single date.
|
||||
|
||||
If the date field is a DateTimeField, we can't just filter on
|
||||
date_field=date because that doesn't take the time into account.
|
||||
"""
|
||||
date_field = self.get_date_field()
|
||||
if self.uses_datetime_field:
|
||||
since = self._make_date_lookup_arg(date)
|
||||
until = self._make_date_lookup_arg(date + datetime.timedelta(days=1))
|
||||
return {
|
||||
'%s__gte' % date_field: since,
|
||||
'%s__lt' % date_field: until,
|
||||
}
|
||||
else:
|
||||
# Skip self._make_date_lookup_arg, it's a no-op in this branch.
|
||||
return {date_field: date}
|
||||
|
||||
|
||||
class BaseDateListView(MultipleObjectMixin, DateMixin, View):
|
||||
"""
|
||||
|
@ -180,7 +227,7 @@ class BaseDateListView(MultipleObjectMixin, DateMixin, View):
|
|||
|
||||
def get_dated_items(self):
|
||||
"""
|
||||
Obtain the list of dates and itesm
|
||||
Obtain the list of dates and items.
|
||||
"""
|
||||
raise NotImplementedError('A DateView must provide an implementation of get_dated_items()')
|
||||
|
||||
|
@ -196,7 +243,8 @@ class BaseDateListView(MultipleObjectMixin, DateMixin, View):
|
|||
paginate_by = self.get_paginate_by(qs)
|
||||
|
||||
if not allow_future:
|
||||
qs = qs.filter(**{'%s__lte' % date_field: timezone.now()})
|
||||
now = timezone.now() if self.uses_datetime_field else datetime.date.today()
|
||||
qs = qs.filter(**{'%s__lte' % date_field: now})
|
||||
|
||||
if not allow_empty:
|
||||
# When pagination is enabled, it's better to do a cheap query
|
||||
|
@ -225,6 +273,7 @@ class BaseDateListView(MultipleObjectMixin, DateMixin, View):
|
|||
|
||||
return date_list
|
||||
|
||||
|
||||
class BaseArchiveIndexView(BaseDateListView):
|
||||
"""
|
||||
Base class for archives of date-based items.
|
||||
|
@ -265,15 +314,23 @@ class BaseYearArchiveView(YearMixin, BaseDateListView):
|
|||
"""
|
||||
Return (date_list, items, extra_context) for this request.
|
||||
"""
|
||||
# Yes, no error checking: the URLpattern ought to validate this; it's
|
||||
# an error if it doesn't.
|
||||
year = self.get_year()
|
||||
|
||||
date_field = self.get_date_field()
|
||||
qs = self.get_dated_queryset(**{date_field+'__year': year})
|
||||
date = _date_from_string(year, self.get_year_format())
|
||||
|
||||
since = self._make_date_lookup_arg(date)
|
||||
until = self._make_date_lookup_arg(datetime.date(date.year + 1, 1, 1))
|
||||
lookup_kwargs = {
|
||||
'%s__gte' % date_field: since,
|
||||
'%s__lt' % date_field: until,
|
||||
}
|
||||
|
||||
qs = self.get_dated_queryset(**lookup_kwargs)
|
||||
date_list = self.get_date_list(qs, 'month')
|
||||
|
||||
if self.get_make_object_list():
|
||||
object_list = qs.order_by('-'+date_field)
|
||||
object_list = qs.order_by('-' + date_field)
|
||||
else:
|
||||
# We need this to be a queryset since parent classes introspect it
|
||||
# to find information about the model.
|
||||
|
@ -312,14 +369,14 @@ class BaseMonthArchiveView(YearMixin, MonthMixin, BaseDateListView):
|
|||
month, self.get_month_format())
|
||||
|
||||
# Construct a date-range lookup.
|
||||
first_day = date.replace(day=1)
|
||||
if first_day.month == 12:
|
||||
last_day = first_day.replace(year=first_day.year + 1, month=1)
|
||||
since = self._make_date_lookup_arg(date)
|
||||
if date.month == 12:
|
||||
until = self._make_date_lookup_arg(datetime.date(date.year + 1, 1, 1))
|
||||
else:
|
||||
last_day = first_day.replace(month=first_day.month + 1)
|
||||
until = self._make_date_lookup_arg(datetime.date(date.year, date.month + 1, 1))
|
||||
lookup_kwargs = {
|
||||
'%s__gte' % date_field: first_day,
|
||||
'%s__lt' % date_field: last_day,
|
||||
'%s__gte' % date_field: since,
|
||||
'%s__lt' % date_field: until,
|
||||
}
|
||||
|
||||
qs = self.get_dated_queryset(**lookup_kwargs)
|
||||
|
@ -362,11 +419,11 @@ class BaseWeekArchiveView(YearMixin, WeekMixin, BaseDateListView):
|
|||
week, week_format)
|
||||
|
||||
# Construct a date-range lookup.
|
||||
first_day = date
|
||||
last_day = date + datetime.timedelta(days=7)
|
||||
since = self._make_date_lookup_arg(date)
|
||||
until = self._make_date_lookup_arg(date + datetime.timedelta(days=7))
|
||||
lookup_kwargs = {
|
||||
'%s__gte' % date_field: first_day,
|
||||
'%s__lt' % date_field: last_day,
|
||||
'%s__gte' % date_field: since,
|
||||
'%s__lt' % date_field: until,
|
||||
}
|
||||
|
||||
qs = self.get_dated_queryset(**lookup_kwargs)
|
||||
|
@ -404,11 +461,7 @@ class BaseDayArchiveView(YearMixin, MonthMixin, DayMixin, BaseDateListView):
|
|||
Do the actual heavy lifting of getting the dated items; this accepts a
|
||||
date object so that TodayArchiveView can be trivial.
|
||||
"""
|
||||
date_field = self.get_date_field()
|
||||
|
||||
field = self.get_queryset().model._meta.get_field(date_field)
|
||||
lookup_kwargs = _date_lookup_for_field(field, date)
|
||||
|
||||
lookup_kwargs = self._make_single_date_lookup(date)
|
||||
qs = self.get_dated_queryset(**lookup_kwargs)
|
||||
|
||||
return (None, qs, {
|
||||
|
@ -474,10 +527,8 @@ class BaseDateDetailView(YearMixin, MonthMixin, DayMixin, DateMixin, BaseDetailV
|
|||
# Filter down a queryset from self.queryset using the date from the
|
||||
# URL. This'll get passed as the queryset to DetailView.get_object,
|
||||
# which'll handle the 404
|
||||
date_field = self.get_date_field()
|
||||
field = qs.model._meta.get_field(date_field)
|
||||
lookup = _date_lookup_for_field(field, date)
|
||||
qs = qs.filter(**lookup)
|
||||
lookup_kwargs = self._make_single_date_lookup(date)
|
||||
qs = qs.filter(**lookup_kwargs)
|
||||
|
||||
return super(BaseDetailView, self).get_object(queryset=qs)
|
||||
|
||||
|
@ -490,10 +541,10 @@ class DateDetailView(SingleObjectTemplateResponseMixin, BaseDateDetailView):
|
|||
template_name_suffix = '_detail'
|
||||
|
||||
|
||||
def _date_from_string(year, year_format, month, month_format, day='', day_format='', delim='__'):
|
||||
def _date_from_string(year, year_format, month='', month_format='', day='', day_format='', delim='__'):
|
||||
"""
|
||||
Helper: get a datetime.date object given a format string and a year,
|
||||
month, and possibly day; raise a 404 for an invalid date.
|
||||
month, and day (only year is mandatory). Raise a 404 for an invalid date.
|
||||
"""
|
||||
format = delim.join((year_format, month_format, day_format))
|
||||
datestr = delim.join((year, month, day))
|
||||
|
@ -548,10 +599,10 @@ def _get_next_prev_month(generic_view, naive_result, is_previous, use_first_day)
|
|||
# Construct a lookup and an ordering depending on whether we're doing
|
||||
# a previous date or a next date lookup.
|
||||
if is_previous:
|
||||
lookup = {'%s__lte' % date_field: naive_result}
|
||||
lookup = {'%s__lte' % date_field: generic_view._make_date_lookup_arg(naive_result)}
|
||||
ordering = '-%s' % date_field
|
||||
else:
|
||||
lookup = {'%s__gte' % date_field: naive_result}
|
||||
lookup = {'%s__gte' % date_field: generic_view._make_date_lookup_arg(naive_result)}
|
||||
ordering = date_field
|
||||
|
||||
qs = generic_view.get_queryset().filter(**lookup).order_by(ordering)
|
||||
|
@ -564,7 +615,9 @@ def _get_next_prev_month(generic_view, naive_result, is_previous, use_first_day)
|
|||
result = None
|
||||
|
||||
# Convert datetimes to a dates
|
||||
if hasattr(result, 'date'):
|
||||
if result and generic_view.uses_datetime_field:
|
||||
if settings.USE_TZ:
|
||||
result = timezone.localtime(result)
|
||||
result = result.date()
|
||||
|
||||
# For month views, we always want to have a date that's the first of the
|
||||
|
@ -577,20 +630,3 @@ def _get_next_prev_month(generic_view, naive_result, is_previous, use_first_day)
|
|||
return result
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def _date_lookup_for_field(field, date):
|
||||
"""
|
||||
Get the lookup kwargs for looking up a date against a given Field. If the
|
||||
date field is a DateTimeField, we can't just do filter(df=date) because
|
||||
that doesn't take the time into account. So we need to make a range lookup
|
||||
in those cases.
|
||||
"""
|
||||
if isinstance(field, models.DateTimeField):
|
||||
date_range = (
|
||||
datetime.datetime.combine(date, datetime.time.min),
|
||||
datetime.datetime.combine(date, datetime.time.max)
|
||||
)
|
||||
return {'%s__range' % field.name: date_range}
|
||||
else:
|
||||
return {field.name: date}
|
||||
|
|
|
@ -748,6 +748,12 @@ DateMixin
|
|||
``QuerySet``'s model that the date-based archive should use to
|
||||
determine the objects on the page.
|
||||
|
||||
When :doc:`time zone support </topics/i18n/timezones>` is enabled and
|
||||
``date_field`` is a ``DateTimeField``, dates are assumed to be in the
|
||||
current time zone. As a consequence, if you have implemented per-user
|
||||
time zone selection, users living in different time zones may view a
|
||||
different set of objects at the same URL.
|
||||
|
||||
.. attribute:: allow_future
|
||||
|
||||
A boolean specifying whether to include "future" objects on this page,
|
||||
|
|
|
@ -4,8 +4,18 @@ import datetime
|
|||
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.utils import timezone
|
||||
|
||||
from .models import Book, BookSigning
|
||||
|
||||
|
||||
import warnings
|
||||
warnings.filterwarnings(
|
||||
'error', r"DateTimeField received a naive datetime",
|
||||
RuntimeWarning, r'django\.db\.models\.fields')
|
||||
|
||||
|
||||
from .models import Book
|
||||
|
||||
|
||||
class ArchiveIndexViewTests(TestCase):
|
||||
|
@ -88,6 +98,18 @@ class ArchiveIndexViewTests(TestCase):
|
|||
with self.assertNumQueries(3):
|
||||
self.client.get('/dates/books/paginated/')
|
||||
|
||||
def test_datetime_archive_view(self):
|
||||
BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0))
|
||||
res = self.client.get('/dates/booksignings/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
@override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi')
|
||||
def test_aware_datetime_archive_view(self):
|
||||
BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc))
|
||||
res = self.client.get('/dates/booksignings/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
|
||||
class YearArchiveViewTests(TestCase):
|
||||
fixtures = ['generic-views-test-data.json']
|
||||
urls = 'regressiontests.generic_views.urls'
|
||||
|
@ -141,6 +163,18 @@ class YearArchiveViewTests(TestCase):
|
|||
res = self.client.get('/dates/books/no_year/')
|
||||
self.assertEqual(res.status_code, 404)
|
||||
|
||||
def test_datetime_year_view(self):
|
||||
BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0))
|
||||
res = self.client.get('/dates/booksignings/2008/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
@override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi')
|
||||
def test_aware_datetime_year_view(self):
|
||||
BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc))
|
||||
res = self.client.get('/dates/booksignings/2008/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
|
||||
class MonthArchiveViewTests(TestCase):
|
||||
fixtures = ['generic-views-test-data.json']
|
||||
urls = 'regressiontests.generic_views.urls'
|
||||
|
@ -245,6 +279,21 @@ class MonthArchiveViewTests(TestCase):
|
|||
self.assertEqual(res.status_code, 200)
|
||||
self.assertEqual(res.context['previous_month'], datetime.date(2010,9,1))
|
||||
|
||||
def test_datetime_month_view(self):
|
||||
BookSigning.objects.create(event_date=datetime.datetime(2008, 2, 1, 12, 0))
|
||||
BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0))
|
||||
BookSigning.objects.create(event_date=datetime.datetime(2008, 6, 3, 12, 0))
|
||||
res = self.client.get('/dates/booksignings/2008/apr/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
@override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi')
|
||||
def test_aware_datetime_month_view(self):
|
||||
BookSigning.objects.create(event_date=datetime.datetime(2008, 2, 1, 12, 0, tzinfo=timezone.utc))
|
||||
BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc))
|
||||
BookSigning.objects.create(event_date=datetime.datetime(2008, 6, 3, 12, 0, tzinfo=timezone.utc))
|
||||
res = self.client.get('/dates/booksignings/2008/apr/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
|
||||
class WeekArchiveViewTests(TestCase):
|
||||
fixtures = ['generic-views-test-data.json']
|
||||
|
@ -300,6 +349,18 @@ class WeekArchiveViewTests(TestCase):
|
|||
self.assertEqual(res.status_code, 200)
|
||||
self.assertEqual(res.context['week'], datetime.date(2008, 9, 29))
|
||||
|
||||
def test_datetime_week_view(self):
|
||||
BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0))
|
||||
res = self.client.get('/dates/booksignings/2008/week/13/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
@override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi')
|
||||
def test_aware_datetime_week_view(self):
|
||||
BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc))
|
||||
res = self.client.get('/dates/booksignings/2008/week/13/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
|
||||
class DayArchiveViewTests(TestCase):
|
||||
fixtures = ['generic-views-test-data.json']
|
||||
urls = 'regressiontests.generic_views.urls'
|
||||
|
@ -388,6 +449,26 @@ class DayArchiveViewTests(TestCase):
|
|||
self.assertEqual(res.status_code, 200)
|
||||
self.assertEqual(res.context['day'], datetime.date.today())
|
||||
|
||||
def test_datetime_day_view(self):
|
||||
BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0))
|
||||
res = self.client.get('/dates/booksignings/2008/apr/2/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
@override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi')
|
||||
def test_aware_datetime_day_view(self):
|
||||
BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc))
|
||||
res = self.client.get('/dates/booksignings/2008/apr/2/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
# 2008-04-02T00:00:00+03:00 (beginning of day) > 2008-04-01T22:00:00+00:00 (book signing event date)
|
||||
BookSigning.objects.filter(pk=1).update(event_date=datetime.datetime(2008, 4, 1, 22, 0, tzinfo=timezone.utc))
|
||||
res = self.client.get('/dates/booksignings/2008/apr/2/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
# 2008-04-03T00:00:00+03:00 (end of day) > 2008-04-02T22:00:00+00:00 (book signing event date)
|
||||
BookSigning.objects.filter(pk=1).update(event_date=datetime.datetime(2008, 4, 2, 22, 0, tzinfo=timezone.utc))
|
||||
res = self.client.get('/dates/booksignings/2008/apr/2/')
|
||||
self.assertEqual(res.status_code, 404)
|
||||
|
||||
|
||||
class DateDetailViewTests(TestCase):
|
||||
fixtures = ['generic-views-test-data.json']
|
||||
urls = 'regressiontests.generic_views.urls'
|
||||
|
@ -441,3 +522,22 @@ class DateDetailViewTests(TestCase):
|
|||
res = self.client.get(
|
||||
'/dates/books/get_object_custom_queryset/2008/oct/01/1/')
|
||||
self.assertEqual(res.status_code, 404)
|
||||
|
||||
def test_datetime_date_detail(self):
|
||||
BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0))
|
||||
res = self.client.get('/dates/booksignings/2008/apr/2/1/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
@override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi')
|
||||
def test_aware_datetime_date_detail(self):
|
||||
BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc))
|
||||
res = self.client.get('/dates/booksignings/2008/apr/2/1/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
# 2008-04-02T00:00:00+03:00 (beginning of day) > 2008-04-01T22:00:00+00:00 (book signing event date)
|
||||
BookSigning.objects.filter(pk=1).update(event_date=datetime.datetime(2008, 4, 1, 22, 0, tzinfo=timezone.utc))
|
||||
res = self.client.get('/dates/booksignings/2008/apr/2/1/')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
# 2008-04-03T00:00:00+03:00 (end of day) > 2008-04-02T22:00:00+00:00 (book signing event date)
|
||||
BookSigning.objects.filter(pk=1).update(event_date=datetime.datetime(2008, 4, 2, 22, 0, tzinfo=timezone.utc))
|
||||
res = self.client.get('/dates/booksignings/2008/apr/2/1/')
|
||||
self.assertEqual(res.status_code, 404)
|
||||
|
|
|
@ -42,3 +42,6 @@ class Book(models.Model):
|
|||
class Page(models.Model):
|
||||
content = models.TextField()
|
||||
template = models.CharField(max_length=300)
|
||||
|
||||
class BookSigning(models.Model):
|
||||
event_date = models.DateTimeField()
|
||||
|
|
|
@ -108,6 +108,8 @@ urlpatterns = patterns('',
|
|||
views.BookArchive.as_view(queryset=None)),
|
||||
(r'^dates/books/paginated/$',
|
||||
views.BookArchive.as_view(paginate_by=10)),
|
||||
(r'^dates/booksignings/$',
|
||||
views.BookSigningArchive.as_view()),
|
||||
|
||||
# ListView
|
||||
(r'^list/dict/$',
|
||||
|
@ -156,6 +158,8 @@ urlpatterns = patterns('',
|
|||
views.BookYearArchive.as_view(make_object_list=True, paginate_by=30)),
|
||||
(r'^dates/books/no_year/$',
|
||||
views.BookYearArchive.as_view()),
|
||||
(r'^dates/booksignings/(?P<year>\d{4})/$',
|
||||
views.BookSigningYearArchive.as_view()),
|
||||
|
||||
# MonthArchiveView
|
||||
(r'^dates/books/(?P<year>\d{4})/(?P<month>[a-z]{3})/$',
|
||||
|
@ -170,6 +174,8 @@ urlpatterns = patterns('',
|
|||
views.BookMonthArchive.as_view(paginate_by=30)),
|
||||
(r'^dates/books/(?P<year>\d{4})/no_month/$',
|
||||
views.BookMonthArchive.as_view()),
|
||||
(r'^dates/booksignings/(?P<year>\d{4})/(?P<month>[a-z]{3})/$',
|
||||
views.BookSigningMonthArchive.as_view()),
|
||||
|
||||
# WeekArchiveView
|
||||
(r'^dates/books/(?P<year>\d{4})/week/(?P<week>\d{1,2})/$',
|
||||
|
@ -184,6 +190,8 @@ urlpatterns = patterns('',
|
|||
views.BookWeekArchive.as_view()),
|
||||
(r'^dates/books/(?P<year>\d{4})/week/(?P<week>\d{1,2})/monday/$',
|
||||
views.BookWeekArchive.as_view(week_format='%W')),
|
||||
(r'^dates/booksignings/(?P<year>\d{4})/week/(?P<week>\d{1,2})/$',
|
||||
views.BookSigningWeekArchive.as_view()),
|
||||
|
||||
# DayArchiveView
|
||||
(r'^dates/books/(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\d{1,2})/$',
|
||||
|
@ -198,12 +206,16 @@ urlpatterns = patterns('',
|
|||
views.BookDayArchive.as_view(paginate_by=True)),
|
||||
(r'^dates/books/(?P<year>\d{4})/(?P<month>[a-z]{3})/no_day/$',
|
||||
views.BookDayArchive.as_view()),
|
||||
(r'^dates/booksignings/(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\d{1,2})/$',
|
||||
views.BookSigningDayArchive.as_view()),
|
||||
|
||||
# TodayArchiveView
|
||||
(r'dates/books/today/$',
|
||||
(r'^dates/books/today/$',
|
||||
views.BookTodayArchive.as_view()),
|
||||
(r'dates/books/today/allow_empty/$',
|
||||
(r'^dates/books/today/allow_empty/$',
|
||||
views.BookTodayArchive.as_view(allow_empty=True)),
|
||||
(r'^dates/booksignings/today/$',
|
||||
views.BookSigningTodayArchive.as_view()),
|
||||
|
||||
# DateDetailView
|
||||
(r'^dates/books/(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\d{1,2})/(?P<pk>\d+)/$',
|
||||
|
@ -221,6 +233,9 @@ urlpatterns = patterns('',
|
|||
(r'^dates/books/get_object_custom_queryset/(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\d{1,2})/(?P<pk>\d+)/$',
|
||||
views.BookDetailGetObjectCustomQueryset.as_view()),
|
||||
|
||||
(r'^dates/booksignings/(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\d{1,2})/(?P<pk>\d+)/$',
|
||||
views.BookSigningDetail.as_view()),
|
||||
|
||||
# Useful for testing redirects
|
||||
(r'^accounts/login/$', 'django.contrib.auth.views.login')
|
||||
)
|
||||
|
|
|
@ -7,7 +7,7 @@ from django.utils.decorators import method_decorator
|
|||
from django.views import generic
|
||||
|
||||
from .forms import AuthorForm
|
||||
from .models import Artist, Author, Book, Page
|
||||
from .models import Artist, Author, Book, Page, BookSigning
|
||||
|
||||
|
||||
class CustomTemplateView(generic.TemplateView):
|
||||
|
@ -198,3 +198,31 @@ class CustomContextView(generic.detail.SingleObjectMixin, generic.View):
|
|||
|
||||
def get_context_object_name(self, obj):
|
||||
return "test_name"
|
||||
|
||||
class BookSigningConfig(object):
|
||||
model = BookSigning
|
||||
date_field = 'event_date'
|
||||
# use the same templates as for books
|
||||
def get_template_names(self):
|
||||
return ['generic_views/book%s.html' % self.template_name_suffix]
|
||||
|
||||
class BookSigningArchive(BookSigningConfig, generic.ArchiveIndexView):
|
||||
pass
|
||||
|
||||
class BookSigningYearArchive(BookSigningConfig, generic.YearArchiveView):
|
||||
pass
|
||||
|
||||
class BookSigningMonthArchive(BookSigningConfig, generic.MonthArchiveView):
|
||||
pass
|
||||
|
||||
class BookSigningWeekArchive(BookSigningConfig, generic.WeekArchiveView):
|
||||
pass
|
||||
|
||||
class BookSigningDayArchive(BookSigningConfig, generic.DayArchiveView):
|
||||
pass
|
||||
|
||||
class BookSigningTodayArchive(BookSigningConfig, generic.TodayArchiveView):
|
||||
pass
|
||||
|
||||
class BookSigningDetail(BookSigningConfig, generic.DateDetailView):
|
||||
context_object_name = 'book'
|
||||
|
|
Loading…
Reference in New Issue