pythondjangodjango-ormmanytomanyfield

Direct assignment to the forward side of a many-to-many set is prohibited. Use emails_for_help.set() instead


I am new to Django and didn't find any reference regarding this issue. I am getting this error when i use many to many field in Django model (models.py). I guess the issue is assigning m2m field in view(views.py) from form(forms.py).

How to assign m2m field in view? (Django version 2.0, python - 3.5)

models.py

class User(AbstractUser):
 username=models.CharField(max_length=20)
 email = models.EmailField(_('email address'), unique=True)


class Setupuser(models.Model):
 organization=models.CharField(max_length=200,blank=False,null=True)
 emails_for_help = models.ManyToManyField(User)

views.py

class Set_user(FormView):
 template_name="pkm_templates/set_up_user.html"
 form_class = Set_User_Form
 success_url = '/thanks/'

 def form_valid(self, form):
    org = form.cleaned_data.get('organization')
    emails = form.cleaned_data.get("emails_for_help")
    instance = Setupuser(organization=org,emails_for_help=emails)
    instance.save()
    return redirect("/")

forms.py

class Set_User_Form(ModelForm):
  emails_for_help = forms.ModelMultipleChoiceField(
    queryset=User.objects.all(),
    widget=forms.CheckboxSelectMultiple
  )

  class Meta:
    model = Setupuser
    fields = ["organization","emails_for_help"]

Solution

  • You need to get the User object and then add it to emails_for_help field. You can't add an object to ManyToManyField when creating an instance. Have a look at the doc.

    class Set_user(FormView):
        template_name="pkm_templates/set_up_user.html"
        form_class = Set_User_Form
        success_url = '/thanks/'
    
        def form_valid(self, form):
            org = form.cleaned_data.get('organization')
            emails = form.cleaned_data.get("share_email_with")
    
            users = User.objects.filter(email__in=emails)
            instance = Setupuser.objects.create(organization=org)
    
            for user in users:
                instance.emails_for_help.add(user)
    
            return redirect("/")
    

    Another way of doing this is to use .set().

    class Set_user(FormView):
        template_name="pkm_templates/set_up_user.html"
        form_class = Set_User_Form
        success_url = '/thanks/'
    
        def form_valid(self, form):
            org = form.cleaned_data.get('organization')
            emails = form.cleaned_data.get("share_email_with")
    
            users = User.objects.filter(email__in=emails)
            instance = Setupuser.objects.create(organization=org)
    
            instance.emails_for_help.set(users)
    
            return redirect("/")
    

    Or you can simply use .add() to add arbitrary number of objects.

    class Set_user(FormView):
        template_name="pkm_templates/set_up_user.html"
        form_class = Set_User_Form
        success_url = '/thanks/'
    
        def form_valid(self, form):
            org = form.cleaned_data.get('organization')
            emails = form.cleaned_data.get("share_email_with")
    
            users = User.objects.filter(email__in=emails)
            instance = Setupuser.objects.create(organization=org)
    
            instance.emails_for_help.add(*users)
    
            return redirect("/")