tudorv
tudorv

Reputation: 53

Django form.is_valid() cleans needed values

I am practicing Django, and I have been stuck on this for a while now. I am trying to write a basic survey app. My issue is that when I submit my form, I can see that request.POST contains the data I need, form.data also contains it, but after I run form.is_valid(), I am left with only the keys in the dict, no values.

class SurveyForm(forms.Form):
    def __init__(self, *args, **kwargs):
        questions = kwargs.pop('questions')
        super(SurveyForm, self).__init__(*args, **kwargs)

        for q in questions:
            choices = []
            for answer in q.choice_set.all():
                choices.append((answer.pk, answer.choice_text))

            self.fields[q.id] = forms.ChoiceField(label=q.question_text, required=False,
                                  choices=choices, widget=forms.RadioSelect)

    def answers(self):
        for q, a in self.cleaned_data.items():
            yield a

If I remove required=False, I keep getting This field is required although, I believe it's due to the validation, because it looks like the page refreshes when I hit submit, while if I actually leave the choices blank, it doesn't refresh and instead a small popup appears above the label.

Here is the view that uses it.

def step(request, survey_id, attempt_id, surveypage_nr):
    survey = get_object_or_404(Survey, pk=survey_id)
    attempt = get_object_or_404(Attempt, pk=attempt_id)
    pages = survey.surveypage_set.all().order_by('page_nr')
    page = pages.filter(page_nr=surveypage_nr).first();
    questions = page.question_set.all()

    form = SurveyForm(request.POST or None, questions=questions)

    if form.is_valid():
        for a in form.answers():
            answer = get_object_or_404(Choice, pk=a)
            attempt.score = attempt.score + answer.score
        attempt.save()
        return HttpResponseRedirect(reverse('results', args=(survey.id, attempt.id,)))

    else:
        context = {'page': page, 'form': form}
        return render(request, 'survey/surveypage.html', context)

I tried checking the contents of the form after the is_valid() call:

request.POST = {'3': '2', '4': '3', 'csrfmiddlewaretoken': 'f..m1'}
form.data = {'3': '2', '4': '3', 'csrfmiddlewaretoken': 'f..m1'}
form.cleaned_data = {3: '', 4: ''}.    # Why are you cleaning my values, Django?


form.fields = OrderedDict([(3, <django.forms.fields.ChoiceField object at 0x106c2d5f8>),
         (4, <django.forms.fields.ChoiceField object at 0x1055ebba8>)])

As you can see, request.POST and form.data contain exactly what I need, but after validation, form.cleaned_data doesn't have values for the keys. Why is this happening? Help pls.

Upvotes: 0

Views: 371

Answers (2)

Daniel Roseman
Daniel Roseman

Reputation: 599778

As the other answer pointed out, the key needs to be a string. Data in a POST is always a string, but your keys are currently int. So when Django goes to validate the data for field '3' it finds nothing, so shows it as empty; meanwhile it doesn't have a field called 3 so throws away that key/value.

You should set your field IDs to strings:

self.fields[str(q.id)] = ...

Upvotes: 1

baskershu
baskershu

Reputation: 109

the key in form.cleaned_data is string , but form.datais int.

Upvotes: 2

Related Questions