如何查看生成到 django 模板变量中的异常?

Posted

技术标签:

【中文标题】如何查看生成到 django 模板变量中的异常?【英文标题】:How to see exception generated into django template variable? 【发布时间】:2011-05-17 09:32:23 【问题描述】:

在 Django 模板中,可以像这样调用对象方法:

 my_object.my_method 

问题是当您在“def my_method(self)”中遇到异常/错误时,在渲染模板时它会被隐藏(而是输出一个空字符串,因此不会出现错误)。

由于我想调试“def my_method(self)”中的问题,我想打开全局 django 标志之类的东西来接收此类异常。

在settings.py中,我已经有了

DEBUG = True 
TEMPLATE_DEBUG = True

我可以收到多种模板异常,但在触发对象方法时却没有。

我能做什么?

【问题讨论】:

我遇到了完全相同的问题...我认为这是 django 本身的错误... 见docs.djangoproject.com/en/3.0/ref/templates/api/… 【参考方案1】:

我能做什么?

在您的视图函数中评估异常生成方法。

def someView( request ):
    .... all the normal work ...

    my_object.my_method() # Just here for debugging.

    return render_to_response( ... all the normal stuff... )

您可以在完成调试后删除该行代码。

【讨论】:

这个方法我已经用过了,很无聊:django中真的没有flag来引发变量异常?? @Eric:“无聊”?对不起,工作让你很无聊。也许你应该再找一份工作。严重地。单元测试会比这更好。在模板中发现异常意味着您未能使用适当的单元测试来调试模型和视图功能。您无需在模板中查找异常,因为有更好的 方法更简单、更可靠且更易于调试。很抱歉,这很无聊。【参考方案2】:

类似于 S. Lott 的回答,激活管理 shell(python manage.py shell)并创建适当的 my_object 实例,调用 my_method。或者把异常处理放在 my_method 中并记录异常。

【讨论】:

【参考方案3】:

我会使用Unit tests 来隔离问题。我知道这是一个间接的答案,但我觉得这是解决和防止问题再次发生的理想方法。

【讨论】:

【参考方案4】:

终于找到了解决办法:我开发了一个模板调试标签:

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 %

【讨论】:

【参考方案5】:

这是我刚刚实现的一个很好的技巧来做到这一点。将其放入您的调试设置中:

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"),
        ,
    ,
]

当解析发现未知或无效值时,这将导致引发 TemplateSyntaxError。我已经对此进行了一些测试(使用未定义的变量名)并且效果很好。我没有用函数返回值等进行测试。事情可能会变得复杂。

【讨论】:

聪明!这可能会产生很多误报,但这是一个非常好的方法。 一个聪明的黑客。太糟糕了 Django 没有开箱即用地提供这个功能。不,这不会导致任何“误报”。 如果这可行,那就太好了,但它似乎对我没有任何作用。使用 django 1.8。 (我刚刚为较新的 django 版本更新了这个答案。)【参考方案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。尽管设置了TEMPLATE_STRING_IF_INVALID,但我注意到string_if_invalid 是空的。这将我引向文档的这一部分:

https://docs.djangoproject.com/en/1.8/ref/templates/upgrading/#the-templates-settings

Django 的模板系统在 Django 1.8 中得到了对多个模板引擎的支持。

...

如果您的设置模块定义了ALLOWED_INCLUDE_ROOTSTEMPLATE_STRING_IF_INVALID,请将它们的值包含在“OPTIONS”字典中的“allowed_include_roots”和“string_if_invalid”键下。

所以除了@slacy's 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" %

【讨论】:

见docs.djangoproject.com/en/3.0/ref/templates/api/…

以上是关于如何查看生成到 django 模板变量中的异常?的主要内容,如果未能解决你的问题,请参考以下文章

Django,如何将变量从 Django 管理员传递到模板?

如何更新 django 模板中的渲染变量?

Django - 如何将 javascript 变量从另一个文件加载到模板

如何在模型 TextField 的 HTML 中使用 Django 模板变量?

将pk传递到Django中的另一个模板

Django 模板生成凌乱的空白 HTML