Comment ajouter plusieurs objets à la relation ManyToMany à la fois dans Django?

185

Sur la base de la documentation Django, je devrais pouvoir passer plusieurs objets à la fois pour être ajouté à une relation manytomany mais j'obtiens un

* TypeError: type non détachable: 'list'

quand j'essaye de passer un jeu de requêtes django dans une liste. Le passage d'un Queryset ou d'un ValuesListQueryset semble également échouer. Y a-t-il un meilleur moyen que d'utiliser une boucle for?

philgo20
la source

Réponses:

320

Utilisation: object.m2mfield.add(*items)comme décrit dans la documentation :

add() accepte un nombre arbitraire d'arguments, pas une liste d'entre eux.

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

Pour développer cette liste en arguments, utilisez *

add(*[obj1, obj2, obj3])

Addenda:

Django n'appelle pas obj.save()pour chaque élément mais utilise à la bulk_create()place.

Yuji 'Tomita' Tomita
la source
Si vous regardez le gestionnaire, il fait juste une boucle for sur les objets et les appels sauvegardés sur eux.
Sam Dolan
3
Une courte expérience du shell montre que @sdolan est en fait (actuellement) incorrect. C'est-à-dire que lorsque je regarde le SQL généré, je ne vois qu'une seule instruction d'insertion: INSERT INTO app_one_twos (one_id, two_id) VALUES (1, 1), (1, 2), (1, 3), (1, 4); C'est dans Django 1.4.
Klaas van Schelven
@KlaasvanSchelven: Je ne me souviens pas avoir testé cela via le SQL généré, mais sur la base de mon commentaire, je suis à peu près sûr que j'ai juste jeté un coup d'œil au code source. Gardez à l'esprit que c'était il y a plus de 2 ans, j'espère donc que les choses ont été un peu optimisées.
Sam Dolan
1
@sdolan Non, ils ne l'ont pas amélioré. J'étais juste en train de le tester.
Saransh Mohapatra
1
Un moyen de le faire par valeur (ex. Id)? Parfois, vous n'avez pas une liste d'objets mais une liste de valeurs d'objet (ie id)? Plutôt que de faire une boucle et de saisir tous les objets dans une autre liste ...
DannyMoshe
48

Pour ajouter, si vous souhaitez les ajouter à partir d'un ensemble de requêtes

Exemple

# 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: insérer les résultats de l'ensemble de requêtes dans ManytoManyfield

Dr Manhattan
la source
3
Cela effectue deux requêtes au lieu d'une :(
DylanYoung
2
Notez que vous n'avez pas besoin de le transformer en liste. Ajoutez simplement (* permissions) directement.
BjornW le
34

Django 1.9 ajoute des moyens supplémentaires pour ajouter à une relation plusieurs-à-plusieurs.

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

set est une nouvelle finesse:

>>> new_list = [obj1, obj2, obj3]
>>> e.related_set.set(new_list)
aéro
la source
2
setétait toujours là je pense. C'est juste que cela fonctionnait par affectation e.related_set = new_listdans les Djangos plus anciens e.related_set.set(new_list). Ils se sont juste rendu compte que «l'explicite vaut mieux que l'implicite».
DylanYoung
1
Attention: pour être concis, set supprime tous les modèles associés existants. Donc, si vous voulez ajouter une liste de nouveaux objets et que vous voulez garder l'existant intact, utilisez add (* newlist)
Tasawar Hussain