Saltar al contenido

¿Cómo crear un objeto para un modelo Django con un campo de muchos a muchos?

Luego de de esta larga recopilación de datos hemos podido solucionar esta problema que pueden tener algunos de nuestros usuarios. Te compartimos la solución y nuestro objetivo es resultarte de mucha ayuda.

Solución:

No puede crear relaciones m2m a partir de objetos no guardados. si tienes el pkS, prueba esto:

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

Actualizar: Después de leer la respuesta de saverio, decidí investigar el tema un poco más a fondo. Aquí están mis hallazgos.

Esta fue mi sugerencia original. Funciona, pero no es óptimo. (Nota: estoy usando Bars y un Foo en lugar de Users y un SamplePero se entiende la idea).

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

Genera la friolera de 7 consultas:

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)

Estoy seguro de que podemos hacerlo mejor. Puede pasar varios objetos al add() método:

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

Como podemos ver, pasar varios objetos guarda uno 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)

No sabía que también puedes asignar una lista de objetos:

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

Desafortunadamente, eso crea una 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)

Intentemos asignar una lista de pks, como sugirió saverio:

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

Como no buscamos los dos Bars, salvamos dos SELECT declaraciones, resultando 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)

Y el ganador es:

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

Paso pks a add() nos da un total de 4 consultas:

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)

Para futuros visitantes, puede crear un objeto y todos sus objetos m2m en 2 consultas usando el nuevo crear_a granel en django 1.4. Tenga en cuenta que esto solo se puede usar si no necesita ninguna procesamiento previo o posterior de los datos con métodos o señales save(). Lo que inserta es exactamente lo que estará en la base de datos

Puede hacer esto sin especificar un modelo “a través” en el campo. Para completar, el siguiente ejemplo crea un modelo de Usuarios en blanco para imitar lo que preguntaba el cartel original.

from django.db import models

class Users(models.Model):
    pass

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

Ahora, en un shell u otro código, cree 2 usuarios, cree un objeto de muestra y agregue los usuarios de forma masiva a ese objeto de muestra.

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 ejemplo rápido:

sample_object = Sample()
sample_object.save()

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

Si aceptas, puedes dejar una reseña acerca de qué te ha impresionado de este ensayo.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *