如何在Django模板中打开无效字典查找的异常?

o0lyfsai  于 2023-11-20  发布在  Go
关注(0)|答案(2)|浏览(123)

当Django模板系统遇到一个无效的字典查找时,它会被一个空字符串默默地替换。
不过,对于特定的字典查找,我希望Django能引发一个异常来帮助调试。
假设一个字典是这样的:

colors = {"red": 1, "green 2, "blue": 3}

字符串
这里应该提出一个例外:

{{ colors.orange }}


当Django模板系统遇到无效的字典查找时,我如何强制它引发异常?

qq24tv8q

qq24tv8q1#

Django模板引擎并不是真的这样做的。但我们可以“破解”它。事实上,我们可以定义自己的InvalidVarException,例如记录,或引发异常:

import inspect
import logging
from itertools import islice

from django.template.base import FilterExpression

UNKNOWN = object()
MISSING = object()

def all_in_stack(type_filter, var_name='self'):
    for item in islice(inspect.stack(), 1):
        obj = item.frame.f_locals.get(var_name, MISSING)
        if obj is not MISSING and isinstance(obj, type_filter):
            yield obj

def look_in_stack(type_filter, var_name='self'):
    for item in all_in_stack(type_filter=type_filter, var_name=var_name):
        return item
    return MISSING

def or_unknown(
    f, *args, _unknown=UNKNOWN, _exception_type_filter=BaseException, **kwargs
):
    try:
        result = f(*args, **kwargs)
        if result is MISSING:
            return _unknown
        return result
    except _exception_type_filter:
        return _unknown

def get_request_from_stack(type_filter: HttpRequest, var_name='request'):
    return look_in_stack(type_filter=type_filter, var_name=var_name)

def get_template_node_from_stack(var_name='self'):
    return look_in_stack(Node, var_name=var_name)

def get_template_location_from_stack(var_name='self'):
    return get_node_file_loc(get_template_node_from_stack(var_name=var_name))

logger = logging.getLogger('templates')

class InvalidVarException(str):
    already = set()

    def __mod__(self, missing, *args, **kwargs):
        missing_str = or_unknown(str, missing)
        request = or_unknown(get_request_from_stack)
        path = request.path
        full_path = request.get_full_path()
        loc = or_unknown(get_template_location_from_stack)
        key = (path, loc, missing_str)
        show = key not in self.already
        if show:
            self.already.add(key)
            try:
                message = f'Missing variable {{{{ {missing_str} }}}} at {loc} for path {full_path}'
                logger.error(message)
            except:
                pass
        return self

    def __bool__(self):
        return True

    def __contains__(self, search):
        return '%s' in search or super().__contains__(search)

def is_simple_constant(expr):
    if not expr.is_var and (not expr.filters):
        return expr.var

字符串
然后我们可以将InvalidVarException注册为string_if_invalid选项:

# settings.py

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        # …
        'OPTIONS': {
            # …
            'string_if_invalid': InvalidVarException(),
            # …
        },
    },
]


我们的想法是,Django通常会在找不到变量的情况下渲染string_if_invalid,从而使引擎静默。但在这里,我们使用了一个技巧来格式化字符串,并使用它来查找模板和变量,然后记录结果。

gblwokeq

gblwokeq2#

你不能直接从django的模板系统中引发异常。它被设计成静默失败。但你可以做的是定义一个自定义的模板标签来验证字典,如果缺少键(无效查找),它会引发异常。

相关问题