Search code examples
djangodjango-modelsdjango-formsdjango-viewsdjango-widget

Django Form (Checkbox) - unable to save data


I am trying to display a form (with multiple select checkbox) and save the data in database. But having some problem.

Here is my model:

class Preference(models.Model):

    CLASS_CHOICES = [('1', '1'), ('2', '2'), ('3', '3')]
    BOARD_CHOICES = [('C', 'CBSE'), ('I', 'ICSE'), ('S', 'State Board')]
    SUBJECT_CHOICES = [('H', 'HINDI'), ('M', 'MATH'), ('E', 'ENGLISH')]

    class = models.CharField(max_length=2, choices=CLASS_CHOICES, default='1', blank=False)
    board = models.CharField(max_length=2, choices=BOARD_CHOICES, default='C', blank=False)
    subject = models.CharField(max_length=2, choices=SUBJECT_CHOICES, default='M', blank=False)

My form:

class PreferenceForm(forms.ModelForm):
    class Meta:
        model = Preference
        fields = ['class', 'board', 'subject']
        widgets = {
            'board': forms.RadioSelect(),
            'subject': forms.CheckboxSelectMultiple()
        } 

My view:

def pref(request):
    form = PreferenceForm(request.POST or None)
    if form.is_valid():
        form.save()
        return render(request, 'website/thanks.html')
    else:
        print(form.errors)
        return render(request, 'website/pref.html', {'form': form})

It displays the form with checkbox but I am unable to save that data to database even when I select a single choice.

Error Displayed:- `

<li>Subject<ul class="errorlist"><li>Select a valid choice. [&#39;H&#39;, &#39;M&#39;] is not one of the available choices.</li></ul></li>

All help/suggestions are appreciated, thanks.


Solution

  • First of all, rename the class field inside your model to something else. class is a Python reserved word and it might cause you trouble later on.

    Secondly, in order to render a CharField as a CheckboxSelectMultiple widget your model's fields declaration must change. Now, you're telling the subject field to accept a single value (amongst the 'H', 'M' and 'E' values), but, via your form, you try to give it multiplte values ([&#39;H&#39;, &#39;M&#39;], which means 'H' and 'M'). That's the cause of your error. It's expecting one value while you give it more than one.

    Solution to your problem is to create another model, say Subject and inside that model declare the SUBJECT_CHOICES and a single field (say name). Then in your Preference model, change the subject field into a ManyToManyField, instead of a CharField. In that way, the subject field of the Preference model will be able to accept more than one values.

    Something like this:

    class Subject(models.Model):
    
        HINDI = 'H'
        MATH = 'M'
        ENGLISH = 'E'
    
        SUBJECT_CHOICES = [
            (HINDI, 'HINDI'),
            (MATH, 'MATH'),
            (ENGLISH, 'ENGLISH')
        ]
    
        name = models.CharField(max_length=2, choices=SUBJECT_CHOICES, default=MATH)
    
        def __str__(self):
            return self.name
    
    
    class Preference(models.Model):
    
        CLASS_CHOICES = [('1', '1'), ('2', '2'), ('3', '3')]
        BOARD_CHOICES = [('C', 'CBSE'), ('I', 'ICSE'), ('S', 'State Board')]
    
        # RENAME THE class FIELD PLEASE, i.e class_number!
        class = models.CharField(max_length=2, choices=CLASS_CHOICES, default='1', blank=False)
        board = models.CharField(max_length=2, choices=BOARD_CHOICES, default='C', blank=False)
        # Notice the plural name of the field. Subjects, not subject, since we're expecting more than one value into this field.
        subjects = models.ManyToManyField(Subject)
    

    Finally:

    1. Don't forget to create the Subject objects (via the admin or the ./manage shell). If you use the admin it's fairly simple. If you use the shell then do the following:

      from myapp.models import Subject
      
      [Subject.objects.create(name=choice[0]) for choice in Subject.SUBJECT_CHOICES]
      
    2. Update the forms.py file with the new field name subjects.