How to create an object for a Django model with a many to many field? How to create an object for a Django model with a many to many field? django django

How to create an object for a Django model with a many to many field?


You cannot create m2m relations from unsaved objects. If you have the pks, try this:

sample_object = Sample()sample_object.save()sample_object.users.add(1,2)

Update: After reading the saverio's answer, I decided to investigate the issue a bit more in depth. Here are my findings.

This was my original suggestion. It works, but isn't optimal. (Note: I'm using Bars and a Foo instead of Users and a Sample, but you get the idea).

bar1 = Bar.objects.get(pk=1)bar2 = Bar.objects.get(pk=2)foo = Foo()foo.save()foo.bars.add(bar1)foo.bars.add(bar2)

It generates a whopping total of 7 queries:

SELECT "app_bar"."id", "app_bar"."name" FROM "app_bar" WHERE "app_bar"."id" = 1SELECT "app_bar"."id", "app_bar"."name" FROM "app_bar" WHERE "app_bar"."id" = 2INSERT INTO "app_foo" ("name") VALUES ()SELECT "app_foo_bars"."bar_id" FROM "app_foo_bars" WHERE ("app_foo_bars"."foo_id" = 1  AND "app_foo_bars"."bar_id" IN (1))INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 1)SELECT "app_foo_bars"."bar_id" FROM "app_foo_bars" WHERE ("app_foo_bars"."foo_id" = 1  AND "app_foo_bars"."bar_id" IN (2))INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 2)

I'm sure we can do better. You can pass multiple objects to the add() method:

bar1 = Bar.objects.get(pk=1)bar2 = Bar.objects.get(pk=2)foo = Foo()foo.save()foo.bars.add(bar1, bar2)

As we can see, passing multiple objects saves one SELECT:

SELECT "app_bar"."id", "app_bar"."name" FROM "app_bar" WHERE "app_bar"."id" = 1SELECT "app_bar"."id", "app_bar"."name" FROM "app_bar" WHERE "app_bar"."id" = 2INSERT INTO "app_foo" ("name") VALUES ()SELECT "app_foo_bars"."bar_id" FROM "app_foo_bars" WHERE ("app_foo_bars"."foo_id" = 1  AND "app_foo_bars"."bar_id" IN (1, 2))INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 1)INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 2)

I wasn't aware that you can also assign a list of objects:

bar1 = Bar.objects.get(pk=1)bar2 = Bar.objects.get(pk=2)foo = Foo()foo.save()foo.bars = [bar1, bar2]

Unfortunately, that creates one additional SELECT:

SELECT "app_bar"."id", "app_bar"."name" FROM "app_bar" WHERE "app_bar"."id" = 1SELECT "app_bar"."id", "app_bar"."name" FROM "app_bar" WHERE "app_bar"."id" = 2INSERT INTO "app_foo" ("name") VALUES ()SELECT "app_foo_bars"."id", "app_foo_bars"."foo_id", "app_foo_bars"."bar_id" FROM "app_foo_bars" WHERE "app_foo_bars"."foo_id" = 1SELECT "app_foo_bars"."bar_id" FROM "app_foo_bars" WHERE ("app_foo_bars"."foo_id" = 1  AND "app_foo_bars"."bar_id" IN (1, 2))INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 1)INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 2)

Let's try to assign a list of pks, as saverio suggested:

foo = Foo()foo.save()foo.bars = [1,2]

As we don't fetch the two Bars, we save two SELECT statements, resulting in a total of 5:

INSERT INTO "app_foo" ("name") VALUES ()SELECT "app_foo_bars"."id", "app_foo_bars"."foo_id", "app_foo_bars"."bar_id" FROM "app_foo_bars" WHERE "app_foo_bars"."foo_id" = 1SELECT "app_foo_bars"."bar_id" FROM "app_foo_bars" WHERE ("app_foo_bars"."foo_id" = 1  AND "app_foo_bars"."bar_id" IN (1, 2))INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 1)INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 2)

And the winner is:

foo = Foo()foo.save()foo.bars.add(1,2)

Passing pks to add() gives us a total of 4 queries:

INSERT INTO "app_foo" ("name") VALUES ()SELECT "app_foo_bars"."bar_id" FROM "app_foo_bars" WHERE ("app_foo_bars"."foo_id" = 1  AND "app_foo_bars"."bar_id" IN (1, 2))INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 1)INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 2)


For future visitors, you can create an object and all of its m2m objects in 2 queries using the new bulk_create in django 1.4. Note that this is only usable if you don't require any pre or post-processing on the data with save() methods or signals. What you insert is exactly what will be in the DB

You can do this without specifying a "through" model on the field. For completeness, the example below creates a blank Users model to mimic what the original poster was asking.

from django.db import modelsclass Users(models.Model):    passclass Sample(models.Model):    users = models.ManyToManyField(Users)

Now, in a shell or other code, create 2 users, create a sample object, and bulk add the users to that sample object.

Users().save()Users().save()# Access the through model directlyThroughModel = Sample.users.throughusers = Users.objects.filter(pk__in=[1,2])sample_object = Sample()sample_object.save()ThroughModel.objects.bulk_create([    ThroughModel(users_id=users[0].pk, sample_id=sample_object.pk),    ThroughModel(users_id=users[1].pk, sample_id=sample_object.pk)])


Django 1.9
A quick example:

sample_object = Sample()sample_object.save()list_of_users = DestinationRate.objects.all()sample_object.users.set(list_of_users)