Why is my ManyToManyField in Django returning only one value?

In my Django project, I have a class with a ManyToManyField:

class ContestCategory(models.Model):
    name = models.CharField(max_length=70, blank=False, null=False)
    judges = models.ManyToManyField('users.CustomUser', null=True)

I can see after migrating that everything worked fine and the table is created. I made a form to change the judges like this:

class JudgesForm(forms.ModelForm):
    class Meta:
        model = ContestCategory
        fields = ['judges']

    def __init__(self, *args, **kwargs):
        super(JudgesForm, self).__init__(*args, **kwargs)

In my view, the form appears and correctly displays all the CustomUser objects there are, and shows no error when saving. However, in my post method, when I do this:

class SubmissionCategoriesDetailView(LoginRequiredMixin, DetailView):
    model = ContestCategory
    template_name = 'reportes/detail_submission_categories.html'
    form_class = JudgesForm

    def post(self, request, *args, **kwargs):# 1711   5656    2230400    7616
        pk_category = kwargs.get('pk')
        judges = request.POST.get('judges')

        logging.info(str(judges))

        return HttpResponseRedirect(reverse('reportes:submission_categories'))

I get in the logs that 'judges' returns the correct Id of my CustomUser but only if I have just one CustomUser selected. Whenever I choose more than one, it only gives me in 'judges' the Id of the last one that was chosen, not of all of them.

Why is this happening? Should I use a different widget than the automatic one or is there something missing from the models? Any help would be appreciated.

Upvotes: 1

Views: 330

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476557

Because a request.POST.get(…) [Django-doc] always returns one value. If you want to return all values, you should make use of request.POST.getlist(…) [Django-doc]:

from django.shortcuts import redirect

class SubmissionCategoriesDetailView(LoginRequiredMixin, DetailView):
    model = ContestCategory
    template_name = 'reportes/detail_submission_categories.html'
    form_class = JudgesForm

    def post(self, request, *args, **kwargs):
        pk_category = kwargs.get('pk')
        judges = request.POST.getlist('judges')

        logging.info(str(judges))

        return redirect('reportes:submission_categories')

It is however better to use the form to process the data:

from django.shortcuts import redirect

class SubmissionCategoriesDetailView(LoginRequiredMixin, DetailView):
    model = ContestCategory
    template_name = 'reportes/detail_submission_categories.html'
    form_class = JudgesForm

    def post(self, request, *args, **kwargs):
        form = JudgesForm(request.POST)
        if form.is_valid():
            logging.info(str(form.cleaned_data['judges']))

        return redirect('reportes:submission_categories')

You might want to take a look at an UpdateView [Django-doc] to handle the boilerplate logic.

Upvotes: 1

Related Questions