Handling Invalid Survey Submissions with Django

0
124
4 min read

What would make a survey submission invalid? The only likely error case for our QuestionVoteForm is if no answer is chosen. What happens, then, if we attempt to submit a survey with missing answers? If we try it, we see that the result is not ideal:

Handling Invalid Survey Submissions with Django

There are at least two problems here. First, the placement of the error messages, above the survey questions, is confusing. It is hard to know what the first error message on the page is referring to, and the second error looks like it is associated with the first question. It would be better to move the error messages closer to where the selection is actually made, such as between the question and answer choice list.

Second, the text of the error message is not very good for this particular form. Technically the list of answer choices is a single form field, but to a general user the word field in reference to a list of choices sounds odd. We will correct both of these errors next.

Coding custom error message and placement

Changing the error message is easy, since Django provides a hook for this. To override the value of the error message issued when a required field is not supplied, we can specify the message we would like as the value for the required key in an error_messages dictionary we pass as an argument in the field declaration. Thus, this new definition for the answer field in QuestionVoteForm will change the error message to Please select an answer below:

class QuestionVoteForm(forms.Form):
answer = forms.ModelChoiceField(widget=forms.RadioSelect,
queryset=None,
empty_label=None,
error_messages={'required':
'Please select an answer below:'})

Changing the placement of the error message requires changing the template. Instead of using the as_p convenience method, we will try displaying the label for the answer field, errors for the answer field, and then the answer field itself, which displays the choices. The {% for %} block that displays the survey forms in the survey/active_survey.html template then becomes:

{% for qform in qforms %}
{{ qform.answer.label }}
{{ qform.answer.errors }}
{{ qform.answer }}
{% endfor %}

How does that work? Better than before. If we try submitting invalid forms now, we see:

Handling Invalid Survey Submissions with Django

While the error message itself is improved, and the placement is better, the exact form of the display is not ideal. By default, the errors are shown as an HTML unordered list. We could use CSS styling to remove the bullet that is appearing (as we will eventually do for the list of choices), but Django also provides an easy way to implement custom error display, so we could try that instead.

To override the error message display, we can specify an alternate error_class attribute for QuestionVoteForm, and in that class, implement a __unicode__ method that returns the error messages with our desired formatting. An initial implementation of this change to QuestionVoteForm and the new class might be:

class QuestionVoteForm(forms.Form):
answer = forms.ModelChoiceField(widget=forms.RadioSelect,
queryset=None,
empty_label=None,
error_messages={'required':
'Please select an answer below:'})

def __init__(self, question, *args, **kwargs):
super(QuestionVoteForm, self).__init__(*args, **kwargs)
self.fields['answer'].queryset = question.answer_set.all()
self.fields['answer'].label = question.question
self.error_class = PlainErrorList

from django.forms.util import ErrorList
class PlainErrorList(ErrorList):
def __unicode__(self):
return u'%s' % ' '.join([e for e in sefl])

The only change to QuestionVoteForm is the addition of setting its error_class attribute to PlainErrorList in its __init__ method. The PlainErrorList class is based on the django.form.util.ErrorList class and simply overrides the __unicode__ method to return the errors as a string with no special HTML formatting. The implementation here makes use of the fact that the base ErrorList class inherits from list, so iterating over the instance itself returns the individual errors in turn. These are then joined together with spaces in between, and the whole string is returned.

Note that we’re only expecting there to ever be one error here, but just in case we are wrong in that assumption, it is safest to code for multiple errors existing. Although our assumption may never be wrong in this case, it’s possible we might decide to re-use this custom error class in other situations where the single possible error expectation doesn’t hold. If we code to our assumption and simply return the first error in the list, this may result in confusing error displays in some situations where there are multiple errors, since we will have prevented reporting all but the first error. If and when we get to that point, we may also find that formatting a list of errors with just spaces intervening is not a good presentation, but we can deal with that later. First, we’d like to simply verify that our customization of the error list display is used.

LEAVE A REPLY

Please enter your comment!
Please enter your name here