2010-11-06 7 views
8

Załóżmy, że każdy z nich jest Projecthas_manyTasks.Zapisz zmiany TYLKO w skojarzeniu has_many po pomyślnym zapisaniu obiektu nadrzędnego?

Jeśli robię

some_project.tasks = list_of_tasks 
some_project.save 

zadania Projektu aktualizowane nawet jeśli Zapisz się niepowodzeniem. Jeśli list_of_tasks składa się z nowych rekordów, zadania projektu zostaną usunięte usunięte, nawet jeśli zapis nie powiedzie się! WHOA!

Jeśli zapis zakończy się niepowodzeniem, projekt powinien mieć takie same zadania, jakie miał, zanim zacząłem z nim mieszać. Jak uzyskać to zachowanie i dlaczego nie jest ono domyślne?

Odpowiedz

8

załączenie oświadczenia w transakcji:

Project.transaction do 
    p.tasks = task_list 
    p.save! 
end 

Sposób save! zgłasza wyjątek momencie błędu, który wycofuje wszelkie zmiany wykonywane na liście zadań.

Możesz przeczytać documentation, jeśli chcesz zanurkować nieco głębiej na temat.

+0

To działa, ale to podejście oznacza, że ​​za każdym razem, gdy aktualizuję zadania w całej mojej aplikacji, muszę pamiętać, aby zawrzeć transakcję (i jestem pewna, że ​​zapomnę). Byłoby znacznie lepiej, gdybym mógł zamknąć to zachowanie w modelu 'Projekt' w sposób DRY. –

+4

Zdefiniuj metodę 'replace_tasks (new_tasks)' która właśnie to – glebm

0

Zanim zadzwonisz pod numer #save, możesz zapytać, czy some_project#valid?. Pomaga to w rozwiązaniu problemu, jeśli zapis nie powiedzie się, ponieważ some_project jest nieprawidłowym rekordem, ale nie jest rozwiązaniem kompleksowym.

Jeśli chodzi o domyślne zachowanie Railsów, ma to sens. Powiedzenie some_project.tasks = list_of_tasks jest jak powiedzenie "usuń wszystkie istniejące zadania z some_project i przypisz te nowe". Opuszczasz odwołanie do istniejącego powiązania Array i przypisujesz nowe. Znajduje to odzwierciedlenie w ActiveRecord w DB.

+1

To NIE DZIAŁA. 'some_project.tasks = list_of_tasks' zdmuchuje istniejącą listę zadań zaraz po jej wywołaniu, bez względu na to, kiedy i czy nazywam' some_project.save'. Naprawdę nie mogę uwierzyć, że to domyślne zachowanie - to naprawdę sprzeczne z intuicją! –

+1

"Powiedzenie' some_project.tasks = list_of_tasks' jest jak powiedzenie "usuń wszystkie istniejące zadania z some_project i przypisz te nowe" ". Myślę, że to raczej powiedzenie "usuń wszystkie istniejące zadania z' some_project' i przypisz te nowe * kiedy zapiszę 'some_project' *.' Some_project.name = "cokolwiek" 'nie będzie działało dopóki nie zapiszę, nie powinno też" some_project.tasks = list_of_tasks' –

+0

@HoraceLoeb: inna bestia, inne zasady.IRLC, większość ORM postępuje zgodnie z tym rozumowaniem, ponieważ jest łatwiejsze do wdrożenia niż oczekiwane zachowanie (wierz mi, nie jesteś pierwszym, który wymyśli to) Nie mogę naprawdę nazwać źródła, przepraszam. – pkoch

3

wierzę, że accepts_nested_attributes_for() zapewni zachowanie chcesz:

class Project < ActiveRecord::Base 
    accepts_nested_attributes_for :tasks 
end 

ten powinien zawinąć wszystko wewnątrz transakcji. Następnie musisz zbudować formularz, który zapełni odpowiednie zadania. Metoda tasks_attributes w modelu projektu jest wywoływana zamiast metody tasks. Aby uzyskać więcej informacji, patrz the API.

3

Może się okazać, że funkcja AutosaveAssociation robi to, co chcesz.

class Project < ActiveRecord::Base 
    has_many :tasks, :autosave => true 
end 

To powinno automatycznie zawinąć zapis w transakcji.

Powiązane problemy