Nested simple_form with polymorphic association. Unpermited parameter - ruby-on-rails

It's a lot of question about it all around, but I can't find the answer.
I've:
group.rb
class Group < ActiveRecord::Base
has_many :descriptions, :as => :describable
accepts_nested_attributes_for :descriptions
end
description.rb
class Description < ActiveRecord::Base
belongs_to :describable, :polymorphic => true
end
groups_controller.rb
def update
#group = Group.find(params[:id])
if #group.update_attributes(group_params)
flash[:success] = "yes"
redirect_to groups_path
else
render 'edit'
end
end
private
def group_params
params.require(:group).permit(:owner_id, :domain, descriptions_attributes: [:id, :content])
end
edit.html.erb
<%= simple_form_for #group do |f| %>
<% if #group[:domain].blank? %>
<%= f.input :domain %>
<% else %>
<%= f.input :domain, readonly: true %>
<% end %>
<%= f.input :owner_id, readonly: true %>
<%= f.simple_fields_for :descriptions do |description| %>
<%= description.input :content %>
<% end %>
<%= f.button :submit %>
<% end %>
In console I've Unpermitted parameter: description and nested attribute does not created. What should I do to save it at last?

I suppose Rails does not convert form name to names_attributes when generating form for nested polymorphic connection, this:
... description: [:content, :other_param, ...]
Works fine for me for polymorphic child.

Related

accepts_nested_attributes_for with hidden_field

Been searching stackoverflow for an answer to this one all day. I have a form to create a new topic. The first post should also be created with the topic. All is well except user_id is not being saved to the post.
Post Model
class Post < ActiveRecord::Base
belongs_to :topic
belongs_to :user
end
Topic Model
class Topic < ActiveRecord::Base
belongs_to :forum
belongs_to :user
has_many :posts
accepts_nested_attributes_for :posts
end
Post Controller
class PostsController < ApplicationController
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
if #post.save
redirect_to topic_path(#post.topic_id)
else
render 'new'
end
end
private
def post_params
params.require(:post).permit(:content, :topic_id, :topic_name, :user_id)
end
end
Topic Controller
class TopicsController < ApplicationController
def new
#topic = Topic.new
#topic.posts.build
end
def create
#topic = Topic.new(topic_params)
if #topic.save
redirect_to #topic
else
render 'new'
end
end
private
def topic_params
params.require(:topic).permit(
:topic_name,
:forum_id,
:user_id,
posts_attributes: [:id, :content, :topic_id, :topic_name, :user_id ] )
end
end
new/topic View
<%= form_for(#topic) do |f| %>
<%= f.hidden_field :forum_id, :value => params[:forum_id] %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<%= f.label :topic_name %>
<%= f.text_field :topic_name %>
<%= f.fields_for :posts do |p| %>
<%= p.label :content %>
<%= p.text_area :content %>
<% end %>
<%= f.submit "Post Topic", class: "btn btn-large btn-success" %>
<% end %>
I am not entirely sure why the user_id is not being passed to the post. Hopefully someone smarter than me can help me learn what to do :)
UPDATE
I changed the strong params in my topics controller to this.
def topic_params
params.require(:topic).permit(
:topic_name,
:forum_id,
posts_attributes: [:content, :topic_id, :id, '_destroy' ] ).merge(:user_id => current_user.id, posts_attributes: [:user_id => current_user.id])
end
Now the user_id is working but none of the posts_attributes like :content are being saved. I'm having a lot of fun with this one..
Notice the form attributes that being generated in the browser, all the nested attributes for post have a prefix like topic[post_attributes], try change the form to:
<%= form_for(#topic) do |f| %>
<%= f.hidden_field :forum_id, :value => params[:forum_id] %>
<%= f.label :topic_name %>
<%= f.text_field :topic_name %>
<%= f.fields_for :posts do |p| %>
<%= p.hidden_field :user_id, :value => current_user.id %>
<%= p.label :content %>
<%= p.text_area :content %>
<% end %>
<%= f.submit "Post Topic", class: "btn btn-large btn-success" %>
<% end %>
Short answer, user_id is not in the posts_attributes since the only attributes there is content, which means that allowing other attributes like topic_id and topic_name is useless.
Now that we cleared that, you SHOULD NOT use a form input for the value of the creator of any model, because it's easy for anyone to tamper with the form and set the value to anything else, like other user's id. Alternatively, you should set the user_id value in the controller, in your case, the TopicsController. Here is the code:
def create
_params = topic_params.deep_merge(user: current_user, posts_attributes: {user: current_user})
#topic = Topic.new(_params)
if #topic.save
redirect_to #topic
else
render 'new'
end
end
and remove the user_id hidden field from the form.
UPDATE: Your last code update contains an error; it should be .merge(:user_id => current_user.id, posts_attributes: {:user_id => current_user.id}). You used a square brackets around :user_id => current_user.id instead of curly ones.

Set the IDs of two parent objects in a nested form / displayed twice

I have 5 Models,
Users, Jobs, Applications, Questions, and Answers
Jobs has many questions
Jobs and Users are associated through Applications
Both Questions and Applications has_many answers.
I'm trying to make an Application create action which will --
Associate a User to a particular job
Allow the user to answer the questions that the particular job has
Right now, I'm getting it to work, but it's displaying the Question and Answer Twice.
I.e The View comes out as -->
This is question one
Text field for question one
This is question two
Text field for question two
This is question one
Text field for question one
This is question two
Text field for question two
This is what my Application#New view looks like -->
<% provide(:title, " Apply to this job") %>
<%= form_for [#job, #application] do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<% #job.questions.each do |question| %>
<%= f.fields_for :answers do |question_field| %>
<%= question_field.label :content, question.content %>
<%= question_field.text_area :content %>
<%= question_field.hidden_field :question_id, :value => question.id %>
<% end %>
<% end %>
<%= f.submit "Submit the application", class: "button" %>
<% end %>
This is my Application Controller ->
class ApplicationsController < ApplicationController
before_filter :set_user_and_job
def new
job = params[:job_id]
#application = Application.build(job)
end
def create
#application = Application.new(application_params)
#application.save
redirect_to root_url, :notice => "You have now applied!"
end
def edit
#application = Application.find(params[:id])
#answers = []
#job.questions.each do |question|
#application.answers.each do |answer|
#answers << answer if answer.question_id == question.id
end
end
end
def update
#application = Application.find(params[:id])
#application.update_attributes(application_params)
redirect_to root_url, :notice => "You have updated your application!"
end
def destroy
Application.find(params[:id]).destroy
flash[:success] = "Application Deleted."
redirect_to root_url
end
def show
#application = Application.find(params[:id])
#answers = []
#job.questions.each do |question|
#application.answers.each do |answer|
#answers << answer if answer.question_id == question.id
end
end
end
private
def set_user_and_job
#user = current_user
#job = Job.find(params[:job_id])
end
def application_params
params.require(:application).permit(:job_id, :user_id,
answers_attributes:[:id, :question_id, :content]).merge(user_id: current_user.id,
job_id: params[:job_id])
end
end
This is my Application Model
# == Schema Information
#
# Table name: applications
#
# id :integer not null, primary key
# user_id :integer
# job_id :integer
# created_at :datetime
# updated_at :datetime
#
class Application < ActiveRecord::Base
belongs_to :job
belongs_to :user
validates :job_id, presence: true
validates :user_id, presence: true
has_many :answers
accepts_nested_attributes_for :answers, :allow_destroy => true
def self.build(job_id)
application = self.new
job = Job.find(job_id)
job.questions.count.times do
application.answers.build
end
application
end
end
This is my edit view(fully functioning) ->
<% provide(:title, " Edit this application") %>
<%= form_for [#job, #application] do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.fields_for :answers do |question_field| %>
<%= question_field.label :content, question_field.object.question.content %>
<%= question_field.text_area :content %>
<% end %>
<%= f.submit "Submit the application", class: "button" %>
<% end %>
I think the reason this is happening is that I'm running a double loop, but I'm not sure how else to also get the question id and question content for each answer.
What do you think?
--
Here's how the parameters look, when I run this form ->
<%= form_for [#job, #application] do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<% #job.questions.each do |question| %>
<%= f.fields_for :answers, question do |question_field| %>
<%= question_field.label :content, question.content %>
<%= question_field.text_area :content %>
<%= question_field.hidden_field :question_id, :value => question.id %>
<% end %>
<% end %>
<%= f.submit "Submit the application", class: "button" %>
<% end %>
Started POST "/jobs/3/applications" for 127.0.0.1 at 2013-12-30 14:26:35 +0400
Processing by ApplicationsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"+gbcaJJjQZ2GfkWiKcOmSf58hf/GEnWonmGrVe1p3ZI=", "application"=>{"question"=>{"content"=>"Sample answer 2", "question_id"=>"6"}}, "commit"=>"Submit the application", "job_id"=>"3"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."remember_token" = '69f212609955f368c0f17873b5dce9f506bd3eb7' LIMIT 1
Job Load (0.1ms) SELECT "jobs".* FROM "jobs" WHERE "jobs"."id" = ? LIMIT 1 [["id", "3"]]
Unpermitted parameters: question
(0.1ms) begin transaction
Try this:
<% #job.questions.each do |question| %>
<%= f.fields_for :answers, question do |question_field| %>
<%= question_field.label :content, question.content %>
<%= question_field.text_area :content %>
<%= question_field.hidden_field :question_id, :value => question.id %>
<% end %>
<% end %>
This should split up the f.fields_for call into different instances of the object
I think you're basically cycling through the questions, which is then showing all the fields, whereas if you make it work for a single instance, it will just show the answer for that question
Maybe Try has_many :through
Maybe we need to implement has_many :through on the answers fields, so that we can create an answer for each question, like this (sorry if I got some associations incorrect):
#app/models/question.rb
Class Question < ActiveRecord::Base
belongs_to :job
has_one :answer
accepts_nested_attributes_for :answer
end
#app/models/answer.rb
Class Answer < ActiveRecord::Base
belongs_to :question
belongs_to :user
end
#app/models/application.rb
Class Application < ActiveRecord::Base
belongs_to :user
belongs_to :job
has_many :questions, through: job #-> maybe
has_many :answers, through: :questions #-> maybe
accepts_nested_attributes_for :questions
def self.build(job_id)
application = self.new
job = Job.find(job_id)
job.questions.count.times do
application.questions.build.build_answer
end
application
end
end
#app/models/job.rb
Class Job < ActiveRecord::Base
has_many :questions
has_many :applications
has_many :answers, through: :applications
has_many :users, through: :applications
end
This will give you this view:
<%= form_for #application do |f| %>
<%= f.fields_for :questions do |q| %>
<%= q.label :content %>
<%= q.fields_for :answer do |a| %>
<%= a.text_area :content %>
<% end %>
<% end %>
<% end %>
You'd have to change your controller to handle the new associations like this:
#app/controllers/applications_controller.rb
def new
job = params[:job_id]
#application = Application.build(job)
end
private
def application_params
params.require(:application).permit(:job_id, :user_id,
questions_attributes: [answer_attributes:[:content]]).merge(user_id: current_user.id,
job_id: params[:job_id])
end

How should I use rails and simple_form for nested resources?

I'm trying to create one resource with another nested resource at the same time. I'm using Rails4 and simple_form 3.0.0rc. Here is my code.
Models:
class User < ActiveRecord::Base
has_one :profile
accepts_nested_attributes_for :profile
end
class Profile < ActiveRecord::Base
belongs_to :user
end
Controller:
class UsersController < ApplicationController
def new
#user = User.new
#user.build_profile
end
def create
user = User.new user_params
user.save
redirect_to root_url
# #par =params
end
private
def user_params
params.require(:user).permit(:email, profile_attributes: [:name])
end
end
View (form for new user)
<%= simple_form_for #user do |f| %>
<%= f.input :email %>
<%= simple_fields_for :profile do |p| %>
<%= p.input :name %>
<% end %>
<%= f.submit %>
<% end %>
When I submit the form, the create action receives this params:
{"utf8"=>"✓",
"authenticity_token"=>"dJAcMcdZnrtTXVIeS2cNBwM+S6dZh7EQEALZx09l8fg=",
"user"=>{"email"=>"vasily.sib#gmail.com"},
"profile"=>{"name"=>"Vasily"},
"commit"=>"Create User",
"action"=>"create",
"controller"=>"users"}
And after calling user_params the only thing that left is
{"email"=>"vasily.sib#gmail.com"}
And, as you can see, there is nothing about profile, so no profile will be created.
What am I doing wrong?
Use f.simple_fields_for instead of simple_fields_for:
<%= f.simple_fields_for :profile do |p| %>
<%= p.input :name %>
<% end %>
In my case I had the object "book" which belongs to "tour" and "tour" has_many "books".
In the "BookController" in the method "new" I find the tour and initialize the book object:
#tour = Tour.find(params[:tour_id])
#book = Book.new
This is the partial form to create a book: _form.html.erb
<%= simple_form_for [#tour, #book] do |f| %>
<%= f.input :name, label: "Name"%>
<%= f.input :NoReservations, label: "Number of Reservations" %>
<%= f.input :email, label: "Email" %>
<h3>Num of available places</h3>
<%= f.button :submit %>
<% end %>

rails: mass-assign error caused by updating child (Post) from parent (Topic) controller (edit view)

I am trying to edit a Topic which has many Posts.
Edit page for a Topic has Topic's name and Post's content that can be edited.
The mass-assignment error occurs in topics_controller.rb, update method, post.update_attributes(params[:post]).
How do I avoid mass-assignment error.
topic.rb
class Topic < ActiveRecord::Base
has_many :posts, :dependent => :destroy
belongs_to :forum
accepts_nested_attributes_for :posts, :allow_destroy => true
attr_accessible :name, :last_post_id, :posts_attributes
end
post.rb
class Post < ActiveRecord::Base
belongs_to :topic
attr_accessible :content
end
topics_controller.rb
def update
#topic = Topic.find(params[:id])
post = #topic.posts.first
if #topic.update_attributes(params[:topic]) && post.update_attributes(params[:post])
topic = Topic.find(#post.topic_id)
flash[:success] = "Success!"
redirect_to topic_posts_path(topic)
else
render 'edit'
end
end
views/topics/edit.html.erb
<%= form_for #topic do |f| %>
<!-- render 'shared/error_messages_topic' -->
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.fields_for #topic.posts.first do |post| %>
<%= render :partial => "posts/form", :locals => {:f => post} %>
<% end %>
<%= f.submit "Edit", class: "btn btn-large btn-primary" %>
<% end %>
views/posts/_form.html.erb
<%= f.label :content %>
<%= f.text_area :content %>
In update method you don't have to update attributes of both the models instead of if #topic.update_attributes(params[:topic]) && post.update_attributes(params[:post]) it should this only if #topic.update_attributes(params[:topic]) it will update the posts automatically.
And change your view from this <%= f.fields_for #topic.posts.first do |post| %> to <%= f.fields_for :posts, #topic.posts.first do |post| %> it will work fine.
For more information read this http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-fields_for

How do I generate multiple inputs in formtastic?

Adapted from the nested form Railscast, I have:
In my model
class Post < ActiveRecord::Base
has_many :fields
accepts_nested_attributes_for :fields
end
class Field < ActiveRecord::Base
belongs_to :post
end
In my controller
def new
#post = Post.new
4.times { #post.fields.build }
respond_to do |format|
format.html
end
end
In my view
<%= semantic_form_for #post do |f| %>
<%= f.inputs do %>
<%= f.input :title %>
<% end %>
<%= semantic_fields_for :fields do |h| %>
<%= h.input :name %>
<% end %>
<%= f.buttons do %>
<%= f.commit_button %>
<% end %>
<% end %>
The problem is that this only generates one :field input even though i ran #post.fields.build four times. I can't figure out how to generate multiple inputs so the user can enter multiple fields.
Sorry if this is obvious but I'm new to Rails and pretty new to programming overall.
Your nested form ain't correct
Change <%= semantic_fields_for :fields do |h| %> to
<%= f.inputs :for => :fields do |h|%>

Resources