Walter Lee Davis
2012-Aug-09 15:25 UTC
Nested form with has_many :through -- how to modify the "through" attributes
This question occurred to me as I was answering a different question. Given a has_many :through relationship, where there are additional attributes in the :through model, how would you construct a form so you could update those attributes? #foo.rb class Foo < ActiveRecord::Base has_many :bars has_many :bazzes, :through => :bars accepts_nested_attributes_for :bazzes end #bar.rb class Bar < ActiveRecord::Base belongs_to :foo belongs_to :baz # also defines the ''blarg'' attribute end #baz.rb class Baz < ActiveRecord::Base has_many :bars has_many :foos, :through => :bars end So in a form_for @foo, in fields_for :bazzes, how would I define a checkbox to set the ''blarg'' attribute to true or false? Does the nested @foo.baz just have a magical #blarg attribute, even though that attribute is defined in the linking model Bar? Thanks, Walter -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org For more options, visit https://groups.google.com/groups/opt_out.
Walter Lee Davis
2012-Aug-09 17:39 UTC
Re: Nested form with has_many :through -- how to modify the "through" attributes
On Aug 9, 2012, at 11:25 AM, Walter Lee Davis wrote:> This question occurred to me as I was answering a different question. Given a has_many :through relationship, where there are additional attributes in the :through model, how would you construct a form so you could update those attributes? > > #foo.rb > class Foo < ActiveRecord::Base > has_many :bars > has_many :bazzes, :through => :bars > accepts_nested_attributes_for :bazzes > end > > #bar.rb > class Bar < ActiveRecord::Base > belongs_to :foo > belongs_to :baz > # also defines the ''blarg'' attribute > end > > #baz.rb > class Baz < ActiveRecord::Base > has_many :bars > has_many :foos, :through => :bars > end > > So in a form_for @foo, in fields_for :bazzes, how would I define a checkbox to set the ''blarg'' attribute to true or false? Does the nested @foo.baz just have a magical #blarg attribute, even though that attribute is defined in the linking model Bar?I''ve gone so far as to scaffold this out, because I really would like to know the answer! Aside from having the plural of Baz wrong, my code above stands, and works as far as having multiple baz objects attached to a foo through a bar, and being able to create and modify these in a nested form. This part of Rails has only gotten easier to use in the latest version. What I cannot seem to do is in any way change the Bar when saving the association of Baz from a nested Foo form. Here''s my view: <%= form_for(@foo) do |f| %> <% if @foo.errors.any? %> <div id="error_explanation"> <h2><%= pluralize(@foo.errors.count, "error") %> prohibited this foo from being saved:</h2> <ul> <% @foo.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> <div class="field"> <%= f.label :name %><br /> <%= f.text_field :name %> </div> <!-- this all works swimmingly --> <%= f.fields_for :bazs do |b| %> <div class="field"> <%= b.label :title, "Baz" %><br /> <%= b.text_field :title %> <!-- here be the error --> <%= b.check_box :blarged %> <!-- what do I do to set bar#blarged= in the context of h/m/t? --> </div> <% end %> <div class="actions"> <%= f.submit %> </div> <% end %> Trying this in console, I can get the list of bazs associated with a foo, and if I inspect one of those, I can see that it has a foo, but I can''t seem to do anything to get the bar that stands between them and get or set any of its properties. Loading development environment (Rails 3.2.7) 1.9.2p320 :001 > f = Foo.last Foo Load (0.1ms) SELECT "foos".* FROM "foos" ORDER BY "foos"."id" DESC LIMIT 1 => #<Foo id: 3, name: "I''m a Foo", created_at: "2012-08-09 16:37:38", updated_at: "2012-08-09 16:37:38"> 1.9.2p320 :002 > f.bars Bar Load (0.3ms) SELECT "bars".* FROM "bars" WHERE "bars"."foo_id" = 3 => [#<Bar id: 1, blarged: nil, foo_id: 3, baz_id: 1, created_at: "2012-08-09 16:37:38", updated_at: "2012-08-09 16:37:38">, #<Bar id: 2, blarged: nil, foo_id: 3, baz_id: 2, created_at: "2012-08-09 16:37:38", updated_at: "2012-08-09 16:37:38">] 1.9.2p320 :004 > f.bazs Baz Load (0.2ms) SELECT "bazs".* FROM "bazs" INNER JOIN "bars" ON "bazs"."id" = "bars"."baz_id" WHERE "bars"."foo_id" = 3 => [#<Baz id: 1, title: "Here''s a baz", created_at: "2012-08-09 16:37:38", updated_at: "2012-08-09 16:37:38">, #<Baz id: 2, title: "Here''s another", created_at: "2012-08-09 16:37:38", updated_at: "2012-08-09 16:37:38">] 1.9.2p320 :005 > f.bazs.first.bar NoMethodError: undefined method `bar'' for #<Baz:0x007ff89a9ba7b8> from /Users/waltd/.rvm/gems/ruby-1.9.2-p320/gems/activemodel-3.2.7/lib/active_model/attribute_methods.rb:407:in `method_missing'' from /Users/waltd/.rvm/gems/ruby-1.9.2-p320/gems/activerecord-3.2.7/lib/active_record/attribute_methods.rb:149:in `method_missing'' from (irb):5 from /Users/waltd/.rvm/gems/ruby-1.9.2-p320/gems/railties-3.2.7/lib/rails/commands/console.rb:47:in `start'' from /Users/waltd/.rvm/gems/ruby-1.9.2-p320/gems/railties-3.2.7/lib/rails/commands/console.rb:8:in `start'' from /Users/waltd/.rvm/gems/ruby-1.9.2-p320/gems/railties-3.2.7/lib/rails/commands.rb:41:in `<top (required)>'' from script/rails:6:in `require'' from script/rails:6:in `<main>'' 1.9.2p320 :006 > It seems to me as though if I use the magical h/m/t and nested form behavior, I don''t get access to the specific bar being used to bridge one foo to one baz. If I manually inspect a foo''s bars, I can see the extra attribute (but I would have to rewrite my form and controller and not use the magic). What''s the trick I need? Thanks in advance, Walter -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
Walter Lee Davis
2012-Aug-09 18:59 UTC
Re: Nested form with has_many :through -- how to modify the "through" attributes
SOLVED: Note to future self: On Aug 9, 2012, at 1:39 PM, Walter Lee Davis wrote:> > On Aug 9, 2012, at 11:25 AM, Walter Lee Davis wrote: > >> This question occurred to me as I was answering a different question. Given a has_many :through relationship, where there are additional attributes in the :through model, how would you construct a form so you could update those attributes? >> >> #foo.rb >> class Foo < ActiveRecord::Base >> has_many :bars >> has_many :bazzes, :through => :bars >> accepts_nested_attributes_for :bazzes >> endclass Foo < ActiveRecord::Base attr_accessible :name, :bars_attributes has_many :bars has_many :bazs, :through => :bars, :dependent => :destroy accepts_nested_attributes_for :bars end>> >> #bar.rb >> class Bar < ActiveRecord::Base >> belongs_to :foo >> belongs_to :baz >> # also defines the ''blarg'' attribute >> end >>class Bar < ActiveRecord::Base attr_accessible :baz_id, :blarged, :foo_id, :baz_attributes, :baz belongs_to :baz belongs_to :foo accepts_nested_attributes_for :baz end>> #baz.rb >> class Baz < ActiveRecord::Base >> has_many :bars >> has_many :foos, :through => :bars >> endclass Baz < ActiveRecord::Base attr_accessible :title has_many :bars has_many :foos, :through => :bars, :dependent => :destroy end>> >> So in a form_for @foo, in fields_for :bazzes, how would I define a checkbox to set the ''blarg'' attribute to true or false? Does the nested @foo.baz just have a magical #blarg attribute, even though that attribute is defined in the linking model Bar? > > I''ve gone so far as to scaffold this out, because I really would like to know the answer! Aside from having the plural of Baz wrong, my code above stands, and works as far as having multiple baz objects attached to a foo through a bar, and being able to create and modify these in a nested form. This part of Rails has only gotten easier to use in the latest version. What I cannot seem to do is in any way change the Bar when saving the association of Baz from a nested Foo form. > > Here''s my view: > > <%= form_for(@foo) do |f| %> > <% if @foo.errors.any? %> > <div id="error_explanation"> > <h2><%= pluralize(@foo.errors.count, "error") %> prohibited this foo from being saved:</h2> > > <ul> > <% @foo.errors.full_messages.each do |msg| %> > <li><%= msg %></li> > <% end %> > </ul> > </div> > <% end %> > > <div class="field"> > <%= f.label :name %><br /> > <%= f.text_field :name %> > </div> > > <!-- this all works swimmingly --> > <%= f.fields_for :bazs do |b| %> > <div class="field"> > <%= b.label :title, "Baz" %><br /> > <%= b.text_field :title %> > > <!-- here be the error --> > <%= b.check_box :blarged %> > <!-- what do I do to set bar#blarged= in the context of h/m/t? --> > > </div> > <% end %> > <div class="actions"> > <%= f.submit %> > </div> > <% end %><%= form_for(@foo) do |f| %> <% if @foo.errors.any? %> <div id="error_explanation"> <h2><%= pluralize(@foo.errors.count, "error") %> prohibited this foo from being saved:</h2> <ul> <% @foo.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> <div class="field"> <%= f.label :name %><br /> <%= f.text_field :name %> </div> <%= f.fields_for :bars do |b| %> <div class="field"> <%= b.fields_for :baz do |z| %> <%= z.label :title, "Baz" %><br /> <%= z.text_field :title %> <% end %> <%= b.check_box :blarged %> </div> <% end %> <div class="actions"> <%= f.submit %> </div> <% end %>> > Trying this in console, I can get the list of bazs associated with a foo, and if I inspect one of those, I can see that it has a foo, but I can''t seem to do anything to get the bar that stands between them and get or set any of its properties. > > Loading development environment (Rails 3.2.7) > 1.9.2p320 :001 > f = Foo.last > Foo Load (0.1ms) SELECT "foos".* FROM "foos" ORDER BY "foos"."id" DESC LIMIT 1 > => #<Foo id: 3, name: "I''m a Foo", created_at: "2012-08-09 16:37:38", updated_at: "2012-08-09 16:37:38"> > 1.9.2p320 :002 > f.bars > Bar Load (0.3ms) SELECT "bars".* FROM "bars" WHERE "bars"."foo_id" = 3 > => [#<Bar id: 1, blarged: nil, foo_id: 3, baz_id: 1, created_at: "2012-08-09 16:37:38", updated_at: "2012-08-09 16:37:38">, #<Bar id: 2, blarged: nil, foo_id: 3, baz_id: 2, created_at: "2012-08-09 16:37:38", updated_at: "2012-08-09 16:37:38">] > 1.9.2p320 :004 > f.bazs > Baz Load (0.2ms) SELECT "bazs".* FROM "bazs" INNER JOIN "bars" ON "bazs"."id" = "bars"."baz_id" WHERE "bars"."foo_id" = 3 > => [#<Baz id: 1, title: "Here''s a baz", created_at: "2012-08-09 16:37:38", updated_at: "2012-08-09 16:37:38">, #<Baz id: 2, title: "Here''s another", created_at: "2012-08-09 16:37:38", updated_at: "2012-08-09 16:37:38">] > 1.9.2p320 :005 > f.bazs.first.bar > NoMethodError: undefined method `bar'' for #<Baz:0x007ff89a9ba7b8> > from /Users/waltd/.rvm/gems/ruby-1.9.2-p320/gems/activemodel-3.2.7/lib/active_model/attribute_methods.rb:407:in `method_missing'' > from /Users/waltd/.rvm/gems/ruby-1.9.2-p320/gems/activerecord-3.2.7/lib/active_record/attribute_methods.rb:149:in `method_missing'' > from (irb):5 > from /Users/waltd/.rvm/gems/ruby-1.9.2-p320/gems/railties-3.2.7/lib/rails/commands/console.rb:47:in `start'' > from /Users/waltd/.rvm/gems/ruby-1.9.2-p320/gems/railties-3.2.7/lib/rails/commands/console.rb:8:in `start'' > from /Users/waltd/.rvm/gems/ruby-1.9.2-p320/gems/railties-3.2.7/lib/rails/commands.rb:41:in `<top (required)>'' > from script/rails:6:in `require'' > from script/rails:6:in `<main>'' > 1.9.2p320 :006 > > > It seems to me as though if I use the magical h/m/t and nested form behavior, I don''t get access to the specific bar being used to bridge one foo to one baz. If I manually inspect a foo''s bars, I can see the extra attribute (but I would have to rewrite my form and controller and not use the magic). What''s the trick I need?The trick was to not try to build a bridge in mid-air. The foo could find a baz, but that found baz did not seem to have any notion of which bar was standing between the two. Nesting my forms one layer at a time did the trick. fields_for bars then contained fields_for baz. Walter> > Thanks in advance, > > Walter > > -- > You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. > To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org > To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe@googlegroups.com. > For more options, visit https://groups.google.com/groups/opt_out. > >-- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
Jean-Sébastien D.
2012-Aug-09 19:03 UTC
Re: Nested form with has_many :through -- how to modify the "through" attributes
Walter Davis wrote in post #1071859:> SOLVED: Note to future self: > > > The trick was to not try to build a bridge in mid-air. The foo could > find a baz, but that found baz did not seem to have any notion of which > bar was standing between the two. Nesting my forms one layer at a time > did the trick. fields_for bars then contained fields_for baz. > > WalterThank you very much i appreciate it!!! -- Posted via http://www.ruby-forum.com/. -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org For more options, visit https://groups.google.com/groups/opt_out.