Rails 邪恶的多步骤表单 - 缺少参数或值为空与不允许的参数
Rails Wicked multi step form - param is missing or the value is empty vs unpermitted parameters
我正在关注 this tutorial 使用 Wicked gem 创建一个多步骤表单。每个用户都有一个答案(用于存储一组问题答案的模型),我有一个问卷控制器,它逐步解决用户将回答并存储在答案中的问题。到目前为止,我只有两个步骤,每个步骤都有一个问题(答案的布尔列 table)。当我逐步回答每个问题时,一切正常,但是当我提交时没有值时,我得到:
param is missing or the value is empty: answer
我的代码...
answer.rb:
class Answer < ApplicationRecord
belongs_to :user
cattr_accessor :questionnaire_steps do
%w(step_one step_two)
end
attr_accessor :current_step #stores the current questionnaire step
validates :user_id, presence: true
with_options if: -> { required_for_step?(:step_one) } do |step|
step.validates :question_one, inclusion: [true, false]
end
with_options if: -> { required_for_step?(:step_two) } do |step|
step.validates :question_two, inclusion: [true, false]
end
def required_for_step?(step)
# All fields are required if no questionnaire step is present
return true if current_step.nil?
# All fields from previous steps are required if the
# step parameter appears before or we are on the current step
return true if self.questionnaire_steps.index(step.to_s) <= self.questionnaire_steps.index(current_step)
end
end
answers_controller.rb:
class AnswersController < ApplicationController
before_action :setup
def setup
@user = current_user
end
def show
@answer = @user.answer
end
def new
@answer = Answer.new
@answer = @user.build_answer
@answer.save(validate: false)
end
def create
@answer = @user.answer
redirect_to questionnaire_path(@answer, Answer.questionnaire_steps.first)
end
end
questionnaire_controller.rb:
class QuestionnaireController < ApplicationController
include Wicked::Wizard
before_action :setup
steps *Answer.questionnaire_steps
def setup
@user = current_user
end
def show
@answer = @user.answer
render_wizard
end
def update
@answer = @user.answer
@answer.update_attributes(answer_params(step))
render_wizard @answer
end
private
def answer_params(step)
permitted_attributes = case step
when "step_one"
[:question_one]
when "step_two"
[:question_two]
end
params.require(:answer).permit(permitted_attributes).merge(current_step: step)
end
end
step_one.html.erb:
<div id="questionnaire" class="container-fluid well inline-headers">
<%= form_for @answer, method: :put, url: wizard_path do |f| %>
<div class="row">
<% if f.object.errors.any? %>
<div id="error_explanation" class="alert alert-dismissible alert-danger" >
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
<ul>
<% f.object.errors.each do |attribute, message| %>
<%= content_tag :li, f.object.errors.full_message(attribute, message), :id => "error_#{attribute}" %>
<% end %>
<ul>
</div>
<% end %>
</div>
<div class="row">
<div class="col-md-8 form-group">
<p>Are you any good at this?</p>
</div>
<div class="col-md-4 form-group">
<%= label_tag(:question_one, "Yes") %>
<%= f.radio_button :question_one, true %>
<%= label_tag(:question_one, "No") %>
<%= f.radio_button :question_one, false %>
</div>
</div>
<div class="row">
<div class="col-md-4 col-md-offset-8 form-group">
<p><%= f.submit "Save & Continue", class: "btn btn-warning" %></p>
</div>
</div>
<% end %>
</div>
routes.rb(诚然,我试图用这些来掩盖 ID,请随意羞辱我以正确的方式做到这一点):
resources :answers, only: [:show, :new, :create]
get 'user/questionnaire', to: 'answers#show'
get 'user/questionnaire/new', to: 'answers#new'
post 'user/questionnaire/new', to: 'answers#create'
resources :questionnaire
当我 select 一个选项并提交时,它会保存到数据库并重定向到下一步,但是如果我在没有 select 一个选项的情况下提交,我会收到参数丢失错误我希望得到验证错误。来自服务器日志:
Processing by QuestionnaireController#update as HTML Parameters:
{"utf8"=>"���",
"authenticity_token"=>"J45sG/YIqTaBECgBqNLr9Ys3rJewDK78d/cKHWFHYEIzAaOiTXhgH6evPZKl5LhnSAMnQnA94CXonqriyj0fCQ==",
"commit"=>"Save & Continue", "id"=>"step_one"}
我尝试了一些可能的修复方法,包括从 params.require(:answer).permit(permitted_attributes).merge(current_step: step)
中删除 require(:answer)
,这让我得到了我预期的验证错误,但不允许保存有效值。在那种情况下,我仍然在服务器日志中收到以下验证错误:
Started PUT "/questionnaire/step_one" for ::1 at 2016-12-08
23:26:14 +0545 Processing by QuestionnaireController#update as HTML
Parameters: {"utf8"=>"���",
"authenticity_token"=>"RWAHfu0BE82ik6gTChh3nmtuyOUa5j0qiz4H03ejIkRR78jHVnHa5IQsvYAHLiQMqFpDMNrXc/MUV6cs3NldDw==",
"answer"=>{"question_one"=>"true"}, "commit"=>"Save & Continue",
"id"=>"step_one"} ...SQL... Unpermitted parameters: utf8, _method,
authenticity_token, answer, commit, id
非常感谢您帮助菜鸟。
更新:我修改了我的路线以匹配教程(嵌套)认为这可能与它有关(即可能没有得到答案 ID)但我仍然收到参数丢失错误。
resources :answers, only: [:show, :new, :create] do
resources :questionnaire, only: [:show, :update], controller: 'questionnaire'
end
get 'user/questionnaire', to: 'answers#show'
get 'user/questionnaire/new', to: 'answers#new'
post 'user/questionnaire/new', to: 'answers#create'
我也试过在提交前加一个hidden_field_tag,像这样:
<%= hidden_field_tag(:answer, @answer) %>
但后来我得到:
undefined method `permit' for #
可能是因为当强参数声明需要散列时,hidden_field_tag 将 @answer.attributes 作为字符串传递。来自服务器:
Started PUT "/answers/46/questionnaire/step_one" for ::1 at
2016-12-12 13:25:19 +0545 Processing by QuestionnaireController#update
as HTML Parameters: {"utf8"=>"���",
"authenticity_token"=>"wlLpKoMvqgyfwB5gyP/qubR4ZpZa6jm9KHeMuiwJTc6IOJZi9PgiSegvF+OzlN09ctEuxuxiAuRs+/B2InRGhA==",
"answer"=>"{\"id\"=>46, \"user_id\"=>4, \"question_one\"=>nil,
\"question_two\"=>nil,
\"percentage_complete\"=>#,
\"completed\"=>false, \"created_at\"=>Mon, 12 Dec 2016 02:12:31 EST
-05:00, \"updated_at\"=>Mon, 12 Dec 2016 02:12:31 EST -05:00}", "commit"=>"Save & Continue", "answer_id"=>"46", "id"=>"step_one"}
请帮忙!
问题是否仍然有效?
我想把它放在评论中,但不能。
我认为问题在于验证。它会检查您是否在相关字段中输入了值,如果没有输入则抛出错误。如果从模型中删除 step.validates 会怎样?
我将 params.require
更改为 params.fetch
并且有效(正确触发了验证,我也可以成功完成具有有效值的步骤)。
我正在关注 this tutorial 使用 Wicked gem 创建一个多步骤表单。每个用户都有一个答案(用于存储一组问题答案的模型),我有一个问卷控制器,它逐步解决用户将回答并存储在答案中的问题。到目前为止,我只有两个步骤,每个步骤都有一个问题(答案的布尔列 table)。当我逐步回答每个问题时,一切正常,但是当我提交时没有值时,我得到:
param is missing or the value is empty: answer
我的代码...
answer.rb:
class Answer < ApplicationRecord
belongs_to :user
cattr_accessor :questionnaire_steps do
%w(step_one step_two)
end
attr_accessor :current_step #stores the current questionnaire step
validates :user_id, presence: true
with_options if: -> { required_for_step?(:step_one) } do |step|
step.validates :question_one, inclusion: [true, false]
end
with_options if: -> { required_for_step?(:step_two) } do |step|
step.validates :question_two, inclusion: [true, false]
end
def required_for_step?(step)
# All fields are required if no questionnaire step is present
return true if current_step.nil?
# All fields from previous steps are required if the
# step parameter appears before or we are on the current step
return true if self.questionnaire_steps.index(step.to_s) <= self.questionnaire_steps.index(current_step)
end
end
answers_controller.rb:
class AnswersController < ApplicationController
before_action :setup
def setup
@user = current_user
end
def show
@answer = @user.answer
end
def new
@answer = Answer.new
@answer = @user.build_answer
@answer.save(validate: false)
end
def create
@answer = @user.answer
redirect_to questionnaire_path(@answer, Answer.questionnaire_steps.first)
end
end
questionnaire_controller.rb:
class QuestionnaireController < ApplicationController
include Wicked::Wizard
before_action :setup
steps *Answer.questionnaire_steps
def setup
@user = current_user
end
def show
@answer = @user.answer
render_wizard
end
def update
@answer = @user.answer
@answer.update_attributes(answer_params(step))
render_wizard @answer
end
private
def answer_params(step)
permitted_attributes = case step
when "step_one"
[:question_one]
when "step_two"
[:question_two]
end
params.require(:answer).permit(permitted_attributes).merge(current_step: step)
end
end
step_one.html.erb:
<div id="questionnaire" class="container-fluid well inline-headers">
<%= form_for @answer, method: :put, url: wizard_path do |f| %>
<div class="row">
<% if f.object.errors.any? %>
<div id="error_explanation" class="alert alert-dismissible alert-danger" >
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
<ul>
<% f.object.errors.each do |attribute, message| %>
<%= content_tag :li, f.object.errors.full_message(attribute, message), :id => "error_#{attribute}" %>
<% end %>
<ul>
</div>
<% end %>
</div>
<div class="row">
<div class="col-md-8 form-group">
<p>Are you any good at this?</p>
</div>
<div class="col-md-4 form-group">
<%= label_tag(:question_one, "Yes") %>
<%= f.radio_button :question_one, true %>
<%= label_tag(:question_one, "No") %>
<%= f.radio_button :question_one, false %>
</div>
</div>
<div class="row">
<div class="col-md-4 col-md-offset-8 form-group">
<p><%= f.submit "Save & Continue", class: "btn btn-warning" %></p>
</div>
</div>
<% end %>
</div>
routes.rb(诚然,我试图用这些来掩盖 ID,请随意羞辱我以正确的方式做到这一点):
resources :answers, only: [:show, :new, :create]
get 'user/questionnaire', to: 'answers#show'
get 'user/questionnaire/new', to: 'answers#new'
post 'user/questionnaire/new', to: 'answers#create'
resources :questionnaire
当我 select 一个选项并提交时,它会保存到数据库并重定向到下一步,但是如果我在没有 select 一个选项的情况下提交,我会收到参数丢失错误我希望得到验证错误。来自服务器日志:
Processing by QuestionnaireController#update as HTML Parameters: {"utf8"=>"���", "authenticity_token"=>"J45sG/YIqTaBECgBqNLr9Ys3rJewDK78d/cKHWFHYEIzAaOiTXhgH6evPZKl5LhnSAMnQnA94CXonqriyj0fCQ==", "commit"=>"Save & Continue", "id"=>"step_one"}
我尝试了一些可能的修复方法,包括从 params.require(:answer).permit(permitted_attributes).merge(current_step: step)
中删除 require(:answer)
,这让我得到了我预期的验证错误,但不允许保存有效值。在那种情况下,我仍然在服务器日志中收到以下验证错误:
Started PUT "/questionnaire/step_one" for ::1 at 2016-12-08 23:26:14 +0545 Processing by QuestionnaireController#update as HTML
Parameters: {"utf8"=>"���", "authenticity_token"=>"RWAHfu0BE82ik6gTChh3nmtuyOUa5j0qiz4H03ejIkRR78jHVnHa5IQsvYAHLiQMqFpDMNrXc/MUV6cs3NldDw==", "answer"=>{"question_one"=>"true"}, "commit"=>"Save & Continue", "id"=>"step_one"} ...SQL... Unpermitted parameters: utf8, _method, authenticity_token, answer, commit, id
非常感谢您帮助菜鸟。
更新:我修改了我的路线以匹配教程(嵌套)认为这可能与它有关(即可能没有得到答案 ID)但我仍然收到参数丢失错误。
resources :answers, only: [:show, :new, :create] do
resources :questionnaire, only: [:show, :update], controller: 'questionnaire'
end
get 'user/questionnaire', to: 'answers#show'
get 'user/questionnaire/new', to: 'answers#new'
post 'user/questionnaire/new', to: 'answers#create'
我也试过在提交前加一个hidden_field_tag,像这样:
<%= hidden_field_tag(:answer, @answer) %>
但后来我得到:
undefined method `permit' for #
可能是因为当强参数声明需要散列时,hidden_field_tag 将 @answer.attributes 作为字符串传递。来自服务器:
Started PUT "/answers/46/questionnaire/step_one" for ::1 at 2016-12-12 13:25:19 +0545 Processing by QuestionnaireController#update as HTML Parameters: {"utf8"=>"���", "authenticity_token"=>"wlLpKoMvqgyfwB5gyP/qubR4ZpZa6jm9KHeMuiwJTc6IOJZi9PgiSegvF+OzlN09ctEuxuxiAuRs+/B2InRGhA==", "answer"=>"{\"id\"=>46, \"user_id\"=>4, \"question_one\"=>nil, \"question_two\"=>nil, \"percentage_complete\"=>#, \"completed\"=>false, \"created_at\"=>Mon, 12 Dec 2016 02:12:31 EST -05:00, \"updated_at\"=>Mon, 12 Dec 2016 02:12:31 EST -05:00}", "commit"=>"Save & Continue", "answer_id"=>"46", "id"=>"step_one"}
请帮忙!
问题是否仍然有效?
我想把它放在评论中,但不能。 我认为问题在于验证。它会检查您是否在相关字段中输入了值,如果没有输入则抛出错误。如果从模型中删除 step.validates 会怎样?
我将 params.require
更改为 params.fetch
并且有效(正确触发了验证,我也可以成功完成具有有效值的步骤)。