0

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?

1
  • have the template data resemble the html structure. If you output a html table use a flat list, if you make an hierarchy with headers or lists use the nested form. Commented Nov 30, 2012 at 11:17

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

1 Comment

A check for the correct answer but no points? That's not right, so 1+ from me...6 years later!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.