Nested forms creates empty instances - ruby-on-rails

I have a Post and a MaterielLink models.
A post has_many materiel_links, and accepts_nested_attributes_for :materiel_links
I use the gem cocoon to create a nested form: on the post form, I want to be able to add links that will be created on submit of the form.
post/new.html.erb:
<%= simple_form_for #post, :html => { :id => "post_form", "data-post-id" => #post.id } do |f| %>
<%= f.simple_fields_for :materiel_links do |materiel_link| %>
<%= render 'materiel_link_fields', f: materiel_link %>
<% end %>
<%= link_to_add_association 'Ajouter', f, :materiel_links%>
<% end %>
_materiel_link_fields.html.erb:
<%= f.fields_for :materiel_links do |materiel_link| %>
<%= materiel_link.text_field :name %>
<%= materiel_link.text_field :link %>
<% end %>
In my post controller:
def update
#materiel_links = #post.materiel_links.build(post_params[:materiel_links_attributes]
if #post.update!(post_params)
session[:current_draft_post_id] = nil
redirect_to post_path(#post)
else
render :new
end
end
I am here in the update action since, for reasons specific to my rails app, the post is created when the posts/new page is rendered (It is created empty, and the user just updates it instead of actually creating it). So the post already exists, but not the materiel_links that I have to create in the update action.
And the params:
def post_params
params.require(:post).permit(:title, materiel_links_attributes: [:name,:link] )
end
I added a raise in the update action, and what is stange is that I can find the link/name for each materiel_link I've added when I type params but with a number before each couple:
>> params
{"utf8"=>"✓", "_method"=>"patch", "authenticity_token"=>"wqzWfaAcwrOOdxViYBO5HaV2bwsNsf5HsvDFEbBYapkOMAPXOJR7oT4zQHbc/hTW8T9a+iH5NRl1WUApxrIjkA==", "post"=>{"title"=>"my title", "materiel_links_attributes"=>{"1459431927732"=>{"materiel_links"=>{"name"=>"mon Lien 1", "link"=>"htttp1"}}, "1459431933881"=>{"materiel_links"=>{"name"=>" Mon lien 2", "link"=>"htttp2"}}}}, "controller"=>"posts", "action"=>"update", "id"=>"1250"}
But nothing in the materiel_links hashes when I type post_params:
>> post_params
=> {"title"=>"my title","materiel_links_attributes"=>{"1459431927732"=>{}, "1459431933881"=>{}}}
The instances of MaterielLink are created, but they are empty: they don't save the link/name.
Where did I go wrong ?

My guess is that because in your update action you used .build before .update, it somehow conflicted with .update because the materiel_links values are passed there once again. You do not need to build in update action anymore; but only in edit action, because the materiel_links will be automatically created/updated when .update(post_params) is called, as post_params already include materiel_links values. Try
def update
if #post.update!(post_params)
#materiel_links = #post.materiel_links
session[:current_draft_post_id] = nil
redirect_to post_path(#post)
else
render :new
end
end
You'd also need whitelist the ID of materiel_link in strong params, so that these materiel_links in the form can be updated (ID not needed to be whitelisted if just create, and no update). You might also want to allow destroy. Update into the following:
def post_params
params.require(:post).permit(:title, materiel_links_attributes: [:id, :name, :link, :_destroy] )
end
# post.rb
accepts_nested_attributes_for :materiel_links, allow_destroy: true

Related

undefined method `commentable' for #<ActiveRecord::Associations::CollectionProxy []> (accesing commentable id and type via polymorphic association)

I tried to fix this issue for a day but cannot find solution.
What I am doing now is to render the comment form
<%= form_for #comment, remote: true do |form|%>
<%= form.hidden_field :question_id, value: #question.id%>
<%= form.hidden_field :commentable_id, :value => #question.comments.commentable.id %>
<%= form.hidden_field :commentable_type, :value => #question.comments.commentable.class.name %>
<% end %>
I just dont know how to access the commetable_id and type that was created with the comment controller to record the id of the comment and the type of the commenting model (e.g. client or lawyer)
# GET /questions/1 or /questions/1.json
def show
set_question
#comment = Comment.new
#comments = Comment.all
end
# GET /questions/1 or /questions/1.json
def show
set_question
#comment = Comment.new
#comments = Comment.all
end
resources :questions do
resources :comments do
end
end
As the error message says, you can't call commentable on #question.comments - an association. Pass an array to the form_for method with the commentable (Question) object and the comment object. You don't need to set any hidden fields.
<%= form_for [#question, #comment] do |f| %>
<div><%= f.label :content %></div>
<div><%= f.text_area :content %></div>
<div><%= f.submit 'Post' %></div>
<% end %>
Replace content with the field where you're storing the comment's content.
This should generate a form tag with an action attribute of /questions/1/comments which upon submission is processed by CommentsController#create.
class CommentsController < ApplicationController
before_action :set_commentable
def create
#comment = #commentable.comments.new(comment_params)
if #comment.save
redirect_to #commentable, notice: 'Comment created'
else
render :new
end
end
private
def set_commentable
# e.g. request.path => '/questions/1/comments'
resource, id = request.path.split('/')[1, 2] # ['questions', '1']
#commentable = resource.singularize.classify.constantize.find(id)
end
def comment_params
params.require(:comment).permit(:content)
end
end
In the set_commentable method, the commentable type and its id are detected from the request path. Since resource is 'questions', resource.singularize.classify.constantize returns the Question model. The commentable object is then found using the find method. The CommentsController#create method creates the comment and redirects to the commentable object which is the question show page (/questions/:id). If there's an error, it renders the new view (you have to create views/comments/new.html.erb to render the form with errors).
You are using question.comments which indicates has many relation, So it will return you an active record association array, So if you want to find the commentable id, you need to take single record from the array. For example if you want first comment commentable id, then use
#question.comments.first.commentable.id
If each comment has different commentable id, you need to iterate through loop.
If you have some factor to apply condition, then apply the condition
#question.comments.where('your condition').first.commentable.id
If you use above code, still you will get the array, so I have used .first. Use a uniq value in where condition, so you will have only single record with that value.

Parameter in find method

I'm following a guide to Ruby on Rails and there is something I don't understand. I have this model called Comment which belongs_to another two models, called User and Book.
This model's controller, Comments has the following create action:
def create
book = Book.find(params[:comment][:book_id])
comment = book.comments.build(comment_params)
comment.user = current_user
if comment.save
redirect_to comment.book
end
end
comment_params is just this:
def comment_params
params.require(:comment).permit(:body)
end
This create action is called when clicking the "Submit" button from this form, which is a partial called _comments located in the books view folder and rendered in the books' show action:
<%= form_for #book.comments.build do |f| %>
<%= f.hidden_field :book_id, value: #book.id %>
<%= f.text_area :body %>
<%= f.submit "Submit" %>
<% end %>
#book is indeed defined in books#show.
I don't understand why I have to pass the [:comment] parameter to the Book.find method in order to find the book. I thought just the [:book_id] would suffice.
You're not actually passing the :comment parameter but rather accessing the :book_id that's nested in the :comment hash. Your params look something like:
{
:comment => {
:book_id => 1
}
}
If you simply passed params[:book_id] you would get back nil.
If book_id is a field in comments table you don't need to retrieve the book. Just do
def create
comment = Comment.new(comment_params)
comment.user = current_user
if comment.save
redirect_to comment.book
end
end
Also, if the User model has a comments association, and in this action you are sure that a current_user is set, you can do
def create
comment = current_user.comments.build(comment_params)
if comment.save
redirect_to comment.book
end
end

param is missing or the value is empty: activity Rails 4

When i try to create an entry in my application i get
ActionController::ParameterMissing in TodosController#create
param is missing or the value is empty: activity
Expected Source:
def todo_params
params.require(:activity).permit(:name, :due)
end
Here is my request:
{"utf8"=>"✓",
"authenticity_token"=>"mR11T50dN8tm/q+S0fNd+mYYZBR6Xq4it4ujtEOmt/7RmA4TjzTKD//XmFU+UumpbmKcaj9DtG6noHOD2pwk2w==",
"todo"=>{"name"=>"Ezekiel",
"activity"=>"test....an 8th time"},
"commit"=>"Create Todo"}
I am not sure why this is not working as the activity parameter is clearly passed through as it is in the request
Here is the controller:
def create
#todo = Todo.create(todo_params)
end
def todo_params
params.require(:activity).permit(:name, :due)
end
And finally my View:
<div class="reveal" id="ezekielTask" data-reveal>
<%= simple_form_for #todo do |f| %>
<%= f.input :name, :as => :hidden, :input_html => {:value => "Ezekiel"} %>
<%= f.input :activity %>
<%= f.button :submit %>
<% end %>
</div>
This part of my rails application is a todo list, it displays fine, i can add entries through the console, this way just will not work and i have no idea why. it is passing the data correctly just not accepting it.
Controller
def create
#todo = Todo.create(todo_params)
respond_to do |format|
if #todo.save
format.html { redirect_to location_path, notice: 'Todo was successfully created.' }
else
format.html { render :new }
}
end
end
end
Params
def todo_params
params.require(:todo).permit(:name, :activity)
end
You need to change the strong params method to require todo and then accept the :name and :activity params
def todo_params
params.require(:todo).permit(:name, :activity)
end
The reason for that is
{"utf8"=>"✓",
"authenticity_token"=>"mR11T50dN8tm/q+S0fNd+mYYZBR6Xq4it4ujtEOmt/7RmA4TjzTKD//XmFU+UumpbmKcaj9DtG6noHOD2pwk2w==",
"todo"=>{"name"=>"Ezekiel",
"activity"=>"test....an 8th time"},
"commit"=>"Create Todo"}
If you see in the params you are receiving name and activity inside todo
In the require parameter normaly we use the name of the model (todo) and inside permit we use the attributes of that model (:name, :activity)
def todo_params
params.require(:todo).permit(:name, :activity)
end

Unpermitted Parameter in spite of being included in strong params

I have a situation where I am getting the error
Unpermitted parameter: incorporation
however I have it listed in the strong params:
def company_params
params.require(:company).permit(:id, :name, :employee_stock_options, :options_pool, :state_corp, :street, :city, :state, :zip, :_destroy,
incorporation_attributes: [:title, :trademark_search, :user_id, :employee_stock_options, :final_submit, :submit, :_destroy],
names_attributes: [:id, :name_string, :suffix, :approved, :snapshot, :company_id, :_destroy],
There's a bit of a catch that might be contributing to the issue:
The controller in question is actually the Incorporation controller. But, as you might notice, we are using it to create a parent model Company, which has_one :incorporation. I realize that this is a bit odd, but I have reasons for wanting my models to be structured this way AND for using the incorporations_controller for doing it.
Accordingly, I have my form structured in the following way:
<%= simple_form_for #company, url: url_for(action: #caction, controller: 'incorporations'), html: {id:"incorporationform"}, remote: false, update: { success: "response", failure: "error"} do |company| %>
<%= company.simple_fields_for #incorporation do |f| %>
<div class="padded-fields">
<div class="form_subsection">
<%= f.input :trademark_search, as: :radio_buttons, label: 'Would you like us to do a trademark search and provide advice regarding any issues we identify in relation to the name you have selected?', input_html: { class: 'form-control radio radio-false' } %>
</div>
</div>
<% end %>
....
<% end %>
Thanks in advance for any insight
Update: My new and create methods are as follows in incorporations_controller
def new
#user=current_user
#company = #user.companies.build
#incorporation = #company.build_incorporation
#action = "new"
#caction = "create"
end
def create
#snapshot="incorporation"
#company = current_user.companies.build(company_params)
#incorporation = #company.build_incorporation
if #company.save
current_user.companies << #company
if params[:final_submit]
redirect_to incorporations_index_path
else
redirect_to edit_incorporation_path(#incorporation), notice: "Successfuly saved incorporation info."
end
else
render 'new', notice: "Something went wrong; form unable to be saved."
# render :nothing => true
end
end
Update 2: In Case it helps, here are the parameters from the log:
"company"=>{"names_attributes"=>{"145\2853672570"=>{"name_string"=>"test19", "suffix"=>"INC", "_destroy"=>"false"}}, "fiscal_year_end_month"=>"", "fiscal_year_end_day"=>"", "street"=>"", "city"=>"", "state"=>"", "zip"\=>"", "issued_common_stock"=>"10,000,000", "employee_stock_options"=>"false", "options_pool"=>"0", "incorporation"=>{"submit"=>"0"}}, "commit"=>"Save"}
I noticed that (unlike other nested attributes) incorporation does not have the _attributes line after it. Might that be of some significance?
Update3: I also seem to be creating an incorporation entry in the incorporations table with the proper ownership assigned. However no other fields are filled out.
You shouldn't have incorporation in your submitted params anyway - it should be incorporation_attributes (as you've already got in your strong params).
--
If you're using fields_for, you should expect [association]_attributes to be passed as a parameter from your form.
Not having it means you've either not got accepts_nested_attributes_for in your parent model, or you have not built your child object:
#app/models/company.rb
class Company < ActiveRecord::Base
has_one :incorporation
accepts_nested_attributes_for :incorporation
end
--
#app/controllers/incorporations_controller.rb
class IncorporationsController < ApplicationController
def new
#company = Company.new
#company.build_incorporation #-> only needed if a new record
end
def create
#company = Company.new company_params
#company.save
end
end
Update
What a strange issue you have - you're passing names_attributes fine and yet incorporation doesn't work.
The one thing I would say, after looking at your params, is that your incorporation is only passing "submit" => "0". I don't see what that is; anyway there are numerous issues with your form:
def new
#company = current_user.companies.new
#company.build_incorporation
...
end
def create
#company = current_user.companies.new company_params
#company.save #-> don't need to "build" in create
end
This will allow you to...
<%= simple_form_for #company, url: url_for(action: #caction, controller: 'incorporations'), html: {id:"incorporationform"}, remote: false, update: { success: "response", failure: "error"} do |company| %>
<%= company.simple_fields_for :incorporation do |f| %>
<%= f.input ...
<% end %>
When using fields_for, you only need to pass the parent object (in your case #company). Building incorporation will automatically populate fields_for without explicitly declaring it.
The error indicates that we need to define this in company model:
accepts_nested_attributes_for :incorporation
attr_accessible :incorporation_attributes

Rails 4 Nested Form "Unpermitted Parameters" Not Answered Anywhere Else

My nested form is not working properly no matter what I try and I searched all the StackExchange's for a solution to this seemingly easy problem. This is where I am right now to get it to work at show up in the view at all.
The form is using the Event controller create action from a non-restful location, hence the global variable (a pages controller, with a specific page, where the form is generated). My ticket model gets generated when the nested form is submitted, and the Event ID gets passed, but it doesn't fill in the "Name" field for the ticket model because it says "Unpermitted Parameters: Ticket." But they're defined as whitelisted in the Events controller! Argh! I'm thinking something is wrong with the form, but nothing I try seems to work.
Any help would be appreciated.
* UPDATED CODE THAT IS NOW WORKING *
Form.html.erb:
<div class="form-inputs">
<%= simple_form_for #event, :html => { :class => 'form-horizontal' } do |f| %>
<div class="row">
<div class="col-xs-6">
<%= f.input :name, class: "control-label" %>
</div>
</div>
<div class="row">
<div class="col-xs-6">
<%= f.simple_fields_for :tickets do |ticket| %>
<%= ticket.input :name %>
<% end %>
</div>
</div>
<div class="form-actions">
<%= f.button :submit, :class => 'btn-primary' %>
<%= link_to t('.cancel', :default => t("helpers.links.cancel")),
launchpad_path, :class => 'btn btn-default' %>
<% end %>
</div>
</div>
Event_Controller.rb
def new (this is totally skipped and unnecessary)
#event = Event.new
#ticket = #event.tickets.build
end
def create
#event = current_user.events.build(event_params)
respond_to do |format|
if #event.save
format.html { redirect_to #event, notice: 'Your event was created.' }
else
format.html { render :new }
end
end
end
def event_params
params.require(:event).permit(:name, tickets_attributes: [ :name, :id, :event_id, :_destroy ])
end
Pages_Controller.rb (where the form originate
def new
#event = Event.new
#ticket = #event.tickets.build
end
Event.rb
class Event < ActiveRecord::Base
# Database Relationships
has_many :tickets, dependent: :destroy
accepts_nested_attributes_for :tickets, :allow_destroy => true
end
Ticket.rb
class Ticket < ActiveRecord::Base
belongs_to :event
end
Routes.rb
resources :events do
resources :tickets
end
As well as the information from Alejandro (which is correct), you also have f.simple_fields_for #ticket, ... whereas you should have f.simple_fields_for :tickets, ...
If you check your log/development.log for the Processing by EventsController#create the line after will be a Parameters: line, you'll see that the parameters that have been sent through are under a :ticket key instead of a :tickets_attributes key because of the fields_for error.
Fix that, and the permit line and you should be fine.
Update
Hopefully you realized that you also don't need the #ticket = #event.tickets.build(event_params[:ticket_attributes]) line at all once that fields_for is fixed too. The setting of all the associated tickets is all done via the Event object thanks to the accepts_nested_attributes_for helper.
Just, remove from create action this line:
#ticket = #event.tickets.build(event_params[:ticket_attributes])
And, change your event_params:
def event_params
params.require(:event).permit(:name, :main_event_image, tickets_attributes: [:id, :name, :cost, :event_id, :registration_id, :created_at])
end
Te field name must be: tickets_attributes: [ ... (tickets in plural). I think this do the trick.
Edit: I'm agree with #smathy, if no fix to f.simple_fields_for :tickets ... it can't work.
Your new method must look like this:
def new
#new_event = Event.new
#new_event.tickets.build
end
I'm a fan of standards, and I prefer use #event instead of #new_event as in your form (it's part of convention over configuration on rails)
I was stuck at the same problem like crazy and at the end I was able to fix it... Try placing the binding.pry in the first line of create method and print the event_params hash and check if you see ticket_attributes hash inside of it ... That's when it ll throw unpermitted parameter ... And I see event has_many tickets , so I am guessing ticket_attributes needs to be pluralized to be tickets_attributes

Resources