**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@googlegroups.com.
To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.

Search Discussions

  • Bala TS at Jan 25, 2012 at 10:32 am
    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@googlegroups.com.
    To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe@googlegroups.com.
    For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.
  • Peter Vandenabeele at Jan 25, 2012 at 12:16 pm

    On Wed, Jan 25, 2012 at 11:31 AM, Bala TS 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@googlegroups.com.
    To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe@googlegroups.com.
    For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.
  • Peter Vandenabeele at Jan 25, 2012 at 12:24 pm

    On Wed, Jan 25, 2012 at 1:15 PM, Peter Vandenabeele wrote:
    On Wed, Jan 25, 2012 at 11:31 AM, Bala TS 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@googlegroups.com.
    To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe@googlegroups.com.
    For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
grouprubyonrails-talk @
categoriesrubyonrails
postedJan 24, '12 at 10:47a
activeJan 25, '12 at 12:24p
posts4
users2
websiterubyonrails.org
irc#RubyOnRails

2 users in discussion

Peter Vandenabeele: 3 posts Bala TS: 1 post

People

Translate

site design / logo © 2021 Grokbase