20

i am working on saving on the same form two tables - having a m2m relation. I don't succeed, my error persists with something like: Cannot set values on a ManyToManyField which specifies an intermediary model. Use Membership's Manager instead where Membership is my 'through table'.

my code :

def save_classroom(request):
   classroom_instance = Classroom()
   if request.method == 'POST':
        form = ClassroomForm(request.POST, request.FILES, user = request.user) 
        if form.is_valid():
           new_obj = form.save(commit=False)
           new_obj.user = request.user 
           new_obj.save()
           membership = Membership(member = request.user,classroom=new_obj)
           membership.save() 
           form.save_m2m()
           return HttpResponseRedirect('.')    
   else:
           form = ClassroomForm(user = request.user)     
   return render_to_response('classroom/classroom_form.html', {
           'form': form,

           }, 
          context_instance=RequestContext(request))  

my models:

class Classroom(models.Model):
     user = models.ForeignKey(User, related_name = 'classroom_creator')
     classname = models.CharField(max_length=140, unique = True)
     date = models.DateTimeField(auto_now=True)
     open_class = models.BooleanField(default=True)
     members = models.ManyToManyField(User,related_name="list of invited members", through = 'Membership')

class Membership(models.Model): 
      accept = models.BooleanField(default=False)
      date = models.DateTimeField(auto_now = True) 
      classroom = models.ForeignKey(Classroom, related_name = 'classroom_membership')
      member = models.ForeignKey(User, related_name = 'user_membership')

where am i wrong?

tshepang
  • 12,111
  • 21
  • 91
  • 136
dana
  • 5,168
  • 20
  • 75
  • 116
  • i'm editing now, and adding the models. thanks! – dana Jun 22 '10 at 08:29
  • Related question has a solution at ORM level: http://stackoverflow.com/questions/22964448/add-for-manytomanyfield-which-specifies-an-intermediary-model – guettli Apr 09 '14 at 13:50
  • Seems this question asked twice, see my answer to the other: http://stackoverflow.com/a/40822731/2863603 – mehmet Nov 26 '16 at 20:24

5 Answers5

37

If you are allowed to modify class Membership, adding auto_created = True might solve your problem,

class Membership(models.Model): 
    class Meta:
        auto_created = True

In Django 1.7, the error message is changed to "Cannot set values on a ManyToManyField which specifies an intermediary model". The solution is the same.

NOTE: This will remove your intermediate model entirely, and all the additional fields with it.

MrE
  • 19,584
  • 12
  • 87
  • 105
Thomas - BeeDesk
  • 1,008
  • 1
  • 11
  • 12
  • 2
    This worked for me, but after resetting database, an strange error occur : relation "app_membership" does not exist –  Jul 24 '15 at 21:09
  • When I do a "makemigrations" after adding the "auto-created" attribute, django wants to remove my own intermediarry model class. Why ? – Raphael Dec 21 '15 at 10:00
  • 1
    @Raphael, it didn't happen to me. It might be another question entirely. (sorry my answer might not be very helpful, but I thought at least give you a quick reply.) – Thomas - BeeDesk Dec 22 '15 at 00:37
  • 5
    Django indeed removes intermediary model when using auto_created. Also, it is nowhere documented - so probably not wise to use it. – chhantyal Jan 27 '16 at 18:11
20

As seen on:

http://docs.djangoproject.com/en/dev/topics/db/models/#intermediary-manytomany

Unlike normal many-to-many fields, you can't use add, create, or assignment (i.e., beatles.members = [...]) to create relationships

I guess your code trips up on the line "form.save_m2m()", which is unnecessary since you already manually create a membership.

Klaas van Schelven
  • 2,374
  • 1
  • 21
  • 35
  • 3
    How are you able to save to a through table? I have a similar problem where save() does not work. – nlr25 Aug 27 '13 at 22:11
  • 2
    I am using Django 1.8 and after waisting almost 2 days nothing works. I have followed all the steps mentioned in the documentation(https://docs.djangoproject.com/en/1.8/topics/db/models/#intermediary-manytomany). But nothing work, So finally I have decided to remove M2M relationship from source Model(i.e Classroom in your case) and just created a intermediate Model. Now its working fine, I am able to save and retrieve the associated data. – Arjun Thakur Feb 24 '16 at 11:03
4

I had a similar error message on a different problem. I post it here just in case it helps others.

I've added a new ManyToManyField on an existing model. This model was used in a ModelForm built with an exclude field.

I fixed the problem by add the new field in the excluded ones.

luc
  • 41,928
  • 25
  • 127
  • 172
3

As of the Django 2.2 release you can also specify a through_defaults on the field in order to use add, create and assignment:

The RelatedManager.add(), create(), remove(), set(), get_or_create(), and update_or_create() methods are now allowed on many-to-many relationships with intermediate models. The new through_defaults argument is used to specify values for new intermediate model instance(s).

In your case since all the fields already have defaults it might just work in 2.2.

Cory
  • 22,772
  • 19
  • 94
  • 91
  • 1
    Any usage examples? Can't find it anywhere – Bren May 16 '19 at 13:06
  • 3
    there are some examples in this section of the docs: https://docs.djangoproject.com/en/2.2/topics/db/models/#extra-fields-on-many-to-many-relationships – Cory May 19 '19 at 06:20
0

As specified in https://docs.djangoproject.com/en/2.1/topics/db/models/

"Unlike normal many-to-many fields, you can’t use add(), create(), or set() to create relationships"

This means for django version 2.1 ,these methods are impossible. But the same page of django 2.2 documentation https://docs.djangoproject.com/en/2.2/topics/db/models/ tells that:

"You can also use add(), create(), or set() to create relationships, as long as you specify through_defaults for any required fields"

So just update the django to 2.2 or newer versions to use the above methods and while creating objects give the argument 'through_defaults' as a python dictionary with keys as your extra field names and values as their default values.

Krish
  • 1