1

i'm trying to implement a ModelMultipleChoiceField in my application, like that: Link

model.py

class Services(models.Model):

    id = models.AutoField(primary_key=True)
    type = models.CharField(max_length=300)

class Professionals_Services(models.Model):
    professional = models.ForeignKey(User, on_delete=models.CASCADE)
    service = models.ForeignKey(Services, on_delete=models.CASCADE)

form.py

class ProfileServicesUpdateForm(forms.ModelForm):
    service = forms.ModelMultipleChoiceField(required=False, queryset=Services.objects.all())

    class Meta:
        model = Professionals_Services
        fields = ['service']

    def clean(self):
        # this condition only if the POST data is cleaned, right?
        cleaned_data = super(ProfileServicesUpdateForm, self).clean()
        print(cleaned_data.get('service'))

view.py

class EditProfileServicesView(CreateView):
    model = Professionals_Services
    form_class = ProfileServicesUpdateForm
    context_object_name = 'services'
    template_name = 'accounts/edit-profile.html'

    @method_decorator(login_required(login_url=reverse_lazy('professionals:login')))
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(self.request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        form = self.form_class(data=request.POST)
        if form.is_valid():
            services = form.save(commit=False)
            services.save()

html

<select class="ui search fluid dropdown" multiple="" name="service" id="id_service">
  {% for service in services_list %}
    <option value="{{ service.id }}">{{ service.type }}</option>
  {% endfor %}
</select>

For development i'm using Pycham Professionals(latest version) with docker, when i run the application and i try to make a POST the answer is:

Cannot assign "<QuerySet [<Services: Services object (2)>, <Services: Services object (5)>, <Services: Services object (6)>, <Services: Services object (7)>]>": "Professionals_Services.service" must be a "Services" instance.

But if i run the application in debug mode and with a breakpoints on the if form.is_valid(): the application works fine

That's because the validate is equal to Unknown not in debug

you know how to fix?

Gianmarco Carrieri
  • 771
  • 2
  • 9
  • 20
  • `service` is a `ForeignKey`, so that means you can only select a *single* item. For multiple ones, you should use a `ManyToManyField`. – Willem Van Onsem Oct 09 '20 at 22:35

1 Answers1

1

Your service is a ForeignKey:

    service = models.ForeignKey(Services, on_delete=models.CASCADE)

A ForeignKey means that you select a single element, not multiple ones. You use a ManyToManyField [Django-doc] to select multiple elements:

class Professionals_Services(models.Model):
    professional = models.ForeignKey(User, on_delete=models.CASCADE)
    service = models.ManyToManyField(Service)

You should also not override the post method, and you can make use of the LoginRequiredMixin [Django-doc] to ensure that the user is logged in:

from django.contrib.auth.mixins import LoginRequiredMixin

class EditProfileServicesView(LoginRequiredMixin, CreateView):
    login_url = reverse_lazy('professionals:login')
    model = Professionals_Services
    form_class = ProfileServicesUpdateForm
    context_object_name = 'services'
    template_name = 'accounts/edit-profile.html'

    def form_valid(self, form):
        form.instance.user = self.request.user
        return super().form_valid(form)

In your Form you should also return the cleaned data:

class ProfileServicesUpdateForm(forms.ModelForm):
    service = forms.ModelMultipleChoiceField(required=False, queryset=Services.objects.all())

    class Meta:
        model = Professionals_Services
        fields = ['service']

    def clean(self):
        # this condition only if the POST data is cleaned, right?
        cleaned_data = super(ProfileServicesUpdateForm, self).clean()
        print(cleaned_data.get('service'))
        return cleaned_data

Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.


Note: Models in Django are written in PerlCase, not snake_case, so you might want to rename the model from Professionals_Services to ProfessionalService.


Note: normally a Django model is given a singular name, so Services instead of Service.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555