Reputation: 3492
I'm having a hard time figuring out how to update a Field in Django Form while editing the Form.
In my form I have a ModelChoiceField and ChoiceField: first is a list of Car manufacturers, second is a list of models available for this manufacturer. Obviously I don't want to fill second field with models for every manufacturer but only for one selected in first ModelChoiceField.
Here's my example Form:
class PostForm(forms.ModelForm):
make = forms.ModelChoiceField(queryset=Manufacturer.objects.all())
model = forms.ChoiceField()
...
To render the template with form I use Class-based View:
class CreatePost(View):
template_name = 'post/edit_post.html'
def get(self, request):
form = PostForm()
return render(request, self.template_name, {'form': form})
def post(self, request):
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.owner = request.user
post.save()
return redirect('homepage')
return render(request, self.template_name, {'form': form})
In my template after I render PostForm
I disable model
field with jQuery
and wait for user to select some value in make
field. Here's my template:
<form class="form-horizontal" action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.non_field_errors }}
{% for field in form %}
<div class="form-group">
<label class="control-label">{{ field.label_tag }}</label>
{{ field }}
{{ field.errors }}
</div>
<div class="form-group">
<button type="submit" class="btn btn-default">Submit</button>
</div>
{% endfor %}
</form>
And then use AJAX
to request values for model
field (in this sample code I only return single item):
$('#id_make').change(function () {
$.ajax({
url: '{% url 'get_models' %}',
data: {'make': $('#id_make :selected').text()},
dataType: 'json',
success: function (data) {
$('#id_model').append($('<option>', {
value: 1,
text: data.model})).removeAttr('disabled');
}
});
})
Here's the view I'm using to process AJAX call (for display purposes and to keep things simple in this question I have only one record in models
table Series
for each manufacturer
and knowing this I only query for one record. series
is CharField that keeps title of model
like 'Lancer' for 'Mitsubishi' manufacturer.):
def get_models(request):
manufacturer = Manufacturer.objects.get(make=request.GET.get('make'))
model = Series.objects.get(make_fk= manufacturer)
return JsonResponse({'model': model.series})
With this done I do see updated options in <select>
element for model ChoiceField
, but after I select new value in model ChoiceField
and submit form I receive:
Select a valid choice. 1 is not one of the available choices."
<select>
element and then submit.I then dug deeper and printed the contents of submitted Form and it appeared that although I saw <select>
element updated in the template, it stayed blank in the Form I've submitted:
<select name="model" class="form-control" id="id_model"></select>
Why is this happening and how to update Form fields itself rather than just <select>
element?
Upvotes: 0
Views: 2432
Reputation: 309049
At the moment, you haven't set any choices of model
, so it will never be valid. You could override the __init__
method and set the choices there.
You could set the choices to be all models, and then in the clean
method verify that the model matches the make.
class PostForm(forms.ModelForm):
make = forms.ModelChoiceField(queryset=Manufacturer.objects.all())
model = forms.ChoiceField()
def __init__(self, *args, **kwargs):
super(PostForm, self).__init__(*args, **kwargs)
if self.data:
self.fields['model'].choices = ...
def clean(self)
# check that model matches make
Since the model
field refers to the series
model, it might be easier to use a ModelChoiceField
and set self.fields['model'].queryset
instead.
This was just a rough idea off the top of my head. The tutorial you found uses a similar approach but is complete and explained in more detail.
Upvotes: 1
Reputation: 3492
I found explained solution with code examples how to implement chained/dependent dropdowns, coincidentally the solution was posted literally right after I posted this question.
Upvotes: 0