【问题标题】:Polymorphic macros in JinjaJinja 中的多态宏
【发布时间】:2011-06-21 16:06:37
【问题描述】:

我正在寻找一种方法来创建一个 Jinja 宏,该宏根据传递的对象类型调用不同的实现。基本上,标准的 Python 方法多态性。现在,我正在使用类似于此的丑陋解决方法:

{% macro menuitem(obj) %}
  {% set type = obj.__class__.__name__ %}
  {% if type == "ImageMenuItem" %}
    {{ imagemenuitem(obj) }}
  {% elif type == "FoobarMenuItem" %}
    {{ foobarmenuitem(obj) }}
  {% else %}
    {{ textmenuitem(obj) }}
  {% endif %}
{% endmacro %}

在纯 Python 中,可以随意使用模块环境,例如globals()[x+'menuitem'],虽然不漂亮,但效果很好。我使用 Jinja 上下文尝试过类似的操作,但后者似乎不包含宏定义。

有什么更好的方法可以实现我的目标?

【问题讨论】:

    标签: python jinja2


    【解决方案1】:

    OOP 的本质:多态。

    Create a presentation Layer for your objects:
    
    class MenuPresentation:
        def present(self):
            raise NotImplementedException()
    
    class ImageMenuPresentation(MenuPresentation):
       def present(self):
           return "magic url "
    
    class TextMenuPresentation(MenuPresentation):
       def present(self):
          return "- text value here"
    

    然后将只是一个问题:

    {% macro menuitem(obj) %}
      {{ obj.present() }}
    {% endmacro %}
    

    【讨论】:

    • 多态确实可以解决这个问题。但是,演示文稿必须包含 HTML,我真的很想将其分离到 Jinja 模板中。不过,您的建议提供了我解决问题所需的最后线索——非常感谢!
    【解决方案2】:

    我现在已经解决了我的问题,类似于 fabrizioM 的建议,但有一个显着的区别:由于菜单项表示可以(并且大多数时候确实)包含 HTML,我不想直接在present 方法。所以我最终在 Python 中实现了菜单定义,在 Jinja 中实现了演示,相互递归弥合了差距。

    不同类型的菜单项由不同的子类表示:

    class MenuItem(object):
        def present(self, macromap):
            return macromap[type(self).__name__](self, macromap)
    
    class TextLink(MenuItem):
        def __init__(self, url, text):
            self.url, self.text = url, text
    
    class Section(MenuItem):
        def __init__(self, text, items):
            self.text, self.items = text, items
    
    class ImageLink(MenuItem):
        ...
    

    上面引用的macromap 是将菜单项的类型映射到实现其表示的宏的字典。这一切都在 Jinja 中定义:

    {% macro TextLink(l, macromap) %}
      <a class="menuitem" href="{{l.url|escape}}">
        {{ l.text|escape }}
      </a>
    {% endmacro %}
    
    {% macro Section(s, macromap) %}
      <div class="heading">{{s.text}}</div>
      <ul class="items">
        {% for item in s.items %}
          <li>{{ item.present(macromap) }}</li>
        {% endfor %}
      </ul>
    {% endmacro %}
    
    {% set default_map = {'TextLink': TextLink, 'Section': Section, ...}
    

    实际的菜单定义清晰地表达为MenuItem 子类的树:

    main_menu = section("Main Menu", [
        section("Product Line 1", [
            TextLink("/products/...", "A product"),
            ...
        ]),
        section(...),
    ])
    

    要开始演示,模板必须调用顶级部分的present 方法,传递一个宏映射来指定如何显示菜单,例如main_menu.present(default_map)。正如在Section 宏中最好看到的那样,菜单项可以让他们的孩子展示自己,他们的present 方法将调用另一个 Jinja 宏,以此类推,递归。

    必须显式地传递宏映射不是很漂亮,但它带来了一个宝贵的好处:现在可以轻松地呈现菜单数据的不同表示,而无需触及菜单定义。例如,可以定义宏地图来呈现主网站菜单,或用于移动设备的变体(以防 CSS 不够用),或 XML 站点地图,甚至是纯文本版本。 (我们实际上最终将这个系统用于网站菜单和站点地图案例。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-19
      • 2023-01-29
      • 1970-01-01
      相关资源
      最近更新 更多