Rails bug: accepts_nested_attributes_for is not updating my has_many association

activerecord, ruby-on-rails

Solution

So I figured it out with some hackery... For some reason you have to load them in memory first. In order to do this you have to perform some sort of function on the array of book versions. just calling `book.book_versions` is not enough:

  @book.book_versions.sort_by(&:name) # this line is to load the book_versions, without it book_versions will not update!! it's a bug in rails I discovered
  if @book.update_attributes(params[:book]) #<-- returns false finally

Problem

I have 2 models: ``` class Book < ActiveRecord::Base has_many :book_versions accepts_nested_attributes_for :book_versions, allow_destroy: true validates_associated :book_versions class BookVersion < ActiveRecord::Base has_many :collection_items has_many :collections, through: :collection_items belongs_to :book validates_presence_of :price, :isbn #<-- validates presence ``` Here are my params. Notice how I leave the `price` of book_version with name `bb` blank. This should be firing off the `validates_presence_of :price` validation in the `BookVersion` model (but it doesn't): "book"=>{"title"=>"zzzzzz", "subtitle"=>"", "author_ids"=>[""], "illustrator_ids"=>[""], "award_ids"=>[""], "theme_ids"=>[""], "publication_date"=>"", "imprint_id"=>"1", "language_ids"=>[""], "eng_vers_id"=>"", "book_versions_attributes"=>{"0"=>{"book_id"=>"2848", "name"=>"alt", "isbn"=>"", "price"=>"", "famis_number"=>"", "famis_price"=>"", "weight_in_pounds"=>""}, "1"=>{"book_id"=>"2848", "name"=>"bb", "isbn"=>"123123123123", "price"=>"", "famis_number"=>"", "famis_price"=>"", "weight_in_pounds"=>"1.0", "inventory"=>"08", "id"=>"1030"}, When I do `@book.update_attributes(params[:book])` in my controller, none of the `book_versions` update even though everything seems valid: ``` >> @book.update_attributes(params[:book]) => true >> @book.book_versions => [#<BookVersion id: 1030, book_id: 2848, name: "bb", isbn: "123123123123", inventory: "08", price: #<BigDecimal:7fc484885b80,'0.1122E2',18(18)>, famis_price: nil, famis_number: "", weight_in_pounds: #<BigDecimal:7fc4848861c0,'0.1E1',9(18)>, width: nil, height: nil, thickness: nil>, #<BookVersion id: 1031, book_id: 2848, name: "lb", isbn: "12312333333", inventory: "02", price: #<BigDecimal:7fc484886670,'0.2244E2',18(18)>, famis_price: nil, famis_number: "", weight_in_pounds: #<BigDecimal:7fc484886760,'0.1E1',9(18)>, width: nil, height: nil, thickness: nil>, #<BookVersion id: 1032, book_id: 2848, name: "hc", isbn: "111213213131", inventory: nil, price: #<BigDecimal:7fc4848869e0,'0.1212E4',9(18)>, famis_price: nil, famis_number: "", weight_in_pounds: #<BigDecimal:7fc484886d28,'0.1E1',9(18)>, width: nil, height: nil, thickness: nil>] >> @book.book_versions.map(&:price) => [#<BigDecimal:7fc484885b80,'0.1122E2',18(18)>, #<BigDecimal:7fc484886670,'0.2244E2',18(18)>, #<BigDecimal:7fc4848869e0,'0.1212E4',9(18)>] >> @book.book_versions.map(&:price).map(&:to_f) => [11.22, 22.44, 1212.0] >> @book.save => true >> @book.book_versions.map(&:price).map(&:to_f) => [11.22, 22.44, 1212.0] #<-- one of these should be `nil`. ``` What's going on? The form works perfectly fine when I am creating a `Book` with many `BookVersion`s. However, it does not update or validate anything when I'm updating an existing book with existing book versions. This is a continuation of my question here: ActiveRecord: validates_associated does not work when updating model? UPDATE ==== Uh... I think it's a bug in rails? Look at what happens: ``` >> @book.update_attributes(params[:book]) => true >> @book.book_versions => [#<BookVersion id: 1030, book_id: 2848, name: "bb", isbn: "123123123123", inventory: "08", price: #<BigDecimal:7fc487ee9488,'0.1122E2',18(18)>, famis_price: nil, famis_number: "", weight_in_pounds: #<BigDecimal:7fc487ee9118,'0.1E1',9(18)>, width: nil, height: nil, thickness: nil>, #<BookVersion id: 1031, book_id: 2848, name: "lb", isbn: "12312333333", inventory: "02", price: #<BigDecimal:7fc487ee8f88,'0.2244E2',18(18)>, famis_price: nil, famis_number: "", weight_in_pounds: #<BigDecimal:7fc487ee8e98,'0.1E1',9(18)>, width: nil, height: nil, thickness: nil>, #<BookVersion id: 1032, book_id: 2848, name: "hc", isbn: "111213213131", inventory: nil, price: #<BigDecimal:7fc487ee8b50,'0.1212E4',9(18)>, famis_price: nil, famis_number: "", weight_in_pounds: #<BigDecimal:7fc487ee89c0,'0.1E1',9(18)>, width: nil, height: nil, thickness: nil>] >> @book.update_attributes(params[:book]) => false ``` But that is only when I use better errors and freeze the controller before `update_attributes`. When I actually put `@book.book_versions` in the controller right before I update the attributes and try running it, it still doesn't work.

Original source