Python Jinja中的多态宏
我正在寻找一种方法,使Jinja宏能够根据所传递对象的类型调用不同的实现。基本上,标准Python方法是多态的。现在,我正在使用一个类似于以下内容的丑陋解决方案:Python Jinja中的多态宏,python,jinja2,Python,Jinja2,我正在寻找一种方法,使Jinja宏能够根据所传递对象的类型调用不同的实现。基本上,标准Python方法是多态的。现在,我正在使用一个类似于以下内容的丑陋解决方案: {% macro menuitem(obj) %} {% set type = obj.__class__.__name__ %} {% if type == "ImageMenuItem" %} {{ imagemenuitem(obj) }} {% elif type == "FoobarMenuItem" %
{% 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上下文进行类似的操作,但后者似乎不包含宏定义
有什么更好的方法可以实现我所追求的目标?面向对象的本质:多态性
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 %}
我现在已经解决了我的问题,与fabrizioM的建议类似,但有一个显著的区别:由于菜单项表示可以(而且大多数情况下确实)包含HTML,我不希望直接在
present
方法中乱用HTML标记。因此,我最终用Python实现了菜单定义,用Jinja实现了菜单定义,相互递归弥补了这一差距
不同类型的菜单项由不同的子类表示:
上面引用的macromap
是将菜单项类型映射到实现其表示的宏的dict。所有这些都在Jinja中定义:
要开始演示,模板必须调用顶层部分的present
方法,传递宏映射以指定如何显示菜单,例如main\u menu.present(默认映射)
。正如在部分
宏中可以看到的那样,菜单项可以要求其子菜单项显示自己,其present
方法将递归调用另一个Jinja宏,以此类推
必须显式地传递宏映射不是很漂亮,但它提供了一个有价值的好处:现在可以轻松地呈现菜单数据的不同表示形式,而无需触及菜单定义。例如,可以定义宏地图来呈现网站主菜单,或移动设备的变体(如果CSS不够),或XML站点地图,甚至是纯文本版本。(事实上,我们在网站菜单和站点地图案例中使用了这个系统。)多态性确实适用于这个问题。但是,演示文稿必须包含HTML,我真的希望将其分离为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):
...
{% 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, ...}
main_menu = section("Main Menu", [
section("Product Line 1", [
TextLink("/products/...", "A product"),
...
]),
section(...),
])