I'm trying to make an app where a user can save goals, milestones for those goals, tasks for the milestones, and tasks for the goal itself. I'm using polymorphic associations, but making a form to input all of them has proven difficult. The problem is that the milestones aren't saving at all, and the milestone tasks are being listed in the database as having type "Goal" instead of type "Milestone" The models and database are set up like the top answer for this question.
I'm hoping someone could take a look at my form_for implementation and see if it's correct, or if the problem is somewhere else. Let me know if you need to see some other code.
<%= nested_form_for #goal do |f| %>
<%= render 'shared/error_messages', :object => f.object %>
<%= render 'shared/goal_fields', :f => f %>
<%= f.fields_for :milestones do |ff| %>
<%= render 'shared/milestone_fields', :f => ff %>
<% end %>
<%= f.fields_for :tasks do |ff| %>
<%= render 'shared/task_fields', :f => ff %>
<% end %>
<%= f.link_to_add "Add Milestone", :milestones %>
<%= f.link_to_add "Add Task", :tasks %>
<%= f.submit %>
<% end %>
The Rails form builder method fields_for allows you to nest attributes for multiple records. This part of your code looks correct (assuming that your partials are working). You can make your fields_for line more explicit by building the relationship off of the goal object as follows:
<%= f.fields_for :milestones, #goal.milestones.build do |ff| %>
<%= render 'shared/milestone_fields', :f => ff %>
<% end %>
Ensure that your models have the following code in order to process the parameters that will be passed to each of these models:
# app/models/goal.rb
has_many :milestones
has_many :tasks
accepts_nested_attributes_for :milestones
accepts_nested_attributes_for :tasks
# app/models/milestone.rb
has_many :tasks
accepts_nested_attributes_for :tasks # For tasks on milestones
Also ensure that if you are using attr_accessible to lock down your model attributes from mass-assignment, that these entries have corresponding entries (milestones_attributes, tasks_attributes, etc)
When you submit the form, look at the rails development log, and ensure that you see the parameters come through in a format similar to:
{:goal => {:milestones_attributes => {:tasks_attributes => {}, :tasks_attributes => {} }}
If all this data is going through, but the record is still not being saved, check for "ROLLBACK" entries in the log that might indicate that a record is not valid, and could not be inserted.
More info on nested_attributes can be found here:
http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
Info on the form helpers utilizing these nested attributes can be found here:
http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-fields_for
Related
in my
<%= nested_form_for #object do |f| %>
I've a nested_form like:
<%=f.fields_for :nested, :url => { :action => "new" } do |build| %>
<%= render 'nested_fields', :f => build %>
<% end %>
and inside that nested_field, I've another fields_for :nested2
My Problem is: I want nested2 appearing 1 time, when nested is called.
I tried inside the new action of the nested controller the
#nested = Nested.new
#nested.nested2.build
but this does only work for the "real" new action.
Is there any solution for that problem?
I'm using the "nested_form" gem.
fields_for lets you specify a particular object to render the fields for, so if you want your nested_fields partial to contain nested fields for a single, newly build nested2 model, you can do it in the fields_for call itself, like this:
# '_nested_fields.html.erb'
...
<%= f.fields_for :nested2, f.object.build_nested2 do |build| %>
<%= ... %>
<% end %>
This is assuming that Nested has_one :nested2, if it's a has_many association the fields_for arguments would be slightly different:
<%= f.fields_for :nested2s, f.object.nested2s.build do |build| %>
f.object allows you to access the form builder's object, and you can then use it's association methods (based on the association type) to build the new object at that point.
I have what seems like a simple query. I need to create a view that will accept multiple records based on a single model. In my case the model is Project, which has 1 foreign key (person) and 2 fields time, role. I need to create a view (form) to insert 5 roles.
<%= form_for(#project) do |f| %>
<% 5.times do |index|%>
<div class="field">
<%= f.label :position %><br />
<%= f.text_field "fields[#{index}][stime]" %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I get an error message: undefined method `fields[0][stime]'
I do not think the railscasts for nested models is what I need.
How would I go about creating this?
EDIT: The Project model code is below:
class Project < ActiveRecord::Base
belongs_to :person
attr_accessible :role, :stime
end
The Projects_Controller code for the new method is below:
def new
#project = Project.new
end
I see you're planning to make some 1-to-many relationship (Product has_many :roles).
Here's some advices.
First, take a look at the accepts_nested_attributes_for method. You need to add it to your model to be able to perform mass-create.
Second, fields_for is what you need to design nested forms.
I'll give you some example of mass-creating for a simple Product has_many :line_items case:
<%= form_for #product do |f| %>
<%= f.fields_for :line_items, [LineItem.new]*5 do |li_fields| %>
<%= li_fields.text_field :quantity %>
<%= li_fields.text_field :price %>
<br>
<% end %>
<%= f.submit "Create line items" %>
<% end %>
All you need is to write in you controller something like:
#product.update_attributes params[:product]
and 5 line_items will be created at once.
Don't forget to white-list association_attributes (see params in your logs to see it). But I think if you get the mass-assignment error you'll do it anyway :)
I hope it helps.
I followed the directions in Railscast #17 HABTM Checkboxes (revised) to get this code for adding services to a project using a has_and_belongs_to_many association:
<% Service.all.each do |service| %>
<%= hidden_field_tag "project[service_ids][]", nil %>
<%= check_box_tag "project[service_ids][]", service.id, #project.service_ids.include?(service.id), id: dom_id(service) %>
<%= label_tag dom_id(service), service.name %><br />
<% end %>
This works correctly, but I'd like to use Formtastic to generate the code in order to keep the formatting consistent with the rest of the page. The video mentions that Formtastic can do this easily, but I can't figure out the code for the life of me.
My guess was to do something like this:
<%= semantic_form_for :services do |f| %>
<%= f.input :name, :as => :check_boxes, :collection => Service.find(:all) %>
<% end %>
and that generates the list of services, but checking the boxes does not do anything. I know that last bit of code need to be linked somehow to the projects_services association, but I don't know how to do it.
Okay, I was trying to make this harder than it is. This is all I had to do:
<%= f.input :services, :as => :check_boxes %>
Have a page where there are multiple input fields of the same thing, Posts. Right now, when a user enters in a question for, let's say 3 fields, the only one that saves to the database is the last one. Whereas, it should save all three and give them each it's own post_id. Also; if the user doesn't enter anything in for the other fields, it should not save in the database either.
new_step_4_html.erb
<%= form_for(#post) do |f| %>
<%= f.text_field :content %>
<%= f.text_field :content %>
<%= f.text_field :content %>
<% end %>
projects_controller.rb
def new_step_4
#post = Post.new
end
Right now, all it does is submit one :content field, obviously because they all share the same id/value. Unfortunately, the Railscasts #197 applies for nested forms, so the javascript and helper stuff he does all applies for nested. I would think this is something simple. Person from IRC mentioned I could do some sort of '3.times' code into the view file or something?
First of all you will probably have to edit the model of you post.
post.rb
has_many :contents, :dependent => :destroy
accepts_nested_attributes_for :contents
You will need another model to store the content fields.
so first generate a model
rails g model content post_id:integer body:text
the model
content.rb
belongs_to :post
Now, in stead of doing <%= f.text_field :content %> a few times, let rails create them, because now you basically let them overwrite each other.
3.times do
content = #post.content.build
end
the form view will be something like this:
<%= form_for #post do |f| %>
<%= f.fields_for :contents do |builder| %>
<%= builder.label :body, "Question" %><br />
<%= builder.text_area :body, :rows => 3 %><br />
<%= end %>
<p><%= f.submit "Submit" %></p>
<% end %>
I did not test this code, but the idea should be correct. Let me know if you need more info.
I've got a model Product in my Rails application, its attributes can be edited, and I want to let user comment every change he makes (a comment can be blank, though). So, Product has_many :comments, it accepts_nested_attributes_for :comments and rejects it if the comment is blank.
Hence, the edit form for Product is a multi-model form. The problems I faced are:
Fields_for helper renders text areas for all comments belonged to the product, so the user can edit all previous comments. I need it to render fields for the new one only.
If validation breaks, and there are no comments, fields_for renders nothing. Should I perform #product.comments.build in the view before fields_for statement every time, or there is more elegant way to do it?
Maybe I'm wrong and fields_for isn't suitable in this situation?
Base on Tots answer I just made it a little simplier (Rails 3 compatible):
<%= f.fields_for :comments, #product.comments.build do |comment| %>
<%= comment.label :comments %><br />
<%= comment.text_area :content %>
<% end %>
<% f.fields_for(:comments, Product.reflect_on_association(:comments).klass.new)
do |builder| %>
<%= builder.label :comment %>
<%= builder.text_area :comment, :rows => 3 %>
<% end %>