ChatGPT解决这个技术问题 Extra ChatGPT

如何为具有多对多字段的 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()

我知道那是错误的,但我相信你会得到我想要做的。你会怎么做?


D
Daniel Holmes

您不能从未保存的对象创建 m2m 关系。如果您有 pk,请尝试以下操作:

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

更新:阅读 saverio's answer 后,我决定更深入地调查这个问题。这是我的发现。

这是我最初的建议。它有效,但不是最佳的。 (注意:我使用的是 BarFoo,而不是 UserSample,但你明白了)。

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)

按照 saverio 的建议,让我们尝试分配一个 pk 列表:

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)

我想补充一下,您可以像这样传递带有 * 的列表: foo.bars.add(*list) 它将列表分解为参数:D
这应该是关于 ManyToMany 的 Django 文档!比 docs.djangoproject.com/en/1.10/topics/db/examples/many_to_manydocs.djangoproject.com/en/1.10/ref/models/fields 更清晰,并且还包含不同方法的性能损失。也许你可以为 Django 1.9 更新它? (设置方法)
我想用一个以上的项目和数量保存具有单个 ID 的模型。有可能吗?? class Cart(models.Model): item = models.ForeignKey(Item, verbose_name="item") 数量 = models.PositiveIntegerField(default=1)
哇。我很惊讶。 :D
D
David Marble

对于未来的访问者,您可以使用 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)

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

我正在尝试使用您的答案 here,但我遇到了困难。想法?
很棒的解决方案!
嗯,这对我有用,但我必须将 bulk_create 中的属性设置为模型实例,而不是 ID。
S
Slipstream

Django 1.9 一个简单的例子:

sample_object = Sample()
sample_object.save()

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

r
rewritten

RelatedObjectManagers 是不同于模型中的字段的“属性”。实现您正在寻找的最简单的方法是

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)

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


i
iraj jelodari

您可以通过这种方式替换相关对象集(Django 1.9 中的新功能):

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

j
jopjk

如果有人想在自指 ManyToMany 字段上回答 David Marbles。通过模型的 id 被称为:“to_'model_name_id”和“from_'model_name'_id”。

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