I'm having trouble getting a has_many :through association working with Rails 4's strong parameters. I have a model called Checkout
and I need to select a person from the Employee
model in the new checkout form. Checkouts and Employees are associated through an Employment
model.
I'm getting this error when I try to create a new checkout:
NoMethodError in CheckoutsController#create
undefined method `employee' for #<Checkout:0x007ff4f8d07f88>
It seems that there's something wrong with either my create action, my checkout parameters or my new checkout form. Here's the create action:
def create
@user = current_user
@checkout = @user.checkouts.build(checkout_params)
respond_to do |format|
if @checkout.save
format.html { redirect_to @checkout, notice: 'Checkout was successfully created.' }
else
format.html { render action: 'new' }
end
end
end
My checkout params:
def checkout_params
params.require(:checkout).permit(:job, :employee_ids, :shift, :date, :hours, :sales, :tips, :owed, :collected, :notes)
end
My new checkout form:
<div class="field">
<%= f.label :employee %><br>
<%= f.collection_select(:employee_ids, Employee.all.collect, :id, :full_name, {:prompt => "Please select"} ) %>
</div>
But I can't figure out what has changed with Rails 4 and strong parameters. In Rails 3 this type of association and form worked for me using attr_accessible instead of strong_parameters.
Relevant Files
Full Trace of the error:
https://gist.github.com/leemcalilly/0cb9e2b539f9e1925a3d
models/checkout.rb:
https://gist.github.com/leemcalilly/012d6eae6b207beb147a
controllers/checkouts_controller.rb:
https://gist.github.com/leemcalilly/a47466504b7783b31773
views/checkouts/_form.html.erb
https://gist.github.com/leemcalilly/ce0b4049b23e3d431f55
models/employee.rb:
https://gist.github.com/leemcalilly/46150bee3e6216fa29d1
controllers/employees_controller.rb:
https://gist.github.com/leemcalilly/04f3acdac0c9a678bca8
models/employment.rb:
https://gist.github.com/leemcalilly/6adad966dd48cb9d1b39
db/schema.rb:
https://gist.github.com/leemcalilly/36be318c677bad75b211
Keep in mind that the name you give to your strong parameters (employees, employee_ids, etc.) is largely irrelevant because it depends on the name you choose to submit. Strong parameters work no "magic" based upon naming conventions.
The reason https://gist.github.com/leemcalilly/a71981da605187d46d96 is throwing an "Unpermitted parameter" error on 'employee_ids' is because it is expecting an array of scalar values, per https://github.com/rails/strong_parameters#nested-parameters, not just a scalar value.
# If instead of:
... "employee_ids" => "1" ...
# You had:
... "employee_ids" => ["1"]
Then your strong parameters would work, specifically:
... { :employee_ids => [] } ...
Because it is receiving an array of scalar values instead of just a scalar value.
Ok, so I actually did not need to nest the parameters. This is what ended up working for me:
# Never trust parameters from the scary internet, only allow the white list through.
def checkout_params
params.require(:checkout).permit(:job, :shift, :employee_ids, :date, :hours, :sales, :tips, :owed, :collected, :notes)
end
Here is the combination of changes that worked.
- db/schema.rb -
https://gist.github.com/leemcalilly/32c792277de335aa54b8
- controllers/checkouts_controller.rb -
https://gist.github.com/leemcalilly/6e01ff8b86b1bdd078ed
- controllers/employees_controller.rb -
https://gist.github.com/leemcalilly/ad8d9f696b3121fd20e8
- models/checkout.rb -
https://gist.github.com/leemcalilly/6ffb28d54c6ea896bac5
- models/employee.rb -
https://gist.github.com/leemcalilly/85f53606494743e3d3b1
- models/employment.rb -
https://gist.github.com/leemcalilly/b9f9c8b750a23c8f3611
- views/checkouts/_form.html.erb -
https://gist.github.com/leemcalilly/d148d00345ad656fcd75
- views/checkouts/index.html.erb -
https://gist.github.com/leemcalilly/95f19b23ed121e40f73d
- views/checkouts/show.html.erb -
https://gist.github.com/leemcalilly/64ef85f66aa5d887489d
Still not quite understanding why this worked though.
I can post the permit statement I use in one of my controllers. This has a many to many association as well. You nest the permit array. Use the lookup association in your permit statement. The only difference should be that yours won't be nested a third time.
In my case, the association
Quote
has_many :quote_items
.
QuoteItems has_many :quote_options, :through => quote_item_quote_options
.
In quotes_controller.rb
params.require(:quote).permit(:quote_date, :good_through, :quote_number, quote_items_attributes: [:id,:quote_id, :item_name, :material_id, quote_item_quote_options_attributes:[:quote_option_id,:quote_item_id,:qty,:_destroy,:id]])