Remove function based views
parent
25daa0e73b
commit
513fd1779d
|
@ -1,12 +0,0 @@
|
|||
from django.views.generic.base import View, TemplateView, RedirectView
|
||||
from django.views.generic.dates import (ArchiveIndexView, YearArchiveView, MonthArchiveView,
|
||||
WeekArchiveView, DayArchiveView, TodayArchiveView,
|
||||
DateDetailView)
|
||||
from django.views.generic.detail import DetailView
|
||||
from django.views.generic.edit import FormView, CreateView, UpdateView, DeleteView
|
||||
from django.views.generic.list import ListView
|
||||
|
||||
|
||||
class GenericViewError(Exception):
|
||||
"""A problem in a generic view."""
|
||||
pass
|
|
@ -1,178 +0,0 @@
|
|||
from functools import update_wrapper
|
||||
from django import http
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils.log import getLogger
|
||||
from django.utils.decorators import classonlymethod
|
||||
|
||||
logger = getLogger('django.request')
|
||||
|
||||
|
||||
class View(object):
|
||||
"""
|
||||
Intentionally simple parent class for all views. Only implements
|
||||
dispatch-by-method and simple sanity checking.
|
||||
"""
|
||||
|
||||
http_method_names = ['get', 'post', 'put', 'delete', 'head', 'options', 'trace']
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
"""
|
||||
Constructor. Called in the URLconf; can contain helpful extra
|
||||
keyword arguments, and other things.
|
||||
"""
|
||||
# Go through keyword arguments, and either save their values to our
|
||||
# instance, or raise an error.
|
||||
for key, value in kwargs.iteritems():
|
||||
setattr(self, key, value)
|
||||
|
||||
@classonlymethod
|
||||
def as_view(cls, **initkwargs):
|
||||
"""
|
||||
Main entry point for a request-response process.
|
||||
"""
|
||||
# sanitize keyword arguments
|
||||
for key in initkwargs:
|
||||
if key in cls.http_method_names:
|
||||
raise TypeError(u"You tried to pass in the %s method name as a "
|
||||
u"keyword argument to %s(). Don't do that."
|
||||
% (key, cls.__name__))
|
||||
if not hasattr(cls, key):
|
||||
raise TypeError(u"%s() received an invalid keyword %r" % (
|
||||
cls.__name__, key))
|
||||
|
||||
def view(request, *args, **kwargs):
|
||||
self = cls(**initkwargs)
|
||||
if hasattr(self, 'get') and not hasattr(self, 'head'):
|
||||
self.head = self.get
|
||||
return self.dispatch(request, *args, **kwargs)
|
||||
|
||||
# take name and docstring from class
|
||||
update_wrapper(view, cls, updated=())
|
||||
|
||||
# and possible attributes set by decorators
|
||||
# like csrf_exempt from dispatch
|
||||
update_wrapper(view, cls.dispatch, assigned=())
|
||||
return view
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
# Try to dispatch to the right method; if a method doesn't exist,
|
||||
# defer to the error handler. Also defer to the error handler if the
|
||||
# request method isn't on the approved list.
|
||||
if request.method.lower() in self.http_method_names:
|
||||
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
|
||||
else:
|
||||
handler = self.http_method_not_allowed
|
||||
self.request = request
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
return handler(request, *args, **kwargs)
|
||||
|
||||
def http_method_not_allowed(self, request, *args, **kwargs):
|
||||
allowed_methods = [m for m in self.http_method_names if hasattr(self, m)]
|
||||
logger.warning('Method Not Allowed (%s): %s', request.method, request.path,
|
||||
extra={
|
||||
'status_code': 405,
|
||||
'request': self.request
|
||||
}
|
||||
)
|
||||
return http.HttpResponseNotAllowed(allowed_methods)
|
||||
|
||||
|
||||
class TemplateResponseMixin(object):
|
||||
"""
|
||||
A mixin that can be used to render a template.
|
||||
"""
|
||||
template_name = None
|
||||
response_class = TemplateResponse
|
||||
|
||||
def render_to_response(self, context, **response_kwargs):
|
||||
"""
|
||||
Returns a response with a template rendered with the given context.
|
||||
"""
|
||||
return self.response_class(
|
||||
request = self.request,
|
||||
template = self.get_template_names(),
|
||||
context = context,
|
||||
**response_kwargs
|
||||
)
|
||||
|
||||
def get_template_names(self):
|
||||
"""
|
||||
Returns a list of template names to be used for the request. Must return
|
||||
a list. May not be called if render_to_response is overridden.
|
||||
"""
|
||||
if self.template_name is None:
|
||||
raise ImproperlyConfigured(
|
||||
"TemplateResponseMixin requires either a definition of "
|
||||
"'template_name' or an implementation of 'get_template_names()'")
|
||||
else:
|
||||
return [self.template_name]
|
||||
|
||||
|
||||
class TemplateView(TemplateResponseMixin, View):
|
||||
"""
|
||||
A view that renders a template.
|
||||
"""
|
||||
def get_context_data(self, **kwargs):
|
||||
return {
|
||||
'params': kwargs
|
||||
}
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
context = self.get_context_data(**kwargs)
|
||||
return self.render_to_response(context)
|
||||
|
||||
|
||||
class RedirectView(View):
|
||||
"""
|
||||
A view that provides a redirect on any GET request.
|
||||
"""
|
||||
permanent = True
|
||||
url = None
|
||||
query_string = False
|
||||
|
||||
def get_redirect_url(self, **kwargs):
|
||||
"""
|
||||
Return the URL redirect to. Keyword arguments from the
|
||||
URL pattern match generating the redirect request
|
||||
are provided as kwargs to this method.
|
||||
"""
|
||||
if self.url:
|
||||
url = self.url % kwargs
|
||||
args = self.request.META.get('QUERY_STRING', '')
|
||||
if args and self.query_string:
|
||||
url = "%s?%s" % (url, args)
|
||||
return url
|
||||
else:
|
||||
return None
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
url = self.get_redirect_url(**kwargs)
|
||||
if url:
|
||||
if self.permanent:
|
||||
return http.HttpResponsePermanentRedirect(url)
|
||||
else:
|
||||
return http.HttpResponseRedirect(url)
|
||||
else:
|
||||
logger.warning('Gone: %s', self.request.path,
|
||||
extra={
|
||||
'status_code': 410,
|
||||
'request': self.request
|
||||
})
|
||||
return http.HttpResponseGone()
|
||||
|
||||
def head(self, request, *args, **kwargs):
|
||||
return self.get(request, *args, **kwargs)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
return self.get(request, *args, **kwargs)
|
||||
|
||||
def options(self, request, *args, **kwargs):
|
||||
return self.get(request, *args, **kwargs)
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
return self.get(request, *args, **kwargs)
|
||||
|
||||
def put(self, request, *args, **kwargs):
|
||||
return self.get(request, *args, **kwargs)
|
|
@ -1,221 +0,0 @@
|
|||
from django.forms.models import ModelFormMetaclass, ModelForm
|
||||
from django.template import RequestContext, loader
|
||||
from django.http import Http404, HttpResponse, HttpResponseRedirect
|
||||
from django.core.xheaders import populate_xheaders
|
||||
from django.core.exceptions import ObjectDoesNotExist, ImproperlyConfigured
|
||||
from django.utils.translation import ugettext
|
||||
from django.contrib.auth.views import redirect_to_login
|
||||
from django.views.generic import GenericViewError
|
||||
from django.contrib import messages
|
||||
|
||||
import warnings
|
||||
warnings.warn(
|
||||
'Function-based generic views have been deprecated; use class-based views instead.',
|
||||
DeprecationWarning
|
||||
)
|
||||
|
||||
|
||||
def apply_extra_context(extra_context, context):
|
||||
"""
|
||||
Adds items from extra_context dict to context. If a value in extra_context
|
||||
is callable, then it is called and the result is added to context.
|
||||
"""
|
||||
for key, value in extra_context.iteritems():
|
||||
if callable(value):
|
||||
context[key] = value()
|
||||
else:
|
||||
context[key] = value
|
||||
|
||||
def get_model_and_form_class(model, form_class):
|
||||
"""
|
||||
Returns a model and form class based on the model and form_class
|
||||
parameters that were passed to the generic view.
|
||||
|
||||
If ``form_class`` is given then its associated model will be returned along
|
||||
with ``form_class`` itself. Otherwise, if ``model`` is given, ``model``
|
||||
itself will be returned along with a ``ModelForm`` class created from
|
||||
``model``.
|
||||
"""
|
||||
if form_class:
|
||||
return form_class._meta.model, form_class
|
||||
if model:
|
||||
# The inner Meta class fails if model = model is used for some reason.
|
||||
tmp_model = model
|
||||
# TODO: we should be able to construct a ModelForm without creating
|
||||
# and passing in a temporary inner class.
|
||||
class Meta:
|
||||
model = tmp_model
|
||||
class_name = model.__name__ + 'Form'
|
||||
form_class = ModelFormMetaclass(class_name, (ModelForm,), {'Meta': Meta})
|
||||
return model, form_class
|
||||
raise GenericViewError("Generic view must be called with either a model or"
|
||||
" form_class argument.")
|
||||
|
||||
def redirect(post_save_redirect, obj):
|
||||
"""
|
||||
Returns a HttpResponseRedirect to ``post_save_redirect``.
|
||||
|
||||
``post_save_redirect`` should be a string, and can contain named string-
|
||||
substitution place holders of ``obj`` field names.
|
||||
|
||||
If ``post_save_redirect`` is None, then redirect to ``obj``'s URL returned
|
||||
by ``get_absolute_url()``. If ``obj`` has no ``get_absolute_url`` method,
|
||||
then raise ImproperlyConfigured.
|
||||
|
||||
This function is meant to handle the post_save_redirect parameter to the
|
||||
``create_object`` and ``update_object`` views.
|
||||
"""
|
||||
if post_save_redirect:
|
||||
return HttpResponseRedirect(post_save_redirect % obj.__dict__)
|
||||
elif hasattr(obj, 'get_absolute_url'):
|
||||
return HttpResponseRedirect(obj.get_absolute_url())
|
||||
else:
|
||||
raise ImproperlyConfigured(
|
||||
"No URL to redirect to. Either pass a post_save_redirect"
|
||||
" parameter to the generic view or define a get_absolute_url"
|
||||
" method on the Model.")
|
||||
|
||||
def lookup_object(model, object_id, slug, slug_field):
|
||||
"""
|
||||
Return the ``model`` object with the passed ``object_id``. If
|
||||
``object_id`` is None, then return the object whose ``slug_field``
|
||||
equals the passed ``slug``. If ``slug`` and ``slug_field`` are not passed,
|
||||
then raise Http404 exception.
|
||||
"""
|
||||
lookup_kwargs = {}
|
||||
if object_id:
|
||||
lookup_kwargs['%s__exact' % model._meta.pk.name] = object_id
|
||||
elif slug and slug_field:
|
||||
lookup_kwargs['%s__exact' % slug_field] = slug
|
||||
else:
|
||||
raise GenericViewError(
|
||||
"Generic view must be called with either an object_id or a"
|
||||
" slug/slug_field.")
|
||||
try:
|
||||
return model.objects.get(**lookup_kwargs)
|
||||
except ObjectDoesNotExist:
|
||||
raise Http404("No %s found for %s"
|
||||
% (model._meta.verbose_name, lookup_kwargs))
|
||||
|
||||
def create_object(request, model=None, template_name=None,
|
||||
template_loader=loader, extra_context=None, post_save_redirect=None,
|
||||
login_required=False, context_processors=None, form_class=None):
|
||||
"""
|
||||
Generic object-creation function.
|
||||
|
||||
Templates: ``<app_label>/<model_name>_form.html``
|
||||
Context:
|
||||
form
|
||||
the form for the object
|
||||
"""
|
||||
if extra_context is None: extra_context = {}
|
||||
if login_required and not request.user.is_authenticated():
|
||||
return redirect_to_login(request.path)
|
||||
|
||||
model, form_class = get_model_and_form_class(model, form_class)
|
||||
if request.method == 'POST':
|
||||
form = form_class(request.POST, request.FILES)
|
||||
if form.is_valid():
|
||||
new_object = form.save()
|
||||
|
||||
msg = ugettext("The %(verbose_name)s was created successfully.") %\
|
||||
{"verbose_name": model._meta.verbose_name}
|
||||
messages.success(request, msg, fail_silently=True)
|
||||
return redirect(post_save_redirect, new_object)
|
||||
else:
|
||||
form = form_class()
|
||||
|
||||
# Create the template, context, response
|
||||
if not template_name:
|
||||
template_name = "%s/%s_form.html" % (model._meta.app_label, model._meta.object_name.lower())
|
||||
t = template_loader.get_template(template_name)
|
||||
c = RequestContext(request, {
|
||||
'form': form,
|
||||
}, context_processors)
|
||||
apply_extra_context(extra_context, c)
|
||||
return HttpResponse(t.render(c))
|
||||
|
||||
def update_object(request, model=None, object_id=None, slug=None,
|
||||
slug_field='slug', template_name=None, template_loader=loader,
|
||||
extra_context=None, post_save_redirect=None, login_required=False,
|
||||
context_processors=None, template_object_name='object',
|
||||
form_class=None):
|
||||
"""
|
||||
Generic object-update function.
|
||||
|
||||
Templates: ``<app_label>/<model_name>_form.html``
|
||||
Context:
|
||||
form
|
||||
the form for the object
|
||||
object
|
||||
the original object being edited
|
||||
"""
|
||||
if extra_context is None: extra_context = {}
|
||||
if login_required and not request.user.is_authenticated():
|
||||
return redirect_to_login(request.path)
|
||||
|
||||
model, form_class = get_model_and_form_class(model, form_class)
|
||||
obj = lookup_object(model, object_id, slug, slug_field)
|
||||
|
||||
if request.method == 'POST':
|
||||
form = form_class(request.POST, request.FILES, instance=obj)
|
||||
if form.is_valid():
|
||||
obj = form.save()
|
||||
msg = ugettext("The %(verbose_name)s was updated successfully.") %\
|
||||
{"verbose_name": model._meta.verbose_name}
|
||||
messages.success(request, msg, fail_silently=True)
|
||||
return redirect(post_save_redirect, obj)
|
||||
else:
|
||||
form = form_class(instance=obj)
|
||||
|
||||
if not template_name:
|
||||
template_name = "%s/%s_form.html" % (model._meta.app_label, model._meta.object_name.lower())
|
||||
t = template_loader.get_template(template_name)
|
||||
c = RequestContext(request, {
|
||||
'form': form,
|
||||
template_object_name: obj,
|
||||
}, context_processors)
|
||||
apply_extra_context(extra_context, c)
|
||||
response = HttpResponse(t.render(c))
|
||||
populate_xheaders(request, response, model, getattr(obj, obj._meta.pk.attname))
|
||||
return response
|
||||
|
||||
def delete_object(request, model, post_delete_redirect, object_id=None,
|
||||
slug=None, slug_field='slug', template_name=None,
|
||||
template_loader=loader, extra_context=None, login_required=False,
|
||||
context_processors=None, template_object_name='object'):
|
||||
"""
|
||||
Generic object-delete function.
|
||||
|
||||
The given template will be used to confirm deletetion if this view is
|
||||
fetched using GET; for safty, deletion will only be performed if this
|
||||
view is POSTed.
|
||||
|
||||
Templates: ``<app_label>/<model_name>_confirm_delete.html``
|
||||
Context:
|
||||
object
|
||||
the original object being deleted
|
||||
"""
|
||||
if extra_context is None: extra_context = {}
|
||||
if login_required and not request.user.is_authenticated():
|
||||
return redirect_to_login(request.path)
|
||||
|
||||
obj = lookup_object(model, object_id, slug, slug_field)
|
||||
|
||||
if request.method == 'POST':
|
||||
obj.delete()
|
||||
msg = ugettext("The %(verbose_name)s was deleted.") %\
|
||||
{"verbose_name": model._meta.verbose_name}
|
||||
messages.success(request, msg, fail_silently=True)
|
||||
return HttpResponseRedirect(post_delete_redirect)
|
||||
else:
|
||||
if not template_name:
|
||||
template_name = "%s/%s_confirm_delete.html" % (model._meta.app_label, model._meta.object_name.lower())
|
||||
t = template_loader.get_template(template_name)
|
||||
c = RequestContext(request, {
|
||||
template_object_name: obj,
|
||||
}, context_processors)
|
||||
apply_extra_context(extra_context, c)
|
||||
response = HttpResponse(t.render(c))
|
||||
populate_xheaders(request, response, model, getattr(obj, obj._meta.pk.attname))
|
||||
return response
|
|
@ -1,376 +0,0 @@
|
|||
import datetime
|
||||
import time
|
||||
|
||||
from django.template import loader, RequestContext
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.core.xheaders import populate_xheaders
|
||||
from django.db.models.fields import DateTimeField
|
||||
from django.http import Http404, HttpResponse
|
||||
from django.utils import timezone
|
||||
|
||||
import warnings
|
||||
warnings.warn(
|
||||
'Function-based generic views have been deprecated; use class-based views instead.',
|
||||
DeprecationWarning
|
||||
)
|
||||
|
||||
|
||||
def archive_index(request, queryset, date_field, num_latest=15,
|
||||
template_name=None, template_loader=loader,
|
||||
extra_context=None, allow_empty=True, context_processors=None,
|
||||
mimetype=None, allow_future=False, template_object_name='latest'):
|
||||
"""
|
||||
Generic top-level archive of date-based objects.
|
||||
|
||||
Templates: ``<app_label>/<model_name>_archive.html``
|
||||
Context:
|
||||
date_list
|
||||
List of years
|
||||
latest
|
||||
Latest N (defaults to 15) objects by date
|
||||
"""
|
||||
if extra_context is None: extra_context = {}
|
||||
model = queryset.model
|
||||
if not allow_future:
|
||||
queryset = queryset.filter(**{'%s__lte' % date_field: timezone.now()})
|
||||
date_list = queryset.dates(date_field, 'year')[::-1]
|
||||
if not date_list and not allow_empty:
|
||||
raise Http404("No %s available" % model._meta.verbose_name)
|
||||
|
||||
if date_list and num_latest:
|
||||
latest = queryset.order_by('-'+date_field)[:num_latest]
|
||||
else:
|
||||
latest = None
|
||||
|
||||
if not template_name:
|
||||
template_name = "%s/%s_archive.html" % (model._meta.app_label, model._meta.object_name.lower())
|
||||
t = template_loader.get_template(template_name)
|
||||
c = RequestContext(request, {
|
||||
'date_list' : date_list,
|
||||
template_object_name : latest,
|
||||
}, context_processors)
|
||||
for key, value in extra_context.items():
|
||||
if callable(value):
|
||||
c[key] = value()
|
||||
else:
|
||||
c[key] = value
|
||||
return HttpResponse(t.render(c), mimetype=mimetype)
|
||||
|
||||
def archive_year(request, year, queryset, date_field, template_name=None,
|
||||
template_loader=loader, extra_context=None, allow_empty=False,
|
||||
context_processors=None, template_object_name='object', mimetype=None,
|
||||
make_object_list=False, allow_future=False):
|
||||
"""
|
||||
Generic yearly archive view.
|
||||
|
||||
Templates: ``<app_label>/<model_name>_archive_year.html``
|
||||
Context:
|
||||
date_list
|
||||
List of months in this year with objects
|
||||
year
|
||||
This year
|
||||
object_list
|
||||
List of objects published in the given month
|
||||
(Only available if make_object_list argument is True)
|
||||
"""
|
||||
if extra_context is None: extra_context = {}
|
||||
model = queryset.model
|
||||
now = timezone.now()
|
||||
|
||||
lookup_kwargs = {'%s__year' % date_field: year}
|
||||
|
||||
# Only bother to check current date if the year isn't in the past and future objects aren't requested.
|
||||
if int(year) >= now.year and not allow_future:
|
||||
lookup_kwargs['%s__lte' % date_field] = now
|
||||
date_list = queryset.filter(**lookup_kwargs).dates(date_field, 'month')
|
||||
if not date_list and not allow_empty:
|
||||
raise Http404
|
||||
if make_object_list:
|
||||
object_list = queryset.filter(**lookup_kwargs)
|
||||
else:
|
||||
object_list = []
|
||||
if not template_name:
|
||||
template_name = "%s/%s_archive_year.html" % (model._meta.app_label, model._meta.object_name.lower())
|
||||
t = template_loader.get_template(template_name)
|
||||
c = RequestContext(request, {
|
||||
'date_list': date_list,
|
||||
'year': year,
|
||||
'%s_list' % template_object_name: object_list,
|
||||
}, context_processors)
|
||||
for key, value in extra_context.items():
|
||||
if callable(value):
|
||||
c[key] = value()
|
||||
else:
|
||||
c[key] = value
|
||||
return HttpResponse(t.render(c), mimetype=mimetype)
|
||||
|
||||
def archive_month(request, year, month, queryset, date_field,
|
||||
month_format='%b', template_name=None, template_loader=loader,
|
||||
extra_context=None, allow_empty=False, context_processors=None,
|
||||
template_object_name='object', mimetype=None, allow_future=False):
|
||||
"""
|
||||
Generic monthly archive view.
|
||||
|
||||
Templates: ``<app_label>/<model_name>_archive_month.html``
|
||||
Context:
|
||||
date_list:
|
||||
List of days in this month with objects
|
||||
month:
|
||||
(date) this month
|
||||
next_month:
|
||||
(date) the first day of the next month, or None if the next month is in the future
|
||||
previous_month:
|
||||
(date) the first day of the previous month
|
||||
object_list:
|
||||
list of objects published in the given month
|
||||
"""
|
||||
if extra_context is None: extra_context = {}
|
||||
try:
|
||||
tt = time.strptime("%s-%s" % (year, month), '%s-%s' % ('%Y', month_format))
|
||||
date = datetime.date(*tt[:3])
|
||||
except ValueError:
|
||||
raise Http404
|
||||
|
||||
model = queryset.model
|
||||
now = timezone.now()
|
||||
|
||||
# Calculate first and last day of month, for use in 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)
|
||||
else:
|
||||
last_day = first_day.replace(month=first_day.month + 1)
|
||||
lookup_kwargs = {
|
||||
'%s__gte' % date_field: first_day,
|
||||
'%s__lt' % date_field: last_day,
|
||||
}
|
||||
|
||||
# Only bother to check current date if the month isn't in the past and future objects are requested.
|
||||
if last_day >= now.date() and not allow_future:
|
||||
lookup_kwargs['%s__lte' % date_field] = now
|
||||
object_list = queryset.filter(**lookup_kwargs)
|
||||
date_list = object_list.dates(date_field, 'day')
|
||||
if not object_list and not allow_empty:
|
||||
raise Http404
|
||||
|
||||
# Calculate the next month, if applicable.
|
||||
if allow_future:
|
||||
next_month = last_day
|
||||
elif last_day <= datetime.date.today():
|
||||
next_month = last_day
|
||||
else:
|
||||
next_month = None
|
||||
|
||||
# Calculate the previous month
|
||||
if first_day.month == 1:
|
||||
previous_month = first_day.replace(year=first_day.year-1,month=12)
|
||||
else:
|
||||
previous_month = first_day.replace(month=first_day.month-1)
|
||||
|
||||
if not template_name:
|
||||
template_name = "%s/%s_archive_month.html" % (model._meta.app_label, model._meta.object_name.lower())
|
||||
t = template_loader.get_template(template_name)
|
||||
c = RequestContext(request, {
|
||||
'date_list': date_list,
|
||||
'%s_list' % template_object_name: object_list,
|
||||
'month': date,
|
||||
'next_month': next_month,
|
||||
'previous_month': previous_month,
|
||||
}, context_processors)
|
||||
for key, value in extra_context.items():
|
||||
if callable(value):
|
||||
c[key] = value()
|
||||
else:
|
||||
c[key] = value
|
||||
return HttpResponse(t.render(c), mimetype=mimetype)
|
||||
|
||||
def archive_week(request, year, week, queryset, date_field,
|
||||
template_name=None, template_loader=loader,
|
||||
extra_context=None, allow_empty=True, context_processors=None,
|
||||
template_object_name='object', mimetype=None, allow_future=False):
|
||||
"""
|
||||
Generic weekly archive view.
|
||||
|
||||
Templates: ``<app_label>/<model_name>_archive_week.html``
|
||||
Context:
|
||||
week:
|
||||
(date) this week
|
||||
object_list:
|
||||
list of objects published in the given week
|
||||
"""
|
||||
if extra_context is None: extra_context = {}
|
||||
try:
|
||||
tt = time.strptime(year+'-0-'+week, '%Y-%w-%U')
|
||||
date = datetime.date(*tt[:3])
|
||||
except ValueError:
|
||||
raise Http404
|
||||
|
||||
model = queryset.model
|
||||
now = timezone.now()
|
||||
|
||||
# Calculate first and last day of week, for use in a date-range lookup.
|
||||
first_day = date
|
||||
last_day = date + datetime.timedelta(days=7)
|
||||
lookup_kwargs = {
|
||||
'%s__gte' % date_field: first_day,
|
||||
'%s__lt' % date_field: last_day,
|
||||
}
|
||||
|
||||
# Only bother to check current date if the week isn't in the past and future objects aren't requested.
|
||||
if last_day >= now.date() and not allow_future:
|
||||
lookup_kwargs['%s__lte' % date_field] = now
|
||||
object_list = queryset.filter(**lookup_kwargs)
|
||||
if not object_list and not allow_empty:
|
||||
raise Http404
|
||||
if not template_name:
|
||||
template_name = "%s/%s_archive_week.html" % (model._meta.app_label, model._meta.object_name.lower())
|
||||
t = template_loader.get_template(template_name)
|
||||
c = RequestContext(request, {
|
||||
'%s_list' % template_object_name: object_list,
|
||||
'week': date,
|
||||
})
|
||||
for key, value in extra_context.items():
|
||||
if callable(value):
|
||||
c[key] = value()
|
||||
else:
|
||||
c[key] = value
|
||||
return HttpResponse(t.render(c), mimetype=mimetype)
|
||||
|
||||
def archive_day(request, year, month, day, queryset, date_field,
|
||||
month_format='%b', day_format='%d', template_name=None,
|
||||
template_loader=loader, extra_context=None, allow_empty=False,
|
||||
context_processors=None, template_object_name='object',
|
||||
mimetype=None, allow_future=False):
|
||||
"""
|
||||
Generic daily archive view.
|
||||
|
||||
Templates: ``<app_label>/<model_name>_archive_day.html``
|
||||
Context:
|
||||
object_list:
|
||||
list of objects published that day
|
||||
day:
|
||||
(datetime) the day
|
||||
previous_day
|
||||
(datetime) the previous day
|
||||
next_day
|
||||
(datetime) the next day, or None if the current day is today
|
||||
"""
|
||||
if extra_context is None: extra_context = {}
|
||||
try:
|
||||
tt = time.strptime('%s-%s-%s' % (year, month, day),
|
||||
'%s-%s-%s' % ('%Y', month_format, day_format))
|
||||
date = datetime.date(*tt[:3])
|
||||
except ValueError:
|
||||
raise Http404
|
||||
|
||||
model = queryset.model
|
||||
now = timezone.now()
|
||||
|
||||
if isinstance(model._meta.get_field(date_field), DateTimeField):
|
||||
lookup_kwargs = {'%s__range' % date_field: (datetime.datetime.combine(date, datetime.time.min), datetime.datetime.combine(date, datetime.time.max))}
|
||||
else:
|
||||
lookup_kwargs = {date_field: date}
|
||||
|
||||
# Only bother to check current date if the date isn't in the past and future objects aren't requested.
|
||||
if date >= now.date() and not allow_future:
|
||||
lookup_kwargs['%s__lte' % date_field] = now
|
||||
object_list = queryset.filter(**lookup_kwargs)
|
||||
if not allow_empty and not object_list:
|
||||
raise Http404
|
||||
|
||||
# Calculate the next day, if applicable.
|
||||
if allow_future:
|
||||
next_day = date + datetime.timedelta(days=1)
|
||||
elif date < datetime.date.today():
|
||||
next_day = date + datetime.timedelta(days=1)
|
||||
else:
|
||||
next_day = None
|
||||
|
||||
if not template_name:
|
||||
template_name = "%s/%s_archive_day.html" % (model._meta.app_label, model._meta.object_name.lower())
|
||||
t = template_loader.get_template(template_name)
|
||||
c = RequestContext(request, {
|
||||
'%s_list' % template_object_name: object_list,
|
||||
'day': date,
|
||||
'previous_day': date - datetime.timedelta(days=1),
|
||||
'next_day': next_day,
|
||||
}, context_processors)
|
||||
for key, value in extra_context.items():
|
||||
if callable(value):
|
||||
c[key] = value()
|
||||
else:
|
||||
c[key] = value
|
||||
return HttpResponse(t.render(c), mimetype=mimetype)
|
||||
|
||||
def archive_today(request, **kwargs):
|
||||
"""
|
||||
Generic daily archive view for today. Same as archive_day view.
|
||||
"""
|
||||
today = datetime.date.today()
|
||||
kwargs.update({
|
||||
'year': str(today.year),
|
||||
'month': today.strftime('%b').lower(),
|
||||
'day': str(today.day),
|
||||
})
|
||||
return archive_day(request, **kwargs)
|
||||
|
||||
def object_detail(request, year, month, day, queryset, date_field,
|
||||
month_format='%b', day_format='%d', object_id=None, slug=None,
|
||||
slug_field='slug', template_name=None, template_name_field=None,
|
||||
template_loader=loader, extra_context=None, context_processors=None,
|
||||
template_object_name='object', mimetype=None, allow_future=False):
|
||||
"""
|
||||
Generic detail view from year/month/day/slug or year/month/day/id structure.
|
||||
|
||||
Templates: ``<app_label>/<model_name>_detail.html``
|
||||
Context:
|
||||
object:
|
||||
the object to be detailed
|
||||
"""
|
||||
if extra_context is None: extra_context = {}
|
||||
try:
|
||||
tt = time.strptime('%s-%s-%s' % (year, month, day),
|
||||
'%s-%s-%s' % ('%Y', month_format, day_format))
|
||||
date = datetime.date(*tt[:3])
|
||||
except ValueError:
|
||||
raise Http404
|
||||
|
||||
model = queryset.model
|
||||
now = timezone.now()
|
||||
|
||||
if isinstance(model._meta.get_field(date_field), DateTimeField):
|
||||
lookup_kwargs = {'%s__range' % date_field: (datetime.datetime.combine(date, datetime.time.min), datetime.datetime.combine(date, datetime.time.max))}
|
||||
else:
|
||||
lookup_kwargs = {date_field: date}
|
||||
|
||||
# Only bother to check current date if the date isn't in the past and future objects aren't requested.
|
||||
if date >= now.date() and not allow_future:
|
||||
lookup_kwargs['%s__lte' % date_field] = now
|
||||
if object_id:
|
||||
lookup_kwargs['%s__exact' % model._meta.pk.name] = object_id
|
||||
elif slug and slug_field:
|
||||
lookup_kwargs['%s__exact' % slug_field] = slug
|
||||
else:
|
||||
raise AttributeError("Generic detail view must be called with either an object_id or a slug/slugfield")
|
||||
try:
|
||||
obj = queryset.get(**lookup_kwargs)
|
||||
except ObjectDoesNotExist:
|
||||
raise Http404("No %s found for" % model._meta.verbose_name)
|
||||
if not template_name:
|
||||
template_name = "%s/%s_detail.html" % (model._meta.app_label, model._meta.object_name.lower())
|
||||
if template_name_field:
|
||||
template_name_list = [getattr(obj, template_name_field), template_name]
|
||||
t = template_loader.select_template(template_name_list)
|
||||
else:
|
||||
t = template_loader.get_template(template_name)
|
||||
c = RequestContext(request, {
|
||||
template_object_name: obj,
|
||||
}, context_processors)
|
||||
for key, value in extra_context.items():
|
||||
if callable(value):
|
||||
c[key] = value()
|
||||
else:
|
||||
c[key] = value
|
||||
response = HttpResponse(t.render(c), mimetype=mimetype)
|
||||
populate_xheaders(request, response, model, getattr(obj, obj._meta.pk.name))
|
||||
return response
|
|
@ -1,607 +0,0 @@
|
|||
import datetime
|
||||
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.translation import ugettext as _
|
||||
from django.utils import timezone
|
||||
from django.views.generic.base import View
|
||||
from django.views.generic.detail import BaseDetailView, SingleObjectTemplateResponseMixin
|
||||
from django.views.generic.list import MultipleObjectMixin, MultipleObjectTemplateResponseMixin
|
||||
|
||||
class YearMixin(object):
|
||||
year_format = '%Y'
|
||||
year = None
|
||||
|
||||
def get_year_format(self):
|
||||
"""
|
||||
Get a year format string in strptime syntax to be used to parse the
|
||||
year from url variables.
|
||||
"""
|
||||
return self.year_format
|
||||
|
||||
def get_year(self):
|
||||
"Return the year for which this view should display data"
|
||||
year = self.year
|
||||
if year is None:
|
||||
try:
|
||||
year = self.kwargs['year']
|
||||
except KeyError:
|
||||
try:
|
||||
year = self.request.GET['year']
|
||||
except KeyError:
|
||||
raise Http404(_(u"No year specified"))
|
||||
return year
|
||||
|
||||
|
||||
class MonthMixin(object):
|
||||
month_format = '%b'
|
||||
month = None
|
||||
|
||||
def get_month_format(self):
|
||||
"""
|
||||
Get a month format string in strptime syntax to be used to parse the
|
||||
month from url variables.
|
||||
"""
|
||||
return self.month_format
|
||||
|
||||
def get_month(self):
|
||||
"Return the month for which this view should display data"
|
||||
month = self.month
|
||||
if month is None:
|
||||
try:
|
||||
month = self.kwargs['month']
|
||||
except KeyError:
|
||||
try:
|
||||
month = self.request.GET['month']
|
||||
except KeyError:
|
||||
raise Http404(_(u"No month specified"))
|
||||
return month
|
||||
|
||||
def get_next_month(self, date):
|
||||
"""
|
||||
Get the next valid month.
|
||||
"""
|
||||
first_day, last_day = _month_bounds(date)
|
||||
next = (last_day + datetime.timedelta(days=1)).replace(day=1)
|
||||
return _get_next_prev_month(self, next, is_previous=False, use_first_day=True)
|
||||
|
||||
def get_previous_month(self, date):
|
||||
"""
|
||||
Get the previous valid month.
|
||||
"""
|
||||
first_day, last_day = _month_bounds(date)
|
||||
prev = (first_day - datetime.timedelta(days=1))
|
||||
return _get_next_prev_month(self, prev, is_previous=True, use_first_day=True)
|
||||
|
||||
|
||||
class DayMixin(object):
|
||||
day_format = '%d'
|
||||
day = None
|
||||
|
||||
def get_day_format(self):
|
||||
"""
|
||||
Get a day format string in strptime syntax to be used to parse the day
|
||||
from url variables.
|
||||
"""
|
||||
return self.day_format
|
||||
|
||||
def get_day(self):
|
||||
"Return the day for which this view should display data"
|
||||
day = self.day
|
||||
if day is None:
|
||||
try:
|
||||
day = self.kwargs['day']
|
||||
except KeyError:
|
||||
try:
|
||||
day = self.request.GET['day']
|
||||
except KeyError:
|
||||
raise Http404(_(u"No day specified"))
|
||||
return day
|
||||
|
||||
def get_next_day(self, date):
|
||||
"""
|
||||
Get the next valid day.
|
||||
"""
|
||||
next = date + datetime.timedelta(days=1)
|
||||
return _get_next_prev_month(self, next, is_previous=False, use_first_day=False)
|
||||
|
||||
def get_previous_day(self, date):
|
||||
"""
|
||||
Get the previous valid day.
|
||||
"""
|
||||
prev = date - datetime.timedelta(days=1)
|
||||
return _get_next_prev_month(self, prev, is_previous=True, use_first_day=False)
|
||||
|
||||
|
||||
class WeekMixin(object):
|
||||
week_format = '%U'
|
||||
week = None
|
||||
|
||||
def get_week_format(self):
|
||||
"""
|
||||
Get a week format string in strptime syntax to be used to parse the
|
||||
week from url variables.
|
||||
"""
|
||||
return self.week_format
|
||||
|
||||
def get_week(self):
|
||||
"Return the week for which this view should display data"
|
||||
week = self.week
|
||||
if week is None:
|
||||
try:
|
||||
week = self.kwargs['week']
|
||||
except KeyError:
|
||||
try:
|
||||
week = self.request.GET['week']
|
||||
except KeyError:
|
||||
raise Http404(_(u"No week specified"))
|
||||
return week
|
||||
|
||||
|
||||
class DateMixin(object):
|
||||
"""
|
||||
Mixin class for views manipulating date-based data.
|
||||
"""
|
||||
date_field = None
|
||||
allow_future = False
|
||||
|
||||
def get_date_field(self):
|
||||
"""
|
||||
Get the name of the date field to be used to filter by.
|
||||
"""
|
||||
if self.date_field is None:
|
||||
raise ImproperlyConfigured(u"%s.date_field is required." % self.__class__.__name__)
|
||||
return self.date_field
|
||||
|
||||
def get_allow_future(self):
|
||||
"""
|
||||
Returns `True` if the view should be allowed to display objects from
|
||||
the future.
|
||||
"""
|
||||
return self.allow_future
|
||||
|
||||
|
||||
class BaseDateListView(MultipleObjectMixin, DateMixin, View):
|
||||
"""
|
||||
Abstract base class for date-based views display a list of objects.
|
||||
"""
|
||||
allow_empty = False
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.date_list, self.object_list, extra_context = self.get_dated_items()
|
||||
context = self.get_context_data(object_list=self.object_list,
|
||||
date_list=self.date_list)
|
||||
context.update(extra_context)
|
||||
return self.render_to_response(context)
|
||||
|
||||
def get_dated_items(self):
|
||||
"""
|
||||
Obtain the list of dates and itesm
|
||||
"""
|
||||
raise NotImplementedError('A DateView must provide an implementation of get_dated_items()')
|
||||
|
||||
def get_dated_queryset(self, **lookup):
|
||||
"""
|
||||
Get a queryset properly filtered according to `allow_future` and any
|
||||
extra lookup kwargs.
|
||||
"""
|
||||
qs = self.get_queryset().filter(**lookup)
|
||||
date_field = self.get_date_field()
|
||||
allow_future = self.get_allow_future()
|
||||
allow_empty = self.get_allow_empty()
|
||||
|
||||
if not allow_future:
|
||||
qs = qs.filter(**{'%s__lte' % date_field: timezone.now()})
|
||||
|
||||
if not allow_empty and not qs:
|
||||
raise Http404(_(u"No %(verbose_name_plural)s available") % {
|
||||
'verbose_name_plural': force_unicode(qs.model._meta.verbose_name_plural)
|
||||
})
|
||||
|
||||
return qs
|
||||
|
||||
def get_date_list(self, queryset, date_type):
|
||||
"""
|
||||
Get a date list by calling `queryset.dates()`, checking along the way
|
||||
for empty lists that aren't allowed.
|
||||
"""
|
||||
date_field = self.get_date_field()
|
||||
allow_empty = self.get_allow_empty()
|
||||
|
||||
date_list = queryset.dates(date_field, date_type)[::-1]
|
||||
if date_list is not None and not date_list and not allow_empty:
|
||||
name = force_unicode(queryset.model._meta.verbose_name_plural)
|
||||
raise Http404(_(u"No %(verbose_name_plural)s available") %
|
||||
{'verbose_name_plural': name})
|
||||
|
||||
return date_list
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""
|
||||
Get the context. Must return a Context (or subclass) instance.
|
||||
"""
|
||||
items = kwargs.pop('object_list')
|
||||
context = super(BaseDateListView, self).get_context_data(object_list=items)
|
||||
context.update(kwargs)
|
||||
return context
|
||||
|
||||
|
||||
class BaseArchiveIndexView(BaseDateListView):
|
||||
"""
|
||||
Base class for archives of date-based items.
|
||||
|
||||
Requires a response mixin.
|
||||
"""
|
||||
context_object_name = 'latest'
|
||||
|
||||
def get_dated_items(self):
|
||||
"""
|
||||
Return (date_list, items, extra_context) for this request.
|
||||
"""
|
||||
qs = self.get_dated_queryset()
|
||||
date_list = self.get_date_list(qs, 'year')
|
||||
|
||||
if date_list:
|
||||
object_list = qs.order_by('-' + self.get_date_field())
|
||||
else:
|
||||
object_list = qs.none()
|
||||
|
||||
return (date_list, object_list, {})
|
||||
|
||||
|
||||
class ArchiveIndexView(MultipleObjectTemplateResponseMixin, BaseArchiveIndexView):
|
||||
"""
|
||||
Top-level archive of date-based items.
|
||||
"""
|
||||
template_name_suffix = '_archive'
|
||||
|
||||
|
||||
class BaseYearArchiveView(YearMixin, BaseDateListView):
|
||||
"""
|
||||
List of objects published in a given year.
|
||||
"""
|
||||
make_object_list = False
|
||||
|
||||
def get_dated_items(self):
|
||||
"""
|
||||
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_list = self.get_date_list(qs, 'month')
|
||||
|
||||
if self.get_make_object_list():
|
||||
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.
|
||||
object_list = qs.none()
|
||||
|
||||
return (date_list, object_list, {'year': year})
|
||||
|
||||
def get_make_object_list(self):
|
||||
"""
|
||||
Return `True` if this view should contain the full list of objects in
|
||||
the given year.
|
||||
"""
|
||||
return self.make_object_list
|
||||
|
||||
|
||||
class YearArchiveView(MultipleObjectTemplateResponseMixin, BaseYearArchiveView):
|
||||
"""
|
||||
List of objects published in a given year.
|
||||
"""
|
||||
template_name_suffix = '_archive_year'
|
||||
|
||||
|
||||
class BaseMonthArchiveView(YearMixin, MonthMixin, BaseDateListView):
|
||||
"""
|
||||
List of objects published in a given year.
|
||||
"""
|
||||
def get_dated_items(self):
|
||||
"""
|
||||
Return (date_list, items, extra_context) for this request.
|
||||
"""
|
||||
year = self.get_year()
|
||||
month = self.get_month()
|
||||
|
||||
date_field = self.get_date_field()
|
||||
date = _date_from_string(year, self.get_year_format(),
|
||||
month, self.get_month_format())
|
||||
|
||||
# Construct a date-range lookup.
|
||||
first_day, last_day = _month_bounds(date)
|
||||
lookup_kwargs = {
|
||||
'%s__gte' % date_field: first_day,
|
||||
'%s__lt' % date_field: last_day,
|
||||
}
|
||||
|
||||
qs = self.get_dated_queryset(**lookup_kwargs)
|
||||
date_list = self.get_date_list(qs, 'day')
|
||||
|
||||
return (date_list, qs, {
|
||||
'month': date,
|
||||
'next_month': self.get_next_month(date),
|
||||
'previous_month': self.get_previous_month(date),
|
||||
})
|
||||
|
||||
|
||||
class MonthArchiveView(MultipleObjectTemplateResponseMixin, BaseMonthArchiveView):
|
||||
"""
|
||||
List of objects published in a given year.
|
||||
"""
|
||||
template_name_suffix = '_archive_month'
|
||||
|
||||
|
||||
class BaseWeekArchiveView(YearMixin, WeekMixin, BaseDateListView):
|
||||
"""
|
||||
List of objects published in a given week.
|
||||
"""
|
||||
|
||||
def get_dated_items(self):
|
||||
"""
|
||||
Return (date_list, items, extra_context) for this request.
|
||||
"""
|
||||
year = self.get_year()
|
||||
week = self.get_week()
|
||||
|
||||
date_field = self.get_date_field()
|
||||
week_format = self.get_week_format()
|
||||
week_start = {
|
||||
'%W': '1',
|
||||
'%U': '0',
|
||||
}[week_format]
|
||||
date = _date_from_string(year, self.get_year_format(),
|
||||
week_start, '%w',
|
||||
week, week_format)
|
||||
|
||||
# Construct a date-range lookup.
|
||||
first_day = date
|
||||
last_day = date + datetime.timedelta(days=7)
|
||||
lookup_kwargs = {
|
||||
'%s__gte' % date_field: first_day,
|
||||
'%s__lt' % date_field: last_day,
|
||||
}
|
||||
|
||||
qs = self.get_dated_queryset(**lookup_kwargs)
|
||||
|
||||
return (None, qs, {'week': date})
|
||||
|
||||
|
||||
class WeekArchiveView(MultipleObjectTemplateResponseMixin, BaseWeekArchiveView):
|
||||
"""
|
||||
List of objects published in a given week.
|
||||
"""
|
||||
template_name_suffix = '_archive_week'
|
||||
|
||||
|
||||
class BaseDayArchiveView(YearMixin, MonthMixin, DayMixin, BaseDateListView):
|
||||
"""
|
||||
List of objects published on a given day.
|
||||
"""
|
||||
def get_dated_items(self):
|
||||
"""
|
||||
Return (date_list, items, extra_context) for this request.
|
||||
"""
|
||||
year = self.get_year()
|
||||
month = self.get_month()
|
||||
day = self.get_day()
|
||||
|
||||
date = _date_from_string(year, self.get_year_format(),
|
||||
month, self.get_month_format(),
|
||||
day, self.get_day_format())
|
||||
|
||||
return self._get_dated_items(date)
|
||||
|
||||
def _get_dated_items(self, date):
|
||||
"""
|
||||
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)
|
||||
|
||||
qs = self.get_dated_queryset(**lookup_kwargs)
|
||||
|
||||
return (None, qs, {
|
||||
'day': date,
|
||||
'previous_day': self.get_previous_day(date),
|
||||
'next_day': self.get_next_day(date),
|
||||
'previous_month': self.get_previous_month(date),
|
||||
'next_month': self.get_next_month(date)
|
||||
})
|
||||
|
||||
|
||||
class DayArchiveView(MultipleObjectTemplateResponseMixin, BaseDayArchiveView):
|
||||
"""
|
||||
List of objects published on a given day.
|
||||
"""
|
||||
template_name_suffix = "_archive_day"
|
||||
|
||||
|
||||
class BaseTodayArchiveView(BaseDayArchiveView):
|
||||
"""
|
||||
List of objects published today.
|
||||
"""
|
||||
|
||||
def get_dated_items(self):
|
||||
"""
|
||||
Return (date_list, items, extra_context) for this request.
|
||||
"""
|
||||
return self._get_dated_items(datetime.date.today())
|
||||
|
||||
|
||||
class TodayArchiveView(MultipleObjectTemplateResponseMixin, BaseTodayArchiveView):
|
||||
"""
|
||||
List of objects published today.
|
||||
"""
|
||||
template_name_suffix = "_archive_day"
|
||||
|
||||
|
||||
class BaseDateDetailView(YearMixin, MonthMixin, DayMixin, DateMixin, BaseDetailView):
|
||||
"""
|
||||
Detail view of a single object on a single date; this differs from the
|
||||
standard DetailView by accepting a year/month/day in the URL.
|
||||
"""
|
||||
def get_object(self, queryset=None):
|
||||
"""
|
||||
Get the object this request displays.
|
||||
"""
|
||||
year = self.get_year()
|
||||
month = self.get_month()
|
||||
day = self.get_day()
|
||||
date = _date_from_string(year, self.get_year_format(),
|
||||
month, self.get_month_format(),
|
||||
day, self.get_day_format())
|
||||
|
||||
# Use a custom queryset if provided
|
||||
qs = queryset or self.get_queryset()
|
||||
|
||||
if not self.get_allow_future() and date > datetime.date.today():
|
||||
raise Http404(_(u"Future %(verbose_name_plural)s not available because %(class_name)s.allow_future is False.") % {
|
||||
'verbose_name_plural': qs.model._meta.verbose_name_plural,
|
||||
'class_name': self.__class__.__name__,
|
||||
})
|
||||
|
||||
# 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)
|
||||
|
||||
return super(BaseDetailView, self).get_object(queryset=qs)
|
||||
|
||||
|
||||
class DateDetailView(SingleObjectTemplateResponseMixin, BaseDateDetailView):
|
||||
"""
|
||||
Detail view of a single object on a single date; this differs from the
|
||||
standard DetailView by accepting a year/month/day in the URL.
|
||||
"""
|
||||
template_name_suffix = '_detail'
|
||||
|
||||
|
||||
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.
|
||||
"""
|
||||
format = delim.join((year_format, month_format, day_format))
|
||||
datestr = delim.join((year, month, day))
|
||||
try:
|
||||
return datetime.datetime.strptime(datestr, format).date()
|
||||
except ValueError:
|
||||
raise Http404(_(u"Invalid date string '%(datestr)s' given format '%(format)s'") % {
|
||||
'datestr': datestr,
|
||||
'format': format,
|
||||
})
|
||||
|
||||
|
||||
def _month_bounds(date):
|
||||
"""
|
||||
Helper: return the first and last days of the month for the given date.
|
||||
"""
|
||||
first_day = date.replace(day=1)
|
||||
if first_day.month == 12:
|
||||
last_day = first_day.replace(year=first_day.year + 1, month=1)
|
||||
else:
|
||||
last_day = first_day.replace(month=first_day.month + 1)
|
||||
|
||||
return first_day, last_day
|
||||
|
||||
|
||||
def _get_next_prev_month(generic_view, naive_result, is_previous, use_first_day):
|
||||
"""
|
||||
Helper: Get the next or the previous valid date. The idea is to allow
|
||||
links on month/day views to never be 404s by never providing a date
|
||||
that'll be invalid for the given view.
|
||||
|
||||
This is a bit complicated since it handles both next and previous months
|
||||
and days (for MonthArchiveView and DayArchiveView); hence the coupling to generic_view.
|
||||
|
||||
However in essence the logic comes down to:
|
||||
|
||||
* If allow_empty and allow_future are both true, this is easy: just
|
||||
return the naive result (just the next/previous day or month,
|
||||
reguardless of object existence.)
|
||||
|
||||
* If allow_empty is true, allow_future is false, and the naive month
|
||||
isn't in the future, then return it; otherwise return None.
|
||||
|
||||
* If allow_empty is false and allow_future is true, return the next
|
||||
date *that contains a valid object*, even if it's in the future. If
|
||||
there are no next objects, return None.
|
||||
|
||||
* If allow_empty is false and allow_future is false, return the next
|
||||
date that contains a valid object. If that date is in the future, or
|
||||
if there are no next objects, return None.
|
||||
|
||||
"""
|
||||
date_field = generic_view.get_date_field()
|
||||
allow_empty = generic_view.get_allow_empty()
|
||||
allow_future = generic_view.get_allow_future()
|
||||
|
||||
# If allow_empty is True the naive value will be valid
|
||||
if allow_empty:
|
||||
result = naive_result
|
||||
|
||||
# Otherwise, we'll need to go to the database to look for an object
|
||||
# whose date_field is at least (greater than/less than) the given
|
||||
# naive result
|
||||
else:
|
||||
# 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}
|
||||
ordering = '-%s' % date_field
|
||||
else:
|
||||
lookup = {'%s__gte' % date_field: naive_result}
|
||||
ordering = date_field
|
||||
|
||||
qs = generic_view.get_queryset().filter(**lookup).order_by(ordering)
|
||||
|
||||
# Snag the first object from the queryset; if it doesn't exist that
|
||||
# means there's no next/previous link available.
|
||||
try:
|
||||
result = getattr(qs[0], date_field)
|
||||
except IndexError:
|
||||
result = None
|
||||
|
||||
# Convert datetimes to a dates
|
||||
if hasattr(result, 'date'):
|
||||
result = result.date()
|
||||
|
||||
# For month views, we always want to have a date that's the first of the
|
||||
# month for consistency's sake.
|
||||
if result and use_first_day:
|
||||
result = result.replace(day=1)
|
||||
|
||||
# Check against future dates.
|
||||
if result and (allow_future or result < datetime.date.today()):
|
||||
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}
|
|
@ -1,150 +0,0 @@
|
|||
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
|
||||
from django.http import Http404
|
||||
from django.utils.encoding import smart_str
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.views.generic.base import TemplateResponseMixin, View
|
||||
|
||||
|
||||
class SingleObjectMixin(object):
|
||||
"""
|
||||
Provides the ability to retrieve a single object for further manipulation.
|
||||
"""
|
||||
model = None
|
||||
queryset = None
|
||||
slug_field = 'slug'
|
||||
context_object_name = None
|
||||
slug_url_kwarg = 'slug'
|
||||
pk_url_kwarg = 'pk'
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
"""
|
||||
Returns the object the view is displaying.
|
||||
|
||||
By default this requires `self.queryset` and a `pk` or `slug` argument
|
||||
in the URLconf, but subclasses can override this to return any object.
|
||||
"""
|
||||
# Use a custom queryset if provided; this is required for subclasses
|
||||
# like DateDetailView
|
||||
if queryset is None:
|
||||
queryset = self.get_queryset()
|
||||
|
||||
# Next, try looking up by primary key.
|
||||
pk = self.kwargs.get(self.pk_url_kwarg, None)
|
||||
slug = self.kwargs.get(self.slug_url_kwarg, None)
|
||||
if pk is not None:
|
||||
queryset = queryset.filter(pk=pk)
|
||||
|
||||
# Next, try looking up by slug.
|
||||
elif slug is not None:
|
||||
slug_field = self.get_slug_field()
|
||||
queryset = queryset.filter(**{slug_field: slug})
|
||||
|
||||
# If none of those are defined, it's an error.
|
||||
else:
|
||||
raise AttributeError(u"Generic detail view %s must be called with "
|
||||
u"either an object pk or a slug."
|
||||
% self.__class__.__name__)
|
||||
|
||||
try:
|
||||
obj = queryset.get()
|
||||
except ObjectDoesNotExist:
|
||||
raise Http404(_(u"No %(verbose_name)s found matching the query") %
|
||||
{'verbose_name': queryset.model._meta.verbose_name})
|
||||
return obj
|
||||
|
||||
def get_queryset(self):
|
||||
"""
|
||||
Get the queryset to look an object up against. May not be called if
|
||||
`get_object` is overridden.
|
||||
"""
|
||||
if self.queryset is None:
|
||||
if self.model:
|
||||
return self.model._default_manager.all()
|
||||
else:
|
||||
raise ImproperlyConfigured(u"%(cls)s is missing a queryset. Define "
|
||||
u"%(cls)s.model, %(cls)s.queryset, or override "
|
||||
u"%(cls)s.get_object()." % {
|
||||
'cls': self.__class__.__name__
|
||||
})
|
||||
return self.queryset._clone()
|
||||
|
||||
def get_slug_field(self):
|
||||
"""
|
||||
Get the name of a slug field to be used to look up by slug.
|
||||
"""
|
||||
return self.slug_field
|
||||
|
||||
def get_context_object_name(self, obj):
|
||||
"""
|
||||
Get the name to use for the object.
|
||||
"""
|
||||
if self.context_object_name:
|
||||
return self.context_object_name
|
||||
elif hasattr(obj, '_meta'):
|
||||
return smart_str(obj._meta.object_name.lower())
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = kwargs
|
||||
context_object_name = self.get_context_object_name(self.object)
|
||||
if context_object_name:
|
||||
context[context_object_name] = self.object
|
||||
return context
|
||||
|
||||
|
||||
class BaseDetailView(SingleObjectMixin, View):
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
context = self.get_context_data(object=self.object)
|
||||
return self.render_to_response(context)
|
||||
|
||||
|
||||
class SingleObjectTemplateResponseMixin(TemplateResponseMixin):
|
||||
template_name_field = None
|
||||
template_name_suffix = '_detail'
|
||||
|
||||
def get_template_names(self):
|
||||
"""
|
||||
Return a list of template names to be used for the request. Must return
|
||||
a list. May not be called if get_template is overridden.
|
||||
"""
|
||||
try:
|
||||
names = super(SingleObjectTemplateResponseMixin, self).get_template_names()
|
||||
except ImproperlyConfigured:
|
||||
# If template_name isn't specified, it's not a problem --
|
||||
# we just start with an empty list.
|
||||
names = []
|
||||
|
||||
# If self.template_name_field is set, grab the value of the field
|
||||
# of that name from the object; this is the most specific template
|
||||
# name, if given.
|
||||
if self.object and self.template_name_field:
|
||||
name = getattr(self.object, self.template_name_field, None)
|
||||
if name:
|
||||
names.insert(0, name)
|
||||
|
||||
# The least-specific option is the default <app>/<model>_detail.html;
|
||||
# only use this if the object in question is a model.
|
||||
if hasattr(self.object, '_meta'):
|
||||
names.append("%s/%s%s.html" % (
|
||||
self.object._meta.app_label,
|
||||
self.object._meta.object_name.lower(),
|
||||
self.template_name_suffix
|
||||
))
|
||||
elif hasattr(self, 'model') and hasattr(self.model, '_meta'):
|
||||
names.append("%s/%s%s.html" % (
|
||||
self.model._meta.app_label,
|
||||
self.model._meta.object_name.lower(),
|
||||
self.template_name_suffix
|
||||
))
|
||||
return names
|
||||
|
||||
|
||||
class DetailView(SingleObjectTemplateResponseMixin, BaseDetailView):
|
||||
"""
|
||||
Render a "detail" view of an object.
|
||||
|
||||
By default this is a model instance looked up from `self.queryset`, but the
|
||||
view will support display of *any* object by overriding `self.get_object()`.
|
||||
"""
|
|
@ -1,242 +0,0 @@
|
|||
from django.forms import models as model_forms
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.views.generic.base import TemplateResponseMixin, View
|
||||
from django.views.generic.detail import (SingleObjectMixin,
|
||||
SingleObjectTemplateResponseMixin, BaseDetailView)
|
||||
|
||||
|
||||
class FormMixin(object):
|
||||
"""
|
||||
A mixin that provides a way to show and handle a form in a request.
|
||||
"""
|
||||
|
||||
initial = {}
|
||||
form_class = None
|
||||
success_url = None
|
||||
|
||||
def get_initial(self):
|
||||
"""
|
||||
Returns the initial data to use for forms on this view.
|
||||
"""
|
||||
return self.initial.copy()
|
||||
|
||||
def get_form_class(self):
|
||||
"""
|
||||
Returns the form class to use in this view
|
||||
"""
|
||||
return self.form_class
|
||||
|
||||
def get_form(self, form_class):
|
||||
"""
|
||||
Returns an instance of the form to be used in this view.
|
||||
"""
|
||||
return form_class(**self.get_form_kwargs())
|
||||
|
||||
def get_form_kwargs(self):
|
||||
"""
|
||||
Returns the keyword arguments for instanciating the form.
|
||||
"""
|
||||
kwargs = {'initial': self.get_initial()}
|
||||
if self.request.method in ('POST', 'PUT'):
|
||||
kwargs.update({
|
||||
'data': self.request.POST,
|
||||
'files': self.request.FILES,
|
||||
})
|
||||
return kwargs
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
return kwargs
|
||||
|
||||
def get_success_url(self):
|
||||
if self.success_url:
|
||||
url = self.success_url
|
||||
else:
|
||||
raise ImproperlyConfigured(
|
||||
"No URL to redirect to. Provide a success_url.")
|
||||
return url
|
||||
|
||||
def form_valid(self, form):
|
||||
return HttpResponseRedirect(self.get_success_url())
|
||||
|
||||
def form_invalid(self, form):
|
||||
return self.render_to_response(self.get_context_data(form=form))
|
||||
|
||||
|
||||
class ModelFormMixin(FormMixin, SingleObjectMixin):
|
||||
"""
|
||||
A mixin that provides a way to show and handle a modelform in a request.
|
||||
"""
|
||||
|
||||
def get_form_class(self):
|
||||
"""
|
||||
Returns the form class to use in this view
|
||||
"""
|
||||
if self.form_class:
|
||||
return self.form_class
|
||||
else:
|
||||
if self.model is not None:
|
||||
# If a model has been explicitly provided, use it
|
||||
model = self.model
|
||||
elif hasattr(self, 'object') and self.object is not None:
|
||||
# If this view is operating on a single object, use
|
||||
# the class of that object
|
||||
model = self.object.__class__
|
||||
else:
|
||||
# Try to get a queryset and extract the model class
|
||||
# from that
|
||||
model = self.get_queryset().model
|
||||
return model_forms.modelform_factory(model)
|
||||
|
||||
def get_form_kwargs(self):
|
||||
"""
|
||||
Returns the keyword arguments for instanciating the form.
|
||||
"""
|
||||
kwargs = super(ModelFormMixin, self).get_form_kwargs()
|
||||
kwargs.update({'instance': self.object})
|
||||
return kwargs
|
||||
|
||||
def get_success_url(self):
|
||||
if self.success_url:
|
||||
url = self.success_url % self.object.__dict__
|
||||
else:
|
||||
try:
|
||||
url = self.object.get_absolute_url()
|
||||
except AttributeError:
|
||||
raise ImproperlyConfigured(
|
||||
"No URL to redirect to. Either provide a url or define"
|
||||
" a get_absolute_url method on the Model.")
|
||||
return url
|
||||
|
||||
def form_valid(self, form):
|
||||
self.object = form.save()
|
||||
return super(ModelFormMixin, self).form_valid(form)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = kwargs
|
||||
if self.object:
|
||||
context['object'] = self.object
|
||||
context_object_name = self.get_context_object_name(self.object)
|
||||
if context_object_name:
|
||||
context[context_object_name] = self.object
|
||||
return context
|
||||
|
||||
|
||||
class ProcessFormView(View):
|
||||
"""
|
||||
A mixin that processes a form on POST.
|
||||
"""
|
||||
def get(self, request, *args, **kwargs):
|
||||
form_class = self.get_form_class()
|
||||
form = self.get_form(form_class)
|
||||
return self.render_to_response(self.get_context_data(form=form))
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
form_class = self.get_form_class()
|
||||
form = self.get_form(form_class)
|
||||
if form.is_valid():
|
||||
return self.form_valid(form)
|
||||
else:
|
||||
return self.form_invalid(form)
|
||||
|
||||
# PUT is a valid HTTP verb for creating (with a known URL) or editing an
|
||||
# object, note that browsers only support POST for now.
|
||||
def put(self, *args, **kwargs):
|
||||
return self.post(*args, **kwargs)
|
||||
|
||||
|
||||
class BaseFormView(FormMixin, ProcessFormView):
|
||||
"""
|
||||
A base view for displaying a form
|
||||
"""
|
||||
|
||||
|
||||
class FormView(TemplateResponseMixin, BaseFormView):
|
||||
"""
|
||||
A view for displaying a form, and rendering a template response.
|
||||
"""
|
||||
|
||||
|
||||
class BaseCreateView(ModelFormMixin, ProcessFormView):
|
||||
"""
|
||||
Base view for creating an new object instance.
|
||||
|
||||
Using this base class requires subclassing to provide a response mixin.
|
||||
"""
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = None
|
||||
return super(BaseCreateView, self).get(request, *args, **kwargs)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.object = None
|
||||
return super(BaseCreateView, self).post(request, *args, **kwargs)
|
||||
|
||||
|
||||
class CreateView(SingleObjectTemplateResponseMixin, BaseCreateView):
|
||||
"""
|
||||
View for creating an new object instance,
|
||||
with a response rendered by template.
|
||||
"""
|
||||
template_name_suffix = '_form'
|
||||
|
||||
|
||||
class BaseUpdateView(ModelFormMixin, ProcessFormView):
|
||||
"""
|
||||
Base view for updating an existing object.
|
||||
|
||||
Using this base class requires subclassing to provide a response mixin.
|
||||
"""
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
return super(BaseUpdateView, self).get(request, *args, **kwargs)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
return super(BaseUpdateView, self).post(request, *args, **kwargs)
|
||||
|
||||
|
||||
class UpdateView(SingleObjectTemplateResponseMixin, BaseUpdateView):
|
||||
"""
|
||||
View for updating an object,
|
||||
with a response rendered by template..
|
||||
"""
|
||||
template_name_suffix = '_form'
|
||||
|
||||
|
||||
class DeletionMixin(object):
|
||||
"""
|
||||
A mixin providing the ability to delete objects
|
||||
"""
|
||||
success_url = None
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
self.object.delete()
|
||||
return HttpResponseRedirect(self.get_success_url())
|
||||
|
||||
# Add support for browsers which only accept GET and POST for now.
|
||||
def post(self, *args, **kwargs):
|
||||
return self.delete(*args, **kwargs)
|
||||
|
||||
def get_success_url(self):
|
||||
if self.success_url:
|
||||
return self.success_url
|
||||
else:
|
||||
raise ImproperlyConfigured(
|
||||
"No URL to redirect to. Provide a success_url.")
|
||||
|
||||
|
||||
class BaseDeleteView(DeletionMixin, BaseDetailView):
|
||||
"""
|
||||
Base view for deleting an object.
|
||||
|
||||
Using this base class requires subclassing to provide a response mixin.
|
||||
"""
|
||||
|
||||
|
||||
class DeleteView(SingleObjectTemplateResponseMixin, BaseDeleteView):
|
||||
"""
|
||||
View for deleting an object retrieved with `self.get_object()`,
|
||||
with a response rendered by template.
|
||||
"""
|
||||
template_name_suffix = '_confirm_delete'
|
|
@ -1,153 +0,0 @@
|
|||
from django.core.paginator import Paginator, InvalidPage
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.http import Http404
|
||||
from django.utils.encoding import smart_str
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.views.generic.base import TemplateResponseMixin, View
|
||||
|
||||
|
||||
class MultipleObjectMixin(object):
|
||||
allow_empty = True
|
||||
queryset = None
|
||||
model = None
|
||||
paginate_by = None
|
||||
context_object_name = None
|
||||
paginator_class = Paginator
|
||||
|
||||
def get_queryset(self):
|
||||
"""
|
||||
Get the list of items for this view. This must be an interable, and may
|
||||
be a queryset (in which qs-specific behavior will be enabled).
|
||||
"""
|
||||
if self.queryset is not None:
|
||||
queryset = self.queryset
|
||||
if hasattr(queryset, '_clone'):
|
||||
queryset = queryset._clone()
|
||||
elif self.model is not None:
|
||||
queryset = self.model._default_manager.all()
|
||||
else:
|
||||
raise ImproperlyConfigured(u"'%s' must define 'queryset' or 'model'"
|
||||
% self.__class__.__name__)
|
||||
return queryset
|
||||
|
||||
def paginate_queryset(self, queryset, page_size):
|
||||
"""
|
||||
Paginate the queryset, if needed.
|
||||
"""
|
||||
paginator = self.get_paginator(queryset, page_size, allow_empty_first_page=self.get_allow_empty())
|
||||
page = self.kwargs.get('page') or self.request.GET.get('page') or 1
|
||||
try:
|
||||
page_number = int(page)
|
||||
except ValueError:
|
||||
if page == 'last':
|
||||
page_number = paginator.num_pages
|
||||
else:
|
||||
raise Http404(_(u"Page is not 'last', nor can it be converted to an int."))
|
||||
try:
|
||||
page = paginator.page(page_number)
|
||||
return (paginator, page, page.object_list, page.has_other_pages())
|
||||
except InvalidPage:
|
||||
raise Http404(_(u'Invalid page (%(page_number)s)') % {
|
||||
'page_number': page_number
|
||||
})
|
||||
|
||||
def get_paginate_by(self, queryset):
|
||||
"""
|
||||
Get the number of items to paginate by, or ``None`` for no pagination.
|
||||
"""
|
||||
return self.paginate_by
|
||||
|
||||
def get_paginator(self, queryset, per_page, orphans=0, allow_empty_first_page=True):
|
||||
"""
|
||||
Return an instance of the paginator for this view.
|
||||
"""
|
||||
return self.paginator_class(queryset, per_page, orphans=orphans, allow_empty_first_page=allow_empty_first_page)
|
||||
|
||||
def get_allow_empty(self):
|
||||
"""
|
||||
Returns ``True`` if the view should display empty lists, and ``False``
|
||||
if a 404 should be raised instead.
|
||||
"""
|
||||
return self.allow_empty
|
||||
|
||||
def get_context_object_name(self, object_list):
|
||||
"""
|
||||
Get the name of the item to be used in the context.
|
||||
"""
|
||||
if self.context_object_name:
|
||||
return self.context_object_name
|
||||
elif hasattr(object_list, 'model'):
|
||||
return smart_str('%s_list' % object_list.model._meta.object_name.lower())
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""
|
||||
Get the context for this view.
|
||||
"""
|
||||
queryset = kwargs.pop('object_list')
|
||||
page_size = self.get_paginate_by(queryset)
|
||||
context_object_name = self.get_context_object_name(queryset)
|
||||
if page_size:
|
||||
paginator, page, queryset, is_paginated = self.paginate_queryset(queryset, page_size)
|
||||
context = {
|
||||
'paginator': paginator,
|
||||
'page_obj': page,
|
||||
'is_paginated': is_paginated,
|
||||
'object_list': queryset
|
||||
}
|
||||
else:
|
||||
context = {
|
||||
'paginator': None,
|
||||
'page_obj': None,
|
||||
'is_paginated': False,
|
||||
'object_list': queryset
|
||||
}
|
||||
context.update(kwargs)
|
||||
if context_object_name is not None:
|
||||
context[context_object_name] = queryset
|
||||
return context
|
||||
|
||||
|
||||
class BaseListView(MultipleObjectMixin, View):
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object_list = self.get_queryset()
|
||||
allow_empty = self.get_allow_empty()
|
||||
if not allow_empty and len(self.object_list) == 0:
|
||||
raise Http404(_(u"Empty list and '%(class_name)s.allow_empty' is False.")
|
||||
% {'class_name': self.__class__.__name__})
|
||||
context = self.get_context_data(object_list=self.object_list)
|
||||
return self.render_to_response(context)
|
||||
|
||||
|
||||
class MultipleObjectTemplateResponseMixin(TemplateResponseMixin):
|
||||
template_name_suffix = '_list'
|
||||
|
||||
def get_template_names(self):
|
||||
"""
|
||||
Return a list of template names to be used for the request. Must return
|
||||
a list. May not be called if get_template is overridden.
|
||||
"""
|
||||
try:
|
||||
names = super(MultipleObjectTemplateResponseMixin, self).get_template_names()
|
||||
except ImproperlyConfigured:
|
||||
# If template_name isn't specified, it's not a problem --
|
||||
# we just start with an empty list.
|
||||
names = []
|
||||
|
||||
# If the list is a queryset, we'll invent a template name based on the
|
||||
# app and model name. This name gets put at the end of the template
|
||||
# name list so that user-supplied names override the automatically-
|
||||
# generated ones.
|
||||
if hasattr(self.object_list, 'model'):
|
||||
opts = self.object_list.model._meta
|
||||
names.append("%s/%s%s.html" % (opts.app_label, opts.object_name.lower(), self.template_name_suffix))
|
||||
|
||||
return names
|
||||
|
||||
|
||||
class ListView(MultipleObjectTemplateResponseMixin, BaseListView):
|
||||
"""
|
||||
Render some list of objects, set by `self.model` or `self.queryset`.
|
||||
`self.queryset` can actually be any iterable of items, not just a queryset.
|
||||
"""
|
|
@ -1,151 +0,0 @@
|
|||
from django.template import loader, RequestContext
|
||||
from django.http import Http404, HttpResponse
|
||||
from django.core.paginator import Paginator, InvalidPage
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
|
||||
import warnings
|
||||
warnings.warn(
|
||||
'Function-based generic views have been deprecated; use class-based views instead.',
|
||||
DeprecationWarning
|
||||
)
|
||||
|
||||
|
||||
def object_list(request, queryset, paginate_by=None, page=None,
|
||||
allow_empty=True, template_name=None, template_loader=loader,
|
||||
extra_context=None, context_processors=None, template_object_name='object',
|
||||
mimetype=None):
|
||||
"""
|
||||
Generic list of objects.
|
||||
|
||||
Templates: ``<app_label>/<model_name>_list.html``
|
||||
Context:
|
||||
object_list
|
||||
list of objects
|
||||
is_paginated
|
||||
are the results paginated?
|
||||
results_per_page
|
||||
number of objects per page (if paginated)
|
||||
has_next
|
||||
is there a next page?
|
||||
has_previous
|
||||
is there a prev page?
|
||||
page
|
||||
the current page
|
||||
next
|
||||
the next page
|
||||
previous
|
||||
the previous page
|
||||
pages
|
||||
number of pages, total
|
||||
hits
|
||||
number of objects, total
|
||||
last_on_page
|
||||
the result number of the last of object in the
|
||||
object_list (1-indexed)
|
||||
first_on_page
|
||||
the result number of the first object in the
|
||||
object_list (1-indexed)
|
||||
page_range:
|
||||
A list of the page numbers (1-indexed).
|
||||
"""
|
||||
if extra_context is None: extra_context = {}
|
||||
queryset = queryset._clone()
|
||||
if paginate_by:
|
||||
paginator = Paginator(queryset, paginate_by, allow_empty_first_page=allow_empty)
|
||||
if not page:
|
||||
page = request.GET.get('page', 1)
|
||||
try:
|
||||
page_number = int(page)
|
||||
except ValueError:
|
||||
if page == 'last':
|
||||
page_number = paginator.num_pages
|
||||
else:
|
||||
# Page is not 'last', nor can it be converted to an int.
|
||||
raise Http404
|
||||
try:
|
||||
page_obj = paginator.page(page_number)
|
||||
except InvalidPage:
|
||||
raise Http404
|
||||
c = RequestContext(request, {
|
||||
'%s_list' % template_object_name: page_obj.object_list,
|
||||
'paginator': paginator,
|
||||
'page_obj': page_obj,
|
||||
'is_paginated': page_obj.has_other_pages(),
|
||||
|
||||
# Legacy template context stuff. New templates should use page_obj
|
||||
# to access this instead.
|
||||
'results_per_page': paginator.per_page,
|
||||
'has_next': page_obj.has_next(),
|
||||
'has_previous': page_obj.has_previous(),
|
||||
'page': page_obj.number,
|
||||
'next': page_obj.next_page_number(),
|
||||
'previous': page_obj.previous_page_number(),
|
||||
'first_on_page': page_obj.start_index(),
|
||||
'last_on_page': page_obj.end_index(),
|
||||
'pages': paginator.num_pages,
|
||||
'hits': paginator.count,
|
||||
'page_range': paginator.page_range,
|
||||
}, context_processors)
|
||||
else:
|
||||
c = RequestContext(request, {
|
||||
'%s_list' % template_object_name: queryset,
|
||||
'paginator': None,
|
||||
'page_obj': None,
|
||||
'is_paginated': False,
|
||||
}, context_processors)
|
||||
if not allow_empty and len(queryset) == 0:
|
||||
raise Http404
|
||||
for key, value in extra_context.items():
|
||||
if callable(value):
|
||||
c[key] = value()
|
||||
else:
|
||||
c[key] = value
|
||||
if not template_name:
|
||||
model = queryset.model
|
||||
template_name = "%s/%s_list.html" % (model._meta.app_label, model._meta.object_name.lower())
|
||||
t = template_loader.get_template(template_name)
|
||||
return HttpResponse(t.render(c), mimetype=mimetype)
|
||||
|
||||
def object_detail(request, queryset, object_id=None, slug=None,
|
||||
slug_field='slug', template_name=None, template_name_field=None,
|
||||
template_loader=loader, extra_context=None,
|
||||
context_processors=None, template_object_name='object',
|
||||
mimetype=None):
|
||||
"""
|
||||
Generic detail of an object.
|
||||
|
||||
Templates: ``<app_label>/<model_name>_detail.html``
|
||||
Context:
|
||||
object
|
||||
the object
|
||||
"""
|
||||
if extra_context is None: extra_context = {}
|
||||
model = queryset.model
|
||||
if object_id:
|
||||
queryset = queryset.filter(pk=object_id)
|
||||
elif slug and slug_field:
|
||||
queryset = queryset.filter(**{slug_field: slug})
|
||||
else:
|
||||
raise AttributeError("Generic detail view must be called with either an object_id or a slug/slug_field.")
|
||||
try:
|
||||
obj = queryset.get()
|
||||
except ObjectDoesNotExist:
|
||||
raise Http404("No %s found matching the query" % (model._meta.verbose_name))
|
||||
if not template_name:
|
||||
template_name = "%s/%s_detail.html" % (model._meta.app_label, model._meta.object_name.lower())
|
||||
if template_name_field:
|
||||
template_name_list = [getattr(obj, template_name_field), template_name]
|
||||
t = template_loader.select_template(template_name_list)
|
||||
else:
|
||||
t = template_loader.get_template(template_name)
|
||||
c = RequestContext(request, {
|
||||
template_object_name: obj,
|
||||
}, context_processors)
|
||||
for key, value in extra_context.items():
|
||||
if callable(value):
|
||||
c[key] = value()
|
||||
else:
|
||||
c[key] = value
|
||||
response = HttpResponse(t.render(c), mimetype=mimetype)
|
||||
populate_xheaders(request, response, model, getattr(obj, obj._meta.pk.name))
|
||||
return response
|
|
@ -1,68 +0,0 @@
|
|||
from django.template import loader, RequestContext
|
||||
from django.http import HttpResponse, HttpResponseRedirect, HttpResponsePermanentRedirect, HttpResponseGone
|
||||
from django.utils.log import getLogger
|
||||
|
||||
import warnings
|
||||
warnings.warn(
|
||||
'Function-based generic views have been deprecated; use class-based views instead.',
|
||||
DeprecationWarning
|
||||
)
|
||||
|
||||
logger = getLogger('django.request')
|
||||
|
||||
|
||||
def direct_to_template(request, template, extra_context=None, mimetype=None, **kwargs):
|
||||
"""
|
||||
Render a given template with any extra URL parameters in the context as
|
||||
``{{ params }}``.
|
||||
"""
|
||||
if extra_context is None: extra_context = {}
|
||||
dictionary = {'params': kwargs}
|
||||
for key, value in extra_context.items():
|
||||
if callable(value):
|
||||
dictionary[key] = value()
|
||||
else:
|
||||
dictionary[key] = value
|
||||
c = RequestContext(request, dictionary)
|
||||
t = loader.get_template(template)
|
||||
return HttpResponse(t.render(c), content_type=mimetype)
|
||||
|
||||
def redirect_to(request, url, permanent=True, query_string=False, **kwargs):
|
||||
"""
|
||||
Redirect to a given URL.
|
||||
|
||||
The given url may contain dict-style string formatting, which will be
|
||||
interpolated against the params in the URL. For example, to redirect from
|
||||
``/foo/<id>/`` to ``/bar/<id>/``, you could use the following URLconf::
|
||||
|
||||
urlpatterns = patterns('',
|
||||
('^foo/(?P<id>\d+)/$', 'django.views.generic.simple.redirect_to', {'url' : '/bar/%(id)s/'}),
|
||||
)
|
||||
|
||||
If the given url is ``None``, a HttpResponseGone (410) will be issued.
|
||||
|
||||
If the ``permanent`` argument is False, then the response will have a 302
|
||||
HTTP status code. Otherwise, the status code will be 301.
|
||||
|
||||
If the ``query_string`` argument is True, then the GET query string
|
||||
from the request is appended to the URL.
|
||||
|
||||
"""
|
||||
args = request.META.get('QUERY_STRING', '')
|
||||
|
||||
if url is not None:
|
||||
if kwargs:
|
||||
url = url % kwargs
|
||||
|
||||
if args and query_string:
|
||||
url = "%s?%s" % (url, args)
|
||||
|
||||
klass = permanent and HttpResponsePermanentRedirect or HttpResponseRedirect
|
||||
return klass(url)
|
||||
else:
|
||||
logger.warning('Gone: %s', request.path,
|
||||
extra={
|
||||
'status_code': 410,
|
||||
'request': request
|
||||
})
|
||||
return HttpResponseGone()
|
Loading…
Reference in New Issue