how to use activemodel collection.build for a has_many :through association

49 views
Skip to first unread message

James Gray

unread,
Jul 21, 2013, 1:28:38 PM7/21/13
to rubyonra...@googlegroups.com
Hi all,

In my controller I am doing the following to populate a nested form for a has_many through association:

    def new
       @specification = Specification.new

    Component.find_each.each do |component|
      @specification.component_specifications.build(:component_id => component.id)
    end

The idea being whenever someone creates or edits a form, it will be populated with all components at that time so that when they save those components that specification will be associated with the newly created specification. The problem I am having is I can't work out how to pass component name to display in my form for the as yet nonexistent component_specification as it is not accessible through the ComponentSpecification model.

My models:

class Specification < ActiveRecord::Base
  attr_accessible :description, :name, :component_specifications_attributes

  validates :name, :presence => true, :uniqueness => true

  has_many :component_specifications
  has_many :components, :through => :component_specifications

  accepts_nested_attributes_for :component_specifications, :allow_destroy => true
end


class ComponentSpecification < ActiveRecord::Base
  attr_accessible :note, :colour, :brand, :components

  has_many :components

  belongs_to :specification
end

class Component < ActiveRecord::Base
  attr_accessible :description, :name

  belongs_to :component_specifications

  validates :name, :presence => true, :uniqueness => true
end

Thanks in advance,
James

Colin Law

unread,
Jul 23, 2013, 3:23:38 AM7/23/13
to rubyonra...@googlegroups.com
On 21 July 2013 18:28, James Gray <zaa...@gmail.com> wrote:
> Hi all,
>
> In my controller I am doing the following to populate a nested form for a
> has_many through association:
>
> def new
> @specification = Specification.new
>
> Component.find_each.each do |component|
> @specification.component_specifications.build(:component_id =>
> component.id)

Don't use .new or .build in the view, do it in the controller so that
everything is prepared before rendering the view. The view code
should only be concerned with displaying the data.

> end
>
> The idea being whenever someone creates or edits a form, it will be
> populated with all components at that time so that when they save those
> components that specification will be associated with the newly created
> specification. The problem I am having is I can't work out how to pass
> component name to display in my form for the as yet nonexistent
> component_specification as it is not accessible through the
> ComponentSpecification model.

I have no idea what you mean by the last sentence. But as I said
above you should be doing this in the controller anyway.

>
> My models:
>
> class Specification < ActiveRecord::Base

You said in the title ActiveModel, (well actually you said activemodel
wihch would be something different again) but I see you meant
ActiveRecord. When asking questions it is important to get the
details right.

Colin

Colin Law

unread,
Jul 23, 2013, 5:23:34 AM7/23/13
to rubyonra...@googlegroups.com
On 23 July 2013 08:23, Colin Law <cla...@googlemail.com> wrote:
> On 21 July 2013 18:28, James Gray <zaa...@gmail.com> wrote:
>> Hi all,
>>
>> In my controller I am doing the following to populate a nested form for a
>> has_many through association:
>>
>> def new
>> @specification = Specification.new
>>
>> Component.find_each.each do |component|
>> @specification.component_specifications.build(:component_id =>
>> component.id)
>
> Don't use .new or .build in the view, do it in the controller so that
> everything is prepared before rendering the view. The view code
> should only be concerned with displaying the data.

Sorry, I seem to have suffered from brain fade when I wrote the above
code, it is in the controller. You might want to put the second part
in a method of the Specification model however. I am not sure why you
are using find_each but that is a different issue.

>
>> end
>>
>> The idea being whenever someone creates or edits a form, it will be
>> populated with all components at that time so that when they save those
>> components that specification will be associated with the newly created
>> specification. The problem I am having is I can't work out how to pass
>> component name to display in my form for the as yet nonexistent
>> component_specification as it is not accessible through the
>> ComponentSpecification model.
>
> I have no idea what you mean by the last sentence. But as I said
> above you should be doing this in the controller anyway.

I still don't understand what the problem you are describing is here
though. Why can't you just say
@specification.component_specifications?

Colin

Colin Law

unread,
Jul 23, 2013, 5:36:54 AM7/23/13
to rubyonra...@googlegroups.com
On 21 July 2013 18:28, James Gray <zaa...@gmail.com> wrote:
I think that should be belongs_to :component, unless my brain is fading again.

>
> belongs_to :specification
> end
>
> class Component < ActiveRecord::Base
> attr_accessible :description, :name
>
> belongs_to :component_specifications

and that should be has_many

Colin

Matt Jones

unread,
Jul 23, 2013, 1:24:31 PM7/23/13
to rubyonra...@googlegroups.com


On Sunday, 21 July 2013 13:28:38 UTC-4, James Gray wrote:
Hi all,

In my controller I am doing the following to populate a nested form for a has_many through association:

    def new
       @specification = Specification.new

    Component.find_each.each do |component|
      @specification.component_specifications.build(:component_id => component.id)
    end

 One note on this: I'm not sure what the find_each is getting you here. If there are enough records to make it important to find them in batches, building a new ComponentSpecification for each is going to be a bad idea.


The idea being whenever someone creates or edits a form, it will be populated with all components at that time so that when they save those components that specification will be associated with the newly created specification. The problem I am having is I can't work out how to pass component name to display in my form for the as yet nonexistent component_specification as it is not accessible through the ComponentSpecification model.

My models:

class Specification < ActiveRecord::Base
  attr_accessible :description, :name, :component_specifications_attributes

  validates :name, :presence => true, :uniqueness => true

  has_many :component_specifications
  has_many :components, :through => :component_specifications

  accepts_nested_attributes_for :component_specifications, :allow_destroy => true
end


class ComponentSpecification < ActiveRecord::Base
  attr_accessible :note, :colour, :brand, :components

  has_many :components

 This doesn't match the code you're using above - setting `component_id` isn't going to load this association. Is it possible this should be a belongs_to :component instead?

 
class Component < ActiveRecord::Base
  attr_accessible :description, :name

  belongs_to :component_specifications

 Again, I think this is the wrong way round - I'm guessing your intended data model is that one Specification can have many Components, with ComponentSpecifications acting as a join table...

--Matt Jones
Reply all
Reply to author
Forward
0 new messages