Alexander Pavlov
Alexander Pavlov

Reputation: 699

Django DB data to HTML table

Ok, this is more or less "present sparse data having two foreign keys in a table, key1 in rows and key2 in columns". Common enough thing to do, I imagine, but with a wee twist.

In Django, I need to make a page with semester score for a group of students. It should look like this:

          Cryptography intro  |    Discrete Math     | ...
         Chkpt1 Chkpt2 Chkpt3 | Chkpt1 Chkpt2 Chkpt3 | ...
Alice      15     26    47    |   10     20     -    | ...
Bob        25     54    73    |   25     54     -    | ...  
Chuck      33     66    99    |   33     60     -    | ...     
...

Students in a group all take the same courses, and the school has defined 3 checkpoints during each semester when all instructors report the scores. Some columns may not be filled yet if the instructor has not yet turned in the scores (Chkpt3 in Discrete Math in the example above).

Model for a Grade references Student, Course, and Checkpoint. Checkpoint references Semester. Students reference Group.

Problem is how to pass data to the template. I get data from the DB with

reportgrades = Grade.objects.filter(student__group__id=group_id, 
                                    checkpoint__semester=semester)

Then I:

  1. go create nested dict gr[studid][courseid][chkptid] in a nested loop, fill it with dashes,
  2. loop over reportgrades to put values into this dict, overwriting dashes where grades exist,

    no_grade = u'—'
    grades = {}
    for student in students:
        grades[student.id] = {}
        for course in courses:
            grades[student.id][course.id] = {}
            for chkpt in checkpoints:
                grades[student.id][course.id][chkpt.id] = no_grade
    
    for g in reportgrades:
        grades[g.student.id][g.course.id][g.checkpoint.id] = g.points
    
  3. create custom template tag to extract these,

    @register.filter
    def getitem ( item, param1 ):
        return item.get(param1)
    
  4. use this inside a 3-nested for in the template

    {% for s in students %}
    <tr>
        <td>{{ s }}</td>
        {% for c in courses %}
            {% for tp in test_periods %}
            <td>{{ grades| getitem:s.id | getitem:c.id | getitem:tp.id }}</td>
            {% endfor %}
        {% endfor %}
    </tr>
    {% endfor %}
    

That's an awful lot to do to get a simple table, is it not? I've considered a linear list of size 3 x NUM_STUDENTS x NUM_COURSES. Not very pretty, either. A better way?

Upvotes: 0

Views: 2461

Answers (1)

acjay
acjay

Reputation: 36641

Keep your template code as simple as possible, as RickyA suggests. Do complicated data transformations in your view instead.

Since you have headers on the tops and sides, I would store these in their own lists. Then I would store the actual data in a list of lists. So for instance, in your view code:

student = Student.objects.all()
context['classes'] = [class.name for class in Class.objects.all()]
context['checkPoints'] = range(1, 4)
context['data'] = ... 

(code to flatten checkpoints for each students in to flat list, 
then make list of these lists, don't forget to add the student 
name as the first item in each row)

And in your template, you do something like

<table>
<thead>
<tr>
<th></th>
{% for class in classes %}
<th colspan="3">{{ class }}</th>
{% endfor %}
</tr>
<!-- something similar for checkpoints -->
</thead>
<tbody>
{% for row in data %}
<tr>
{% for item in row %}
{% if not forloop.counter0 %}
<th>{{ item }}</th>
{% else %}
<td>{{ item }}</td>
{% endif %}
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>

I think that is valid Django template code. I've been using Jinja2 lately, so my apologizes if I threw in a feature that doesn't work, but it should be okay.

Upvotes: 1

Related Questions