当 Django 在渲染模板时遇到未定义的变量错误时,我怎样才能告诉我?
我已经尝试了明显的
DEBUG = True
和TEMPLATE_DEBUG = True
,但它们没有帮助。
将其放入调试设置中:
class InvalidString(str):
def __mod__(self, other):
from django.template.base import TemplateSyntaxError
raise TemplateSyntaxError(
"Undefined variable or unknown value for: \"%s\"" % other)
TEMPLATES = [
{
(...)
'OPTIONS': {
(...)
'string_if_invalid': InvalidString("%s")
},
},
]
TEMPLATE_STRING_IF_INVALID = InvalidString("%s")
当模板引擎看到或发现未定义的值时,这应该会引发错误。
根据 django 文档, 默认情况下,未定义的变量被视为“”(空字符串)。而在 if for regroup 中,它是 None。 如果您要识别未定义的变量,请更改设置中的 TEMPLATE_STRING_IF_INVALID。 '%s' 使无效变量被渲染为其变量名,这样您就可以轻松识别。 如何处理无效变量
查找上下文中不存在的模板变量对我来说很重要,因为多次出现错误,因为视图已更改但模板没有更改。
我使用了这种技术,在
manage.py
中实现,以达到在使用上下文中找不到的模板变量时破坏测试的效果。请注意,此技术适用于 for
循环和 if
语句,而不仅仅是 {{ variables }}
。
import sys
# sometimes it's OK if a variable is undefined:
allowed_undefined_variables = [
'variable_1',
'variable_2',
]
if 'test' in sys.argv:
import django.template.base as template_base
old_resolve = template_base.Variable.resolve
def new_resolve(self, context):
try:
value = old_resolve(self, context)
except template_base.VariableDoesNotExist as e:
# if it's not a variable that's allowed to not exist then raise a
# base Exception so Nodes can't catch it (which will make the test
# fail)
if self.var not in allowed_undefined_variables:
raise Exception(e)
# re-raise the original and let the individual Nodes deal with it
# however they'd like
raise e
return value
template_base.Variable.resolve = new_resolve
Django 似乎依赖于未定义的变量作为一个简单的空字符串。因此,我们不要更改此行为或使其抛出异常,而是保持不变,但让它记录警告!
在您的
settings.py
文件中:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
# ...
'OPTIONS': {
# ...
'string_if_invalid': InvalidStringShowWarning("%s"),
},
}
]
(在较新的 Django 版本中,
string_if_invalid
取代了 TEMPLATE_STRING_IF_INVALID
。)
更进一步,您需要定义
InvalidStringShowWarning
类,使其在记录警告时表现良好:
class InvalidStringShowWarning(str):
def __mod__(self, other):
import logging
logger = logging.getLogger(__name__)
logger.warning("In template, undefined variable or unknown value for: '%s'" % (other,))
return ""
def __bool__(self): # if using Python 2, use __nonzero__ instead
# make the template tag `default` use its fallback value
return False
您应该能够在
python manage.py runserver
的输出中看到警告。
我认为这是 Django 的一个重大疏忽,也是我不喜欢使用他们的默认模板引擎的主要原因。可悲的事实是,至少现在(Django 1.9),你无法可靠地实现这种效果。
你可以让Django在遇到
{{ undefined_variable }}
时引发异常 - 通过使用slacy的答案中描述的“黑客”。你不能让Django在
{% if undefined_variable %}
或{% for x in undefined_variable %}
等上引发相同的异常。“黑客”在这种情况下不起作用。即使在可以的情况下,Django 作者也强烈建议不要在生产环境中使用此技术。除非您确定不在应用程序中使用 Django 的内置模板,否则您应该在 DEBUG
模式下使用“the hack”
only。
但是,如果你现在坚持使用 Django 的模板,我建议使用 slacy 的答案,只需确保你处于
DEBUG
模式即可。
阅读模板中如何处理无效变量。基本上,只需将 TEMPLATE_STRING_IF_INVALID 设置为 settings.py 中的某些内容即可。
TEMPLATE_STRING_IF_INVALID = "He's dead Jim! [%s]"
我接下来使用:
import logging
from django.utils.html import format_html
from django.utils.safestring import mark_safe
class InvalidTemplateVariable(str):
"""
Class for override output that the Django template system
determinated as invalid (e.g. misspelled) variables.
"""
# styles for display message in HTML`s pages
styles = mark_safe('style="color: red; font-weight: bold;"')
def __mod__(self, variable):
"""Overide a standart output here."""
# access to current settings
from django.conf import settings
# display the message on page in make log it only on stage development
if settings.DEBUG is True:
# format message with captured variable
msg = 'Attention! A variable "{}" does not exists.'.format(variable)
# get logger and make
logger = self.get_logger()
logger.warning(msg)
# mark text as non-escaped in HTML
return format_html('<i {}>{}</i>', self.styles, msg)
# on production it will be not displayed
return ''
def get_logger(self):
"""Create own logger with advanced error`s details."""
logger = logging.getLogger(self.__class__.__name__)
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
return logger
在设置文件中使用(默认为settings.py):
TEMPLATES = [
{
......
'OPTIONS': {
.....................
'string_if_invalid': InvalidTemplateVariable('%s'),
.....................
},
},
]
或直接
TEMPLATES[0]['OPTIONS']['string_if_invalid'] = InvalidTemplateVariable('%s')
DEBUG = True 时的结果:
在页面上
在控制台中
> System check identified 1 issue (0 silenced). October 03, 2016 -
> 12:21:40 Django version 1.10.1, using settings 'settings.development'
> Starting development server at http://127.0.0.1:8000/ Quit the server
> with CONTROL-C. 2016-10-03 12:21:44,472 - InvalidTemplateVariable -
> WARNING - Attention! A variable "form.media" does not exists.
您可以使用 pytest-django 设置 FAIL_INVALID_TEMPLATE_VARS
如果 pytest 执行代码,则会检查无效的变量。
[pytest]
DJANGO_SETTINGS_MODULE = mysite.settings
FAIL_INVALID_TEMPLATE_VARS = True
如果模板中有未定义的变量,django不会告诉你。
您可以在视图中打印这个变量。