Collection has a different setter logic for persistent and new record. New record writes changes to the database only after save is triggered, persisted record on the contrary writes changes to the database once ids or array of records assigned via setter.
This inconsistency leads to a different issues, even not related to the inconsistency between new and persisted record. I just list some of them I already faced with in a production: - *Validation raises an error.* Lets asume we have has_many records, which should create a has_many through associations while passing ids to the parent record via *collection_ids*. In case when has_many through will have a validation error - this will raise an error, which only could be rescued, even if record is saved with soft `*save*`, not `*save!*`. - *Collection assignment writes changes at the moment of passing data via setter, not while `save` is invoked.* Lets asume I have a model with a lot of different associations. This model feeded via large form with different ids and other fields. When I set a parameters through a ` *assign_attributes*` method (without any transaction block), and save returns false because of the validation failed, collection ids will be changed anyway. One of the solutions would be use transactions and raise an exception with *ActiveRecord::Rollback*. Other solution would be use *update_attributes* or *update* in the latest version of rails. Other one is to use undocumented *with_transaction_returning_status**.* - *Difference between new and persisted record.* I believe there are not many developers who work with rails framework know about the difference between new and persisted record with has_many and has_many through records. - *has_one works differently.* Means you can update _id of a has_many or belongs_to via setter and it will not modify anything in the database. Main case to reproduce an issue: begin require "bundler/inline" rescue LoadError => e $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler" raise e end gemfile(true) do source "https://rubygems.org" gem "rails", path: "../projects/rails/" gem "sqlite3" end require "active_record" require "minitest/autorun" require "logger" # This connection will do for database-independent bug reports. ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") ActiveRecord::Base.logger = Logger.new(STDOUT) ActiveRecord::Schema.define do create_table :posts, force: true do |t| end create_table :comments, force: true do |t| t.integer :post_id end end class Post < ActiveRecord::Base has_many :comments end class Comment < ActiveRecord::Base belongs_to :post end class BugTest < Minitest::Test def test_association_stuff comment = Comment.create! attributes = {comment_ids: [comment.id]} #new_post = Post.new(attributes) #assert_equal 1, new_post.comments.size post = Post.create! post.assign_attributes(attributes) assert_equal 1, post.comments.size assert_equal 0, post.comments.count post.save! assert_equal 1, post.comments.size assert_equal 1, post.comments.count end end Please let me know if you would like to see the other cases I've listed above. Solution would be to change behaviour for the autosave has_many records, so the associations of the autosave records will never modify database on data passed to a setter. -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To post to this group, send email to [email protected]. Visit this group at https://groups.google.com/group/rubyonrails-core. For more options, visit https://groups.google.com/d/optout.
