Howto: Access a second related model in a nested attribute builder block - ruby-on-rails

I have a basic has_many through relationship:
class Foo < ActiveRecord::Base
has_many :bars, :dependent => :destroy
has_many :wtfs :through => :bars
accepts_nested_attributes_for :bars, :wtfs
end
On my crud forms I have a builder block for the wtf, but I need the label to come from the bar (an attribute called label for instance). What's the proper method to do this?
Here's the most simple scaffold:
<h1>New foo</h1>
<% form_for(#foo) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<h2>Bars</h2>
<% f.fields_for :wtfs do |builder| %>
<%= builder.hidden_field :bar_id %>
<p>
<%= builder.text_field :wtf_data_i_need_to_set %>
</p>
<% end %>
<p>
<%= f.submit 'Create' %>
</p>
<% end %>
<%= link_to 'Back', foos_path %>

The answer was found in analyzing how the rails FormBuilder works. So in the example above where I need to access the actual wtf object so I can get a property on bar, I need to do the following:
<h2>Bars</h2>
<% f.fields_for :wtfs do |builder| %>
<%= builder.hidden_field :bar_id %>
<p>
<%= builder.label builder.object.bar.data_i_need_for_a_label %>
<%= builder.text_field :wtf_data_i_need_to_set %>
</p>
<% end %>

Related

Rails 4: fields_for in fields_for

I am learning RoR and i am trying to find how to set a fields_for in another one with has_one models like this:
class Child < ActiveRecord::Base
belongs_to :father
accepts_nested_attributes_for :father
end
class Father < ActiveRecord::Base
has_one :child
belongs_to :grandfather
accepts_nested_attributes_for :grandfather
end
class Grandfather < ActiveRecord::Base
has_one :father
end
I used Nested Model Form Part 1 on Railscasts to get these:
In children_controller.rb:
def new
#child = Child.new
father=#child.build_father
father.build_grandfather
end
def child_params
params.require(:child).permit(:name, father_attributes:[:name], grandfather_attributes:[:name])
end
And my form:
<%= form_for(#child) do |f| %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
mother:<br>
<%= f.fields_for :father do |ff| %>
<%= ff.label :name %>
<%= ff.text_field :name %><br>
grand mother:<br>
<%= f.fields_for :grandfather do |fff| %>
<%= fff.label :name %>
<%= fff.text_field :name %>
<% end %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I am trying to retrieve the datas with:
<%= child.father.name %>
<%= child.father.grandfather.name %>
but the grandfather's name won't work.
I cannot find the mistake(s)...anyone to help on this?
Thanks!
Try switching:
<%= f.fields_for :grandfather do |fff| %>
to:
<%= ff.fields_for :grandfather do |fff| %>
And switching:
params.require(:child).permit(:name, father_attributes:[:name], grandfather_attributes:[:name])
To:
params.require(:child).permit(:name, father_attributes:[:name, grandfather_attributes:[:name]])

ActiveModel::MassAssignmentSecurity::Error with nested attributes

Hi im trying to create a relation one-to-many in my rails app.
Fist i create my models
class Produto < ActiveRecord::Base
attr_accessible :compra, :descricao, :estoque, :venda
has_many :precos
accepts_nested_attributes_for :precos
end
class Preco < ActiveRecord::Base
attr_accessible :compra_decimal, :produto_id, :venda_decimal
belongs_to :produto
end
Then i created my controller
class ProdutosController < ApplicationController
def new
#produto = Produto.new
#produto.precos.build
end
def create
#produto = Produto.new(params[:produto])
if #produto.save?
redirect_to produtos_path
end
end
end
After this i created my .html.erb pages:
_form
<%= form_for #produto do |f| %>
<p>
<%= f.label :descricao %><br/>
<%= f.text_field :descricao %>
</p>
<p>
<%= f.label :compra %><br/>
<%= f.text_field :compra %>
</p>
<p>
<%= f.label :venda %><br/>
<%= f.text_field :venda %>
</p>
<p>
<%= f.label :estoque %><br/>
<%= f.text_field :estoque %>
</p>
<%= f.fields_for :precos do |builder| %>
<%= render "precos", :f => builder %>
<% end %>
<p><%= f.submit %></p>
<% end %>
_precos
<p>
<%= f.label :venda_decimal %><br/>
<%= f.text_field :venda_decimal %>
</p>
<p>
<%= f.label :compra_decimal %><br/>
<%= f.text_field :compra_decimal %>
</p>
new
<%= render "form" %>
then, when i submit the form this error appears:
ActiveModel::MassAssignmentSecurity::Error in ProdutosController#create
Can't mass-assign protected attributes: precos_attributes
does anyone have any idea about it?
Just change your model:
class Produto < ActiveRecord::Base
attr_accessible :compra, :descricao, :estoque, :venda, :precos_attributes
has_many :precos
accepts_nested_attributes_for :precos
end

Using accepts_nested_attributes_for with group_by

I am trying to use the accepts_nested_attributes_for method within my model, however I need to render the records grouped by another association. I have got this to work but the method I have used seems like a bit of a hack.
Is there a better way to structure this?
My Model
has_many :quantities
has_many :ingredients, :through => :quantities, :uniq => true
has_many :sizes, :through => :quantities, :uniq => true
has_many :photos, :as => :imageable
accepts_nested_attributes_for :quantities
My View
<%= form_for [:admin, #recipe] do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<% #recipe.quantities.group_by(&:size).each do |size, quantities| %>
<h3><%= size.name %></h3>
<%= f.fields_for :quantities do |builder| %>
<% if builder.object.size == size %>
<p>
<%= builder.text_area :value, :rows => 1 %>
</p>
<% end %>
<% end %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
You can get rid of the if builder.object.size == size part with this :
<% #recipe.quantities.group_by(&:size).each do |size, quantities_for_size| %>
<h3><%= size.name %></h3>
<%= f.fields_for :quantities, quantities_for_size do |builder| %>
<p><%= builder.text_area :value, :rows => 1 %></p>
<% end %>
<% end %>
passing the quantities_for_size as a second argument to fields_for should make it use it instead of the whole quantities associated to the recipe. See the docs on #fields_for for more information.

How to call a method on an object in a nested form in Ruby on Rails 3?

I have these two models
class Invoice < ActiveRecord::Base
has_many :items
accepts_nested_attributes_for :items
...
end
class Item < ActiveRecord::Base
belongs_to :invoice
def total
price * quantity
end
...
end
and this nested (!) form that posts to both models:
<h1>Add an Invoice</h1>
<%= form_for #invoice do |f| %>
<p>
<%= f.label :recipient %>
<%= f.text_field :recipient %> </p>
<p>
<%= f.label :date %>
<%= f.text_area :date %>
</p>
<h2>Items</h2>
<p>
<%= f.fields_for(:items) do |f| %>
<%= f.label :description %>
<%= f.text_field :description %>
<%= f.label :price %>
<%= f.text_field :price %>
<%= f.label :quantity %>
<%= f.text_field :quantity %>
<%= f.label :total %>
<%= f.total %><!-- this method call is not working! -->
<% end %>
</p>
<%= f.submit %>
<% end %>
How can I do calculations on my items within the form?
In my Items model I have this method:
def total
price * quantity
end
However, in the form I can't get it to work with f.total. I keep getting this error:
undefined method `total' for #<ActionView::Helpers::FormBuilder:0x10ec05558>
What am I missing here?
You are calling a method not on your model object, but on f, which is a form helper (ActionView::Helpers::FormBuilder). Error message gives a hint to this.
To call on the item, you need to replace
<%= f.total %>
with
<%= f.object.total %>

fields_for text_field doesn't support string data type

I am examining the complex_form_example project on github. I modified the migration file so that :content changed from t.text to t.string (as shown below): Please notice that t.string is the only place I changed in the github example project and before the change it all worked.
class CreateQuestions < ActiveRecord::Migration
def self.up
create_table :questions do |t|
t.integer :survey_id
t.string :content
##t.text :content
t.timestamps
end
end
def self.down
drop_table :questions
end
end
Here is the nested_form_for code:
<%= nested_form_for #survey do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<%= f.fields_for :questions, :questions do |g|%>
<p>
<%= g.label :content, "Question" %><br />
<%= g.input :content, :rows => 3 %><br />
<%= g.link_to_remove "remove" %>
</p>
<%= g.fields_for :answers %>
<p><%= g.link_to_add "Add an answer", :answers %></p>
<% end %>
<p><%= f.link_to_add "Add a question", :questions %></p>
<p><%= f.submit "Submit" %></p>
<% end %>
And now I get the following error message:
undefined method `content' for :questions:Symbol
Extracted source (around line #10):
7: <%= f.fields_for :questions, :questions do |g|%>
8: <p>
9: <%= g.label :content, "Question" %><br />
10: <%= g.text_field :content, :rows => 3 %><br />
11: <%= g.link_to_remove "remove" %>
12: </p>
13: <%= g.fields_for :answers %>
My question is, is there a fields_for field name that supports string type directly?
You have :questions, :questions...Do you see that? :questions appears two times before do
The fields_for helper is designed to take an object for which you are building the fields for or, alternatively, the name of the collection of objects which to build fields for.
The first example is something like this:
<%= f.fields_for :object do |object_fields| %>
# fields go here
In this case, it will build the fields for that object.
The second example is the same, but different:
<%= f.fields_for :questions do |question_fields| 5>
# fields go here
<% end %>
Assuming you have a questions method on whatever object f is representing, this will iterate through all those objects in that collection and present the same fields for each of the objects.
Please note: it is not necessary to specify :questions a second time. You only need to tell Rails once.
Now, if you're doing this you're probably going to want to have accepts_nested_attributes_for :questions in the parent model (whatever it is that has_many :questions) so that the fields are passed through to the controller and into the model's create or update_attributes calls successfully.
I seem to have resolved my own issue, by carefully matching fields_for tag with end tag. This works as follows: in view/survey
<%= nested_form_for #survey do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<%= f.fields_for :questions do |g|%>
<%= render "question_fields", :f => g %>
<% end %>
<p><%= f.link_to_add "Add a question", :questions %></p>
<p><%= f.submit "Submit" %></p>
<% end %>
In partial _question_fields:
<p>
<%= f.label :content, "Question" %><br />
<%= f.text_field :content, :rows => 3 %><br />
<%= f.link_to_remove "remove" %>
</p>
<%= f.fields_for :answers %>
<p><%= f.link_to_add "Add an answer", :answers %></p>
Note that the "f.fields_for :answers" uses the partial _answer_fields by default.

Resources