Name attribute of input field identical modelform

I am trying to loop through a Model PendingRequest and check the every request instance of a user and approve or decline the users request for each instance.I am using a ModelForm to render my form however, the part that gives issue is that the radio button to click on whether to approve or decline do not share the same input name attribue

here is my template

    <form method="post">
      {% csrf_token %}
      <table>
        <thead>
          <tr>
            <th>Book Title</th>
            <th>Status</th>
          </tr>
        </thead>
        <tbody>
          {% for book in books %}
          <tr>
            <td>{{ book.book.title }}</td>
            <td>
              <input type="radio" name="{{ book.id }}" value="approved"> Approve
              <input type="radio" name="{{ book.id }}" value="not_approved"> Decline
            </td>
          </tr>
          {% endfor %}
        </tbody>
      </table>
      <button type="submit">Update</button>
    </form>     

so what i want to achieve is to replace <input type="radio" name="{{ book.id }}" value="approved"> and <input type="radio" name="{{ book.id }}" value="not_approved"> with this below such that when it is rendered, every loop will have same the name attribute but different values

    <form method="post">
      {% csrf_token %}
      <table>
        <thead>
          <tr>
            <th>Book Title</th>
            <th>Status</th>
          </tr>
        </thead>
        <tbody>
          {% for book in books %}
          <tr>
            <td>{{ book.book.title }}</td>
            <td>
              {% for choice in form.approved %}
                {{choice.tag}}
              {% endfor %}
              {% for choice in form.not_approved %}
                {{choice.tag}}
              {% endfor %}
            </td>
          </tr>
          {% endfor %}
        </tbody>
      </table>
      <button type="submit">Update</button>
    </form>     

here is my forms.py

class Approve(forms.ModelForm):
    approved = forms.BooleanField(widget=forms.RadioSelect(choices=[(True, 'Approve')]))
    not_approved = forms.BooleanField(widget=forms.RadioSelect(choices=[(True, 'Decline')]))
    class Meta:
        model = PendingRequest
        exclude = ["member", "book_request", "book"]

models.py

class PendingRequest(models.Model):
    book_request = models.OneToOneField(BorrowBook, on_delete=models.CASCADE, null=True)
    member = models.ForeignKey(User, on_delete=models.CASCADE, default="", null=True)
    book = models.ForeignKey(Books, on_delete=models.CASCADE, default="", null=True)
    approved = models.BooleanField(default=False)
    not_approved = models.BooleanField(default=False)
    approval_date = models.DateTimeField(auto_now=True, null=True)

views.py

def approve(request, pk):
    books = PendingRequest.objects.filter(member__id=pk)
    if request.method == "POST":
        for book in books:
            form = Approve(request.POST, instance=book)
            if form.is_valid():
                    book.approve = form.cleaned_data.get(f'approve_{book.id}')
                    book.save()
            return redirect(index)
    else:
        form = Approve()
    return render(request, "books/approve.html", {"form": form, "books": books})

You are making this a little complicated for yourself...

You have two booleans in that PendingRequest model. One for approved and one for not_approved. You only want one, because if approved is False, then it's already not_approved by definition.

You also are making life difficult by having two radio button fields with one radio button each for field. Radio buttons are for multiple choice, one value allowed. You could use two radio buttons for one field (a checkbox is often better) but having two radio buttons for two separate fields is not only bad HTML, you'll likely be able to select both at once. If you give them the same name to prevent that, they won't be linked to the correct field without a lot of weird post-POST fiddling about. It's very messy. I'd really advise using a single field.

Regardless if you take that advice, for multiple forms of the same model, Django can handle this situation with a modelformset, something like...

views.py

PendingRequestFormSet = modelformset_factory(PendingRequest , form = Approve)
#we have to also bring in the book title along with the forms , even though we are not using it in the form,
# so we'll zip up the queryset with the formset.
# We use select_related in the queryset to prevent additional database calls in the template when the title gets evaluated
pending_requests = PendingRequest.objects.filter(member__id=pk).select_related('book')
formset = PendingRequestFormSet(queryset=pending_requests))
pending_requests_and_formset = zip(queryset, formset)
...
return render(request, "books/approve.html", {"form": form, "books": books, 'pending_requests_and_formset', pending_requests_and_formset })

template.html

 ...
 <form method="post">
     {{ formset.management_form }}
     {% for request, form in pending_requests_and_formset %}
        <td>{{request.book.title}}</td>
        <td>{{ form }}</td>
     {% endfor %}
 </form>
Back to Top