Forum: Ruby on Rails habtm and collection_select question

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
François M. (Guest)
on 2005-12-08 17:10
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.
mrj (Guest)
on 2005-12-09 00:24
(Received via mailing list)
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.
François M. (Guest)
on 2005-12-09 06:05
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.
mrj (Guest)
on 2005-12-09 11:36
(Received via mailing list)
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.
This topic is locked and can not be replied to.