Reputation: 11943
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
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