Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is possible to recursively save a record in Rails?

For example, user has one list, list has many items, and I have the following code in the controller:

@user = User.new()
@list = List.new()
(1..10).each { |i| @list.items << (Item.new(:order => i)) }
@user.list = @list

Now if I call @user.save, @list and 10 items wouldn't be save to the database. How should I rewrite this code?

like image 914
Lai Yu-Hsuan Avatar asked Oct 09 '11 08:10

Lai Yu-Hsuan


1 Answers

By setting up appropriate associations and validations you can ensure that when you save the user, the associated list and items are saved at the same time. Furthermore, you can ensure that everything is saved in a single transaction and that the save will be aborted if a child record cannot be saved.

Given the following model definitions for User, List and Item

class User < ActiveRecord::Base
  belongs_to :list
  validates_associated :list
end

class List < ActiveRecord::Base
  has_many :items
  validates_associated :items
end

class Item < ActiveRecord::Base
  validates :name, presence: true
end

Calling user.save on a user with an invalid list item will now return false and none of the records will be saved. You can query user.errors to find out why the save was aborted.

user = User.new
list = user.build_list
list.items.build(name: nil) 
user.save # => false
user.errors.messages # => {:list=>["is invalid"]}
user.list.error.messages # => {:items=>["is invalid"]} 

Since Item validates presence of name, the item's save failed and none of the records were saved. All you should see in your SQL log are something like:

(0.2ms)  BEGIN
(0.2ms)  ROLLBACK

Now if all records are valid:

user = User.new
list = user.build_list(name: 'My List')
list.items.build(name: 'Widget')
user.save # => true

ActiveRecord will save the records in the correct order in order to set foreign_keys. All 3 inserts are also wrapped in a transaction:

(0.2ms)  BEGIN
SQL (0.3ms)  INSERT INTO `lists` (`name`) VALUES ('My list')
SQL (0.1ms)  INSERT INTO `items` (`list_id`, `name`) VALUES (4, 'Widget')
SQL (0.3ms)  INSERT INTO `users` (`list_id`) VALUES (4)
(0.3ms)  COMMIT
like image 94
infused Avatar answered Oct 03 '22 23:10

infused