如何查看django模板变量中的异常?
在Django的模板中,你可以这样调用一个对象的方法:
{{ my_object.my_method }}
但是,如果在'定义我的方法(def my_method(self))'的时候出现了异常或者错误,这个错误在渲染模板的时候是看不见的(输出的只是一个空字符串,所以不会显示任何错误信息)。
因为我想调试一下'定义我的方法(def my_method(self))'里面出了什么问题,所以我想开启一个类似于全局的Django标志,以便捕捉到这样的异常。
在settings.py文件中,我已经有了:
DEBUG = True
TEMPLATE_DEBUG = True
我可以接收到很多种模板异常,但是当我触发一个对象方法的时候却没有任何异常。
我该怎么办呢?
6 个回答
TEMPLATE_STRING_IF_INVALID
对我来说没用。一个简单的解决办法是打开 env/lib64/python2.7/site-packages/django/template/base.py
文件,找到 except Exception
这一行,然后在里面加上 print e
(假设你在用 manage.py runserver
,这样可以看到打印的输出)。
不过,往下几行有 current = context.template.engine.string_if_invalid
。我注意到 string_if_invalid
是空的,尽管我已经设置了 TEMPLATE_STRING_IF_INVALID
。这让我看到了文档中的这一部分:
https://docs.djangoproject.com/en/1.8/ref/templates/upgrading/#the-templates-settings
Django 的模板系统在 1.8 版本时进行了大改进,增加了对多种模板引擎的支持。
...
如果你的设置模块定义了
ALLOWED_INCLUDE_ROOTS
或TEMPLATE_STRING_IF_INVALID
,那么需要在OPTIONS
字典中,把它们的值放在 'allowed_include_roots
' 和 'string_if_invalid
' 这两个键下。
所以除了 @slacy的 TemplateSyntaxError 解决办法,
class InvalidString(str): def __mod__(self, other): from django.template.base import TemplateSyntaxError raise TemplateSyntaxError( "Undefined variable or unknown value for: %s" % other) TEMPLATE_STRING_IF_INVALID = InvalidString("%s")
你还需要这样定义 string_if_invalid
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'string_if_invalid': TEMPLATE_STRING_IF_INVALID,
...
这样一来,我发现了很多我之前都不知道的问题。其实这个功能应该默认就开启。为了处理那些期望静默失败的标签和过滤器,我在它们周围加了条件判断:
{% if obj.might_not_exist %}
{{ obj.might_not_exist }}
{% endif %}
不过我怀疑这只是因为 {% if %}
语句会静默失败。另一种方法可能是创建一个 hasattr
过滤器: {% if obj|hasattr:"might_not_exist" %}
。
最后我找到了解决办法:我开发了一个模板调试标签:
from django import template
import traceback
class DebugVariable(template.Variable):
def _resolve_lookup(self, context):
current = context
for bit in self.lookups:
try: # dictionary lookup
current = current[bit]
except (TypeError, AttributeError, KeyError):
try: # attribute lookup
current = getattr(current, bit)
if callable(current):
if getattr(current, 'alters_data', False):
current = settings.TEMPLATE_STRING_IF_INVALID
else:
try: # method call (assuming no args required)
current = current()
except:
raise Exception("Template Object Method Error : %s" % traceback.format_exc())
except (TypeError, AttributeError):
try: # list-index lookup
current = current[int(bit)]
except (IndexError, # list index out of range
ValueError, # invalid literal for int()
KeyError, # current is a dict without `int(bit)` key
TypeError, # unsubscriptable object
):
raise template.VariableDoesNotExist("Failed lookup for key [%s] in %r", (bit, current)) # missing attribute
except Exception, e:
if getattr(e, 'silent_variable_failure', False):
current = settings.TEMPLATE_STRING_IF_INVALID
else:
raise
except Exception, e:
if getattr(e, 'silent_variable_failure', False):
current = settings.TEMPLATE_STRING_IF_INVALID
else:
raise
return current
class DebugVarNode(template.Node):
def __init__(self, var):
self.var = DebugVariable(var)
def render(self, context):
return self.var.resolve(context)
@register.tag('debug_var')
def do_debug_var(parser, token):
"""
raise every variable rendering exception, TypeError included (usually hidden by django)
Syntax::
{% debug_var obj.my_method %} instead of {{ obj.my_method }}
"""
bits = token.contents.split()
if len(bits) != 2:
raise template.TemplateSyntaxError("'%s' tag takes one argument" % bits[0])
return DebugVarNode(bits[1])
所以现在在我的模板里,我只需要替换
{{ my_object.my_method }} by {% debug_var my_object.my_method %}
我刚刚实现了一个很不错的小技巧,可以做到这一点。你可以把这个放在你的调试设置里:
class InvalidString(str):
def __mod__(self, other):
from django.template.base import TemplateSyntaxError
raise TemplateSyntaxError(
"Undefined variable or unknown value for: %s" % other)
// this option is deprecated since django 1.8
TEMPLATE_STRING_IF_INVALID = InvalidString("%s")
// put it in template's options instead
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
// ...
'OPTIONS': {
'string_if_invalid': InvalidString("%s"),
},
},
]
这样一来,当解析器遇到一个未知或无效的值时,就会抛出一个模板语法错误。 我试过一些(用未定义的变量名),效果很好。 但是我还没有测试过函数返回值之类的,可能会变得复杂。