일단 커밋. 오랫동안 커밋을 안해서 꼬였다.
리팩토리 중.
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
from django.views.generic.base import View
|
||||
|
||||
__all__ = ["View"]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
80
.venv/lib/python3.12/site-packages/django/views/csrf.py
Normal file
80
.venv/lib/python3.12/site-packages/django/views/csrf.py
Normal file
@@ -0,0 +1,80 @@
|
||||
from pathlib import Path
|
||||
|
||||
from django.conf import settings
|
||||
from django.http import HttpResponseForbidden
|
||||
from django.template import Context, Engine, TemplateDoesNotExist, loader
|
||||
from django.utils.translation import gettext as _
|
||||
from django.utils.version import get_docs_version
|
||||
|
||||
CSRF_FAILURE_TEMPLATE_NAME = "403_csrf.html"
|
||||
|
||||
|
||||
def builtin_template_path(name):
|
||||
"""
|
||||
Return a path to a builtin template.
|
||||
|
||||
Avoid calling this function at the module level or in a class-definition
|
||||
because __file__ may not exist, e.g. in frozen environments.
|
||||
"""
|
||||
return Path(__file__).parent / "templates" / name
|
||||
|
||||
|
||||
def csrf_failure(request, reason="", template_name=CSRF_FAILURE_TEMPLATE_NAME):
|
||||
"""
|
||||
Default view used when request fails CSRF protection
|
||||
"""
|
||||
from django.middleware.csrf import REASON_NO_CSRF_COOKIE, REASON_NO_REFERER
|
||||
|
||||
c = {
|
||||
"title": _("Forbidden"),
|
||||
"main": _("CSRF verification failed. Request aborted."),
|
||||
"reason": reason,
|
||||
"no_referer": reason == REASON_NO_REFERER,
|
||||
"no_referer1": _(
|
||||
"You are seeing this message because this HTTPS site requires a "
|
||||
"“Referer header” to be sent by your web browser, but none was "
|
||||
"sent. This header is required for security reasons, to ensure "
|
||||
"that your browser is not being hijacked by third parties."
|
||||
),
|
||||
"no_referer2": _(
|
||||
"If you have configured your browser to disable “Referer” headers, "
|
||||
"please re-enable them, at least for this site, or for HTTPS "
|
||||
"connections, or for “same-origin” requests."
|
||||
),
|
||||
"no_referer3": _(
|
||||
'If you are using the <meta name="referrer" '
|
||||
'content="no-referrer"> tag or including the “Referrer-Policy: '
|
||||
"no-referrer” header, please remove them. The CSRF protection "
|
||||
"requires the “Referer” header to do strict referer checking. If "
|
||||
"you’re concerned about privacy, use alternatives like "
|
||||
'<a rel="noreferrer" …> for links to third-party sites.'
|
||||
),
|
||||
"no_cookie": reason == REASON_NO_CSRF_COOKIE,
|
||||
"no_cookie1": _(
|
||||
"You are seeing this message because this site requires a CSRF "
|
||||
"cookie when submitting forms. This cookie is required for "
|
||||
"security reasons, to ensure that your browser is not being "
|
||||
"hijacked by third parties."
|
||||
),
|
||||
"no_cookie2": _(
|
||||
"If you have configured your browser to disable cookies, please "
|
||||
"re-enable them, at least for this site, or for “same-origin” "
|
||||
"requests."
|
||||
),
|
||||
"DEBUG": settings.DEBUG,
|
||||
"docs_version": get_docs_version(),
|
||||
"more": _("More information is available with DEBUG=True."),
|
||||
}
|
||||
try:
|
||||
t = loader.get_template(template_name)
|
||||
body = t.render(request=request)
|
||||
except TemplateDoesNotExist:
|
||||
if template_name == CSRF_FAILURE_TEMPLATE_NAME:
|
||||
# If the default template doesn't exist, use the fallback template.
|
||||
with builtin_template_path("csrf_403.html").open(encoding="utf-8") as fh:
|
||||
t = Engine().from_string(fh.read())
|
||||
body = t.render(Context(c))
|
||||
else:
|
||||
# Raise if a developer-specified template doesn't exist.
|
||||
raise
|
||||
return HttpResponseForbidden(body)
|
||||
665
.venv/lib/python3.12/site-packages/django/views/debug.py
Normal file
665
.venv/lib/python3.12/site-packages/django/views/debug.py
Normal file
@@ -0,0 +1,665 @@
|
||||
import functools
|
||||
import inspect
|
||||
import itertools
|
||||
import re
|
||||
import sys
|
||||
import types
|
||||
import warnings
|
||||
from pathlib import Path
|
||||
|
||||
from django.conf import settings
|
||||
from django.http import Http404, HttpResponse, HttpResponseNotFound
|
||||
from django.template import Context, Engine, TemplateDoesNotExist
|
||||
from django.template.defaultfilters import pprint
|
||||
from django.urls import resolve
|
||||
from django.utils import timezone
|
||||
from django.utils.datastructures import MultiValueDict
|
||||
from django.utils.encoding import force_str
|
||||
from django.utils.module_loading import import_string
|
||||
from django.utils.regex_helper import _lazy_re_compile
|
||||
from django.utils.version import PY311, get_docs_version
|
||||
from django.views.decorators.debug import coroutine_functions_to_sensitive_variables
|
||||
|
||||
# Minimal Django templates engine to render the error templates
|
||||
# regardless of the project's TEMPLATES setting. Templates are
|
||||
# read directly from the filesystem so that the error handler
|
||||
# works even if the template loader is broken.
|
||||
DEBUG_ENGINE = Engine(
|
||||
debug=True,
|
||||
libraries={"i18n": "django.templatetags.i18n"},
|
||||
)
|
||||
|
||||
|
||||
def builtin_template_path(name):
|
||||
"""
|
||||
Return a path to a builtin template.
|
||||
|
||||
Avoid calling this function at the module level or in a class-definition
|
||||
because __file__ may not exist, e.g. in frozen environments.
|
||||
"""
|
||||
return Path(__file__).parent / "templates" / name
|
||||
|
||||
|
||||
class ExceptionCycleWarning(UserWarning):
|
||||
pass
|
||||
|
||||
|
||||
class CallableSettingWrapper:
|
||||
"""
|
||||
Object to wrap callable appearing in settings.
|
||||
* Not to call in the debug page (#21345).
|
||||
* Not to break the debug page if the callable forbidding to set attributes
|
||||
(#23070).
|
||||
"""
|
||||
|
||||
def __init__(self, callable_setting):
|
||||
self._wrapped = callable_setting
|
||||
|
||||
def __repr__(self):
|
||||
return repr(self._wrapped)
|
||||
|
||||
|
||||
def technical_500_response(request, exc_type, exc_value, tb, status_code=500):
|
||||
"""
|
||||
Create a technical server error response. The last three arguments are
|
||||
the values returned from sys.exc_info() and friends.
|
||||
"""
|
||||
reporter = get_exception_reporter_class(request)(request, exc_type, exc_value, tb)
|
||||
if request.accepts("text/html"):
|
||||
html = reporter.get_traceback_html()
|
||||
return HttpResponse(html, status=status_code)
|
||||
else:
|
||||
text = reporter.get_traceback_text()
|
||||
return HttpResponse(
|
||||
text, status=status_code, content_type="text/plain; charset=utf-8"
|
||||
)
|
||||
|
||||
|
||||
@functools.lru_cache
|
||||
def get_default_exception_reporter_filter():
|
||||
# Instantiate the default filter for the first time and cache it.
|
||||
return import_string(settings.DEFAULT_EXCEPTION_REPORTER_FILTER)()
|
||||
|
||||
|
||||
def get_exception_reporter_filter(request):
|
||||
default_filter = get_default_exception_reporter_filter()
|
||||
return getattr(request, "exception_reporter_filter", default_filter)
|
||||
|
||||
|
||||
def get_exception_reporter_class(request):
|
||||
default_exception_reporter_class = import_string(
|
||||
settings.DEFAULT_EXCEPTION_REPORTER
|
||||
)
|
||||
return getattr(
|
||||
request, "exception_reporter_class", default_exception_reporter_class
|
||||
)
|
||||
|
||||
|
||||
def get_caller(request):
|
||||
resolver_match = request.resolver_match
|
||||
if resolver_match is None:
|
||||
try:
|
||||
resolver_match = resolve(request.path)
|
||||
except Http404:
|
||||
pass
|
||||
return "" if resolver_match is None else resolver_match._func_path
|
||||
|
||||
|
||||
class SafeExceptionReporterFilter:
|
||||
"""
|
||||
Use annotations made by the sensitive_post_parameters and
|
||||
sensitive_variables decorators to filter out sensitive information.
|
||||
"""
|
||||
|
||||
cleansed_substitute = "********************"
|
||||
hidden_settings = _lazy_re_compile(
|
||||
"API|AUTH|TOKEN|KEY|SECRET|PASS|SIGNATURE|HTTP_COOKIE", flags=re.I
|
||||
)
|
||||
|
||||
def cleanse_setting(self, key, value):
|
||||
"""
|
||||
Cleanse an individual setting key/value of sensitive content. If the
|
||||
value is a dictionary, recursively cleanse the keys in that dictionary.
|
||||
"""
|
||||
if key == settings.SESSION_COOKIE_NAME:
|
||||
is_sensitive = True
|
||||
else:
|
||||
try:
|
||||
is_sensitive = self.hidden_settings.search(key)
|
||||
except TypeError:
|
||||
is_sensitive = False
|
||||
|
||||
if is_sensitive:
|
||||
cleansed = self.cleansed_substitute
|
||||
elif isinstance(value, dict):
|
||||
cleansed = {k: self.cleanse_setting(k, v) for k, v in value.items()}
|
||||
elif isinstance(value, list):
|
||||
cleansed = [self.cleanse_setting("", v) for v in value]
|
||||
elif isinstance(value, tuple):
|
||||
cleansed = tuple([self.cleanse_setting("", v) for v in value])
|
||||
else:
|
||||
cleansed = value
|
||||
|
||||
if callable(cleansed):
|
||||
cleansed = CallableSettingWrapper(cleansed)
|
||||
|
||||
return cleansed
|
||||
|
||||
def get_safe_settings(self):
|
||||
"""
|
||||
Return a dictionary of the settings module with values of sensitive
|
||||
settings replaced with stars (*********).
|
||||
"""
|
||||
settings_dict = {}
|
||||
for k in dir(settings):
|
||||
if k.isupper():
|
||||
settings_dict[k] = self.cleanse_setting(k, getattr(settings, k))
|
||||
return settings_dict
|
||||
|
||||
def get_safe_request_meta(self, request):
|
||||
"""
|
||||
Return a dictionary of request.META with sensitive values redacted.
|
||||
"""
|
||||
if not hasattr(request, "META"):
|
||||
return {}
|
||||
return {k: self.cleanse_setting(k, v) for k, v in request.META.items()}
|
||||
|
||||
def get_safe_cookies(self, request):
|
||||
"""
|
||||
Return a dictionary of request.COOKIES with sensitive values redacted.
|
||||
"""
|
||||
if not hasattr(request, "COOKIES"):
|
||||
return {}
|
||||
return {k: self.cleanse_setting(k, v) for k, v in request.COOKIES.items()}
|
||||
|
||||
def is_active(self, request):
|
||||
"""
|
||||
This filter is to add safety in production environments (i.e. DEBUG
|
||||
is False). If DEBUG is True then your site is not safe anyway.
|
||||
This hook is provided as a convenience to easily activate or
|
||||
deactivate the filter on a per request basis.
|
||||
"""
|
||||
return settings.DEBUG is False
|
||||
|
||||
def get_cleansed_multivaluedict(self, request, multivaluedict):
|
||||
"""
|
||||
Replace the keys in a MultiValueDict marked as sensitive with stars.
|
||||
This mitigates leaking sensitive POST parameters if something like
|
||||
request.POST['nonexistent_key'] throws an exception (#21098).
|
||||
"""
|
||||
sensitive_post_parameters = getattr(request, "sensitive_post_parameters", [])
|
||||
if self.is_active(request) and sensitive_post_parameters:
|
||||
multivaluedict = multivaluedict.copy()
|
||||
for param in sensitive_post_parameters:
|
||||
if param in multivaluedict:
|
||||
multivaluedict[param] = self.cleansed_substitute
|
||||
return multivaluedict
|
||||
|
||||
def get_post_parameters(self, request):
|
||||
"""
|
||||
Replace the values of POST parameters marked as sensitive with
|
||||
stars (*********).
|
||||
"""
|
||||
if request is None:
|
||||
return {}
|
||||
else:
|
||||
sensitive_post_parameters = getattr(
|
||||
request, "sensitive_post_parameters", []
|
||||
)
|
||||
if self.is_active(request) and sensitive_post_parameters:
|
||||
cleansed = request.POST.copy()
|
||||
if sensitive_post_parameters == "__ALL__":
|
||||
# Cleanse all parameters.
|
||||
for k in cleansed:
|
||||
cleansed[k] = self.cleansed_substitute
|
||||
return cleansed
|
||||
else:
|
||||
# Cleanse only the specified parameters.
|
||||
for param in sensitive_post_parameters:
|
||||
if param in cleansed:
|
||||
cleansed[param] = self.cleansed_substitute
|
||||
return cleansed
|
||||
else:
|
||||
return request.POST
|
||||
|
||||
def cleanse_special_types(self, request, value):
|
||||
try:
|
||||
# If value is lazy or a complex object of another kind, this check
|
||||
# might raise an exception. isinstance checks that lazy
|
||||
# MultiValueDicts will have a return value.
|
||||
is_multivalue_dict = isinstance(value, MultiValueDict)
|
||||
except Exception as e:
|
||||
return "{!r} while evaluating {!r}".format(e, value)
|
||||
|
||||
if is_multivalue_dict:
|
||||
# Cleanse MultiValueDicts (request.POST is the one we usually care about)
|
||||
value = self.get_cleansed_multivaluedict(request, value)
|
||||
return value
|
||||
|
||||
def get_traceback_frame_variables(self, request, tb_frame):
|
||||
"""
|
||||
Replace the values of variables marked as sensitive with
|
||||
stars (*********).
|
||||
"""
|
||||
sensitive_variables = None
|
||||
|
||||
# Coroutines don't have a proper `f_back` so they need to be inspected
|
||||
# separately. Handle this by stashing the registered sensitive
|
||||
# variables in a global dict indexed by `hash(file_path:line_number)`.
|
||||
if (
|
||||
tb_frame.f_code.co_flags & inspect.CO_COROUTINE != 0
|
||||
and tb_frame.f_code.co_name != "sensitive_variables_wrapper"
|
||||
):
|
||||
key = hash(
|
||||
f"{tb_frame.f_code.co_filename}:{tb_frame.f_code.co_firstlineno}"
|
||||
)
|
||||
sensitive_variables = coroutine_functions_to_sensitive_variables.get(
|
||||
key, None
|
||||
)
|
||||
|
||||
if sensitive_variables is None:
|
||||
# Loop through the frame's callers to see if the
|
||||
# sensitive_variables decorator was used.
|
||||
current_frame = tb_frame
|
||||
while current_frame is not None:
|
||||
if (
|
||||
current_frame.f_code.co_name == "sensitive_variables_wrapper"
|
||||
and "sensitive_variables_wrapper" in current_frame.f_locals
|
||||
):
|
||||
# The sensitive_variables decorator was used, so take note
|
||||
# of the sensitive variables' names.
|
||||
wrapper = current_frame.f_locals["sensitive_variables_wrapper"]
|
||||
sensitive_variables = getattr(wrapper, "sensitive_variables", None)
|
||||
break
|
||||
current_frame = current_frame.f_back
|
||||
|
||||
cleansed = {}
|
||||
if self.is_active(request) and sensitive_variables:
|
||||
if sensitive_variables == "__ALL__":
|
||||
# Cleanse all variables
|
||||
for name in tb_frame.f_locals:
|
||||
cleansed[name] = self.cleansed_substitute
|
||||
else:
|
||||
# Cleanse specified variables
|
||||
for name, value in tb_frame.f_locals.items():
|
||||
if name in sensitive_variables:
|
||||
value = self.cleansed_substitute
|
||||
else:
|
||||
value = self.cleanse_special_types(request, value)
|
||||
cleansed[name] = value
|
||||
else:
|
||||
# Potentially cleanse the request and any MultiValueDicts if they
|
||||
# are one of the frame variables.
|
||||
for name, value in tb_frame.f_locals.items():
|
||||
cleansed[name] = self.cleanse_special_types(request, value)
|
||||
|
||||
if (
|
||||
tb_frame.f_code.co_name == "sensitive_variables_wrapper"
|
||||
and "sensitive_variables_wrapper" in tb_frame.f_locals
|
||||
):
|
||||
# For good measure, obfuscate the decorated function's arguments in
|
||||
# the sensitive_variables decorator's frame, in case the variables
|
||||
# associated with those arguments were meant to be obfuscated from
|
||||
# the decorated function's frame.
|
||||
cleansed["func_args"] = self.cleansed_substitute
|
||||
cleansed["func_kwargs"] = self.cleansed_substitute
|
||||
|
||||
return cleansed.items()
|
||||
|
||||
|
||||
class ExceptionReporter:
|
||||
"""Organize and coordinate reporting on exceptions."""
|
||||
|
||||
@property
|
||||
def html_template_path(self):
|
||||
return builtin_template_path("technical_500.html")
|
||||
|
||||
@property
|
||||
def text_template_path(self):
|
||||
return builtin_template_path("technical_500.txt")
|
||||
|
||||
def __init__(self, request, exc_type, exc_value, tb, is_email=False):
|
||||
self.request = request
|
||||
self.filter = get_exception_reporter_filter(self.request)
|
||||
self.exc_type = exc_type
|
||||
self.exc_value = exc_value
|
||||
self.tb = tb
|
||||
self.is_email = is_email
|
||||
|
||||
self.template_info = getattr(self.exc_value, "template_debug", None)
|
||||
self.template_does_not_exist = False
|
||||
self.postmortem = None
|
||||
|
||||
def _get_raw_insecure_uri(self):
|
||||
"""
|
||||
Return an absolute URI from variables available in this request. Skip
|
||||
allowed hosts protection, so may return insecure URI.
|
||||
"""
|
||||
return "{scheme}://{host}{path}".format(
|
||||
scheme=self.request.scheme,
|
||||
host=self.request._get_raw_host(),
|
||||
path=self.request.get_full_path(),
|
||||
)
|
||||
|
||||
def get_traceback_data(self):
|
||||
"""Return a dictionary containing traceback information."""
|
||||
if self.exc_type and issubclass(self.exc_type, TemplateDoesNotExist):
|
||||
self.template_does_not_exist = True
|
||||
self.postmortem = self.exc_value.chain or [self.exc_value]
|
||||
|
||||
frames = self.get_traceback_frames()
|
||||
for i, frame in enumerate(frames):
|
||||
if "vars" in frame:
|
||||
frame_vars = []
|
||||
for k, v in frame["vars"]:
|
||||
v = pprint(v)
|
||||
# Trim large blobs of data
|
||||
if len(v) > 4096:
|
||||
v = "%s… <trimmed %d bytes string>" % (v[0:4096], len(v))
|
||||
frame_vars.append((k, v))
|
||||
frame["vars"] = frame_vars
|
||||
frames[i] = frame
|
||||
|
||||
unicode_hint = ""
|
||||
if self.exc_type and issubclass(self.exc_type, UnicodeError):
|
||||
start = getattr(self.exc_value, "start", None)
|
||||
end = getattr(self.exc_value, "end", None)
|
||||
if start is not None and end is not None:
|
||||
unicode_str = self.exc_value.args[1]
|
||||
unicode_hint = force_str(
|
||||
unicode_str[max(start - 5, 0) : min(end + 5, len(unicode_str))],
|
||||
"ascii",
|
||||
errors="replace",
|
||||
)
|
||||
from django import get_version
|
||||
|
||||
if self.request is None:
|
||||
user_str = None
|
||||
else:
|
||||
try:
|
||||
user_str = str(self.request.user)
|
||||
except Exception:
|
||||
# request.user may raise OperationalError if the database is
|
||||
# unavailable, for example.
|
||||
user_str = "[unable to retrieve the current user]"
|
||||
|
||||
c = {
|
||||
"is_email": self.is_email,
|
||||
"unicode_hint": unicode_hint,
|
||||
"frames": frames,
|
||||
"request": self.request,
|
||||
"request_meta": self.filter.get_safe_request_meta(self.request),
|
||||
"request_COOKIES_items": self.filter.get_safe_cookies(self.request).items(),
|
||||
"user_str": user_str,
|
||||
"filtered_POST_items": list(
|
||||
self.filter.get_post_parameters(self.request).items()
|
||||
),
|
||||
"settings": self.filter.get_safe_settings(),
|
||||
"sys_executable": sys.executable,
|
||||
"sys_version_info": "%d.%d.%d" % sys.version_info[0:3],
|
||||
"server_time": timezone.now(),
|
||||
"django_version_info": get_version(),
|
||||
"sys_path": sys.path,
|
||||
"template_info": self.template_info,
|
||||
"template_does_not_exist": self.template_does_not_exist,
|
||||
"postmortem": self.postmortem,
|
||||
}
|
||||
if self.request is not None:
|
||||
c["request_GET_items"] = self.request.GET.items()
|
||||
c["request_FILES_items"] = self.request.FILES.items()
|
||||
c["request_insecure_uri"] = self._get_raw_insecure_uri()
|
||||
c["raising_view_name"] = get_caller(self.request)
|
||||
|
||||
# Check whether exception info is available
|
||||
if self.exc_type:
|
||||
c["exception_type"] = self.exc_type.__name__
|
||||
if self.exc_value:
|
||||
c["exception_value"] = str(self.exc_value)
|
||||
if exc_notes := getattr(self.exc_value, "__notes__", None):
|
||||
c["exception_notes"] = "\n" + "\n".join(exc_notes)
|
||||
if frames:
|
||||
c["lastframe"] = frames[-1]
|
||||
return c
|
||||
|
||||
def get_traceback_html(self):
|
||||
"""Return HTML version of debug 500 HTTP error page."""
|
||||
with self.html_template_path.open(encoding="utf-8") as fh:
|
||||
t = DEBUG_ENGINE.from_string(fh.read())
|
||||
c = Context(self.get_traceback_data(), use_l10n=False)
|
||||
return t.render(c)
|
||||
|
||||
def get_traceback_text(self):
|
||||
"""Return plain text version of debug 500 HTTP error page."""
|
||||
with self.text_template_path.open(encoding="utf-8") as fh:
|
||||
t = DEBUG_ENGINE.from_string(fh.read())
|
||||
c = Context(self.get_traceback_data(), autoescape=False, use_l10n=False)
|
||||
return t.render(c)
|
||||
|
||||
def _get_source(self, filename, loader, module_name):
|
||||
source = None
|
||||
if hasattr(loader, "get_source"):
|
||||
try:
|
||||
source = loader.get_source(module_name)
|
||||
except ImportError:
|
||||
pass
|
||||
if source is not None:
|
||||
source = source.splitlines()
|
||||
if source is None:
|
||||
try:
|
||||
with open(filename, "rb") as fp:
|
||||
source = fp.read().splitlines()
|
||||
except OSError:
|
||||
pass
|
||||
return source
|
||||
|
||||
def _get_lines_from_file(
|
||||
self, filename, lineno, context_lines, loader=None, module_name=None
|
||||
):
|
||||
"""
|
||||
Return context_lines before and after lineno from file.
|
||||
Return (pre_context_lineno, pre_context, context_line, post_context).
|
||||
"""
|
||||
source = self._get_source(filename, loader, module_name)
|
||||
if source is None:
|
||||
return None, [], None, []
|
||||
|
||||
# If we just read the source from a file, or if the loader did not
|
||||
# apply tokenize.detect_encoding to decode the source into a
|
||||
# string, then we should do that ourselves.
|
||||
if isinstance(source[0], bytes):
|
||||
encoding = "ascii"
|
||||
for line in source[:2]:
|
||||
# File coding may be specified. Match pattern from PEP-263
|
||||
# (https://www.python.org/dev/peps/pep-0263/)
|
||||
match = re.search(rb"coding[:=]\s*([-\w.]+)", line)
|
||||
if match:
|
||||
encoding = match[1].decode("ascii")
|
||||
break
|
||||
source = [str(sline, encoding, "replace") for sline in source]
|
||||
|
||||
lower_bound = max(0, lineno - context_lines)
|
||||
upper_bound = lineno + context_lines
|
||||
|
||||
try:
|
||||
pre_context = source[lower_bound:lineno]
|
||||
context_line = source[lineno]
|
||||
post_context = source[lineno + 1 : upper_bound]
|
||||
except IndexError:
|
||||
return None, [], None, []
|
||||
return lower_bound, pre_context, context_line, post_context
|
||||
|
||||
def _get_explicit_or_implicit_cause(self, exc_value):
|
||||
explicit = getattr(exc_value, "__cause__", None)
|
||||
suppress_context = getattr(exc_value, "__suppress_context__", None)
|
||||
implicit = getattr(exc_value, "__context__", None)
|
||||
return explicit or (None if suppress_context else implicit)
|
||||
|
||||
def get_traceback_frames(self):
|
||||
# Get the exception and all its causes
|
||||
exceptions = []
|
||||
exc_value = self.exc_value
|
||||
while exc_value:
|
||||
exceptions.append(exc_value)
|
||||
exc_value = self._get_explicit_or_implicit_cause(exc_value)
|
||||
if exc_value in exceptions:
|
||||
warnings.warn(
|
||||
"Cycle in the exception chain detected: exception '%s' "
|
||||
"encountered again." % exc_value,
|
||||
ExceptionCycleWarning,
|
||||
)
|
||||
# Avoid infinite loop if there's a cyclic reference (#29393).
|
||||
break
|
||||
|
||||
frames = []
|
||||
# No exceptions were supplied to ExceptionReporter
|
||||
if not exceptions:
|
||||
return frames
|
||||
|
||||
# In case there's just one exception, take the traceback from self.tb
|
||||
exc_value = exceptions.pop()
|
||||
tb = self.tb if not exceptions else exc_value.__traceback__
|
||||
while True:
|
||||
frames.extend(self.get_exception_traceback_frames(exc_value, tb))
|
||||
try:
|
||||
exc_value = exceptions.pop()
|
||||
except IndexError:
|
||||
break
|
||||
tb = exc_value.__traceback__
|
||||
return frames
|
||||
|
||||
def get_exception_traceback_frames(self, exc_value, tb):
|
||||
exc_cause = self._get_explicit_or_implicit_cause(exc_value)
|
||||
exc_cause_explicit = getattr(exc_value, "__cause__", True)
|
||||
if tb is None:
|
||||
yield {
|
||||
"exc_cause": exc_cause,
|
||||
"exc_cause_explicit": exc_cause_explicit,
|
||||
"tb": None,
|
||||
"type": "user",
|
||||
}
|
||||
while tb is not None:
|
||||
# Support for __traceback_hide__ which is used by a few libraries
|
||||
# to hide internal frames.
|
||||
if tb.tb_frame.f_locals.get("__traceback_hide__"):
|
||||
tb = tb.tb_next
|
||||
continue
|
||||
filename = tb.tb_frame.f_code.co_filename
|
||||
function = tb.tb_frame.f_code.co_name
|
||||
lineno = tb.tb_lineno - 1
|
||||
loader = tb.tb_frame.f_globals.get("__loader__")
|
||||
module_name = tb.tb_frame.f_globals.get("__name__") or ""
|
||||
(
|
||||
pre_context_lineno,
|
||||
pre_context,
|
||||
context_line,
|
||||
post_context,
|
||||
) = self._get_lines_from_file(
|
||||
filename,
|
||||
lineno,
|
||||
7,
|
||||
loader,
|
||||
module_name,
|
||||
)
|
||||
if pre_context_lineno is None:
|
||||
pre_context_lineno = lineno
|
||||
pre_context = []
|
||||
context_line = "<source code not available>"
|
||||
post_context = []
|
||||
|
||||
colno = tb_area_colno = ""
|
||||
if PY311:
|
||||
_, _, start_column, end_column = next(
|
||||
itertools.islice(
|
||||
tb.tb_frame.f_code.co_positions(), tb.tb_lasti // 2, None
|
||||
)
|
||||
)
|
||||
if start_column and end_column:
|
||||
underline = "^" * (end_column - start_column)
|
||||
spaces = " " * (start_column + len(str(lineno + 1)) + 2)
|
||||
colno = f"\n{spaces}{underline}"
|
||||
tb_area_spaces = " " * (
|
||||
4
|
||||
+ start_column
|
||||
- (len(context_line) - len(context_line.lstrip()))
|
||||
)
|
||||
tb_area_colno = f"\n{tb_area_spaces}{underline}"
|
||||
yield {
|
||||
"exc_cause": exc_cause,
|
||||
"exc_cause_explicit": exc_cause_explicit,
|
||||
"tb": tb,
|
||||
"type": "django" if module_name.startswith("django.") else "user",
|
||||
"filename": filename,
|
||||
"function": function,
|
||||
"lineno": lineno + 1,
|
||||
"vars": self.filter.get_traceback_frame_variables(
|
||||
self.request, tb.tb_frame
|
||||
),
|
||||
"id": id(tb),
|
||||
"pre_context": pre_context,
|
||||
"context_line": context_line,
|
||||
"post_context": post_context,
|
||||
"pre_context_lineno": pre_context_lineno + 1,
|
||||
"colno": colno,
|
||||
"tb_area_colno": tb_area_colno,
|
||||
}
|
||||
tb = tb.tb_next
|
||||
|
||||
|
||||
def technical_404_response(request, exception):
|
||||
"""Create a technical 404 error response. `exception` is the Http404."""
|
||||
try:
|
||||
error_url = exception.args[0]["path"]
|
||||
except (IndexError, TypeError, KeyError):
|
||||
error_url = request.path_info[1:] # Trim leading slash
|
||||
|
||||
try:
|
||||
tried = exception.args[0]["tried"]
|
||||
except (IndexError, TypeError, KeyError):
|
||||
resolved = True
|
||||
tried = request.resolver_match.tried if request.resolver_match else None
|
||||
else:
|
||||
resolved = False
|
||||
if not tried or ( # empty URLconf
|
||||
request.path_info == "/"
|
||||
and len(tried) == 1
|
||||
and len(tried[0]) == 1 # default URLconf
|
||||
and getattr(tried[0][0], "app_name", "")
|
||||
== getattr(tried[0][0], "namespace", "")
|
||||
== "admin"
|
||||
):
|
||||
return default_urlconf(request)
|
||||
|
||||
urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF)
|
||||
if isinstance(urlconf, types.ModuleType):
|
||||
urlconf = urlconf.__name__
|
||||
|
||||
with builtin_template_path("technical_404.html").open(encoding="utf-8") as fh:
|
||||
t = DEBUG_ENGINE.from_string(fh.read())
|
||||
reporter_filter = get_default_exception_reporter_filter()
|
||||
c = Context(
|
||||
{
|
||||
"urlconf": urlconf,
|
||||
"root_urlconf": settings.ROOT_URLCONF,
|
||||
"request_path": error_url,
|
||||
"urlpatterns": tried,
|
||||
"resolved": resolved,
|
||||
"reason": str(exception),
|
||||
"request": request,
|
||||
"settings": reporter_filter.get_safe_settings(),
|
||||
"raising_view_name": get_caller(request),
|
||||
}
|
||||
)
|
||||
return HttpResponseNotFound(t.render(c))
|
||||
|
||||
|
||||
def default_urlconf(request):
|
||||
"""Create an empty URLconf 404 error response."""
|
||||
with builtin_template_path("default_urlconf.html").open(encoding="utf-8") as fh:
|
||||
t = DEBUG_ENGINE.from_string(fh.read())
|
||||
c = Context(
|
||||
{
|
||||
"version": get_docs_version(),
|
||||
}
|
||||
)
|
||||
|
||||
return HttpResponse(t.render(c))
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,84 @@
|
||||
from functools import wraps
|
||||
|
||||
from asgiref.sync import iscoroutinefunction
|
||||
|
||||
from django.middleware.cache import CacheMiddleware
|
||||
from django.utils.cache import add_never_cache_headers, patch_cache_control
|
||||
from django.utils.decorators import decorator_from_middleware_with_args
|
||||
|
||||
|
||||
def cache_page(timeout, *, cache=None, key_prefix=None):
|
||||
"""
|
||||
Decorator for views that tries getting the page from the cache and
|
||||
populates the cache if the page isn't in the cache yet.
|
||||
|
||||
The cache is keyed by the URL and some data from the headers.
|
||||
Additionally there is the key prefix that is used to distinguish different
|
||||
cache areas in a multi-site setup. You could use the
|
||||
get_current_site().domain, for example, as that is unique across a Django
|
||||
project.
|
||||
|
||||
Additionally, all headers from the response's Vary header will be taken
|
||||
into account on caching -- just like the middleware does.
|
||||
"""
|
||||
return decorator_from_middleware_with_args(CacheMiddleware)(
|
||||
page_timeout=timeout,
|
||||
cache_alias=cache,
|
||||
key_prefix=key_prefix,
|
||||
)
|
||||
|
||||
|
||||
def _check_request(request, decorator_name):
|
||||
# Ensure argument looks like a request.
|
||||
if not hasattr(request, "META"):
|
||||
raise TypeError(
|
||||
f"{decorator_name} didn't receive an HttpRequest. If you are "
|
||||
"decorating a classmethod, be sure to use @method_decorator."
|
||||
)
|
||||
|
||||
|
||||
def cache_control(**kwargs):
|
||||
def _cache_controller(viewfunc):
|
||||
if iscoroutinefunction(viewfunc):
|
||||
|
||||
async def _view_wrapper(request, *args, **kw):
|
||||
_check_request(request, "cache_control")
|
||||
response = await viewfunc(request, *args, **kw)
|
||||
patch_cache_control(response, **kwargs)
|
||||
return response
|
||||
|
||||
else:
|
||||
|
||||
def _view_wrapper(request, *args, **kw):
|
||||
_check_request(request, "cache_control")
|
||||
response = viewfunc(request, *args, **kw)
|
||||
patch_cache_control(response, **kwargs)
|
||||
return response
|
||||
|
||||
return wraps(viewfunc)(_view_wrapper)
|
||||
|
||||
return _cache_controller
|
||||
|
||||
|
||||
def never_cache(view_func):
|
||||
"""
|
||||
Decorator that adds headers to a response so that it will never be cached.
|
||||
"""
|
||||
|
||||
if iscoroutinefunction(view_func):
|
||||
|
||||
async def _view_wrapper(request, *args, **kwargs):
|
||||
_check_request(request, "never_cache")
|
||||
response = await view_func(request, *args, **kwargs)
|
||||
add_never_cache_headers(response)
|
||||
return response
|
||||
|
||||
else:
|
||||
|
||||
def _view_wrapper(request, *args, **kwargs):
|
||||
_check_request(request, "never_cache")
|
||||
response = view_func(request, *args, **kwargs)
|
||||
add_never_cache_headers(response)
|
||||
return response
|
||||
|
||||
return wraps(view_func)(_view_wrapper)
|
||||
@@ -0,0 +1,90 @@
|
||||
from functools import wraps
|
||||
|
||||
from asgiref.sync import iscoroutinefunction
|
||||
|
||||
|
||||
def xframe_options_deny(view_func):
|
||||
"""
|
||||
Modify a view function so its response has the X-Frame-Options HTTP
|
||||
header set to 'DENY' as long as the response doesn't already have that
|
||||
header set. Usage:
|
||||
|
||||
@xframe_options_deny
|
||||
def some_view(request):
|
||||
...
|
||||
"""
|
||||
|
||||
if iscoroutinefunction(view_func):
|
||||
|
||||
async def _view_wrapper(*args, **kwargs):
|
||||
response = await view_func(*args, **kwargs)
|
||||
if response.get("X-Frame-Options") is None:
|
||||
response["X-Frame-Options"] = "DENY"
|
||||
return response
|
||||
|
||||
else:
|
||||
|
||||
def _view_wrapper(*args, **kwargs):
|
||||
response = view_func(*args, **kwargs)
|
||||
if response.get("X-Frame-Options") is None:
|
||||
response["X-Frame-Options"] = "DENY"
|
||||
return response
|
||||
|
||||
return wraps(view_func)(_view_wrapper)
|
||||
|
||||
|
||||
def xframe_options_sameorigin(view_func):
|
||||
"""
|
||||
Modify a view function so its response has the X-Frame-Options HTTP
|
||||
header set to 'SAMEORIGIN' as long as the response doesn't already have
|
||||
that header set. Usage:
|
||||
|
||||
@xframe_options_sameorigin
|
||||
def some_view(request):
|
||||
...
|
||||
"""
|
||||
|
||||
if iscoroutinefunction(view_func):
|
||||
|
||||
async def _view_wrapper(*args, **kwargs):
|
||||
response = await view_func(*args, **kwargs)
|
||||
if response.get("X-Frame-Options") is None:
|
||||
response["X-Frame-Options"] = "SAMEORIGIN"
|
||||
return response
|
||||
|
||||
else:
|
||||
|
||||
def _view_wrapper(*args, **kwargs):
|
||||
response = view_func(*args, **kwargs)
|
||||
if response.get("X-Frame-Options") is None:
|
||||
response["X-Frame-Options"] = "SAMEORIGIN"
|
||||
return response
|
||||
|
||||
return wraps(view_func)(_view_wrapper)
|
||||
|
||||
|
||||
def xframe_options_exempt(view_func):
|
||||
"""
|
||||
Modify a view function by setting a response variable that instructs
|
||||
XFrameOptionsMiddleware to NOT set the X-Frame-Options HTTP header. Usage:
|
||||
|
||||
@xframe_options_exempt
|
||||
def some_view(request):
|
||||
...
|
||||
"""
|
||||
|
||||
if iscoroutinefunction(view_func):
|
||||
|
||||
async def _view_wrapper(*args, **kwargs):
|
||||
response = await view_func(*args, **kwargs)
|
||||
response.xframe_options_exempt = True
|
||||
return response
|
||||
|
||||
else:
|
||||
|
||||
def _view_wrapper(*args, **kwargs):
|
||||
response = view_func(*args, **kwargs)
|
||||
response.xframe_options_exempt = True
|
||||
return response
|
||||
|
||||
return wraps(view_func)(_view_wrapper)
|
||||
@@ -0,0 +1,27 @@
|
||||
from functools import wraps
|
||||
|
||||
from asgiref.sync import iscoroutinefunction
|
||||
|
||||
|
||||
def no_append_slash(view_func):
|
||||
"""
|
||||
Mark a view function as excluded from CommonMiddleware's APPEND_SLASH
|
||||
redirection.
|
||||
"""
|
||||
|
||||
# view_func.should_append_slash = False would also work, but decorators are
|
||||
# nicer if they don't have side effects, so return a new function.
|
||||
|
||||
if iscoroutinefunction(view_func):
|
||||
|
||||
async def _view_wrapper(request, *args, **kwargs):
|
||||
return await view_func(request, *args, **kwargs)
|
||||
|
||||
else:
|
||||
|
||||
def _view_wrapper(request, *args, **kwargs):
|
||||
return view_func(request, *args, **kwargs)
|
||||
|
||||
_view_wrapper.should_append_slash = False
|
||||
|
||||
return wraps(view_func)(_view_wrapper)
|
||||
@@ -0,0 +1,69 @@
|
||||
from functools import wraps
|
||||
|
||||
from asgiref.sync import iscoroutinefunction
|
||||
|
||||
from django.middleware.csrf import CsrfViewMiddleware, get_token
|
||||
from django.utils.decorators import decorator_from_middleware
|
||||
|
||||
csrf_protect = decorator_from_middleware(CsrfViewMiddleware)
|
||||
csrf_protect.__name__ = "csrf_protect"
|
||||
csrf_protect.__doc__ = """
|
||||
This decorator adds CSRF protection in exactly the same way as
|
||||
CsrfViewMiddleware, but it can be used on a per view basis. Using both, or
|
||||
using the decorator multiple times, is harmless and efficient.
|
||||
"""
|
||||
|
||||
|
||||
class _EnsureCsrfToken(CsrfViewMiddleware):
|
||||
# Behave like CsrfViewMiddleware but don't reject requests or log warnings.
|
||||
def _reject(self, request, reason):
|
||||
return None
|
||||
|
||||
|
||||
requires_csrf_token = decorator_from_middleware(_EnsureCsrfToken)
|
||||
requires_csrf_token.__name__ = "requires_csrf_token"
|
||||
requires_csrf_token.__doc__ = """
|
||||
Use this decorator on views that need a correct csrf_token available to
|
||||
RequestContext, but without the CSRF protection that csrf_protect
|
||||
enforces.
|
||||
"""
|
||||
|
||||
|
||||
class _EnsureCsrfCookie(CsrfViewMiddleware):
|
||||
def _reject(self, request, reason):
|
||||
return None
|
||||
|
||||
def process_view(self, request, callback, callback_args, callback_kwargs):
|
||||
retval = super().process_view(request, callback, callback_args, callback_kwargs)
|
||||
# Force process_response to send the cookie
|
||||
get_token(request)
|
||||
return retval
|
||||
|
||||
|
||||
ensure_csrf_cookie = decorator_from_middleware(_EnsureCsrfCookie)
|
||||
ensure_csrf_cookie.__name__ = "ensure_csrf_cookie"
|
||||
ensure_csrf_cookie.__doc__ = """
|
||||
Use this decorator to ensure that a view sets a CSRF cookie, whether or not it
|
||||
uses the csrf_token template tag, or the CsrfViewMiddleware is used.
|
||||
"""
|
||||
|
||||
|
||||
def csrf_exempt(view_func):
|
||||
"""Mark a view function as being exempt from the CSRF view protection."""
|
||||
|
||||
# view_func.csrf_exempt = True would also work, but decorators are nicer
|
||||
# if they don't have side effects, so return a new function.
|
||||
|
||||
if iscoroutinefunction(view_func):
|
||||
|
||||
async def _view_wrapper(request, *args, **kwargs):
|
||||
return await view_func(request, *args, **kwargs)
|
||||
|
||||
else:
|
||||
|
||||
def _view_wrapper(request, *args, **kwargs):
|
||||
return view_func(request, *args, **kwargs)
|
||||
|
||||
_view_wrapper.csrf_exempt = True
|
||||
|
||||
return wraps(view_func)(_view_wrapper)
|
||||
@@ -0,0 +1,147 @@
|
||||
import inspect
|
||||
from functools import wraps
|
||||
|
||||
from asgiref.sync import iscoroutinefunction
|
||||
|
||||
from django.http import HttpRequest
|
||||
|
||||
coroutine_functions_to_sensitive_variables = {}
|
||||
|
||||
|
||||
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()
|
||||
...
|
||||
"""
|
||||
if len(variables) == 1 and callable(variables[0]):
|
||||
raise TypeError(
|
||||
"sensitive_variables() must be called to use it as a decorator, "
|
||||
"e.g., use @sensitive_variables(), not @sensitive_variables."
|
||||
)
|
||||
|
||||
def decorator(func):
|
||||
if iscoroutinefunction(func):
|
||||
sensitive_variables_wrapper = func
|
||||
|
||||
wrapped_func = func
|
||||
while getattr(wrapped_func, "__wrapped__", None) is not None:
|
||||
wrapped_func = wrapped_func.__wrapped__
|
||||
|
||||
try:
|
||||
file_path = inspect.getfile(wrapped_func)
|
||||
except TypeError: # Raises for builtins or native functions.
|
||||
raise ValueError(
|
||||
f"{func.__name__} cannot safely be wrapped by "
|
||||
"@sensitive_variables, make it either non-async or defined in a "
|
||||
"Python file (not a builtin or from a native extension)."
|
||||
)
|
||||
else:
|
||||
# A source file may not be available (e.g. in .pyc-only builds),
|
||||
# use the first line number instead.
|
||||
first_line_number = wrapped_func.__code__.co_firstlineno
|
||||
key = hash(f"{file_path}:{first_line_number}")
|
||||
|
||||
if variables:
|
||||
coroutine_functions_to_sensitive_variables[key] = variables
|
||||
else:
|
||||
coroutine_functions_to_sensitive_variables[key] = "__ALL__"
|
||||
|
||||
else:
|
||||
|
||||
@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)
|
||||
...
|
||||
"""
|
||||
if len(parameters) == 1 and callable(parameters[0]):
|
||||
raise TypeError(
|
||||
"sensitive_post_parameters() must be called to use it as a "
|
||||
"decorator, e.g., use @sensitive_post_parameters(), not "
|
||||
"@sensitive_post_parameters."
|
||||
)
|
||||
|
||||
def decorator(view):
|
||||
if iscoroutinefunction(view):
|
||||
|
||||
@wraps(view)
|
||||
async def sensitive_post_parameters_wrapper(request, *args, **kwargs):
|
||||
if not isinstance(request, HttpRequest):
|
||||
raise TypeError(
|
||||
"sensitive_post_parameters didn't receive an HttpRequest "
|
||||
"object. If you are decorating a classmethod, make sure to use "
|
||||
"@method_decorator."
|
||||
)
|
||||
if parameters:
|
||||
request.sensitive_post_parameters = parameters
|
||||
else:
|
||||
request.sensitive_post_parameters = "__ALL__"
|
||||
return await view(request, *args, **kwargs)
|
||||
|
||||
else:
|
||||
|
||||
@wraps(view)
|
||||
def sensitive_post_parameters_wrapper(request, *args, **kwargs):
|
||||
if not isinstance(request, HttpRequest):
|
||||
raise TypeError(
|
||||
"sensitive_post_parameters didn't receive an HttpRequest "
|
||||
"object. If you are decorating a classmethod, make 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,5 @@
|
||||
from django.middleware.gzip import GZipMiddleware
|
||||
from django.utils.decorators import decorator_from_middleware
|
||||
|
||||
gzip_page = decorator_from_middleware(GZipMiddleware)
|
||||
gzip_page.__doc__ = "Decorator for views that gzips pages if the client supports it."
|
||||
@@ -0,0 +1,169 @@
|
||||
"""
|
||||
Decorators for views based on HTTP headers.
|
||||
"""
|
||||
|
||||
import datetime
|
||||
from functools import wraps
|
||||
|
||||
from asgiref.sync import iscoroutinefunction
|
||||
|
||||
from django.http import HttpResponseNotAllowed
|
||||
from django.middleware.http import ConditionalGetMiddleware
|
||||
from django.utils import timezone
|
||||
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):
|
||||
if iscoroutinefunction(func):
|
||||
|
||||
@wraps(func)
|
||||
async 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 await func(request, *args, **kwargs)
|
||||
|
||||
else:
|
||||
|
||||
@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):
|
||||
def _pre_process_request(request, *args, **kwargs):
|
||||
# Compute values (if any) for the requested resource.
|
||||
res_last_modified = None
|
||||
if last_modified_func:
|
||||
if dt := last_modified_func(request, *args, **kwargs):
|
||||
if not timezone.is_aware(dt):
|
||||
dt = timezone.make_aware(dt, datetime.timezone.utc)
|
||||
res_last_modified = int(dt.timestamp())
|
||||
# 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
|
||||
response = get_conditional_response(
|
||||
request,
|
||||
etag=res_etag,
|
||||
last_modified=res_last_modified,
|
||||
)
|
||||
return response, res_etag, res_last_modified
|
||||
|
||||
def _post_process_request(request, response, res_etag, res_last_modified):
|
||||
# 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.headers["Last-Modified"] = http_date(res_last_modified)
|
||||
if res_etag:
|
||||
response.headers.setdefault("ETag", res_etag)
|
||||
|
||||
if iscoroutinefunction(func):
|
||||
|
||||
@wraps(func)
|
||||
async def inner(request, *args, **kwargs):
|
||||
response, res_etag, res_last_modified = _pre_process_request(
|
||||
request, *args, **kwargs
|
||||
)
|
||||
if response is None:
|
||||
response = await func(request, *args, **kwargs)
|
||||
_post_process_request(request, response, res_etag, res_last_modified)
|
||||
return response
|
||||
|
||||
else:
|
||||
|
||||
@wraps(func)
|
||||
def inner(request, *args, **kwargs):
|
||||
response, res_etag, res_last_modified = _pre_process_request(
|
||||
request, *args, **kwargs
|
||||
)
|
||||
if response is None:
|
||||
response = func(request, *args, **kwargs)
|
||||
_post_process_request(request, response, res_etag, res_last_modified)
|
||||
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,44 @@
|
||||
from functools import wraps
|
||||
|
||||
from asgiref.sync import iscoroutinefunction
|
||||
|
||||
from django.utils.cache import patch_vary_headers
|
||||
|
||||
|
||||
def vary_on_headers(*headers):
|
||||
"""
|
||||
A view decorator that adds the specified headers to the Vary header of the
|
||||
response. Usage:
|
||||
|
||||
@vary_on_headers('Cookie', 'Accept-language')
|
||||
def index(request):
|
||||
...
|
||||
|
||||
Note that the header names are not case-sensitive.
|
||||
"""
|
||||
|
||||
def decorator(func):
|
||||
if iscoroutinefunction(func):
|
||||
|
||||
async def _view_wrapper(request, *args, **kwargs):
|
||||
response = await func(request, *args, **kwargs)
|
||||
patch_vary_headers(response, headers)
|
||||
return response
|
||||
|
||||
else:
|
||||
|
||||
def _view_wrapper(request, *args, **kwargs):
|
||||
response = func(request, *args, **kwargs)
|
||||
patch_vary_headers(response, headers)
|
||||
return response
|
||||
|
||||
return wraps(func)(_view_wrapper)
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
vary_on_cookie = vary_on_headers("Cookie")
|
||||
vary_on_cookie.__doc__ = (
|
||||
'A view decorator that adds "Cookie" to the Vary header of a response. This '
|
||||
"indicates that a page's contents depends on cookies."
|
||||
)
|
||||
150
.venv/lib/python3.12/site-packages/django/views/defaults.py
Normal file
150
.venv/lib/python3.12/site-packages/django/views/defaults.py
Normal file
@@ -0,0 +1,150 @@
|
||||
from urllib.parse import quote
|
||||
|
||||
from django.http import (
|
||||
HttpResponseBadRequest,
|
||||
HttpResponseForbidden,
|
||||
HttpResponseNotFound,
|
||||
HttpResponseServerError,
|
||||
)
|
||||
from django.template import Context, Engine, TemplateDoesNotExist, loader
|
||||
from django.views.decorators.csrf import requires_csrf_token
|
||||
|
||||
ERROR_404_TEMPLATE_NAME = "404.html"
|
||||
ERROR_403_TEMPLATE_NAME = "403.html"
|
||||
ERROR_400_TEMPLATE_NAME = "400.html"
|
||||
ERROR_500_TEMPLATE_NAME = "500.html"
|
||||
ERROR_PAGE_TEMPLATE = """
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>%(title)s</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>%(title)s</h1><p>%(details)s</p>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
|
||||
# These views can be called when CsrfViewMiddleware.process_view() not run,
|
||||
# therefore need @requires_csrf_token in case the template needs
|
||||
# {% csrf_token %}.
|
||||
|
||||
|
||||
@requires_csrf_token
|
||||
def page_not_found(request, exception, template_name=ERROR_404_TEMPLATE_NAME):
|
||||
"""
|
||||
Default 404 handler.
|
||||
|
||||
Templates: :template:`404.html`
|
||||
Context:
|
||||
request_path
|
||||
The path of the requested URL (e.g., '/app/pages/bad_page/'). It's
|
||||
quoted to prevent a content injection attack.
|
||||
exception
|
||||
The message from the exception which triggered the 404 (if one was
|
||||
supplied), or the exception class name
|
||||
"""
|
||||
exception_repr = exception.__class__.__name__
|
||||
# Try to get an "interesting" exception message, if any (and not the ugly
|
||||
# Resolver404 dictionary)
|
||||
try:
|
||||
message = exception.args[0]
|
||||
except (AttributeError, IndexError):
|
||||
pass
|
||||
else:
|
||||
if isinstance(message, str):
|
||||
exception_repr = message
|
||||
context = {
|
||||
"request_path": quote(request.path),
|
||||
"exception": exception_repr,
|
||||
}
|
||||
try:
|
||||
template = loader.get_template(template_name)
|
||||
body = template.render(context, request)
|
||||
except TemplateDoesNotExist:
|
||||
if template_name != ERROR_404_TEMPLATE_NAME:
|
||||
# Reraise if it's a missing custom template.
|
||||
raise
|
||||
# Render template (even though there are no substitutions) to allow
|
||||
# inspecting the context in tests.
|
||||
template = Engine().from_string(
|
||||
ERROR_PAGE_TEMPLATE
|
||||
% {
|
||||
"title": "Not Found",
|
||||
"details": "The requested resource was not found on this server.",
|
||||
},
|
||||
)
|
||||
body = template.render(Context(context))
|
||||
return HttpResponseNotFound(body)
|
||||
|
||||
|
||||
@requires_csrf_token
|
||||
def server_error(request, template_name=ERROR_500_TEMPLATE_NAME):
|
||||
"""
|
||||
500 error handler.
|
||||
|
||||
Templates: :template:`500.html`
|
||||
Context: None
|
||||
"""
|
||||
try:
|
||||
template = loader.get_template(template_name)
|
||||
except TemplateDoesNotExist:
|
||||
if template_name != ERROR_500_TEMPLATE_NAME:
|
||||
# Reraise if it's a missing custom template.
|
||||
raise
|
||||
return HttpResponseServerError(
|
||||
ERROR_PAGE_TEMPLATE % {"title": "Server Error (500)", "details": ""},
|
||||
)
|
||||
return HttpResponseServerError(template.render())
|
||||
|
||||
|
||||
@requires_csrf_token
|
||||
def bad_request(request, exception, template_name=ERROR_400_TEMPLATE_NAME):
|
||||
"""
|
||||
400 error handler.
|
||||
|
||||
Templates: :template:`400.html`
|
||||
Context: None
|
||||
"""
|
||||
try:
|
||||
template = loader.get_template(template_name)
|
||||
body = template.render(request=request)
|
||||
except TemplateDoesNotExist:
|
||||
if template_name != ERROR_400_TEMPLATE_NAME:
|
||||
# Reraise if it's a missing custom template.
|
||||
raise
|
||||
return HttpResponseBadRequest(
|
||||
ERROR_PAGE_TEMPLATE % {"title": "Bad Request (400)", "details": ""},
|
||||
)
|
||||
# No exception content is passed to the template, to not disclose any
|
||||
# sensitive information.
|
||||
return HttpResponseBadRequest(body)
|
||||
|
||||
|
||||
@requires_csrf_token
|
||||
def permission_denied(request, exception, template_name=ERROR_403_TEMPLATE_NAME):
|
||||
"""
|
||||
Permission denied (403) handler.
|
||||
|
||||
Templates: :template:`403.html`
|
||||
Context:
|
||||
exception
|
||||
The message from the exception which triggered the 403 (if one was
|
||||
supplied).
|
||||
|
||||
If the template does not exist, an Http403 response containing the text
|
||||
"403 Forbidden" (as per RFC 9110 Section 15.5.4) will be returned.
|
||||
"""
|
||||
try:
|
||||
template = loader.get_template(template_name)
|
||||
except TemplateDoesNotExist:
|
||||
if template_name != ERROR_403_TEMPLATE_NAME:
|
||||
# Reraise if it's a missing custom template.
|
||||
raise
|
||||
return HttpResponseForbidden(
|
||||
ERROR_PAGE_TEMPLATE % {"title": "403 Forbidden", "details": ""},
|
||||
)
|
||||
return HttpResponseForbidden(
|
||||
template.render(request=request, context={"exception": str(exception)})
|
||||
)
|
||||
@@ -0,0 +1,39 @@
|
||||
from django.views.generic.base import RedirectView, TemplateView, View
|
||||
from django.views.generic.dates import (
|
||||
ArchiveIndexView,
|
||||
DateDetailView,
|
||||
DayArchiveView,
|
||||
MonthArchiveView,
|
||||
TodayArchiveView,
|
||||
WeekArchiveView,
|
||||
YearArchiveView,
|
||||
)
|
||||
from django.views.generic.detail import DetailView
|
||||
from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
|
||||
from django.views.generic.list import ListView
|
||||
|
||||
__all__ = [
|
||||
"View",
|
||||
"TemplateView",
|
||||
"RedirectView",
|
||||
"ArchiveIndexView",
|
||||
"YearArchiveView",
|
||||
"MonthArchiveView",
|
||||
"WeekArchiveView",
|
||||
"DayArchiveView",
|
||||
"TodayArchiveView",
|
||||
"DateDetailView",
|
||||
"DetailView",
|
||||
"FormView",
|
||||
"CreateView",
|
||||
"UpdateView",
|
||||
"DeleteView",
|
||||
"ListView",
|
||||
"GenericViewError",
|
||||
]
|
||||
|
||||
|
||||
class GenericViewError(Exception):
|
||||
"""A problem in a generic view."""
|
||||
|
||||
pass
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
286
.venv/lib/python3.12/site-packages/django/views/generic/base.py
Normal file
286
.venv/lib/python3.12/site-packages/django/views/generic/base.py
Normal file
@@ -0,0 +1,286 @@
|
||||
import logging
|
||||
|
||||
from asgiref.sync import iscoroutinefunction, markcoroutinefunction
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.http import (
|
||||
HttpResponse,
|
||||
HttpResponseGone,
|
||||
HttpResponseNotAllowed,
|
||||
HttpResponsePermanentRedirect,
|
||||
HttpResponseRedirect,
|
||||
)
|
||||
from django.template.response import TemplateResponse
|
||||
from django.urls import reverse
|
||||
from django.utils.decorators import classonlymethod
|
||||
from django.utils.functional import classproperty
|
||||
from django.utils.log import log_response
|
||||
|
||||
logger = logging.getLogger("django.request")
|
||||
|
||||
|
||||
class ContextMixin:
|
||||
"""
|
||||
A default context mixin that passes the keyword arguments received by
|
||||
get_context_data() as the template context.
|
||||
"""
|
||||
|
||||
extra_context = None
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs.setdefault("view", self)
|
||||
if self.extra_context is not None:
|
||||
kwargs.update(self.extra_context)
|
||||
return kwargs
|
||||
|
||||
|
||||
class View:
|
||||
"""
|
||||
Intentionally simple parent class for all views. Only implements
|
||||
dispatch-by-method and simple sanity checking.
|
||||
"""
|
||||
|
||||
http_method_names = [
|
||||
"get",
|
||||
"post",
|
||||
"put",
|
||||
"patch",
|
||||
"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.items():
|
||||
setattr(self, key, value)
|
||||
|
||||
@classproperty
|
||||
def view_is_async(cls):
|
||||
handlers = [
|
||||
getattr(cls, method)
|
||||
for method in cls.http_method_names
|
||||
if (method != "options" and hasattr(cls, method))
|
||||
]
|
||||
if not handlers:
|
||||
return False
|
||||
is_async = iscoroutinefunction(handlers[0])
|
||||
if not all(iscoroutinefunction(h) == is_async for h in handlers[1:]):
|
||||
raise ImproperlyConfigured(
|
||||
f"{cls.__qualname__} HTTP handlers must either be all sync or all "
|
||||
"async."
|
||||
)
|
||||
return is_async
|
||||
|
||||
@classonlymethod
|
||||
def as_view(cls, **initkwargs):
|
||||
"""Main entry point for a request-response process."""
|
||||
for key in initkwargs:
|
||||
if key in cls.http_method_names:
|
||||
raise TypeError(
|
||||
"The method name %s is not accepted as a keyword argument "
|
||||
"to %s()." % (key, cls.__name__)
|
||||
)
|
||||
if not hasattr(cls, key):
|
||||
raise TypeError(
|
||||
"%s() received an invalid keyword %r. as_view "
|
||||
"only accepts arguments that are already "
|
||||
"attributes of the class." % (cls.__name__, key)
|
||||
)
|
||||
|
||||
def view(request, *args, **kwargs):
|
||||
self = cls(**initkwargs)
|
||||
self.setup(request, *args, **kwargs)
|
||||
if not hasattr(self, "request"):
|
||||
raise AttributeError(
|
||||
"%s instance has no 'request' attribute. Did you override "
|
||||
"setup() and forget to call super()?" % cls.__name__
|
||||
)
|
||||
return self.dispatch(request, *args, **kwargs)
|
||||
|
||||
view.view_class = cls
|
||||
view.view_initkwargs = initkwargs
|
||||
|
||||
# __name__ and __qualname__ are intentionally left unchanged as
|
||||
# view_class should be used to robustly determine the name of the view
|
||||
# instead.
|
||||
view.__doc__ = cls.__doc__
|
||||
view.__module__ = cls.__module__
|
||||
view.__annotations__ = cls.dispatch.__annotations__
|
||||
# Copy possible attributes set by decorators, e.g. @csrf_exempt, from
|
||||
# the dispatch method.
|
||||
view.__dict__.update(cls.dispatch.__dict__)
|
||||
|
||||
# Mark the callback if the view class is async.
|
||||
if cls.view_is_async:
|
||||
markcoroutinefunction(view)
|
||||
|
||||
return view
|
||||
|
||||
def setup(self, request, *args, **kwargs):
|
||||
"""Initialize attributes shared by all view methods."""
|
||||
if hasattr(self, "get") and not hasattr(self, "head"):
|
||||
self.head = self.get
|
||||
self.request = request
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
|
||||
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
|
||||
return handler(request, *args, **kwargs)
|
||||
|
||||
def http_method_not_allowed(self, request, *args, **kwargs):
|
||||
response = HttpResponseNotAllowed(self._allowed_methods())
|
||||
log_response(
|
||||
"Method Not Allowed (%s): %s",
|
||||
request.method,
|
||||
request.path,
|
||||
response=response,
|
||||
request=request,
|
||||
)
|
||||
|
||||
if self.view_is_async:
|
||||
|
||||
async def func():
|
||||
return response
|
||||
|
||||
return func()
|
||||
else:
|
||||
return response
|
||||
|
||||
def options(self, request, *args, **kwargs):
|
||||
"""Handle responding to requests for the OPTIONS HTTP verb."""
|
||||
response = HttpResponse()
|
||||
response.headers["Allow"] = ", ".join(self._allowed_methods())
|
||||
response.headers["Content-Length"] = "0"
|
||||
|
||||
if self.view_is_async:
|
||||
|
||||
async def func():
|
||||
return response
|
||||
|
||||
return func()
|
||||
else:
|
||||
return response
|
||||
|
||||
def _allowed_methods(self):
|
||||
return [m.upper() for m in self.http_method_names if hasattr(self, m)]
|
||||
|
||||
|
||||
class TemplateResponseMixin:
|
||||
"""A mixin that can be used to render a template."""
|
||||
|
||||
template_name = None
|
||||
template_engine = None
|
||||
response_class = TemplateResponse
|
||||
content_type = None
|
||||
|
||||
def render_to_response(self, context, **response_kwargs):
|
||||
"""
|
||||
Return a response, using the `response_class` for this view, with a
|
||||
template rendered with the given context.
|
||||
|
||||
Pass response_kwargs to the constructor of the response class.
|
||||
"""
|
||||
response_kwargs.setdefault("content_type", self.content_type)
|
||||
return self.response_class(
|
||||
request=self.request,
|
||||
template=self.get_template_names(),
|
||||
context=context,
|
||||
using=self.template_engine,
|
||||
**response_kwargs,
|
||||
)
|
||||
|
||||
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.
|
||||
"""
|
||||
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, ContextMixin, View):
|
||||
"""
|
||||
Render a template. Pass keyword arguments from the URLconf to the context.
|
||||
"""
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
context = self.get_context_data(**kwargs)
|
||||
return self.render_to_response(context)
|
||||
|
||||
|
||||
class RedirectView(View):
|
||||
"""Provide a redirect on any GET request."""
|
||||
|
||||
permanent = False
|
||||
url = None
|
||||
pattern_name = None
|
||||
query_string = False
|
||||
|
||||
def get_redirect_url(self, *args, **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
|
||||
elif self.pattern_name:
|
||||
url = reverse(self.pattern_name, args=args, kwargs=kwargs)
|
||||
else:
|
||||
return None
|
||||
|
||||
args = self.request.META.get("QUERY_STRING", "")
|
||||
if args and self.query_string:
|
||||
url = "%s?%s" % (url, args)
|
||||
return url
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
url = self.get_redirect_url(*args, **kwargs)
|
||||
if url:
|
||||
if self.permanent:
|
||||
return HttpResponsePermanentRedirect(url)
|
||||
else:
|
||||
return HttpResponseRedirect(url)
|
||||
else:
|
||||
response = HttpResponseGone()
|
||||
log_response("Gone: %s", request.path, response=response, request=request)
|
||||
return response
|
||||
|
||||
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)
|
||||
|
||||
def patch(self, request, *args, **kwargs):
|
||||
return self.get(request, *args, **kwargs)
|
||||
823
.venv/lib/python3.12/site-packages/django/views/generic/dates.py
Normal file
823
.venv/lib/python3.12/site-packages/django/views/generic/dates.py
Normal file
@@ -0,0 +1,823 @@
|
||||
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 in {"%W", "%V"}: # 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):
|
||||
"""
|
||||
Base class for date-based views displaying a list of objects.
|
||||
|
||||
This requires subclassing to provide a response mixin.
|
||||
"""
|
||||
|
||||
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 view for archives of date-based items.
|
||||
|
||||
This requires subclassing to provide 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):
|
||||
"""
|
||||
Base view for a list of objects published in a given year.
|
||||
|
||||
This requires subclassing to provide a response mixin.
|
||||
"""
|
||||
|
||||
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):
|
||||
"""
|
||||
Base view for a list of objects published in a given month.
|
||||
|
||||
This requires subclassing to provide a response mixin.
|
||||
"""
|
||||
|
||||
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):
|
||||
"""
|
||||
Base view for a list of objects published in a given week.
|
||||
|
||||
This requires subclassing to provide a response mixin.
|
||||
"""
|
||||
|
||||
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", "%V": "1"}
|
||||
try:
|
||||
week_start = week_choices[week_format]
|
||||
except KeyError:
|
||||
raise ValueError(
|
||||
"Unknown week format %r. Choices are: %s"
|
||||
% (
|
||||
week_format,
|
||||
", ".join(sorted(week_choices)),
|
||||
)
|
||||
)
|
||||
year_format = self.get_year_format()
|
||||
if week_format == "%V" and year_format != "%G":
|
||||
raise ValueError(
|
||||
"ISO week directive '%s' is incompatible with the year "
|
||||
"directive '%s'. Use the ISO year '%%G' instead."
|
||||
% (
|
||||
week_format,
|
||||
year_format,
|
||||
)
|
||||
)
|
||||
date = _date_from_string(year, 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):
|
||||
"""
|
||||
Base view for a list of objects published on a given day.
|
||||
|
||||
This requires subclassing to provide a response mixin.
|
||||
"""
|
||||
|
||||
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):
|
||||
"""
|
||||
Base view for a list of objects published today.
|
||||
|
||||
This requires subclassing to provide a response mixin.
|
||||
"""
|
||||
|
||||
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):
|
||||
"""
|
||||
Base detail view for a single object on a single date; this differs from the
|
||||
standard DetailView by accepting a year/month/day in the URL.
|
||||
|
||||
This requires subclassing to provide a response mixin.
|
||||
"""
|
||||
|
||||
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,189 @@
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.db import models
|
||||
from django.http import Http404
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic.base import ContextMixin, TemplateResponseMixin, View
|
||||
|
||||
|
||||
class SingleObjectMixin(ContextMixin):
|
||||
"""
|
||||
Provide 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"
|
||||
query_pk_and_slug = False
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
"""
|
||||
Return the object the view is displaying.
|
||||
|
||||
Require `self.queryset` and a `pk` or `slug` argument in the URLconf.
|
||||
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)
|
||||
slug = self.kwargs.get(self.slug_url_kwarg)
|
||||
if pk is not None:
|
||||
queryset = queryset.filter(pk=pk)
|
||||
|
||||
# Next, try looking up by slug.
|
||||
if slug is not None and (pk is None or self.query_pk_and_slug):
|
||||
slug_field = self.get_slug_field()
|
||||
queryset = queryset.filter(**{slug_field: slug})
|
||||
|
||||
# If none of those are defined, it's an error.
|
||||
if pk is None and slug is None:
|
||||
raise AttributeError(
|
||||
"Generic detail view %s must be called with either an object "
|
||||
"pk or a slug in the URLconf." % self.__class__.__name__
|
||||
)
|
||||
|
||||
try:
|
||||
# Get the single item from the filtered queryset
|
||||
obj = queryset.get()
|
||||
except queryset.model.DoesNotExist:
|
||||
raise Http404(
|
||||
_("No %(verbose_name)s found matching the query")
|
||||
% {"verbose_name": queryset.model._meta.verbose_name}
|
||||
)
|
||||
return obj
|
||||
|
||||
def get_queryset(self):
|
||||
"""
|
||||
Return the `QuerySet` that will be used to look up the object.
|
||||
|
||||
This method is called by the default implementation of get_object() and
|
||||
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(
|
||||
"%(cls)s is missing a QuerySet. Define "
|
||||
"%(cls)s.model, %(cls)s.queryset, or override "
|
||||
"%(cls)s.get_queryset()." % {"cls": self.__class__.__name__}
|
||||
)
|
||||
return self.queryset.all()
|
||||
|
||||
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 isinstance(obj, models.Model):
|
||||
return obj._meta.model_name
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Insert the single object into the context dict."""
|
||||
context = {}
|
||||
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
|
||||
context.update(kwargs)
|
||||
return super().get_context_data(**context)
|
||||
|
||||
|
||||
class BaseDetailView(SingleObjectMixin, View):
|
||||
"""
|
||||
Base view for displaying a single object.
|
||||
|
||||
This requires subclassing to provide a response mixin.
|
||||
"""
|
||||
|
||||
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. May not be
|
||||
called if render_to_response() is overridden. Return a list containing
|
||||
``template_name``, if set on the value. Otherwise, return a list
|
||||
containing:
|
||||
|
||||
* the contents of the ``template_name_field`` field on the
|
||||
object instance that the view is operating upon (if available)
|
||||
* ``<app_label>/<model_name><template_name_suffix>.html``
|
||||
"""
|
||||
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 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 isinstance(self.object, models.Model):
|
||||
object_meta = self.object._meta
|
||||
names.append(
|
||||
"%s/%s%s.html"
|
||||
% (
|
||||
object_meta.app_label,
|
||||
object_meta.model_name,
|
||||
self.template_name_suffix,
|
||||
)
|
||||
)
|
||||
elif getattr(self, "model", None) is not None and issubclass(
|
||||
self.model, models.Model
|
||||
):
|
||||
names.append(
|
||||
"%s/%s%s.html"
|
||||
% (
|
||||
self.model._meta.app_label,
|
||||
self.model._meta.model_name,
|
||||
self.template_name_suffix,
|
||||
)
|
||||
)
|
||||
|
||||
# If we still haven't managed to find any template names, we should
|
||||
# re-raise the ImproperlyConfigured to alert the user.
|
||||
if not names:
|
||||
raise ImproperlyConfigured(
|
||||
"SingleObjectTemplateResponseMixin requires a definition "
|
||||
"of 'template_name', 'template_name_field', or 'model'; "
|
||||
"or an implementation of 'get_template_names()'."
|
||||
)
|
||||
|
||||
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()`.
|
||||
"""
|
||||
274
.venv/lib/python3.12/site-packages/django/views/generic/edit.py
Normal file
274
.venv/lib/python3.12/site-packages/django/views/generic/edit.py
Normal file
@@ -0,0 +1,274 @@
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.forms import Form
|
||||
from django.forms import models as model_forms
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.views.generic.base import ContextMixin, TemplateResponseMixin, View
|
||||
from django.views.generic.detail import (
|
||||
BaseDetailView,
|
||||
SingleObjectMixin,
|
||||
SingleObjectTemplateResponseMixin,
|
||||
)
|
||||
|
||||
|
||||
class FormMixin(ContextMixin):
|
||||
"""Provide a way to show and handle a form in a request."""
|
||||
|
||||
initial = {}
|
||||
form_class = None
|
||||
success_url = None
|
||||
prefix = None
|
||||
|
||||
def get_initial(self):
|
||||
"""Return the initial data to use for forms on this view."""
|
||||
return self.initial.copy()
|
||||
|
||||
def get_prefix(self):
|
||||
"""Return the prefix to use for forms."""
|
||||
return self.prefix
|
||||
|
||||
def get_form_class(self):
|
||||
"""Return the form class to use."""
|
||||
return self.form_class
|
||||
|
||||
def get_form(self, form_class=None):
|
||||
"""Return an instance of the form to be used in this view."""
|
||||
if form_class is None:
|
||||
form_class = self.get_form_class()
|
||||
return form_class(**self.get_form_kwargs())
|
||||
|
||||
def get_form_kwargs(self):
|
||||
"""Return the keyword arguments for instantiating the form."""
|
||||
kwargs = {
|
||||
"initial": self.get_initial(),
|
||||
"prefix": self.get_prefix(),
|
||||
}
|
||||
|
||||
if self.request.method in ("POST", "PUT"):
|
||||
kwargs.update(
|
||||
{
|
||||
"data": self.request.POST,
|
||||
"files": self.request.FILES,
|
||||
}
|
||||
)
|
||||
return kwargs
|
||||
|
||||
def get_success_url(self):
|
||||
"""Return the URL to redirect to after processing a valid form."""
|
||||
if not self.success_url:
|
||||
raise ImproperlyConfigured("No URL to redirect to. Provide a success_url.")
|
||||
return str(self.success_url) # success_url may be lazy
|
||||
|
||||
def form_valid(self, form):
|
||||
"""If the form is valid, redirect to the supplied URL."""
|
||||
return HttpResponseRedirect(self.get_success_url())
|
||||
|
||||
def form_invalid(self, form):
|
||||
"""If the form is invalid, render the invalid form."""
|
||||
return self.render_to_response(self.get_context_data(form=form))
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Insert the form into the context dict."""
|
||||
if "form" not in kwargs:
|
||||
kwargs["form"] = self.get_form()
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
|
||||
class ModelFormMixin(FormMixin, SingleObjectMixin):
|
||||
"""Provide a way to show and handle a ModelForm in a request."""
|
||||
|
||||
fields = None
|
||||
|
||||
def get_form_class(self):
|
||||
"""Return the form class to use in this view."""
|
||||
if self.fields is not None and self.form_class:
|
||||
raise ImproperlyConfigured(
|
||||
"Specifying both 'fields' and 'form_class' is not permitted."
|
||||
)
|
||||
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 getattr(self, "object", None) 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
|
||||
|
||||
if self.fields is None:
|
||||
raise ImproperlyConfigured(
|
||||
"Using ModelFormMixin (base class of %s) without "
|
||||
"the 'fields' attribute is prohibited." % self.__class__.__name__
|
||||
)
|
||||
|
||||
return model_forms.modelform_factory(model, fields=self.fields)
|
||||
|
||||
def get_form_kwargs(self):
|
||||
"""Return the keyword arguments for instantiating the form."""
|
||||
kwargs = super().get_form_kwargs()
|
||||
if hasattr(self, "object"):
|
||||
kwargs.update({"instance": self.object})
|
||||
return kwargs
|
||||
|
||||
def get_success_url(self):
|
||||
"""Return the URL to redirect to after processing a valid form."""
|
||||
if self.success_url:
|
||||
url = self.success_url.format(**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):
|
||||
"""If the form is valid, save the associated model."""
|
||||
self.object = form.save()
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class ProcessFormView(View):
|
||||
"""Render a form on GET and processes it on POST."""
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""Handle GET requests: instantiate a blank version of the form."""
|
||||
return self.render_to_response(self.get_context_data())
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""
|
||||
Handle POST requests: instantiate a form instance with the passed
|
||||
POST variables and then check if it's valid.
|
||||
"""
|
||||
form = self.get_form()
|
||||
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 a new object instance.
|
||||
|
||||
This requires subclassing to provide a response mixin.
|
||||
"""
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = None
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.object = None
|
||||
return super().post(request, *args, **kwargs)
|
||||
|
||||
|
||||
class CreateView(SingleObjectTemplateResponseMixin, BaseCreateView):
|
||||
"""
|
||||
View for creating a new object, with a response rendered by a template.
|
||||
"""
|
||||
|
||||
template_name_suffix = "_form"
|
||||
|
||||
|
||||
class BaseUpdateView(ModelFormMixin, ProcessFormView):
|
||||
"""
|
||||
Base view for updating an existing object.
|
||||
|
||||
This requires subclassing to provide a response mixin.
|
||||
"""
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
return super().post(request, *args, **kwargs)
|
||||
|
||||
|
||||
class UpdateView(SingleObjectTemplateResponseMixin, BaseUpdateView):
|
||||
"""View for updating an object, with a response rendered by a template."""
|
||||
|
||||
template_name_suffix = "_form"
|
||||
|
||||
|
||||
class DeletionMixin:
|
||||
"""Provide the ability to delete objects."""
|
||||
|
||||
success_url = None
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
"""
|
||||
Call the delete() method on the fetched object and then redirect to the
|
||||
success URL.
|
||||
"""
|
||||
self.object = self.get_object()
|
||||
success_url = self.get_success_url()
|
||||
self.object.delete()
|
||||
return HttpResponseRedirect(success_url)
|
||||
|
||||
# Add support for browsers which only accept GET and POST for now.
|
||||
def post(self, request, *args, **kwargs):
|
||||
return self.delete(request, *args, **kwargs)
|
||||
|
||||
def get_success_url(self):
|
||||
if self.success_url:
|
||||
return self.success_url.format(**self.object.__dict__)
|
||||
else:
|
||||
raise ImproperlyConfigured("No URL to redirect to. Provide a success_url.")
|
||||
|
||||
|
||||
class BaseDeleteView(DeletionMixin, FormMixin, BaseDetailView):
|
||||
"""
|
||||
Base view for deleting an object.
|
||||
|
||||
This requires subclassing to provide a response mixin.
|
||||
"""
|
||||
|
||||
form_class = Form
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
# Set self.object before the usual form processing flow.
|
||||
# Inlined because having DeletionMixin as the first base, for
|
||||
# get_success_url(), makes leveraging super() with ProcessFormView
|
||||
# overly complex.
|
||||
self.object = self.get_object()
|
||||
form = self.get_form()
|
||||
if form.is_valid():
|
||||
return self.form_valid(form)
|
||||
else:
|
||||
return self.form_invalid(form)
|
||||
|
||||
def form_valid(self, form):
|
||||
success_url = self.get_success_url()
|
||||
self.object.delete()
|
||||
return HttpResponseRedirect(success_url)
|
||||
|
||||
|
||||
class DeleteView(SingleObjectTemplateResponseMixin, BaseDeleteView):
|
||||
"""
|
||||
View for deleting an object retrieved with self.get_object(), with a
|
||||
response rendered by a template.
|
||||
"""
|
||||
|
||||
template_name_suffix = "_confirm_delete"
|
||||
224
.venv/lib/python3.12/site-packages/django/views/generic/list.py
Normal file
224
.venv/lib/python3.12/site-packages/django/views/generic/list.py
Normal file
@@ -0,0 +1,224 @@
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.core.paginator import InvalidPage, Paginator
|
||||
from django.db.models 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):
|
||||
"""
|
||||
Base view for displaying a list of objects.
|
||||
|
||||
This requires subclassing to provide a response mixin.
|
||||
"""
|
||||
|
||||
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.
|
||||
"""
|
||||
251
.venv/lib/python3.12/site-packages/django/views/i18n.py
Normal file
251
.venv/lib/python3.12/site-packages/django/views/i18n.py
Normal file
@@ -0,0 +1,251 @@
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
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 url_has_allowed_host_and_scheme
|
||||
from django.utils.translation import check_for_language, get_language
|
||||
from django.utils.translation.trans_real import DjangoTranslation
|
||||
from django.views.generic import View
|
||||
|
||||
LANGUAGE_QUERY_PARAMETER = "language"
|
||||
|
||||
|
||||
def builtin_template_path(name):
|
||||
"""
|
||||
Return a path to a builtin template.
|
||||
|
||||
Avoid calling this function at the module level or in a class-definition
|
||||
because __file__ may not exist, e.g. in frozen environments.
|
||||
"""
|
||||
return Path(__file__).parent / "templates" / name
|
||||
|
||||
|
||||
def set_language(request):
|
||||
"""
|
||||
Redirect to a given URL while setting the chosen language in the language
|
||||
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_url = request.POST.get("next", request.GET.get("next"))
|
||||
if (
|
||||
next_url or request.accepts("text/html")
|
||||
) and not url_has_allowed_host_and_scheme(
|
||||
url=next_url,
|
||||
allowed_hosts={request.get_host()},
|
||||
require_https=request.is_secure(),
|
||||
):
|
||||
next_url = request.META.get("HTTP_REFERER")
|
||||
if not url_has_allowed_host_and_scheme(
|
||||
url=next_url,
|
||||
allowed_hosts={request.get_host()},
|
||||
require_https=request.is_secure(),
|
||||
):
|
||||
next_url = "/"
|
||||
response = HttpResponseRedirect(next_url) if next_url 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_url:
|
||||
next_trans = translate_url(next_url, lang_code)
|
||||
if next_trans != next_url:
|
||||
response = HttpResponseRedirect(next_trans)
|
||||
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,
|
||||
secure=settings.LANGUAGE_COOKIE_SECURE,
|
||||
httponly=settings.LANGUAGE_COOKIE_HTTPONLY,
|
||||
samesite=settings.LANGUAGE_COOKIE_SAMESITE,
|
||||
)
|
||||
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}
|
||||
|
||||
|
||||
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 path() 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[1])
|
||||
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 = {}
|
||||
catalog = {}
|
||||
translation = self.translation
|
||||
seen_keys = set()
|
||||
while True:
|
||||
for key, value in translation._catalog.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)
|
||||
if translation._fallback:
|
||||
translation = translation._fallback
|
||||
else:
|
||||
break
|
||||
|
||||
num_plurals = self._num_plurals
|
||||
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 ")
|
||||
|
||||
with builtin_template_path("i18n_catalog.js").open(encoding="utf-8") as fh:
|
||||
template = Engine().from_string(fh.read())
|
||||
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)
|
||||
122
.venv/lib/python3.12/site-packages/django/views/static.py
Normal file
122
.venv/lib/python3.12/site-packages/django/views/static.py
Normal file
@@ -0,0 +1,122 @@
|
||||
"""
|
||||
Views and functions for serving static files. These are only to be used
|
||||
during development, and SHOULD NOT be used in a production setting.
|
||||
"""
|
||||
|
||||
import mimetypes
|
||||
import posixpath
|
||||
from pathlib import Path
|
||||
|
||||
from django.http import FileResponse, Http404, HttpResponse, HttpResponseNotModified
|
||||
from django.template import Context, Engine, TemplateDoesNotExist, loader
|
||||
from django.utils._os import safe_join
|
||||
from django.utils.http import http_date, parse_http_date
|
||||
from django.utils.translation import gettext as _
|
||||
from django.utils.translation import gettext_lazy
|
||||
|
||||
|
||||
def builtin_template_path(name):
|
||||
"""
|
||||
Return a path to a builtin template.
|
||||
|
||||
Avoid calling this function at the module level or in a class-definition
|
||||
because __file__ may not exist, e.g. in frozen environments.
|
||||
"""
|
||||
return Path(__file__).parent / "templates" / name
|
||||
|
||||
|
||||
def serve(request, path, document_root=None, show_indexes=False):
|
||||
"""
|
||||
Serve static files below a given point in the directory structure.
|
||||
|
||||
To use, put a URL pattern such as::
|
||||
|
||||
from django.views.static import serve
|
||||
|
||||
path('<path:path>', serve, {'document_root': '/path/to/my/files/'})
|
||||
|
||||
in your URLconf. You must provide the ``document_root`` param. You may
|
||||
also set ``show_indexes`` to ``True`` if you'd like to serve a basic index
|
||||
of the directory. This index view will use the template hardcoded below,
|
||||
but if you'd like to override it, you can create a template called
|
||||
``static/directory_index.html``.
|
||||
"""
|
||||
path = posixpath.normpath(path).lstrip("/")
|
||||
fullpath = Path(safe_join(document_root, path))
|
||||
if fullpath.is_dir():
|
||||
if show_indexes:
|
||||
return directory_index(path, fullpath)
|
||||
raise Http404(_("Directory indexes are not allowed here."))
|
||||
if not fullpath.exists():
|
||||
raise Http404(_("“%(path)s” does not exist") % {"path": fullpath})
|
||||
# Respect the If-Modified-Since header.
|
||||
statobj = fullpath.stat()
|
||||
if not was_modified_since(
|
||||
request.META.get("HTTP_IF_MODIFIED_SINCE"), statobj.st_mtime
|
||||
):
|
||||
return HttpResponseNotModified()
|
||||
content_type, encoding = mimetypes.guess_type(str(fullpath))
|
||||
content_type = content_type or "application/octet-stream"
|
||||
response = FileResponse(fullpath.open("rb"), content_type=content_type)
|
||||
response.headers["Last-Modified"] = http_date(statobj.st_mtime)
|
||||
if encoding:
|
||||
response.headers["Content-Encoding"] = encoding
|
||||
return response
|
||||
|
||||
|
||||
# Translatable string for static directory index template title.
|
||||
template_translatable = gettext_lazy("Index of %(directory)s")
|
||||
|
||||
|
||||
def directory_index(path, fullpath):
|
||||
try:
|
||||
t = loader.select_template(
|
||||
[
|
||||
"static/directory_index.html",
|
||||
"static/directory_index",
|
||||
]
|
||||
)
|
||||
except TemplateDoesNotExist:
|
||||
with builtin_template_path("directory_index.html").open(encoding="utf-8") as fh:
|
||||
t = Engine(libraries={"i18n": "django.templatetags.i18n"}).from_string(
|
||||
fh.read()
|
||||
)
|
||||
c = Context()
|
||||
else:
|
||||
c = {}
|
||||
files = []
|
||||
for f in fullpath.iterdir():
|
||||
if not f.name.startswith("."):
|
||||
url = str(f.relative_to(fullpath))
|
||||
if f.is_dir():
|
||||
url += "/"
|
||||
files.append(url)
|
||||
c.update(
|
||||
{
|
||||
"directory": path + "/",
|
||||
"file_list": files,
|
||||
}
|
||||
)
|
||||
return HttpResponse(t.render(c))
|
||||
|
||||
|
||||
def was_modified_since(header=None, mtime=0):
|
||||
"""
|
||||
Was something modified since the user last downloaded it?
|
||||
|
||||
header
|
||||
This is the value of the If-Modified-Since header. If this is None,
|
||||
I'll just return True.
|
||||
|
||||
mtime
|
||||
This is the modification time of the item we're talking about.
|
||||
"""
|
||||
try:
|
||||
if header is None:
|
||||
raise ValueError
|
||||
header_mtime = parse_http_date(header)
|
||||
if int(mtime) > header_mtime:
|
||||
raise ValueError
|
||||
except (ValueError, OverflowError):
|
||||
return True
|
||||
return False
|
||||
@@ -0,0 +1,84 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||
<meta name="robots" content="NONE,NOARCHIVE">
|
||||
<title>403 Forbidden</title>
|
||||
<style>
|
||||
html * { padding:0; margin:0; }
|
||||
body * { padding:10px 20px; }
|
||||
body * * { padding:0; }
|
||||
body { font-family: 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; }
|
||||
#info { background:#f6f6f6; }
|
||||
#info ul { margin: 0.5em 4em; }
|
||||
#info p, #summary p { padding-top:10px; }
|
||||
#summary { background: #ffc; }
|
||||
#explanation { background:#eee; border-bottom: 0px none; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="summary">
|
||||
<h1>{{ title }} <span>(403)</span></h1>
|
||||
<p>{{ main }}</p>
|
||||
{% if no_referer %}
|
||||
<p>{{ no_referer1 }}</p>
|
||||
<p>{{ no_referer2 }}</p>
|
||||
<p>{{ no_referer3 }}</p>
|
||||
{% endif %}
|
||||
{% if no_cookie %}
|
||||
<p>{{ no_cookie1 }}</p>
|
||||
<p>{{ no_cookie2 }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if DEBUG %}
|
||||
<div id="info">
|
||||
<h2>Help</h2>
|
||||
{% if reason %}
|
||||
<p>Reason given for failure:</p>
|
||||
<pre>
|
||||
{{ reason }}
|
||||
</pre>
|
||||
{% endif %}
|
||||
|
||||
<p>In general, this can occur when there is a genuine Cross Site Request Forgery, or when
|
||||
<a
|
||||
href="https://docs.djangoproject.com/en/{{ docs_version }}/ref/csrf/">Django’s
|
||||
CSRF mechanism</a> has not been used correctly. For POST forms, you need to
|
||||
ensure:</p>
|
||||
|
||||
<ul>
|
||||
<li>Your browser is accepting cookies.</li>
|
||||
|
||||
<li>The view function passes a <code>request</code> to the template’s <a
|
||||
href="https://docs.djangoproject.com/en/{{ docs_version }}/topics/templates/#django.template.backends.base.Template.render"><code>render</code></a>
|
||||
method.</li>
|
||||
|
||||
<li>In the template, there is a <code>{% templatetag openblock %} csrf_token
|
||||
{% templatetag closeblock %}</code> template tag inside each POST form that
|
||||
targets an internal URL.</li>
|
||||
|
||||
<li>If you are not using <code>CsrfViewMiddleware</code>, then you must use
|
||||
<code>csrf_protect</code> on any views that use the <code>csrf_token</code>
|
||||
template tag, as well as those that accept the POST data.</li>
|
||||
|
||||
<li>The form has a valid CSRF token. After logging in in another browser
|
||||
tab or hitting the back button after a login, you may need to reload the
|
||||
page with the form, because the token is rotated after a login.</li>
|
||||
</ul>
|
||||
|
||||
<p>You’re seeing the help section of this page because you have <code>DEBUG =
|
||||
True</code> in your Django settings file. Change that to <code>False</code>,
|
||||
and only the initial error message will be displayed. </p>
|
||||
|
||||
<p>You can customize this page using the CSRF_FAILURE_VIEW setting.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<div id="explanation">
|
||||
<p><small>{{ more }}</small></p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,241 @@
|
||||
{% load i18n %}
|
||||
<!doctype html>
|
||||
{% get_current_language as LANGUAGE_CODE %}{% get_current_language_bidi as LANGUAGE_BIDI %}
|
||||
<html lang="{{ LANGUAGE_CODE|default:'en-us' }}" dir="{{ LANGUAGE_BIDI|yesno:'rtl,ltr,auto' }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>{% translate "The install worked successfully! Congratulations!" %}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
html {
|
||||
line-height: 1.15;
|
||||
}
|
||||
a {
|
||||
color: #092e20;
|
||||
}
|
||||
body {
|
||||
max-width: 960px;
|
||||
color: #525252;
|
||||
font-family: "Segoe UI", system-ui, sans-serif;
|
||||
margin: 0 auto;
|
||||
}
|
||||
main {
|
||||
text-align: center;
|
||||
}
|
||||
h1, h2, h3, h4, h5, p, ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-weight: 400;
|
||||
}
|
||||
.logo {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 19.16 6.696'%3E%3Cg fill='rgb(9 46 32)'%3E%3Cpath d='m2.259 3.55e-8h1.048v4.851c-0.5377 0.1021-0.9324 0.1429-1.361 0.1429-1.279 0-1.946-0.5784-1.946-1.688 0-1.068 0.7078-1.763 1.804-1.763 0.1701 0 0.2994 0.01365 0.456 0.0544v-1.598zm0 2.442c-0.1225-0.04079-0.2246-0.0544-0.3539-0.0544-0.5308 0-0.8371 0.3267-0.8371 0.8983 0 0.5582 0.2927 0.8644 0.8303 0.8644 0.1156 0 0.211-0.00681 0.3607-0.02713z'/%3E%3Cpath d='m4.975 1.618v2.43c0 0.837-0.06125 1.239-0.245 1.586-0.1702 0.3335-0.3948 0.5444-0.8575 0.7758l-0.9732-0.4628c0.4628-0.2178 0.6874-0.4082 0.8303-0.701 0.1498-0.2994 0.1974-0.6466 0.1974-1.559v-2.069zm-1.048-1.613h1.048v1.075h-1.048z'/%3E%3Cpath d='m5.608 1.857c0.4628-0.2178 0.9052-0.313 1.388-0.313 0.5377 0 0.8915 0.1429 1.048 0.422 0.08842 0.1565 0.1156 0.3606 0.1156 0.7963v2.13c-0.4696 0.06814-1.062 0.1157-1.497 0.1157-0.8779 0-1.273-0.3063-1.273-0.9868 0-0.7351 0.524-1.075 1.81-1.184v-0.2314c0-0.1905-0.09527-0.2585-0.3607-0.2585-0.3879 0-0.8235 0.1088-1.232 0.3198v-0.8099zm1.64 1.667c-0.6942 0.06814-0.9188 0.177-0.9188 0.4492 0 0.2042 0.1293 0.2995 0.4152 0.2995 0.1566 0 0.2994-0.01365 0.5036-0.04759z'/%3E%3Cpath d='m8.671 1.782c0.6193-0.1634 1.13-0.2382 1.647-0.2382 0.5377 0 0.9256 0.1224 1.157 0.3607 0.2178 0.2245 0.2858 0.4695 0.2858 0.9936v2.055h-1.048v-2.015c0-0.4015-0.1361-0.5513-0.5104-0.5513-0.1429 0-0.2722 0.01365-0.4833 0.0749v2.491h-1.048v-3.171z'/%3E%3Cpath d='m12.17 5.525c0.3676 0.1905 0.735 0.279 1.123 0.279 0.6873 0 0.98-0.279 0.98-0.946v-0.0205c-0.2042 0.1021-0.4084 0.143-0.6805 0.143-0.9188 0-1.504-0.6058-1.504-1.565 0-1.191 0.8644-1.865 2.396-1.865 0.4492 0 0.8644 0.04759 1.368 0.1496l-0.3589 0.7561c-0.2791-0.05449-0.02235-0.00733-0.2332-0.02775v0.1089l0.01357 0.4423 0.0068 0.5717c0.0068 0.1428 0.0068 0.2858 0.01365 0.4287v0.2859c0 0.8984-0.07486 1.32-0.2994 1.667-0.3267 0.5105-0.8916 0.7623-1.695 0.7623-0.4084 0-0.7622-0.06129-1.13-0.2042v-0.9663zm2.083-3.131h-0.03398-0.0749c-0.2041-0.00681-0.4423 0.04759-0.6057 0.1497-0.2517 0.143-0.3811 0.4016-0.3811 0.7691 0 0.5241 0.2587 0.8235 0.7214 0.8235 0.1429 0 0.2586-0.02722 0.3947-0.06805v-0.3607c0-0.1225-0.0068-0.2587-0.0068-0.4016l-0.0068-0.4832-0.0068-0.3471v-0.08171z'/%3E%3Cpath d='m17.48 1.53c1.048 0 1.688 0.6602 1.688 1.729 0 1.096-0.6669 1.783-1.729 1.783-1.048 0-1.695-0.6601-1.695-1.722 4.4e-5 -1.103 0.667-1.79 1.736-1.79zm-0.0205 2.668c0.4016 0 0.6398-0.3335 0.6398-0.912 0-0.5716-0.2314-0.9119-0.6329-0.9119-0.4152 0-0.6535 0.3336-0.6535 0.9119 4.4e-5 0.5786 0.2383 0.912 0.6465 0.912z'/%3E%3C/g%3E%3C/svg%3E");
|
||||
color: #092e20;
|
||||
background-position-x: center;
|
||||
background-repeat: no-repeat;
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
margin-top: 16px;
|
||||
overflow: hidden;
|
||||
text-decoration: none;
|
||||
text-indent: 100%;
|
||||
display: inline-block;
|
||||
}
|
||||
.figure {
|
||||
margin-top: 22vh;
|
||||
max-width: 265px;
|
||||
position: relative;
|
||||
z-index: -9;
|
||||
overflow: visible;
|
||||
}
|
||||
.exhaust__line {
|
||||
animation: thrust 70ms 100 ease-in-out alternate;
|
||||
}
|
||||
.smoke {
|
||||
animation: smoke .1s 70 ease-in-out alternate;
|
||||
}
|
||||
@keyframes smoke {
|
||||
0% {
|
||||
transform: translate3d(-5px, 0, 0);
|
||||
}
|
||||
100% {
|
||||
transform: translate3d(5px, 0, 0);
|
||||
}
|
||||
}
|
||||
.flame {
|
||||
animation: burnInner2 .1s 70 ease-in-out alternate;
|
||||
}
|
||||
@keyframes burnInner2 {
|
||||
0% {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
100% {
|
||||
transform: translate3d(0, 3px, 0);
|
||||
}
|
||||
}
|
||||
@keyframes thrust {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: .5;
|
||||
}
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.exhaust__line,
|
||||
.smoke,
|
||||
.flame {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
h1 {
|
||||
font-size: 1.375rem;
|
||||
max-width: 32rem;
|
||||
margin: 5px auto 0;
|
||||
}
|
||||
main p {
|
||||
line-height: 1.25;
|
||||
max-width: 26rem;
|
||||
margin: 15px auto 0;
|
||||
}
|
||||
footer {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 5px;
|
||||
padding: 25px 0;
|
||||
position: fixed;
|
||||
box-sizing: border-box;
|
||||
left: 50%;
|
||||
bottom: 0;
|
||||
width: 960px;
|
||||
transform: translateX(-50%);
|
||||
transform-style: preserve-3d;
|
||||
border-top: 1px solid #efefef;
|
||||
}
|
||||
.option {
|
||||
display: grid;
|
||||
grid-template-columns: min-content 1fr;
|
||||
gap: 10px;
|
||||
box-sizing: border-box;
|
||||
text-decoration: none;
|
||||
}
|
||||
.option svg {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
fill: gray;
|
||||
border: 1px solid #d6d6d6;
|
||||
padding: 5px;
|
||||
border-radius: 100%;
|
||||
}
|
||||
.option p {
|
||||
font-weight: 300;
|
||||
line-height: 1.25;
|
||||
color: #525252;
|
||||
display: table;
|
||||
}
|
||||
.option .option__heading {
|
||||
color: #092e20;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
@media (max-width: 996px) {
|
||||
body, footer {
|
||||
max-width: 780px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 800px) {
|
||||
footer {
|
||||
height: 100%;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 60px;
|
||||
position: relative;
|
||||
padding: 25px;
|
||||
}
|
||||
.figure {
|
||||
margin-top: 10px;
|
||||
}
|
||||
main {
|
||||
padding: 0 25px;
|
||||
}
|
||||
main h1 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
footer {
|
||||
width: 100%;
|
||||
margin-top: 50px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 801px) and (max-height: 730px) {
|
||||
.figure {
|
||||
margin-top: 80px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 801px) and (max-height: 600px) {
|
||||
footer {
|
||||
position: relative;
|
||||
margin: 135px auto 0;
|
||||
}
|
||||
.figure {
|
||||
margin-top: 50px;
|
||||
}
|
||||
}
|
||||
.sr-only {
|
||||
clip: rect(1px, 1px, 1px, 1px);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<svg class="figure" viewBox="0 0 508 268" aria-hidden="true">
|
||||
<path d="M305.2 156.6c0 4.6-.5 9-1.6 13.2-2.5-4.4-5.6-8.4-9.2-12-4.6-4.6-10-8.4-16-11.2 2.8-11.2 4.5-22.9 5-34.6 1.8 1.4 3.5 2.9 5 4.5 10.5 10.3 16.8 24.5 16.8 40.1zm-75-10c-6 2.8-11.4 6.6-16 11.2-3.5 3.6-6.6 7.6-9.1 12-1-4.3-1.6-8.7-1.6-13.2 0-15.7 6.3-29.9 16.6-40.1 1.6-1.6 3.3-3.1 5.1-4.5.6 11.8 2.2 23.4 5 34.6z" fill="#2E3B39" fill-rule="nonzero"/>
|
||||
<path d="M282.981 152.6c16.125-48.1 6.375-104-29.25-142.6-35.625 38.5-45.25 94.5-29.25 142.6h58.5z" stroke="#FFF" stroke-width="3.396" fill="#6DDCBD"/>
|
||||
<path d="M271 29.7c-4.4-10.6-9.9-20.6-16.6-29.7-6.7 9-12.2 19-16.6 29.7H271z" stroke="#FFF" stroke-width="3" fill="#2E3B39"/>
|
||||
<circle stroke="#FFF" stroke-width="7" fill="none" cx="254.3" cy="76.8" r="12.2"/>
|
||||
<path class="smoke" d="M507.812 234.24c0-2.16-.632-4.32-1.58-6.24-3.318-6.72-11.85-11.52-21.804-11.52-1.106 0-2.212.12-3.318.24-.474-11.52-12.956-20.76-28.282-20.76-3.318 0-6.636.48-9.638 1.32-4.74-6.72-14.062-11.28-24.806-11.28-.79 0-1.58 0-2.37.12-.79 0-1.58-.12-2.37-.12-10.744 0-20.066 4.56-24.806 11.28a35.326 35.326 0 00-9.638-1.32c-15.642 0-28.282 9.6-28.282 21.48 0 1.32.158 2.76.474 3.96a26.09 26.09 0 00-4.424-.36c-8.058 0-15.01 3.12-19.118 7.8-3.476-1.68-7.742-2.76-12.324-2.76-12.008 0-21.804 7.08-22.752 15.96h-.158c-9.322 0-17.38 4.32-20.856 10.44-4.108-3.6-10.27-6-17.222-6h-1.264c-6.794 0-12.956 2.28-17.222 6-3.476-6.12-11.534-10.44-20.856-10.44h-.158c-.948-9-10.744-15.96-22.752-15.96-4.582 0-8.69.96-12.324 2.76-4.108-4.68-11.06-7.8-19.118-7.8-1.422 0-3.002.12-4.424.36.316-1.32.474-2.64.474-3.96 0-11.88-12.64-21.48-28.282-21.48-3.318 0-6.636.48-9.638 1.32-4.74-6.72-14.062-11.28-24.806-11.28-.79 0-1.58 0-2.37.12-.79 0-1.58-.12-2.37-.12-10.744 0-20.066 4.56-24.806 11.28a35.326 35.326 0 00-9.638-1.32c-15.326 0-27.808 9.24-28.282 20.76-1.106-.12-2.212-.24-3.318-.24-9.954 0-18.486 4.8-21.804 11.52-.948 1.92-1.58 4.08-1.58 6.24 0 4.8 2.528 9.12 6.636 12.36-.79 1.44-1.264 3.12-1.264 4.8 0 7.2 7.742 13.08 17.222 13.08h462.15c9.48 0 17.222-5.88 17.222-13.08 0-1.68-.474-3.36-1.264-4.8 4.582-3.24 7.11-7.56 7.11-12.36z" fill="#E6E9EE"/>
|
||||
<path fill="#6DDCBD" d="M239 152h30v8h-30z"/>
|
||||
<path class="exhaust__line" fill="#E6E9EE" d="M250 172h7v90h-7z"/>
|
||||
<path class="flame" d="M250.27 178.834l-5.32-8.93s-2.47-5.7 3.458-6.118h10.26s6.232.266 3.306 6.194l-5.244 8.93s-3.23 4.37-6.46 0v-.076z" fill="#AA2247"/>
|
||||
</svg>
|
||||
<h1>{% translate "The install worked successfully! Congratulations!" %}</h1>
|
||||
<p>
|
||||
{% blocktranslate %}View <a href="https://docs.djangoproject.com/en/{{ version }}/releases/" target="_blank" rel="noopener">release notes</a> for Django {{ version }}{% endblocktranslate %}
|
||||
</p>
|
||||
<p>{% blocktranslate %}You are seeing this page because <a href="https://docs.djangoproject.com/en/{{ version }}/ref/settings/#debug" target="_blank" rel="noopener">DEBUG=True</a> is in your settings file and you have not configured any URLs.{% endblocktranslate %}</p>
|
||||
<a class="logo" href="https://www.djangoproject.com/" target="_blank" rel="noopener">Django</a>
|
||||
</main>
|
||||
<footer>
|
||||
<a class="option" href="https://docs.djangoproject.com/en/{{ version }}/" target="_blank" rel="noopener">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z"></path>
|
||||
</svg>
|
||||
<p>
|
||||
<span class="option__heading">{% translate "Django Documentation" %}</span><span class="sr-only">.</span><br>
|
||||
{% translate 'Topics, references, & how-to’s' %}
|
||||
</p>
|
||||
</a>
|
||||
<a class="option" href="https://docs.djangoproject.com/en/{{ version }}/intro/tutorial01/" target="_blank" rel="noopener">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"></path>
|
||||
</svg>
|
||||
<p>
|
||||
<span class="option__heading">{% translate "Tutorial: A Polling App" %}</span><span class="sr-only">.</span><br>
|
||||
{% translate "Get started with Django" %}
|
||||
</p>
|
||||
</a>
|
||||
<a class="option" href="https://www.djangoproject.com/community/" target="_blank" rel="noopener">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M16.5 13c-1.2 0-3.07.34-4.5 1-1.43-.67-3.3-1-4.5-1C5.33 13 1 14.08 1 16.25V19h22v-2.75c0-2.17-4.33-3.25-6.5-3.25zm-4 4.5h-10v-1.25c0-.54 2.56-1.75 5-1.75s5 1.21 5 1.75v1.25zm9 0H14v-1.25c0-.46-.2-.86-.52-1.22.88-.3 1.96-.53 3.02-.53 2.44 0 5 1.21 5 1.75v1.25zM7.5 12c1.93 0 3.5-1.57 3.5-3.5S9.43 5 7.5 5 4 6.57 4 8.5 5.57 12 7.5 12zm0-5.5c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm9 5.5c1.93 0 3.5-1.57 3.5-3.5S18.43 5 16.5 5 13 6.57 13 8.5s1.57 3.5 3.5 3.5zm0-5.5c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2z"></path>
|
||||
</svg>
|
||||
<p>
|
||||
<span class="option__heading">{% translate "Django Community" %}</span><span class="sr-only">.</span><br>
|
||||
{% translate "Connect, get help, or contribute" %}
|
||||
</p>
|
||||
</a>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,21 @@
|
||||
{% load i18n %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<meta http-equiv="Content-Language" content="en-us">
|
||||
<meta name="robots" content="NONE,NOARCHIVE">
|
||||
<title>{% blocktranslate %}Index of {{ directory }}{% endblocktranslate %}</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>{% blocktranslate %}Index of {{ directory }}{% endblocktranslate %}</h1>
|
||||
<ul>
|
||||
{% if directory != "/" %}
|
||||
<li><a href="../">../</a></li>
|
||||
{% endif %}
|
||||
{% for f in file_list %}
|
||||
<li><a href="{{ f|urlencode }}">{{ f }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,102 @@
|
||||
{% autoescape off %}
|
||||
'use strict';
|
||||
{
|
||||
const globals = this;
|
||||
const django = globals.django || (globals.django = {});
|
||||
|
||||
{% if plural %}
|
||||
django.pluralidx = function(n) {
|
||||
const 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 %}
|
||||
const newcatalog = {{ catalog_str }};
|
||||
for (const key in newcatalog) {
|
||||
django.catalog[key] = newcatalog[key];
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
if (!django.jsi18n_initialized) {
|
||||
django.gettext = function(msgid) {
|
||||
const value = django.catalog[msgid];
|
||||
if (typeof value === 'undefined') {
|
||||
return msgid;
|
||||
} else {
|
||||
return (typeof value === 'string') ? value : value[0];
|
||||
}
|
||||
};
|
||||
|
||||
django.ngettext = function(singular, plural, count) {
|
||||
const 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) {
|
||||
let value = django.gettext(context + '\x04' + msgid);
|
||||
if (value.includes('\x04')) {
|
||||
value = msgid;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
django.npgettext = function(context, singular, plural, count) {
|
||||
let value = django.ngettext(context + '\x04' + singular, context + '\x04' + plural, count);
|
||||
if (value.includes('\x04')) {
|
||||
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) {
|
||||
const 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;
|
||||
}
|
||||
};
|
||||
{% endautoescape %}
|
||||
@@ -0,0 +1,85 @@
|
||||
<!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>
|
||||
html * { padding:0; margin:0; }
|
||||
body * { padding:10px 20px; }
|
||||
body * * { padding:0; }
|
||||
body { font-family: sans-serif; background:#eee; color:#000; }
|
||||
body > :where(header, main, footer) { border-bottom:1px solid #ddd; }
|
||||
h1 { font-weight:normal; margin-bottom:.4em; }
|
||||
h1 small { 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; }
|
||||
pre.exception_value { font-family: sans-serif; color: #575757; font-size: 1.5em; margin: 10px 0 10px 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header id="summary">
|
||||
<h1>Page not found <small>(404)</small></h1>
|
||||
{% if reason and resolved %}<pre class="exception_value">{{ reason }}</pre>{% endif %}
|
||||
<table class="meta">
|
||||
<tr>
|
||||
<th scope="row">Request Method:</th>
|
||||
<td>{{ request.META.REQUEST_METHOD }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Request URL:</th>
|
||||
<td>{{ request.build_absolute_uri }}</td>
|
||||
</tr>
|
||||
{% if raising_view_name %}
|
||||
<tr>
|
||||
<th scope="row">Raised by:</th>
|
||||
<td>{{ raising_view_name }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
</header>
|
||||
|
||||
<main 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 %}
|
||||
<code>
|
||||
{{ pat.pattern }}
|
||||
{% if forloop.last and pat.name %}[name='{{ pat.name }}']{% endif %}
|
||||
</code>
|
||||
{% endfor %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
<p>
|
||||
{% if request_path %}
|
||||
The current path, <code>{{ request_path }}</code>,
|
||||
{% else %}
|
||||
The empty path
|
||||
{% endif %}
|
||||
{% if resolved %}matched the last one.{% else %}didn’t match any of these.{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
</main>
|
||||
|
||||
<footer 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>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,497 @@
|
||||
<!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>
|
||||
html * { padding:0; margin:0; }
|
||||
body * { padding:10px 20px; }
|
||||
body * * { padding:0; }
|
||||
body { font-family: sans-serif; background-color:#fff; color:#000; }
|
||||
body > :where(header, main, footer) { 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; word-break: break-word; }
|
||||
summary { cursor: pointer; }
|
||||
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: 0.6875rem; border:1px solid #ddd;
|
||||
}
|
||||
tbody th { width:12em; text-align:right; color:#666; padding-right:.5em; }
|
||||
table.vars { margin:5px 10px 2px 40px; width: auto; }
|
||||
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.cause { word-break: break-word; }
|
||||
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, summary.commands { margin-left: 40px; }
|
||||
div.commands a, summary.commands { color:#555; text-decoration:none; }
|
||||
.user div.commands a { color: black; }
|
||||
#summary { background: #ffc; }
|
||||
#summary h2 { font-weight: normal; color: #666; }
|
||||
#info { padding: 0; }
|
||||
#info > * { padding:10px 20px; }
|
||||
#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: 0.7rem; font-weight:normal; }
|
||||
span.commands a:link {color:#5E5694;}
|
||||
pre.exception_value { font-family: sans-serif; color: #575757; font-size: 1.5rem; margin: 10px 0 10px 0; }
|
||||
.append-bottom { margin-bottom: 10px; }
|
||||
.fname { user-select: all; }
|
||||
</style>
|
||||
{% if not is_email %}
|
||||
<script>
|
||||
function hideAll(elems) {
|
||||
for (var e = 0; e < elems.length; e++) {
|
||||
elems[e].style.display = 'none';
|
||||
}
|
||||
}
|
||||
window.onload = function() {
|
||||
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 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>
|
||||
<header 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 }}{% if exception_notes %}{{ exception_notes }}{% endif %}{% else %}No exception message supplied{% endif %}</pre>
|
||||
<table class="meta">
|
||||
{% if request %}
|
||||
<tr>
|
||||
<th scope="row">Request Method:</th>
|
||||
<td>{{ request.META.REQUEST_METHOD }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Request URL:</th>
|
||||
<td>{{ request_insecure_uri }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<th scope="row">Django Version:</th>
|
||||
<td>{{ django_version_info }}</td>
|
||||
</tr>
|
||||
{% if exception_type %}
|
||||
<tr>
|
||||
<th scope="row">Exception Type:</th>
|
||||
<td>{{ exception_type }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if exception_type and exception_value %}
|
||||
<tr>
|
||||
<th scope="row">Exception Value:</th>
|
||||
<td><pre>{{ exception_value|force_escape }}</pre></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if lastframe %}
|
||||
<tr>
|
||||
<th scope="row">Exception Location:</th>
|
||||
<td><span class="fname">{{ lastframe.filename }}</span>, line {{ lastframe.lineno }}, in {{ lastframe.function }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if raising_view_name %}
|
||||
<tr>
|
||||
<th scope="row">Raised during:</th>
|
||||
<td>{{ raising_view_name }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<th scope="row">Python Executable:</th>
|
||||
<td>{{ sys_executable }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Python Version:</th>
|
||||
<td>{{ sys_version_info }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Python Path:</th>
|
||||
<td><pre><code>{{ sys_path|pprint }}</code></pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Server time:</th>
|
||||
<td>{{server_time|date:"r"}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</header>
|
||||
|
||||
<main id="info">
|
||||
{% 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|force_escape }}</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 scope="row">{{ source_line.0 }}</th>
|
||||
<td>{{ template_info.before }}<span class="specific">{{ template_info.during }}</span>{{ template_info.after }}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><th scope="row">{{ source_line.0 }}</th>
|
||||
<td>{{ source_line.1 }}</td></tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if frames %}
|
||||
<div id="traceback">
|
||||
<h2>Traceback{% if not is_email %} <span class="commands"><a href="#" role="button" 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 class="cause"><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 }}">
|
||||
{% if frame.tb %}
|
||||
<code class="fname">{{ frame.filename }}</code>, line {{ frame.lineno }}, in {{ frame.function }}
|
||||
{% elif forloop.first %}
|
||||
None
|
||||
{% else %}
|
||||
Traceback: None
|
||||
{% endif %}
|
||||
|
||||
{% 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 }}{{ frame.colno }}</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 %}
|
||||
{% if is_email %}
|
||||
<div class="commands">
|
||||
<h2>Local Vars</h2>
|
||||
</div>
|
||||
{% else %}
|
||||
<details>
|
||||
<summary class="commands">Local vars</summary>
|
||||
{% endif %}
|
||||
<table class="vars" id="v{{ frame.id }}">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Variable</th>
|
||||
<th scope="col">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>
|
||||
{% if not is_email %}</details>{% endif %}
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% if not is_email %}
|
||||
<form action="https://dpaste.com/" name="pasteform" id="pasteform" method="post">
|
||||
<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_insecure_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|force_escape }}
|
||||
{% 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 (most recent call last):{% 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 %} {% if frame.tb %}File "{{ frame.filename }}"{% if frame.context_line %}, line {{ frame.lineno }}{% endif %}, in {{ frame.function }}
|
||||
{% if frame.context_line %} {% spaceless %}{{ frame.context_line }}{% endspaceless %}{{ frame.tb_area_colno }}{% endif %}{% elif forloop.first %}None{% else %}Traceback: None{% endif %}{% endfor %}
|
||||
|
||||
Exception Type: {{ exception_type }}{% if request %} at {{ request.path_info }}{% endif %}
|
||||
Exception Value: {{ exception_value|force_escape }}{% if exception_notes %}{{ exception_notes }}{% endif %}
|
||||
</textarea>
|
||||
<br><br>
|
||||
<input type="submit" value="Share this traceback on a public website">
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% 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 scope="col">Variable</th>
|
||||
<th scope="col">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 scope="col">Variable</th>
|
||||
<th scope="col">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 scope="col">Variable</th>
|
||||
<th scope="col">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 scope="col">Variable</th>
|
||||
<th scope="col">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 scope="col">Variable</th>
|
||||
<th scope="col">Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for k, v in request_meta.items|dictsort:0 %}
|
||||
<tr>
|
||||
<td>{{ k }}</td>
|
||||
<td class="code"><pre>{{ v|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 scope="col">Setting</th>
|
||||
<th scope="col">Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for k, v in settings.items|dictsort:0 %}
|
||||
<tr>
|
||||
<td>{{ k }}</td>
|
||||
<td class="code"><pre>{{ v|pprint }}</pre></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
{% if not is_email %}
|
||||
<footer 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>
|
||||
</footer>
|
||||
{% endif %}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,66 @@
|
||||
{% firstof exception_type 'Report' %}{% if request %} at {{ request.path_info }}{% endif %}
|
||||
{% firstof exception_value 'No exception message supplied' %}
|
||||
{% if request %}
|
||||
Request Method: {{ request.META.REQUEST_METHOD }}
|
||||
Request URL: {{ request_insecure_uri }}{% endif %}
|
||||
Django Version: {{ django_version_info }}
|
||||
Python Executable: {{ sys_executable }}
|
||||
Python Version: {{ sys_version_info }}
|
||||
Python Path: {{ sys_path }}
|
||||
Server time: {{server_time|date:"r"}}
|
||||
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 %}{% if frames %}
|
||||
|
||||
Traceback (most recent call last):
|
||||
{% for frame in frames %}{% ifchanged frame.exc_cause %}{% if frame.exc_cause %}
|
||||
{% if frame.exc_cause_explicit %}The above exception ({{ frame.exc_cause }}) was the direct cause of the following exception:{% else %}During handling of the above exception ({{ frame.exc_cause }}), another exception occurred:{% endif %}
|
||||
{% endif %}{% endifchanged %} {% if frame.tb %}File "{{ frame.filename }}"{% if frame.context_line %}, line {{ frame.lineno }}{% endif %}, in {{ frame.function }}
|
||||
{% if frame.context_line %} {% spaceless %}{{ frame.context_line }}{% endspaceless %}{{ frame.tb_area_colno }}{% endif %}{% elif forloop.first %}None{% else %}Traceback: None{% endif %}
|
||||
{% endfor %}
|
||||
{% if exception_type %}Exception Type: {{ exception_type }}{% if request %} at {{ request.path_info }}{% endif %}
|
||||
{% if exception_value %}Exception Value: {{ exception_value }}{% endif %}{% if exception_notes %}{{ exception_notes }}{% endif %}{% endif %}{% endif %}
|
||||
{% if raising_view_name %}Raised during: {{ raising_view_name }}{% endif %}
|
||||
{% if request %}Request information:
|
||||
{% if user_str %}USER: {{ user_str }}{% endif %}
|
||||
|
||||
GET:{% for k, v in request_GET_items %}
|
||||
{{ k }} = {{ v|stringformat:"r" }}{% empty %} No GET data{% endfor %}
|
||||
|
||||
POST:{% for k, v in filtered_POST_items %}
|
||||
{{ k }} = {{ v|stringformat:"r" }}{% empty %} No POST data{% endfor %}
|
||||
|
||||
FILES:{% for k, v in request_FILES_items %}
|
||||
{{ k }} = {{ v|stringformat:"r" }}{% empty %} No FILES data{% endfor %}
|
||||
|
||||
COOKIES:{% for k, v in request_COOKIES_items %}
|
||||
{{ k }} = {{ v|stringformat:"r" }}{% empty %} No cookie data{% endfor %}
|
||||
|
||||
META:{% for k, v in request_meta.items|dictsort:0 %}
|
||||
{{ k }} = {{ v|stringformat:"r" }}{% endfor %}
|
||||
{% else %}Request data not supplied
|
||||
{% endif %}
|
||||
Settings:
|
||||
Using settings module {{ settings.SETTINGS_MODULE }}{% for k, v in settings.items|dictsort:0 %}
|
||||
{{ k }} = {{ v|stringformat:"r" }}{% endfor %}
|
||||
|
||||
{% if not is_email %}
|
||||
You’re seeing this error because you have DEBUG = True in your
|
||||
Django settings file. Change that to False, and Django will
|
||||
display a standard page generated by the handler for this status code.
|
||||
{% endif %}
|
||||
Reference in New Issue
Block a user