2009-07-28 1 views
13

Oto przykład:Jak zaktualizować pole manytomany w Django?

Jeśli mam tych klas

class Author(models.Model): 
    name = models.CharField(max_length=45) 

class Book(models.Model): 
    name = models.CharField(max_length=45) 
    authors = models.ManyToManyField(Author) 

W bazie danych mam jednego autora o nazwie „George” a drugi o nazwie „Georfe”. Ten ostatni to błąd. Tak więc chcę w każdej Księdze, która ma "Georfe" jako jeden z jego autorów, zastąpić ją autorem "George".

W języku SQL jest naprawdę łatwa do zrobienia. Jeśli id ​​= "George" i 3 "Georfe id" = 7 i nazwa tabeli relacja jest "author_book":

UPDATE author_book SET id=3 WHERE id=7; 

Czy to możliwe, aby to zrobić z Django ORM?

znalazłem sposób, aby to zrobić: I pętla koryta wszystkie związane z ksiąg literówka autora i zrobić:

book.authors.add(Author.objects.get(id=3)) 
book.authors.remove(Author.objects.get(id=7)) 

Ale nie uważam to rozwiązanie naprawdę elegancki i skuteczny. Czy istnieje rozwiązanie bez pętli?

+0

Jeśli jest to jednorazowa okazja do naprawienia niektórych literówek, dlaczego nie uruchomić po prostu surowego kodu SQL? – thedz

+0

Dane w bazie danych pochodzą ze starej bazy danych plików i istnieje wiele literówek do naprawienia. Nie chcę samodzielnie naprawiać wszystkich literówek. Chciałbym, żeby mój klient mógł to zrobić w interfejsie administratora. – Etienne

Odpowiedz

14

Uwaga: Ten kod będzie usunąć Zły autor "georfe", a także aktualizowanie książek, aby wskazać właściwego autora. Jeśli nie chcesz tego zrobić, użyj .remove() jako wzmianek o odpowiedziach @ jcdyer.

Czy możesz zrobić coś takiego?

george_author = Author.objects.get(name="George") 
for book in Book.objects.filter(authors__name="Georfe"): 
    book.authors.add(george_author.id) 
    book.authors.filter(name="Georfe").delete() 

Podejrzewam, że będzie to łatwiejsze, jeśli miał wyraźny tabelę łączącą dwa modele (z „do” arg słów kluczowych) - w tym przypadku, to masz dostęp do tabeli relacji bezpośrednio i mógł po prostu zrobić na nim .update(id=george_author.id).

+0

Zobacz mój komentarz do jcd, twoje rozwiązanie jest w większości takie samo (z wyjątkiem usuwania, ale nie chcę usunąć błędnie napisanego autora, więc usunięcie jest lepsze w moim przypadku). Myślę, że twoje rozwiązanie z aktualizacją jest właściwym sposobem na to. Akceptuję tę odpowiedź.Niestety mój przypadek jest bardziej złożony, ponieważ rozszerzam ModelAdmin i wszystko musi być ogólne i nie sądzę, abym mógł bezpośrednio korzystać z tabeli relacji (ponieważ nie mogłem być pewien, czy ustawiono "najmniejszy" argument). Mam rację? – Etienne

+0

W instancji można zobaczyć argument "through": book.authors.through = 'ModelName "- nie jestem jednak pewien, czy można uzyskać do niego dostęp za pośrednictwem samej klasy. –

+0

Sprawdzam argument "through" na instancji i jeśli nie jest on ustawiony w ManyToManyField, to jest to None. Tak więc na razie zachowam rozwiązanie pętli za pomocą polecenia dodaj/usuń (lub usuń). Może to nie jest eleganckie i wydajne, ale działa wystarczająco dobrze dla naszych potrzeb. Dzięki Ian, jcd – Etienne

14

Dzięki wygenerowanej automatycznie tabeli można wykonać dwustopniowe wstawianie i usuwanie, co jest dobre dla czytelności.

george = Author.objects.get(name='George') 
georfe = Author.objects.get(name='Georfe') 

book.authors.add(george) 
book.authors.remove(georfe) 
assert george in book.authors 

Jeśli masz wyraźnie określone przez tabeli (autorzy = models.ManyToManyField (Autor, poprzez = BookAuthors) Następnie można zmienić relacje wyraźnie na BookAuthor. Mało znany jest fakt, że ten model już istnieje, są generowane przez Django automatycznie. Zazwyczaj tylko powinny tworzyć wyraźne poprzez modelu jeśli masz dodatkowe dane, które chcesz zapisać (na przykład rozdziały dany autor napisał w książce multi-autor).

# This line is only needed without a custom through model. 
BookAuthor = Book.authors.through 
book_author = BookAuthor.objects.get(author=georfe, book=great_american_novel) 
book_author.author = george 
book_author.save() 
assert george in book.authors 
+0

Ha, piszemy głównie to samo w tym samym czasie! Ale nie wiedziałem o "wątkowym" argumencie ManyToManyField. Zajrzę do tego. Ale sposób, w jaki Ian wykorzystuje tabelę relacji z aktualizacją, wydaje się bardziej skuteczny. – Etienne