Search code examples
pythondjangodjango-viewsdjango-forms

Django ManytoMany form with checkboxes: how to keep "checked" and "unchecked" values?


I'm trying to display and use a form that uses a manytomany field, widgetized as checkboxes

The problem I'm having is when I view the page, the state of the checkboxes isn't visible (all the checkboxes show as unchecked, but there are related rows in the database for the manytomany relations). What I'd like to do is display an "edit" form with all the correct statuses for the checkboxes (i.e. checked checkboxes for existing manytomany values in the database, unchecked checkboxes for missing values)

here's the form declaration:

class EditPatientForm(forms.ModelForm):
    flags = forms.ModelMultipleChoiceField(
            queryset=PatientFlag.objects.filter(visible_on_edit=True),
            widget=forms.CheckboxSelectMultiple,
            required=False)
    class Meta:
        model = Patient
        exclude = ('profile_picture','registered_on') 
        fields = "__all__"

    helper = FormHelper()

Every "patient" has some associated "flags" such as "has diabetes" or "has heart problems". I want to display a form with a list of checkboxes so the user can select "has diabetes" as a checkbox, which then gets saved in the database.

Here are the two models:

class PatientFlag(models.Model):    
    name = models.CharField(max_length=255, null=True)
    question = models.CharField(max_length=255, null=True)
    description = models.TextField(null=True)
    visible_on_create = models.BooleanField( default=True)
    visible_on_edit = models.BooleanField(default=True)

class Patient(models.Model):
    """Represents a patient"""
    first_name = models.CharField(max_length=255)
    last_name = models.CharField(max_length=255)
    flags = models.ManyToManyField(PatientFlag, db_index=True, related_name='patient')

Now, when I go and edit the patient using this form, I get a list of all the checkboxes representing "flags", but they are all always unchecked

How can I fix this?


Solution

  • to make sure that the checkboxes in your EditPatientForm are pre-selected based on the existing flags associated with a patient you need to pass the instance of the patient to the form when you instantiate it. assuming you are using django's generic UpdateView you can modify your view like this:

    from django.views.generic.edit import UpdateView
    from .models import Patient
    from .forms import EditPatientForm
    
    class EditPatientView(UpdateView):
        model = Patient
        form_class = EditPatientForm
        template_name = 'your_template.html'
    
        def get_form(self, form_class=None):
            form = super(EditPatientView, self).get_form(form_class)
            form.helper = FormHelper()
            return form
    
        def get_context_data(self, **kwargs):
            context = super().get_context_data(**kwargs)
            context['form'].instance = self.object  # you pass the instance to the form
            return context
    

    remember to replace your_template.html with the path to your template now when the EditPatientForm is rendered it will take into account the existing flags associated with the patient instance and per-select the checkboxes accordingly

    if you are not using django's generic views make sure you pass the instance when creating the form in your view like this:

    def edit_patient_view(request, patient_id):
        patient = get_object_or_404(Patient, pk=patient_id)
        
        if request.method == 'POST':
            form = EditPatientForm(request.POST, instance=patient)
            if form.is_valid():
                form.save()
                # you cand redirect or do something after successful form submission
        else:
            form = EditPatientForm(instance=patient)
    
        return render(request, 'your_template.html', {'form': form, 'patient': patient})
    

    I hope this helps you.