Save changes to a has_many association ONLY when you successfully save the parent object?
Suppose each Project
has_many
Tasks
.
If I do
some_project.tasks = list_of_tasks
some_project.save
The project's tasks get updated even if the save fails. If list_of_tasks开发者_高级运维
consists of new records, the project's tasks get deleted even if the save fails! WHOA!
If the save fails, the project should have the same tasks it had before I started messing with it. How do I get this behavior and why isn't it the default?
Enclose the statements in a transaction:
Project.transaction do
p.tasks = task_list
p.save!
end
The save!
method throws an exception upon error, which rolls back any changes done to task list.
You can read the documentation if you want to dive a bit more deeply on the subject.
I believe that accepts_nested_attributes_for() will provide the behavior you want:
class Project < ActiveRecord::Base
accepts_nested_attributes_for :tasks
end
This should wrap everything inside a transaction. You then need to build the form that populates the tasks accordingly. The method tasks_attributes
in your Project model is called instead of the tasks
method. See the API for more information.
You might find that the AutosaveAssociation feature does what you want.
class Project < ActiveRecord::Base
has_many :tasks, :autosave => true
end
This should wrap the save in a transaction automatically.
Before calling #save
you can ask if some_project#valid?
. That helps the issue if the save fails due to some_project
being an invalid record, but is not a comprehensive solution.
As for the Rails' default behavior, it makes sense. Saying some_project.tasks = list_of_tasks
is like saying "remove all existing tasks from some_project and assign these new ones". You are dropping the reference to the existing association Array and assigning a brand new one. This is reflected by ActiveRecord in the DB.
精彩评论