Nie można tworzyć relacji m2m z niezapisanych obiektów. Jeśli masz pk
, spróbuj tego:
sample_object = Sample()
sample_object.save()
sample_object.users.add(1,2)
Aktualizacja: po przeczytaniu odpowiedzi saverio , postanowiłem dokładniej zbadać problem. Oto moje ustalenia.
To była moja pierwotna sugestia. Działa, ale nie jest optymalne. (Uwaga: używam Bar
s i a Foo
zamiast User
si a Sample
, ale masz pomysł).
bar1 = Bar.objects.get(pk=1)
bar2 = Bar.objects.get(pk=2)
foo = Foo()
foo.save()
foo.bars.add(bar1)
foo.bars.add(bar2)
Generuje aż 7 zapytań:
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)
Jestem pewien, że możemy zrobić lepiej. Do add()
metody można przekazać wiele obiektów :
bar1 = Bar.objects.get(pk=1)
bar2 = Bar.objects.get(pk=2)
foo = Foo()
foo.save()
foo.bars.add(bar1, bar2)
Jak widać, przekazanie wielu obiektów oszczędza jeden 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)
Nie wiedziałem, że możesz również przypisać listę obiektów:
bar1 = Bar.objects.get(pk=1)
bar2 = Bar.objects.get(pk=2)
foo = Foo()
foo.save()
foo.bars = [bar1, bar2]
Niestety, tworzy to jeden dodatkowy 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)
Spróbujmy przypisać listę pk
s, jak zasugerował saverio:
foo = Foo()
foo.save()
foo.bars = [1,2]
Ponieważ nie pobieramy dwóch Bar
s, zapisujemy dwie SELECT
instrukcje, w sumie 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)
A zwycięzcą jest:
foo = Foo()
foo.save()
foo.bars.add(1,2)
Przekazanie pk
s do add()
daje w sumie 4 zapytania:
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)
Dla przyszłych odwiedzających możesz utworzyć obiekt i wszystkie jego obiekty m2m w 2 zapytaniach, używając nowego bulk_create w django 1.4. Zauważ, że jest to użyteczne tylko wtedy, gdy nie potrzebujesz żadnego przetwarzania wstępnego lub końcowego na danych za pomocą metod lub sygnałów save (). To, co wstawisz, jest dokładnie tym, co będzie w DB
Możesz to zrobić bez określania modelu „przez” na polu. Aby uzyskać kompletność, poniższy przykład tworzy pusty model użytkowników, aby naśladować to, o co prosił oryginalny plakat.
Teraz w powłoce lub innym kodzie utwórz 2 użytkowników, utwórz przykładowy obiekt i zbiorczo dodaj użytkowników do tego przykładowego obiektu.
źródło
Django 1.9
Szybki przykład:
źródło
RelatedObjectManagers to inne „atrybuty” niż pola w modelu. Najprostszym sposobem na osiągnięcie tego, czego szukasz, jest
To to samo, co przypisanie listy użytkowników, bez dodatkowych zapytań i budowania modelu.
Jeśli przeszkadza Ci liczba zapytań (zamiast prostoty), to optymalne rozwiązanie wymaga trzech zapytań:
To zadziała, ponieważ już wiemy, że lista „użytkowników” jest pusta, więc możemy bezmyślnie tworzyć.
źródło
W ten sposób można zamienić zestaw powiązanych obiektów (nowość w Django 1.9):
źródło
Jeśli ktoś chce zrobić David Marbles, odpowiedz na pole ManyToMany, które odsyła do siebie. Identyfikatory modelu przelotowego nazywane są: „to_'model_name_id” i „from_'model_name'_id”.
Jeśli to nie zadziała, możesz sprawdzić połączenie django.
źródło