# HABTM: join table records being deleted & not restored when validation fails

**URL:** <https://www.ruby-forum.com/t/habtm-join-table-records-being-deleted-not-restored-when-validation-fails/183708>\
**Category:** Rails\
**Created:** [February 18, 2010, 10:48pm UTC](https://www.ruby-forum.com/t/habtm-join-table-records-being-deleted-not-restored-when-validation-fails/183708 "2010-02-18T22:48:57Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![ian](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/i/bb73d2/32.png) [@ian](https://www.ruby-forum.com/u/ian)\
**Post date:** [February 18, 2010, 10:48pm UTC](https://www.ruby-forum.com/t/habtm-join-table-records-being-deleted-not-restored-when-validation-fails/183708/1 "2010-02-18T22:48:57Z")

</div>

Rails: 2.1.2  
Ruby: 1.8.6

I have two models joined by HABTM: Sessions and Learners. On the  
Session edit form, there’s a checkbox list of Learners. Users check  
which Learners will attend the session. Very straight forward.

One of the business rules is that a Session must have at least one  
learner. We’re enforcing that with a validation procedure in the  
Session model.

validate :has\_at\_least\_one\_learner?

def has\_at\_least\_one\_learner?  
if (self.learners.empty?)  
errors.add\_to\_base(“At least one learner must be selected.”)  
end  
end

In the Session controller, we have this code for the Update action:

@session = @project.sessions.find(params[:id])  
params[:session][:learner\_ids] ||= []

if @session.update\_attributes(params[:session])  
… successful update  
else  
… bad update  
end

All pretty simple. And, indeed, if you try to save a Session without  
any learners selected, you get the correct validation error.

But what I’m seeing is that even though Rails is displaying the error  
message, it’s still deleting the associated records in the join table,  
learners\_sessions.

What I’d like is for Rails to restore the deleted join records if the  
update fails. I thought that using transactions would do the trick,  
but so far I haven’t had any success there either.

@session = @project.sessions.find(params[:id])  
params[:session][:learner\_ids] ||= []

@session.transaction do  
begin  
@session.update\_attributes!(params[:session])  
rescue ActiveRecord::RecordInvalid  
– bad update  
else  
– good update  
end  
end

Any ideas? How can I get the validation error message to display and  
still keep my records in the join table?

---

<div class="post-metadata">

**Author:** ![ian](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/i/bb73d2/32.png) [@ian](https://www.ruby-forum.com/u/ian)\
**Post date:** [February 19, 2010, 1:45am UTC](https://www.ruby-forum.com/t/habtm-join-table-records-being-deleted-not-restored-when-validation-fails/183708/2 "2010-02-19T01:45:08Z")

</div>

> … what I’m seeing is that even though Rails is displaying the error  
> message, it’s still deleting the associated records in the join table,  
> learners\_sessions.

Though I have not used it, there seems to be a plugin that addresses  
this kind of issue:

[http://code.google.com/p/habtm-with-deferred-save/](http://code.google.com/p/habtm-with-deferred-save/)

Good luck!

–  
Kurt W.  
I am looking for a new Rails job:  
[http://www.CircleW.org/kurt/pages/resume](http://www.CircleW.org/kurt/pages/resume)

---

<div class="post-metadata">

**Author:** ![ian](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/i/bb73d2/32.png) [@ian](https://www.ruby-forum.com/u/ian)\
**Post date:** [February 19, 2010, 10:07am UTC](https://www.ruby-forum.com/t/habtm-join-table-records-being-deleted-not-restored-when-validation-fails/183708/3 "2010-02-19T10:07:19Z")

</div>

On 18 February 2010 21:47, Ian [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> learners\_sessions.
> 
> What I’d like is for Rails to restore the deleted join records if the  
> update fails. I thought that using transactions would do the trick,  
> but so far I haven’t had any success there either.

I may be missing a piece of the jigsaw; but instead of putting a blank  
array into the params[:session][:learner\_ids] if it’s nil, why don’t  
you populate it with the existing ids? So Rails will either use the  
newly selected ids from the form, of repopulate with the old ones?

---

<div class="post-metadata">

**Author:** ![ian](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/i/bb73d2/32.png) [@ian](https://www.ruby-forum.com/u/ian)\
**Post date:** [February 19, 2010, 5:21pm UTC](https://www.ruby-forum.com/t/habtm-join-table-records-being-deleted-not-restored-when-validation-fails/183708/4 "2010-02-19T17:21:08Z")

</div>

UI wise, that ends up being a bit awkward. The user thinks they’ve  
deselected all learners, but if they edit the page again, all the  
learners are still selected.

---

<div class="post-metadata">

**Author:** ![ian](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/i/bb73d2/32.png) [@ian](https://www.ruby-forum.com/u/ian)\
**Post date:** [February 19, 2010, 5:21pm UTC](https://www.ruby-forum.com/t/habtm-join-table-records-being-deleted-not-restored-when-validation-fails/183708/5 "2010-02-19T17:21:08Z")

</div>

After a little bit of testing, this plugin seems to solve the problem.  
Thanks.

---

<div class="post-metadata">

**Author:** ![ian](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/i/bb73d2/32.png) [@ian](https://www.ruby-forum.com/u/ian)\
**Post date:** [February 19, 2010, 5:30pm UTC](https://www.ruby-forum.com/t/habtm-join-table-records-being-deleted-not-restored-when-validation-fails/183708/6 "2010-02-19T17:30:30Z")

</div>

On 19 February 2010 16:19, Ian [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> UI wise, that ends up being a bit awkward. The user thinks they’ve  
> deselected all learners, but if they edit the page again, all the  
> learners are still selected.

Sheesh.  
The save the learners in an array, and restore them yourself on any  
failure to update

old\_learners = @session.learners.map {|learner| learner.id}

if @session.update\_attributes(params[:session])  
… successful update  
else  
… bad update  
@session.update\_attributes{:learner\_ids =\> old\_learners}  
end

But seeing as you’ve got the plugin doing what you need, it’s academic.
