Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Save collection of updated records all at once

As I understand it, the build method can be used to build up a collection of associated records before saving. Then, when calling save, all the child records will be validated and saved, and if there are validation errors the parent record will have an error reflecting this. First question is, is this correct?

But my main question is, assuming the above is valid, is it possible to do the same thing with updates, not creates? In other words, is there a way to update several records in a collection associated with a parent record, then save the parent record and have all the updates take place at once (with an error in the parent if there are validation errors in the children)?

Edit: So to summarize, I'm wondering the right way to handle a case where a parent record and several associated child records need to be updated and saved all at once, with any errors aborting the whole save process.

like image 326
jrdioko Avatar asked Mar 04 '11 23:03

jrdioko


2 Answers

Firstly +1 to @andrea for Transactions - cool stuff I didn't know

But easiest way here to go is to use accepts_nested_attributes_for method for model.

Lets make an example. We have got two models: Post title:string and Comment body:string post:references

lets look into models:

class Post < ActiveRecord::Base
  has_many :comments
  validates :title, :presence => true
  accepts_nested_attributes_for :comments # this is our hero
end

class Comment < ActiveRecord::Base
  belongs_to :post
  validates :body, :presence => true
end

You see: we have got some validations here. So let's go to rails console to do some tests:

post = Post.new
post.save
#=> false
post.errors
#=> #<OrderedHash {:title=>["can't be blank"]}>
post.title = "My post title"
# now the interesting: adding association
# one comment is ok and second with __empty__ body
post.comments_attributes = [{:body => "My cooment"}, {:body => nil}]
post.save
#=> false
post.errors
#=> #<OrderedHash {:"comments.body"=>["can't be blank"]}>
# Cool! everything works fine
# let's now cleean our comments and add some new valid
post.comments.destroy_all
post.comments_attributes = [{:body => "first comment"}, {:body => "second comment"}]
post.save
#=> true

Great! All works fine.

Now lets do the same things with update:

post = Post.last
post.comments.count # We have got already two comments with ID:1 and ID:2
#=> 2
# Lets change first comment's body
post.comments_attributes = [{:id => 1, :body => "Changed body"}] # second comment isn't changed
post.save
#=> true
# Now let's check validation
post.comments_attributes => [{:id => 1, :body => nil}]
post.save
#=> false
post.errors
#=> #<OrderedHash {:"comments.body"=>["can't be blank"]}>

This works!

SO how can you use it. In your models the same way, and in views like common forms but with fields_for tag for association. Also you can use very deep nesting for your association with validations nd it will work perfect.

like image 72
fl00r Avatar answered Oct 29 '22 08:10

fl00r


Try using validates_associated :some_child_records in your Patient class.

If you only want this to occur on updates, just use the :on option like validates_associated :some_child_records, :on => :update

More info here:

  • http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#method-i-validates_associated
like image 40
bowsersenior Avatar answered Oct 29 '22 06:10

bowsersenior