Related
I am using Rails as a backend and React as my front-end. On the React side, I am using fetch to do POST request to my model named schedule. I am also adding a child attributes for worker model.
Here are some code snippets that I have. I am using has_many :through relationship in rails.
My Rails models and controller:
//schedule.rb
has_many :workers, through: :rosters, dependent: :destroy
has_many :rosters, inverse_of: :schedule
//worker.rb
has_many :schedules, through: :rosters
has_many :rosters, inverse_of: :worker
//roster.rb
belongs_to :schedule
belongs_to :worker
//schedules_controller.rb
def create
#schedule = Schedule.new(schedule_params)
#workers = #schedule.rosters.build.build_worker
if #schedule.save
render json: #schedule
else
render json: #schedule, status: :unprocessable_entity
end
end
...
def schedule_params
params.permit(:date, :user_id, :workers_attributes => [:id, :name, :phone])
end
On React side:
//inside Client.js
function postSchedule(date, cb) {
return fetch(`api/schedules`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
date: date,
user_id: 1,
workers_attributes: [{name: "Iggy Test", phone: "123-456-7890"}, {name: "Iggy Test 2", phone: "987-654-3210"}]
})
}).then((response) => response.json())
.then(cb);
};
//inside main app:
postSchedule(){
Client.postSchedule(this.state.date, (schedule) => {
this.setState({schedules: this.state.schedules.concat([schedule])})
})
};
The problem that I have is, when I submit a new schedule, I expect to see a new schedule with two workers: "Iggy Test" and "Iggy Test 2". However, when I looked inside Rails, it is creating 3 workers: "Iggy Test", "Iggy Test 2", and nil.
Here is what is happening when I submit the request:
Started POST "/api/schedules" for 127.0.0.1 at 2017-05-24 09:55:16 -0700
Processing by SchedulesController#create as */*
Parameters: {"date"=>"2017-05-27T02:00:00.000Z", "user_id"=>1, "workers_attributes"=>[{"name"=>"Iggy Test", "phone"=>"
123-456-7890"}, {"name"=>"Iggy Test 2", "phone"=>"987-654-3210"}], "schedule"=>{"date"=>"2017-05-27T02:00:00.000Z", "use
r_id"=>1}}
Unpermitted parameter: schedule
(0.1ms) begin transaction
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
SQL (0.4ms) INSERT INTO "schedules" ("date", "created_at", "updated_at", "user_id") VALUES (?, ?, ?, ?) [["date", 20
17-05-27 02:00:00 UTC], ["created_at", 2017-05-24 16:55:16 UTC], ["updated_at", 2017-05-24 16:55:16 UTC], ["user_id", 1]
]
SQL (0.2ms) INSERT INTO "workers" ("name", "phone", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["name", "Iggy
Test"], ["phone", "123-456-7890"], ["created_at", 2017-05-24 16:55:16 UTC], ["updated_at", 2017-05-24 16:55:16 UTC]]
SQL (0.2ms) INSERT INTO "rosters" ("worker_id", "created_at", "updated_at") VALUES (?, ?, ?) [["worker_id", 64], ["c
reated_at", 2017-05-24 16:55:16 UTC], ["updated_at", 2017-05-24 16:55:16 UTC]]
SQL (0.1ms) INSERT INTO "workers" ("name", "phone", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["name", "Iggy
Test 2"], ["phone", "987-654-3210"], ["created_at", 2017-05-24 16:55:16 UTC], ["updated_at", 2017-05-24 16:55:16 UTC]]
SQL (0.1ms) INSERT INTO "rosters" ("worker_id", "created_at", "updated_at") VALUES (?, ?, ?) [["worker_id", 65], ["c
reated_at", 2017-05-24 16:55:16 UTC], ["updated_at", 2017-05-24 16:55:16 UTC]]
SQL (0.2ms) INSERT INTO "workers" ("created_at", "updated_at") VALUES (?, ?) [["created_at", 2017-05-24 16:55:16 UTC
], ["updated_at", 2017-05-24 16:55:16 UTC]]
SQL (0.2ms) INSERT INTO "rosters" ("schedule_id", "worker_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["sc
hedule_id", 57], ["worker_id", 66], ["created_at", 2017-05-24 16:55:16 UTC], ["updated_at", 2017-05-24 16:55:16 UTC]]
SQL (0.7ms) UPDATE "rosters" SET "schedule_id" = ?, "updated_at" = ? WHERE "rosters"."id" = ? [["schedule_id", 57],
["updated_at", 2017-05-24 16:55:16 UTC], ["id", 60]]
SQL (0.1ms) UPDATE "rosters" SET "schedule_id" = ?, "updated_at" = ? WHERE "rosters"."id" = ? [["schedule_id", 57],
["updated_at", 2017-05-24 16:55:16 UTC], ["id", 61]]
(2.6ms) commit transaction
Completed 200 OK in 68ms (Views: 0.8ms | ActiveRecord: 5.6ms)
The log created a schedule, then a worker (Iggy Test), then a roster (for that schedule and Iggy Test), then another worker (Iggy Test 2), then another roster (for Iggy Test 2 and that schedule) - instead of stopping, it created another worker (nil) and a roster for that nil worker.
Why is it behaving such? How can I fix it to create only the specified workers?
As a side note - if you noticed, the log says unpermitted parameter: schedule. That message disappears when I add require(:schedule) inside my schedule_params, but it would instead create only one nil worker.
accepts_nested_attributes_for is not creating an extra record. You are.
def create
#schedule = Schedule.new(schedule_params)
# This adds a worker with no attributes
#workers = #schedule.rosters.build.build_worker
if #schedule.save
render json: #schedule
else
render json: #schedule, status: :unprocessable_entity
end
end
"Unpermitted parameter: schedule" is just a warning that there was a param is in the params hash that was not whitelisted by .permit. It is logged since it could be a malicious attempt to fish for mass assignment vulnerabilities.
.require takes a single key from the params hash and raises an error if it not present and is not what you want when you have a flat params hash.
Instead you should look into why the params sent by react include both the unwrapped params and I don't get why you are sending both the unwrapped params and a "schedule"=>{"date"=>"2017-05-27T02:00:00.000Z", "user_id"=>1} hash. I don't really know React but I'm guessing it has something to do with this.state.schedules.concat([schedule])
I have looked at previous SO solutions on accepts_nested_attributes here and here, but I am still getting the error. I am using React as front end and Rails back end. I am trying to create a request to be sent to schedules, and from there to populate to workers.
I am using Rails 5.0.2. I have a schedule, worker, roster models.
//Schedule
has_many :workers, through: :rosters, dependent: :destroy
has_many :rosters
accepts_nested_attributes_for :workers #implement accept_nested_attributes here
//Roster
belongs_to :schedule
belongs_to :worker
//Worker
has_many :schedules, through: :rosters
has_many :rosters
And here is my Schedule controller:
def create
#schedule = Schedule.new(schedule_params)
if #schedule.save
render json: #schedule
else
render json: #schedule, status: :unprocessable_entity
end
end
...
private
def schedule_params
params.permit(:date, :user_id, :workers_attributes => [:worker_id, :name, :phone])
end
Here is the error that I got:
app/controllers/schedules_controller.rb:13:in `create'
Started POST "/api/schedules" for 127.0.0.1 at 2017-05-23 10:30:38 -0700
Processing by SchedulesController#create as */*
Parameters: {"date"=>"2017-05-25T02:00:00.000Z", "user_id"=>1, "workers_attributes"=>{"name"=>"Iggy Test", "phone"=>"1
23-456-7890"}, "schedule"=>{"date"=>"2017-05-25T02:00:00.000Z", "user_id"=>1}}
Unpermitted parameter: schedule
Completed 500 Internal Server Error in 15ms (ActiveRecord: 0.0ms)
TypeError (no implicit conversion of Symbol into Integer):
app/controllers/schedules_controller.rb:13:in `create'
Why is my request shows Unpermitted parameter schedule? If I remove workers_attributes and only have params.permit(:date, :user_id), it works. I can't figure out why the error points to schedule. How can I make successful POST nested_attributes request to rails?
I am using fetch to do POST request from react side:
...
return fetch(`api/schedules`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
date: date,
user_id: 1,
workers_attributes: {name: "Iggy Test", phone: "123-456-7890"}
})
EDIT:
After following answer from #gabrielhilal, I added require(): params.require(:schedule).permit(:date, :user_id, :workers_attributes => [:id, :name, :phone]), and edited the fetch POST on React's end to have array of objects instead of plain objects: workers_attributes: [{name: "Iggy Test", phone: "123-456-7890"}]. It does not complain anymore, and it does register new schedule. However, new workers are all nil:
#Worker.last shows:
#<Worker id: 32, name: nil, phone: nil, created_at: "2017-05-23 19:36:09", updated_at: "2017-05-23 19:36:09">
Sorry, don't mean to create nested question, but does anyone know why it is nil?
EDIT 2:
I got it to work, sort of.
If I have
def create
#schedule = Schedule.new(schedule_params)
#workers = #schedule.rosters.build.build_worker
...
and
//schedule_params
params.permit(:date, :user_id, :workers_attributes => [:id, :name, :pho
ne])
I was able to have "Iggy Test" to display, but it immediately creates another nil worker.
Log:
Started POST "/api/schedules" for 127.0.0.1 at 2017-05-23 20:42:38 -0700
Processing by SchedulesController#create as */*
Parameters: {"date"=>"2017-05-26T02:00:00.000Z", "user_id"=>1, "workers_attributes"=>[{"name"=>"Iggy Test", "phone"=>"
123-456-7890"}], "schedule"=>{"date"=>"2017-05-26T02:00:00.000Z", "user_id"=>1}}
Unpermitted parameter: schedule
(0.1ms) begin transaction
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
SQL (1.1ms) INSERT INTO "schedules" ("date", "created_at", "updated_at", "user_id") VALUES (?, ?, ?, ?) [["date", 20
17-05-26 02:00:00 UTC], ["created_at", 2017-05-24 03:42:38 UTC], ["updated_at", 2017-05-24 03:42:38 UTC], ["user_id", 1]
]
SQL (0.2ms) INSERT INTO "workers" ("name", "phone", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["name", "Iggy
Test"], ["phone", "123-456-7890"], ["created_at", 2017-05-24 03:42:38 UTC], ["updated_at", 2017-05-24 03:42:38 UTC]]
SQL (0.2ms) INSERT INTO "rosters" ("worker_id", "created_at", "updated_at") VALUES (?, ?, ?) [["worker_id", 56], ["c
reated_at", 2017-05-24 03:42:38 UTC], ["updated_at", 2017-05-24 03:42:38 UTC]]
SQL (0.1ms) INSERT INTO "workers" ("created_at", "updated_at") VALUES (?, ?) [["created_at", 2017-05-24 03:42:38 UTC
], ["updated_at", 2017-05-24 03:42:38 UTC]]
SQL (0.6ms) INSERT INTO "rosters" ("schedule_id", "worker_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["sc
hedule_id", 52], ["worker_id", 57], ["created_at", 2017-05-24 03:42:38 UTC], ["updated_at", 2017-05-24 03:42:38 UTC]]
SQL (0.4ms) UPDATE "rosters" SET "schedule_id" = ?, "updated_at" = ? WHERE "rosters"."id" = ? [["schedule_id", 52],
["updated_at", 2017-05-24 03:42:38 UTC], ["id", 52]]
(5.6ms) commit transaction
Completed 200 OK in 417ms (Views: 6.9ms | ActiveRecord: 14.7ms)
If I modified params to have require(:schedule)
params.require(:schedule).permit(:date, :user_id, :workers_attributes => [:id, :name, :phone])
It creates a nil worker only.
Log:
Started POST "/api/schedules" for 127.0.0.1 at 2017-05-23 20:45:03 -0700
Processing by SchedulesController#create as */*
Parameters: {"date"=>"2017-05-26T02:00:00.000Z", "user_id"=>1, "workers_attributes"=>[{"name"=>"Iggy Test", "phone"=>"
123-456-7890"}], "schedule"=>{"date"=>"2017-05-26T02:00:00.000Z", "user_id"=>1}}
(0.1ms) begin transaction
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
SQL (0.4ms) INSERT INTO "schedules" ("date", "created_at", "updated_at", "user_id") VALUES (?, ?, ?, ?) [["date", 20
17-05-26 02:00:00 UTC], ["created_at", 2017-05-24 03:45:03 UTC], ["updated_at", 2017-05-24 03:45:03 UTC], ["user_id", 1]
]
SQL (0.2ms) INSERT INTO "workers" ("created_at", "updated_at") VALUES (?, ?) [["created_at", 2017-05-24 03:45:03 UTC
], ["updated_at", 2017-05-24 03:45:03 UTC]]
SQL (0.3ms) INSERT INTO "rosters" ("schedule_id", "worker_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["sc
hedule_id", 53], ["worker_id", 58], ["created_at", 2017-05-24 03:45:03 UTC], ["updated_at", 2017-05-24 03:45:03 UTC]]
(2.4ms) commit transaction
Completed 200 OK in 81ms (Views: 1.3ms | ActiveRecord: 8.3ms)
Your post:
{
"date"=>"2017-05-25T02:00:00.000Z",
"user_id"=>1,
"workers_attributes"=>{
"name"=>"Iggy Test",
"phone"=>"123-456-7890"
},
"schedule"=> {
"date"=>"2017-05-25T02:00:00.000Z",
"user_id"=>1
}
}
You have two issues:
schedule is not permitted (that's why you see the message in the logs), but it will be just ignored anyway (won't raise any error).
the workers_attributes should be a collection of workers and not a simple hash, so that's why you are having the error.
You should get something like the following in the post request:
{
"date"=>"2017-05-25T02:00:00.000Z",
"user_id"=>1,
"workers_attributes"=>{
"0" => {
"name"=>"Iggy Test",
"phone"=>"123-456-7890"
}
}
}
I'm using tinymce-rails-image-upload to upload images with paperclip (following this demo-app). When I try to upload an image I'm getting an 'umpermitted parameters' reminder and the image doesn't upload. The upload modal shows 'Got a bad response from server':
Processing by TinymceAssetsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"auth token", "hint"=>"", "file"=>#<ActionDispatch::Http::UploadedFile:0x000001025a2780 #tempfile=# <File:/var/folders/t4/86vsrmds42j84r36kwpng7k00000gn/T/RackMultipart20150207- 12522-9rj6xq>, #original_filename="applecash.jpg", #content_type="image/jpeg", #headers="Content-Disposition: form-data; name=\"file\"; filename=\"applecash.jpg\"\r\nContent-Type: image/jpeg\r\n">, "alt"=>""}
Command :: identify -format '%wx%h,%[exif:orientation]' '/var/folders/t4/86vsrmds42j84r36kwpng7k00000gn/T/RackMultipart20150207-12522-9rj6xq[0]' 2>/dev/null
Unpermitted parameters: utf8, authenticity_token
(0.1ms) begin transaction
Question Load (0.5ms) SELECT "questions".* FROM "questions" WHERE (questions.position IS NOT NULL) AND (1 = 1) ORDER BY questions.position DESC LIMIT 1
Binary data inserted for `string` type on column `file_content_type`
SQL (0.8ms) INSERT INTO "questions" ("created_at", "file_content_type", "file_file_name", "file_file_size", "file_updated_at", "position", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?) [["created_at", Sun, 08 Feb 2015 18:35:07 UTC +00:00], ["file_content_type", "image/jpeg"], ["file_file_name", "timcook.jpg"], ["file_file_size", 120040], ["file_updated_at", Sun, 08 Feb 2015 18:35:07 UTC +00:00], ["position", 9], ["updated_at", Sun, 08 Feb 2015 18:35:07 UTC +00:00]]
(7.3ms) commit transaction
Completed 200 OK in 68ms (Views: 0.6ms | ActiveRecord: 8.7ms)
Here's the controller:
class TinymceAssetsController < ApplicationController
respond_to :json
def create
geometry = Paperclip::Geometry.from_file params[:file]
question = Question.create params.permit(:file, :alt, :hint)
render json: {
question: {
url: question.file.url,
height: geometry.height.to_i,
width: geometry.width.to_i
}
}, layout: false, content_type: "text/html"
end
end
and the question model:
class Question < ActiveRecord::Base
has_attached_file :file
end
and the view:
<%= simple_form_for [#comment, Question.new] do |f| %>
<%= f.text_area :body, :class => "tinymce", :rows => 10, :cols => 60 %>
<% end %>
<%= tinymce plugins: ["uploadimage"] %>
It's ok to not permit some of the parameters, so Unpermitted parameters: utf8, authenticity_token is a reminder, not an exception. I suggest you log what are question model instance errors:
question = Question.create params.permit(:file, :alt, :hint)
logger.debug question.errors.full_messages
May be wrong, but, you have the following:
question = Question.create params.permit(:file, :alt, :hint)
I think you'd just make that read like this and it would work:
question = Question.create params.permit(:file, :alt, :hint, :utf8, :authenticity_token)
The error is clear. You have not permitted those parameters. But they exist. You expressly permit the other three there so I presume you have to permit those two additional.
That's my guess and I'm sticking to it :).
I have a create action in my ProductsController (the params come from a form in my view):
def create
vendor = #current_vendor
product = Product.create(:name => params[:product][:name])
vendor.products << product
vendor.belongings.create(:product_id => product.id, :count => params[:belonging][:count], :detail => params[:belonging][:detail])
if vendor.save
flash[:notice] = "Produkt hinzugefügt!"
redirect_back_or_default root_url
else
render :action => :new
end
end
It creates a variable "vendor", which stores the currently logged-in vendor (Authlogic)
A new Product is created (the product name is from the input field in the form) and stored in the variable "product"
The "product" is being connected to the current vendor
In the belongings table, additional informations to the product are being stored
it saves the whole thing
It's a many-to-many relationship throught the belongings table.
My problem is, the create action always creates the product twice!
Thanks for your help! :)
My console log when I create a new object through my form is:
Started POST "/products" for 127.0.0.1 at 2013-09-15 20:40:26 +0200
Processing by ProductsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"lNk/qQMP0xhlCuGgHtU+d5NEvIlCFcPSKB0FxDZH0zY=", "product"=>{"name"=>"Erdbeeren"}, "belonging"=>{"count"=>"20", "detail"=>"Rot"}, "commit"=>"Create"}
DEPRECATION WARNING: ActiveRecord::Base#with_scope and #with_exclusive_scope are deprecated. Please use ActiveRecord::Relation#scoping instead. (You can use #merge to merge multiple scopes together.). (called from current_vendor_session at /Users/reto_gian/Desktop/dici/app/controllers/application_controller.rb:11)
Vendor Load (0.3ms) SELECT "vendors".* FROM "vendors" WHERE "vendors"."persistence_token" = '04f75db0e2ef108ddb0ae1be1da167536d47b4d79c60ecb443ad2ea5717ecd752388e581f9379746568c72372be4f08585aa5581915b1be64dc412cded73a705' LIMIT 1
(0.1ms) begin transaction
SQL (0.8ms) INSERT INTO "products" ("created_at", "name", "updated_at") VALUES (?, ?, ?) [["created_at", Sun, 15 Sep 2013 18:40:26 UTC +00:00], ["name", "Erdbeeren"], ["updated_at", Sun, 15 Sep 2013 18:40:26 UTC +00:00]]
(0.8ms) commit transaction
(0.1ms) begin transaction
SQL (0.6ms) INSERT INTO "belongings" ("created_at", "product_id", "updated_at", "vendor_id") VALUES (?, ?, ?, ?) [["created_at", Sun, 15 Sep 2013 18:40:26 UTC +00:00], ["product_id", 7], ["updated_at", Sun, 15 Sep 2013 18:40:26 UTC +00:00], ["vendor_id", 1]]
(0.9ms) commit transaction
(0.1ms) begin transaction
SQL (0.6ms) INSERT INTO "belongings" ("count", "created_at", "detail", "product_id", "updated_at", "vendor_id") VALUES (?, ?, ?, ?, ?, ?) [["count", "20"], ["created_at", Sun, 15 Sep 2013 18:40:26 UTC +00:00], ["detail", "Rot"], ["product_id", 7], ["updated_at", Sun, 15 Sep 2013 18:40:26 UTC +00:00], ["vendor_id", 1]]
(0.9ms) commit transaction
Redirected to http://localhost:3000/
Completed 302 Found in 30ms (ActiveRecord: 5.1ms)
I think the problem could be in the line
vendor.products << product
this is adding the variable product (which is Product.create(:name => params[:product][:name]) to vendor.products a second time - which is unnecessary and likely the source of your problem
I have declared the accepts_nested_attributes_for and everything works fine but only when I save the model into the database, the nested model is not saved along the master model, anyone know why?
In my log/development.log file:
Started POST "/users/1/business_infos" for 127.0.0.1 at 2011-10-22 23:08:59 -0500
Processing by BusinessInfosController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Vw9GWH4cnr8z+PPged0mal/CVsbdBM1NcDehRb7zl7Q=", "business_info"=>{"business_name"=>"microsoft", "locations_attributes"=>{"0"=>{"address"=>"New York, NY"}, "1"=>{"address"=>""}, "2"=>{"address"=>""}}, "business_hours"=>"5am-7pm, Mon - Fri", "business_logo"=>#<ActionDispatch::Http::UploadedFile:0xa219684 #original_filename="rails.png", #content_type="image/png", #headers="Content-Disposition: form-data; name=\"business_info[business_logo]\"; filename=\"rails.png\"\r\nContent-Type: image/png\r\n", #tempfile=#<File:/tmp/RackMultipart20111022-28086-fbv7x6>>, "business_phone"=>"6666666666", "business_web_addr"=>"www.microsoft.com"}, "commit"=>"Create", "user_id"=>"1"}
[1m[36mUser Load (0.3ms)[0m [1mSELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1[0m [["id", "1"]]
[1m[35mSQL (4.9ms)[0m INSERT INTO "business_infos" ("business_addr", "business_hours", "business_logo", "business_name", "business_phone", "business_web_addr", "confirmed", "created_at", "multi_location", "qr_code", "updated_at", "user_id") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) [["business_addr", nil], ["business_hours", "5am-7pm, Mon - Fri"], ["business_logo", "/assets/business_logo/microsoft.png"], ["business_name", "microsoft"], ["business_phone", "6666666666"], ["business_web_addr", "www.microsoft.com"], ["confirmed", nil], ["created_at", Sun, 23 Oct 2011 04:08:59 UTC +00:00], ["multi_location", nil], ["qr_code", nil], ["updated_at", Sun, 23 Oct 2011 04:08:59 UTC +00:00], ["user_id", 1]]
[1m[36mBusinessInfo Load (0.2ms)[0m [1mSELECT "business_infos".* FROM "business_infos" WHERE "business_infos"."user_id" = 1 LIMIT 1[0m
[1m[35m (1.0ms)[0m UPDATE "business_infos" SET "qr_code" = '/assets/qr_code/microsoft.png', "updated_at" = '2011-10-23 04:09:00.314348' WHERE "business_infos"."id" = 8
Redirected to http://localhost:3000/users/1
Completed 302 Found in 613ms
if you see Parameters in the log, there contains a location_attributes(location model), which is the nested attributes for business_info model, but if you see the INSERT INTO "business_infos" query, there are no insertion to the location table!
I have posted a more detailed question at this link Rails mode accepts_nested_attributes_for working properly Any clue?