Dmitriy Likhten
Dmitriy Likhten

Reputation: 5206

Re-Associating a child object to another parent -- Rails (2.3.11) delete has_many associated object without sql operation

Given the following definitions

Foo
    has_many :bars, :dependent => :nullify, :autosave => false

Bar
    validates_presense_of :foo
    foo_id is not null # database constraint

I would like to re-associate a Bar object to a different Foo object. I need it all correctly done in memory so I can render everything in case after all work validation fails.

The problem is when I do something like

bar.foo.bars.delete(bar)
bar.foo = other_foo
other_foo.bars << bar

I get a failure that at bar.foo.bars.delete(bar) a sql update executes nulling out my bar's foo_id property. Therefore sql constraint exception and failure. I instead need it to just work all in memory and fail validation if necessary.

Edit:

The thing is this is all in a transaction so I don't care about db updates, I just don't want them triggering database constraint issues, yet I want my constraints to be present and not allow a null value in the db for my Bar object. When my save executes, if it succeeds, I want my save to re-associate everything as appropriate. If it fails I want my memory model to be in the correctly updated (but not valid) state, this way I can render my page with the updated values and messages that the update failed.

Honestly my main goal is to have the following situation:

before:

foo1.bars => [bar1, bar2]
foo2.bars => [bar3]

after:

foo1.bars => [bar1]
foo2.bars => [bar3, bar2]

So my main goal is to solve this problem (in-memory full update of all appropriate objects), if it involves not dealing with re-associating like I already have, I'll accept that too.

Upvotes: 3

Views: 1084

Answers (2)

sled
sled

Reputation: 14625

To remove an associated child (bar1) element from the parent's (foo1) collection of bars you can use

foo1.bars.delete_if{|b| b.id == bar1.id}

This will remove bar1 from the foo1's bars collection but bar1 will remain unchanged in the database because delete_if works on the Enumerable on which the HasManyAssociation is built on.

Upvotes: 1

Jimmy
Jimmy

Reputation: 9815

You can override the delete method or just create a before_destroy filter. Rails documentation

Upvotes: 0

Related Questions