【问题标题】:How to render a python dict as an html table with vertical columns如何将python dict呈现为具有垂直列的html表
【发布时间】:2011-10-16 14:08:03
【问题描述】:

我有一个 python dict,其中每个键对应一个标题,与每个标题关联的列表包含任意数量的值:

data = { 
    "heading1": ['h1-val1', 'h1-val2', 'h1-val3', ],
    "heading2": ['h2-val1', ],
    "heading3": ['h3-val1', 'h3-val2', 'h3-val3', 'h3-val4', ],
} 

我需要在 Django 模板中将其呈现为表格,其中的值垂直列在每个标题下方,任何缺失的值都呈现为空表格单元格:

<table>
<thead>
    <tr>
    <th>heading1</th>
    <th>heading2</th>
    <th>heading3</th>
    </tr>
</thead>
<tbody>
    <tr>
    <td>h1-val1</td>
    <td>h2-val1</td>
    <td>h3-val1</td>
    </tr>
    <tr>
    <td>h1-val2</td>
    <td></td>
    <td>h3-val2</td>
    </tr>
    <tr>
    <td>h1-val3</td>
    <td></td>
    <td>h3-val3</td>
    </tr>
    <tr>
    <td></td>
    <td></td>
    <td>h3-val4</td>
    </tr>
</tbody>
</table>

实现这一目标的最佳方法是什么?

我的第一个倾向是将原始 dict 重新排列为 2D 矩阵,然后将其传递到模板中。不过,我确定我不是第一个遇到这种问题的人,我很好奇其他人是如何解决这个问题的。

更新:仅供参考,这是我对这个问题的原始解决方案(我对此不太满意)。

# Using the data dict from the question:
size = max(len(data['heading1']), len(data['heading2']), len(data['heading3']))
matrix = [[None, None, None] for i in range(size)] # initialize an empty matrix

# manually copy the data into the appropriate column :(
i = 0
for item in data['heading1']:
    matrix[i][0] = item
    i += 1
i = 0
for item in data['heading2']:
    matrix[i][1] = item
    i += 1
i = 0
for item in data['heading3']:
    matrix[i][2] = item
    i += 1

然后我将矩阵传递到如下所示的模板中:

<table>
<thead><tr>
    <th>heading1</th>
    <th>heading2</th>
    <th>heading3</th>
</tr></thead>
<tbody>
{% for row in matrix %}
    <tr>
    {% for col in row %}
        <td>{% if col %}{{ col }}{% else %}&nbsp;{% endif %}</td>
    {% endfor %}
    </tr>
{% endfor %}
</tbody>
</table>

【问题讨论】:

    标签: django django-templates


    【解决方案1】:

    试试下面的代码。请注意,由于您对数据使用关联数组,因此无法保证标题出现的顺序。

    print "<table>"
    
    print "<thead><tr>"
    order = []
    for k in data.keys():
        print "<td>" + k + "</td>"
        order.append(k)
    print "</tr></thead>"
    
    print "<tbody>"
    for k in order:
        print "<tr>"
        for v in data[k]:
            print "<td>" + v + "</td>"
        print "</tr>"
    print "</tbody>"
    print "</table>"    
    

    【讨论】:

    • 这不起作用。首先,我需要组织数据,以便可以将其传递给 Django 模板(当然,我可以将其存储在字符串中而不是打印出来,但我希望所有的演示都在模板中完成)。第二点是,这会在其自己的行中打印与每个标题关联的所有数据。我想把这些信息放在一个专栏里。
    【解决方案2】:

    如果我们稍微改变一下游戏规则,实际上很容易扭转局面(只要您的列表没有填写...)

    from django.template import Context, Template
    
    data = {
        "heading1": ['h1-val1', 'h1-val2', 'h1-val3', ],
        "heading2": ['h2-val1', ],
        "heading3": ['h3-val1', 'h3-val2', 'h3-val3', 'h3-val4', ],
    }
    
    # we'll need to split the headings from the data
    # rather than using keys() I'm just hard coding so I can control the order
    headings = ["heading1", "heading2", "heading3"]
    
    columns = [data[heading] for heading in headings]
    
    # get the length of the longest column
    max_len = len(max(columns, key=len))
    
    for col in columns:
        # padding the short columns with None
        col += [None,] * (max_len - len(col))
    
    # Then rotate the structure...
    rows = [[col[i] for col in columns] for i in range(max_len)]
    
    
    dj_template ="""
    <table>
    {# headings #}
        <tr>
        {% for heading in headings %}
            <th>{{ heading }}</th>
        {% endfor %}
        </tr>
    {# data #}
    {% for row in data %}
        <tr>
            {% for val in row %}
            <td>{{ val|default:'' }}</td>
            {% endfor %}
        </tr>
    {% endfor %}
    </table>
    """
    
    # finally, the code I used to render the template:
    tmpl = Template(dj_template)
    tmpl.render(Context(dict(data=rows, headings=headings)))
    

    对我来说,这会产生以下内容(删除空白行):

    <table>
        <tr>
            <th>heading1</th>
            <th>heading2</th>
            <th>heading3</th>
        </tr>
        <tr>
            <td>h1-val1</td>
            <td>h2-val1</td>
            <td>h3-val1</td>
        </tr>
        <tr>
            <td>h1-val2</td>
            <td></td>
            <td>h3-val2</td>
        </tr>
        <tr>
            <td>h1-val3</td>
            <td></td>
            <td>h3-val3</td>
        </tr>
        <tr>
            <td></td>
            <td></td>
            <td>h3-val4</td>
        </tr>
    </table>
    

    【讨论】:

    • 应该在上面提到这一点,但是您需要拆分标题和数据的原因就像 arunkumar 提到的那样:您不能指望dict.items() 以可预测的顺序为您提供密钥.碰巧从数据中拆分标题简化了模板结构。
    • 我已经更新了代码来处理列表的填充。应该消除初始化空矩阵的需要(预先)。
    【解决方案3】:

    Kenneth Reitz 建议您也可以使用 tablib 解决此问题,所以我想我也将其包含在此处:

    import tablib
    
    d = tablib.Dataset()
    d.append_col(['h1-val1', 'h1-val2', 'h1-val3', ''], header="heading1")
    d.append_col(['h2-val1', 'h2-val2', '', ''], header="heading2")  
    d.append_col(['h3-val1', 'h3-val2', 'h3-val3', 'h3-val4', ], header="heading3") 
    d.headers = ['heading1', 'heading2', 'heading3']
    

    这会转储数据集中的所有相关数据,然后您可以使用以下内容在模板中呈现这些数据:

    {{ d.html }}
    

    生成的 html 如下所示:

    <table>
    <thead>
    <tr><th>heading1</th>
    <th>heading2</th>
    <th>heading3</th></tr>
    </thead>
    <tr><td>h1-val1</td>
    <td>h2-val1</td>
    <td>h3-val1</td></tr>
    <tr><td>h1-val2</td>
    <td>h2-val2</td>
    <td>h3-val2</td></tr>
    <tr><td>h1-val3</td>
    <td></td>
    <td>h3-val3</td></tr>
    <tr><td></td>
    <td></td>
    <td>h3-val4</td></tr>
    </table>
    

    【讨论】:

    • 我真的需要对 html 做更多的自定义,这大概是我没有使用这个解决方案的唯一原因。那,Owen's solution below 效果很好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-09
    • 1970-01-01
    • 2022-06-17
    • 1970-01-01
    • 2020-07-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多