For example, user
has one list
, list
has many item
s, 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 item
s wouldn't be save to the database. How should I rewrite this code?
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With