What are the steps to make a ModelForm work with a ManyToMany relationship with an intermediary model in Django?

If you use the save method right now, Django will try to save using the manager (which Django doesn’t allow). Unfortunately, the behavior you want is a little bit trickier than what ModelForm does by default. What you need to do is create a formset.

First of all, you will need to change the options of your ClientForm so that it doesn’t display the groupes attribute.

class ClientForm(ModelForm):
    class Meta:
        model = Client
        exclude = ('groupes',)

Next, you must change the view to display the formset:

from django.forms.models import inlineformset_factory

def modifier(request, id):
    client = Client.objects.get(id=id)    
    form = ClientForm(instance = client)
    # Create the formset class
    GroupeFormset = inlineformset_factory(Client, Groupe)
    # Create the formset
    formset = GroupeFormset(instance = client)

    dict = {
        "form": form
        , "formset" : formset
        , "instance" : client
    }

    if request.method == "POST":
        form = ClientForm(request.POST, instance = client)
        formset = GroupeFormset(request.POST, instance = client)

        if form.is_valid() and formset.is_valid():
            client_mod = form.save()
            formset.save()

            id = client_mod.id
            return HttpResponseRedirect(
                "/client/%(id)s/?err=success" % {"id" : id}
            )
        else:
            return HttpResponseRedirect(
                "/client/%(id)s/?err=warning" % {"id" : id}
            )

    return render_to_response(
        "client/modifier.html"
        , dict
        , context_instance=RequestContext(request)
    )

And obviously, you must also tweak your template to render the formset.

If you need any other advice on formsets, see these articles:

Model formsets
Formsets

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)