mirror of
https://github.com/django/django.git
synced 2025-07-05 02:09:13 +00:00
More repr cleanups.
Fixed template debugging and now it captures exceptions that occur during rendering. This pinpoints which template tag caused the exceptions and cuts down the stacktrace to what happened inside the template tag. Refactored some change list stuff. git-svn-id: http://code.djangoproject.com/svn/django/branches/new-admin@1046 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
0f9d7018a0
commit
13c27a87d6
@ -3,9 +3,10 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<div>
|
<div>
|
||||||
<h2> Error in Template </h2>
|
<h2> Error in Template </h2>
|
||||||
<pre>
|
<pre>
|
||||||
{{message}}
|
{{message}}
|
||||||
</pre>
|
{{traceback}}
|
||||||
|
</pre>
|
||||||
|
|
||||||
{%if top%}
|
{%if top%}
|
||||||
...
|
...
|
||||||
|
@ -106,16 +106,11 @@ def pagination(cl):
|
|||||||
}
|
}
|
||||||
pagination = inclusion_tag('admin/pagination')(pagination)
|
pagination = inclusion_tag('admin/pagination')(pagination)
|
||||||
|
|
||||||
#@simple_tag
|
|
||||||
def result_list(cl):
|
def result_headers(cl):
|
||||||
result_list, lookup_opts, mod, order_field, order_type, params, is_popup, opts = \
|
lookup_opts = cl.lookup_opts
|
||||||
cl.result_list, cl.lookup_opts, cl.mod, cl.order_field, cl.order_type, cl.params, cl.is_popup, cl.opts
|
|
||||||
|
|
||||||
raw_template = []
|
for i, field_name in enumerate(lookup_opts.admin.list_display):
|
||||||
if result_list:
|
|
||||||
# Table headers.
|
|
||||||
raw_template.append('<table cellspacing="0">\n<thead>\n<tr>\n')
|
|
||||||
for i, field_name in enumerate(lookup_opts.admin.list_display):
|
|
||||||
try:
|
try:
|
||||||
f = lookup_opts.get_field(field_name)
|
f = lookup_opts.get_field(field_name)
|
||||||
except meta.FieldDoesNotExist:
|
except meta.FieldDoesNotExist:
|
||||||
@ -131,91 +126,100 @@ def result_list(cl):
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
header = func.__name__
|
header = func.__name__
|
||||||
# Non-field list_display values don't get ordering capability.
|
# Non-field list_display values don't get ordering capability.
|
||||||
raw_template.append('<th>%s</th>' % capfirst(header))
|
yield {"text": header}
|
||||||
else:
|
else:
|
||||||
if isinstance(f.rel, meta.ManyToOne) and f.null:
|
if isinstance(f.rel, meta.ManyToOne) and f.null:
|
||||||
raw_template.append('<th>%s</th>' % capfirst(f.verbose_name))
|
raw_template.append('<th>%s</th>' % capfirst(f.verbose_name))
|
||||||
|
yield {"text": f.verbose_name}
|
||||||
else:
|
else:
|
||||||
th_classes = []
|
th_classes = []
|
||||||
new_order_type = 'asc'
|
new_order_type = 'asc'
|
||||||
if field_name == order_field:
|
if field_name == cl.order_field:
|
||||||
th_classes.append('sorted %sending' % order_type.lower())
|
th_classes.append('sorted %sending' % order_type.lower())
|
||||||
new_order_type = {'asc': 'desc', 'desc': 'asc'}[order_type.lower()]
|
new_order_type = {'asc': 'desc', 'desc': 'asc'}[order_type.lower()]
|
||||||
raw_template.append('<th%s><a href="%s">%s</a></th>' % \
|
|
||||||
((th_classes and ' class="%s"' % ' '.join(th_classes) or ''),
|
yield {"text" : f.verbose_name,
|
||||||
cl.get_query_string( {ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
|
"sortable": True,
|
||||||
capfirst(f.verbose_name)))
|
"order" : new_order_type,
|
||||||
raw_template.append('</tr>\n</thead>\n')
|
"class_attrib" : (th_classes and ' class="%s"' % ' '.join(th_classes) or '') }
|
||||||
# Result rows.
|
|
||||||
pk = lookup_opts.pk.name
|
def items_for_result(cl, result):
|
||||||
for i, result in enumerate(result_list):
|
first = True
|
||||||
raw_template.append('<tr class="row%s">\n' % (i % 2 + 1))
|
pk = cl.lookup_opts.pk.name
|
||||||
for j, field_name in enumerate(lookup_opts.admin.list_display):
|
for field_name in cl.lookup_opts.admin.list_display:
|
||||||
row_class = ''
|
row_class = ''
|
||||||
try:
|
try:
|
||||||
f = lookup_opts.get_field(field_name)
|
f = cl.lookup_opts.get_field(field_name)
|
||||||
except meta.FieldDoesNotExist:
|
except meta.FieldDoesNotExist:
|
||||||
# For non-field list_display values, the value is a method
|
# For non-field list_display values, the value is a method
|
||||||
# name. Execute the method.
|
# name. Execute the method.
|
||||||
try:
|
try:
|
||||||
result_repr = strip_tags(str(getattr(result, field_name)()))
|
result_repr = strip_tags(str(getattr(result, field_name)()))
|
||||||
except ObjectDoesNotExist:
|
except ObjectDoesNotExist:
|
||||||
result_repr = EMPTY_CHANGELIST_VALUE
|
result_repr = EMPTY_CHANGELIST_VALUE
|
||||||
|
else:
|
||||||
|
field_val = getattr(result, f.column)
|
||||||
|
|
||||||
|
if isinstance(f.rel, meta.ManyToOne):
|
||||||
|
if field_val is not None:
|
||||||
|
result_repr = getattr(result, 'get_%s' % f.name)()
|
||||||
else:
|
else:
|
||||||
field_val = getattr(result, f.column)
|
result_repr = EMPTY_CHANGELIST_VALUE
|
||||||
# Foreign-key fields are special: Use the repr of the
|
# Dates are special: They're formatted in a certain way.
|
||||||
# related object.
|
elif isinstance(f, meta.DateField):
|
||||||
if isinstance(f.rel, meta.ManyToOne):
|
if field_val:
|
||||||
if field_val is not None:
|
if isinstance(f, meta.DateTimeField):
|
||||||
result_repr = getattr(result, 'get_%s' % f.name)()
|
result_repr = dateformat.format(field_val, 'N j, Y, P')
|
||||||
else:
|
|
||||||
result_repr = EMPTY_CHANGELIST_VALUE
|
|
||||||
# Dates are special: They're formatted in a certain way.
|
|
||||||
elif isinstance(f, meta.DateField):
|
|
||||||
if field_val:
|
|
||||||
if isinstance(f, meta.DateTimeField):
|
|
||||||
result_repr = dateformat.format(field_val, 'N j, Y, P')
|
|
||||||
else:
|
|
||||||
result_repr = dateformat.format(field_val, 'N j, Y')
|
|
||||||
else:
|
|
||||||
result_repr = EMPTY_CHANGELIST_VALUE
|
|
||||||
row_class = ' class="nowrap"'
|
|
||||||
# Booleans are special: We use images.
|
|
||||||
elif isinstance(f, meta.BooleanField) or isinstance(f, meta.NullBooleanField):
|
|
||||||
BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'}
|
|
||||||
result_repr = '<img src="%simg/admin/icon-%s.gif" alt="%s" />' % (ADMIN_MEDIA_PREFIX, BOOLEAN_MAPPING[field_val], field_val)
|
|
||||||
# ImageFields are special: Use a thumbnail.
|
|
||||||
elif isinstance(f, meta.ImageField):
|
|
||||||
from django.parts.media.photos import get_thumbnail_url
|
|
||||||
result_repr = '<img src="%s" alt="%s" title="%s" />' % (get_thumbnail_url(getattr(result, 'get_%s_url' % f.name)(), '120'), field_val, field_val)
|
|
||||||
# FloatFields are special: Zero-pad the decimals.
|
|
||||||
elif isinstance(f, meta.FloatField):
|
|
||||||
if field_val is not None:
|
|
||||||
result_repr = ('%%.%sf' % f.decimal_places) % field_val
|
|
||||||
else:
|
|
||||||
result_repr = EMPTY_CHANGELIST_VALUE
|
|
||||||
# Fields with choices are special: Use the representation
|
|
||||||
# of the choice.
|
|
||||||
elif f.choices:
|
|
||||||
result_repr = dict(f.choices).get(field_val, EMPTY_CHANGELIST_VALUE)
|
|
||||||
else:
|
else:
|
||||||
result_repr = strip_tags(str(field_val))
|
result_repr = dateformat.format(field_val, 'N j, Y')
|
||||||
# Some browsers don't like empty "<td></td>"s.
|
|
||||||
if result_repr == '':
|
|
||||||
result_repr = ' '
|
|
||||||
if j == 0: # First column is a special case
|
|
||||||
result_id = getattr(result, pk)
|
|
||||||
raw_template.append('<th%s><a href="%s/"%s>%s</a></th>' % \
|
|
||||||
(row_class, result_id, (is_popup and ' onclick="opener.dismissRelatedLookupPopup(window, %r); return false;"' % result_id or ''), result_repr))
|
|
||||||
else:
|
else:
|
||||||
raw_template.append('<td%s>%s</td>' % (row_class, result_repr))
|
result_repr = EMPTY_CHANGELIST_VALUE
|
||||||
raw_template.append('</tr>\n')
|
row_class = ' class="nowrap"'
|
||||||
del result_list # to free memory
|
# Booleans are special: We use images.
|
||||||
raw_template.append('</table>\n')
|
elif isinstance(f, meta.BooleanField) or isinstance(f, meta.NullBooleanField):
|
||||||
else:
|
BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'}
|
||||||
raw_template.append('<p>No %s matched your search criteria.</p>' % opts.verbose_name_plural)
|
result_repr = '<img src="%simg/admin/icon-%s.gif" alt="%s" />' % (ADMIN_MEDIA_PREFIX, BOOLEAN_MAPPING[field_val], field_val)
|
||||||
return ''.join(raw_template)
|
# ImageFields are special: Use a thumbnail.
|
||||||
result_list = simple_tag(result_list)
|
elif isinstance(f, meta.ImageField):
|
||||||
|
from django.parts.media.photos import get_thumbnail_url
|
||||||
|
result_repr = '<img src="%s" alt="%s" title="%s" />' % (get_thumbnail_url(getattr(result, 'get_%s_url' % f.name)(), '120'), field_val, field_val)
|
||||||
|
# FloatFields are special: Zero-pad the decimals.
|
||||||
|
elif isinstance(f, meta.FloatField):
|
||||||
|
if field_val is not None:
|
||||||
|
result_repr = ('%%.%sf' % f.decimal_places) % field_val
|
||||||
|
else:
|
||||||
|
result_repr = EMPTY_CHANGELIST_VALUE
|
||||||
|
# Fields with choices are special: Use the representation
|
||||||
|
# of the choice.
|
||||||
|
elif f.choices:
|
||||||
|
result_repr = dict(f.choices).get(field_val, EMPTY_CHANGELIST_VALUE)
|
||||||
|
else:
|
||||||
|
result_repr = strip_tags(str(field_val))
|
||||||
|
if result_repr == '':
|
||||||
|
result_repr = ' '
|
||||||
|
if first: # First column is a special case
|
||||||
|
first = False
|
||||||
|
result_id = getattr(result, pk)
|
||||||
|
yield ('<th%s><a href="%s/"%s>%s</a></th>' % \
|
||||||
|
(row_class, result_id, (cl.is_popup and ' onclick="opener.dismissRelatedLookupPopup(window, %r); return false;"' % result_id or ''), result_repr))
|
||||||
|
else:
|
||||||
|
yield ('<td%s>%s</td>' % (row_class, result_repr))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def results(cl):
|
||||||
|
for res in cl.result_list:
|
||||||
|
yield list(items_for_result(cl,res))
|
||||||
|
|
||||||
|
#@inclusion_tag("admin/change_list_results")
|
||||||
|
def result_list(cl):
|
||||||
|
res = list(results(cl))
|
||||||
|
return {'cl': cl,
|
||||||
|
'result_headers': list(result_headers(cl)),
|
||||||
|
'results': list(results(cl)), }
|
||||||
|
result_list = inclusion_tag("admin/change_list_results")(result_list)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#@simple_tag
|
#@simple_tag
|
||||||
def date_hierarchy(cl):
|
def date_hierarchy(cl):
|
||||||
|
@ -345,10 +345,15 @@ class ChangeList(object):
|
|||||||
# If the order-by field is a field with a relationship, order by the value
|
# If the order-by field is a field with a relationship, order by the value
|
||||||
# in the related table.
|
# in the related table.
|
||||||
lookup_order_field = order_field
|
lookup_order_field = order_field
|
||||||
if isinstance(lookup_opts.get_field(order_field).rel, meta.ManyToOne):
|
try:
|
||||||
f = lookup_opts.get_field(order_field)
|
f = lookup_opts.get_field(order_field)
|
||||||
rel_ordering = f.rel.to.ordering and f.rel.to.ordering[0] or f.rel.to.pk.column
|
except meta.FieldDoesNotExist:
|
||||||
lookup_order_field = '%s.%s' % (f.rel.to.db_table, rel_ordering)
|
pass
|
||||||
|
else:
|
||||||
|
if isinstance(lookup_opts.get_field(order_field).rel, meta.ManyToOne):
|
||||||
|
f = lookup_opts.get_field(order_field)
|
||||||
|
rel_ordering = f.rel.to.ordering and f.rel.to.ordering[0] or f.rel.to.pk.column
|
||||||
|
lookup_order_field = '%s.%s' % (f.rel.to.db_table, rel_ordering)
|
||||||
# Use select_related if one of the list_display options is a field with a
|
# Use select_related if one of the list_display options is a field with a
|
||||||
# relationship.
|
# relationship.
|
||||||
for field_name in lookup_opts.admin.list_display:
|
for field_name in lookup_opts.admin.list_display:
|
||||||
|
@ -1728,11 +1728,11 @@ def manipulator_save(opts, klass, add, change, self, new_data):
|
|||||||
# Calculate whether any fields have changed.
|
# Calculate whether any fields have changed.
|
||||||
if change:
|
if change:
|
||||||
if not old_rel_obj: # This object didn't exist before.
|
if not old_rel_obj: # This object didn't exist before.
|
||||||
self.fields_added.append('%s "%r"' % (related.opts.verbose_name, new_rel_obj))
|
self.fields_added.append('%s "%s"' % (related.opts.verbose_name, new_rel_obj))
|
||||||
else:
|
else:
|
||||||
for f in related.opts.fields:
|
for f in related.opts.fields:
|
||||||
if not f.primary_key and f != related.field and str(getattr(old_rel_obj, f.column)) != str(getattr(new_rel_obj, f.column)):
|
if not f.primary_key and f != related.field and str(getattr(old_rel_obj, f.column)) != str(getattr(new_rel_obj, f.column)):
|
||||||
self.fields_changed.append('%s for %s "%r"' % (f.verbose_name, related.opts.verbose_name, new_rel_obj))
|
self.fields_changed.append('%s for %s "%s"' % (f.verbose_name, related.opts.verbose_name, new_rel_obj))
|
||||||
|
|
||||||
# Save many-to-many objects.
|
# Save many-to-many objects.
|
||||||
for f in related.opts.many_to_many:
|
for f in related.opts.many_to_many:
|
||||||
@ -1745,7 +1745,7 @@ def manipulator_save(opts, klass, add, change, self, new_data):
|
|||||||
# the primary key (ID) was provided, delete the item.
|
# the primary key (ID) was provided, delete the item.
|
||||||
if change and all_cores_blank and old_rel_obj:
|
if change and all_cores_blank and old_rel_obj:
|
||||||
new_rel_obj.delete()
|
new_rel_obj.delete()
|
||||||
self.fields_deleted.append('%s "%r"' % (related.opts.verbose_name, old_rel_obj))
|
self.fields_deleted.append('%s "%s"' % (related.opts.verbose_name, old_rel_obj))
|
||||||
|
|
||||||
|
|
||||||
# Save the order, if applicable.
|
# Save the order, if applicable.
|
||||||
|
@ -143,14 +143,7 @@ class Template:
|
|||||||
return self.nodelist.render(context)
|
return self.nodelist.render(context)
|
||||||
|
|
||||||
def compile_string(template_string, origin):
|
def compile_string(template_string, origin):
|
||||||
"Compiles template_string into NodeList ready for rendering"
|
"Compiles template_string into NodeList ready for rendering"
|
||||||
if TEMPLATE_DEBUG:
|
|
||||||
lexer_factory = DebugLexer
|
|
||||||
parser_factory = DebugParser
|
|
||||||
else:
|
|
||||||
lexer_factory = Lexer
|
|
||||||
parser_factory = Parser
|
|
||||||
|
|
||||||
lexer = lexer_factory(template_string, origin)
|
lexer = lexer_factory(template_string, origin)
|
||||||
parser = parser_factory(lexer.tokenize())
|
parser = parser_factory(lexer.tokenize())
|
||||||
return parser.parse()
|
return parser.parse()
|
||||||
@ -356,7 +349,9 @@ class Parser(object):
|
|||||||
def delete_first_token(self):
|
def delete_first_token(self):
|
||||||
del self.tokens[0]
|
del self.tokens[0]
|
||||||
|
|
||||||
|
def format_source(source):
|
||||||
|
return "at %s, line %d" % source
|
||||||
|
|
||||||
class DebugParser(Parser):
|
class DebugParser(Parser):
|
||||||
def __init__(self, lexer):
|
def __init__(self, lexer):
|
||||||
super(DebugParser, self).__init__(lexer)
|
super(DebugParser, self).__init__(lexer)
|
||||||
@ -384,13 +379,13 @@ class DebugParser(Parser):
|
|||||||
super(DebugParser, self).extend_nodelist(nodelist, node, token)
|
super(DebugParser, self).extend_nodelist(nodelist, node, token)
|
||||||
|
|
||||||
def empty_variable(self, token):
|
def empty_variable(self, token):
|
||||||
raise self.error( token.source, "Empty variable tag %s" % self.format_source(token.source))
|
raise self.error( token.source, "Empty variable tag %s" % format_source(token.source))
|
||||||
|
|
||||||
def empty_block_tag(self, token):
|
def empty_block_tag(self, token):
|
||||||
raise self.error( token.source, "Empty block tag %s" % self.format_source(token.source))
|
raise self.error( token.source, "Empty block tag %s" % format_source(token.source))
|
||||||
|
|
||||||
def invalid_block_tag(self, token, command):
|
def invalid_block_tag(self, token, command):
|
||||||
raise self.error( token.source, "Invalid block tag: '%s' %s" % (command, self.format_source(token.source)))
|
raise self.error( token.source, "Invalid block tag: '%s' %s" % (command, format_source(token.source)))
|
||||||
|
|
||||||
def unclosed_block_tag(self, token, parse_until):
|
def unclosed_block_tag(self, token, parse_until):
|
||||||
(command, (origin,line)) = self.command_stack.pop()
|
(command, (origin,line)) = self.command_stack.pop()
|
||||||
@ -402,6 +397,13 @@ class DebugParser(Parser):
|
|||||||
if not hasattr(e, 'source'):
|
if not hasattr(e, 'source'):
|
||||||
e.source = token.source
|
e.source = token.source
|
||||||
|
|
||||||
|
if TEMPLATE_DEBUG:
|
||||||
|
lexer_factory = DebugLexer
|
||||||
|
parser_factory = DebugParser
|
||||||
|
else:
|
||||||
|
lexer_factory = Lexer
|
||||||
|
parser_factory = Parser
|
||||||
|
|
||||||
class FilterParser:
|
class FilterParser:
|
||||||
"""Parse a variable token and its optional filters (all as a single string),
|
"""Parse a variable token and its optional filters (all as a single string),
|
||||||
and return a list of tuples of the filter name and arguments.
|
and return a list of tuples of the filter name and arguments.
|
||||||
@ -589,7 +591,6 @@ class RegexFilterParser(object):
|
|||||||
raise TemplateSyntaxError, "Could not parse the remainder: %s" % token[upto:]
|
raise TemplateSyntaxError, "Could not parse the remainder: %s" % token[upto:]
|
||||||
self.var , self.filters = var, filters
|
self.var , self.filters = var, filters
|
||||||
|
|
||||||
|
|
||||||
def get_filters_from_token(token):
|
def get_filters_from_token(token):
|
||||||
"Convenient wrapper for FilterParser"
|
"Convenient wrapper for FilterParser"
|
||||||
p = RegexFilterParser(token)
|
p = RegexFilterParser(token)
|
||||||
@ -682,12 +683,7 @@ class NodeList(list):
|
|||||||
bits = []
|
bits = []
|
||||||
for node in self:
|
for node in self:
|
||||||
if isinstance(node, Node):
|
if isinstance(node, Node):
|
||||||
try:
|
bits.append(self.render_node(node, context))
|
||||||
result = node.render(context)
|
|
||||||
except TemplateSyntaxError, e:
|
|
||||||
if not self.handle_render_error(node, e):
|
|
||||||
raise
|
|
||||||
bits.append(result)
|
|
||||||
else:
|
else:
|
||||||
bits.append(node)
|
bits.append(node)
|
||||||
return ''.join(bits)
|
return ''.join(bits)
|
||||||
@ -699,14 +695,30 @@ class NodeList(list):
|
|||||||
nodes.extend(node.get_nodes_by_type(nodetype))
|
nodes.extend(node.get_nodes_by_type(nodetype))
|
||||||
return nodes
|
return nodes
|
||||||
|
|
||||||
def handle_render_error(self, node, exception):
|
def render_node(self, node, context):
|
||||||
pass
|
return(node.render(context))
|
||||||
|
|
||||||
class DebugNodeList(NodeList):
|
class DebugNodeList(NodeList):
|
||||||
def handle_render_error(self, node, exception):
|
def render_node(self, node, context):
|
||||||
if not hasattr(exception, 'source'):
|
try:
|
||||||
exception.source = node.source
|
result = node.render(context)
|
||||||
|
except TemplateSyntaxError, e:
|
||||||
|
if not hasattr(e, 'source'):
|
||||||
|
e.source = node.source
|
||||||
|
raise
|
||||||
|
except Exception, e:
|
||||||
|
from traceback import extract_tb, format_list, format_exception_only
|
||||||
|
from sys import exc_info
|
||||||
|
t,v,tb = exc_info()
|
||||||
|
frames = extract_tb(tb)
|
||||||
|
frames.pop(0)
|
||||||
|
wrapped = TemplateSyntaxError( ' While rendering %s , caught exception:\n %s'
|
||||||
|
% ( format_source(node.source),
|
||||||
|
"".join(format_exception_only(t,v)).replace('\n','')))
|
||||||
|
wrapped.source = node.source
|
||||||
|
wrapped.traceback = "".join(format_list(frames))
|
||||||
|
raise wrapped
|
||||||
|
return result
|
||||||
|
|
||||||
class TextNode(Node):
|
class TextNode(Node):
|
||||||
def __init__(self, s):
|
def __init__(self, s):
|
||||||
|
@ -47,11 +47,11 @@ class LoaderOrigin(Origin):
|
|||||||
self.loader, self.name, self.dirs = loader, name, dirs
|
self.loader, self.name, self.dirs = loader, name, dirs
|
||||||
|
|
||||||
def reload(self):
|
def reload(self):
|
||||||
return self.loader(self.name, self.dirs)
|
return self.loader(self.name, self.dirs)[0]
|
||||||
|
|
||||||
def make_origin(display_name, loader, name, dirs):
|
def make_origin(display_name, loader, name, dirs):
|
||||||
if TEMPLATE_DEBUG:
|
if TEMPLATE_DEBUG:
|
||||||
LoaderOrigin(display_name, loader, name, dirs)
|
return LoaderOrigin(display_name, loader, name, dirs)
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -1,25 +1,30 @@
|
|||||||
from django.core.extensions import render_to_response
|
|
||||||
from django.utils.html import escape
|
|
||||||
|
|
||||||
context_lines = 10
|
|
||||||
|
|
||||||
class TemplateDebugMiddleware(object):
|
class TemplateDebugMiddleware(object):
|
||||||
def process_exception(self, request, exception):
|
def process_exception(self, request, exception):
|
||||||
|
from django.core.template.loader import render_to_string
|
||||||
|
from django.utils.html import escape
|
||||||
|
from django.utils.httpwrappers import HttpResponseServerError
|
||||||
|
from django.core.extensions import DjangoContext
|
||||||
|
from itertools import count, izip
|
||||||
|
context_lines = 10
|
||||||
if hasattr(exception, 'source'):
|
if hasattr(exception, 'source'):
|
||||||
origin, line = exception.source
|
origin, line = exception.source
|
||||||
template_source = origin.reload()
|
template_source = origin.reload()
|
||||||
|
|
||||||
source_lines = [ (i + 1,s) for (i,s) in enumerate(escape(template_source).split("\n"))]
|
source_lines = [ (i,s) for (i,s) in izip(count(1), escape(template_source).split("\n"))]
|
||||||
total = len(source_lines)
|
total = len(source_lines)
|
||||||
top = max(0, line - context_lines)
|
top = max(0, line - context_lines)
|
||||||
bottom = min(total, line + 1 + context_lines)
|
bottom = min(total, line + 1 + context_lines)
|
||||||
|
traceback = hasattr(exception, 'traceback') and exception.traceback or ''
|
||||||
return render_to_response('template_debug', {
|
return HttpResponseServerError(
|
||||||
'message' : exception.args[0],
|
render_to_string('template_debug',
|
||||||
'source_lines' : source_lines[top:bottom],
|
DjangoContext(request, {
|
||||||
'top': top ,
|
'message' : exception.args[0],
|
||||||
'bottom': bottom ,
|
'traceback' : traceback,
|
||||||
'total' : total,
|
'source_lines' : source_lines[top:bottom],
|
||||||
'line' : line
|
'top': top ,
|
||||||
})
|
'bottom': bottom ,
|
||||||
|
'total' : total,
|
||||||
|
'line' : line
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
)
|
Loading…
x
Reference in New Issue
Block a user