繁体   English   中英

如何处理 Jinja2 中的属性访问错误?

[英]How to handle attribute access errors in Jinja2?

我有这个:

template = '{{invoice.customer.address.city}}'

它工作正常。 但有时invoice.customer is Null or invoice.customer.address is Null然后 jinja 抛出jinja2.exceptions.UndefinedError: 'None' has no attribute 'address'因为它无法到达那个.city部分。 那么,如果它无法访问某个属性,我该如何告诉它静默失败呢?

谢谢!

如果您经常这样做,而不是创建每个属性过滤器,您可以将 Vor 的答案概括为适用于任意嵌套字典,如下所示:

import jinja2

def filter_nested_dict(value, default, path):
    keys = path.split('.')
    for key in keys:
        try:
            value = value[key]
        except KeyError:
            return default

    return value


env = jinja2.Environment()
env.filters['nested_dict'] = filter_nested_dict

template = env.from_string('''
  City: {{invoice|nested_dict('<none>', 'customer.address.city')}}''')

鉴于上述,这:

print template.render(invoice={})

给你:

City: <none>

和这个:

print template.render(invoice={'customer': {'address': {'city': 'boston'}}})

给你:

City: boston

我建议您创建一个自定义过滤器并将整个invoice对象传递给它,而不是尝试在 Jinja 中找到解决方法。

例如:

import jinja2 


def get_city_from_invoice(invoice):
  try:
      return invoice['customer']['address']['city']
  except KeyError:
      return None

env = jinja2.Environment()
env.filters['get_city_from_invoice'] = get_city_from_invoice

d = {'invoice': {'customer': {'address': {'city': 'foo'}}}}
d1 = {'invoice': {'no-customers': 1 }}

print "d: ", env.from_string('{{ invoice | get_city_from_invoice }}').render(d)
print "d1: ", env.from_string('{{ invoice | get_city_from_invoice }}').render(d1)

将打印:

d:  foo
d1:  None

好的,我想我明白了。 答案似乎是使用全局变量,就像这里描述的那样

所以我试图以此为基础,结果是这样的:

def jinja_global_eval(c, expr):
    """Evaluates an expression. Param c is data context"""
    try:
        return str(eval(expr))
    except:
        return ''

使用templating_env.globals['eval'] = jinja_global_eval将其安装到我的模板环境中后,我现在可以在我的模板中执行此操作:

{{eval(invoice, 'c.customer.address.city')}}

和这个:

{{eval(invoice, 'c.customer.get_current_balance()')}}

在调试期间它可能会咬我的裤子,但为了避免它,可以在jinja_global_eval安装一个简单的日志记录。 无论如何,感谢所有试图提供帮助的人。

它需要进一步测试,因为它可能会破坏事物,但是如何扩展Environment类并像这样覆盖 gettatr(或 getitem)方法

from jinja2 import Environment

class SEnvironment(Environment):
    ERROR_STRING = 'my_error_string'
    def getattr(self, obj, attribute):
        """Get an item or attribute of an object but prefer the attribute.
                Unlike :meth:`getitem` the attribute *must* be a bytestring.
                """
        try:
            return getattr(obj, attribute)
        except AttributeError:
            pass
        try:
            return obj[attribute]
        except (TypeError, LookupError, AttributeError):
            return SEnvironment.ERROR_STRING # this lines changes

那么如果你想处理错误,你可以创建像raise_errordislay_error这样的过滤器

def raise_error(obj):
    if obj == SEnvironment.ERROR_STRING:
        raise Exception('an error occured')
    return obj
        

def print_error(obj, _str='other error'):
    if obj == SEnvironment.ERROR_STRING:
        return _str
    return obj

jinja_env = SEnvironment()
jinja_env.filters['raise_error'] = raise_error
jinja_env.filters['print_error'] = print_error
jinja_env = jinja_env.from_string("""{{ test1.test2.test3 }}""") # -> my_error_string
#jinja_env = jinja_env.from_string("""{{ test1.test2.test3|print_error('<none>') }}""") # -> <none>
#jinja_env = jinja_env.from_string("""{{ test1.test2.test3|raise_error }}""") # -> Exception: an error occured
res = jinja_env.render({
    'test1': {
        'test2': None
    }
})

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM