Can't permit nested param in Rails 4 - ruby-on-rails

I've been ripping my hair off with this one. I have read all the doc about Rails 4 integrating strong params and that now everything has to be explicitly whitelisted. But it still won't go through!!!
Here is my setup
Models
class Course < ActiveRecord::Base
has_many :chapters
accepts_nested_attributes_for :chapters
end
class Chapter < ActiveRecord::Base
belongs_to :course
end
Controller
class CoursesController < ApplicationController
respond_to :json
def create
#course = Course.create permitted_params
respond_with #course
end
private
def permitted_params
params.require(:course).permit(:name, chapters_attributes: [:title, :content])
end
end
JSON from client
{
"course": {
"chapters": [{
"title": "qwerty",
"content": "foobar"
}],
"name": "Test course"
}
}
Server log
Started POST "/json/courses" for 10.0.2.2 at 2014-02-24 15:29:44 +0000
Processing by CoursesController#create as JSON
Parameters: {"course"=>{"chapters"=>[{"title"=>"qwerty", "content"=>"foobar"}], "name"=>"Test course"}}
Unpermitted parameters: chapters
Completed 201 Created in 96ms (Views: 52.1ms | ActiveRecord: 4.1ms)
Unpermitted params: chapters. I've been staring at this for hours with no avail. I honestly don't know what I'm doing wrong. Please tell me its right there and I just forgot some stupid magical param so I can move on.

I believe you just need to change to 'chapters' in your permitted_params method :
def permitted_params
params.require(:course).permit(:name, chapters: [:title, :content])
end
instead of "chapters_attributes"

I believe the issue is not in the controller or model, but in the JSON sent in the request.
Processing by CoursesController#create as JSON
Parameters: {"course"=>{"chapters"=>[{"title"=>"qwerty", "content"=>"foobar"}], "name"=>"Test course"}}
should instead be
Processing by CoursesController#create as JSON
Parameters: {"course"=>{"chapters_attributes"=>[{"title"=>"qwerty", "content"=>"foobar"}], "name"=>"Test course"}}
If you post your view code we can probably track down the issue fairly quickly.

Related

Rails API not receiving cURL POST with nested json correctly

I have a Rails 5 api which should be able to create Visit objects from JSON sent in HTTP POST requests and store that in the database.
The Visit model has an association (has_one) with the VisitComplement model.
The post request that I'm sending with cURL also has fields for this association.
The problem is that the VisitComplement data is not being stored in the database, only the Visit data is being stored.
Visit model:
class Visit < ApplicationRecord
has_one :visit_complement
has_one :physical_exam
accepts_nested_attributes_for :visit_complement
end
VisitsController:
class VisitsController < ApplicationController
def create
#visit = Visit.new(visit_params)
if #visit.save
render json: #visit, status: :created, location: #visit
else
render json: #visit.errors, status: :unprocessable_entity
end
end
private
def visit_params
params.require(:visit).permit(:fumo, :alcool, visit_complement_attributes: [:sintomas])
end
end
The cURL POST request:
curl -H "Accept: application/json" -H "Content-type: application/json" -d '{"fumo": "10", "visit_complement_attributes": {"sintomas": "febre"}}' http://localhost:3000/api/v1/visits
When I run the above command I get the following output from the server:
<!-- language: lang-none -->
Started POST "/api/v1/visits" for ::1 at 2016-11-13 16:14:56 -0200
Processing by VisitsController#create as JSON
Parameters: {"fumo"=>"10", "visit_complement_attributes"=>{"sintomas"=>"febre"}, "visit"=>{"fumo"=>"10"}}
Can't verify CSRF token authenticity.
(0.2ms) begin transaction
SQL (0.5ms) INSERT INTO "visits" ("fumo", "created_at", "updated_at") VALUES (?, ?, ?) [["fumo", 10], ["created_at", 2016-11-13 18:14:56 UTC], ["updated_at", 2016-11-13 18:14:56 UTC]]
(1.0ms) commit transaction
Completed 201 Created in 33ms (Views: 3.0ms | ActiveRecord: 2.4ms)
Aditional Info
Although it's a rails API, I didn't create it with the --api option.
I'm using the rack-cors gem to bypass the CSRF authentication problem
I don't have a controller for the VisitComplement model.
Update your Visit model as shown below.
class Visit < ApplicationRecord
has_one :visit_complement
has_one :physical_exam
accepts_nested_attributes_for :visit_complement
attr_accesible :visit_complement_attributes
end
I hope this works for you.
Found the answer. The problem is that my VisitComplement class was like this:
class VisitComplement < ApplicationRecord
belongs_to :visit
end
This belongs_to association expects that the father exists before
creating the child as said here Rails nested form error, child must exist
To solve the problem, either remove the association or add require: false.
Also had to change the visit_params method to:
def visit_params
params.permit(:id, :fumo, :visit_complement_attributes => [:id, :visit_id, :sintomas])
end

Strong params, nested attributes: Can't mass-assign

I'm using Rails 4.0.2 and can't get strong parameters to work with nested attributes.
# Model
has_one :availability
accepts_nested_attributes_for :availability
# Controller
def base_content_params
params.require("base_content").permit(:enabled, :language, :title, :description,
availability_attributes: [:duration, :slots])
end
# View
form_for [app, base_content] do |form|
form.fields_for :availability do | a |
a.select 'duration', duration_values
end
form.fields_for :availability do | a |
a.select 'slots', [*(1..10)]
end
But I keep getting:
Can't mass-assign protected attributes for BaseContent: availability_attributes
>> base_content_params
=> {"enabled"=>"false", "title"=>"test", "description"=>"", availability_attributes"=>{"duration"=>"30", "slots"=>"10"}}
# request parameters
{"utf8"=>"✓", "_method"=>"patch", "authenticity_token"=>"---", "base_content"=>{"enabled"=>"false", "language_id"=>"12938", "title"=>"test", "description"=>"", "content"=>"", "terms"=>"", "category"=>"product", "category_mode"=>"appy_booking", "responder_email"=>"", "price"=>"111.00", "price_per"=>"unit", "availability_attributes"=>{"start_at(5i)"=>"17:45:00", "id"=>"1", "duration"=>"30", "slots"=>"10"}, "reward_points"=>"100", "hash_tags"=>"", "lat"=>"", "lng"=>""}, "commit"=>"Save & Continue edit", "geocoder_lat"=>"0.0", "geocoder_lng"=>"0.0", "pac-input"=>"", "action"=>"update", "controller"=>"backend/base_contents", "app_id"=>"1898", "id"=>"16108"}
What am I missing here?
EDIT
# BaseContent Model
class BaseContent < ActiveRecord::Base
attr_accessible :enabled, :price, :price_per, :app, :menu,
# App Model
class App < ActiveRecord::Base
attr_accessible :name, :allow_search, :display_logo_badge, #... etc
This should be a comment, but it was too long.
From your comments, it's now clearer what the problem is. If you're using Rails 4.0.2, you have to switch to using strong_params in your controller:
#app/controllers/your_controller.rb
class YourController < ApplicationController
def create
#model = Model.new model_params
#model.save
end
private
def model_params
params.require(:model).permit(:attribute1, :attribute2)
end
end
I would strongly recommend you go through your models, remove any attr_accessible references, get rid of the protected_attributes gem and rebuild the functionality for strong params.
--
Another issue I can see is the way you're calling your form:
form_for [app, base_content] do |form|
Why are you nesting base_content (which should be an instance variable) under app? If anything, I'd expect something along the lines of...
form_for #base_content do |form|

Rails 4 API with Strong Parameters?

I'm building a simple API with Rails 4, but with my "create" method, it all goes horribly wrong.
Here is the relevant part of my routes file:
namespace :api, defaults: { format: 'json' } do
# /api/... Api::
scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do
resources :users
end
end
Here is the api/v1/users_controller.rb:
class Api::V1::UsersController < ApplicationController
protect_from_forgery except: :create
respond_to :json
def index
respond_to do |format|
format.html {render text: "Your data was sucessfully loaded. Thanks"}
format.json { render text: User.last.to_json }
end
end
def show
respond_with User.find(params[:id])
end
def create
respond_with User.create(user_params)
end
def update
respond_with User.update(params[:id], params[:users])
end
def destroy
respond_with User.destroy(params[:id])
end
private
def user_params
params.require(:user).permit(:name, :age, :location, :genre_ids => [], :instrument_ids => [])
end
end
Whenever I try to add an API with JSON, I get "{"errors":{"name":["can't be blank"]}}"
It works to create a user with my regular controller, but I have a feeling my API controller is getting messed up because of the Strong Parameters.
Any suggestions for how to do this correctly in Rails 4?
Also, I have a few Has-Many-Through relationships through my user model. The API's user controller should be able to see that off the bat, right?
Thanks
EDIT:
I'm now getting this error:
EDIT:
{
"name": "Sally",
"age": "23",
"location": "Blue York",
"genre_ids": [1, 2, 3]
}
EDIT AGAIN
Even with adding the User parameter in my JSON call, it still gives me the same error of the :user param missing. Am I using strong parameters incorrectly? In my "regular" users_controller, I can create a user easily with a form that I have set up, but with this API controller, I can't seem to create one with JSON. Any other suggestions?
EDIT YET AGAIN
Here Is The Log From Start to Error
rails s
=> Booting WEBrick
=> Rails 4.0.1 application starting in development on http://0.0.0.0:3000
=> Run `rails server -h` for more startup options
=> Ctrl-C to shutdown server
[2013-12-19 14:03:01] INFO WEBrick 1.3.1
[2013-12-19 14:03:01] INFO ruby 1.9.3 (2013-02-22) [x86_64-darwin10.8.0]
[2013-12-19 14:03:01] INFO WEBrick::HTTPServer#start: pid=53778 port=3000
Started GET "/api/users" for 127.0.0.1 at 2013-12-19 14:03:02 -0500
ActiveRecord::SchemaMigration Load (0.1ms) SELECT "schema_migrations".* FROM "schema_migrations"
Processing by Api::V1::UsersController#index as JSON
User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT 1
Rendered text template (0.0ms)
Completed 200 OK in 142ms (Views: 27.8ms | ActiveRecord: 0.6ms)
[2013-12-19 14:03:03] WARN Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true
[2013-12-19 14:03:03] WARN Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true
Started POST "/api/users" for 127.0.0.1 at 2013-12-19 14:03:37 -0500
Processing by Api::V1::UsersController#create as JSON
Completed 400 Bad Request in 1ms
ActionController::ParameterMissing (param not found: user):
app/controllers/api/v1/users_controller.rb:40:in `user_params'
app/controllers/api/v1/users_controller.rb:20:in `create'
Rendered /usr/local/rvm/gems/ruby-1.9.3-p392/gems/actionpack- 4.0.1/lib/action_dispatch/middleware/templates/rescues/_source.erb (0.7ms)
Rendered /usr/local/rvm/gems/ruby-1.9.3-p392/gems/actionpack-4.0.1/lib/action_dispatch/middleware/templates/rescues/_trace.erb (1.0ms)
Rendered /usr/local/rvm/gems/ruby-1.9.3-p392/gems/actionpack-4.0.1/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (0.8ms)
Rendered /usr/local/rvm/gems/ruby-1.9.3-p392/gems/actionpack- 4.0.1/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (31.6ms)
EDIT #6
Here is my "real" users_controller that lives in my app and not my API. The form creates a user from this controller and NOT the API controller.
class UsersController < ApplicationController
def index
#users = User.all
#genres = Genre.all
#instruments = Instrument.all
render json: #users
end
def new
#user = User.new
end
def show
#user = User.find(params[:id])
end
def create
#user = User.new(user_params)
if #user.save
render json: #user, status: :created, location: #user
else
render json: #user.errors, status: :unprocessable_entity
end
end
private
def user_params
params.require(:user).permit(:name, :age, :location, :genre_ids => [], :instrument_ids => [])
end
end
ALSO - The User Form
<div class="row">
<div class="span6 offset3">
<%= form_for(#user) do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :age %>
<%= f.text_field :age %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :location %>
<%= f.text_field :location %>
<br>
<% Genre.all.each do |genre| %>
<%= check_box_tag "user[genre_ids][]", genre.id %>
<%= genre.name %><br>
<% end %>
<br>
<% Instrument.all.each do |instrument| %>
<%= check_box_tag "user[instrument_ids][]", instrument.id %>
<%= instrument.name %><br>
<% end %>
<%= f.submit "Create My Account!" %>
<% end %>
</div>
</div>
<%= users_path %>
Here is my user.rb File
class User < ActiveRecord::Base
validates :name, presence: true, length: { maximum: 50 }
has_many :generalizations
has_many :genres, through: :generalizations
has_many :instrumentations
has_many :instruments, through: :instrumentations
end
Here is what I have in my routes file:
namespace :api do
namespace :v1 do
resources :users
end
end
My POST Request
POST /api/v1/users HTTP/1.1
Host: localhost:3000
Cache-Control: no-cache
{ "user": { "name": "Sally", "age": "23", "location": "Blue York", "genre_ids": [1, 2, 3] } }
UPDATE
I changed my strong-params to be this:
def user_params
params.require(:user).permit(:name, :age, :location, :genre_ids => [], :instrument_ids => []) if params[:user]
end
So the "if" statement at the end makes the error go away, but whenever I post to my API, it gives me back "null". So this could be the same problem as before, but shown in a different way. But, at the same time, it could be progress!
Here Is The Log For The Previous Update
Started POST "/api/v1/users" for 127.0.0.1 at 2013-12-21 11:38:03 -0500
Processing by API::V1::UsersController#create as */*
(0.1ms) begin transaction
[deprecated] I18n.enforce_available_locales will default to true in the future. If you really want to skip validation of your locale you can set I18n.enforce_available_locales = false to avoid this message.
(0.1ms) rollback transaction
User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT 1
Rendered text template (0.0ms)
Completed 200 OK in 20ms (Views: 0.3ms | ActiveRecord: 0.6ms)
FINAL UPDATE
I was missing a few things, but the main thing that did it was that I was missing "Content-Type - application/json" as my Header.
I feel so accomplished! Thanks for all your help, everyone!
According to your code parameters in the JSON you are posting should be inside params[:user]. So the JSON should look like:
{
"user": {
"name": "Sally",
"age": "23",
"location": "Blue York",
"genre_ids": [1, 2, 3]
}
}
Rails 4 is a great choice for building APIs. I would go with the rails-api gem. It will perform way better than a full blown Rails stack.
I have built plenty of API's in Rails using the Rails API gem. Usually in combination with RABL (which you can use to create nice templates to render your JSON). I am not a big fan of integrating an API directly into your production Rails app (serving websites and JSON) as you will create a big mess over time when starting to add more versions to your API. There are some very good Railscasts (www.railscasts.com): Search for API.
When accessing your API you would use a global filter in your application_controller.rb file. You could do something like this:
before_filter :authenticate_user, :except => 'users#index'
private
def authenticate_user
#current_user = User.find_by_api_key(params[:token])
unless #current_user
render :status=>403, :json=>{:message=>"Invalid token"}
end
end
def current_user
#current_user
end
end
In this case you would send the token in your request (that's quick and dirty, rather use the header instead) as a request parameter. You need to add the API key or whatever you want to use to your user model. Just create a migration adding api_key or however you want to call it to the user model or create a new table with keys, secrets etc. and a user_id field for your belongs_to (and a User has_many api_keys, or has_one). This way you can allow your users at any time to change their keys etc. (re-generate) without messing with usernames/password or even allow them to have multiple API keys with some tags (testing, production, etc). For your user signup you could add to your model:
before_create :generate_api_key
and then create a simple method like:
def generate_api_key
begin
self.api_key = SecureRandom.hex
end while self.class.exists?(api_key: api_key)
end
Hope it helps!

Rails 3 : How can I make auto grading system nested_form based online test system?

I'm still making online test program.
This is my model.
Survey
class Survey < ActiveRecord::Base
belongs_to :user
has_many :questions, :dependent => :destroy
accepts_nested_attributes_for :questions, :reject_if => lambda {|a| a[:content].blank?}, :allow_destroy => true
Question : there is is_correct column which indicates whether students get the right answer or not.
class Question < ActiveRecord::Base
belongs_to :survey
has_many :answers, :dependent => :destroy
accepts_nested_attributes_for :answers, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
Answer : there is correct column which teacher checks making the survey(test), and there is user_answer column which students mark taking the test.
class Answer < ActviveRecord::Base
belongs_to :question
and I made taking exam interface on show.html.erb in survey views. So if students fill this check box and click the submit button, they can get their result page. but I can't show the result in the result page.
This is my survey controller.
def grading
#survey = Survey.new
#survey.user_id = current_user.id
if #survey.questions.answers.user_answer and #survey.questions.answers.correct
#survey.questions.is_correct = true
end
redirect_to results_surveys_path(#survey)
end
def results
end
The error message I saw is 'undefined method `answers' for []:ActiveRecord::Relation'. I thought that there were problem between question and answer table...
I thought auto grading part is easy, but I was wrong. I have no idea about this and I don't have any reference, except your help.
any idea welcome.
Thanks advanced.
Updated Question
Here is another question.
Now I can access to nested objects.(I think and I hope) but the result page(result.html.erb in survey views) can't show any result : "undefined method `name' for nil:NilClass".
result.html.erb
<h1><%= #survey.name %></h1>
<h3><%= #survey.user.username %></h3>
As I told in previous link, I have another form_tag in show.html.erb in survey views. then redirect to the result page with routes.rb.
resources :surveys do
collection do
get 'results'
end
end
I thought I can show the result page using is_correct column in question tables.
I didn't write anything in the result method in survey controller. Because when I redirect the page I wrote like this. Which means using #survey in result method, doesn't it?
redirect_to results_surveys_path(#survey)
Here is the result of rake routes.
seriousin#classcasts:~/ClassCasts$ rake routes | grep survey
results_surveys GET /surveys/results(.:format) surveys#results
surveys GET /surveys(.:format) surveys#index
POST /surveys(.:format) surveys#create
new_survey GET /surveys/new(.:format) surveys#new
edit_survey GET /surveys/:id/edit(.:format) surveys#edit
survey GET /surveys/:id(.:format) surveys#show
PUT /surveys/:id(.:format) surveys#update
DELETE /surveys/:id(.:format) surveys#destroy
surveys_grading POST /surveys/grading(.:format) surveys#grading
seriousin#classcasts:~/ClassCasts$
I think my basic idea caused all of my problem. Here is my survey controller.
class SurveysController < ApplicationController
def index
#surveys = Survey.all
end
def grading
#survey = Survey.new
#survey.user_id = current_user.id
#survey.questions.each do |question|
question.auto_check
end
redirect_to results_survey_path(#survey)
end
def results
#survey = Survey.where(params[:id])
end
def show
#survey = Survey.find(params[:id])
end
def new
#survey = Survey.new
end
def edit
#survey = Survey.find(params[:id])
end
def create
#survey = Survey.new(params[:survey])
#survey.user_id = current_user.id
end
def update
#survey = Survey.find(params[:id])
end
def destroy
#survey = Survey.find(params[:id])
#survey.destroy
end
end
as you can see, I'm using show page in survey views as another input form with grading method. I can use '#survey = Survey.new' in create method, it makes sense! but as I wrote in grading method, it generates another new survey, I think.
So I need to change that line. can you please help me?
Sending data
OK. when I submit in _form.html.erb in survey views, I can send data like this.
Parameters: {"id"=>"14", "survey"=>{"name"=>"The First Test!", "description"=>"This is the first test!", "questions_attributes"=>{"0"=>{"_destroy"=>"false", "id"=>"41", "content"=>"Question 2", "answers_attributes"=>{"0"=>{"_destroy"=>"false", "id"=>"66", "content"=>"Answer 2 of Question 2", "correct"=>"0"}, "1"=>{"_destroy"=>"false", "id"=>"67", "content"=>"Answer 1 of Question 2", "correct"=>"1"}}}, "1"=>{"_destroy"=>"false", "id"=>"42", "content"=>"Question 1", "answers_attributes"=>{"0"=>{"_destroy"=>"false", "id"=>"68", "content"=>"Answer 2 of Question 1", "correct"=>"0"}, "1"=>{"_destroy"=>"false", "id"=>"69", "content"=>"Answer 1 of Question 1", "correct"=>"1"}}}, "1376575795482"=>{"_destroy"=>"false", "content"=>"Question 3", "answers_attributes"=>{"1376575802879"=>{"_destroy"=>"false", "content"=>"Answer 1 of Question 3", "correct"=>"0"}}}}}, "commit"=>"Update Survey", "utf8"=>"✓", "authenticity_token"=>"/vNuB5Ck3QM5p+5ksL3tlmb+ti5nTA/qS96+vbPQkNw="}
This is OK. but because of form_tag in show.html.erb, show page contains another input form.
<%= form_tag({:controller => "surveys", :action => "grading"}) do %>
after submit again in show.html.erb I want to redirect to the results.html.erb with proper result. but there are errors like this.
Started POST "/surveys/grading" for 110.174.136.30 at Thu Aug 15 23:19:52 +0900 2013
Processing by SurveysController#grading as HTML
Parameters: {"utf8"=>"✓", "#<Answer:0x7fafd8c5f5a0>"=>"1", "#<Answer:0x7fafd95704a0>"=>"0", "#<Answer:0x7fafd9116a58>"=>"1", "authenticity_token"=>"/vNuB5Ck3QM5p+5ksL3tlmb+ti5nTA/qS96+vbPQkNw=", "#<Answer:0x7fafd8d03a38>"=>"0", "commit"=>"Submit", "#<Answer:0x7fafd8cfc580>"=>"1"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 12 LIMIT 1
Completed 404 Not Found in 3ms
ActionController::RoutingError (No route matches {:action=>"results", :id=>#<Survey id: nil, name: nil, description: nil, attempts: nil, user_id: 12, created_at: nil, updated_at: nil>, :controller=>"surveys"}):
app/controllers/surveys_controller.rb:31:in `grading'
Do you think that I need to change whloe answering mechanism?
Try like this, you are trying to call your method on an array. your have to iterate over it and then you can assign any value to it. you can do some re-factoring in your code as well to avoid loops.
in your grading method, do this:
def grading
#survey = Survey.new
#survey.user_id = current_user.id
#survey.questions.each do |question|
question.auto_check
end
redirect_to results_surveys_path(#survey)
end
In your Question model write a method auto_check like this:
def auto_check
answers.each do |answer|
is_correct = true if answer.user_answer and answer.correct
self.save!
end
end
I think it is a better approach.Thanks.
Updated Answer:
Whenever you try to pass an id in your path, that means that is a member function, if you are defining collection in your routes, that means you cannot pass an id in that(As you routes output shows. Have a keen look into it). change your routes like this:
resources :surveys do
member do
  get 'results'
end
end
Now you can access your url like this:
results_survey_path(#survey)
and in your results method :
def results
#survey = Survey.where(params[:id])
end
Hope, now it will work.

Rails 4 Strong Params has_many with JSON

I'm attempting to pass json up on the client side and have rails take care of handling the object creation.
Here are my models:
class Order < ActiveRecord::Base
has_many :order_items, :autosave => true
belongs_to :menu_session
end
class OrderItem < ActiveRecord::Base
belongs_to :order
has_one :menu_item
end
Controller
class OrderController < ApplicationController
#POST /order/create
def create
#order = Order.new(order_params)
#order.save
end
private
def order_params
params.require(:order).permit(:comments, :menu_session_id, :order_items => [:menu_item_id])
end
end
The json data:
{'order': {'comments': 'none', 'menu_session_id': '9', 'order_items':[{'menu_item_id': '5'}, {'menu_item_id': '5'}]}};
The javascript
var data = {};
data.order = {'comments': 'none', 'menu_session_id': '9', 'order_items':[{'menu_item_id': '5'}, {'menu_item_id': '5'}]};
$.post('http://localhost:3000/order/create', orders, function(){}, 'json');
Finally, the error log:
Started POST "/order/create" for 127.0.0.1 at 2013-07-10 22:30:36 -0400
Processing by OrderController#create as JSON
Parameters: {"order"=>{"comments"=>"none", "menu_session_id"=>"9", "order_items"=>{"0"=>{"menu_item_id"=>"5"}, "1"=>{"menu_item_id"=>"5"}}}}
Completed 500 Internal Server Error in 52ms
ActiveRecord::AssociationTypeMismatch (OrderItem(#28109220) expected, got Array(#16050620)):
app/controllers/order_controller.rb:5:in `create'
Clearly, either my json is messed up or the ruby .permit is wrong. However, I've been playing with variations of this for a while now and cannot get it to work. The official documentation doesn't seem to venture into this, and every example I have found here deals with forms.
Anyone have any idea what is going on? I can't be the first to try this approach.
UPDATE:
Worked around it by making the following changes:
class OrderController < ApplicationController
#POST /order/create
def create
#order = Order.new(order_params)
order_items = order_item_params
order_items.each do |item|
#order.order_items << OrderItem.new(menu_item_id: item)
end
#order.save
end
private
def order_params
params.require(:order).permit(:comments, :menu_session_id)
end
def order_item_params
params.require(:order_items)
end
end
json: {"order":{"comments":"none","menu_session_id":"9"},"order_items":["5","5"]}
I don't think this would be the best way to do it, so I'm going to leave the question unanswered for now in hopes there is a best practice.
The workaround is not necessary in this case. ActiveRecord provides an automagic way of creating child elements directly through the params hash. In order to accomplish this, follow the steps bellow:
Configure Nested Attributes in the model
class Order < ActiveRecord::Base
# autosave is already enabled with accepts_nested_attributes_for
has_many :order_items
belongs_to :menu_session
accepts_nested_attributes_for :order_items
end
Include a *_attributes key in your JSON. In your case, change the order_items key to order_items_attributes
{'order': {'comments': 'none', 'menu_session_id': '9', 'order_items_attributes':[{'menu_item_id': '5'}, {'menu_item_id': '5'}]}};
In your controller, make permit accept your new key
def order_params
params.require(:order).permit(:comments, :menu_session_id, :order_items_attributes => [:menu_item_id])
end
There is some more awesomeness possible to accomplish with Nested Attributes. For further information, see ActiveRecord::NestedAttributes at Rails API

Resources