|
38
|
1 |
import datetime |
|
|
2 |
import time |
|
|
3 |
|
|
|
4 |
from django.template import loader, RequestContext |
|
|
5 |
from django.core.exceptions import ObjectDoesNotExist |
|
|
6 |
from django.core.xheaders import populate_xheaders |
|
|
7 |
from django.db.models.fields import DateTimeField |
|
|
8 |
from django.http import Http404, HttpResponse |
|
|
9 |
|
|
|
10 |
def archive_index(request, queryset, date_field, num_latest=15, |
|
|
11 |
template_name=None, template_loader=loader, |
|
|
12 |
extra_context=None, allow_empty=True, context_processors=None, |
|
|
13 |
mimetype=None, allow_future=False, template_object_name='latest'): |
|
|
14 |
""" |
|
|
15 |
Generic top-level archive of date-based objects. |
|
|
16 |
|
|
|
17 |
Templates: ``<app_label>/<model_name>_archive.html`` |
|
|
18 |
Context: |
|
|
19 |
date_list |
|
|
20 |
List of years |
|
|
21 |
latest |
|
|
22 |
Latest N (defaults to 15) objects by date |
|
|
23 |
""" |
|
|
24 |
if extra_context is None: extra_context = {} |
|
|
25 |
model = queryset.model |
|
|
26 |
if not allow_future: |
|
|
27 |
queryset = queryset.filter(**{'%s__lte' % date_field: datetime.datetime.now()}) |
|
|
28 |
date_list = queryset.dates(date_field, 'year')[::-1] |
|
|
29 |
if not date_list and not allow_empty: |
|
|
30 |
raise Http404("No %s available" % model._meta.verbose_name) |
|
|
31 |
|
|
|
32 |
if date_list and num_latest: |
|
|
33 |
latest = queryset.order_by('-'+date_field)[:num_latest] |
|
|
34 |
else: |
|
|
35 |
latest = None |
|
|
36 |
|
|
|
37 |
if not template_name: |
|
|
38 |
template_name = "%s/%s_archive.html" % (model._meta.app_label, model._meta.object_name.lower()) |
|
|
39 |
t = template_loader.get_template(template_name) |
|
|
40 |
c = RequestContext(request, { |
|
|
41 |
'date_list' : date_list, |
|
|
42 |
template_object_name : latest, |
|
|
43 |
}, context_processors) |
|
|
44 |
for key, value in extra_context.items(): |
|
|
45 |
if callable(value): |
|
|
46 |
c[key] = value() |
|
|
47 |
else: |
|
|
48 |
c[key] = value |
|
|
49 |
return HttpResponse(t.render(c), mimetype=mimetype) |
|
|
50 |
|
|
|
51 |
def archive_year(request, year, queryset, date_field, template_name=None, |
|
|
52 |
template_loader=loader, extra_context=None, allow_empty=False, |
|
|
53 |
context_processors=None, template_object_name='object', mimetype=None, |
|
|
54 |
make_object_list=False, allow_future=False): |
|
|
55 |
""" |
|
|
56 |
Generic yearly archive view. |
|
|
57 |
|
|
|
58 |
Templates: ``<app_label>/<model_name>_archive_year.html`` |
|
|
59 |
Context: |
|
|
60 |
date_list |
|
|
61 |
List of months in this year with objects |
|
|
62 |
year |
|
|
63 |
This year |
|
|
64 |
object_list |
|
|
65 |
List of objects published in the given month |
|
|
66 |
(Only available if make_object_list argument is True) |
|
|
67 |
""" |
|
|
68 |
if extra_context is None: extra_context = {} |
|
|
69 |
model = queryset.model |
|
|
70 |
now = datetime.datetime.now() |
|
|
71 |
|
|
|
72 |
lookup_kwargs = {'%s__year' % date_field: year} |
|
|
73 |
|
|
|
74 |
# Only bother to check current date if the year isn't in the past and future objects aren't requested. |
|
|
75 |
if int(year) >= now.year and not allow_future: |
|
|
76 |
lookup_kwargs['%s__lte' % date_field] = now |
|
|
77 |
date_list = queryset.filter(**lookup_kwargs).dates(date_field, 'month') |
|
|
78 |
if not date_list and not allow_empty: |
|
|
79 |
raise Http404 |
|
|
80 |
if make_object_list: |
|
|
81 |
object_list = queryset.filter(**lookup_kwargs) |
|
|
82 |
else: |
|
|
83 |
object_list = [] |
|
|
84 |
if not template_name: |
|
|
85 |
template_name = "%s/%s_archive_year.html" % (model._meta.app_label, model._meta.object_name.lower()) |
|
|
86 |
t = template_loader.get_template(template_name) |
|
|
87 |
c = RequestContext(request, { |
|
|
88 |
'date_list': date_list, |
|
|
89 |
'year': year, |
|
|
90 |
'%s_list' % template_object_name: object_list, |
|
|
91 |
}, context_processors) |
|
|
92 |
for key, value in extra_context.items(): |
|
|
93 |
if callable(value): |
|
|
94 |
c[key] = value() |
|
|
95 |
else: |
|
|
96 |
c[key] = value |
|
|
97 |
return HttpResponse(t.render(c), mimetype=mimetype) |
|
|
98 |
|
|
|
99 |
def archive_month(request, year, month, queryset, date_field, |
|
|
100 |
month_format='%b', template_name=None, template_loader=loader, |
|
|
101 |
extra_context=None, allow_empty=False, context_processors=None, |
|
|
102 |
template_object_name='object', mimetype=None, allow_future=False): |
|
|
103 |
""" |
|
|
104 |
Generic monthly archive view. |
|
|
105 |
|
|
|
106 |
Templates: ``<app_label>/<model_name>_archive_month.html`` |
|
|
107 |
Context: |
|
|
108 |
date_list: |
|
|
109 |
List of days in this month with objects |
|
|
110 |
month: |
|
|
111 |
(date) this month |
|
|
112 |
next_month: |
|
|
113 |
(date) the first day of the next month, or None if the next month is in the future |
|
|
114 |
previous_month: |
|
|
115 |
(date) the first day of the previous month |
|
|
116 |
object_list: |
|
|
117 |
list of objects published in the given month |
|
|
118 |
""" |
|
|
119 |
if extra_context is None: extra_context = {} |
|
|
120 |
try: |
|
|
121 |
tt = time.strptime("%s-%s" % (year, month), '%s-%s' % ('%Y', month_format)) |
|
|
122 |
date = datetime.date(*tt[:3]) |
|
|
123 |
except ValueError: |
|
|
124 |
raise Http404 |
|
|
125 |
|
|
|
126 |
model = queryset.model |
|
|
127 |
now = datetime.datetime.now() |
|
|
128 |
|
|
|
129 |
# Calculate first and last day of month, for use in a date-range lookup. |
|
|
130 |
first_day = date.replace(day=1) |
|
|
131 |
if first_day.month == 12: |
|
|
132 |
last_day = first_day.replace(year=first_day.year + 1, month=1) |
|
|
133 |
else: |
|
|
134 |
last_day = first_day.replace(month=first_day.month + 1) |
|
|
135 |
lookup_kwargs = { |
|
|
136 |
'%s__gte' % date_field: first_day, |
|
|
137 |
'%s__lt' % date_field: last_day, |
|
|
138 |
} |
|
|
139 |
|
|
|
140 |
# Only bother to check current date if the month isn't in the past and future objects are requested. |
|
|
141 |
if last_day >= now.date() and not allow_future: |
|
|
142 |
lookup_kwargs['%s__lte' % date_field] = now |
|
|
143 |
object_list = queryset.filter(**lookup_kwargs) |
|
|
144 |
date_list = object_list.dates(date_field, 'day') |
|
|
145 |
if not object_list and not allow_empty: |
|
|
146 |
raise Http404 |
|
|
147 |
|
|
|
148 |
# Calculate the next month, if applicable. |
|
|
149 |
if allow_future: |
|
|
150 |
next_month = last_day |
|
|
151 |
elif last_day <= datetime.date.today(): |
|
|
152 |
next_month = last_day |
|
|
153 |
else: |
|
|
154 |
next_month = None |
|
|
155 |
|
|
|
156 |
# Calculate the previous month |
|
|
157 |
if first_day.month == 1: |
|
|
158 |
previous_month = first_day.replace(year=first_day.year-1,month=12) |
|
|
159 |
else: |
|
|
160 |
previous_month = first_day.replace(month=first_day.month-1) |
|
|
161 |
|
|
|
162 |
if not template_name: |
|
|
163 |
template_name = "%s/%s_archive_month.html" % (model._meta.app_label, model._meta.object_name.lower()) |
|
|
164 |
t = template_loader.get_template(template_name) |
|
|
165 |
c = RequestContext(request, { |
|
|
166 |
'date_list': date_list, |
|
|
167 |
'%s_list' % template_object_name: object_list, |
|
|
168 |
'month': date, |
|
|
169 |
'next_month': next_month, |
|
|
170 |
'previous_month': previous_month, |
|
|
171 |
}, context_processors) |
|
|
172 |
for key, value in extra_context.items(): |
|
|
173 |
if callable(value): |
|
|
174 |
c[key] = value() |
|
|
175 |
else: |
|
|
176 |
c[key] = value |
|
|
177 |
return HttpResponse(t.render(c), mimetype=mimetype) |
|
|
178 |
|
|
|
179 |
def archive_week(request, year, week, queryset, date_field, |
|
|
180 |
template_name=None, template_loader=loader, |
|
|
181 |
extra_context=None, allow_empty=True, context_processors=None, |
|
|
182 |
template_object_name='object', mimetype=None, allow_future=False): |
|
|
183 |
""" |
|
|
184 |
Generic weekly archive view. |
|
|
185 |
|
|
|
186 |
Templates: ``<app_label>/<model_name>_archive_week.html`` |
|
|
187 |
Context: |
|
|
188 |
week: |
|
|
189 |
(date) this week |
|
|
190 |
object_list: |
|
|
191 |
list of objects published in the given week |
|
|
192 |
""" |
|
|
193 |
if extra_context is None: extra_context = {} |
|
|
194 |
try: |
|
|
195 |
tt = time.strptime(year+'-0-'+week, '%Y-%w-%U') |
|
|
196 |
date = datetime.date(*tt[:3]) |
|
|
197 |
except ValueError: |
|
|
198 |
raise Http404 |
|
|
199 |
|
|
|
200 |
model = queryset.model |
|
|
201 |
now = datetime.datetime.now() |
|
|
202 |
|
|
|
203 |
# Calculate first and last day of week, for use in a date-range lookup. |
|
|
204 |
first_day = date |
|
|
205 |
last_day = date + datetime.timedelta(days=7) |
|
|
206 |
lookup_kwargs = { |
|
|
207 |
'%s__gte' % date_field: first_day, |
|
|
208 |
'%s__lt' % date_field: last_day, |
|
|
209 |
} |
|
|
210 |
|
|
|
211 |
# Only bother to check current date if the week isn't in the past and future objects aren't requested. |
|
|
212 |
if last_day >= now.date() and not allow_future: |
|
|
213 |
lookup_kwargs['%s__lte' % date_field] = now |
|
|
214 |
object_list = queryset.filter(**lookup_kwargs) |
|
|
215 |
if not object_list and not allow_empty: |
|
|
216 |
raise Http404 |
|
|
217 |
if not template_name: |
|
|
218 |
template_name = "%s/%s_archive_week.html" % (model._meta.app_label, model._meta.object_name.lower()) |
|
|
219 |
t = template_loader.get_template(template_name) |
|
|
220 |
c = RequestContext(request, { |
|
|
221 |
'%s_list' % template_object_name: object_list, |
|
|
222 |
'week': date, |
|
|
223 |
}) |
|
|
224 |
for key, value in extra_context.items(): |
|
|
225 |
if callable(value): |
|
|
226 |
c[key] = value() |
|
|
227 |
else: |
|
|
228 |
c[key] = value |
|
|
229 |
return HttpResponse(t.render(c), mimetype=mimetype) |
|
|
230 |
|
|
|
231 |
def archive_day(request, year, month, day, queryset, date_field, |
|
|
232 |
month_format='%b', day_format='%d', template_name=None, |
|
|
233 |
template_loader=loader, extra_context=None, allow_empty=False, |
|
|
234 |
context_processors=None, template_object_name='object', |
|
|
235 |
mimetype=None, allow_future=False): |
|
|
236 |
""" |
|
|
237 |
Generic daily archive view. |
|
|
238 |
|
|
|
239 |
Templates: ``<app_label>/<model_name>_archive_day.html`` |
|
|
240 |
Context: |
|
|
241 |
object_list: |
|
|
242 |
list of objects published that day |
|
|
243 |
day: |
|
|
244 |
(datetime) the day |
|
|
245 |
previous_day |
|
|
246 |
(datetime) the previous day |
|
|
247 |
next_day |
|
|
248 |
(datetime) the next day, or None if the current day is today |
|
|
249 |
""" |
|
|
250 |
if extra_context is None: extra_context = {} |
|
|
251 |
try: |
|
|
252 |
tt = time.strptime('%s-%s-%s' % (year, month, day), |
|
|
253 |
'%s-%s-%s' % ('%Y', month_format, day_format)) |
|
|
254 |
date = datetime.date(*tt[:3]) |
|
|
255 |
except ValueError: |
|
|
256 |
raise Http404 |
|
|
257 |
|
|
|
258 |
model = queryset.model |
|
|
259 |
now = datetime.datetime.now() |
|
|
260 |
|
|
|
261 |
if isinstance(model._meta.get_field(date_field), DateTimeField): |
|
|
262 |
lookup_kwargs = {'%s__range' % date_field: (datetime.datetime.combine(date, datetime.time.min), datetime.datetime.combine(date, datetime.time.max))} |
|
|
263 |
else: |
|
|
264 |
lookup_kwargs = {date_field: date} |
|
|
265 |
|
|
|
266 |
# Only bother to check current date if the date isn't in the past and future objects aren't requested. |
|
|
267 |
if date >= now.date() and not allow_future: |
|
|
268 |
lookup_kwargs['%s__lte' % date_field] = now |
|
|
269 |
object_list = queryset.filter(**lookup_kwargs) |
|
|
270 |
if not allow_empty and not object_list: |
|
|
271 |
raise Http404 |
|
|
272 |
|
|
|
273 |
# Calculate the next day, if applicable. |
|
|
274 |
if allow_future: |
|
|
275 |
next_day = date + datetime.timedelta(days=1) |
|
|
276 |
elif date < datetime.date.today(): |
|
|
277 |
next_day = date + datetime.timedelta(days=1) |
|
|
278 |
else: |
|
|
279 |
next_day = None |
|
|
280 |
|
|
|
281 |
if not template_name: |
|
|
282 |
template_name = "%s/%s_archive_day.html" % (model._meta.app_label, model._meta.object_name.lower()) |
|
|
283 |
t = template_loader.get_template(template_name) |
|
|
284 |
c = RequestContext(request, { |
|
|
285 |
'%s_list' % template_object_name: object_list, |
|
|
286 |
'day': date, |
|
|
287 |
'previous_day': date - datetime.timedelta(days=1), |
|
|
288 |
'next_day': next_day, |
|
|
289 |
}, context_processors) |
|
|
290 |
for key, value in extra_context.items(): |
|
|
291 |
if callable(value): |
|
|
292 |
c[key] = value() |
|
|
293 |
else: |
|
|
294 |
c[key] = value |
|
|
295 |
return HttpResponse(t.render(c), mimetype=mimetype) |
|
|
296 |
|
|
|
297 |
def archive_today(request, **kwargs): |
|
|
298 |
""" |
|
|
299 |
Generic daily archive view for today. Same as archive_day view. |
|
|
300 |
""" |
|
|
301 |
today = datetime.date.today() |
|
|
302 |
kwargs.update({ |
|
|
303 |
'year': str(today.year), |
|
|
304 |
'month': today.strftime('%b').lower(), |
|
|
305 |
'day': str(today.day), |
|
|
306 |
}) |
|
|
307 |
return archive_day(request, **kwargs) |
|
|
308 |
|
|
|
309 |
def object_detail(request, year, month, day, queryset, date_field, |
|
|
310 |
month_format='%b', day_format='%d', object_id=None, slug=None, |
|
|
311 |
slug_field='slug', template_name=None, template_name_field=None, |
|
|
312 |
template_loader=loader, extra_context=None, context_processors=None, |
|
|
313 |
template_object_name='object', mimetype=None, allow_future=False): |
|
|
314 |
""" |
|
|
315 |
Generic detail view from year/month/day/slug or year/month/day/id structure. |
|
|
316 |
|
|
|
317 |
Templates: ``<app_label>/<model_name>_detail.html`` |
|
|
318 |
Context: |
|
|
319 |
object: |
|
|
320 |
the object to be detailed |
|
|
321 |
""" |
|
|
322 |
if extra_context is None: extra_context = {} |
|
|
323 |
try: |
|
|
324 |
tt = time.strptime('%s-%s-%s' % (year, month, day), |
|
|
325 |
'%s-%s-%s' % ('%Y', month_format, day_format)) |
|
|
326 |
date = datetime.date(*tt[:3]) |
|
|
327 |
except ValueError: |
|
|
328 |
raise Http404 |
|
|
329 |
|
|
|
330 |
model = queryset.model |
|
|
331 |
now = datetime.datetime.now() |
|
|
332 |
|
|
|
333 |
if isinstance(model._meta.get_field(date_field), DateTimeField): |
|
|
334 |
lookup_kwargs = {'%s__range' % date_field: (datetime.datetime.combine(date, datetime.time.min), datetime.datetime.combine(date, datetime.time.max))} |
|
|
335 |
else: |
|
|
336 |
lookup_kwargs = {date_field: date} |
|
|
337 |
|
|
|
338 |
# Only bother to check current date if the date isn't in the past and future objects aren't requested. |
|
|
339 |
if date >= now.date() and not allow_future: |
|
|
340 |
lookup_kwargs['%s__lte' % date_field] = now |
|
|
341 |
if object_id: |
|
|
342 |
lookup_kwargs['%s__exact' % model._meta.pk.name] = object_id |
|
|
343 |
elif slug and slug_field: |
|
|
344 |
lookup_kwargs['%s__exact' % slug_field] = slug |
|
|
345 |
else: |
|
|
346 |
raise AttributeError("Generic detail view must be called with either an object_id or a slug/slugfield") |
|
|
347 |
try: |
|
|
348 |
obj = queryset.get(**lookup_kwargs) |
|
|
349 |
except ObjectDoesNotExist: |
|
|
350 |
raise Http404("No %s found for" % model._meta.verbose_name) |
|
|
351 |
if not template_name: |
|
|
352 |
template_name = "%s/%s_detail.html" % (model._meta.app_label, model._meta.object_name.lower()) |
|
|
353 |
if template_name_field: |
|
|
354 |
template_name_list = [getattr(obj, template_name_field), template_name] |
|
|
355 |
t = template_loader.select_template(template_name_list) |
|
|
356 |
else: |
|
|
357 |
t = template_loader.get_template(template_name) |
|
|
358 |
c = RequestContext(request, { |
|
|
359 |
template_object_name: obj, |
|
|
360 |
}, context_processors) |
|
|
361 |
for key, value in extra_context.items(): |
|
|
362 |
if callable(value): |
|
|
363 |
c[key] = value() |
|
|
364 |
else: |
|
|
365 |
c[key] = value |
|
|
366 |
response = HttpResponse(t.render(c), mimetype=mimetype) |
|
|
367 |
populate_xheaders(request, response, model, getattr(obj, obj._meta.pk.name)) |
|
|
368 |
return response |