Peter Vandenabeele
2012-Jan-24 10:47 UTC
Invalid associated child object is silently not auto-saved and does not make saving of parent fail
**TL;DR: child.valid? == false parent.save #=> true child.new_record? #=> true child is not saved, but parent _is_ saved I had expected this to block saving of parent Hi, I am confused by this behavior (ruby 1.9.3 Rails 3.2.0): class Parent < ActiveRecord::Base has_one :child end class Child < ActiveRecord::Base validates :name, :presence => true end $ rails c Loading development environment (Rails 3.2.0) 1.9.3-p0 :001 > p = Parent.new(:name => "dad") => #<Parent id: nil, name: "dad", created_at: nil, updated_at: nil> 1.9.3-p0 :002 > p.child = Child.new(:name => "Sarah") (0.2ms) BEGIN (0.2ms) COMMIT => #<Child id: nil, name: "Sarah", parent_id: nil, created_at: nil, updated_at: nil> 1.9.3-p0 :003 > p.save! (0.2ms) BEGIN SQL (4.8ms) INSERT INTO "parents" ("created_at", "name", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["created_at", Tue, 24 Jan 2012 10:06:59 UTC +00:00], ["name", "dad"], ["updated_at", Tue, 24 Jan 2012 10:06:59 UTC +00:00]] SQL (0.8ms) INSERT INTO "children" ("created_at", "name", "parent_id", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["created_at", Tue, 24 Jan 2012 10:06:59 UTC +00:00], ["name", "Sarah"], ["parent_id", 2], ["updated_at", Tue, 24 Jan 2012 10:06:59 UTC +00:00]] (9.8ms) COMMIT => true # both are saved as expected (with child "auto-saved") 1.9.3-p0 :004 > m = Parent.new(:name => "mom") => #<Parent id: nil, name: "mom", created_at: nil, updated_at: nil> 1.9.3-p0 :005 > m.child = Child.new(:name => nil) # EMPTY NAME (0.2ms) BEGIN (0.2ms) COMMIT => #<Child id: nil, name: nil, parent_id: nil, created_at: nil, updated_at: nil> 1.9.3-p0 :006 > m.valid? => true 1.9.3-p0 :007 > m.child.valid? => false # child is not valid (name is not present) 1.9.3-p0 :008 > m.save! (0.2ms) BEGIN SQL (0.5ms) INSERT INTO "parents" ("created_at", "name", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["created_at", Tue, 24 Jan 2012 10:07:42 UTC +00:00], ["name", "mom"], ["updated_at", Tue, 24 Jan 2012 10:07:42 UTC +00:00]] (12.3ms) COMMIT => true # the save of the parent happily continues and the child is silently not auto-saved. I had expected that the entire save! would have failed in a transaction, so that either ALL or NOTHING are saved. When I add to the model e.g. the `:autosave => true` option, I get the expected behavior: class Parent < ActiveRecord::Base has_one :child, :autosave => true end class Child < ActiveRecord::Base validates :name, :presence => true end $ rails c Loading development environment (Rails 3.2.0) 1.9.3-p0 :001 > # with :autosave => true on the `has_one :child` association 1.9.3-p0 :002 > m = Parent.new(:name => "mom") => #<Parent id: nil, name: "mom", created_at: nil, updated_at: nil> 1.9.3-p0 :003 > m.valid? => true 1.9.3-p0 :004 > m.child = Child.new(:name => nil) (0.1ms) BEGIN (0.1ms) COMMIT => #<Child id: nil, name: nil, parent_id: nil, created_at: nil, updated_at: nil> 1.9.3-p0 :005 > m.valid? => false # it seems `:autosave => true` also implies `validates_associated` on the association ? 1.9.3-p0 :006 > m.child.valid? => false 1.9.3-p0 :007 > m.save! (0.2ms) BEGIN (0.2ms) ROLLBACK ActiveRecord::RecordInvalid: Validation failed: Child name can''t be blank ... Next to `:autosave => true`, also using `validates_associated :child` or `accepts_nested_attributes_for` all result in the behavior I had expected (save does "all or nothing"). But, I would expect the standard functionality (without :autosave => true or validates_associated) to not save anything (neither parent or children) in the transaction when one of the objects for saving is invalid. I feel the current behavior allows a "silent" failure where only half of the expected objects is saved while the save(!) returns success. I am not pleading to make `:autosave => true` or `validates_associated` the default on all associations. I am pleading for the "ad-hoc" measure that * if ActiveRecord decides to auto-save associated objects together with the main object * and one of thos auot-saves fails on any of those associated objects * then the entire transaction is rolled back and a non-success result is returned If there is interest in this, I may look in the code and try to find the place to fix it, but maybe there are fundamental reasons for the way it works today. Thanks for your time, Peter -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.
Bala TS
2012-Jan-25 10:31 UTC
Re: Invalid associated child object is silently not auto-saved and does not make saving of parent fa
Hai! One-to-one use has_one in the base, and belongs_to in the associated model. class Parent < ActiveRecord::Base has_one :child end class Child < ActiveRecord::Base belongs_to :parent # foreign key - parent_id end Try this way: Bye:) Bdeveloper01 -- Posted via http://www.ruby-forum.com/. -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.
Peter Vandenabeele
2012-Jan-25 12:15 UTC
Re: Re: Invalid associated child object is silently not auto-saved and does not make saving of parent fa
On Wed, Jan 25, 2012 at 11:31 AM, Bala TS <lists-fsXkhYbjdPsEEoCn2XhGlw@public.gmane.org> wrote:> Hai! > > One-to-one > > use has_one in the base, and belongs_to in the associated model. > > class Parent < ActiveRecord::Base > has_one :child > end > > class Child < ActiveRecord::Base > belongs_to :parent # foreign key - parent_id > end >Thank you for the feedback. I tried and this is the result: class Child < ActiveRecord::Base has_one :parent validates :name, :presence => true end class Parent < ActiveRecord::Base has_one :child end $ rails c Loading development environment (Rails 3.2.0) 1.9.3-p0 :001 > p = Parent.new => #<Parent id: nil, name: nil, created_at: nil, updated_at: nil> 1.9.3-p0 :002 > c1 = p.build_child (0.2ms) BEGIN (0.2ms) COMMIT => #<Child id: nil, name: nil, parent_id: nil, created_at: nil, updated_at: nil> 1.9.3-p0 :003 > p.valid? => true 1.9.3-p0 :004 > c1.valid? => false 1.9.3-p0 :005 > p.save (0.2ms) BEGIN SQL (5.8ms) INSERT INTO "parents" ("created_at", "name", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["created_at", Wed, 25 Jan 2012 11:58:00 UTC +00:00], ["name", nil], ["updated_at", Wed, 25 Jan 2012 11:58:00 UTC +00:00]] (24.2ms) COMMIT => true The line above reports "success" (true), while the child is not auto-saved Now trying to implicitly save a non-valid child with a straight assignment. 1.9.3-p0 :022 > p.child = Child.new (0.2ms) BEGIN (0.4ms) UPDATE "children" SET "parent_id" = NULL, "updated_at" ''2012-01-25 12:08:36.168342'' WHERE "children"."id" = 4 (0.2ms) ROLLBACK ActiveRecord::RecordNotSaved: Failed to save the new associated child. from /home/peterv/.rvm/gems/ruby-1.9.3-p0@associated_validations/gems/activerecord-3.2.0/lib/active_record/associations/has_one_association.rb:23:in `block in replace'' from /home/peterv/.rvm/gems/ruby-1.9.3-p0@associated_validations/gems/activerecord-3.2.0/lib/active_record/connection_adapters/abstract/database_statements.rb:190:in `transaction'' from /home/peterv/.rvm/gems/ruby-1.9.3-p0@associated_validations/gems/activerecord-3.2.0/lib/active_record/transactions.rb:208:in `transaction'' from /home/peterv/.rvm/gems/ruby-1.9.3-p0@associated_validations/gems/activerecord-3.2.0/lib/active_record/associations/has_one_association.rb:11:in `replace'' from /home/peterv/.rvm/gems/ruby-1.9.3-p0@associated_validations/gems/activerecord-3.2.0/lib/active_record/associations/singular_association.rb:17:in `writer'' from /home/peterv/.rvm/gems/ruby-1.9.3-p0@associated_validations/gems/activerecord-3.2.0/lib/active_record/associations/builder/association.rb:51:in `block in define_writers'' I would have expected this behavior (an exception for p.save!) that is happening for an explicit save of an associated child, to also occur on an "auto-save" of an associated child. Thanks, Peter -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.
Peter Vandenabeele
2012-Jan-25 12:23 UTC
Re: Re: Invalid associated child object is silently not auto-saved and does not make saving of parent fa
On Wed, Jan 25, 2012 at 1:15 PM, Peter Vandenabeele <peter-jNuWw7i2w7syMbTcgqFhxg@public.gmane.org>wrote:> On Wed, Jan 25, 2012 at 11:31 AM, Bala TS <lists-fsXkhYbjdPsEEoCn2XhGlw@public.gmane.org> wrote: > >> Hai! >> >> One-to-one >> >> use has_one in the base, and belongs_to in the associated model. >> >> class Parent < ActiveRecord::Base >> has_one :child >> end >> >> class Child < ActiveRecord::Base >> belongs_to :parent # foreign key - parent_id >> end >> > > Thank you for the feedback. I tried and this is the result: > > class Child < ActiveRecord::Base > has_one :parent > validates :name, :presence => true > end > > > class Parent < ActiveRecord::Base > has_one :child > end >Sorry, that should have been Child belongs_to :parent ... But, the results are the same. Trying again: .../app/models$ cat * class Child < ActiveRecord::Base belongs_to :parent validates :name, :presence => true end class Parent < ActiveRecord::Base has_one :child end $ rails c Loading development environment (Rails 3.2.0) 1.9.3-p0 :001 > p = Parent.new => #<Parent id: nil, name: nil, created_at: nil, updated_at: nil> 1.9.3-p0 :002 > c1 = p.build_child (0.2ms) BEGIN (0.2ms) COMMIT => #<Child id: nil, name: nil, parent_id: nil, created_at: nil, updated_at: nil> 1.9.3-p0 :003 > p.valid? => true 1.9.3-p0 :004 > c1.valid? => false 1.9.3-p0 :005 > p.save (0.2ms) BEGIN SQL (5.7ms) INSERT INTO "parents" ("created_at", "name", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["created_at", Wed, 25 Jan 2012 12:18:32 UTC +00:00], ["name", nil], ["updated_at", Wed, 25 Jan 2012 12:18:32 UTC +00:00]] (21.9ms) COMMIT => true 1.9.3-p0 :006 > p.child = Child.new (0.2ms) BEGIN (0.2ms) ROLLBACK ActiveRecord::RecordNotSaved: Failed to save the new associated child. from /home/peterv/.rvm/gems/ruby-1.9.3-p0@associated_validations/gems/activerecord-3.2.0/lib/active_record/associations/has_one_association.rb:23:in `block in replace'' from /home/peterv/.rvm/gems/ruby-1.9.3-p0@associated_validations/gems/activerecord-3.2.0/lib/active_record/connection_adapters/abstract/database_statements.rb:190:in `transaction'' from /home/peterv/.rvm/gems/ruby-1.9.3-p0@associated_validations/gems/activerecord-3.2.0/lib/active_record/transactions.rb:208:in `transaction'' from /home/peterv/.rvm/gems/ruby-1.9.3-p0@associated_validations/gems/activerecord-3.2.0/lib/active_record/associations/has_one_association.rb:11:in `replace'' from /home/peterv/.rvm/gems/ruby-1.9.3-p0@associated_validations/gems/activerecord-3.2.0/lib/active_record/associations/singular_association.rb:17:in `writer'' from /home/peterv/.rvm/gems/ruby-1.9.3-p0@associated_validations/gems/activerecord-3.2.0/lib/active_record/associations/builder/association.rb:51:in `block in define_writers'' Thanks, Peter -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.