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

我的模式是:

class Sample(models.Model):
users = models.ManyToManyField(User)

我想在这个模型中同时保存 user1user2:

user1 = User.objects.get(pk=1)
user2 = User.objects.get(pk=2)
sample_object = Sample(users=user1, users=user2)
sample_object.save()

我知道这样不对,但我相信你会得到我想要的,你会怎么做?

119413 次浏览

不能从未保存的对象创建 m2m 关系:

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

更新: 在阅读了 Saverio 的回答之后,我决定对这个问题进行更深入的研究。

这是我最初的建议。有用,但不是最佳选择。(注意: 我使用的是 Bars 和 Foo,而不是 Users 和 Sample,不过你应该明白我的意思)。

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

它总共生成了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)

我相信我们可以做得更好。你可以将多个对象传递给 add()方法:

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

正如我们所看到的,传递多个对象可以节省一个 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"."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)

我不知道还可以分配对象列表:

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

不幸的是,这又增加了一个 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)

让我们试着分配一个 pk列表,就像 saverio 建议的那样:

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

由于我们没有获取这两个 Bar,所以我们保存了两个 SELECT语句,结果总共是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)

获胜者是:

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

pk传递给 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)

RelatedObjectManager 与 Model 中的字段是不同的“属性”

sample_object = Sample.objects.create()
sample_object.users = [1, 2]

这与分配用户列表相同,不需要额外的查询和模型构建。

如果查询的数量是困扰您的问题(而不是简单性) ,那么最佳解决方案需要三个查询:

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)

这将工作,因为我们已经知道“用户”列表是空的,所以我们可以无意识地创建。

对于未来的访问者,您可以使用 django 1.4中的新 < a href = “ https://docs.djangoproject.com/en/dev/ref/model/querysets/# django.db.mods.query.QuerySet.mass _ create”rel = “ norefrer”> mass _ create 2个查询中创建一个对象及其所有 m2m 对象。请注意,只有在不需要使用 save ()方法或信号对数据进行 任何预处理或后处理时,才能使用这种方法。插入的内容与数据库中的内容完全一致

不需要在字段中指定“ through”模型即可完成此操作。为了完整起见,下面的例子创建了一个空白的用户模型来模仿原始海报的要求。

from django.db import models


class Users(models.Model):
pass


class Sample(models.Model):
users = models.ManyToManyField(Users)

现在,在 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)
])

您可以用这种方式替换相关对象集(Django 1.9中的 new) :

new_list = [user1, user2, user3]
sample_object.related_set.set(new_list)

姜戈1.9
举个简单的例子:

sample_object = Sample()
sample_object.save()


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

如果有人正在寻找做大卫大理石答案在一个自我参考许多领域。贯穿模型的 id 称为: “ to _‘ model _ name _ id”和 “ from _‘ model _ name’_ id”。

如果这不起作用,您可以检查 django 连接。