1

I'm using django and i'm creating a table from the tutorial data. As i was building my .html i easily got a loop to write the data from my choice instance, but i can't get the same thing to work for the column names. I saw here how to get the model fields but i can't get a loop to do write them for me.

table.html

{% extends 'base.html'%}

{% block content%} 
<div class="container">
    <div class="row">
      <p><h3 class="text-primary"> Python Django DataTables </h3></p>
      
<hr style="border-top:1px solid #000; clear:both;" />
<table id = "myTable" class ="table table-bordered">
    <thead class = "alert-warning"> 
        <tr>
<!-- i would like not to have to write those one by one for future projects --> 
            <th> Choice </th> 
            <th> Votes </th>
        </tr>
    </thead> 
    <tbody>
<!-- this is the kind of loop I wanted for the columns--> 
        {% for item in qs %} 
        <tr> 
            <td contenteditable='true'>{{item.choice_text}}</td>
            <td>{{item.votes}}</td>  

        </tr>
        {% endfor %}
    </tbody>
</table>

{% endblock content%}

views.py

class ChoiceTableView(TemplateView):
    model = Question
    template_name = 'polls/table.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["qs"] = Choice.objects.all()
        return context

models.py

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)
    
    def __str__(self):
        return self.choice_text
  • can you add some details on the kind of error or undesired result you are getting? – Carlo Jul 22 '22 at 16:20
  • The django admin sight does this kind of thing on what it calls a "list view". You could look at its source code to see how they do it there. – Code-Apprentice Jul 22 '22 at 18:49

1 Answers1

1

If you want a template that can accomplish a completely automatic printing of header and values, you can do it with a different queryset:

context["qs"] = Choice.objects.all().values( 'the', 'fields', 'you', 'want')

and in the template, each "object" is a dictionary {'the':value_of_the, 'fields':..., ...}. So, object.keys() is the headers and object.values is the matching data.

So, I think this might work (may need debugging)

<table id = "myTable" class ="table table-bordered">
    {% for item in qs %}
      {% if forloop.first %}
         <thead class = "alert-warning"> 
         <tr>
           {% for name in item.keys %} <th>{{name}}</th> {% endfor %}
         </tr>
         </thead>

         <tbody>
      {% endif %}

      <!-- still in the outer loop iterating over qs -->
      <tr> 
        {% for value in item.values %}
           <td {% if forloop.first %}contenteditable='true'{%endif%} >{{value}}</td>
           <!-- the above forloop.first refers to the INNER loop -->
        {% endfor %}
     </tr>
    {% endfor %}
</tbody>
</table>

However, you'll get choice_text as a column header, not choice, so I feel it would be better to pass the headers in the context:

context["qs"] = Choice.objects.all().values( 'choice_text', 'votes')
context["headers"] = ('Choice','Votes')

which would also make a more readable template with no necessary use of forloop.first. Instead, two outer loops, one for the header and one for the body. If you use .values_list instead of .values on the queryset, you will get a list-of-lists instead of a list-of-dicts, which then doesn't need .values in the template, just {% for value in item %}

nigel222
  • 7,582
  • 1
  • 14
  • 22