¿Cómo agregar múltiples objetos a la relación ManyToMany a la vez en Django?

185

Basado en el documento de Django, debería poder pasar varios objetos a la vez para agregarlos a una relación de muchas personas, pero obtengo un

* TypeError: tipo no compartible: 'lista'

cuando intento pasar un conjunto de consultas django incluido en una lista. Pasar un Queryset o un ValuesListQueryset también parece fallar. ¿Hay una mejor manera que usar un bucle for?

philgo20
fuente

Respuestas:

320

Uso: object.m2mfield.add(*items)como se describe en la documentación :

add() acepta un número arbitrario de argumentos, no una lista de ellos.

add(obj1, obj2, obj3, ...)

Para expandir esa lista en argumentos, use *

add(*[obj1, obj2, obj3])

Apéndice:

Django no llama obj.save()a cada elemento, sino que lo utiliza bulk_create().

Yuji 'Tomita' Tomita
fuente
Si observa el administrador, solo realiza un bucle for sobre los objetos y las llamadas se guardan en ellos.
Sam Dolan
3
Un breve experimento del shell muestra que @sdolan es de hecho (actualmente) incorrecto. Es decir, cuando miro el SQL generado, solo veo una sola instrucción de inserción: INSERT INTO app_one_twos (one_id, two_id) VALUES (1, 1), (1, 2), (1, 3), (1, 4); Esto está en Django 1.4.
Klaas van Schelven
@KlaasvanSchelven: No recuerdo haber probado esto a través del sql generado, pero según mi comentario, estoy bastante seguro de que eché un vistazo al código fuente. Tenga en cuenta que esto fue hace más de 2 años, por lo que espero que las cosas se hayan optimizado un poco.
Sam Dolan
1
@sdolan No, no lo han mejorado. Solo lo estaba probando.
Saransh Mohapatra
1
¿Alguna forma de hacer esto por valor (ej. Id)? A veces no tienes una lista de objetos sino una lista de valores de objetos (es decir, id). En lugar de recorrer y agarrar todos los objetos en otra lista ...
DannyMoshe
48

Para agregar, si desea agregarlos desde un conjunto de consultas

Ejemplo

# Returns a queryset
permissions = Permission.objects.all()

# Add the results to the many to many field (notice the *)

group = MyGroup.objects.get(name='test')

group.permissions.add(*permissions)

De: Insertar resultados del conjunto de consultas en ManytoManyfield

Dr. Manhattan
fuente
3
Esto realiza dos consultas en lugar de una :(
DylanYoung
2
Tenga en cuenta que no necesita convertirlo en una lista. Simplemente agregue (* permisos) directamente.
BjornW
34

Django 1.9 agrega formas adicionales para agregar a una relación de muchos a muchos.

Documentación: https://docs.djangoproject.com/en/dev/ref/models/relations/#django.db.models.fields.related.RelatedManager.set

set es un nuevo detalle:

>>> new_list = [obj1, obj2, obj3]
>>> e.related_set.set(new_list)
aero
fuente
2
setSiempre estuvo allí, creo. Es solo que solía funcionar a través de la asignación e.related_set = new_listen Djangos más antiguos e.related_set.set(new_list). Se dieron cuenta de que "explícito es mejor que implícito".
DylanYoung
1
Precaución: para ser conciso, set elimina todos los modelos asociados existentes. Entonces, si desea agregar una lista de nuevos objetos y desea mantener intacto el existente, use add (* newlist)
Tasawar Hussain