änderung1
This commit is contained in:
1 parent
cc1d096bc3
commit
cbdda613b7
2098 files changed
+274524
No files matched your search
Whitespace-only changes.
@@ -0,0 +1,78 @@
|
||||
import functools
|
||||
|
||||
from django.http import HttpRequest
|
||||
|
||||
|
||||
def sensitive_variables(*variables):
|
||||
"""
|
||||
Indicate which variables used in the decorated function are sensitive so
|
||||
that those variables can later be treated in a special way, for example
|
||||
by hiding them when logging unhandled exceptions.
|
||||
|
||||
Accept two forms:
|
||||
|
||||
* with specified variable names:
|
||||
|
||||
@sensitive_variables('user', 'password', 'credit_card')
|
||||
def my_function(user):
|
||||
password = user.pass_word
|
||||
credit_card = user.credit_card_number
|
||||
...
|
||||
|
||||
* without any specified variable names, in which case consider all
|
||||
variables are sensitive:
|
||||
|
||||
@sensitive_variables()
|
||||
def my_function()
|
||||
...
|
||||
"""
|
||||
def decorator(func):
|
||||
@functools.wraps(func)
|
||||
def sensitive_variables_wrapper(*func_args, **func_kwargs):
|
||||
if variables:
|
||||
sensitive_variables_wrapper.sensitive_variables = variables
|
||||
else:
|
||||
sensitive_variables_wrapper.sensitive_variables = '__ALL__'
|
||||
return func(*func_args, **func_kwargs)
|
||||
return sensitive_variables_wrapper
|
||||
return decorator
|
||||
|
||||
|
||||
def sensitive_post_parameters(*parameters):
|
||||
"""
|
||||
Indicate which POST parameters used in the decorated view are sensitive,
|
||||
so that those parameters can later be treated in a special way, for example
|
||||
by hiding them when logging unhandled exceptions.
|
||||
|
||||
Accept two forms:
|
||||
|
||||
* with specified parameters:
|
||||
|
||||
@sensitive_post_parameters('password', 'credit_card')
|
||||
def my_view(request):
|
||||
pw = request.POST['password']
|
||||
cc = request.POST['credit_card']
|
||||
...
|
||||
|
||||
* without any specified parameters, in which case consider all
|
||||
variables are sensitive:
|
||||
|
||||
@sensitive_post_parameters()
|
||||
def my_view(request)
|
||||
...
|
||||
"""
|
||||
def decorator(view):
|
||||
@functools.wraps(view)
|
||||
def sensitive_post_parameters_wrapper(request, *args, **kwargs):
|
||||
assert isinstance(request, HttpRequest), (
|
||||
"sensitive_post_parameters didn't receive an HttpRequest. "
|
||||
"If you are decorating a classmethod, be sure to use "
|
||||
"@method_decorator."
|
||||
)
|
||||
if parameters:
|
||||
request.sensitive_post_parameters = parameters
|
||||
else:
|
||||
request.sensitive_post_parameters = '__ALL__'
|
||||
return view(request, *args, **kwargs)
|
||||
return sensitive_post_parameters_wrapper
|
||||
return decorator
|
||||
@@ -0,0 +1,121 @@
|
||||
"""
|
||||
Decorators for views based on HTTP headers.
|
||||
"""
|
||||
|
||||
from calendar import timegm
|
||||
from functools import wraps
|
||||
|
||||
from django.http import HttpResponseNotAllowed
|
||||
from django.middleware.http import ConditionalGetMiddleware
|
||||
from django.utils.cache import get_conditional_response
|
||||
from django.utils.decorators import decorator_from_middleware
|
||||
from django.utils.http import http_date, quote_etag
|
||||
from django.utils.log import log_response
|
||||
|
||||
conditional_page = decorator_from_middleware(ConditionalGetMiddleware)
|
||||
|
||||
|
||||
def require_http_methods(request_method_list):
|
||||
"""
|
||||
Decorator to make a view only accept particular request methods. Usage::
|
||||
|
||||
@require_http_methods(["GET", "POST"])
|
||||
def my_view(request):
|
||||
# I can assume now that only GET or POST requests make it this far
|
||||
# ...
|
||||
|
||||
Note that request methods should be in uppercase.
|
||||
"""
|
||||
def decorator(func):
|
||||
@wraps(func)
|
||||
def inner(request, *args, **kwargs):
|
||||
if request.method not in request_method_list:
|
||||
response = HttpResponseNotAllowed(request_method_list)
|
||||
log_response(
|
||||
'Method Not Allowed (%s): %s', request.method, request.path,
|
||||
response=response,
|
||||
request=request,
|
||||
)
|
||||
return response
|
||||
return func(request, *args, **kwargs)
|
||||
return inner
|
||||
return decorator
|
||||
|
||||
|
||||
require_GET = require_http_methods(["GET"])
|
||||
require_GET.__doc__ = "Decorator to require that a view only accepts the GET method."
|
||||
|
||||
require_POST = require_http_methods(["POST"])
|
||||
require_POST.__doc__ = "Decorator to require that a view only accepts the POST method."
|
||||
|
||||
require_safe = require_http_methods(["GET", "HEAD"])
|
||||
require_safe.__doc__ = "Decorator to require that a view only accepts safe methods: GET and HEAD."
|
||||
|
||||
|
||||
def condition(etag_func=None, last_modified_func=None):
|
||||
"""
|
||||
Decorator to support conditional retrieval (or change) for a view
|
||||
function.
|
||||
|
||||
The parameters are callables to compute the ETag and last modified time for
|
||||
the requested resource, respectively. The callables are passed the same
|
||||
parameters as the view itself. The ETag function should return a string (or
|
||||
None if the resource doesn't exist), while the last_modified function
|
||||
should return a datetime object (or None if the resource doesn't exist).
|
||||
|
||||
The ETag function should return a complete ETag, including quotes (e.g.
|
||||
'"etag"'), since that's the only way to distinguish between weak and strong
|
||||
ETags. If an unquoted ETag is returned (e.g. 'etag'), it will be converted
|
||||
to a strong ETag by adding quotes.
|
||||
|
||||
This decorator will either pass control to the wrapped view function or
|
||||
return an HTTP 304 response (unmodified) or 412 response (precondition
|
||||
failed), depending upon the request method. In either case, the decorator
|
||||
will add the generated ETag and Last-Modified headers to the response if
|
||||
the headers aren't already set and if the request's method is safe.
|
||||
"""
|
||||
def decorator(func):
|
||||
@wraps(func)
|
||||
def inner(request, *args, **kwargs):
|
||||
# Compute values (if any) for the requested resource.
|
||||
def get_last_modified():
|
||||
if last_modified_func:
|
||||
dt = last_modified_func(request, *args, **kwargs)
|
||||
if dt:
|
||||
return timegm(dt.utctimetuple())
|
||||
|
||||
# The value from etag_func() could be quoted or unquoted.
|
||||
res_etag = etag_func(request, *args, **kwargs) if etag_func else None
|
||||
res_etag = quote_etag(res_etag) if res_etag is not None else None
|
||||
res_last_modified = get_last_modified()
|
||||
|
||||
response = get_conditional_response(
|
||||
request,
|
||||
etag=res_etag,
|
||||
last_modified=res_last_modified,
|
||||
)
|
||||
|
||||
if response is None:
|
||||
response = func(request, *args, **kwargs)
|
||||
|
||||
# Set relevant headers on the response if they don't already exist
|
||||
# and if the request method is safe.
|
||||
if request.method in ('GET', 'HEAD'):
|
||||
if res_last_modified and not response.has_header('Last-Modified'):
|
||||
response['Last-Modified'] = http_date(res_last_modified)
|
||||
if res_etag:
|
||||
response.setdefault('ETag', res_etag)
|
||||
|
||||
return response
|
||||
|
||||
return inner
|
||||
return decorator
|
||||
|
||||
|
||||
# Shortcut decorators for common cases based on ETag or Last-Modified only
|
||||
def etag(etag_func):
|
||||
return condition(etag_func=etag_func)
|
||||
|
||||
|
||||
def last_modified(last_modified_func):
|
||||
return condition(last_modified_func=last_modified_func)
|
||||
@@ -0,0 +1,724 @@
|
||||
import datetime
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.db import models
|
||||
from django.http import Http404
|
||||
from django.utils import timezone
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.translation import gettext as _
|
||||
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:
|
||||
"""Mixin for views manipulating year-based data."""
|
||||
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(_("No year specified"))
|
||||
return year
|
||||
|
||||
def get_next_year(self, date):
|
||||
"""Get the next valid year."""
|
||||
return _get_next_prev(self, date, is_previous=False, period='year')
|
||||
|
||||
def get_previous_year(self, date):
|
||||
"""Get the previous valid year."""
|
||||
return _get_next_prev(self, date, is_previous=True, period='year')
|
||||
|
||||
def _get_next_year(self, date):
|
||||
"""
|
||||
Return the start date of the next interval.
|
||||
|
||||
The interval is defined by start date <= item date < next start date.
|
||||
"""
|
||||
try:
|
||||
return date.replace(year=date.year + 1, month=1, day=1)
|
||||
except ValueError:
|
||||
raise Http404(_("Date out of range"))
|
||||
|
||||
def _get_current_year(self, date):
|
||||
"""Return the start date of the current interval."""
|
||||
return date.replace(month=1, day=1)
|
||||
|
||||
|
||||
class MonthMixin:
|
||||
"""Mixin for views manipulating month-based data."""
|
||||
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(_("No month specified"))
|
||||
return month
|
||||
|
||||
def get_next_month(self, date):
|
||||
"""Get the next valid month."""
|
||||
return _get_next_prev(self, date, is_previous=False, period='month')
|
||||
|
||||
def get_previous_month(self, date):
|
||||
"""Get the previous valid month."""
|
||||
return _get_next_prev(self, date, is_previous=True, period='month')
|
||||
|
||||
def _get_next_month(self, date):
|
||||
"""
|
||||
Return the start date of the next interval.
|
||||
|
||||
The interval is defined by start date <= item date < next start date.
|
||||
"""
|
||||
if date.month == 12:
|
||||
try:
|
||||
return date.replace(year=date.year + 1, month=1, day=1)
|
||||
except ValueError:
|
||||
raise Http404(_("Date out of range"))
|
||||
else:
|
||||
return date.replace(month=date.month + 1, day=1)
|
||||
|
||||
def _get_current_month(self, date):
|
||||
"""Return the start date of the previous interval."""
|
||||
return date.replace(day=1)
|
||||
|
||||
|
||||
class DayMixin:
|
||||
"""Mixin for views manipulating day-based data."""
|
||||
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(_("No day specified"))
|
||||
return day
|
||||
|
||||
def get_next_day(self, date):
|
||||
"""Get the next valid day."""
|
||||
return _get_next_prev(self, date, is_previous=False, period='day')
|
||||
|
||||
def get_previous_day(self, date):
|
||||
"""Get the previous valid day."""
|
||||
return _get_next_prev(self, date, is_previous=True, period='day')
|
||||
|
||||
def _get_next_day(self, date):
|
||||
"""
|
||||
Return the start date of the next interval.
|
||||
|
||||
The interval is defined by start date <= item date < next start date.
|
||||
"""
|
||||
return date + datetime.timedelta(days=1)
|
||||
|
||||
def _get_current_day(self, date):
|
||||
"""Return the start date of the current interval."""
|
||||
return date
|
||||
|
||||
|
||||
class WeekMixin:
|
||||
"""Mixin for views manipulating week-based data."""
|
||||
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(_("No week specified"))
|
||||
return week
|
||||
|
||||
def get_next_week(self, date):
|
||||
"""Get the next valid week."""
|
||||
return _get_next_prev(self, date, is_previous=False, period='week')
|
||||
|
||||
def get_previous_week(self, date):
|
||||
"""Get the previous valid week."""
|
||||
return _get_next_prev(self, date, is_previous=True, period='week')
|
||||
|
||||
def _get_next_week(self, date):
|
||||
"""
|
||||
Return the start date of the next interval.
|
||||
|
||||
The interval is defined by start date <= item date < next start date.
|
||||
"""
|
||||
try:
|
||||
return date + datetime.timedelta(days=7 - self._get_weekday(date))
|
||||
except OverflowError:
|
||||
raise Http404(_("Date out of range"))
|
||||
|
||||
def _get_current_week(self, date):
|
||||
"""Return the start date of the current interval."""
|
||||
return date - datetime.timedelta(self._get_weekday(date))
|
||||
|
||||
def _get_weekday(self, date):
|
||||
"""
|
||||
Return the weekday for a given date.
|
||||
|
||||
The first day according to the week format is 0 and the last day is 6.
|
||||
"""
|
||||
week_format = self.get_week_format()
|
||||
if week_format == '%W': # week starts on Monday
|
||||
return date.weekday()
|
||||
elif week_format == '%U': # week starts on Sunday
|
||||
return (date.weekday() + 1) % 7
|
||||
else:
|
||||
raise ValueError("unknown week format: %s" % week_format)
|
||||
|
||||
|
||||
class DateMixin:
|
||||
"""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("%s.date_field is required." % self.__class__.__name__)
|
||||
return self.date_field
|
||||
|
||||
def get_allow_future(self):
|
||||
"""
|
||||
Return `True` if the view should be allowed to display objects from
|
||||
the future.
|
||||
"""
|
||||
return self.allow_future
|
||||
|
||||
# Note: the following three methods only work in subclasses that also
|
||||
# inherit SingleObjectMixin or MultipleObjectMixin.
|
||||
|
||||
@cached_property
|
||||
def uses_datetime_field(self):
|
||||
"""
|
||||
Return `True` if the date field is a `DateTimeField` and `False`
|
||||
if it's a `DateField`.
|
||||
"""
|
||||
model = self.get_queryset().model if self.model is None else self.model
|
||||
field = model._meta.get_field(self.get_date_field())
|
||||
return isinstance(field, models.DateTimeField)
|
||||
|
||||
def _make_date_lookup_arg(self, value):
|
||||
"""
|
||||
Convert a date into a datetime when the date field is a DateTimeField.
|
||||
|
||||
When time zone support is enabled, `date` is assumed to be in the
|
||||
current time zone, so that displayed items are consistent with the URL.
|
||||
"""
|
||||
if self.uses_datetime_field:
|
||||
value = datetime.datetime.combine(value, datetime.time.min)
|
||||
if settings.USE_TZ:
|
||||
value = timezone.make_aware(value)
|
||||
return value
|
||||
|
||||
def _make_single_date_lookup(self, date):
|
||||
"""
|
||||
Get the lookup kwargs for filtering on a single date.
|
||||
|
||||
If the date field is a DateTimeField, we can't just filter on
|
||||
date_field=date because that doesn't take the time into account.
|
||||
"""
|
||||
date_field = self.get_date_field()
|
||||
if self.uses_datetime_field:
|
||||
since = self._make_date_lookup_arg(date)
|
||||
until = self._make_date_lookup_arg(date + datetime.timedelta(days=1))
|
||||
return {
|
||||
'%s__gte' % date_field: since,
|
||||
'%s__lt' % date_field: until,
|
||||
}
|
||||
else:
|
||||
# Skip self._make_date_lookup_arg, it's a no-op in this branch.
|
||||
return {date_field: date}
|
||||
|
||||
|
||||
class BaseDateListView(MultipleObjectMixin, DateMixin, View):
|
||||
"""Abstract base class for date-based views displaying a list of objects."""
|
||||
allow_empty = False
|
||||
date_list_period = 'year'
|
||||
|
||||
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,
|
||||
**extra_context
|
||||
)
|
||||
return self.render_to_response(context)
|
||||
|
||||
def get_dated_items(self):
|
||||
"""Obtain the list of dates and items."""
|
||||
raise NotImplementedError('A DateView must provide an implementation of get_dated_items()')
|
||||
|
||||
def get_ordering(self):
|
||||
"""
|
||||
Return the field or fields to use for ordering the queryset; use the
|
||||
date field by default.
|
||||
"""
|
||||
return '-%s' % self.get_date_field() if self.ordering is None else self.ordering
|
||||
|
||||
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()
|
||||
paginate_by = self.get_paginate_by(qs)
|
||||
|
||||
if not allow_future:
|
||||
now = timezone.now() if self.uses_datetime_field else timezone_today()
|
||||
qs = qs.filter(**{'%s__lte' % date_field: now})
|
||||
|
||||
if not allow_empty:
|
||||
# When pagination is enabled, it's better to do a cheap query
|
||||
# than to load the unpaginated queryset in memory.
|
||||
is_empty = not qs if paginate_by is None else not qs.exists()
|
||||
if is_empty:
|
||||
raise Http404(_("No %(verbose_name_plural)s available") % {
|
||||
'verbose_name_plural': qs.model._meta.verbose_name_plural,
|
||||
})
|
||||
|
||||
return qs
|
||||
|
||||
def get_date_list_period(self):
|
||||
"""
|
||||
Get the aggregation period for the list of dates: 'year', 'month', or
|
||||
'day'.
|
||||
"""
|
||||
return self.date_list_period
|
||||
|
||||
def get_date_list(self, queryset, date_type=None, ordering='ASC'):
|
||||
"""
|
||||
Get a date list by calling `queryset.dates/datetimes()`, checking
|
||||
along the way for empty lists that aren't allowed.
|
||||
"""
|
||||
date_field = self.get_date_field()
|
||||
allow_empty = self.get_allow_empty()
|
||||
if date_type is None:
|
||||
date_type = self.get_date_list_period()
|
||||
|
||||
if self.uses_datetime_field:
|
||||
date_list = queryset.datetimes(date_field, date_type, ordering)
|
||||
else:
|
||||
date_list = queryset.dates(date_field, date_type, ordering)
|
||||
if date_list is not None and not date_list and not allow_empty:
|
||||
raise Http404(
|
||||
_("No %(verbose_name_plural)s available") % {
|
||||
'verbose_name_plural': queryset.model._meta.verbose_name_plural,
|
||||
}
|
||||
)
|
||||
|
||||
return date_list
|
||||
|
||||
|
||||
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, ordering='DESC')
|
||||
|
||||
if not date_list:
|
||||
qs = qs.none()
|
||||
|
||||
return (date_list, qs, {})
|
||||
|
||||
|
||||
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."""
|
||||
date_list_period = 'month'
|
||||
make_object_list = False
|
||||
|
||||
def get_dated_items(self):
|
||||
"""Return (date_list, items, extra_context) for this request."""
|
||||
year = self.get_year()
|
||||
|
||||
date_field = self.get_date_field()
|
||||
date = _date_from_string(year, self.get_year_format())
|
||||
|
||||
since = self._make_date_lookup_arg(date)
|
||||
until = self._make_date_lookup_arg(self._get_next_year(date))
|
||||
lookup_kwargs = {
|
||||
'%s__gte' % date_field: since,
|
||||
'%s__lt' % date_field: until,
|
||||
}
|
||||
|
||||
qs = self.get_dated_queryset(**lookup_kwargs)
|
||||
date_list = self.get_date_list(qs)
|
||||
|
||||
if not self.get_make_object_list():
|
||||
# We need this to be a queryset since parent classes introspect it
|
||||
# to find information about the model.
|
||||
qs = qs.none()
|
||||
|
||||
return (date_list, qs, {
|
||||
'year': date,
|
||||
'next_year': self.get_next_year(date),
|
||||
'previous_year': self.get_previous_year(date),
|
||||
})
|
||||
|
||||
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 month."""
|
||||
date_list_period = 'day'
|
||||
|
||||
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())
|
||||
|
||||
since = self._make_date_lookup_arg(date)
|
||||
until = self._make_date_lookup_arg(self._get_next_month(date))
|
||||
lookup_kwargs = {
|
||||
'%s__gte' % date_field: since,
|
||||
'%s__lt' % date_field: until,
|
||||
}
|
||||
|
||||
qs = self.get_dated_queryset(**lookup_kwargs)
|
||||
date_list = self.get_date_list(qs)
|
||||
|
||||
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 month."""
|
||||
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_choices = {'%W': '1', '%U': '0'}
|
||||
try:
|
||||
week_start = week_choices[week_format]
|
||||
except KeyError:
|
||||
raise ValueError('Unknown week format %r. Choices are: %s' % (
|
||||
week_format,
|
||||
', '.join(sorted(week_choices)),
|
||||
))
|
||||
date = _date_from_string(year, self.get_year_format(),
|
||||
week_start, '%w',
|
||||
week, week_format)
|
||||
|
||||
since = self._make_date_lookup_arg(date)
|
||||
until = self._make_date_lookup_arg(self._get_next_week(date))
|
||||
lookup_kwargs = {
|
||||
'%s__gte' % date_field: since,
|
||||
'%s__lt' % date_field: until,
|
||||
}
|
||||
|
||||
qs = self.get_dated_queryset(**lookup_kwargs)
|
||||
|
||||
return (None, qs, {
|
||||
'week': date,
|
||||
'next_week': self.get_next_week(date),
|
||||
'previous_week': self.get_previous_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.
|
||||
"""
|
||||
lookup_kwargs = self._make_single_date_lookup(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 = self.get_queryset() if queryset is None else queryset
|
||||
|
||||
if not self.get_allow_future() and date > datetime.date.today():
|
||||
raise Http404(_(
|
||||
"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
|
||||
lookup_kwargs = self._make_single_date_lookup(date)
|
||||
qs = qs.filter(**lookup_kwargs)
|
||||
|
||||
return super().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='__'):
|
||||
"""
|
||||
Get a datetime.date object given a format string and a year, month, and day
|
||||
(only year is mandatory). Raise a 404 for an invalid date.
|
||||
"""
|
||||
format = year_format + delim + month_format + delim + day_format
|
||||
datestr = str(year) + delim + str(month) + delim + str(day)
|
||||
try:
|
||||
return datetime.datetime.strptime(datestr, format).date()
|
||||
except ValueError:
|
||||
raise Http404(_("Invalid date string '%(datestr)s' given format '%(format)s'") % {
|
||||
'datestr': datestr,
|
||||
'format': format,
|
||||
})
|
||||
|
||||
|
||||
def _get_next_prev(generic_view, date, is_previous, period):
|
||||
"""
|
||||
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 different intervals of time,
|
||||
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/week/month,
|
||||
regardless of object existence.)
|
||||
|
||||
* If allow_empty is true, allow_future is false, and the naive result
|
||||
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()
|
||||
|
||||
get_current = getattr(generic_view, '_get_current_%s' % period)
|
||||
get_next = getattr(generic_view, '_get_next_%s' % period)
|
||||
|
||||
# Bounds of the current interval
|
||||
start, end = get_current(date), get_next(date)
|
||||
|
||||
# If allow_empty is True, the naive result will be valid
|
||||
if allow_empty:
|
||||
if is_previous:
|
||||
result = get_current(start - datetime.timedelta(days=1))
|
||||
else:
|
||||
result = end
|
||||
|
||||
if allow_future or result <= timezone_today():
|
||||
return result
|
||||
else:
|
||||
return None
|
||||
|
||||
# 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__lt' % date_field: generic_view._make_date_lookup_arg(start)}
|
||||
ordering = '-%s' % date_field
|
||||
else:
|
||||
lookup = {'%s__gte' % date_field: generic_view._make_date_lookup_arg(end)}
|
||||
ordering = date_field
|
||||
|
||||
# Filter out objects in the future if appropriate.
|
||||
if not allow_future:
|
||||
# Fortunately, to match the implementation of allow_future,
|
||||
# we need __lte, which doesn't conflict with __lt above.
|
||||
if generic_view.uses_datetime_field:
|
||||
now = timezone.now()
|
||||
else:
|
||||
now = timezone_today()
|
||||
lookup['%s__lte' % date_field] = now
|
||||
|
||||
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:
|
||||
return None
|
||||
|
||||
# Convert datetimes to dates in the current time zone.
|
||||
if generic_view.uses_datetime_field:
|
||||
if settings.USE_TZ:
|
||||
result = timezone.localtime(result)
|
||||
result = result.date()
|
||||
|
||||
# Return the first day of the period.
|
||||
return get_current(result)
|
||||
|
||||
|
||||
def timezone_today():
|
||||
"""Return the current date in the current time zone."""
|
||||
if settings.USE_TZ:
|
||||
return timezone.localdate()
|
||||
else:
|
||||
return datetime.date.today()
|
||||
@@ -0,0 +1,198 @@
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.core.paginator import InvalidPage, Paginator
|
||||
from django.db.models.query import QuerySet
|
||||
from django.http import Http404
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic.base import ContextMixin, TemplateResponseMixin, View
|
||||
|
||||
|
||||
class MultipleObjectMixin(ContextMixin):
|
||||
"""A mixin for views manipulating multiple objects."""
|
||||
allow_empty = True
|
||||
queryset = None
|
||||
model = None
|
||||
paginate_by = None
|
||||
paginate_orphans = 0
|
||||
context_object_name = None
|
||||
paginator_class = Paginator
|
||||
page_kwarg = 'page'
|
||||
ordering = None
|
||||
|
||||
def get_queryset(self):
|
||||
"""
|
||||
Return the list of items for this view.
|
||||
|
||||
The return value must be an iterable and may be an instance of
|
||||
`QuerySet` in which case `QuerySet` specific behavior will be enabled.
|
||||
"""
|
||||
if self.queryset is not None:
|
||||
queryset = self.queryset
|
||||
if isinstance(queryset, QuerySet):
|
||||
queryset = queryset.all()
|
||||
elif self.model is not None:
|
||||
queryset = self.model._default_manager.all()
|
||||
else:
|
||||
raise ImproperlyConfigured(
|
||||
"%(cls)s is missing a QuerySet. Define "
|
||||
"%(cls)s.model, %(cls)s.queryset, or override "
|
||||
"%(cls)s.get_queryset()." % {
|
||||
'cls': self.__class__.__name__
|
||||
}
|
||||
)
|
||||
ordering = self.get_ordering()
|
||||
if ordering:
|
||||
if isinstance(ordering, str):
|
||||
ordering = (ordering,)
|
||||
queryset = queryset.order_by(*ordering)
|
||||
|
||||
return queryset
|
||||
|
||||
def get_ordering(self):
|
||||
"""Return the field or fields to use for ordering the queryset."""
|
||||
return self.ordering
|
||||
|
||||
def paginate_queryset(self, queryset, page_size):
|
||||
"""Paginate the queryset, if needed."""
|
||||
paginator = self.get_paginator(
|
||||
queryset, page_size, orphans=self.get_paginate_orphans(),
|
||||
allow_empty_first_page=self.get_allow_empty())
|
||||
page_kwarg = self.page_kwarg
|
||||
page = self.kwargs.get(page_kwarg) or self.request.GET.get(page_kwarg) or 1
|
||||
try:
|
||||
page_number = int(page)
|
||||
except ValueError:
|
||||
if page == 'last':
|
||||
page_number = paginator.num_pages
|
||||
else:
|
||||
raise Http404(_("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 as e:
|
||||
raise Http404(_('Invalid page (%(page_number)s): %(message)s') % {
|
||||
'page_number': page_number,
|
||||
'message': str(e)
|
||||
})
|
||||
|
||||
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, **kwargs):
|
||||
"""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, **kwargs)
|
||||
|
||||
def get_paginate_orphans(self):
|
||||
"""
|
||||
Return the maximum number of orphans extend the last page by when
|
||||
paginating.
|
||||
"""
|
||||
return self.paginate_orphans
|
||||
|
||||
def get_allow_empty(self):
|
||||
"""
|
||||
Return ``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 '%s_list' % object_list.model._meta.model_name
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_context_data(self, *, object_list=None, **kwargs):
|
||||
"""Get the context for this view."""
|
||||
queryset = object_list if object_list is not None else self.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
|
||||
}
|
||||
if context_object_name is not None:
|
||||
context[context_object_name] = queryset
|
||||
context.update(kwargs)
|
||||
return super().get_context_data(**context)
|
||||
|
||||
|
||||
class BaseListView(MultipleObjectMixin, View):
|
||||
"""A base view for displaying a list of objects."""
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object_list = self.get_queryset()
|
||||
allow_empty = self.get_allow_empty()
|
||||
|
||||
if not allow_empty:
|
||||
# When pagination is enabled and object_list is a queryset,
|
||||
# it's better to do a cheap query than to load the unpaginated
|
||||
# queryset in memory.
|
||||
if self.get_paginate_by(self.object_list) is not None and hasattr(self.object_list, 'exists'):
|
||||
is_empty = not self.object_list.exists()
|
||||
else:
|
||||
is_empty = not self.object_list
|
||||
if is_empty:
|
||||
raise Http404(_("Empty list and '%(class_name)s.allow_empty' is False.") % {
|
||||
'class_name': self.__class__.__name__,
|
||||
})
|
||||
context = self.get_context_data()
|
||||
return self.render_to_response(context)
|
||||
|
||||
|
||||
class MultipleObjectTemplateResponseMixin(TemplateResponseMixin):
|
||||
"""Mixin for responding with a template and list of objects."""
|
||||
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 render_to_response is overridden.
|
||||
"""
|
||||
try:
|
||||
names = super().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.model_name, self.template_name_suffix))
|
||||
elif not names:
|
||||
raise ImproperlyConfigured(
|
||||
"%(cls)s requires either a 'template_name' attribute "
|
||||
"or a get_queryset() method that returns a QuerySet." % {
|
||||
'cls': self.__class__.__name__,
|
||||
}
|
||||
)
|
||||
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.
|
||||
"""
|
||||
@@ -0,0 +1,308 @@
|
||||
import itertools
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from urllib.parse import unquote
|
||||
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
|
||||
from django.template import Context, Engine
|
||||
from django.urls import translate_url
|
||||
from django.utils.formats import get_format
|
||||
from django.utils.http import is_safe_url
|
||||
from django.utils.translation import (
|
||||
LANGUAGE_SESSION_KEY, check_for_language, get_language,
|
||||
)
|
||||
from django.utils.translation.trans_real import DjangoTranslation
|
||||
from django.views.generic import View
|
||||
|
||||
LANGUAGE_QUERY_PARAMETER = 'language'
|
||||
|
||||
|
||||
def set_language(request):
|
||||
"""
|
||||
Redirect to a given URL while setting the chosen language in the session
|
||||
(if enabled) and in a cookie. The URL and the language code need to be
|
||||
specified in the request parameters.
|
||||
|
||||
Since this view changes how the user will see the rest of the site, it must
|
||||
only be accessed as a POST request. If called as a GET request, it will
|
||||
redirect to the page in the request (the 'next' parameter) without changing
|
||||
any state.
|
||||
"""
|
||||
next = request.POST.get('next', request.GET.get('next'))
|
||||
if ((next or not request.is_ajax()) and
|
||||
not is_safe_url(url=next, allowed_hosts={request.get_host()}, require_https=request.is_secure())):
|
||||
next = request.META.get('HTTP_REFERER')
|
||||
next = next and unquote(next) # HTTP_REFERER may be encoded.
|
||||
if not is_safe_url(url=next, allowed_hosts={request.get_host()}, require_https=request.is_secure()):
|
||||
next = '/'
|
||||
response = HttpResponseRedirect(next) if next else HttpResponse(status=204)
|
||||
if request.method == 'POST':
|
||||
lang_code = request.POST.get(LANGUAGE_QUERY_PARAMETER)
|
||||
if lang_code and check_for_language(lang_code):
|
||||
if next:
|
||||
next_trans = translate_url(next, lang_code)
|
||||
if next_trans != next:
|
||||
response = HttpResponseRedirect(next_trans)
|
||||
if hasattr(request, 'session'):
|
||||
request.session[LANGUAGE_SESSION_KEY] = lang_code
|
||||
response.set_cookie(
|
||||
settings.LANGUAGE_COOKIE_NAME, lang_code,
|
||||
max_age=settings.LANGUAGE_COOKIE_AGE,
|
||||
path=settings.LANGUAGE_COOKIE_PATH,
|
||||
domain=settings.LANGUAGE_COOKIE_DOMAIN,
|
||||
)
|
||||
return response
|
||||
|
||||
|
||||
def get_formats():
|
||||
"""Return all formats strings required for i18n to work."""
|
||||
FORMAT_SETTINGS = (
|
||||
'DATE_FORMAT', 'DATETIME_FORMAT', 'TIME_FORMAT',
|
||||
'YEAR_MONTH_FORMAT', 'MONTH_DAY_FORMAT', 'SHORT_DATE_FORMAT',
|
||||
'SHORT_DATETIME_FORMAT', 'FIRST_DAY_OF_WEEK', 'DECIMAL_SEPARATOR',
|
||||
'THOUSAND_SEPARATOR', 'NUMBER_GROUPING',
|
||||
'DATE_INPUT_FORMATS', 'TIME_INPUT_FORMATS', 'DATETIME_INPUT_FORMATS'
|
||||
)
|
||||
return {attr: get_format(attr) for attr in FORMAT_SETTINGS}
|
||||
|
||||
|
||||
js_catalog_template = r"""
|
||||
{% autoescape off %}
|
||||
(function(globals) {
|
||||
|
||||
var django = globals.django || (globals.django = {});
|
||||
|
||||
{% if plural %}
|
||||
django.pluralidx = function(n) {
|
||||
var v={{ plural }};
|
||||
if (typeof(v) == 'boolean') {
|
||||
return v ? 1 : 0;
|
||||
} else {
|
||||
return v;
|
||||
}
|
||||
};
|
||||
{% else %}
|
||||
django.pluralidx = function(count) { return (count == 1) ? 0 : 1; };
|
||||
{% endif %}
|
||||
|
||||
/* gettext library */
|
||||
|
||||
django.catalog = django.catalog || {};
|
||||
{% if catalog_str %}
|
||||
var newcatalog = {{ catalog_str }};
|
||||
for (var key in newcatalog) {
|
||||
django.catalog[key] = newcatalog[key];
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
if (!django.jsi18n_initialized) {
|
||||
django.gettext = function(msgid) {
|
||||
var value = django.catalog[msgid];
|
||||
if (typeof(value) == 'undefined') {
|
||||
return msgid;
|
||||
} else {
|
||||
return (typeof(value) == 'string') ? value : value[0];
|
||||
}
|
||||
};
|
||||
|
||||
django.ngettext = function(singular, plural, count) {
|
||||
var value = django.catalog[singular];
|
||||
if (typeof(value) == 'undefined') {
|
||||
return (count == 1) ? singular : plural;
|
||||
} else {
|
||||
return value.constructor === Array ? value[django.pluralidx(count)] : value;
|
||||
}
|
||||
};
|
||||
|
||||
django.gettext_noop = function(msgid) { return msgid; };
|
||||
|
||||
django.pgettext = function(context, msgid) {
|
||||
var value = django.gettext(context + '\x04' + msgid);
|
||||
if (value.indexOf('\x04') != -1) {
|
||||
value = msgid;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
django.npgettext = function(context, singular, plural, count) {
|
||||
var value = django.ngettext(context + '\x04' + singular, context + '\x04' + plural, count);
|
||||
if (value.indexOf('\x04') != -1) {
|
||||
value = django.ngettext(singular, plural, count);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
django.interpolate = function(fmt, obj, named) {
|
||||
if (named) {
|
||||
return fmt.replace(/%\(\w+\)s/g, function(match){return String(obj[match.slice(2,-2)])});
|
||||
} else {
|
||||
return fmt.replace(/%s/g, function(match){return String(obj.shift())});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* formatting library */
|
||||
|
||||
django.formats = {{ formats_str }};
|
||||
|
||||
django.get_format = function(format_type) {
|
||||
var value = django.formats[format_type];
|
||||
if (typeof(value) == 'undefined') {
|
||||
return format_type;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
/* add to global namespace */
|
||||
globals.pluralidx = django.pluralidx;
|
||||
globals.gettext = django.gettext;
|
||||
globals.ngettext = django.ngettext;
|
||||
globals.gettext_noop = django.gettext_noop;
|
||||
globals.pgettext = django.pgettext;
|
||||
globals.npgettext = django.npgettext;
|
||||
globals.interpolate = django.interpolate;
|
||||
globals.get_format = django.get_format;
|
||||
|
||||
django.jsi18n_initialized = true;
|
||||
}
|
||||
|
||||
}(this));
|
||||
{% endautoescape %}
|
||||
"""
|
||||
|
||||
|
||||
class JavaScriptCatalog(View):
|
||||
"""
|
||||
Return the selected language catalog as a JavaScript library.
|
||||
|
||||
Receive the list of packages to check for translations in the `packages`
|
||||
kwarg either from the extra dictionary passed to the url() function or as a
|
||||
plus-sign delimited string from the request. Default is 'django.conf'.
|
||||
|
||||
You can override the gettext domain for this view, but usually you don't
|
||||
want to do that as JavaScript messages go to the djangojs domain. This
|
||||
might be needed if you deliver your JavaScript source from Django templates.
|
||||
"""
|
||||
domain = 'djangojs'
|
||||
packages = None
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
locale = get_language()
|
||||
domain = kwargs.get('domain', self.domain)
|
||||
# If packages are not provided, default to all installed packages, as
|
||||
# DjangoTranslation without localedirs harvests them all.
|
||||
packages = kwargs.get('packages', '')
|
||||
packages = packages.split('+') if packages else self.packages
|
||||
paths = self.get_paths(packages) if packages else None
|
||||
self.translation = DjangoTranslation(locale, domain=domain, localedirs=paths)
|
||||
context = self.get_context_data(**kwargs)
|
||||
return self.render_to_response(context)
|
||||
|
||||
def get_paths(self, packages):
|
||||
allowable_packages = {app_config.name: app_config for app_config in apps.get_app_configs()}
|
||||
app_configs = [allowable_packages[p] for p in packages if p in allowable_packages]
|
||||
if len(app_configs) < len(packages):
|
||||
excluded = [p for p in packages if p not in allowable_packages]
|
||||
raise ValueError(
|
||||
'Invalid package(s) provided to JavaScriptCatalog: %s' % ','.join(excluded)
|
||||
)
|
||||
# paths of requested packages
|
||||
return [os.path.join(app.path, 'locale') for app in app_configs]
|
||||
|
||||
@property
|
||||
def _num_plurals(self):
|
||||
"""
|
||||
Return the number of plurals for this catalog language, or 2 if no
|
||||
plural string is available.
|
||||
"""
|
||||
match = re.search(r'nplurals=\s*(\d+)', self._plural_string or '')
|
||||
if match:
|
||||
return int(match.groups()[0])
|
||||
return 2
|
||||
|
||||
@property
|
||||
def _plural_string(self):
|
||||
"""
|
||||
Return the plural string (including nplurals) for this catalog language,
|
||||
or None if no plural string is available.
|
||||
"""
|
||||
if '' in self.translation._catalog:
|
||||
for line in self.translation._catalog[''].split('\n'):
|
||||
if line.startswith('Plural-Forms:'):
|
||||
return line.split(':', 1)[1].strip()
|
||||
return None
|
||||
|
||||
def get_plural(self):
|
||||
plural = self._plural_string
|
||||
if plural is not None:
|
||||
# This should be a compiled function of a typical plural-form:
|
||||
# Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 :
|
||||
# n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;
|
||||
plural = [el.strip() for el in plural.split(';') if el.strip().startswith('plural=')][0].split('=', 1)[1]
|
||||
return plural
|
||||
|
||||
def get_catalog(self):
|
||||
pdict = {}
|
||||
num_plurals = self._num_plurals
|
||||
catalog = {}
|
||||
trans_cat = self.translation._catalog
|
||||
trans_fallback_cat = self.translation._fallback._catalog if self.translation._fallback else {}
|
||||
seen_keys = set()
|
||||
for key, value in itertools.chain(trans_cat.items(), trans_fallback_cat.items()):
|
||||
if key == '' or key in seen_keys:
|
||||
continue
|
||||
if isinstance(key, str):
|
||||
catalog[key] = value
|
||||
elif isinstance(key, tuple):
|
||||
msgid, cnt = key
|
||||
pdict.setdefault(msgid, {})[cnt] = value
|
||||
else:
|
||||
raise TypeError(key)
|
||||
seen_keys.add(key)
|
||||
for k, v in pdict.items():
|
||||
catalog[k] = [v.get(i, '') for i in range(num_plurals)]
|
||||
return catalog
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
return {
|
||||
'catalog': self.get_catalog(),
|
||||
'formats': get_formats(),
|
||||
'plural': self.get_plural(),
|
||||
}
|
||||
|
||||
def render_to_response(self, context, **response_kwargs):
|
||||
def indent(s):
|
||||
return s.replace('\n', '\n ')
|
||||
|
||||
template = Engine().from_string(js_catalog_template)
|
||||
context['catalog_str'] = indent(
|
||||
json.dumps(context['catalog'], sort_keys=True, indent=2)
|
||||
) if context['catalog'] else None
|
||||
context['formats_str'] = indent(json.dumps(context['formats'], sort_keys=True, indent=2))
|
||||
|
||||
return HttpResponse(template.render(Context(context)), 'text/javascript; charset="utf-8"')
|
||||
|
||||
|
||||
class JSONCatalog(JavaScriptCatalog):
|
||||
"""
|
||||
Return the selected language catalog as a JSON object.
|
||||
|
||||
Receive the same parameters as JavaScriptCatalog and return a response
|
||||
with a JSON object of the following format:
|
||||
|
||||
{
|
||||
"catalog": {
|
||||
# Translations catalog
|
||||
},
|
||||
"formats": {
|
||||
# Language formats for date, time, etc.
|
||||
},
|
||||
"plural": '...' # Expression for plural forms, or null.
|
||||
}
|
||||
"""
|
||||
def render_to_response(self, context, **response_kwargs):
|
||||
return JsonResponse(context)
|
||||
@@ -0,0 +1,79 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||
<title>Page not found at {{ request.path_info }}</title>
|
||||
<meta name="robots" content="NONE,NOARCHIVE">
|
||||
<style type="text/css">
|
||||
html * { padding:0; margin:0; }
|
||||
body * { padding:10px 20px; }
|
||||
body * * { padding:0; }
|
||||
body { font:small sans-serif; background:#eee; color:#000; }
|
||||
body>div { border-bottom:1px solid #ddd; }
|
||||
h1 { font-weight:normal; margin-bottom:.4em; }
|
||||
h1 span { font-size:60%; color:#666; font-weight:normal; }
|
||||
table { border:none; border-collapse: collapse; width:100%; }
|
||||
td, th { vertical-align:top; padding:2px 3px; }
|
||||
th { width:12em; text-align:right; color:#666; padding-right:.5em; }
|
||||
#info { background:#f6f6f6; }
|
||||
#info ol { margin: 0.5em 4em; }
|
||||
#info ol li { font-family: monospace; }
|
||||
#summary { background: #ffc; }
|
||||
#explanation { background:#eee; border-bottom: 0px none; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="summary">
|
||||
<h1>Page not found <span>(404)</span></h1>
|
||||
<table class="meta">
|
||||
<tr>
|
||||
<th>Request Method:</th>
|
||||
<td>{{ request.META.REQUEST_METHOD }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Request URL:</th>
|
||||
<td>{{ request.build_absolute_uri }}</td>
|
||||
</tr>
|
||||
{% if raising_view_name %}
|
||||
<tr>
|
||||
<th>Raised by:</th>
|
||||
<td>{{ raising_view_name }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
</div>
|
||||
<div id="info">
|
||||
{% if urlpatterns %}
|
||||
<p>
|
||||
Using the URLconf defined in <code>{{ urlconf }}</code>,
|
||||
Django tried these URL patterns, in this order:
|
||||
</p>
|
||||
<ol>
|
||||
{% for pattern in urlpatterns %}
|
||||
<li>
|
||||
{% for pat in pattern %}
|
||||
{{ pat.pattern }}
|
||||
{% if forloop.last and pat.name %}[name='{{ pat.name }}']{% endif %}
|
||||
{% endfor %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
<p>
|
||||
{% if request_path %}
|
||||
The current path, <code>{{ request_path }}</code>,{% else %}
|
||||
The empty path{% endif %} didn't match any of these.
|
||||
</p>
|
||||
{% else %}
|
||||
<p>{{ reason }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div id="explanation">
|
||||
<p>
|
||||
You're seeing this error because you have <code>DEBUG = True</code> in
|
||||
your Django settings file. Change that to <code>False</code>, and Django
|
||||
will display a standard 404 page.
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,484 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||
<meta name="robots" content="NONE,NOARCHIVE">
|
||||
<title>{% if exception_type %}{{ exception_type }}{% else %}Report{% endif %}
|
||||
{% if request %} at {{ request.path_info }}{% endif %}</title>
|
||||
<style type="text/css">
|
||||
html * { padding:0; margin:0; }
|
||||
body * { padding:10px 20px; }
|
||||
body * * { padding:0; }
|
||||
body { font:small sans-serif; background-color:#fff; color:#000; }
|
||||
body>div { border-bottom:1px solid #ddd; }
|
||||
h1 { font-weight:normal; }
|
||||
h2 { margin-bottom:.8em; }
|
||||
h3 { margin:1em 0 .5em 0; }
|
||||
h4 { margin:0 0 .5em 0; font-weight: normal; }
|
||||
code, pre { font-size: 100%; white-space: pre-wrap; }
|
||||
table { border:1px solid #ccc; border-collapse: collapse; width:100%; background:white; }
|
||||
tbody td, tbody th { vertical-align:top; padding:2px 3px; }
|
||||
thead th {
|
||||
padding:1px 6px 1px 3px; background:#fefefe; text-align:left;
|
||||
font-weight:normal; font-size:11px; border:1px solid #ddd;
|
||||
}
|
||||
tbody th { width:12em; text-align:right; color:#666; padding-right:.5em; }
|
||||
table.vars { margin:5px 0 2px 40px; }
|
||||
table.vars td, table.req td { font-family:monospace; }
|
||||
table td.code { width:100%; }
|
||||
table td.code pre { overflow:hidden; }
|
||||
table.source th { color:#666; }
|
||||
table.source td { font-family:monospace; white-space:pre; border-bottom:1px solid #eee; }
|
||||
ul.traceback { list-style-type:none; color: #222; }
|
||||
ul.traceback li.frame { padding-bottom:1em; color:#4f4f4f; }
|
||||
ul.traceback li.user { background-color:#e0e0e0; color:#000 }
|
||||
div.context { padding:10px 0; overflow:hidden; }
|
||||
div.context ol { padding-left:30px; margin:0 10px; list-style-position: inside; }
|
||||
div.context ol li { font-family:monospace; white-space:pre; color:#777; cursor:pointer; padding-left: 2px; }
|
||||
div.context ol li pre { display:inline; }
|
||||
div.context ol.context-line li { color:#464646; background-color:#dfdfdf; padding: 3px 2px; }
|
||||
div.context ol.context-line li span { position:absolute; right:32px; }
|
||||
.user div.context ol.context-line li { background-color:#bbb; color:#000; }
|
||||
.user div.context ol li { color:#666; }
|
||||
div.commands { margin-left: 40px; }
|
||||
div.commands a { color:#555; text-decoration:none; }
|
||||
.user div.commands a { color: black; }
|
||||
#summary { background: #ffc; }
|
||||
#summary h2 { font-weight: normal; color: #666; }
|
||||
#explanation { background:#eee; }
|
||||
#template, #template-not-exist { background:#f6f6f6; }
|
||||
#template-not-exist ul { margin: 0 0 10px 20px; }
|
||||
#template-not-exist .postmortem-section { margin-bottom: 3px; }
|
||||
#unicode-hint { background:#eee; }
|
||||
#traceback { background:#eee; }
|
||||
#requestinfo { background:#f6f6f6; padding-left:120px; }
|
||||
#summary table { border:none; background:transparent; }
|
||||
#requestinfo h2, #requestinfo h3 { position:relative; margin-left:-100px; }
|
||||
#requestinfo h3 { margin-bottom:-1em; }
|
||||
.error { background: #ffc; }
|
||||
.specific { color:#cc3300; font-weight:bold; }
|
||||
h2 span.commands { font-size:.7em; font-weight:normal; }
|
||||
span.commands a:link {color:#5E5694;}
|
||||
pre.exception_value { font-family: sans-serif; color: #575757; font-size: 1.5em; margin: 10px 0 10px 0; }
|
||||
.append-bottom { margin-bottom: 10px; }
|
||||
</style>
|
||||
{% if not is_email %}
|
||||
<script type="text/javascript">
|
||||
function hideAll(elems) {
|
||||
for (var e = 0; e < elems.length; e++) {
|
||||
elems[e].style.display = 'none';
|
||||
}
|
||||
}
|
||||
window.onload = function() {
|
||||
hideAll(document.querySelectorAll('table.vars'));
|
||||
hideAll(document.querySelectorAll('ol.pre-context'));
|
||||
hideAll(document.querySelectorAll('ol.post-context'));
|
||||
hideAll(document.querySelectorAll('div.pastebin'));
|
||||
}
|
||||
function toggle() {
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
var e = document.getElementById(arguments[i]);
|
||||
if (e) {
|
||||
e.style.display = e.style.display == 'none' ? 'block': 'none';
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function varToggle(link, id) {
|
||||
toggle('v' + id);
|
||||
var s = link.getElementsByTagName('span')[0];
|
||||
var uarr = String.fromCharCode(0x25b6);
|
||||
var darr = String.fromCharCode(0x25bc);
|
||||
s.textContent = s.textContent == uarr ? darr : uarr;
|
||||
return false;
|
||||
}
|
||||
function switchPastebinFriendly(link) {
|
||||
s1 = "Switch to copy-and-paste view";
|
||||
s2 = "Switch back to interactive view";
|
||||
link.textContent = link.textContent.trim() == s1 ? s2: s1;
|
||||
toggle('browserTraceback', 'pastebinTraceback');
|
||||
return false;
|
||||
}
|
||||
</script>
|
||||
{% endif %}
|
||||
</head>
|
||||
<body>
|
||||
<div id="summary">
|
||||
<h1>{% if exception_type %}{{ exception_type }}{% else %}Report{% endif %}
|
||||
{% if request %} at {{ request.path_info }}{% endif %}</h1>
|
||||
<pre class="exception_value">{% if exception_value %}{{ exception_value|force_escape }}{% else %}No exception message supplied{% endif %}</pre>
|
||||
<table class="meta">
|
||||
{% if request %}
|
||||
<tr>
|
||||
<th>Request Method:</th>
|
||||
<td>{{ request.META.REQUEST_METHOD }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Request URL:</th>
|
||||
<td>{{ request.get_raw_uri }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<th>Django Version:</th>
|
||||
<td>{{ django_version_info }}</td>
|
||||
</tr>
|
||||
{% if exception_type %}
|
||||
<tr>
|
||||
<th>Exception Type:</th>
|
||||
<td>{{ exception_type }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if exception_type and exception_value %}
|
||||
<tr>
|
||||
<th>Exception Value:</th>
|
||||
<td><pre>{{ exception_value|force_escape }}</pre></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if lastframe %}
|
||||
<tr>
|
||||
<th>Exception Location:</th>
|
||||
<td>{{ lastframe.filename }} in {{ lastframe.function }}, line {{ lastframe.lineno }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<th>Python Executable:</th>
|
||||
<td>{{ sys_executable }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Python Version:</th>
|
||||
<td>{{ sys_version_info }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Python Path:</th>
|
||||
<td><pre>{{ sys_path|pprint }}</pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Server time:</th>
|
||||
<td>{{server_time|date:"r"}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% if unicode_hint %}
|
||||
<div id="unicode-hint">
|
||||
<h2>Unicode error hint</h2>
|
||||
<p>The string that could not be encoded/decoded was: <strong>{{ unicode_hint }}</strong></p>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if template_does_not_exist %}
|
||||
<div id="template-not-exist">
|
||||
<h2>Template-loader postmortem</h2>
|
||||
{% if postmortem %}
|
||||
<p class="append-bottom">Django tried loading these templates, in this order:</p>
|
||||
{% for entry in postmortem %}
|
||||
<p class="postmortem-section">Using engine <code>{{ entry.backend.name }}</code>:</p>
|
||||
<ul>
|
||||
{% if entry.tried %}
|
||||
{% for attempt in entry.tried %}
|
||||
<li><code>{{ attempt.0.loader_name }}</code>: {{ attempt.0.name }} ({{ attempt.1 }})</li>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<li>This engine did not provide a list of tried templates.</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<p>No templates were found because your 'TEMPLATES' setting is not configured.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if template_info %}
|
||||
<div id="template">
|
||||
<h2>Error during template rendering</h2>
|
||||
<p>In template <code>{{ template_info.name }}</code>, error at line <strong>{{ template_info.line }}</strong></p>
|
||||
<h3>{{ template_info.message }}</h3>
|
||||
<table class="source{% if template_info.top %} cut-top{% endif %}
|
||||
{% if template_info.bottom != template_info.total %} cut-bottom{% endif %}">
|
||||
{% for source_line in template_info.source_lines %}
|
||||
{% if source_line.0 == template_info.line %}
|
||||
<tr class="error"><th>{{ source_line.0 }}</th>
|
||||
<td>{{ template_info.before }}<span class="specific">{{ template_info.during }}</span>{{ template_info.after }}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><th>{{ source_line.0 }}</th>
|
||||
<td>{{ source_line.1 }}</td></tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if frames %}
|
||||
<div id="traceback">
|
||||
<h2>Traceback <span class="commands">{% if not is_email %}<a href="#" onclick="return switchPastebinFriendly(this);">
|
||||
Switch to copy-and-paste view</a></span>{% endif %}
|
||||
</h2>
|
||||
<div id="browserTraceback">
|
||||
<ul class="traceback">
|
||||
{% for frame in frames %}
|
||||
{% ifchanged frame.exc_cause %}{% if frame.exc_cause %}
|
||||
<li><h3>
|
||||
{% if frame.exc_cause_explicit %}
|
||||
The above exception ({{ frame.exc_cause|force_escape }}) was the direct cause of the following exception:
|
||||
{% else %}
|
||||
During handling of the above exception ({{ frame.exc_cause|force_escape }}), another exception occurred:
|
||||
{% endif %}
|
||||
</h3></li>
|
||||
{% endif %}{% endifchanged %}
|
||||
<li class="frame {{ frame.type }}">
|
||||
<code>{{ frame.filename }}</code> in <code>{{ frame.function }}</code>
|
||||
|
||||
{% if frame.context_line %}
|
||||
<div class="context" id="c{{ frame.id }}">
|
||||
{% if frame.pre_context and not is_email %}
|
||||
<ol start="{{ frame.pre_context_lineno }}" class="pre-context" id="pre{{ frame.id }}">
|
||||
{% for line in frame.pre_context %}
|
||||
<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')"><pre>{{ line }}</pre></li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
{% endif %}
|
||||
<ol start="{{ frame.lineno }}" class="context-line">
|
||||
<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')"><pre>{{ frame.context_line }}</pre>{% if not is_email %} <span>…</span>{% endif %}</li>
|
||||
</ol>
|
||||
{% if frame.post_context and not is_email %}
|
||||
<ol start='{{ frame.lineno|add:"1" }}' class="post-context" id="post{{ frame.id }}">
|
||||
{% for line in frame.post_context %}
|
||||
<li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')"><pre>{{ line }}</pre></li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if frame.vars %}
|
||||
<div class="commands">
|
||||
{% if is_email %}
|
||||
<h2>Local Vars</h2>
|
||||
{% else %}
|
||||
<a href="#" onclick="return varToggle(this, '{{ frame.id }}')"><span>▶</span> Local vars</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<table class="vars" id="v{{ frame.id }}">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Variable</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for var in frame.vars|dictsort:0 %}
|
||||
<tr>
|
||||
<td>{{ var.0 }}</td>
|
||||
<td class="code"><pre>{{ var.1 }}</pre></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<form action="http://dpaste.com/" name="pasteform" id="pasteform" method="post">
|
||||
{% if not is_email %}
|
||||
<div id="pastebinTraceback" class="pastebin">
|
||||
<input type="hidden" name="language" value="PythonConsole">
|
||||
<input type="hidden" name="title"
|
||||
value="{{ exception_type }}{% if request %} at {{ request.path_info }}{% endif %}">
|
||||
<input type="hidden" name="source" value="Django Dpaste Agent">
|
||||
<input type="hidden" name="poster" value="Django">
|
||||
<textarea name="content" id="traceback_area" cols="140" rows="25">
|
||||
Environment:
|
||||
|
||||
{% if request %}
|
||||
Request Method: {{ request.META.REQUEST_METHOD }}
|
||||
Request URL: {{ request.get_raw_uri }}
|
||||
{% endif %}
|
||||
Django Version: {{ django_version_info }}
|
||||
Python Version: {{ sys_version_info }}
|
||||
Installed Applications:
|
||||
{{ settings.INSTALLED_APPS|pprint }}
|
||||
Installed Middleware:
|
||||
{{ settings.MIDDLEWARE|pprint }}
|
||||
|
||||
{% if template_does_not_exist %}Template loader postmortem
|
||||
{% if postmortem %}Django tried loading these templates, in this order:
|
||||
{% for entry in postmortem %}
|
||||
Using engine {{ entry.backend.name }}:
|
||||
{% if entry.tried %}{% for attempt in entry.tried %} * {{ attempt.0.loader_name }}: {{ attempt.0.name }} ({{ attempt.1 }})
|
||||
{% endfor %}{% else %} This engine did not provide a list of tried templates.
|
||||
{% endif %}{% endfor %}
|
||||
{% else %}No templates were found because your 'TEMPLATES' setting is not configured.
|
||||
{% endif %}{% endif %}{% if template_info %}
|
||||
Template error:
|
||||
In template {{ template_info.name }}, error at line {{ template_info.line }}
|
||||
{{ template_info.message }}
|
||||
{% for source_line in template_info.source_lines %}{% if source_line.0 == template_info.line %} {{ source_line.0 }} : {{ template_info.before }} {{ template_info.during }} {{ template_info.after }}{% else %} {{ source_line.0 }} : {{ source_line.1 }}{% endif %}{% endfor %}{% endif %}
|
||||
|
||||
Traceback:{% for frame in frames %}
|
||||
{% ifchanged frame.exc_cause %}{% if frame.exc_cause %}{% if frame.exc_cause_explicit %}
|
||||
The above exception ({{ frame.exc_cause|force_escape }}) was the direct cause of the following exception:
|
||||
{% else %}
|
||||
During handling of the above exception ({{ frame.exc_cause|force_escape }}), another exception occurred:
|
||||
{% endif %}{% endif %}{% endifchanged %}
|
||||
File "{{ frame.filename }}" in {{ frame.function }}
|
||||
{% if frame.context_line %} {{ frame.lineno }}. {{ frame.context_line }}{% endif %}{% endfor %}
|
||||
|
||||
Exception Type: {{ exception_type }}{% if request %} at {{ request.path_info }}{% endif %}
|
||||
Exception Value: {{ exception_value|force_escape }}
|
||||
</textarea>
|
||||
<br><br>
|
||||
<input type="submit" value="Share this traceback on a public website">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<div id="requestinfo">
|
||||
<h2>Request information</h2>
|
||||
|
||||
{% if request %}
|
||||
{% if user_str %}
|
||||
<h3 id="user-info">USER</h3>
|
||||
<p>{{ user_str }}</p>
|
||||
{% endif %}
|
||||
|
||||
<h3 id="get-info">GET</h3>
|
||||
{% if request.GET %}
|
||||
<table class="req">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Variable</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for k, v in request_GET_items %}
|
||||
<tr>
|
||||
<td>{{ k }}</td>
|
||||
<td class="code"><pre>{{ v|pprint }}</pre></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p>No GET data</p>
|
||||
{% endif %}
|
||||
|
||||
<h3 id="post-info">POST</h3>
|
||||
{% if filtered_POST_items %}
|
||||
<table class="req">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Variable</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for k, v in filtered_POST_items %}
|
||||
<tr>
|
||||
<td>{{ k }}</td>
|
||||
<td class="code"><pre>{{ v|pprint }}</pre></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p>No POST data</p>
|
||||
{% endif %}
|
||||
<h3 id="files-info">FILES</h3>
|
||||
{% if request.FILES %}
|
||||
<table class="req">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Variable</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for k, v in request_FILES_items %}
|
||||
<tr>
|
||||
<td>{{ k }}</td>
|
||||
<td class="code"><pre>{{ v|pprint }}</pre></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p>No FILES data</p>
|
||||
{% endif %}
|
||||
|
||||
|
||||
<h3 id="cookie-info">COOKIES</h3>
|
||||
{% if request.COOKIES %}
|
||||
<table class="req">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Variable</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for k, v in request_COOKIES_items %}
|
||||
<tr>
|
||||
<td>{{ k }}</td>
|
||||
<td class="code"><pre>{{ v|pprint }}</pre></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p>No cookie data</p>
|
||||
{% endif %}
|
||||
|
||||
<h3 id="meta-info">META</h3>
|
||||
<table class="req">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Variable</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for var in request.META.items|dictsort:0 %}
|
||||
<tr>
|
||||
<td>{{ var.0 }}</td>
|
||||
<td class="code"><pre>{{ var.1|pprint }}</pre></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p>Request data not supplied</p>
|
||||
{% endif %}
|
||||
|
||||
<h3 id="settings-info">Settings</h3>
|
||||
<h4>Using settings module <code>{{ settings.SETTINGS_MODULE }}</code></h4>
|
||||
<table class="req">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Setting</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for var in settings.items|dictsort:0 %}
|
||||
<tr>
|
||||
<td>{{ var.0 }}</td>
|
||||
<td class="code"><pre>{{ var.1|pprint }}</pre></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
{% if not is_email %}
|
||||
<div id="explanation">
|
||||
<p>
|
||||
You're seeing this error because you have <code>DEBUG = True</code> in your
|
||||
Django settings file. Change that to <code>False</code>, and Django will
|
||||
display a standard page generated by the handler for this status code.
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user