Как добавить несколько объектов в отношения ManyToMany одновременно в Django?

185

Основываясь на Django Doc, я должен быть в состоянии передать несколько объектов одновременно, чтобы добавить их во многие отношения, но я получаю

* TypeError: unhashable тип: 'список'

когда я пытаюсь передать набор запросов Django, приведенный в списке. Передача Queryset или ValuesListQueryset, похоже, тоже не удалась. Есть ли лучший способ, чем использовать цикл for?

philgo20
источник

Ответы:

320

Использование: object.m2mfield.add(*items)как описано в документации :

add() принимает произвольное количество аргументов, а не их список.

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

Чтобы расширить этот список в аргументы, используйте *

add(*[obj1, obj2, obj3])

Приложение:

Джанго не вызывает obj.save()для каждого элемента, но использует bulk_create()вместо этого.

Юджи "Томита" Томита
источник
Если вы посмотрите на менеджера, он просто выполняет цикл for над объектами и сохраняет вызовы на них.
Сэм Долан
3
Короткий эксперимент из оболочки показывает, что @sdolan на самом деле (в настоящее время) неверен. Т.е. когда я смотрю на сгенерированный SQL, я вижу только один оператор вставки: INSERT INTO app_one_twos (one_id, two_id) VALUES (1, 1), (1, 2), (1, 3), (1, 4); Это в Django 1.4.
Клаас ван Шельвен
@KlaasvanSchelven: я не помню, чтобы тестировал это через сгенерированный sql, но, основываясь на моем комментарии, я почти уверен, что просто взглянул на исходный код. Имейте в виду, что это было более 2 лет назад, поэтому я надеюсь, что все было немного оптимизировано.
Сэм Долан
1
@sdolan Нет, они не улучшили его. Я просто проверял это.
Саранш Мохапатра
1
Любой способ сделать это по значению (например, идентификатор)? Иногда у вас нет списка объектов, но список значений объекта (т.е. идентификаторы)? Вместо того, чтобы перебирать и захватывать все объекты в другом списке ...
DannyMoshe
48

Чтобы добавить, если вы хотите добавить их из набора запросов

пример

# 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)

From: вставить результаты набора запросов в ManytoManyfield

Доктор манхэттен
источник
3
Это выполняет два запроса вместо одного :(
DylanYoung
2
Обратите внимание, что вам не нужно превращать его в список. Просто добавьте (* разрешения) напрямую.
BjornW
34

Django 1.9 добавляет дополнительные способы добавления отношений «многие ко многим».

Документация: https://docs.djangoproject.com/en/dev/ref/models/relations/#django.db.models.fields.related.RelatedManager.set

set это новая тонкость:

>>> new_list = [obj1, obj2, obj3]
>>> e.related_set.set(new_list)
аэро
источник
2
setя всегда был там Просто то, что раньше работало через назначение e.related_set = new_listв старых Djangos, эквивалентно e.related_set.set(new_list). Они просто поняли, что «явное лучше, чем неявное».
DylanYoung
1
Внимание: для краткости set удаляет все существующие связанные модели. Так что, если вы хотите добавить список новых объектов и хотите сохранить существующий в целости, используйте add (* newlist)
Tasawar Hussain