Home > database >  django choicefield Select a valid choice
django choicefield Select a valid choice

Time:02-21

I'm trying to submit my modelform with ajax loaded info of my other models. For the ChoiceField of my modelform I got this error;

Select a valid choice. 69 is not one of the available choices.

the request.POST seems well. I got all my fields properly. As my remind_object field is CharField already, I should be able to save ['69'] to there but I can't figure out why I got this error ?

*** request.POST:  <QueryDict: {'csrfmiddlewaretoken': ['dTtWIUKCOytmYVsbGf7StxMmd4ywPd0gzvvraAgrqiLuiUfLv3xo2TD1lv9Xpcxs'], 'topic': ['dsfdsfs'], 'remind_date': ['2022-02-22 13:45:59'], 'reminder_object_type': ['Client'], 'remind_object': ['69'], 'detail': ['<p>fdgfdgfd</p>\r\n'], 'submit_reminder_create_form': ['']}>

models.py

class Reminder(models.Model):
    user_owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='reminder_user') # user.reminder_user
    topic = models.CharField(max_length=100, blank=True, null=True)
    remind_date = models.DateTimeField(blank=True, null=True)
    reminder_object_type = models.CharField(max_length=100, blank=True, null=True)
    # Client
    # Contact
    # Action
    remind_object = models.CharField(max_length=999, blank=True, null=True)
    detail = RichTextField(max_length=999999,blank=True, null=True)
    reminder_status = models.CharField(max_length=100, default="Active", blank=True, null=True)

    slug = models.SlugField(max_length=200, blank=True, null=True) 
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    def __str__(self):
        return str(self.topic) 

    def save(self, *args, **kwargs): 
        self.slug = slugify(str(self.topic)   "-"   str(self.user_owner)   "-"    str(get_random_code()))
        super().save(*args, **kwargs) 

    def get_absolute_url(self):
        return reverse('reminder-detailview', kwargs={'slug': self.slug})

forms.py

class ReminderModelForm(forms.ModelForm):
    class Meta:
        model = models.Reminder 
        fields = ('reminder_object_type', 'topic', 'remind_date', 'remind_object', 'detail')
    
    def __init__(self, *args, **kwargs): 
        super().__init__(*args, **kwargs) 
        self.fields['reminder_object_type'] = forms.ChoiceField(required=True, label="", widget=forms.RadioSelect(attrs={'class': 'form-check form-control'}), choices=(
            ("Client", "Client"),
            ("Contact", "Contact"),
            ("Action", "Action")
        ))
        self.fields['topic'] = forms.CharField(required=True, max_length=100, label="", widget=forms.TextInput(attrs={'class':'form-control', 'placeholder': 'Topic'}))
        self.fields['remind_date'] = forms.DateTimeField(required=True, label="", validators=[validate_greater_than_now], widget=DateTimeWidget(
            attrs={'class':'form-control'}, 
            usel10n = True,
            bootstrap_version=4
            ) )
        self.fields['remind_object'] = forms.ChoiceField(required=False, label="", widget=forms.Select(attrs={'class': 'form-control'}))
        self.fields['detail'] = forms.CharField(required=False, max_length=999999, widget=CKEditorWidget(attrs={'class' : 'form-control'}))

views.py

class ReminderListView(generic.ListView):
    model = models.Reminder
    template_name = 'crm/reminder_list.html'
    ordering = ['-created']

    def post(self, request, *args, **kwargs):
        if 'submit_reminder_create_form' in self.request.POST:
            print("*** request.POST: ", request.POST)
            reminder_form = forms.ReminderModelForm(self.request.POST, self.request.FILES)
            if reminder_form.is_valid():
                print("*** request.POST: ", request.POST)
                instance = reminder_form.save(commit=False)
                instance.user_owner = self.request.user
                instance.save()
                reminder_form = forms.ReminderModelForm()
                messages.success(self.request, f'{instance.topic} created successfully')
                return redirect('reminder-detailview', slug=instance.slug)
            else:
                messages.warning(self.request, 'The form is not valid, please check the form fields properly!')
                context = {
                    'reminder_create_form':forms.ReminderModelForm(self.request.POST, self.request.FILES),
                    'all_reminders': models.Reminder.objects.all().order_by('-created')
                    }
                return render(request, 'crm/reminder_list.html', context, status=404)

    def get_context_data(self, **kwargs):
        context = super(ReminderListView, self).get_context_data(**kwargs)
        context['all_reminders'] = models.Reminder.objects.all().order_by('-created')
        context['sidebar_in'] = 'Reminder'
        context['time_is'] = timezone.now()
        context['reminder_create_form'] = forms.ReminderModelForm

        context['active_reminders'] = models.Reminder.objects.filter(reminder_status = 'Active').filter(remind_date__lte = timezone.now() ).order_by('-created')
        return context

html

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
{{ reminder_create_form.media }}
<form id="reminder-create-form" method="POST" enctype="multipart/form-data" > {% csrf_token %}
    {{reminder_create_form|crispy}}
</form>

<!-- jquery -->
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<!-- my custom -->
<script>
    // bu js kısmı; sadece html formuna verilerin istediğimiz gibi bağlı-dropdown şeklinde yüklenmesini sağlıyor, ama forma kaydetmede forms.py da overwrite ettiğimiz init çalışacak 
    $(document.getElementById("reminder-create-form").elements["reminder_object_type"]).change(function () { 
        var selectedValue = $(this).val(); 
        $.ajax({ 
            url: "{% url 'ajax_reminder_type_view' %}",
            data: {  'gonderilen_kelime': selectedValue,  },
            success: function (data) { 
                $(document.getElementById("reminder-create-form").elements["remind_object"]).html(data); 
            }
        });
    });
</script>

ajax_loading.html

{% for i in aranacak_objeler %}
<option value="{{ i.pk }}">
    {% if i.name %}
        {{ i.name }} {% if i.client_owner %} - Client: {{i.client_owner}} {% endif %}
    {% else %}
        {{ i.topic }} - Client: {{i.client_owner}} - Created: {{i.created|date:"F d, Y"}}
    {% endif %}
</option>
{% endfor %}

CodePudding user response:

The error occurs during the process of form validation through to_python or validate method of django.form.Field class. What you are doing is passing an invalid value to your field. Invalid values can be ones not in a choice list or tuple, but if you are sure the value is in the choice list, just check the field and its widget.

Values used for field validation strictly depends on input formats specified by the widget. If you are using forms.RadioSelect for the field's widget, the widget passes a string value to the field whereas forms.CheckboxSelectMultiple widget passes a listed values like ['apple', 'orange', ...]. The thing is each field has its own acceptable data type for validation. For instance, MultipleChoiceField accepts only lists or tuples whereas ChoiceField only accepts a string. Perhaps there might be a collision between the value type specified in the field's widget, the one used for validation, and value type the field expects. Maybe you want to save a string value for CharField while passing to a single-element list value (or the other way around)?

You can check which data type is acceptable for specific fields here. https://github.com/django/django/blob/737542390af27616d93f86cd418e2d7f3e874b27/django/forms/fields.py

  • Related