noblerare
noblerare

Reputation: 11943

Trouble with Django form validation

I have been pounding my head on this problem, been looking up Django docs on how to do form validation and can't seem to get any progress so I'm turning to SO for help.

I have a website with a Django form on which I display two ChoiceFields. One contains a list of car makes and the other contains a list of car models. The second field is initially blank and is populated with my Javascript when the user selects a car make. When the user selects both a make and model and clicks Submit, then a page will be loaded with info pertaining to the make/model that the user selected.

Now, if the user selects only a make but no model or no make at all, I want to display an error saying "Please select a make and model to continue."

So far, no matter what I've tried, I am unable to get that functionality.

forms.py

from django import form

makes = (('Honda', 'Honda'), ('Ford', 'Ford'), ('Nissan', 'Nissan'), ('GM', 'GM'))

class SearchForm:
    make = forms.ChoiceField(choices=makes, required=True, widget=forms.Select(attrs={'size':'5', 'autofocus'='on'}))
    model = forms.ChoiceField(required=True, widget=forms.Select(attrs={'size':'5'}))

    def clean(self):
        cleaned_data = super(SearchForm, self).clean()
        m1 = cleaned_data.get("make")
        m2 = cleaned_data.get("model")

        if not m1 or not m2:
            raise forms.ValidationError("Please select a make and model to continue.")
        return cleaned_data

    def clean_model(self):
        data = self.cleaned_data["model"]

        if not data: // if the user didn't select a model
             raise forms.ValidationError("Please select a model to continue.")
        return data

views.py

from mysite.forms import SearchForm

def search(request):
    searchform = SearchForm()
    return render(request, "search.html", {"form" : searchform})

def car_info(request):
    searchform = SearchForm(request.GET)
    if searchform.is_valid():
        // code to parse the make and model and return the corresponding page
        return render(request, "car_info.html", {})
    else: // there is an error so redisplay the page with the new form object
        return render(request, "search.html", {"form" : searchform})

search.html

...
<form action="{% url 'mysite.views.car_info' %}" method="GET" name="sform">
    {{ searchform.make }}
    {{ searchform.model }}
    <button>Search away!</button>
</form>

{% if searchform.errors %}
    <div class="error">{{ searchform.non_field_errors.as_text }}</div>
{% endif %}
...

In my index.html, I have a link to the search page by having: {% url 'mysite.views.search' %}

The behavior that I am getting is that when I don't select a make, I get the error. This is correct. When I do select a make but don't select a model, I also get the error. Good. But, when I do select a make and model, I get the same error and it will not take me to the car_info.html page.

Any help would be greatly appreciated.

EDIT:

Something weird is happening. I changed my forms.py to raise the Validation Error just to see what would pop out.

raise forms.ValidationError(str(clean_make) + " " + str(clean_model)) 

Then, I selected both make and model and the error was raised outputting the correct make for the make but "None" for the model, even though I had selected a model! Why is this?

EDIT 2:

Okay, I may know why the model is outputting "None". In my forms.py, I don't specify the choices for the model because that will be filled in with this Javascript code that runs on the search.html page:

$(document).ready(function() {
    $("#id_make").change(init);
});

function init() {
    populateModel();
}

function populateModel() {
    var make = $("#id_make")
    var model = $("#id_model") //select fields created by Django ChoiceField widget

    var mod_values = values[mak.val()];
    model.empty();
    $.each(mod_values, function(k,v) {
        model.append($("<option></option>").attr("value", v).text(k));
    });
}

var values = {
    "Honda" : { 
        "Accord" : "accord",
        "Civic" : "civic",
        "Odyssey" : "odyssey",
    },

    "Ford" : {
         "F-150" : "f150", 
         "Taurus" : "taurus", 
         "Fusion" : "fusion",
    },

    "Nissan" : { 
          "Sentra" : "sentra",
          "Maxima" : "maxima", 
          "Altima" : "altima",
    }, 
}

So, when I do:

 model = cleaned_data.get("version")

that'll be empty, right?

Now, I am now unsure how to fix it.

Upvotes: 0

Views: 289

Answers (1)

DavidM
DavidM

Reputation: 1437

As a first debugging step, since you are using the GET method, you should be able to inspect the URL for correctness. When you submit the form with both a make and model selected, the URL should look like:

?make=XXXX&model=YYYY

Since it does, you are absolutely correct that the form is cleaning out the model because essentially it believes there are no valid entries for model. You'll need to create a custom field that validates the model:

class ModelField(forms.ChoiceField):

    def valid_value(self, value):
        if value is not None and value != '':
            return True
        return False

Then, in your form:

model = ModelField(required=True, widget=forms.Select(attrs={'size':'5'}))

Upvotes: 1

Related Questions