Iterating through values in a template

I am trying to make a generic template that can be used over several pages. Its basic structure is several topics, each with its own set of questions. I’ve setup. table with radio buttons n the layout works fine.

The issue I have is getting the questions for each topic into the table. I can hardcode each set of questions, pass them via context but that means repeating the same HTML for each topic and question set. I’d like to loop through so I have an outer loop for the topic and an inner loop that populates each row with the associated questions and a radio button.

Relevant code snippets

views.py:

def leadership(request):
    column_header=Column_Header.objects.all()
    topic_list=Topic.objects.all()
    topic = Topic.objects.get(id=1)
    questions1 = topic.question_set.all()
    topic = Topic.objects.get(id=2)
    questions2 = topic.question_set.all()
    topic = Topic.objects.get(id=3)
    questions3 = topic.question_set.all()


    question_list = {}
    for n in range(1,len(topic_list)+1):
         topic = Topic.objects.get(id=n)
         questions = {}
         questions["question"] = [list(topic.question_set.values_list("question"))]
         question_list[ topic ] = (questions)

template.html:

            <form action="" method="POST">
            {% for topic in topic %}
                <td colspan="7">{{ topic }}</td>
                    <tr>{% for question in question_list %}</tr>

                          <td class="question">{{ question }}</td>
                              {% for n in nchoices %}
                                  {% if n == 0 %} 
                                  <td>
                                  
                                    <input name= {{question.id}} type="radio" value={{ n }}  id="{{name}}" /><lable> Not Sure</lable>
                              
                                  </td>
                                  {% else %}
                                  <td>
                                  
                                  <input name={{question.id}} type="radio" value={{ n }}  id="{{name}}" /><lable> {{n}}</lable>
                                  </td>
                                  {% endif %}
                              {% endfor %}
                          </tr>
                          {% endfor %}

            {% endfor %}

If I replace

<tr>{% for question in question_list %}</tr>

with

<tr>{% for question in questions1 %}</tr>

The questions and radio buttons are laid out but that means creating separate HTML for each topic, and given the number of topics on each separate page can vary means multiple templates rather than one generic one.

Here is the question_list:

{<Topic: Risk Assessment and Planning>: {'question': [[('Conducts annual risk assessments and identifies key risks and mitigation action',), ('Identifies key risks and mitigation actions',), ('Uses risk assessment to set business continuity policy and objectives compatible with the organization’s strategic direction',)]]}, 

<Topic: Oversight>: {'question': [[('Provides the necessary resources for accomplishing business continuity objectives',), ('Integrates business continuity plans into business processes',), ('Communicates the importance of effective business continuity and adhering to requirements',), ('Provides direction and support to staff to drive business continuity effectiveness',)]]}, 

<Topic: Execution and Improvement>: {'question': [[('Conduct annual test of business continuity plans',), ('Ensures intended business continuity outcomes are achieved',), ('Promotes continual improvement by incorporating lessens learned from periodic testing or event reviews',)]]}}

To see if I understand the situation: You have a dict. The keys for the dict are the topics, and the values are the questions and options for that topic.

This means you’re likely going to want to iterate over that dict, giving you both the keys and the values.

As shown in the docs for the for tag, this would be done as {% for topic, questions in question_list %} (??? - you don’t show what you’re putting in the context, so I’m guessing this based on what you have posted)

You can then iterate over questions inside that loop for your individual questions.

Side note: I’m not understanding your structures at all - your queries make this more awkward than it needs to be. If you are only going to need those three questions, you can more easily build your data structure as:
topics = Topic.objects.filter(id__in=[1,2,3]).order_by(id)

If you actually want all topics, remove the filter clause and replace it with all().

As a performance-enhancement, you could add the prefetch_related('question_set') clause to the query. (It’s not necessary for you to do so, but it will reduce the number of queries being issued.)

This avoids all the excess data manipulation that you’re doing in the view. (Your current view is going to execute somewhere in the neighborhood of 9 - 12 queries when you really only need 2.)

In the ideal world, you’d actually create a form for this such that you don’t need to manually render these options. If all of those questions yield the same type of set of answers ( 1 - n or “Not Sure”), then you could build the form by adding these as fields using the RadioSelect widget and the defined set of choices. This reduces your template to (potentially) something as simple as {{ form }}.

Sorry for the confusion. I’ve tried so many variations I basically got lost in the code and haven’t pulled unused code.

The top block of code was where I created separate question sets that then had HTML code for each set.

The for loop was to create a dictionary I could iterate through to get the questions by topic.

Here’s my layout, using just questions2 to show how it should layout:

I had also tried the for loop.

<tr>{% for topic, questions in questions_list %}</tr>
<td colspan="7">{{ topic }}</td>
                <tr>{% for question in questions_list%}</tr>
                          <td class="question">{{ question }}</td>

but all I get is the column headers, no topics or questions.

when I used

            {% for topic in topic %}
                <td colspan="7">{{ topic }}</td>
                     <tr>{% for question in questions_list%}</tr>
                          <td class="question">{{ question }}</td>

I’d get topics but no questions

Thanks for the prefech tip.

Try simplifying the queries first, before building the templates to render them. It’s a lot easier to work forward from your queryset than it is to work backward from the template.

Create your query, use the Django shell to examine what data structures it natively produces for you, then work on your template from that.

Side note question: Are all questions always going to have “Not Sure” and 1 - 5 as the options?

Ok ill do that.

Yes, they all us the same radio button style.

I’ve looked at docs for the RadioSelect widget but am not sure how to use it.

In your case, based upon the sample shown, you would probably want to create a custom template for it, so that you render the question and the options as a table row.
… which thinking about it makes me think that you may be better off not trying to do this all in one template.

In looking at the representation you’ve presented, I could see creating a separate template for the rows containing the question and options, and using the {% include %} tag to render each row as you’re iterating through the data. (Again, this wouldn’t be necessarily required, but may help to keep your templates more understandable.)

Thanks. I’ve been learning about forms. If you can suggest any sites to look at I’d appreciate it as the tutorial/docs are bit confusing.

I also tried to put a variable in the loop with the Values of question1, question2 and question3 to no avail:

<tr>{% for question in {{ questions }} }</tr> <---- Loop through ['question1', 'question2', 'question3']

In the mean time, Im trying to get my template to work; with all kinds of different for … in permutations

For some reason It doesn’t read the values in the dictionary neither topic nor question.

Maybe because there is no key for the questions?

Here is the dictionary:

defaultdict(<class 'list'>, {<Topic: Risk Assessment and Planning>: ['Conducts annual risk assessments and identifies key risks and mitigation action', 'Identifies key risks and mitigation actions', 'Uses risk assessment to set business continuity policy and objectives compatible with the organization’s strategic direction'], <Topic: Oversight>: ['Provides the necessary resources for accomplishing business continuity objectives', 'Integrates business continuity plans into business processes', 'Communicates the importance of effective business continuity and adhering to requirements', 'Provides direction and support to staff to drive business continuity effectiveness'], <Topic: Execution and Improvement>: ['Conduct annual test of business continuity plans', 'Ensures intended business continuity outcomes are achieved', 'Promotes continual improvement by incorporating lessens learned from periodic testing or event reviews']})

Maybe because there is no key for the questions? Should the format be:

 {<Topic: Risk Assessment and Planning>: [Question: 'Conducts annual risk assessments and identifies key risks and mitigation action', 'Identifies key risks and mitigation actions', 'Uses risk assessment to set business continuity policy and objectives compatible with the organization’s strategic direction'],

Views.py:

    column_header=Column_Header.objects.all() #headers for table columns
    topic_list=Topic.objects.all()#Get topics taht will be used for list - get those for selected area
    questions = Question.objects.prefetch_related('topic').all()
    questions_list = defaultdict(list)
    for question in questions:
        questions_list[question.topic].append(question.question)

HTML:

            <tr>{% for topic, questions in questions_list.items %}</tr>
                <td colspan="7">{{ topic }}</td>
                <tr>{% for question in questions %}</tr>
                          <td class="question">{{ question }}</td>

No need to use the braces here. Any symbolic token in a tag is assumed to be a variable unless quoted - in which case it’s interpreted as a string.
e.g.:
{% some_tag questions %}
questions here is a reference to the variable
{% some_tag "questions" %}
questions here is the string.

Regarding the view/template - please post the models.

In general:

None of this is needed. The querysets are iterable objects within the templates.

(I would need to see the models to give you a good example of the right usage for this.)

Thanks.

When I tried just using the query set from

questions = Question.objects.prefetch_related('topic').all()

It would put all of teh questions under each topic instead of only he ones for that topic

Here are the models:

class Topic(models.Model):
    topic = models.CharField(max_length=30)
    area = models.ForeignKey(
        Area, on_delete=models.CASCADE, null=True
    )

    def __str__(self):
        return f"{self.topic}"

class Question(models.Model):
    question = models.CharField(max_length=200)
    topic = models.ForeignKey(
        Topic, on_delete=models.CASCADE, null=True
    )  # Django will store this as topic_id on database


    def __str__(self):
        return self.question

class Answer(models.Model):
    value = models.IntegerField(null = True)
    question = models.ForeignKey(
        Question, on_delete=models.CASCADE, null  = True, unique=False
    )

    respondent = models.ForeignKey(
        Respondent, on_delete=models.CASCADE, null  = True

    def __str__(self):
        return f"{self.respondent} answer to {self.question} is {self.value}"

So your nested data structure (Question within Topic) can be created as:
topics = Topic.objects.all().prefetch_related('question_set')

This means you can iterate over each topic, and within each topic you can iterate over each question.
e.g.:

{% for a_topic in topics %}
  {{ a_topic.topic }}
  {% for a_question in a_topic.question_set.all %}
    {{ a_question.question }}
  {% endfor %}
{% endfor %}

Thanks again.

As I understand it:

This loop then generates HTML taking the first tips, puts it in a_topic and then I use the {{a.topic.topic}} to get the value of the variable. For the questions, a_topic_question_set.all has all the questions and {{a_question_question}} creates the variable containing the value as it loops through.

As a side note is a_topic special, or it could also be:

{% for some_name in topics %}
  {{ some_name.topic }}

Thanks again. It works after I fixed a typo in my template.

Except instead of radio buttons from:

 <td class="question">{{ a_question.question }}</td>
                              {% for n in nchoices %}
                                  {% if n == 0 %} 
                                  <td>
                                  
                                    <input name= {{question.id}} type="radio" value={{ n }}  id="{{name}}" /><lable> Not Sure</lable>
                              
                                  </td>
                                  {% else %}
                                  <td>
                                  
                                  <input name={{question.id}} type="radio" value={{ n }}  id="{{name}}" /><lable> {{n}}</lable>
                                  </td>

I also replace question_id with a_question_id so I can enter the POST data into teh database.

Note - this is done intrinsically within Django. The only question is whether it’s done up front (here) or on demand. Using prefetch_related is a performance enhancement, not a functional requirement.

For example, if you were to write:
topic = Topic.objects.get(id=1)
You would still be able to iterate over topic.question_set.all() in your view.

Not at all. Your alternative is perfectly acceptable.

I actually started out with writing:

{% for topic in topics %}
  {{ topic.topic }}

but I thought that would be confusing.

1 Like

Thanks for all your help as I learn Django. On to setting up a login page to be able to report results by users.