Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ModelChoiceField gives “Select a valid choice” populating select with ajax call

I've tried all possible solutions on several threads and I'm still unable to fix the problem. I have the following code:

models.py

class CustomerVisit(models.Model):
  start_date = models.DateField()
  end_date = models.DateField()
  customer = models.ForeignKey(Customer)
  address = models.ForeignKey(Address)

forms.py

address = forms.ModelChoiceField(label='Address',
                                 queryset=Address.objects.none(),
                             widget=forms.Select(attrs={'style': 'width: 100%;'}))
customer = forms.ModelChoiceField(label='Customer',
                                  queryset=Customer.objects.all(),
                          widget=forms.Select(attrs={'style': 'width: 100%;'}))

views.py

if request.method == "POST":
    # Cleaning fields
    post = request.POST.copy()
    post['address'] = Address.objects.get(id=post['address'])
    post['start_date'] = dateparser.parse(post['start_date'])
    post['end_date'] = dateparser.parse(post['end_date'])
    # Updating request.POST
    request.POST = post
    form = CustomerVisitForm(request.POST)
    if form.is_valid():
        form.save(commit=True)
        return redirect("customervisit:calendar")

js

$("#id_customer").select2({}).on("change", function () {
    var customer_id = $("#id_customer").val();
    var id_address = $("#id_address");
    id_address.select2({
        ajax: {
            url: '/get_customer_address/' + customer_id,
            dataType: "json",
            type: "GET",
            data: function (params) {

                var queryParameters = {
                    term: params.term
                }
                return queryParameters;
            },
            processResults: function (data) {
                return {
                    results: $.map(data, function (item) {
                        return {
                            text: item.text,
                            id: item.id
                        }
                    })
                };
            }
        }
    });
});

My address select its being populated based on customer selection using ajax call using select2. After reading several threads I noticed that modelchoicefield expects a Address object so that's why I'm using the following code on my view before the form is being validated: post['address'] = Address.objects.get(id=post['address']) but I'm still getting the Select a valid choice. That choice is not one of the available choices. error

I'm using queryset=Address.objects.none(), because I need an empty select

like image 684
Marco Herrarte Avatar asked Oct 28 '17 18:10

Marco Herrarte


2 Answers

Problem solved.

If someone in the future have the same error as me, checking to_python method from the ModelChoiceField saved my day:

def to_python(self, value):
    if value in self.empty_values:
        return None
    try:
        key = self.to_field_name or 'pk'
        value = self.queryset.get(**{key: value})
    except (ValueError, TypeError, self.queryset.model.DoesNotExist):
        raise ValidationError(self.error_messages['invalid_choice'], code='invalid_choice')
    return value

So I changed my queryset to queryset=Address.objects instead of queryset=Address.objects.none() or queryset=Address.objects.all()

Thanks Daniel Roseman for your comments

like image 63
Marco Herrarte Avatar answered Oct 10 '22 17:10

Marco Herrarte


I know what you're trying to achieve, my solution would be to initialise the field as charfield with widget select, and then override the clean method. Example:

class InvoiceForm(forms.ModelForm):
    customer = forms.CharField(widget=forms.Select(attrs={'style': 'min-width: 150px;'}))

    class Meta:
        model = Invoice
        exclude = ['id', 'created', 'is_paid']

    def clean_customer(self):
        customer_id = self.cleaned_data['customer']
        customer = Customer.objects.get(id=customer_id)
        if customer: 
            return customer 
        else:
            raise ValidationError("Customer ID invalid")
like image 21
Fendy Heryanto Johan Avatar answered Oct 10 '22 17:10

Fendy Heryanto Johan