我有这个:
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
部分。那么,如果它无法访问属性,我怎么告诉它只是无声地失败?
谢谢!
答案 0 :(得分:2)
如果您经常这样做,而不是创建每个属性 过滤你可以概括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
答案 1 :(得分:2)
好的,我想我明白了。答案似乎是使用全局变量,就像它描述here
所以我试图以此为基础,结果是:
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
中安装一个简单的日志记录。无论如何,感谢所有试图提供帮助的人。
答案 2 :(得分:1)
我建议您创建一个自定义过滤器并将整个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
答案 3 :(得分:0)
它需要进一步测试,因为它可能会破坏一些东西,但是如何扩展
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_error
或 dislay_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
}
})