1

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.

nik_m
  • 11,825
  • 4
  • 43
  • 57

1 Answers1

1

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.

nik_m
  • 11,825
  • 4
  • 43
  • 57
  • Thanks for your reply. By using your method when I display the choices in the form it just displays a single checkbox with label 'Subject object' instead of all the subjects. – VIVEK YADAV Apr 30 '17 at 10:12
  • 1
    Updated my answer. You have to create the `Subject` objects first and of course put a `__str__` method inside it to print the name instead of the ugly `'Subject object'`. – nik_m Apr 30 '17 at 10:30
  • Already changed the field name to subjects and created the Subject objects but still not able to display. Can u please guide me about creating Subject objects properly. – VIVEK YADAV Apr 30 '17 at 10:38
  • Updated once more. – nik_m Apr 30 '17 at 10:56
  • 1
    Glad I could help. If you like, in order to avoid accidentaly duplicated values of subject (because right now you can enter another value of `H` or `M`, ending up to have multiple values of the same names), you can change the definition of `name` into this: `name = models.CharField(max_length=2, unique=True, choices=SUBJECT_CHOICES, default=MATH)`. Of course, after that run migrations ;) – nik_m Apr 30 '17 at 11:02