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

Python 如何为具有多对多字段的Django模型创建对象?,python,django,django-models,django-orm,Python,Django,Django Models,Django Orm,我的模型: class Sample(models.Model): users = models.ManyToManyField(User) 我想在该模型中保存user1和user2: user1 = User.objects.get(pk=1) user2 = User.objects.get(pk=2) sample_object = Sample(users=user1, users=user2) sample_object.save() 我知道这是错误的,但我相信你会得到我想

我的模型:

class Sample(models.Model):
    users = models.ManyToManyField(User)
我想在该模型中保存
user1
user2

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

我知道这是错误的,但我相信你会得到我想要做的。您将如何执行此操作?

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

sample_object = Sample()
sample_object.save()
sample_object.users.add(1,2)
更新:在阅读了之后,我决定更深入地调查这个问题。以下是我的发现

这是我最初的建议。这是可行的,但不是最优的。(注意:我使用的是
Bar
s和
Foo
,而不是
User
s和
Sample
,但你明白了)

它总共生成了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)
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)
我不知道您还可以指定对象列表:

bar1 = Bar.objects.get(pk=1)
bar2 = Bar.objects.get(pk=2)
foo = Foo()
foo.save()
foo.bars = [bar1, bar2]
不幸的是,这会创建一个额外的
选择

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)
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
s列表,正如saverio建议的那样:

foo = Foo()
foo.save()
foo.bars = [1,2]
由于我们不获取两个
s,我们保存了两个
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
s传递到
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与模型中的字段是不同的“属性”。实现你想要的最简单的方法是

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中新的2查询中创建一个对象及其所有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)
])

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


Django 1.9
一个简单的例子:

sample_object = Sample()
sample_object.save()

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

如果有人想做大卫·马布里斯(David Marbles)的作品,请回答一个自我介绍的许多领域。直通模型的ID称为: “至模型名称”和 “来自型号名称”“id”


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

我想添加,你可以传递一个带有*like so:foo.bar.add(*list)的列表,它会将列表分解成参数:d这应该是django文档关于很多!更清楚,然后或,还包括不同方法的性能惩罚。也许你可以为Django 1.9更新它?(set方法)我想用一个id保存模型,该id包含多个项目和数量。有可能吗??类购物车(models.Model):item=models.ForeignKey(item,verbose_name=“item”)quantity=models.PositiveIntegerField(默认值=1)Wow。我很惊讶我想用你的答案,但我被卡住了。想法?知道没有明确定义的中间(通过)类就可以做到这一点当然是有益的,但是,为了代码可读性,建议使用中间类。看,这是一个很棒的解决方案!嗯,这对我来说很有效,但我必须在批量创建中将属性设置为模型实例,而不是ID。