我正在使用Flask/Python应用程序,并且在jinja模板中呈现namedtuple类型数据时面临问题。
我用的是版本
下面是我的工作示例代码:
from collections import namedtuple
from flask import Flask, render_template, render_template_string
# ....
app = Flask(__name__)
Category = namedtuple(
'Category',
['name', 'priority', 'menus', 'has_next_level']
)
MenuItem = namedtuple(
'MenuItem',
['title', 'url', 'target', 'id', 'parent_id', 'object_type']
)
# --- Sample input value render_template()
menu_content = {
50: {
'AGENTS': Category(
name='AGENTS',
priority=1,
menus=[
MenuItem(
title='Agent2',
url='/monitoring/dashboard/6/agent/2',
target=None,
id=2,
parent_id=None,
object_type='agent'
),
MenuItem(
title='Postgres Enterprise Manager Host',
url='/monitoring/dashboard/6/agent/1',
target=None,
id=1,
parent_id=None,
object_type='agent'
)],
has_next_level=True
)
}
}
output = None
with app.app_context():
output = render_template_string(
'{{ menu_content|tojson|safe }}',
menu_content=menu_content
)
print(output)输出,我得到:
{"50": {"AGENTS": ["AGENTS", 1, [["Agent2", "/monitoring/dashboard/6/agent/2", null, 2, null, "agent"], ["Postgres Enterprise Manager Host", "/monitoring/dashboard/6/agent/1", null, 1, null, "agent"]], true]}
预期产出:
{"50": {"AGENTS": {"has_next_level": true, "menus": [{"id": 2, "object_type": "agent", "parent_id": null, "target": null, "title": "Agent2", "url": "/monitoring/dashboard/6/agent/2"}, {"id": 1, "object_type": "agent", "parent_id": null, "target": null, "title": "Postgres Enterprise Manager Host", "url": "/monitoring/dashboard/6/agent/1"}]}
我在这里有遗漏什么吗?
发布于 2021-05-22 16:58:24
谢谢在你的问题中添加细节,这样我就可以复制你的问题了。
problem
的
解决方案:
from collections import namedtuple
from flask import Flask, render_template, render_template_string
import json
from collections import OrderedDict
# Function that will be used to convert the namedtuple to a dict
def namedtuple_asdict(obj):
if hasattr(obj, "_asdict"): # detect namedtuple
return OrderedDict(zip(obj._fields, (namedtuple_asdict(item) for item in obj)))
elif isinstance(obj, str): # iterables - strings
return obj
elif hasattr(obj, "keys"): # iterables - mapping
return OrderedDict(zip(obj.keys(), (namedtuple_asdict(item) for item in obj.values())))
elif hasattr(obj, "__iter__"): # iterables - sequence
return type(obj)((namedtuple_asdict(item) for item in obj))
else: # non-iterable cannot contain namedtuples
return obj
# ....
app = Flask(__name__)
Category = namedtuple(
'Category',
['name', 'priority', 'menus', 'has_next_level']
)
MenuItem = namedtuple(
'MenuItem',
['title', 'url', 'target', 'id', 'parent_id', 'object_type']
)
# --- Sample input value render_template()
menu_content = {
50: {
'AGENTS': Category(
name='AGENTS',
priority=1,
menus=[
MenuItem(
title='Agent2',
url='/monitoring/dashboard/6/agent/2',
target=None,
id=2,
parent_id=None,
object_type='agent'
),
MenuItem(
title='Postgres Enterprise Manager Host',
url='/monitoring/dashboard/6/agent/1',
target=None,
id=1,
parent_id=None,
object_type='agent'
)],
has_next_level=True
)
}
}
# Convert the dict of dict of namedtuple etc. to JSON string
menu_content_JSONified = json.dumps(namedtuple_asdict(menu_content))
output = None
with app.app_context():
output = render_template_string(
'{{ menu_content|safe }}',
menu_content=menu_content_JSONified
)
print(output)输出:
{"50":
{"AGENTS":
{"name": "AGENTS",
"priority": 1,
"menus": [
{"title": "Agent2", "url": "/monitoring/dashboard/6/agent/2", "target": null, "id": 2, "parent_id": null, "object_type": "agent"},
{"title": "Postgres Enterprise Manager Host", "url": "/monitoring/dashboard/6/agent/1", "target": null, "id": 1, "parent_id": null, "object_type": "agent"}],
"has_next_level": true
}
}
}通过调用json.dumps,您将得到一个JSON字符串。如果您只使用namedtuple_asdict,您将得到一个OrderedDict。
解释:
主要问题是,您想要JSONify的对象默认不受json.JSONEncoder (cf )的支持。这里的转换表:https://docs.python.org/3.6/library/json.html#py-to-json-table)。
首先,对象menu_content是dict of dict of namedtuple。
其次,这个namedtuple包含一个str、一个int、一个list和一个bool。
第三,list包含2个namedtuple。
因此,我们必须找到一种方法来说明如何正确地将这种类型的结构转换为JSON。
我们可以扩展json.JSONEncoder (就像文档中的ComplexEncoder示例(https://docs.python.org/3.6/library/json.html) ),或者创建一个可以处理这个问题的函数。
在这里,我们使用一个函数来检测每个对象的类型并进行转换,以便得到正确的结果。
这个函数的功劳是@MisterMiyagi,他在这里发布了:Serializing a nested namedtuple into JSON with Python >= 2.7。我刚刚对Python3做了一些修改。
您注意到您的namedtuple被转换为list:这是因为json.JSONEncoder使用的转换表(参见上面的链接)告诉您如何将json.JSONEncoder的list和tuple转换为JSON数组。
namedtuple是tuple的子类,因此您的namedtuple被转换为JSON数组。
示例:
menu_item = MenuItem(
title='Agent2',
url='url2',
target=None,
id=2,
parent_id=None,
object_type='agent'
)
menu_item_json_dumps = json.dumps(menu_item)
print(menu_item_json_dumps)
# OUTPUT: ["Agent2", "url2", null, 2, null, "agent"]
menu_item_as_dict_json_dumps = json.dumps(menu_item._asdict())
print(menu_item_as_dict_json_dumps)
# OUTPUT: {"title": "Agent2", "url": "url2", "target": null, "id": 2, "parent_id": null, "object_type": "agent"}您可以在@martineau中找到更多有关这方面的信息,请在这里回答:Why doesn't JSONEncoder work for namedtuples?
在需要时,调用上面的函数将调用_asdict()。您可以调整它,以便例如跳过一些无值或其他。
https://stackoverflow.com/questions/67637325
复制相似问题