1

I am currently developing a website with a number of questionnaires on it that are in html table format. The questionnaires are supposed to be answered in a in a Likert fashion with radio buttons. For that I'm trying to render a 'radioSelect' widget as a html table instead of its standard list format. I would like it to look like this:

|----------|---------|-----------|---------|----------|
|          |  Never  | Sometimes |  Often  |  Always  |
|----------|---------|-----------|---------|----------|
| Question | First 0 | Second 0  | Third 0 | Fourth 0 |
|----------|---------|-----------|---------|----------|
| Question | First 0 | Second 0  | Third 0 | Fourth 0 |
|----------|---------|-----------|---------|----------|

...

|----------|---------|-----------|---------|----------|
| Question | First 0 | Second 0  | Third 0 | Fourth 0 |
|----------|---------|-----------|---------|----------|

And I guess that the html code for the rendered form should look somewhat like this:

<td><label for="id_choice_field_0"><input type="radio" name="choice_field" value="1" id="id_choice_field_0" required />First</label></td>
<td><label for="id_choice_field_1"><input type="radio" name="choice_field" value="2" id="id_choice_field_1" required />Second</label></td>
...
<td><label for="id_choice_field_n"><input type="radio" name="choice_field" value="n" id="id_choice_field_n" required />n</label></td>

I've tried to figure out how to that based on the answers from this threat from 7 years ago. I'm still a beginner though and a lot has changed in Django since then so I was unfortunately not able to make it work.

I also played around with css based on this thread and I tried to use 'as_table' in my html code but I had no success with those either.

From going through all the other threats that seemed relevant to my problem and from reading the documentation I understand that I have to override the default renderer of 'radioSelect' with my own custom renderer in order to replace the list tags with table tags. Based on that assumption I made the following attempt:

forms.py:

from django import forms
from django.utils.safestring import mark_safe

class MyCustomRenderer(forms.RadioSelect):
    def render(self):
        return( mark_safe( u''.join( [ u'<td>%s</td>' % force_unicode(w.tag()) for w in self ] )))

CHOICES = (('1', 'First',),('2', 'Second',),('3', 'Third',),('4', 'Fourth',))

class SelectForm(forms.Form):
    choice_field = forms.ChoiceField(widget=forms.RadioSelect(renderer=MyCustomRenderer), choices=CHOICES, label='TEST')

views.py:

from django.shortcuts import render, HttpResponse
from django.views.generic import TemplateView
from accounts.forms import SelectForm

class HomeView(TemplateView):
    template_name = 'accounts/formtest.html'

    def get(self, request):
        form = SelectForm() 
        return render(request, self.template_name, {'form': form})

    def post(self, request):
        form = SelectForm(request.POST)
        if form.is_valid():
        text = form.cleaned_data['choice_field']

        args = {'form': form, 'text': text}
        return render(request, self.template_name, args)

formtest.html:

{% extends 'base.html' %}

{% block body %}
<h1>Form test</h1>

<form method="post">
    {% csrf_token %}
    <table>
        <thead>
            <tr>
                <th></th>
                <th>Never</th>
                <th>Sometimes</th>
                <th>Often</th>
                <th>Always</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                {% for radio in form %}
                <td class="question_align">Question<td>
                        {{ radio }}
                {% endfor %}
            </tr>
        </tbody>
    </table>

    <button type="submit">Submit</button>

</form>

<br>

<h1>{{ text }}</h1>
{% endblock %}

I was able to work through a number of error messages but I got completely stuck at the following error message:

TypeError: __init__() got an unexpected keyword argument 'renderer'

I assume that this error refers to the attribute of the widget. I don't understand how to fix this error however, since I thought that the 'renderer' attribute is needed to override the default renderer.

I would greatly appreciate any kind of help with this. If I'm going into the wrong direction, is there a better way to achieve this maybe?

PS: While having consulted StackOverflow many many times this is the first time that I'm asking a question myself. Any feedback on how to improve my question is very welcome!

jono3030
  • 412
  • 5
  • 14
  • I realize that the mark_safe function that gets returned won't put the the items into the correct format but I'll figure that out once I learned how to change the list tags to table tags. – jono3030 Jun 26 '18 at 03:49

1 Answers1

1

I eventually found the answer to my question in the Django documentation. Turns out that the approach I detailed in my question was obsolete and wouldn't work in recent Django versions. The team that develops Django created a much more elegant and straightforward method: instead of overriding the renderer in your forms.py you have to use the Django templating language in your html file. It's described here.

I will play around with it more (and update my answer if I find sth important to add) but as far as I understood it you have to:

1) specify the field by name when calling the form;
2) add the necessary tags manually;
3) use the radiobutton's tag attribute.

Here's the code that works for me:

{% extends 'base.html' %}

{% block body %}
<h1>Form test</h1>

<form method="post">
    {% csrf_token %}
    <table>
        <thead>
            <tr>
                <th></th>
                <th>Never</th>
                <th>Sometimes</th>
                <th>Often</th>
                <th>Always</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td class="question_align">Question<td>
                {% for radio in form.choice_field %}
                    <td class="question_align">{{ radio.tag }}</td>
                {% endfor %}
            </tr>
        </tbody>
    </table>

    <button type="submit">Submit</button>

</form>

<br>

<h1>{{ text }}</h1>
{% endblock %}

It's rather obvious once you know it.

Anyway, hope that helps someone!

jono3030
  • 412
  • 5
  • 14