Моя модель →
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()
Я знаю это неправильно: D, но я уверен, что вы получите то, что я хочу сделать... так как бы вы это сделали?
Вы не можете создавать отношения m2m из несохраненных объектов. Если у вас есть pks, попробуйте следующее:
sample_object = Sample()
sample_object.save()
sample_object.users.add(1,2)
Обновление:. После прочтения saverio answer я решил изучить проблему более подробно. Вот мои выводы.
Это было мое первоначальное предложение. Он работает, но не оптимален. (Примечание. Я использую Bar
и Foo
вместо User
и 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
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
для добавления() дает нам в общей сложности 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)
Для будущих посетителей вы можете создать объект и все его объекты m2m в 2 запросах, используя новый bulk_create в django 1.4. Обратите внимание, что это можно использовать только в том случае, если вы не требуете предварительной или последующей обработки данных с помощью методов или сигналов save(). То, что вы вставляете, именно то, что будет в DB
Вы можете сделать это без указания модели "через" в поле. Для полноты приведенный ниже пример создает пустую модель "Пользователи" для имитации того, что запрашивал оригинальный плакат.
from django.db import models
class Users(models.Model):
pass
class Sample(models.Model):
users = models.ManyToManyField(Users)
Теперь, в оболочке или другом коде, создайте 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
Быстрый пример:
sample_object = Sample()
sample_object.save()
list_of_users = DestinationRate.objects.all()
sample_object.users.set(list_of_users)
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.9):
new_list = [user1, user2, user3]
sample_object.related_set.set(new_list)