Rails: Multi-model form won't write data to models - ruby-on-rails

Ok this is driving me round the bend. I have three models [which are relevant to this quesiton]: Outfit, Outfit_relationship and Answer. Outfit is the parent model and the others are the childs. The Outfit model looks like this:
class Outfit < ActiveRecord::Base
attr_accessible :user_id, :outfit_origin_id, :outfit_parent_id, :outfitrelationship_id #review before going live
attr_accessible :item_id, :image_size_height, :image_size_width, :image_x_coord, :image_y_coord, :zindex, :outfit_id
attr_accessible :description, :question_id, :user_id, :outfit_id
has_many :answers
has_many :outfit_relationships
accepts_nested_attributes_for :outfit_relationships, :allow_destroy => :true
accepts_nested_attributes_for :answers
Note that the 2nd and 3rd attr_accessible are to access the attributes from the other models. I'm not sure this is absolutely necessary, some articles say it is, some say it isn't, so I put it in.
I've created a multi-model form for this data which I want to publish with one button. Here is the code:
<%= form_for(#outfit) do |post_outfit| %>
<%= post_outfit.fields_for #outfit.outfit_relationships do |build| %>
<table>
<tr>
<td>X Coord <%= build.text_area :image_x_coord, :size => '1x1' %></td>
<td>Y Coord <%= build.text_area :image_y_coord, :size => '1x1' %></td>
<td>Z Index <%= build.text_area :zindex, :size => '1x1' %></td>
<td>Height <%= build.text_area :image_size_height, :size => '1x1' %></td>
<td>Weight <%= build.text_area :image_size_width, :size => '1x1' %></td>
</tr>
</table>
<% end %>
<%= post_outfit.fields_for #outfit.answers do |build| %></br></br>
<%= image_tag current_user.fbprofileimage, :size => "40x40" %></br>
<%= current_user.name %></br>
Comment: <%= build.text_area :description, :size => '10x10' %>
<% end %>
<%= post_outfit.fields_for #outfit do |build| %> </br>
origin id: <%= build.text_area :outfit_origin_id, :size => '1x1' %></br>
parent id: <%= build.text_area :outfit_parent_id, :size => '1x1' %></br>
<% end %>
<div id="ss_QID_actions_container">
<%= post_outfit.submit "Submit checked", :class => "btn btn-small btn-primary" %>
</div>
<% end %>
And here are the relevant buts of the outfit controller:
def new
#outfit = Outfit.new
#outfit.save
#outfit.answers.build
#outfit.outfit_relationships.build
respond_to do |format|
format.html # new.html.erb
format.json { render json: #outfit }
end
end
def create
#outfit = Outfit.new(params[:id])
#comment = #outfit.answers.create(params[:answer])
#outfitrelationship = #outfit.outfit_relationships.create(params[:outfit_relationship])
redirect_to outfit_path(#outfit)
So the problem is nothing gets written into my database apart from the IDs. I'm sure I'm dong something stupid here, but can't figure out why.

Related

Unpermitted parameter nested form inside a nested form

I have a Workgroup object, which has many resource_quantities. The Resource_quantity belongs to a resource. I want a form that can create the Workgroup and its children resource_quantities all in one place. I am trying to build a form using nested attributes. My problem is that when I submit my form where I can add many resources_quantities and their related resource, I get
Parameters: {"utf8"=>"✓", "workgroup"=>{"name"=>"Living room", "description"=>"installation floor", "contractor_id"=>"1", "resource_quantities_attributes"=>{"1543577850668"=>{"quantity"=>"22", "resources"=>{"name"=>"wood", "unit_type"=>"Matériel", "purchase_price"=>"20", "price"=>"22", "unit_measure"=>"u", "vat"=>"12"}, "_destroy"=>"false"}}}, "commit"=>"Create Workgroup"}
The fact that I receive a resources params instead of a resource_attributes results in a Unpermitted parameter: :resources. I have tried this params.require(:workgroup).permit! but i still received resources which results in a unknown attribute 'resources' for ResourceQuantity.
Here are the useful piece of code:
Workgroups controller
def new
#workgroup = Workgroup.new
#workgroup.resource_quantities.build.build_resource
end
def create
#workgroup = Workgroup.new(workgroup_params)
if #workgroup.save!
raise
redirect_to resources_path, notice: 'Resource was successfully created.'
else
render :new
end
end
def workgroup_params
params.require(:workgroup).permit(:name, :description, :contractor_id, :_destroy, {resource_quantities_attributes: [:quantity, :_destroy, {resources_attributes: [ :name, :unit_type, :price, :contractor_id, :purchase_price, :unit_measure, :vat]}]})
end
My Three Models
class Workgroup < ApplicationRecord
has_many :resource_quantities, inverse_of: :workgroup
has_many :resources, through: :resource_quantities
accepts_nested_attributes_for :resource_quantities, allow_destroy: true
end
class ResourceQuantity < ApplicationRecord
belongs_to :workgroup, optional: true
belongs_to :resource, optional: true
accepts_nested_attributes_for :resource, :allow_destroy => true
end
class Resource < ApplicationRecord
has_many :workgroups, through: :resource_quantities
has_many :resource_quantities, inverse_of: :resource
end
My form for Workgroup which integrate the form for resource_quantities
<%= simple_form_for(#workgroup) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :name %>
<%= f.input :description %>
<%= f.association :contractor, as: :hidden, input_html: {value: f.object.contractor || "#{current_user.contractor.id}"} %>
<h3> Resources </h3>
<table class='large_table'>
<tbody class="add_resource">
<%= f.simple_fields_for :resource_quantities do |builder| %>
<%= render 'resource_quantity_fields', f: builder %>
<% end %>
</tbody>
</table>
</div>
<div class="form-actions">
<%= f.button :submit %>
<%= link_to_add_association 'Ajouter une resource', f, :resource_quantities, class: 'btn btn-primary', data: { association_insertion_node: '.add_resource', association_insertion_method: :append } %>
</div>
<% end %>
This is my partial to add the fields related to resource_quantity and resource
<tr class="nested-fields">
<td>
<%= f.input :quantity %>
</td>
<%= f.simple_fields_for :resources do |e| %>
<td><%= e.input :name %></td>
<td><%= e.input :unit_type, collection: Resource::TYPES %></td>
<td><%= e.input :purchase_price %></td>
<td><%= e.input :price %></td>
<td><%= e.input :unit_measure, collection: Resource::UNIT_MEASURE%></td>
<td><%= e.input :vat, collection: Resource::VAT%></td>
<td>
<%= link_to_remove_association theme_icon_tag("trash"), f %>
</td>
<% end %>
</tr>
Hope someone will be able to help me I am rookie in rails
It seems that by putting resources_attributes into a string is enabling me to get the right params in the consol.
simple_fields_for "resources_attributes" do |e|
I don't know if it is good practice but it works fine.
Can you try the following change?
Replace
<%= f.simple_fields_for :resources do |e| %>
with
<%= f.simple_fields_for :resources_attributes do |e| %>

How to assign strong parameters of deeply-nested attibutes in Rails4?

I'm trying nested objects in a Rails4-app according to this Railscast. The model survey has_many :questions, the model questions in turn has_many :answers, the model answers belongs_to :questions and questions belongs_to :survey.
Now, the models were at first completely separated from one another which worked fine although I did not want it that way. I preferred them to be nested into one another so that I could assign and display the different objects at the same time.
I then had to figure out how to white-list strong parameters of these nested objects/attributes and there were good questions here that helped me with that.
When I create my database entries everything works fine. The problem comes when I want to edit the object in the database. In the log I get "Unpermitted parameter: answers" even though I've whitelisted every attribute including the ones for answers. I simply don't understand why.
Anybody who can point me in the right direction?
My surveys_controller:
class SurveysController < ApplicationController
before_action :set_survey, only: [:show, :edit, :update, :destroy]
def index
#surveys = Survey.all
end
def show
#survey = Survey.find(params[:id])
end
def new
#survey = Survey.new
3.times do
question = #survey.questions.build
4.times { question.answers.build }
end
end
def create
#survey = Survey.new(survey_params)
if #survey.save
flash[:notice] = 'Survey was successfully created.'
redirect_to(:action => 'index')
else
render('new')
end
end
def edit
#survey = Survey.find(params[:id])
end
def update
#Find an existing object using form parameters
#survey = Survey.find(params[:id])
#Update the object
if #survey.update_attributes(survey_params)
flash[:notice] = "Survey updated successfully."
#If update succeeds, redirect to 'show' action.
redirect_to(:action => 'show', :id => #survey.id)
else
#Else redisplay the 'edit' form.
render('edit')
end
end
def delete
#survey = Survey.find(params[:id])
end
def destroy
#survey = Survey.find(params[:id]).destroy
flash[:notice] = "Survey destroyed successfully."
redirect_to(:action => 'index')
end
private
def set_survey
#survey = Survey.find(params[:id])
end
def survey_params
params.require(:survey).permit(:name, questions_attributes: [:survey_id, :id, :content, answers_attributes: [:id, :question_id, :correct_answer, :content]])
end
end
My survey.rb model:
class Survey < ActiveRecord::Base
has_many :questions, :dependent => :destroy
accepts_nested_attributes_for :questions, :reject_if => lambda { |a| a[:content].blank? }
scope :sorted, lambda { order("questions.created_at DESC")}
end
EDIT: My question.rb-model:
class Question < ActiveRecord::Base
belongs_to :survey
has_many :answers, :dependent => :destroy
accepts_nested_attributes_for :answers, :reject_if => lambda { |a| a[:content].blank? }
scope :sorted, lambda { order("questions.created_at DESC")}
end
my answer.rb-model
class Answer < ActiveRecord::Base
belongs_to :question
end
my /surveys/show.html.erb
<td><%= link_to('<< Back to list', {:action => 'index'}, :class => 'action_index') %></td>
<div class="survey show">
<h2><strong>Show survey:</strong></h2>
<table summary="Survey detail view">
<tr>
<th>Survey name: </th>
<td><%= h #survey.name %></td>
</tr>
<tr>
<th>Question: </th>
<td><% for question in #survey.questions do %>
<li><%= h question.content %></li>
<ul>
<% for answer in question.answers do %>
<li><%= h answer.content %></li>
<% end %>
</ul>
<% end %>
</td>
</tr>
<tr>
<th>Created_at: </th>
<td><%= #survey.created_at %></td>
</tr>
<tr>
<td><%= link_to('Edit', {:action => 'edit', :id => #survey.id }, :class => 'action_edit') %></td>
<td><%= link_to('Delete', {:action => 'destroy', :id => #survey.id }, :class => 'action_edit') %></td>
</tr>
</table>
</div>
My _form_for.html.erb
<%= form_for #survey do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<%= f.fields_for :questions do |ff| %>
<%= render 'question_fields', :f => ff %>
<% end %>
<%= f.fields_for :answers do |fff| %>
<%= render 'answer_fields', :f => fff %>
<% end %>
<p><%= f.submit "Submit" %></p>
<% end %>
My _question_field.html.erb
<p>
<%= f.label :content, "Question" %><br />
<%= f.text_area :content, :rows => 3 %><br />
</p>
My _answer_field.html.erb
<p>
<%= f.label :content, "Answer" %>
<%= f.text_field :content %>
<%= f.radio_button :correct_answer, true %>
</p>
You have posted your Survey model twice instead of your Question model. Does your question model accept_nested_attributes_for :answers?
Assuming that you have and that I understand your structure correctly, your problem is most likely in your form - instead of f.fields_for :answers, you should have ff.fields_for :answers, nested within f.fields_for :questions, as this is a nested resource of Question and not Survey. So:
<%= f.fields_for :questions do |ff| %>
<%= render 'question_fields', :f => ff %>
<%= ff.fields_for :answers do |fff| %>
<%= render 'answer_fields', :f => fff %>
<% end %>
<% end %>

Insert data into multiple tables from one controller/view [Rails 4]

I have more curious questions for all you amazing people!
I am creating a forum and when you create a topic, you are also creating the first post at the same time.
I need to assign variables to certain fields.
Example: :user_id => current_user.id,
I don't have the param settings correct, so many of the fields are NULL when stored in the database.
Models
class Topic < ActiveRecord::Base
belongs_to :forum
has_many :posts, :dependent => :destroy
belongs_to :user
accepts_nested_attributes_for :posts
end
class Post < ActiveRecord::Base
belongs_to :topic
belongs_to :user
end
Topics Controller
# GET /topics/new
def new
#topic = Topic.new
#topic.posts.build
end
def create
#topic = Topic.new(topic_params)
if #topic.save
##topic.responses = Post.new(params[:responses])
flash[:success] = "Topic Posted"
redirect_to "/forums/#{#topic.forum_id}"
else
render :new
end
end
def topic_params
# last_post_at = (:last_post_at => Time.now)
params.require(:topic).permit(
:name,
:description,
[:last_poster_id => current_user.id],
[:last_post_at => Time.now],
[:user_id => current_user.id],
:forum_id,
posts_attributes: [:id, :content, :topic_id, :user_id => current.user.id] )
end
Post Controller
# GET /posts/new
def new
#post = Post.new
end
def create
#post = Post.new(
:content => params[:post][:content],
:topic_id => params[:post][:topic_id],
:user_id => current_user.id)
if #post.save
#topic = Topic.find(#post.topic_id)
#topic.update_attributes(
:last_poster_id => current_user.id,
:last_post_at => Time.now)
flash[:notice] = "Successfully created post."
redirect_to "/topics/#{#post.topic_id}"
else
render :action => 'new'
end
end
_form for View/Topic
<%= form_for(#topic) do |f| %>
<% if params[:forum] %>
<input type="hidden"
id="topic_forum_id"
name="topic[forum_id]"
value="<%= params[:forum] %>" />
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_field :description %>
</div>
<%= f.fields_for :posts do |p| %>
<%= p.label :content %><br />
<%= p.text_area :content %>
<% end %>
<%= f.submit :class => "btn btn-primary" %>
<% end %>
You'll likely be looking for a function called:
accepts_nested_attributes_for
You put this into the model you're working with (in your case Post) and it will pass paeans for the nested model through to the corresponding controller
There is a good RailsCast about this and I've gr some experience with it too. If you want me to post working live code, let me know (I'm on my iPhone)
Live Code
Models
#app/models/image_post.rb
belongs_to :post, :class_name => 'Post'
belongs_to :image, :class_name => 'Image'
accepts_nested_attributes_for :image, :allow_destroy => true
#app/models/post.rb
has_many :images, -> { uniq }, :class_name => 'Image', :through => :images_posts, dependent: :destroy
has_many :images_posts, :class_name => 'ImagePost'
accepts_nested_attributes_for :images_posts, :allow_destroy => true
Controller
def new
#post = Post.new
#post.images_posts.build.build_image
end
def create
#Using Inherited Resources Gem
create!
end
private
def permitted_params
{:post => params.require(:post).permit(:title, :body, images_posts_attributes: [:caption, image_attributes: [:image]] )}
end
Form
<%= form_for [:admin, resource], :html => { :multipart => true } do |f| %>
<table class="resource_table">
<thead>
<th colspan="2"><%= params[:action].capitalize %> <%= resource_class %></th>
</thead>
<tbody class="form">
<% attributes.each do |attr| %>
<tr class="<%= cycle('odd', '')%>">
<td><%= resource_class.human_attribute_name(attr) %></td>
<td>
<% if attr == "body" %>
<%= f.text_area attr, :rows => 60, :cols => 80, :class => "redactor" %>
<% else %>
<%= f.text_field attr, :value => resource.public_send(attr).to_s %>
<% end %>
</td>
</tr>
<% end %>
<%= f.fields_for :images_posts do |images_posts| %>
<%= images_posts.fields_for :image do |images| %>
<tr>
<td>Image</td>
<td><%= images.file_field :image %></td>
</tr>
<% end %>
<tr>
<td>Caption</td>
<td><%= images_posts.text_field :caption %></td>
</tr>
<% end %>
<tr class="dull">
<td colspan="2"><%= f.submit "Go" %></td>
</tr>
</tbody>
</table>
<% end %>
Use accepts_nested_attributes
class topic
accepts_nested_attributes :posts
end
class post
accepts_nested_attributes :topic
end
Then in form you can use fields_for posts while creating topic form
Also in post form fileds_for for topic
You may want to fetch the post params as
params[:topic].fetch(:post_attributes, nil)
Rails 4 has been sanitized the mass-assignment to be called as strong_params
Example

rails nested_forms error and logic

I'am trying to add some fields to my nested form. I've included the gem nested_forms (https://github.com/ryanb/nested_form).
For my prebuilt maps, it works fine, but i can't add new fields.
My controller:
def new
#people = Person.all
#vehicles = Vehicle.all
#roles = Role.all
#pratice_people = []
#people.each do |a|
if a.at1 == true
#pratice_people << a
end
end
#practice = Practice.new
#pratice_people.count.times { #practice.uebung_maps.build }
render action: "new"
end
and my form:
<% #runs = 0 %>
<%= f.fields_for :uebung_maps do |map| %>
<tr>
<%= map.hidden_field :role_id, :id => "role_id_#{#runs}" %>
<%= map.hidden_field :vehicle_id, :id => "vehicle_id_#{#runs}" %>
<%= map.hidden_field :person_id , :value => #pratice_people[#runs].id %><br/>
<td><%= #pratice_people[#runs].name %></td>
<td><%= map.select :role_id, options_from_collection_for_select(#roles, :id, :name), :include_blank => true %></td>
<td><%= map.select :vehicle_id, options_from_collection_for_select(#vehicles, :id, :name), :include_blank => true %></td>
<td><%= map.text_field :time %></td>
</tr>
<% #runs += 1 %>
<% end %>
<%= f.link_to_add "+" , :uebung_maps %>
If i try to access the page, i get following error report
Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
Do I have to (or how to) create a logic to rerun Practice.uebung_maps.build?, because I thought this is done within the nested_forms gem....
First, make sure the models are created correctly.
class Practice < ActiveRecord::Base
has_many :uebung_maps
accepts_nested_attributes_for :uebung_maps
end
class UebungMap < ActiveRecord::Base
end
Second, make sure the form_for is nested correctly
<%= nested_form_for #practice do |f| %>
<%= f.fields_for :uebung_maps do |uebung_maps_form| %>
<%= uebung_maps_form.text_field :time %>
<% end %>
<p><%= f.link_to_add "+", :uebung_maps %></p>
<% end %>

formtastic - subset of values in accepts_nested_attributes_for

I have a model:
class Contact < ActiveRecord::Base
has_many :phones
accepts_nested_attributes_for :phones
end
I want to build 50 phone #s that users can add (there may already be phones 1 or 5, but I always want 50 available)
In my controller:
while contact.phones.length < 50
contact.phones.build({:phone_type_id => PhoneType['a_cool_type'].id})
end
In my view, I want to have 2 columns of phone #s 25 rows each
<%= semantic_form_for contact do |form| %>
<table width=50%>
<%= form.inputs :for => :phones[0..25] do |phone_form| %>
<td align="center"><%= phone_form.input :number, :label => false %></td>
....
<% end %>
</table>
<table width=50%>
<%= form.inputs :for => :phones[25..49] do |phone_form| %>
<td align="center"><%= phone_form.input :number, :label => false %></td>
....
<% end %>
</table>
<%end %>
Obviously the line:
<%= form.inputs :for => :phones[25..49] do |phone_form| %>
doesn't work, but it conveys my intention ( I hope). I want to have more control over how formtastic grabs the underlying object association.
The following works, but I can't do two columns easily without fancy css.
<%= form.inputs :for => :phones do |phone_form| %>
Any suggestions?
---------- Update ----
I was able to get around this in a roundabout way:
I built up a separate list of phone #s not as contact.phones.build, but Phone.new(:contact_id => contact.id) and store those in a list called #new_phones
Then my form looks like this:
<%= semantic_form_for #contact, :url => ..., do |f| %>
<% #new_phones[0...25].each_with_index do |phone, i| %>
<%= f.fields_for :phones, phone, :child_index => i do |phone_form| %>
<%= render "phone_fields", {:phone_form => phone_form, :phone => phone} %>
<%end%>
<% end %>
....
<% #new_phones[25...50].each_with_index do |phone, i| %>
<%= f.fields_for :phones, phone, :child_index => i+25 do |phone_form| %>
<%= render "phone_fields", {:phone_form => phone_form, :phone => phone} %>
<%end%>
<% end %>
<%end%>
This allowed me to display 25 phones on one part of the page, and 25 on another, with nested_attributes_for :phones working as expected on form submit.
I've always had problems with getting nested attributes working as I want but this may help resolve your issue.
Model:
class Contact < ActiveRecord::Base
has_many :phones
accepts_nested_attributes_for :phones
end
Controller:
See we're looping #contract.phones.build 50 times, this creates 50 new instances.
class Contact < ApplicationController
def new
#contact = Contact.new
25.times do
#contact.phones.build
end
end
end
View new.html.erb :
...
<%= p.semantic_fields_for :phones do |ec| %>
<%= ec.input :number %>
<% end %>
...
I did try a few attempts to intercept the loop, sadly with no definite clean avail.

Resources