Jak dodać wiele obiektów do relacji ManyToMany jednocześnie w Django?

184

Opierając się na dokumencie Django, powinienem być w stanie przekazać wiele obiektów jednocześnie, aby zostać dodanym do relacji wielu narodowości, ale otrzymuję

* TypeError: unhashable type: 'list'

kiedy próbuję przekazać zestaw zapytań django rzucony na listę. Przekazywanie zestawu zapytań lub ValuesListQueryset również się nie udaje. Czy istnieje lepszy sposób niż użycie pętli for?

philgo20
źródło

Odpowiedzi:

320

Użyj: object.m2mfield.add(*items)zgodnie z opisem w dokumentacji :

add() akceptuje dowolną liczbę argumentów, a nie ich listę.

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

Aby rozwinąć tę listę do argumentów, użyj *

add(*[obj1, obj2, obj3])

Uzupełnienie:

Django nie woła obj.save()o każdy przedmiot, ale bulk_create()zamiast tego używa .

Yuji „Tomita” Tomita
źródło
Jeśli spojrzysz na menedżera, po prostu wykonuje pętlę for nad obiektami i zapisuje na nich wywołania.
Sam Dolan,
3
Krótki eksperyment z powłoki pokazuje, że @sdolan jest (obecnie) niepoprawny. To znaczy, kiedy patrzę na wygenerowany SQL, widzę tylko jedną instrukcję wstawiania: INSERT INTO app_one_twos (one_id, two_id) VALUES (1, 1), (1, 2), (1, 3), (1, 4); To jest w Django 1.4.
Klaas van Schelven
@KlaasvanSchelven: Nie pamiętam testowania tego za pomocą wygenerowanego kodu SQL, ale na podstawie mojego komentarza jestem prawie pewien, że rzuciłem okiem na kod źródłowy. Pamiętaj, że było to ponad 2 lata temu, więc mam nadzieję, że wszystko zostało nieco zoptymalizowane.
Sam Dolan
1
@sdolan Nie, nie poprawili tego. Właśnie to testowałem.
Saransh Mohapatra
1
Jakiś sposób to zrobić według wartości (np. Id)? Czasami nie masz listy obiektów, ale listę wartości obiektów (tj. Identyfikatorów)? Zamiast
zapętlać
48

Aby dodać, jeśli chcesz dodać je z zestawu zapytań

Przykład

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

Od: Wstaw wyniki zestawu zapytań do ManytoManyfield

Dr Manhattan
źródło
3
Wykonuje dwa zapytania zamiast jednego :(
DylanYoung
2
Pamiętaj, że nie musisz przekształcać go w listę. Po prostu dodaj bezpośrednio (* uprawnienia).
BjornW
34

Django 1.9 dodaje dodatkowe sposoby dodawania do relacji wiele do wielu.

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

set jest nową subtelnością:

>>> new_list = [obj1, obj2, obj3]
>>> e.related_set.set(new_list)
aero
źródło
2
setmyślę, że zawsze tam był. e.related_set = new_listTyle , że kiedyś działało poprzez przypisanie w starszych Djangos e.related_set.set(new_list). Właśnie zdali sobie sprawę, że „jawne jest lepsze niż niejawne”.
DylanYoung
1
Uwaga: Aby być zwięzłym, zestaw usuwa wszystkie istniejące skojarzone modele. Więc jeśli chcesz dodać listę nowych obiektów i chcesz zachować istniejący, nienaruszony, użyj add (* newlist)
Tasawar Hussain