Wie erstelle ich ein Objekt für ein Django-Modell mit vielen zu vielen Feldern?


138

Mein Modell:

class Sample(models.Model):
    users = models.ManyToManyField(User)

Ich möchte beide user1und user2in diesem Modell speichern :

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

Ich weiß, dass das falsch ist, aber ich bin sicher, Sie bekommen, was ich tun möchte. Wie würdest du es machen ?

Antworten:


240

Sie können keine m2m-Beziehungen aus nicht gespeicherten Objekten erstellen. Wenn Sie das pks haben, versuchen Sie dies:

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

Update: Nachdem ich die Antwort des Sparers gelesen hatte , beschloss ich, das Problem etwas eingehender zu untersuchen. Hier sind meine Ergebnisse.

Dies war mein ursprünglicher Vorschlag. Es funktioniert, ist aber nicht optimal. (Hinweis: Ich verwende Bars und a Fooanstelle von Users und a Sample, aber Sie haben die Idee).

bar1 = Bar.objects.get(pk=1)
bar2 = Bar.objects.get(pk=2)
foo = Foo()
foo.save()
foo.bars.add(bar1)
foo.bars.add(bar2)

Es werden insgesamt 7 Abfragen generiert:

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)

Ich bin sicher, wir können es besser machen. Sie können mehrere Objekte an die add()Methode übergeben:

bar1 = Bar.objects.get(pk=1)
bar2 = Bar.objects.get(pk=2)
foo = Foo()
foo.save()
foo.bars.add(bar1, bar2)

Wie wir sehen können, spart das Übergeben mehrerer Objekte eines 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)

Mir war nicht bewusst, dass Sie auch eine Liste von Objekten zuweisen können:

bar1 = Bar.objects.get(pk=1)
bar2 = Bar.objects.get(pk=2)
foo = Foo()
foo.save()
foo.bars = [bar1, bar2]

Leider schafft das eine zusätzliche 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)

Versuchen wir, eine Liste von pks zuzuweisen , wie von saverio vorgeschlagen:

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

Da wir die beiden Bars nicht abrufen , speichern wir zwei SELECTAnweisungen, was insgesamt 5 ergibt:

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)

Und der Gewinner ist:

foo = Foo()
foo.save()
foo.bars.add(1,2)

Wenn Sie pks an übergeben add(), erhalten Sie insgesamt 4 Abfragen:

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)

25
Ich möchte hinzufügen, Sie können eine Liste mit * wie folgt übergeben: foo.bars.add (* Liste) und es wird die Liste in Argumente explodieren: D
— Guillermo Siliceo Trueba

1
Dies sollten die Django Docs über ManyToMany sein! viel klarer als docs.djangoproject.com/de/1.10/topics/db/examples/many_to_many oder docs.djangoproject.com/de/1.10/ref/models/fields und auch mit den Leistungseinbußen für die verschiedenen Methoden. Vielleicht können Sie es für Django 1.9 aktualisieren? (die eingestellte Methode)
— gabn88

Ich möchte ein Modell mit einer einzelnen ID mit mehr als einem Artikel und einer Menge speichern. Wird es möglich sein?? Klasse Cart (models.Model): item = models.ForeignKey (Item, verbose_name = "item") Quantität = models.PositiveIntegerField (Standard = 1)
— Nitesh

Beeindruckend. Ich bin erstaunt. : D
— М.Б.

111

Für zukünftige Besucher können Sie ein Objekt und alle seine m2m-Objekte in 2 Abfragen mit dem neuen Bulk_Create in Django 1.4 erstellen. Beachten Sie, dass dies nur verwendet werden kann, wenn Sie keine Vor- oder Nachbearbeitung der Daten mit save () -Methoden oder -Signalen benötigen . Was Sie einfügen, ist genau das, was in der DB sein wird

Sie können dies tun, ohne ein "Durch" -Modell auf dem Feld anzugeben. Der Vollständigkeit halber erstellt das folgende Beispiel ein leeres Benutzermodell, um die Anforderungen des Originalplakats nachzuahmen.

from django.db import models

class Users(models.Model):
    pass

class Sample(models.Model):
    users = models.ManyToManyField(Users)

Erstellen Sie nun in einer Shell oder einem anderen Code zwei Benutzer, erstellen Sie ein Beispielobjekt und fügen Sie die Benutzer in großen Mengen zu diesem Beispielobjekt hinzu.

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

Ich versuche hier Ihre Antwort zu verwenden , aber ich stecke fest. Gedanken?
— Pureferret

Es ist sicherlich informativ zu wissen, dass dies ohne eine explizit definierte Intermediate (through) -Klasse möglich ist. Aus Gründen der Lesbarkeit des Codes wird jedoch die Verwendung einer Intermediate-Klasse empfohlen. Siehe hier .
— colm.anseo

tolle Lösung!
— Pymarco

19

Django 1.9
Ein kurzes Beispiel:

sample_object = Sample()
sample_object.save()

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

8

RelatedObjectManager sind andere "Attribute" als Felder in einem Modell. Der einfachste Weg, um das zu erreichen, wonach Sie suchen, ist

sample_object = Sample.objects.create()
sample_object.users = [1, 2]

Dies entspricht dem Zuweisen einer Benutzerliste ohne die zusätzlichen Abfragen und die Modellbildung.

Wenn Sie die Anzahl der Abfragen stört (anstelle der Einfachheit), erfordert die optimale Lösung drei Abfragen:

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)

Dies funktioniert, da wir bereits wissen, dass die Benutzerliste leer ist, sodass wir sinnlos erstellen können.


2

Sie können den Satz verwandter Objekte auf diese Weise ersetzen (neu in Django 1.9):

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

0

Wenn jemand nach David Marbles sucht, antworten Sie auf ein selbstverweisendes ManyToMany-Feld. Die IDs des Durchgangsmodells heißen: "to_'model_name_id" und "from_'model_name'_id".

Wenn das nicht funktioniert, können Sie die Django-Verbindung überprüfen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.