Search code examples
djangohtml-table

Django template bracket notation with variable key does not work


I am trying to dynamically render a table based on a table model and it's columns. In the template, I loop over each item in the query set and then over each column from a list of hard coded column names, but Django returns a Could not parse the remainder: '[column]' from 'table[column]' error. table.id renders the correct id value but table[column[0]] returns an error. Does Django not allow bracket notation fro variables?

In this example table is a dict of a model class and columns is a list of the model's column names.

        <tbody>
            {% for table in query_set %}
            <tr>
                {% for column in columns %}
                    <td>{{table[column]}}</td>
                {% endfor %}
            </tr>
            {% endfor %}
        </tbody>

I have also tried using the .items method on the table dict:

        <tbody>
            {% for table in query_set %}
            <tr>
                {% for key, value in table.items %}
                    <td>{{value}}</td>
                {% endfor %}
            </tr>
            {% endfor %}
        </tbody>

but this renders nothing. table.items seems to be undefined.

The corresponding view:

def index(request):
    cur_user = request.user
    Model = get_model(query.table)
    columns = get_table_columns(query.table)

    #
    # code for processing raw SQL query here
    #

    query_set = Model.objects.raw(query_string)

    context = {
        'columns': columns,
        'cur_user': cur_user,
        'query_set': query_set
    }
    return render(request, 'flight_deck/index.html', context)

Solution

  • Django's template language does not support subscripting (i.e. x[y]), this has been done deliberately to prevent people from writing business logic in the templates. One can use Jinja as a template language, but this is not necessary and to some extent counterproductive, precisely to prevent writing business logic in the template.

    It thus makes more sense to "prepare" the data to pass it in a more accessible format to the template, like:

    def index(request):
        Model = get_model(query.table)
        columns = get_table_columns(query.table)
    
        #
        # code for processing raw SQL query here
        #
    
        queryset = Model.objects.raw(query_string)
        data = [[getattr(row, column) for column in columns] for row in queryset]
    
        context = {'columns': columns, 'data': data}
        return render(request, 'flight_deck/index.html', context)

    and render this with:

    {% for row in data %}
    <tr>
        {% for cell in row %}
            <td>{{ cell }}</td>
        {% endfor %}
    </tr>
    {% endfor %}

    Note: There is no need to pass the current user into the template, if you use render(…) [Django-doc] with a set request, the request is accessible in the template, so you can use {{ request.user }}.


    Note: Using raw queries is often not a good idea: one has to be careful about SQL injection but likely more important: it does not allow further post-processing of the queryset like ordering, paginating, etc. at the database level.