Forum: Ruby on Rails Association collections problems

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.
E3bce57ea4282d89d348aeeb70261f7f?d=identicon&s=25 Jonas Bengtsson (jonas)
on 2005-11-17 00:10
Hi all!

I find association collections to be quite hard to understand and use.

I have a Leg class that has_many :choices, and a Choice class that
belongs_to :leg. In a controller I want to update both a Leg and its
Choices. If I do it like this:

@leg.choices.update params[:choice].keys, params[:choice].values

It updates the database directly (or more specifically only updates
records that validates). So if I want to make the update to the @leg and
@leg.choices as an atomic action I guess that I will have to introduce a
transaction. However, then all the changes will be lost for the user
since the form will reflect the unchanged database, right?

So then I tried to first make the updates to the choices and then save
everything in a transaction, like this:

@leg.attributes = params[:leg]
choices = params[:choice]
if choices
  choices.each do |key, value|
    @leg.choices.find(key).attributes = value
  end
end
if request.post?
  Leg.transaction do
  @leg.save!
  @leg.choices.each {|choice| choice.save! }
end

But it tuns out that I can't make changes to individual records in the
association collection, so the @leg.choices.find().attributes=() doesn't
change anything.

So now I'm basically out of ideas. Does anyone got any pointers?

Thanks in advance!
/Jonas
E3bce57ea4282d89d348aeeb70261f7f?d=identicon&s=25 Jonas Bengtsson (jonas)
on 2005-11-17 22:16
Anyone?

/Jonas

jonas wrote:
> I find association collections to be quite hard to understand and use.
C9c7a65848f13e2b1a226bbe43bc3672?d=identicon&s=25 Pete Yandell (pete)
on 2005-11-18 00:36
(Received via mailing list)
First of all, when you do @leg.choices.find(key).attributes = value,
you're asking Rails to query the database for a new instance of that
particular choice and set the attributes on that. You then discard
this new instance without saving it.

But I'm not really sure what you're trying to do. As far as I can
see, there's no update method on a has_many relationship, so your
first bit of code shouldn't work. Can you give a concrete example of
what your params might consist of, and how you expect the database to
be changed as result?

Cheers,

Pete Yandell
E3bce57ea4282d89d348aeeb70261f7f?d=identicon&s=25 Jonas Bengtsson (jonas)
on 2005-11-18 01:22
Thanks for taking the time to respond Pete!

Yes, I thought that @leg.choices.find would not fetch new records from
the database but find one of records that are already fetched from the
database.

@leg.choices.update did work, but not the way I wanted and I can't find
the documentation for it.

To rehash/clarify the problem:
I have Leg that has_many :choices, and Choice that belongs_to :leg. For
this example Tag has :this as an attribute, and Choice got :that. Before
the action the database looks like this:
legs
[id=1, this="Hello"]
choices
[id=1, leg_id=1, that="World"]
[id=2, leg_id=1, that="Europe"]
The parameters look like this:
{
"leg"=> {"this"=>"Hi"},
"choice"=>
   {
   "1"=>{"that"=>"y'all"},
   "2"=>{"that"=>"everyone"}
   }
}
After the action I want the database to look like this:
legs
[id=1, this="Hi"]
choices
[id=1, leg_id=1, that="y'all"]
[id=2, leg_id=1, that="everyone"]

I seem to have found a way that works now, however I must be missing
something since it requires way too many lines of code :-)

If I change the association collection to a regular Array, it behaves
the way I thought it would originally. I then update the choices with
the values from the parameters. And lastly I save everything.

    @leg.attributes = params[:leg]

    if params[:choice]
      choices = @leg.choices.to_a
      params[:choice].each do |key, value|
        choice = choices.find {|c| c.id.to_s == key}
        choice.attributes = value if choice
      end
    end

    if request.post?
      Leg.transaction do
        err = !@leg.save
        @leg.choices.each {|choice| err ||= !choice.save }
        raise if err
      end
    end

This seems to work, but is there an easier way?

/Jonas

pete wrote:
> First of all, when you do @leg.choices.find(key).attributes = value,
> you're asking Rails to query the database for a new instance of that
> particular choice and set the attributes on that. You then discard
> this new instance without saving it.
>
> But I'm not really sure what you're trying to do. As far as I can
> see, there's no update method on a has_many relationship, so your
> first bit of code shouldn't work. Can you give a concrete example of
> what your params might consist of, and how you expect the database to
> be changed as result?
>
> Cheers,
>
> Pete Yandell
This topic is locked and can not be replied to.