Anupam
Anupam

Reputation: 15620

Django form - type of variable changes when reloaded after validation error

I have spent some time on this but cannot figure out the exact cause of the following behaviour.

I have a Django form and in the template I am trying to see if an integer is present in a list and then doing something with it.

{% if pk in form.area.value %} {# form.area.value is a list like [7,21] #}
    do something
{% endif%}

Everything works fine except in cases where the form is reloaded after a validation error. In such cases, the list that I am comparing with, gets converted to list of strings (from a list of ints) on its own and the if test fails. So [7,21] above becomes ['7','21']

In case it helps, this is how the form is being rendered in the view:

On the GET request (where the if condition works fine):

form = SchoolForm(instance = school)
return render(request, 'edit-school.html', {'form': form, 'school_id': school_id})

After the POST request (where the if condition fails in the template):

form = SchoolForm(request.POST or None, instance=school, request=request)
return render(request, 'edit-school.html', {'form': form, 'school_id': school_id})

Update: Got it to work by converting string values after the POST request in the form back to int by declaring another method in the form (as suggested by @bruno in the answer below) and using it in the template. Here's the code snippet (of how its being used in the template):

<div class="checkbox">
    {% for pk, choice in form.area.field.widget.choices %}
        <label for="id_{{sub_type}}_{{form.area.name}}_{{ forloop.counter0 }}">
            <input class="form-control" id="id_{{sub_type}}_{{form.area.name}}_{{ forloop.counter0 }}" name="{{form.area.name}}" type="checkbox" value="{{ pk }}" {% if pk in form.area_values %} checked="checked" {% endif %}/>
            <span class="badge">{{ choice }}</span>
        </label>
    {% endfor %}
</div>

Upvotes: 2

Views: 411

Answers (1)

bruno desthuilliers
bruno desthuilliers

Reputation: 77902

After the user submitted the form, {{ form.area.value }} (which in Python resolves to form["area"].value) is populated with the raw data from request.POST, which are indeed strings.

You didn't clearly explain what kind of "fancy thing" you're trying to do in the template so it's hard to come with a "best" solution for your use case, but as a general rule, templates are not the place for anything fancy - that's what Python is for (either in your view, in your form, in a custom templatetag or filter etc).

A very quick and simple solution would be to add a method to your form that returns the area boundfield values as ints:

class SchoolForm(forms.ModelForm):
    # your code here

    def area_values(self):
        # XXX this might require some conditionals or
        # error handling to be failsafe 
        # works with both python 2.x and 3.x
        return [int(v) for v in self["area"].value()]

then in your template, replace {% if pk in form.area.value %} with {% if pk in form.area_values %}

Upvotes: 3

Related Questions