简体   繁体   中英

Ruby on Rails: has_many through frustrations

I'm having a frustrating problem with a has_many through: namely the fact that the through models are not created until save. Unfortunately, I need to set data on these models prior to saving the parent.

Here's the loose setup:

class Wtf < ActiveRecord::Base
  belongs_to :foo
  belongs_to :bar
end

class Bar < ActiveRecord::Base
  has_many :wtfs
  has_many :foos, :through => :wtfs
end


class Foo < ActiveRecord::Base
  has_many :wtfs
  has_many :bars, :through => :wtfs

  def after_initialize
    Bar.all.each do |bar|
      bars << bar
    end
  end

end

Everything is fine except that I need to access the "wtf"'s prior to save:

f = Foo.new => #

f.bars => [list of bars]

empty list here

f.wtfs => []

f.save! => true

now I get stuff

f.wtfs => [list of stuff]

I even went so far as to explicitly create the wtfs doing this:

 def after_initialize
    Bar.all.each do |bar|
      wtfs << Wtf.new( :foo => self, :bar => bar, :data_i_need_to_set => 10)
    end
  end

This causes the f.wtfs to be populated, but not the bars. When I save and retrieve, I get double the expected wtfs.

Anyone have any ideas?

I think you have the right idea with creating the Wtfs directly. I think it will turn out OK if you just set the bars at the same time:

def after_initialize
  Bar.all.each do |bar|
    wtfs << Wtf.new(:bar => bar, :data_i_need_to_set => 10)  # Rails should auto-assign :foo => self
    bars << bar
  end
end

Rails should save the records correctly because they are the same collection of objects. The only drag might be that if rails doesn't have the smarts to check if a new Bar record in the bars collection already has a Wtf associated, it might create one anyway. Try it out.

Couldn't you write a before_save handler on Wtf that would set the data you need to set? It would have access to both the foo and bar, if needed.

You could set the method that populates bar to an after_create, like this:

class Foo < ActiveRecord::Base
  has_many :wtfs
  has_many :bars, :through => :wtfs
  after_create :associate_bars

  def associate_bars
    Bar.all.each do |bar|
      bars << bar
    end
  end
end

This would make the wtfs be already be created when this method is called.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM