【问题标题】:Creating and populating a dictionary in jinja2在 jinja2 中创建和填充字典
【发布时间】:2017-12-12 09:55:26
【问题描述】:
import jinja2 
from jinja2 import Template

records = [{'a':1,'b':1, 'c':1},{'a':1,'b':1, 'c':1}, {'a':2,'b':1, 'c':1}, {'a':3,'b':1, 'c':1}]    

t = jinja2.Template("""
{% set record_info = dict() %}
{% for item in records %}
{% set key =  str(item['a'])+str(item['b'])+str(item['c']) %}
{% if key in record_info %}
{% set record_info.key += 1 %}
{% else %}
{% set record_info.key = 1 %}
{% endif %}
{% endfor %}
{{record_info}}""")    

这给了我:

Traceback (most recent call last):
  File "<stdin>", line 11, in <module>
  File "/Library/Python/2.7/site-packages/jinja2/environment.py", line 945, in __new__
    return env.from_string(source, template_class=cls)
  File "/Library/Python/2.7/site-packages/jinja2/environment.py", line 880, in from_string
    return cls.from_code(self, self.compile(source), globals, None)
  File "/Library/Python/2.7/site-packages/jinja2/environment.py", line 591, in compile
    self.handle_exception(exc_info, source_hint=source_hint)
  File "/Library/Python/2.7/site-packages/jinja2/environment.py", line 780, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "<unknown>", line 6, in template
jinja2.exceptions.TemplateSyntaxError: expected token 'end of statement block', got '.'

我做错了什么?

【问题讨论】:

  • 看起来您在模板中添加了很多逻辑,这些逻辑可以更容易地在代码中执行。考虑在代码中准备好数据结构,然后使用模板来渲染它
  • @danielcorin 你是对的。但在这种情况下,我无法将预先准备好的数据结构传递给模板。
  • 为什么不呢?实际目标是什么?

标签: python python-2.7 jinja2 templating


【解决方案1】:

我可以这样做,有什么改进吗?

records = [{'a':1,'b':1, 'c':1},{'a':1,'b':1, 'c':1}, {'a':2,'b':1, 'c':1}, {'a':3,'b':1, 'c':1}]    
t = jinja2.Template("""
{% set record_info = dict() %}
{% for item in records %}
{% set key =  item['a'] ~ ':' ~ item['b'] ~ ':' ~ item['c'] %}
{% if key in record_info %}
{% set _dummy = record_info.update( {key: record_info[key]+1 })  %}
{% else %}
{% set _dummy = record_info.update({ key:1 }) %}
{% endif %}
{% endfor %}
{{record_info}}""")    
print t.render(records=records)

{u'1:1:1': 2, u'3:1:1': 1, u'2:1:1': 1}

【讨论】:

    【解决方案2】:

    您可以使用“设置”。

    {% set x = my_dict.__setitem__("key", "value") %}
    

    然后您可以使用 my_dict,它将具有更新的值(不要介意 x,它就在那里,这样它就不会抛出错误)。这已经在另一个答案中,在here

    【讨论】:

    • 当我在 Jinja2(DBT 下方)尝试这个时,我得到:access to attribute '__setitem__' of 'dict' object is unsafe.
    【解决方案3】:

    快速解决方案

    现有的答案工作得很好,但既然你要求任何改进,我会尝试。

    from jinja2 import Template
    
    records = [{'a':1,'b':1, 'c':1},{'a':1,'b':1, 'c':1}, {'a':2,'b':1, 'c':1}, {'a':3,'b':1, 'c':1}]
    
    t = Template("""
    {%- set record_info = dict() %}
    {%- for item in records %}
      {%- set key = item['a'] ~ ':' ~ item['b'] ~ ':' ~ item['c'] %}
      {%- do record_info.update({key: 1 if not record_info[key] else record_info[key]+1}) %}
    {%- endfor -%}
    {{record_info}}""",
    extensions=['jinja2.ext.do']
    )
    
    print(t.render(records=records))
    
    

    改进

    • 显示导入 - 一些来到这里的 SO 用户可能是 Python 新手,需要完整的上下文
    • 使用 Python3 和 print() 函数(不是批评 - 我意识到这是在 2017 年,虽然 from __future__ 也可以工作)
    • 允许使用do“表达式语句”扩展
    • 使用do 而不是设置_dummy 变量
    • 使用{%--%}- 添加到这些开始/结束标签中,避免所有额外呈现的换行符/空格

    其他变化

    这些是否是改进是主观的 - 可读性得到改善还是受到阻碍?

    • 使用内联 if(三元)来删除 else 子句

    注意事项

    使用默认值

    这一行:

    {%- do record_info.update({key: 1 if not record_info[key] else record_info[key]+1}) %}
    

    也可以使用default

    {%- do record_info.update({key: (record_info[key] | default(0)) + 1 }) %}
    

    请注意,对现有record_info[key] 的检查会使用默认值,即使在密钥尚不存在时它是错误的。

    例如,这不起作用:

    {%- do record_info.update({key: (record_info[key]+1 | default(1)) }) %}
    

    ...因为当它尝试评估 record_info[key]+1 时,record_info[key] 错误未被处理。

    环境和加载器

    文档建议使用Loader,例如PackageLoader 支持模板继承。在您的示例中,它看起来像一个快速设置,因此我将忽略该要求。请注意,直接使用 Template 构造函数无论如何都会创建一个 shared 环境,而没有加载器或继承的好处。

    如果您使用PackageLoader,您将拥有一个包名,并使用一个名为templates 的文件夹来加载它们。将模板放在单独的文件中也很好。

    要在没有这些单独文件的情况下快速使用真正的加载器,FunctionLoader 很有用。您可以向函数添加更多模板字符串并按名称返回它们,例如

    from jinja2 import Environment, FunctionLoader
    
    records = [{'a':1,'b':1, 'c':1},{'a':1,'b':1, 'c':1}, {'a':2,'b':1, 'c':1}, {'a':3,'b':1, 'c':1}]
    
    def my_template(name: str) -> str:
        if name == 'first_one':
            return """
    {%- set record_info = dict() %}
    {%- for item in records %}
    {%- set key = item['a'] ~ ':' ~ item['b'] ~ ':' ~ item['c'] %}
    {%- do record_info.update({key: (record_info[key] | default(0)) + 1}) %}
    {%- endfor -%}
    {{record_info}}"""
        else:
            return """
    """
    
    jinja_env = Environment(
        loader=FunctionLoader(my_template),
        extensions=['jinja2.ext.do']
    )
    
    t = jinja_env.get_template('first_one')
    
    print(t.render(records=records))
    

    我知道这只是为实验设置了一个快速而肮脏的示例,但我认为这是一个更好的 样板 起点,因为它明确使用了 jinja EnvironmentLoaders 所以可以更容易扩展,当我在几年后不可避免地回到自己的答案时,当我忘记这一切时,我会很感激:D

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-01-23
      • 2022-11-25
      • 2023-02-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-10
      • 1970-01-01
      相关资源
      最近更新 更多