Comment créer un objet pour un modèle Django avec beaucoup de champ?

mon modèle - >

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

je veux faire (enregistrer les utilisateurs, 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 mal : D, mais je suis sûr que vous obtenez ce que je veux faire...alors comment feriez-vous ?

107
demandé sur Serjik 2011-08-09 16:26:46

5 réponses

vous ne pouvez pas créer des relations m2m à partir d'objets non revêtus. Si vous avez les PK, essayez ceci:

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

mise à jour: après avoir lu la réponse de saverio , j'ai décidé d'étudier la question un peu plus en profondeur. Voici mes conclusions.

c'était ma suggestion originale. Il fonctionne, mais n'est pas optimal. (Note: j'utilise Bar s et un Foo au lieu 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 impressionnant 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 passer 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 voir, en passant plusieurs objets sauve 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 n'étais pas au courant 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 supplémentaire 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)

essayons d'attribuer une liste de `pk', comme saverio suggéré:

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

comme nous ne récupérons pas les deux Bar s, nous sauvons deux SELECT déclarations, résultant dans 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)

passant pk s à ajouter () nous donne un total de 4 questions:

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)
190
répondu Daniel Hepper 2017-05-23 12:18:36

pour les futurs visiteurs, vous pouvez créer un objet et tous ses objets m2m dans 2 requêtes en utilisant le nouveau bulk_create dans django 1.4. Notez que ceci n'est utilisable que si vous n'avez pas besoin de n'importe quel pré ou post-traitement sur les données avec les méthodes ou les signaux save (). Ce que vous insérez est exactement ce qui sera dans le DB

Vous pouvez le faire sans spécifier de "travers" modèle sur le champ. Pour être complet, l'exemple ci-dessous crée un modèle D'utilisateur vierge pour imiter ce que l'affiche originale demandait.

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éer 2 utilisateurs, créer un objet échantillon, et en vrac ajouter les utilisateurs à cet objet échantillon.

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)
])
86
répondu David Marble 2012-04-12 02:02:13

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)
11
répondu Slipstream 2016-06-22 15:59:37

RelatedObjectManagers sont différents" attributs " que les champs dans un Model. La façon la plus simple d'atteindre ce que vous recherchez est

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

c'est la même chose que d'attribuer une liste D'utilisateurs, sans les requêtes supplémentaires et la construction du modèle.

si le nombre de requêtes est ce qui vous dérange (au lieu de la simplicité), alors 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 marchera parce que nous nous savons déjà que la liste des "utilisateurs" est vide, donc nous pouvons créer sans réfléchir.

7
répondu rewritten 2011-08-10 10:06:48

vous pourriez remplacer l'ensemble des objets liés de cette façon (nouveau dans Django 1.9):

new_list = [user1, user2, user3]
sample_object.related_set.set(new_list)
1
répondu iraj jelodari 2016-02-28 21:42:57