# Habtm and collection\_select question

**URL:** <https://www.ruby-forum.com/t/habtm-and-collection-select-question/49764>\
**Category:** Rails\
**Created:** [December 8, 2005, 4:10pm UTC](https://www.ruby-forum.com/t/habtm-and-collection-select-question/49764 "2005-12-08T16:10:07Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![zerohalo](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/z/5fc32e/32.png) [@zerohalo](https://www.ruby-forum.com/u/zerohalo)\
**Post date:** [December 8, 2005, 4:10pm UTC](https://www.ruby-forum.com/t/habtm-and-collection-select-question/49764/1 "2005-12-08T16:10:07Z")

</div>

I apologize in advance for my ignorance, but I’m stuck with a problem  
and after a couple of hours of looking for answers in the documentation  
and help, I’m still stuck so I was wondering if someone might have the  
answer.

I have tables Projects and Stages, each with an habtm relationship to  
each other, and a projects\_stages table which contains projects\_id,  
stages\_id and a date field. (A project can have any number of stages,  
and completed stages are recorded in project\_stages.)

On …/projects/edit I display the completed steps for the project (all  
records in projects\_stages which have project\_id for the selected  
project). So far so good.

Now I want a drop-down field to select the next step and write a new  
record to the projects\_stages field. So I put the following:

\<%= collection\_select(:project, :stages, Stage.find\_all, :id,  
:description, {:include\_blank =\> true}) %\>

The select box populates correctly, but when saving I get an error that  
Stage expected but received String. The error page shows that the  
stage\_id is being passed back to the controller, but in this instance  
what I need is to add a row to the project\_stages table containing both  
the project\_id and the stages\_id. Does Rails ‘magically’ do this (like  
it does with a has\_many relationship) and I’m just using  
collection\_select incorrectly, or is there some other way I’m supposed  
to do this? I’m not sure what is the proper approach in this case.

Thanks.

---

<div class="post-metadata">

**Author:** ![zerohalo](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/z/5fc32e/32.png) [@zerohalo](https://www.ruby-forum.com/u/zerohalo)\
**Post date:** [December 8, 2005, 11:24pm UTC](https://www.ruby-forum.com/t/habtm-and-collection-select-question/49764/2 "2005-12-08T23:24:36Z")

</div>

zero halo wrote:

> \<%= collection\_select(:project, :stages, Stage.find\_all, :id,  
> :description, {:include\_blank =\> true}) %\>
> 
> The select box populates correctly, but when saving I get an error that  
> Stage expected but received String. The error page shows that the

If I understand you correctly, the user is selecting the step that  
they’ve just completed, to be added to the project.stages completed  
list.

In this case I’d recommend you create an attr\_accessor in your  
project model called newly\_completed\_stage\_id, then use

\<%= collection\_select(:project, :newly\_completed\_stage\_id,  
Stage.find\_all - @project.stages, :id, :description, {:include\_blank =\>  
true}) %\>

and have a before\_save method in Projects to add any new stage:

stages.push\_with\_attributes(  
Stage.find(newly\_completed\_stage\_id.to\_i), :date\_completed =\> Date.today  
) unless newly\_completed\_stage\_id.blank?

–  
We develop, watch us RoR, in numbers too big to ignore.

---

<div class="post-metadata">

**Author:** ![zerohalo](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/z/5fc32e/32.png) [@zerohalo](https://www.ruby-forum.com/u/zerohalo)\
**Post date:** [December 9, 2005, 5:05am UTC](https://www.ruby-forum.com/t/habtm-and-collection-select-question/49764/3 "2005-12-09T05:05:57Z")

</div>

mrj wrote:

> In this case I’d recommend you create an attr\_accessor in your  
> project model called newly\_completed\_stage\_id, then use
> 
> \<%= collection\_select(:project, :newly\_completed\_stage\_id,  
> Stage.find\_all - @project.stages, :id, :description, {:include\_blank =\>  
> true}) %\>
> 
> and have a before\_save method in Projects to add any new stage:
> 
> stages.push\_with\_attributes(  
> Stage.find(newly\_completed\_stage\_id.to\_i), :date\_completed =\> Date.today  
> ) unless newly\_completed\_stage\_id.blank?

Awesome, thanks! I must say, I have a hard time wrapping my head around  
some of the Rails behind-the-scenes magic, such as understanding exactly  
how push\_with\_attributes knows to add a new record to my project\_stages  
join table, but hey, it works! I should have gotten it from reading the  
Agile book, but my brain’s a bit foggy these days. This might be helpful  
to post on the Rails wiki. I’ll attempt a write-up though I’m not sure I  
can explain it all like it should.

I also liked the ‘Stage.find\_all - @project.stages’ little bit of code.  
I didn’t realize you could subtract one collection from another like  
that. Very cool.

---

<div class="post-metadata">

**Author:** ![zerohalo](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/z/5fc32e/32.png) [@zerohalo](https://www.ruby-forum.com/u/zerohalo)\
**Post date:** [December 9, 2005, 10:36am UTC](https://www.ruby-forum.com/t/habtm-and-collection-select-question/49764/4 "2005-12-09T10:36:37Z")

</div>

zero halo wrote:

> to post on the Rails wiki. I’ll attempt a write-up though I’m not sure I  
> can explain it all like it should.

The job of push\_with\_attributes is to add an object to the list of  
objects in a HABTM association. To do this it must add a record to the  
join table, geting the project\_id from the Project object on which  
stages is called, the stage\_id from the first parameter Stage object,  
and the attributes associated with the join from the second parameter.  
The stage object itself is not saved because it is not a new record.

> I also liked the ‘Stage.find\_all - @project.stages’ little bit of code.  
> I didn’t realize you could subtract one collection from another like  
> that. Very cool.

That works because Rails cleverly uses an AR object’s id attribute for  
equality comparison.

One other thing: find\_all is depricated, use find(:all) instead.

–  
We develop, watch us RoR, in numbers too big to ignore.
