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

Python 如何处理Jinja2中的属性访问错误?,python,python-3.x,jinja2,Python,Python 3.x,Jinja2,我有这个: template = '{{invoice.customer.address.city}}' 而且效果很好。但有时invoice.customer为Null或invoice.customer.address为Null,然后jinja抛出jinja2.exceptions.undeinderError:“None”没有属性“address”,因为它无法到达.city部分。那么,如果它无法访问属性,我该如何告诉它以静默方式失败呢 谢谢 我建议您创建一个自定义过滤器,并将整个invoic

我有这个:

template = '{{invoice.customer.address.city}}'
而且效果很好。但有时
invoice.customer为Null或invoice.customer.address为Null
,然后jinja抛出
jinja2.exceptions.undeinderError:“None”没有属性“address”
,因为它无法到达
.city
部分。那么,如果它无法访问属性,我该如何告诉它以静默方式失败呢


谢谢

我建议您创建一个自定义过滤器,并将整个
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

如果您经常这样做,而不是创建每个属性 你可以将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')}}''')
给你:

City: <none>
City: boston
给你:

City: <none>
City: boston

好的,我想我明白了。答案似乎在于使用globals,就像上面描述的那样

因此,我试图在此基础上进一步发展,结果是:

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\u global\u eval
中安装一个简单的日志记录。无论如何,感谢所有试图帮助它的人。

它需要进一步的测试,因为它可能会破坏东西,但是扩展它又如何呢
Environment
像这样初始化并重写getATR(或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\u error
dislay\u 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
    }
})

def raise_错误(obj):
如果obj==SEnvironment.ERROR\u字符串:
引发异常(“发生错误”)
返回obj
def打印错误(obj,str='other error'):
如果obj==SEnvironment.ERROR\u字符串:
返回
返回obj
jinja_env=SEnvironment()
jinja_环境过滤器['raise_error']=raise_error
jinja_环境过滤器['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(“”)}}”)#->
#jinja_env=jinja_env.from_string(“{{test1.test2.test3}}}”)异常:发生错误
res=jinja_env.render({
“测试1”:{
“test2”:无
}
})

您打算为每个案例编写一个自定义过滤器吗?一个简单的发票模板需要一打!是的,那好多了,但还是太麻烦了。{invoice{124; nested_dict('''customer.address.city')}}}''是一个瞬间无法使用的模板-该模板将变得不可读。我承认,我看不出此模板与您提出的方案在可读性方面有多大差异,但我很高兴您找到了一个适合您的解决方案。您是对的,我的解决方案的可读性并不比你的好多少,但它还有另一个优点——它处理可调用项。有争论。这是一件好事:)