首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Namedtuple没有在烧瓶中正确地呈现

Namedtuple没有在烧瓶中正确地呈现
EN

Stack Overflow用户
提问于 2021-05-21 12:57:42
回答 1查看 146关注 0票数 1

我正在使用Flask/Python应用程序,并且在jinja模板中呈现namedtuple类型数据时面临问题。

我用的是版本

  • Python3.6
  • 烧瓶1.1.2
  • Jinja2 3.0.1

下面是我的工作示例代码:

代码语言:javascript
复制
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"}]}

我在这里有遗漏什么吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-05-22 16:58:24

谢谢在你的问题中添加细节,这样我就可以复制你的问题了。

problem

  • Explanation

  1. 解决方案

解决方案:

代码语言:javascript
复制
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)

输出:

代码语言:javascript
复制
{"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_contentdict 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.JSONEncoderlisttuple转换为JSON数组。

namedtupletuple的子类,因此您的namedtuple被转换为JSON数组。

示例:

代码语言:javascript
复制
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()。您可以调整它,以便例如跳过一些无值或其他。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67637325

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档