What's the correct way to print a matrix with labels in a Django template?

[亡魂溺海] 提交于 2020-01-05 04:35:10

问题


I want to do something very simple in Django: Print a matrix as an HTML table, and add labels for the rows and columns. These are the variables in my view:

matrix = np.array([
    [101, 102, 103],
    [201, 202, 203],
])
colnames = ['Foo', 'Bar', 'Barf']
rownames = ['Spam', 'Eggs']

I want a to get a table that looks like this:

      Foo  Bar  Barf
Spam  101  102  103
Eggs  201  202  203

My template code looks like this:

<table>
    <tr>
        <th></th>
        {% for colname in colnames %}
            <th>{{ colname }}</th>
        {% endfor %}
    </tr>
    {% for rowname in rownames %}{% with forloop.counter0 as rowindex %}
        <tr>
            <th>{{ rowname }}</th>
            {% for colname in colnames %}{% with forloop.counter0 as colindex %}
                <td>TABLECELL</td>
            {% endwith %}{% endfor %}
        </tr>
    {% endwith %}{% endfor %}
</table>

Output for different values of TABLECELL:

{{ rowindex }}, {{ colindex }} --> table with the indices    :)

{{ matrix.0.0 }} --> table full of 101s    :)

{{ matrix.rowindex.colindex }} --> table with empty cells    :(

As the first two things work, it doesn't seem crazy to assume that the last one would give the intended result. My only explanation is that rowindex and colindex might be strings — and of course int() is among the many things that are forbidden in Django templates.

Does anyone know how I can make this work? Or ideally: Does anyone know how this is intended to be done in Django?

EDIT 1:

It seems I have to pass the enumerated lists to the template. I provide them as enum_colnames and enum_rownames, but now I can't even execute the nested for loop:

<table>
    <tr>
        <th></th>
        {% for unused_colindex, colname in enum_colnames %}
            <th>{{ colname }}</th>
        {% endfor %}
    </tr>
    {% for rowindex, rowname in enum_rownames %}
        <tr>
            <th>{{ rowname }}</th>
            {% for doesnt_work_anyway in enum_colnames %}
                <td>You don't see me.</td>
            {% endfor %}
        </tr>
    {% endfor %}
</table>

This gives a table with all the <th>s filled with the correct labels, but no <td>s at all.

EDIT 2:

I found an insanely ugly "solution", which I publish here as an example of something that "works", but is clearly not an answer to my question — how this should be done in Django. Here it comes:

derp = ['', 'Foo', 'Bar', 'Barf', 'Spam', 101, 102, 103, 'Eggs', 201, 202, 203]
iderp = enumerate(derp)
<table>
    {% for i, d in iderp %}
        {% if i < 4 %}  <!-- if top row: th -->
            {% cycle '<tr><th>' '<th>' '<th>' '<th>' %}
        {% else %}  <!-- else: td -->
            {% cycle '<tr><th>' '<td>' '<td>' '<td>' %}
        {% endif %}
            {{ d }}
        {% if i < 4 %}  <!-- if top row: th -->
            {% cycle '</th>' '</th>' '</th>' '</th></tr>' %}
        {% else %}  <!-- else: td -->
            {% cycle '</th>' '</th>' '</td>' '</td></tr>' %}
        {% endif %}
    {% endfor %}
</table>

Note how it can only be used for tables of this particular width. So in this form it is not even a real solution for the initial problem.


回答1:


Thanks to Paulo Almeida for providing the two essential hints in his answer:

  • The table — including labels — should be built in the view.

  • forloop.first can be used to put the labels in <th>s.

table = [
    ['', 'Foo', 'Bar', 'Barf'],
    ['Spam', 101, 102, 103],
    ['Eggs', 201, 202, 203],
]
<table>
    {% for row in table %}
        <tr>
        {% for cell in row %}
            {% if forloop.first or forloop.parentloop.first %} <th> {% else %} <td> {% endif %}
                {{ cell }}
            {% if forloop.first or forloop.parentloop.first %} </th> {% else %} </td> {% endif %}
        {% endfor %}
        </tr>
    {% endfor %}
</table>

I guess the answer to my implicit question — how the values of multidimensional arrays can be accessed from the template — is that this is not possible.




回答2:


Your problem seems to be a good use case for a feature request suggesting the use of iterables in the cycle tag. Then you could just {% cycle rownames %} in the first <td>.

Unfortunately, that's not possible, as far as I can see. I would take that logic away from the template and into the view (or, more likely, an utility function or model method, depending on the specifics), building a list that is easy to process in the template:

table = [
    ['Spam', 101, 102, 103],
    ['Eggs', 201, 202, 203],
]

The function to build the table might look like this:

def build_table(rownames, matrix):
    table = []
    for rowname, values in zip(rownames, matrix):
        row = [rowname]
        row.extend(values.tolist())
        table.append(row)
    return table


来源:https://stackoverflow.com/questions/28457774/whats-the-correct-way-to-print-a-matrix-with-labels-in-a-django-template

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!