Reputation: 158
I have a form in an app with some nested fields.
I'm using simple_form_for
, bootstrap, slim and Rails 4.2 and a form backing object as described here
Here's the proof params are missing on the server side (N.B. I have checked the authenticity_token
field is in the markup)
Started POST "/example_sentences/9/breakdowns" for ::1 at 2015-08-31 12:37:57 +0900
ActiveRecord::SchemaMigration Load (18.8ms) SELECT "schema_migrations".* FROM "schema_migrations"
Processing by BreakdownsController#create as HTML
Parameters: {"example_sentence_id"=>"9"}
Can't verify CSRF token authenticity
Switching off CSRF protection to try and debug gives this:
Started POST "/example_sentences/9/breakdowns" for ::1 at 2015-08-31 13:00:05 +0900
Processing by BreakdownsController#create as HTML
Parameters: {"example_sentence_id"=>"9"}
User Load (24.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT 1 [["id", 1]]
Completed 400 Bad Request in 355ms (ActiveRecord: 156.3ms)
ActionController::ParameterMissing - param is missing or the value is empty: breakdown:
Obviously, there's quite a number of factors that mean this is deviating from the Rails way, so any number of things could be going wrong. And showing the view code would introduce a lot more questions.
So, I'll show the generated HTML (minus styling info) and ask if anyone can see anything clearly wrong with this. I'm kind of hoping it will be a facepalm moment and missing something obvious.
<form novalidate="novalidate" id="new_breakdown" action="/example_sentences/9/breakdowns" accept-charset="UTF-8" method="post">
<input name="utf8" type="hidden" value="✓">
<input type="hidden" name="authenticity_token" value="cOU87EZXRkV1a0QA1ohwlqM5Ny43QvgcRaqKan9mdgs12jR8RnJfyVXp9VIJHkMRkBUvZ16Io7QaGcBazjVqGw==">
<input type="text" value="これ" name="breakdown[word_mapping][1][text]" id="breakdown_word_mapping_1_text" >
<input type="text" value="this" name="breakdown[word_mapping][1][translation]" id="breakdown_word_mapping_1_translation">
<input type="text" value="は" name="breakdown[word_mapping][2][text]" id="breakdown_word_mapping_2_text">
<input type="text" value="" name="breakdown[word_mapping][2][translation]" id="breakdown_word_mapping_2_translation">
<input type="submit" name="commit" value="Save translations">
</form>
In ~/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/actionpack-4.2.3/lib/action_controller/metal/request_forgery_protection.rb
during the :valid_authenticity_token?
check line 277
For request.body.read I can see the authenticity token "utf8=%E2%9C%93&authenticity_token=RTFWCJZ3QnRgnNWFF66ej2aetx7H5n7BcbLKW4v145YADl6YllJb%2BEAeZNfIOK0IVbKvV64sJWkuAYBrOqb%2Fhg%3D%3D&breakdown%5Bword_mapping%5D%5B1%5D%5Btext%5D=%E3%81%93%E3%82%8C&breakdown%5Bword_mapping%5D%5B1%5D%5Btranslation%5D=this&breakdown%5Bword_mapping%5D%5B1%5D%5Breading%5D=&breakdown%5Bword_mapping%5D%5B1%5D%5B_destroy%5D=0&breakdown%5Bword_mapping%5D%5B2%5D%5Btext%5D=%E3%81%AF&breakdown%5Bword_mapping%5D%5B2%5D%5Btranslation%5D=&breakdown%5Bword_mapping%5D%5B2%5D%5Breading%5D=&breakdown%5Bword_mapping%5D%5B2%5D%5B_destroy%5D=0&breakdown%5Bword_mapping%5D%5B3%5D%5Btext%5D=%E3%83%9A%E3%83%B3&breakdown%5Bword_mapping%5D%5B3%5D%5Btranslation%5D=pen&breakdown%5Bword_mapping%5D%5B3%5D%5Breading%5D=&breakdown%5Bword_mapping%5D%5B3%5D%5B_destroy%5D=0&breakdown%5Bword_mapping%5D%5B4%5D%5Btext%5D=%E3%81%A7%E3%81%99&breakdown%5Bword_mapping%5D%5B4%5D%5Btranslation%5D=&breakdown%5Bword_mapping%5D%5B4%5D%5Breading%5D=&breakdown%5Bword_mapping%5D%5B4%5D%5B_destroy%5D=0&breakdown%5Bword_mapping%5D%5B%5D%5Btext%5D=%E3%81%93%E3%82%8C&breakdown%5Bword_mapping%5D%5B%5D%5Btranslation%5D=&breakdown%5Bword_mapping%5D%5B%5D%5Breading%5D=&breakdown%5Bword_mapping%5D%5B%5D%5B_destroy%5D=0&breakdown%5Bword_mapping%5D%5B%5D%5Btext%5D=%E3%81%AF&breakdown%5Bword_mapping%5D%5B%5D%5Btranslation%5D=&breakdown%5Bword_mapping%5D%5B%5D%5Breading%5D=&breakdown%5Bword_mapping%5D%5B%5D%5B_destroy%5D=0&breakdown%5Bword_mapping%5D%5B%5D%5Btext%5D=%E3%83%9A%E3%83%B3&breakdown%5Bword_mapping%5D%5B%5D%5Btranslation%5D=&breakdown%5Bword_mapping%5D%5B%5D%5Breading%5D=&breakdown%5Bword_mapping%5D%5B%5D%5B_destroy%5D=0&breakdown%5Bword_mapping%5D%5B%5D%5Btext%5D=%E3%81%A7%E3%81%99&breakdown%5Bword_mapping%5D%5B%5D%5Btranslation%5D=&breakdown%5Bword_mapping%5D%5B%5D%5Breading%5D=&breakdown%5Bword_mapping%5D%5B%5D%5B_destroy%5D=0&commit=Save+translations"
But the value of encoded_masked_token
is nil
In fact the path_parameters
is the only part of the params I'm getting in the request.
in: "~/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/actionpack-4.2.3/lib/action_dispatch/http/request.rb"
(byebug) pp @env.keys.sort.grep(/action_dispatch/).grep(/param/).map{|k| [k, @env[k]]}
[["action_dispatch.parameter_filter", [:password]],
["action_dispatch.request.parameters",
{"controller"=>"breakdowns",
"action"=>"create",
"example_sentence_id"=>"9"}],
["action_dispatch.request.path_parameters",
{:controller=>"breakdowns", :action=>"create", :example_sentence_id=>"9"}],
["action_dispatch.request.query_parameters", {}],
["action_dispatch.request.request_parameters", {}]]
So I'm somehow losing the params already.
Upvotes: 0
Views: 768
Reputation: 158
OK so the problem was with the format of the name param. I didn't catch where exactly in the Rails stack all the params including the authenticity_token/CSRF token get lost but basically:
Instead of this:
breakdown[word_mapping][0][translation]
This is correct:
breakdown[word_mappings_attributes][0][translation]
It's possibly worth adding extra info in case anyone else had a similar issue.
I was using a nested form with a form-backing object as described in the pivotal blog post linked in the OP.
My slim code looked like this, based on an out-of-date recommendation elsewhere:
= f.simple_fields_for "word_mapping[]", @breakdown.word_mappings do |w|
= render "word_mapping_fields", f: w
which was resulting in the output HTML shown in the post.
The correct version is:
= f.simple_fields_for "word_mappings", @breakdown.word_mappings do |w|
= render "word_mapping_fields", f: w
On my form-backing object I had to delegate, word_mappings_attributes=
to the AR
object ExampleSentence
in this case. Although using delegate
itself didn't work so I had to manually write the code.
def word_mappings_attributes=(attrs)
example_sentence.word_mappings_attributes=attrs
end
I also had to add the following to the form:
class << self
delegate :reflect_on_association, to: ExampleSentence
end
Upvotes: 0
Reputation: 25049
From the error message, it sounds like your code to require your parameters is incorrect.
If you have something like the following:
params.require(breakdown: [fields])
Then it should be
params.require(:breakdown).permit(fields)
Give it a try.
Upvotes: 2