Q:

Select2 widget for ManyToMany relationship

I am using django-select2 widgets to get a nicer ManyToMany widget. The view looks nicer and I can search and select many different users. However, when I save the model, the list of allowed users is blank. I tried this direct from the view and from a unit tests. I cannot fathom what I am doing wrong.
Any idea?
I strongly suspect there are two problems: One in the saving of the form created model and another one is how I set the data dictionary values.

My Model:
class ProjectCode(models.Model):
    allowed_users = models.ManyToManyField(User, blank=True)

My Form:
class ProjectCodeForm(forms.ModelForm):
    class Meta:
        model = ProjectCode
        fields = '__all__'
        widgets = {'allowed_users': Select2MultipleWidget(), }

My View:
def create_code(request):
    context = {}
    if request.method == 'POST':
        form = ProjectCodeForm(request.POST)
        if form.is_valid():
            new = form.save(commit=False)
            new.save()
    form = ProjectCodeForm()
    context['form'] = form
    return render(request, 'order_book/create_code.html', context)

My Test:
def test_code_create_post_pass(self):
    init = len(ProjectCode.objects.all())
    data = {'allowed_users': [self.jdoe.pk, ], }
    response = self.client.post('/order_book/code/create/', data)
    self.assertEqual(response.status_code, 200)
    self.assertContains(response, "Create a project code")
    self.assertEqual(init + 1, len(ProjectCode.objects.all()))
    self.assertEqual("jdoe", ProjectCode.objects.all()[0].display_allowed_users())

This tests fails with:
======================================================================
FAIL: test_code_create_post_pass (order_book.tests.AuthorisedViewTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/path/tests.py", line 385, in test_code_create_post_pass
    "jdoe", ProjectCode.objects.all()[0].display_allowed_users())
AssertionError: u'jdoe' != u''
- jdoe
+

A:

Post question, continue to play with pdb and an idea pops up… I modified the view to:
def create_code(request):
    context = {}
    if request.method == 'POST':
        form = ProjectCodeForm(request.POST)
        if form.is_valid():
            new = form.save(commit=False)
            # I am doing "clever" things with the new object here,
            # like setting a creator and created date/time…
            new.save()
            for usr in form.cleaned_data['allowed_users']:  # ← This is NOT correct
                new.allowed_users.add(usr)  # ← Neither is this…
    form = ProjectCodeForm()
    context['form'] = form
    return render(request, 'order_book/create_code.html', context)

I was getting an error saying that allowed_users could not be set as new did not have a primary key yet. This lead me to try to set the data after having saved the object.
Note that the test data is correct and that the test now passes fine.
Alternatively, you could read the freaking documentation and use the correct method save_m2m():
def create_code(request):
    context = {}
    if request.method == 'POST':
        form = ProjectCodeForm(request.POST)
        if form.is_valid():
            new = form.save(commit=False)
            # I am doing "clever" things with the new object here,
            # like setting a creator and created date/time…
            new.save()
            form.save_m2m()  # ← This is CORRECT!
    form = ProjectCodeForm()
    context['form'] = form
    return render(request, 'order_book/create_code.html', context)

