【发布时间】:2011-05-17 01:44:22
【问题描述】:
当它在渲染模板时遇到未定义变量错误时,如何让 Django 告诉我?
我已经尝试了明显的DEBUG = True 和TEMPLATE_DEBUG = True,但它们没有帮助。
【问题讨论】:
当它在渲染模板时遇到未定义变量错误时,如何让 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)
TEMPLATE_STRING_IF_INVALID = InvalidString("%s")
当模板引擎看到或发现未定义的值时,这应该会引发错误。
【讨论】:
{% if foobar %} 或 {% for foo in foobar %} 中未定义的 foobar
default:'<string>' 模板过滤器。没有变量能够默认为特定字符串,因为这会导致每个未定义的变量在 default 过滤器运行之前转换为 ''。
default 标签的行为!
根据 django 文档, 默认情况下,未定义的变量被视为''(空字符串)。而在 if for regroup 中,它是 None。 如果您要识别未定义的变量,请更改设置中的 TEMPLATE_STRING_IF_INVALID。 '%s' 使无效变量呈现为它的变量名,这样你就可以很容易地识别。 how-invalid-variables-are-handled
【讨论】:
string_if_invalid。
似乎 Django 依赖未定义的变量是一个简单的空字符串。因此,与其改变这种行为或让它抛出异常,不如让它保持不变,但让它记录一个警告!
在您的settings.py 文件中:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
# ...
'OPTIONS': {
# ...
'string_if_invalid': InvalidStringShowWarning("%s"),
},
}
]
(string_if_invalid 在较新的 Django 版本中替换 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 的输出中看到警告。
【讨论】:
default_if_none 还是default 模板标签?无论如何,感谢您对此进行调查,这是需要测试的。
default 过滤器,它显然只有在模板变量实际上未定义时才会激活。但这会将其从未定义的模板 var 转换为空字符串,因此 default 会跳过它。不过,@Beau 的解决方案不会弄乱default 过滤器。
default 过滤器。但你的解释并不完全正确。 default 过滤器适用于空字符串以及任何其他虚假值,因此它适用于某些已定义的变量。由于其他原因,这个答案打破了default。
default 过滤器的作用。但是为什么这个解决方案会破坏它呢?也许是关于% 的事情?
default
查找上下文中不存在的模板变量对我来说很重要,因为有好几次错误将其投入生产,因为视图已更改但模板没有更改。
我使用了在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
【讨论】:
skipped_templates = ('rest_framework', 'django'); tpl = context.template.name; if tpl and tpl.startswith(skipped_templates): raise e
django.template.base.Variable.resolve 使用一个函数,当VariableDoesNotExist 被抛出时会做一些额外的事情,然后重新提升它,您可以将自己插入系统而不会影响系统的行为。例如将 allowed_undefined_variables 替换为仅记录 self.var 的代码,以便您知道何时使用了未定义的变量。
我认为这是 Django 的主要疏忽,也是我不喜欢使用其默认模板引擎的主要原因。可悲的事实是,至少目前(Django 1.9),您无法可靠地实现此效果。
您可以让 Django 在遇到 {{ undefined_variable }} 时引发异常 - 通过使用 slacy 的答案中描述的“黑客”。
你不能让 Django 在 {% if undefined_variable %} 或 {% for x in undefined_variable %} 等上引发相同的异常。“黑客”在这种情况下不起作用。
即使在可以的情况下,Django 作者也强烈反对在生产环境中使用这种技术。除非您确定您的应用程序中没有使用 Django 的内置模板,否则您应该仅在DEBUG 模式下使用“the hack”。
但是,如果您现在仍然坚持使用 Django 的模板,我建议您使用 slacy 的答案,只要确保您处于 DEBUG 模式即可。
【讨论】:
阅读模板中的how invalid variable are handled。基本上,只需在 settings.py 中设置 TEMPLATE_STRING_IF_INVALID 即可。
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 settingFAIL_INVALID_TEMPLATE_VARS
如果 pytest 执行代码,则检查无效变量。
[pytest]
DJANGO_SETTINGS_MODULE = mysite.settings
FAIL_INVALID_TEMPLATE_VARS = True
【讨论】:
如果模板中有未定义的变量,django 不会告诉你的。
您可以在视图中打印此变量。
【讨论】: