2017-01-19 04:30:21 +08:00
|
|
|
import functools
|
2007-12-11 13:38:34 +08:00
|
|
|
import re
|
|
|
|
import sys
|
2010-12-13 06:54:04 +08:00
|
|
|
import types
|
2017-04-01 23:31:54 +08:00
|
|
|
from pathlib import Path
|
2007-12-11 13:38:34 +08:00
|
|
|
|
2005-11-15 01:44:50 +08:00
|
|
|
from django.conf import settings
|
2019-08-10 04:40:25 +08:00
|
|
|
from django.http import Http404, HttpResponse, HttpResponseNotFound
|
2015-04-25 03:33:03 +08:00
|
|
|
from django.template import Context, Engine, TemplateDoesNotExist
|
2017-08-10 09:12:37 +08:00
|
|
|
from django.template.defaultfilters import pprint
|
2019-08-10 04:40:25 +08:00
|
|
|
from django.urls import resolve
|
2017-01-19 04:30:21 +08:00
|
|
|
from django.utils import timezone
|
2013-09-17 19:46:38 +08:00
|
|
|
from django.utils.datastructures import MultiValueDict
|
2017-01-26 17:08:08 +08:00
|
|
|
from django.utils.encoding import force_str
|
2014-01-21 04:15:14 +08:00
|
|
|
from django.utils.module_loading import import_string
|
2019-10-26 22:42:32 +08:00
|
|
|
from django.utils.regex_helper import _lazy_re_compile
|
2017-08-07 22:33:55 +08:00
|
|
|
from django.utils.version import get_docs_version
|
2005-11-15 01:44:50 +08:00
|
|
|
|
2015-01-11 17:06:42 +08:00
|
|
|
# Minimal Django templates engine to render the error templates
|
2017-04-01 23:31:54 +08:00
|
|
|
# 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.
|
2017-08-07 22:33:55 +08:00
|
|
|
DEBUG_ENGINE = Engine(
|
|
|
|
debug=True,
|
|
|
|
libraries={'i18n': 'django.templatetags.i18n'},
|
|
|
|
)
|
2015-01-11 17:06:42 +08:00
|
|
|
|
2017-04-01 23:31:54 +08:00
|
|
|
CURRENT_DIR = Path(__file__).parent
|
|
|
|
|
2013-11-03 07:53:29 +08:00
|
|
|
|
2017-01-19 15:39:46 +08:00
|
|
|
class CallableSettingWrapper:
|
2017-01-25 04:36:07 +08:00
|
|
|
"""
|
|
|
|
Object to wrap callable appearing in settings.
|
2014-07-26 18:06:48 +08:00
|
|
|
* Not to call in the debug page (#21345).
|
2017-01-25 04:36:07 +08:00
|
|
|
* Not to break the debug page if the callable forbidding to set attributes
|
|
|
|
(#23070).
|
2014-07-26 18:06:48 +08:00
|
|
|
"""
|
|
|
|
def __init__(self, callable_setting):
|
|
|
|
self._wrapped = callable_setting
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return repr(self._wrapped)
|
|
|
|
|
|
|
|
|
2014-06-24 12:13:34 +08:00
|
|
|
def technical_500_response(request, exc_type, exc_value, tb, status_code=500):
|
2005-11-15 01:44:50 +08:00
|
|
|
"""
|
2005-12-07 14:02:09 +08:00
|
|
|
Create a technical server error response. The last three arguments are
|
2005-11-15 01:44:50 +08:00
|
|
|
the values returned from sys.exc_info() and friends.
|
|
|
|
"""
|
2019-09-08 01:08:12 +08:00
|
|
|
reporter = get_exception_reporter_class(request)(request, exc_type, exc_value, tb)
|
2019-12-15 22:30:35 +08:00
|
|
|
if request.accepts('text/html'):
|
2011-10-02 10:53:58 +08:00
|
|
|
html = reporter.get_traceback_html()
|
2014-06-24 12:13:34 +08:00
|
|
|
return HttpResponse(html, status=status_code, content_type='text/html')
|
2019-12-15 22:30:35 +08:00
|
|
|
else:
|
|
|
|
text = reporter.get_traceback_text()
|
|
|
|
return HttpResponse(text, status=status_code, content_type='text/plain; charset=utf-8')
|
2008-03-18 22:34:48 +08:00
|
|
|
|
2014-11-19 04:52:58 +08:00
|
|
|
|
2017-01-19 04:30:21 +08:00
|
|
|
@functools.lru_cache()
|
2014-11-19 04:52:58 +08:00
|
|
|
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)()
|
2011-06-09 06:18:46 +08:00
|
|
|
|
2013-11-03 07:53:29 +08:00
|
|
|
|
2011-06-09 06:18:46 +08:00
|
|
|
def get_exception_reporter_filter(request):
|
2014-11-19 04:52:58 +08:00
|
|
|
default_filter = get_default_exception_reporter_filter()
|
|
|
|
return getattr(request, 'exception_reporter_filter', default_filter)
|
2011-06-09 06:18:46 +08:00
|
|
|
|
2013-11-03 07:53:29 +08:00
|
|
|
|
2019-09-08 01:08:12 +08:00
|
|
|
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)
|
|
|
|
|
|
|
|
|
2020-01-09 22:54:05 +08:00
|
|
|
class SafeExceptionReporterFilter:
|
2011-06-09 06:18:46 +08:00
|
|
|
"""
|
|
|
|
Use annotations made by the sensitive_post_parameters and
|
|
|
|
sensitive_variables decorators to filter out sensitive information.
|
|
|
|
"""
|
2020-01-09 17:00:07 +08:00
|
|
|
cleansed_substitute = '********************'
|
|
|
|
hidden_settings = _lazy_re_compile('API|TOKEN|KEY|SECRET|PASS|SIGNATURE', 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.
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
if self.hidden_settings.search(key):
|
|
|
|
cleansed = self.cleansed_substitute
|
|
|
|
elif isinstance(value, dict):
|
|
|
|
cleansed = {k: self.cleanse_setting(k, v) for k, v in value.items()}
|
2020-04-11 09:25:23 +08:00
|
|
|
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])
|
2020-01-09 17:00:07 +08:00
|
|
|
else:
|
|
|
|
cleansed = value
|
|
|
|
except TypeError:
|
|
|
|
# If the key isn't regex-able, just return as-is.
|
|
|
|
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
|
2011-06-09 06:18:46 +08:00
|
|
|
|
2020-01-09 18:37:19 +08:00
|
|
|
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()}
|
|
|
|
|
2011-06-09 06:18:46 +08:00
|
|
|
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
|
|
|
|
|
2013-09-17 19:46:38 +08:00
|
|
|
def get_cleansed_multivaluedict(self, request, multivaluedict):
|
|
|
|
"""
|
2017-01-25 04:36:07 +08:00
|
|
|
Replace the keys in a MultiValueDict marked as sensitive with stars.
|
2013-09-17 19:46:38 +08:00
|
|
|
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:
|
2020-01-09 17:00:07 +08:00
|
|
|
multivaluedict[param] = self.cleansed_substitute
|
2013-09-17 19:46:38 +08:00
|
|
|
return multivaluedict
|
|
|
|
|
2011-06-09 06:18:46 +08:00
|
|
|
def get_post_parameters(self, request):
|
|
|
|
"""
|
2017-01-25 04:36:07 +08:00
|
|
|
Replace the values of POST parameters marked as sensitive with
|
2011-06-09 06:18:46 +08:00
|
|
|
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.
|
2017-12-07 06:17:59 +08:00
|
|
|
for k in cleansed:
|
2020-01-09 17:00:07 +08:00
|
|
|
cleansed[k] = self.cleansed_substitute
|
2011-06-09 06:18:46 +08:00
|
|
|
return cleansed
|
|
|
|
else:
|
|
|
|
# Cleanse only the specified parameters.
|
|
|
|
for param in sensitive_post_parameters:
|
2011-09-10 03:33:40 +08:00
|
|
|
if param in cleansed:
|
2020-01-09 17:00:07 +08:00
|
|
|
cleansed[param] = self.cleansed_substitute
|
2011-06-09 06:18:46 +08:00
|
|
|
return cleansed
|
|
|
|
else:
|
|
|
|
return request.POST
|
|
|
|
|
2013-09-17 19:46:38 +08:00
|
|
|
def cleanse_special_types(self, request, value):
|
2015-03-07 20:18:04 +08:00
|
|
|
try:
|
|
|
|
# If value is lazy or a complex object of another kind, this check
|
2015-07-09 23:27:07 +08:00
|
|
|
# might raise an exception. isinstance checks that lazy
|
|
|
|
# MultiValueDicts will have a return value.
|
|
|
|
is_multivalue_dict = isinstance(value, MultiValueDict)
|
2015-03-07 20:18:04 +08:00
|
|
|
except Exception as e:
|
|
|
|
return '{!r} while evaluating {!r}'.format(e, value)
|
|
|
|
|
2015-07-09 23:27:07 +08:00
|
|
|
if is_multivalue_dict:
|
2013-09-17 19:46:38 +08:00
|
|
|
# Cleanse MultiValueDicts (request.POST is the one we usually care about)
|
|
|
|
value = self.get_cleansed_multivaluedict(request, value)
|
|
|
|
return value
|
|
|
|
|
2011-06-09 06:18:46 +08:00
|
|
|
def get_traceback_frame_variables(self, request, tb_frame):
|
|
|
|
"""
|
2017-01-25 04:36:07 +08:00
|
|
|
Replace the values of variables marked as sensitive with
|
2011-06-09 06:18:46 +08:00
|
|
|
stars (*********).
|
|
|
|
"""
|
2012-06-04 08:33:09 +08:00
|
|
|
# Loop through the frame's callers to see if the sensitive_variables
|
|
|
|
# decorator was used.
|
|
|
|
current_frame = tb_frame.f_back
|
|
|
|
sensitive_variables = None
|
|
|
|
while current_frame is not None:
|
2016-04-04 08:37:32 +08:00
|
|
|
if (current_frame.f_code.co_name == 'sensitive_variables_wrapper' and
|
|
|
|
'sensitive_variables_wrapper' in current_frame.f_locals):
|
2012-06-04 08:33:09 +08:00
|
|
|
# The sensitive_variables decorator was used, so we 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
|
|
|
|
|
2013-01-01 01:34:08 +08:00
|
|
|
cleansed = {}
|
2011-06-09 06:18:46 +08:00
|
|
|
if self.is_active(request) and sensitive_variables:
|
|
|
|
if sensitive_variables == '__ALL__':
|
|
|
|
# Cleanse all variables
|
2017-12-07 06:17:59 +08:00
|
|
|
for name in tb_frame.f_locals:
|
2020-01-09 17:00:07 +08:00
|
|
|
cleansed[name] = self.cleansed_substitute
|
2011-06-09 06:18:46 +08:00
|
|
|
else:
|
|
|
|
# Cleanse specified variables
|
|
|
|
for name, value in tb_frame.f_locals.items():
|
|
|
|
if name in sensitive_variables:
|
2020-01-09 17:00:07 +08:00
|
|
|
value = self.cleansed_substitute
|
2013-09-17 19:46:38 +08:00
|
|
|
else:
|
|
|
|
value = self.cleanse_special_types(request, value)
|
2013-01-01 01:34:08 +08:00
|
|
|
cleansed[name] = value
|
2011-06-09 06:18:46 +08:00
|
|
|
else:
|
2013-09-17 19:46:38 +08:00
|
|
|
# Potentially cleanse the request and any MultiValueDicts if they
|
|
|
|
# are one of the frame variables.
|
2011-06-09 06:18:46 +08:00
|
|
|
for name, value in tb_frame.f_locals.items():
|
2013-09-17 19:46:38 +08:00
|
|
|
cleansed[name] = self.cleanse_special_types(request, value)
|
2013-01-01 01:34:08 +08:00
|
|
|
|
2016-04-04 08:37:32 +08:00
|
|
|
if (tb_frame.f_code.co_name == 'sensitive_variables_wrapper' and
|
|
|
|
'sensitive_variables_wrapper' in tb_frame.f_locals):
|
2013-01-01 01:34:08 +08:00
|
|
|
# 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.
|
2020-01-09 17:00:07 +08:00
|
|
|
cleansed['func_args'] = self.cleansed_substitute
|
|
|
|
cleansed['func_kwargs'] = self.cleansed_substitute
|
2013-01-01 01:34:08 +08:00
|
|
|
|
|
|
|
return cleansed.items()
|
2011-06-09 06:18:46 +08:00
|
|
|
|
2013-11-03 07:53:29 +08:00
|
|
|
|
2017-01-19 15:39:46 +08:00
|
|
|
class ExceptionReporter:
|
2017-01-25 04:36:07 +08:00
|
|
|
"""Organize and coordinate reporting on exceptions."""
|
2010-12-06 22:21:51 +08:00
|
|
|
def __init__(self, request, exc_type, exc_value, tb, is_email=False):
|
2008-07-16 02:47:49 +08:00
|
|
|
self.request = request
|
2011-06-09 06:18:46 +08:00
|
|
|
self.filter = get_exception_reporter_filter(self.request)
|
2008-07-16 02:47:49 +08:00
|
|
|
self.exc_type = exc_type
|
|
|
|
self.exc_value = exc_value
|
|
|
|
self.tb = tb
|
2010-12-06 22:21:51 +08:00
|
|
|
self.is_email = is_email
|
2008-07-16 02:47:49 +08:00
|
|
|
|
2015-03-06 23:53:25 +08:00
|
|
|
self.template_info = getattr(self.exc_value, 'template_debug', None)
|
2008-07-16 02:47:49 +08:00
|
|
|
self.template_does_not_exist = False
|
2015-03-05 23:52:22 +08:00
|
|
|
self.postmortem = None
|
2008-07-16 02:47:49 +08:00
|
|
|
|
2011-10-02 10:53:58 +08:00
|
|
|
def get_traceback_data(self):
|
2012-10-20 20:50:44 +08:00
|
|
|
"""Return a dictionary containing traceback information."""
|
2011-02-01 22:18:07 +08:00
|
|
|
if self.exc_type and issubclass(self.exc_type, TemplateDoesNotExist):
|
2015-03-05 23:52:22 +08:00
|
|
|
self.template_does_not_exist = True
|
2015-04-25 03:33:03 +08:00
|
|
|
self.postmortem = self.exc_value.chain or [self.exc_value]
|
2015-01-11 17:06:42 +08:00
|
|
|
|
2008-07-16 02:47:49 +08:00
|
|
|
frames = self.get_traceback_frames()
|
2011-01-13 21:47:03 +08:00
|
|
|
for i, frame in enumerate(frames):
|
2011-02-01 22:18:07 +08:00
|
|
|
if 'vars' in frame:
|
2014-08-13 21:32:14 +08:00
|
|
|
frame_vars = []
|
|
|
|
for k, v in frame['vars']:
|
|
|
|
v = pprint(v)
|
|
|
|
# Trim large blobs of data
|
|
|
|
if len(v) > 4096:
|
2018-08-22 21:13:58 +08:00
|
|
|
v = '%s… <trimmed %d bytes string>' % (v[0:4096], len(v))
|
2017-08-10 09:12:37 +08:00
|
|
|
frame_vars.append((k, v))
|
2014-08-13 21:32:14 +08:00
|
|
|
frame['vars'] = frame_vars
|
2011-01-13 21:47:03 +08:00
|
|
|
frames[i] = frame
|
2008-02-03 10:24:56 +08:00
|
|
|
|
2008-07-16 02:47:49 +08:00
|
|
|
unicode_hint = ''
|
2011-02-01 22:18:07 +08:00
|
|
|
if self.exc_type and issubclass(self.exc_type, UnicodeError):
|
2008-07-16 02:47:49 +08:00
|
|
|
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]
|
2017-01-26 17:08:08 +08:00
|
|
|
unicode_hint = force_str(
|
2014-09-04 20:15:09 +08:00
|
|
|
unicode_str[max(start - 5, 0):min(end + 5, len(unicode_str))],
|
|
|
|
'ascii', errors='replace'
|
|
|
|
)
|
2008-07-16 02:47:49 +08:00
|
|
|
from django import get_version
|
2016-12-04 02:51:32 +08:00
|
|
|
|
|
|
|
if self.request is None:
|
|
|
|
user_str = None
|
|
|
|
else:
|
|
|
|
try:
|
2017-04-22 01:52:26 +08:00
|
|
|
user_str = str(self.request.user)
|
2016-12-04 02:51:32 +08:00
|
|
|
except Exception:
|
|
|
|
# request.user may raise OperationalError if the database is
|
|
|
|
# unavailable, for example.
|
|
|
|
user_str = '[unable to retrieve the current user]'
|
|
|
|
|
2011-10-02 10:53:58 +08:00
|
|
|
c = {
|
2010-12-06 22:21:51 +08:00
|
|
|
'is_email': self.is_email,
|
2008-07-16 02:47:49 +08:00
|
|
|
'unicode_hint': unicode_hint,
|
|
|
|
'frames': frames,
|
|
|
|
'request': self.request,
|
2020-01-09 18:37:19 +08:00
|
|
|
'request_meta': self.filter.get_safe_request_meta(self.request),
|
2016-12-04 02:51:32 +08:00
|
|
|
'user_str': user_str,
|
2017-04-15 21:21:35 +08:00
|
|
|
'filtered_POST_items': list(self.filter.get_post_parameters(self.request).items()),
|
2020-01-09 17:00:07 +08:00
|
|
|
'settings': self.filter.get_safe_settings(),
|
2008-07-16 02:47:49 +08:00
|
|
|
'sys_executable': sys.executable,
|
|
|
|
'sys_version_info': '%d.%d.%d' % sys.version_info[0:3],
|
2015-02-11 04:37:53 +08:00
|
|
|
'server_time': timezone.now(),
|
2008-07-16 02:47:49 +08:00
|
|
|
'django_version_info': get_version(),
|
2013-10-27 09:27:42 +08:00
|
|
|
'sys_path': sys.path,
|
2008-07-16 02:47:49 +08:00
|
|
|
'template_info': self.template_info,
|
|
|
|
'template_does_not_exist': self.template_does_not_exist,
|
2015-03-05 23:52:22 +08:00
|
|
|
'postmortem': self.postmortem,
|
2011-10-02 10:53:58 +08:00
|
|
|
}
|
2016-09-07 19:09:45 +08:00
|
|
|
if self.request is not None:
|
|
|
|
c['request_GET_items'] = self.request.GET.items()
|
|
|
|
c['request_FILES_items'] = self.request.FILES.items()
|
|
|
|
c['request_COOKIES_items'] = self.request.COOKIES.items()
|
2011-02-01 22:18:07 +08:00
|
|
|
# Check whether exception info is available
|
|
|
|
if self.exc_type:
|
|
|
|
c['exception_type'] = self.exc_type.__name__
|
|
|
|
if self.exc_value:
|
2017-04-22 01:52:26 +08:00
|
|
|
c['exception_value'] = str(self.exc_value)
|
2011-02-01 22:18:07 +08:00
|
|
|
if frames:
|
|
|
|
c['lastframe'] = frames[-1]
|
2011-10-02 10:53:58 +08:00
|
|
|
return c
|
|
|
|
|
|
|
|
def get_traceback_html(self):
|
2017-01-25 04:36:07 +08:00
|
|
|
"""Return HTML version of debug 500 HTTP error page."""
|
2019-04-05 20:38:35 +08:00
|
|
|
with Path(CURRENT_DIR, 'templates', 'technical_500.html').open(encoding='utf-8') as fh:
|
2017-04-01 23:31:54 +08:00
|
|
|
t = DEBUG_ENGINE.from_string(fh.read())
|
2012-10-20 20:50:44 +08:00
|
|
|
c = Context(self.get_traceback_data(), use_l10n=False)
|
2011-10-02 10:53:58 +08:00
|
|
|
return t.render(c)
|
|
|
|
|
|
|
|
def get_traceback_text(self):
|
2017-01-25 04:36:07 +08:00
|
|
|
"""Return plain text version of debug 500 HTTP error page."""
|
2019-04-05 20:38:35 +08:00
|
|
|
with Path(CURRENT_DIR, 'templates', 'technical_500.txt').open(encoding='utf-8') as fh:
|
2017-04-01 23:31:54 +08:00
|
|
|
t = DEBUG_ENGINE.from_string(fh.read())
|
2012-10-20 20:50:44 +08:00
|
|
|
c = Context(self.get_traceback_data(), autoescape=False, use_l10n=False)
|
2008-07-16 02:47:49 +08:00
|
|
|
return t.render(c)
|
|
|
|
|
2019-10-09 05:52:00 +08:00
|
|
|
def _get_source(self, filename, loader, module_name):
|
2008-07-16 02:47:49 +08:00
|
|
|
source = None
|
2018-01-04 00:34:10 +08:00
|
|
|
if hasattr(loader, 'get_source'):
|
2017-09-07 20:16:21 +08:00
|
|
|
try:
|
2013-11-21 01:50:36 +08:00
|
|
|
source = loader.get_source(module_name)
|
2017-09-07 20:16:21 +08:00
|
|
|
except ImportError:
|
|
|
|
pass
|
2008-07-16 02:47:49 +08:00
|
|
|
if source is not None:
|
|
|
|
source = source.splitlines()
|
|
|
|
if source is None:
|
2017-09-07 20:16:21 +08:00
|
|
|
try:
|
2012-05-19 23:43:34 +08:00
|
|
|
with open(filename, 'rb') as fp:
|
2013-05-19 00:26:34 +08:00
|
|
|
source = fp.read().splitlines()
|
2019-01-28 23:01:35 +08:00
|
|
|
except OSError:
|
2017-09-07 20:16:21 +08:00
|
|
|
pass
|
2019-10-09 05:52:00 +08:00
|
|
|
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)
|
2008-07-16 02:47:49 +08:00
|
|
|
if source is None:
|
|
|
|
return None, [], None, []
|
|
|
|
|
2012-09-27 03:41:47 +08:00
|
|
|
# If we just read the source from a file, or if the loader did not
|
2017-01-21 05:04:05 +08:00
|
|
|
# apply tokenize.detect_encoding to decode the source into a
|
2012-09-27 03:41:47 +08:00
|
|
|
# string, then we should do that ourselves.
|
2016-12-29 23:27:49 +08:00
|
|
|
if isinstance(source[0], bytes):
|
2012-09-27 03:41:47 +08:00
|
|
|
encoding = 'ascii'
|
|
|
|
for line in source[:2]:
|
|
|
|
# File coding may be specified. Match pattern from PEP-263
|
2018-09-26 14:48:47 +08:00
|
|
|
# (https://www.python.org/dev/peps/pep-0263/)
|
2012-09-27 03:41:47 +08:00
|
|
|
match = re.search(br'coding[:=]\s*([-\w.]+)', line)
|
|
|
|
if match:
|
2020-05-11 04:03:39 +08:00
|
|
|
encoding = match[1].decode('ascii')
|
2012-09-27 03:41:47 +08:00
|
|
|
break
|
2016-12-29 23:27:49 +08:00
|
|
|
source = [str(sline, encoding, 'replace') for sline in source]
|
2008-07-16 02:47:49 +08:00
|
|
|
|
|
|
|
lower_bound = max(0, lineno - context_lines)
|
|
|
|
upper_bound = lineno + context_lines
|
|
|
|
|
2019-10-09 06:00:57 +08:00
|
|
|
try:
|
|
|
|
pre_context = source[lower_bound:lineno]
|
|
|
|
context_line = source[lineno]
|
|
|
|
post_context = source[lineno + 1:upper_bound]
|
|
|
|
except IndexError:
|
|
|
|
return None, [], None, []
|
2008-07-16 02:47:49 +08:00
|
|
|
return lower_bound, pre_context, context_line, post_context
|
|
|
|
|
|
|
|
def get_traceback_frames(self):
|
2014-11-15 23:03:20 +08:00
|
|
|
def explicit_or_implicit_cause(exc_value):
|
|
|
|
explicit = getattr(exc_value, '__cause__', None)
|
|
|
|
implicit = getattr(exc_value, '__context__', None)
|
|
|
|
return explicit or implicit
|
|
|
|
|
|
|
|
# Get the exception and all its causes
|
|
|
|
exceptions = []
|
|
|
|
exc_value = self.exc_value
|
|
|
|
while exc_value:
|
|
|
|
exceptions.append(exc_value)
|
|
|
|
exc_value = explicit_or_implicit_cause(exc_value)
|
2018-12-06 20:58:29 +08:00
|
|
|
if exc_value in exceptions:
|
|
|
|
# Avoid infinite loop if there's a cyclic reference (#29393).
|
|
|
|
break
|
2014-11-15 23:03:20 +08:00
|
|
|
|
2008-07-16 02:47:49 +08:00
|
|
|
frames = []
|
2014-11-15 23:03:20 +08:00
|
|
|
# No exceptions were supplied to ExceptionReporter
|
|
|
|
if not exceptions:
|
|
|
|
return frames
|
|
|
|
|
2016-12-01 18:38:01 +08:00
|
|
|
# In case there's just one exception, take the traceback from self.tb
|
2014-11-15 23:03:20 +08:00
|
|
|
exc_value = exceptions.pop()
|
2016-12-01 18:38:01 +08:00
|
|
|
tb = self.tb if not exceptions else exc_value.__traceback__
|
2014-11-15 23:03:20 +08:00
|
|
|
|
2008-07-16 02:47:49 +08:00
|
|
|
while tb is not None:
|
2011-06-09 06:18:46 +08:00
|
|
|
# Support for __traceback_hide__ which is used by a few libraries
|
2008-07-16 02:47:49 +08:00
|
|
|
# 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__')
|
2011-06-09 20:45:11 +08:00
|
|
|
module_name = tb.tb_frame.f_globals.get('__name__') or ''
|
2014-09-04 20:15:09 +08:00
|
|
|
pre_context_lineno, pre_context, context_line, post_context = self._get_lines_from_file(
|
|
|
|
filename, lineno, 7, loader, module_name,
|
|
|
|
)
|
2017-08-10 09:01:33 +08:00
|
|
|
if pre_context_lineno is None:
|
|
|
|
pre_context_lineno = lineno
|
|
|
|
pre_context = []
|
|
|
|
context_line = '<source code not available>'
|
|
|
|
post_context = []
|
|
|
|
frames.append({
|
|
|
|
'exc_cause': explicit_or_implicit_cause(exc_value),
|
|
|
|
'exc_cause_explicit': getattr(exc_value, '__cause__', True),
|
|
|
|
'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,
|
|
|
|
})
|
2014-11-15 23:03:20 +08:00
|
|
|
|
|
|
|
# If the traceback for current exception is consumed, try the
|
|
|
|
# other exception.
|
2016-12-01 18:38:01 +08:00
|
|
|
if not tb.tb_next and exceptions:
|
2014-11-15 23:03:20 +08:00
|
|
|
exc_value = exceptions.pop()
|
|
|
|
tb = exc_value.__traceback__
|
|
|
|
else:
|
|
|
|
tb = tb.tb_next
|
2005-11-17 11:10:03 +08:00
|
|
|
|
2008-07-16 02:47:49 +08:00
|
|
|
return frames
|
|
|
|
|
2005-11-15 01:44:50 +08:00
|
|
|
|
|
|
|
def technical_404_response(request, exception):
|
2017-01-25 04:36:07 +08:00
|
|
|
"""Create a technical 404 error response. `exception` is the Http404."""
|
2005-11-15 01:44:50 +08:00
|
|
|
try:
|
2014-02-16 00:45:16 +08:00
|
|
|
error_url = exception.args[0]['path']
|
|
|
|
except (IndexError, TypeError, KeyError):
|
2014-02-16 02:42:12 +08:00
|
|
|
error_url = request.path_info[1:] # Trim leading slash
|
2014-02-16 00:45:16 +08:00
|
|
|
|
|
|
|
try:
|
|
|
|
tried = exception.args[0]['tried']
|
|
|
|
except (IndexError, TypeError, KeyError):
|
2005-11-15 01:44:50 +08:00
|
|
|
tried = []
|
2005-12-06 13:04:56 +08:00
|
|
|
else:
|
2016-04-04 08:37:32 +08:00
|
|
|
if (not tried or ( # empty URLconf
|
|
|
|
request.path == '/' and
|
|
|
|
len(tried) == 1 and # default URLconf
|
|
|
|
len(tried[0]) == 1 and
|
|
|
|
getattr(tried[0][0], 'app_name', '') == getattr(tried[0][0], 'namespace', '') == 'admin'
|
|
|
|
)):
|
Simplified default project template.
Squashed commit of:
commit 508ec9144b35c50794708225b496bde1eb5e60aa
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Tue Jan 29 22:50:55 2013 +0100
Tweaked default settings file.
* Explained why BASE_DIR exists.
* Added a link to the database configuration options, and put it in its
own section.
* Moved sensitive settings that must be changed for production at the
top.
commit 6515fd2f1aa73a86dc8dbd2ccf512ddb6b140d57
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Tue Jan 29 14:35:21 2013 +0100
Documented the simplified app & project templates in the changelog.
commit 2c5b576c2ea91d84273a019b3d0b3b8b4da72f23
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Tue Jan 29 13:59:27 2013 +0100
Minor fixes in tutorials 5 and 6.
commit 55a51531be8104f21b3cca3f6bf70b0a7139a041
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Tue Jan 29 13:51:11 2013 +0100
Updated tutorial 2 for the new project template.
commit 29ddae87bdaecff12dd31b16b000c01efbde9e20
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Tue Jan 29 11:58:54 2013 +0100
Updated tutorial 1 for the new project template.
commit 0ecb9f6e2514cfd26a678a280d471433375101a3
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Tue Jan 29 11:29:13 2013 +0100
Adjusted the default URLconf detection to account for the admin.
It's now enabled by default.
commit 5fb4da0d3d09dac28dd94e3fde92b9d4335c0565
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Tue Jan 29 10:36:55 2013 +0100
Added security warnings for the most sensitive settings.
commit 718d84bd8ac4a42fb4b28ec93965de32680f091e
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 23:24:06 2013 +0100
Used an absolute path for the SQLite database.
This ensures the settings file works regardless of which directory
django-admin.py / manage.py is invoked from.
BASE_DIR got a +1 from a BDFL and another core dev. It doesn't involve
the concept of a "Django project"; it's just a convenient way to express
relative paths within the source code repository for non-Python files.
Thanks Jacob Kaplan-Moss for the suggestion.
commit 1b559b4bcda622e10909b68fe5cab90db6727dd9
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 23:22:40 2013 +0100
Removed STATIC_ROOT from the default settings template.
It isn't necessary in development, and it confuses beginners to no end.
Thanks Carl Meyer for the suggestion.
commit a55f141a500bb7c9a1bc259bbe1954c13b199671
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 23:21:43 2013 +0100
Removed MEDIA_ROOT/URL from default settings template.
Many sites will never deal with user-uploaded files, and MEDIA_ROOT is
complicated to explain.
Thanks Carl Meyer for the suggestion.
commit 44bf2f2441420fd9429ee9fe1f7207f92dd87e70
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 22:22:09 2013 +0100
Removed logging config.
This configuration is applied regardless of the value of LOGGING;
duplicating it in LOGGING is confusing.
commit eac747e848eaed65fd5f6f254f0a7559d856f88f
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 22:05:31 2013 +0100
Enabled the locale middleware by default.
USE_I18N is True by default, and doesn't work well without
LocaleMiddleware.
commit d806c62b2d00826dc2688c84b092627b8d571cab
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 22:03:16 2013 +0100
Enabled clickjacking protection by default.
commit 99152c30e6a15003f0b6737dc78e87adf462aacb
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 22:01:48 2013 +0100
Reorganized settings in logical sections, and trimmed comments.
commit d37ffdfcb24b7e0ec7cc113d07190f65fb12fb8a
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 16:54:11 2013 +0100
Avoided misleading TEMPLATE_DEBUG = DEBUG.
According to the docs TEMPLATE_DEBUG works only when DEBUG = True.
commit 15d9478d3a9850e85841e7cf09cf83050371c6bf
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 16:46:25 2013 +0100
Removed STATICFILES_FINDERS/TEMPLATE_LOADERS from default settings file.
Only developers with special needs ever need to change these settings.
commit 574da0eb5bfb4570883756914b4dbd7e20e1f61e
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 16:45:01 2013 +0100
Removed STATICFILES/TEMPLATES_DIRS from default settings file.
The current best practice is to put static files and templates in
applications, for easier testing and deployment.
commit 8cb18dbe56629aa1be74718a07e7cc66b4f9c9f0
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 16:24:16 2013 +0100
Removed settings related to email reporting from default settings file.
While handy for small scale projects, it isn't exactly a best practice.
commit 8ecbfcb3638058f0c49922540f874a7d802d864f
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Tue Jan 29 18:54:43 2013 +0100
Documented how to enable the sites framework.
commit 23fc91a6fa67d91ddd9d71b1c3e0dc26bdad9841
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 16:28:59 2013 +0100
Disabled the sites framework by default.
RequestSite does the job for single-domain websites.
commit c4d82eb8afc0eb8568bf9c4d12644272415e3960
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Tue Jan 29 00:08:33 2013 +0100
Added a default admin.py to the application template.
Thanks Ryan D Hiebert for the suggestion.
commit 4071dc771e5c44b1c5ebb9beecefb164ae465e22
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 10:59:49 2013 +0100
Enabled the admin by default.
Everyone uses the admin.
commit c807a31f8d89e7e7fd97380e3023f7983a8b6fcb
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 10:57:05 2013 +0100
Removed admindocs from default project template.
commit 09e4ce0e652a97da1a9e285046a91c8ad7a9189c
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 16:32:52 2013 +0100
Added links to the settings documentation.
commit 5b8f5eaef364eb790fcde6f9e86f7d266074cca8
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 11:06:54 2013 +0100
Used a significant example for URLconf includes.
commit 908e91d6fcee2a3cb51ca26ecdf12a6a24e69ef8
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 16:22:31 2013 +0100
Moved code comments about WSGI to docs, and rewrote said docs.
commit 50417e51996146f891d08ca8b74dcc736a581932
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 15:51:50 2013 +0100
Normalized the default application template.
Removed the default test that 1 + 1 = 2, because it's been committed
way too many times, in too many projects.
Added an import of `render` for views, because the first view will
often be:
def home(request):
return render(request, "mysite/home.html")
2013-01-28 22:51:50 +08:00
|
|
|
return default_urlconf(request)
|
2005-11-17 11:10:03 +08:00
|
|
|
|
2010-12-13 06:54:04 +08:00
|
|
|
urlconf = getattr(request, 'urlconf', settings.ROOT_URLCONF)
|
|
|
|
if isinstance(urlconf, types.ModuleType):
|
|
|
|
urlconf = urlconf.__name__
|
|
|
|
|
2014-06-16 15:39:18 +08:00
|
|
|
caller = ''
|
|
|
|
try:
|
|
|
|
resolver_match = resolve(request.path)
|
2019-08-10 04:40:25 +08:00
|
|
|
except Http404:
|
2014-06-16 15:39:18 +08:00
|
|
|
pass
|
|
|
|
else:
|
|
|
|
obj = resolver_match.func
|
|
|
|
|
|
|
|
if hasattr(obj, '__name__'):
|
|
|
|
caller = obj.__name__
|
|
|
|
elif hasattr(obj, '__class__') and hasattr(obj.__class__, '__name__'):
|
|
|
|
caller = obj.__class__.__name__
|
|
|
|
|
|
|
|
if hasattr(obj, '__module__'):
|
|
|
|
module = obj.__module__
|
|
|
|
caller = '%s.%s' % (module, caller)
|
|
|
|
|
2019-04-05 20:45:41 +08:00
|
|
|
with Path(CURRENT_DIR, 'templates', 'technical_404.html').open(encoding='utf-8') as fh:
|
2017-04-01 23:31:54 +08:00
|
|
|
t = DEBUG_ENGINE.from_string(fh.read())
|
2020-01-09 17:00:07 +08:00
|
|
|
reporter_filter = get_default_exception_reporter_filter()
|
2005-11-15 01:44:50 +08:00
|
|
|
c = Context({
|
2010-12-13 06:54:04 +08:00
|
|
|
'urlconf': urlconf,
|
2005-11-24 07:10:17 +08:00
|
|
|
'root_urlconf': settings.ROOT_URLCONF,
|
2014-02-15 21:41:01 +08:00
|
|
|
'request_path': error_url,
|
2005-11-24 07:10:17 +08:00
|
|
|
'urlpatterns': tried,
|
2017-01-12 06:17:25 +08:00
|
|
|
'reason': str(exception),
|
2005-11-24 07:10:17 +08:00
|
|
|
'request': request,
|
2020-01-09 17:00:07 +08:00
|
|
|
'settings': reporter_filter.get_safe_settings(),
|
2014-06-16 15:39:18 +08:00
|
|
|
'raising_view_name': caller,
|
2005-12-07 14:02:09 +08:00
|
|
|
})
|
2012-07-01 03:19:07 +08:00
|
|
|
return HttpResponseNotFound(t.render(c), content_type='text/html')
|
2005-12-07 14:02:09 +08:00
|
|
|
|
2013-11-03 07:53:29 +08:00
|
|
|
|
Simplified default project template.
Squashed commit of:
commit 508ec9144b35c50794708225b496bde1eb5e60aa
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Tue Jan 29 22:50:55 2013 +0100
Tweaked default settings file.
* Explained why BASE_DIR exists.
* Added a link to the database configuration options, and put it in its
own section.
* Moved sensitive settings that must be changed for production at the
top.
commit 6515fd2f1aa73a86dc8dbd2ccf512ddb6b140d57
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Tue Jan 29 14:35:21 2013 +0100
Documented the simplified app & project templates in the changelog.
commit 2c5b576c2ea91d84273a019b3d0b3b8b4da72f23
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Tue Jan 29 13:59:27 2013 +0100
Minor fixes in tutorials 5 and 6.
commit 55a51531be8104f21b3cca3f6bf70b0a7139a041
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Tue Jan 29 13:51:11 2013 +0100
Updated tutorial 2 for the new project template.
commit 29ddae87bdaecff12dd31b16b000c01efbde9e20
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Tue Jan 29 11:58:54 2013 +0100
Updated tutorial 1 for the new project template.
commit 0ecb9f6e2514cfd26a678a280d471433375101a3
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Tue Jan 29 11:29:13 2013 +0100
Adjusted the default URLconf detection to account for the admin.
It's now enabled by default.
commit 5fb4da0d3d09dac28dd94e3fde92b9d4335c0565
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Tue Jan 29 10:36:55 2013 +0100
Added security warnings for the most sensitive settings.
commit 718d84bd8ac4a42fb4b28ec93965de32680f091e
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 23:24:06 2013 +0100
Used an absolute path for the SQLite database.
This ensures the settings file works regardless of which directory
django-admin.py / manage.py is invoked from.
BASE_DIR got a +1 from a BDFL and another core dev. It doesn't involve
the concept of a "Django project"; it's just a convenient way to express
relative paths within the source code repository for non-Python files.
Thanks Jacob Kaplan-Moss for the suggestion.
commit 1b559b4bcda622e10909b68fe5cab90db6727dd9
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 23:22:40 2013 +0100
Removed STATIC_ROOT from the default settings template.
It isn't necessary in development, and it confuses beginners to no end.
Thanks Carl Meyer for the suggestion.
commit a55f141a500bb7c9a1bc259bbe1954c13b199671
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 23:21:43 2013 +0100
Removed MEDIA_ROOT/URL from default settings template.
Many sites will never deal with user-uploaded files, and MEDIA_ROOT is
complicated to explain.
Thanks Carl Meyer for the suggestion.
commit 44bf2f2441420fd9429ee9fe1f7207f92dd87e70
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 22:22:09 2013 +0100
Removed logging config.
This configuration is applied regardless of the value of LOGGING;
duplicating it in LOGGING is confusing.
commit eac747e848eaed65fd5f6f254f0a7559d856f88f
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 22:05:31 2013 +0100
Enabled the locale middleware by default.
USE_I18N is True by default, and doesn't work well without
LocaleMiddleware.
commit d806c62b2d00826dc2688c84b092627b8d571cab
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 22:03:16 2013 +0100
Enabled clickjacking protection by default.
commit 99152c30e6a15003f0b6737dc78e87adf462aacb
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 22:01:48 2013 +0100
Reorganized settings in logical sections, and trimmed comments.
commit d37ffdfcb24b7e0ec7cc113d07190f65fb12fb8a
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 16:54:11 2013 +0100
Avoided misleading TEMPLATE_DEBUG = DEBUG.
According to the docs TEMPLATE_DEBUG works only when DEBUG = True.
commit 15d9478d3a9850e85841e7cf09cf83050371c6bf
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 16:46:25 2013 +0100
Removed STATICFILES_FINDERS/TEMPLATE_LOADERS from default settings file.
Only developers with special needs ever need to change these settings.
commit 574da0eb5bfb4570883756914b4dbd7e20e1f61e
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 16:45:01 2013 +0100
Removed STATICFILES/TEMPLATES_DIRS from default settings file.
The current best practice is to put static files and templates in
applications, for easier testing and deployment.
commit 8cb18dbe56629aa1be74718a07e7cc66b4f9c9f0
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 16:24:16 2013 +0100
Removed settings related to email reporting from default settings file.
While handy for small scale projects, it isn't exactly a best practice.
commit 8ecbfcb3638058f0c49922540f874a7d802d864f
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Tue Jan 29 18:54:43 2013 +0100
Documented how to enable the sites framework.
commit 23fc91a6fa67d91ddd9d71b1c3e0dc26bdad9841
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 16:28:59 2013 +0100
Disabled the sites framework by default.
RequestSite does the job for single-domain websites.
commit c4d82eb8afc0eb8568bf9c4d12644272415e3960
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Tue Jan 29 00:08:33 2013 +0100
Added a default admin.py to the application template.
Thanks Ryan D Hiebert for the suggestion.
commit 4071dc771e5c44b1c5ebb9beecefb164ae465e22
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 10:59:49 2013 +0100
Enabled the admin by default.
Everyone uses the admin.
commit c807a31f8d89e7e7fd97380e3023f7983a8b6fcb
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 10:57:05 2013 +0100
Removed admindocs from default project template.
commit 09e4ce0e652a97da1a9e285046a91c8ad7a9189c
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 16:32:52 2013 +0100
Added links to the settings documentation.
commit 5b8f5eaef364eb790fcde6f9e86f7d266074cca8
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 11:06:54 2013 +0100
Used a significant example for URLconf includes.
commit 908e91d6fcee2a3cb51ca26ecdf12a6a24e69ef8
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 16:22:31 2013 +0100
Moved code comments about WSGI to docs, and rewrote said docs.
commit 50417e51996146f891d08ca8b74dcc736a581932
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 15:51:50 2013 +0100
Normalized the default application template.
Removed the default test that 1 + 1 = 2, because it's been committed
way too many times, in too many projects.
Added an import of `render` for views, because the first view will
often be:
def home(request):
return render(request, "mysite/home.html")
2013-01-28 22:51:50 +08:00
|
|
|
def default_urlconf(request):
|
2017-01-25 04:36:07 +08:00
|
|
|
"""Create an empty URLconf 404 error response."""
|
2019-04-05 20:45:41 +08:00
|
|
|
with Path(CURRENT_DIR, 'templates', 'default_urlconf.html').open(encoding='utf-8') as fh:
|
2017-04-01 23:31:54 +08:00
|
|
|
t = DEBUG_ENGINE.from_string(fh.read())
|
2014-05-18 06:26:46 +08:00
|
|
|
c = Context({
|
2017-08-07 22:33:55 +08:00
|
|
|
'version': get_docs_version(),
|
2014-05-18 06:26:46 +08:00
|
|
|
})
|
|
|
|
|
2012-07-01 03:19:07 +08:00
|
|
|
return HttpResponse(t.render(c), content_type='text/html')
|