【问题标题】:Show undefined variable errors in Django templates?在 Django 模板中显示未定义的变量错误?
【发布时间】:2011-05-17 01:44:22
【问题描述】:

当它在渲染模板时遇到未定义变量错误时,如何让 Django 告诉我?

我已经尝试了明显的DEBUG = TrueTEMPLATE_DEBUG = True,但它们没有帮助。

【问题讨论】:

    标签: django django-templates


    【解决方案1】:

    把它放在你的调试设置中:

    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
    • 警告!使用它会完全破坏 Django 提供的default:'<string>' 模板过滤器。没有变量能够默认为特定字符串,因为这会导致每个未定义的变量在 default 过滤器运行之前转换为 ''
    • @coredumperror 如果没有先引发错误,它会破坏default 标签的行为!
    【解决方案2】:

    根据 django 文档, 默认情况下,未定义的变量被视为''(空字符串)。而在 if for regroup 中,它是 None。 如果您要识别未定义的变量,请更改设置中的 TEMPLATE_STRING_IF_INVALID。 '%s' 使无效变量呈现为它的变量名,这样你就可以很容易地识别。 how-invalid-variables-are-handled

    【讨论】:

    • 另一个技巧是将 TEMPLATE_STRING_IF_INVALID 设置为 "%s %s",这将导致出现格式错误。对于更清洁的东西,请参阅我的答案。
    • 在较新的 Django 版本中,使用string_if_invalid
    【解决方案3】:

    如何在模板中记录未定义变量的警告

    似乎 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 的输出中看到警告。

    【讨论】:

    • @coredumperror 你说的是default_if_none 还是default 模板标签?无论如何,感谢您对此进行调查,这是需要测试的。
    • 我的意思是default 过滤器,它显然只有在模板变量实际上未定义时才会激活。但这会将其从未定义的模板 var 转换为空字符串,因此 default 会跳过它。不过,@Beau 的解决方案不会弄乱default 过滤器。
    • 你是对的。由于某种原因,我的回答确实打破了default 过滤器。但你的解释并不完全正确。 default 过滤器适用于空字符串以及任何其他虚假值,因此它适用于某些已定义的变量。由于其他原因,这个答案打破了default
    • 嗯,你是对的。我误解了default 过滤器的作用。但是为什么这个解决方案会破坏它呢?也许是关于% 的事情?
    • @coredumperror 查看我的编辑。它现在适用于default
    【解决方案4】:

    查找上下文中不存在的模板变量对我来说很重要,因为有好几次错误将其投入生产,因为视图已更改但模板没有更改。

    我使用了在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 的代码,以便您知道何时使用了未定义的变量。
    【解决方案5】:

    我认为这是 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 模式即可。

    【讨论】:

      【解决方案6】:

      阅读模板中的how invalid variable are handled。基本上,只需在 settings.py 中设置 TEMPLATE_STRING_IF_INVALID 即可。

      TEMPLATE_STRING_IF_INVALID = "He's dead Jim! [%s]"
      

      【讨论】:

      • 这行得通,但有些地方这些字符串会消失在噪音中(例如,在 块中)。
      • 您是否在寻找系统抛出异常?如果不是,您可以查看非显示块中项目的来源。
      【解决方案7】:

      我用下一个:

      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.
      

      【讨论】:

        【解决方案8】:

        您可以使用pytest-django settingFAIL_INVALID_TEMPLATE_VARS

        如果 pytest 执行代码,则检查无效变量。

        [pytest]
        DJANGO_SETTINGS_MODULE = mysite.settings
        FAIL_INVALID_TEMPLATE_VARS = True
        

        【讨论】:

          【解决方案9】:

          如果模板中有未定义的变量,django 不会告诉你的。

          您可以在视图中打印此变量。

          【讨论】:

          • 显然这不是真的;例如,请参阅 wilio 的评论。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-10-20
          • 2016-04-01
          • 2014-11-20
          • 1970-01-01
          • 2015-08-12
          相关资源
          最近更新 更多