如何为具有多对多字段的Django模型创建对象?

Sai*_*hna 126 python django django-models django-orm

我的模特 - >

class Sample(models.Model):
    users = models.ManyToManyField(User)
Run Code Online (Sandbox Code Playgroud)

我想这样做(在这个模型中保存用户,user1和user2) - >

user1 = User.objects.get(pk=1)
user2 = User.objects.get(pk=2)
sample_object = Sample(users=user1, users=user2)
sample_object.save()
Run Code Online (Sandbox Code Playgroud)

我知道这是错的:D,但我确定你得到了我想要做的......所以你会怎么做?

Dan*_*per 221

您无法从未保存的对象创建m2m关系.如果你有pks,试试这个:

sample_object = Sample()
sample_object.save()
sample_object.users.add(1,2)
Run Code Online (Sandbox Code Playgroud)

更新:在阅读saverio的答案后,我决定更深入地研究这个问题.以下是我的发现.

这是我最初的建议.它有效,但不是最佳的.(注意:我使用pks而Bar不是Foos和a User,但你明白了).

bar1 = Bar.objects.get(pk=1)
bar2 = Bar.objects.get(pk=2)
foo = Foo()
foo.save()
foo.bars.add(bar1)
foo.bars.add(bar2)
Run Code Online (Sandbox Code Playgroud)

它产生了总共7个查询:

SELECT "app_bar"."id", "app_bar"."name" FROM "app_bar" WHERE "app_bar"."id" = 1
SELECT "app_bar"."id", "app_bar"."name" FROM "app_bar" WHERE "app_bar"."id" = 2
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))
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)
Run Code Online (Sandbox Code Playgroud)

我相信我们可以做得更好.您可以将多个对象传递给该Sample方法:

bar1 = Bar.objects.get(pk=1)
bar2 = Bar.objects.get(pk=2)
foo = Foo()
foo.save()
foo.bars.add(bar1, bar2)
Run Code Online (Sandbox Code Playgroud)

我们可以看到,传递多个对象可以节省一个add():

SELECT "app_bar"."id", "app_bar"."name" FROM "app_bar" WHERE "app_bar"."id" = 1
SELECT "app_bar"."id", "app_bar"."name" FROM "app_bar" WHERE "app_bar"."id" = 2
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)
Run Code Online (Sandbox Code Playgroud)

我不知道你也可以分配一个对象列表:

bar1 = Bar.objects.get(pk=1)
bar2 = Bar.objects.get(pk=2)
foo = Foo()
foo.save()
foo.bars = [bar1, bar2]
Run Code Online (Sandbox Code Playgroud)

不幸的是,这增加了一个SELECT:

SELECT "app_bar"."id", "app_bar"."name" FROM "app_bar" WHERE "app_bar"."id" = 1
SELECT "app_bar"."id", "app_bar"."name" FROM "app_bar" WHERE "app_bar"."id" = 2
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" = 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 (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)
Run Code Online (Sandbox Code Playgroud)

让我们尝试分配一个'pk'列表,正如saverio建议的那样:

foo = Foo()
foo.save()
foo.bars = [1,2]
Run Code Online (Sandbox Code Playgroud)

由于我们没有获取两个SELECTs,我们保存两个pk语句,总共产生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" = 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 (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)
Run Code Online (Sandbox Code Playgroud)

最终获胜者是:

foo = Foo()
foo.save()
foo.bars.add(1,2)
Run Code Online (Sandbox Code Playgroud)

Bars 传递给add()会给我们总共4个查询:

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)
Run Code Online (Sandbox Code Playgroud)

  • 我想补充一点,你可以传递一个带有*的列表,如:foo.bars.add(*list),它会将列表分解为参数:D (24认同)

小智 101

对于将来的访问者,您可以使用django 1.4中的新bulk_create2个查询中创建一个对象及其所有m2m对象.请注意,仅当您不需要使用save()方法或信号对数据进行任何预处理或后处理时,此选项才可用.您插入的内容正是数据库中的内容

您可以在不指定字段上的"直通"模型的情况下执行此操作.为了完整起见,下面的示例创建一个空白的用户模型,以模仿原始海报的要求.

from django.db import models

class Users(models.Model):
    pass

class Sample(models.Model):
    users = models.ManyToManyField(Users)
Run Code Online (Sandbox Code Playgroud)

现在,在shell或其他代码中,创建2个用户,创建一个示例对象,并将用户批量添加到该示例对象.

Users().save()
Users().save()

# Access the through model directly
ThroughModel = Sample.users.through

users = 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)
])
Run Code Online (Sandbox Code Playgroud)


Sli*_*eam 17

Django 1.9
一个简单的例子:

sample_object = Sample()
sample_object.save()

list_of_users = DestinationRate.objects.all()
sample_object.users.set(list_of_users)
Run Code Online (Sandbox Code Playgroud)


rew*_*ten 8

RelatedObjectManagers与Model中的字段不同,是"属性".实现您正在寻找的最简单的方法是

sample_object = Sample.objects.create()
sample_object.users = [1, 2]
Run Code Online (Sandbox Code Playgroud)

这与分配用户列表相同,没有其他查询和模型构建.

如果查询的数量令您烦恼(而不是简单),那么最佳解决方案需要三个查询:

sample_object = Sample.objects.create()
sample_id = sample_object.id
sample_object.users.through.objects.create(user_id=1, sample_id=sample_id)
sample_object.users.through.objects.create(user_id=2, sample_id=sample_id)
Run Code Online (Sandbox Code Playgroud)

这将有效,因为我们已经知道'用户'列表是空的,所以我们可以无意识地创建.