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

发布于 2021-01-29 16:43:25

我的模特:

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()

我知道这是错误的,但是我敢肯定您会实现我想做的。你会怎么做?

关注者
0
被浏览
49
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

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

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

    更新: 阅读了saverio的答案后,我决定对这个问题进行更深入的研究。这是我的发现。

    这是我最初的建议。它可以工作,但不是最佳选择。(注意:我使用Bar的是s和aFoo而不是Users和a 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)
    

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

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

    由于不获取两个Bars,因此保存了两个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)
    

    路过pks到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)
    


知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看