Mon modele:
class Sample(models.Model):
users = models.ManyToManyField(User)
Je veux sauver les deux user1
et user2
dans ce modèle:
user1 = User.objects.get(pk=1)
user2 = User.objects.get(pk=2)
sample_object = Sample(users=user1, users=user2)
sample_object.save()
Je sais que c'est faux, mais je suis sûr que vous obtenez ce que je veux faire. Comment le ferais-tu?
Vous ne pouvez pas créer de relations m2m à partir d'objets non sauvegardés. Si vous avez le pk
s, essayez ceci:
sample_object = Sample()
sample_object.save()
sample_object.users.add(1,2)
Mise à jour: Après avoir lu le réponse de saverio , j'ai décidé d'examiner le problème un peu plus en profondeur. Voici mes conclusions.
C'était ma suggestion initiale. Cela fonctionne, mais n'est pas optimal. (Remarque: j'utilise Bar
s et un Foo
à la place de User
s et un Sample
, mais vous voyez l'idée).
bar1 = Bar.objects.get(pk=1)
bar2 = Bar.objects.get(pk=2)
foo = Foo()
foo.save()
foo.bars.add(bar1)
foo.bars.add(bar2)
Il génère un total énorme de 7 requêtes:
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)
Je suis sûr que nous pouvons faire mieux. Vous pouvez transmettre plusieurs objets à la méthode add()
:
bar1 = Bar.objects.get(pk=1)
bar2 = Bar.objects.get(pk=2)
foo = Foo()
foo.save()
foo.bars.add(bar1, bar2)
Comme nous pouvons le constater, passer plusieurs objets enregistre un 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)
Je ne savais pas que vous pouvez aussi assigner une liste d'objets:
bar1 = Bar.objects.get(pk=1)
bar2 = Bar.objects.get(pk=2)
foo = Foo()
foo.save()
foo.bars = [bar1, bar2]
Malheureusement, cela crée un SELECT
supplémentaire:
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)
Essayons d’attribuer une liste de pk
s, comme suggéré par saverio:
foo = Foo()
foo.save()
foo.bars = [1,2]
Comme nous ne récupérons pas les deux Bar
s, nous sauvegardons deux instructions SELECT
, ce qui donne un total de 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)
Et le gagnant est:
foo = Foo()
foo.save()
foo.bars.add(1,2)
Passer pk
s à add()
nous donne un total de 4 requêtes:
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)
Pour les futurs visiteurs, vous pouvez créer un objet et tous ses objets m2m en 2 requêtes à l'aide de la nouvelle - bulk_create dans Django 1.4. Notez que cela n’est utilisable que si vous n’avez pas besoin de aucun pré ou post-traitement des données avec des méthodes ou des signaux save (). Ce que vous insérez est exactement ce qui sera dans la base de données
Vous pouvez le faire sans spécifier de modèle "à travers" sur le terrain. Par souci d'exhaustivité, l'exemple ci-dessous crée un modèle Utilisateurs vierge pour imiter ce que demandait l'affiche originale.
from Django.db import models
class Users(models.Model):
pass
class Sample(models.Model):
users = models.ManyToManyField(Users)
Maintenant, dans un shell ou un autre code, créez 2 utilisateurs, créez un exemple d'objet et ajoutez en bloc les utilisateurs à cet exemple d'objet.
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
Un exemple rapide:
sample_object = Sample()
sample_object.save()
list_of_users = DestinationRate.objects.all()
sample_object.users.set(list_of_users)
Les gestionnaires associés sont des "attributs" différents des champs d'un modèle. Le moyen le plus simple de réaliser ce que vous recherchez est
sample_object = Sample.objects.create()
sample_object.users = [1, 2]
Cela revient à assigner une liste d'utilisateurs, sans les requêtes supplémentaires et la construction du modèle.
Si le nombre de requêtes vous dérange (au lieu de la simplicité), la solution optimale nécessite trois requêtes:
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)
Cela fonctionnera, car nous savons déjà que la liste des utilisateurs est vide et que nous pouvons créer sans réfléchir.
Vous pouvez remplacer l'ensemble d'objets liés de cette manière (nouveauté Django 1.9):
new_list = [user1, user2, user3]
sample_object.related_set.set(new_list)