我遇到了一个很像Railscast 196-197: Nested Model Form中的情况,但是,我遇到了这种方法和强参数之间的冲突,我找不到填充子对象上的父记录id字段的好方法。因为我不希望它可以通过表单赋值(防止用户将子记录与不属于他们的父记录关联)。(见下面的代码)但这似乎是Rails可以为我做的一种聪明、简单的方法。
这是密码...
有一个父对象(称为Survey),它有_many个子对象(称为Questions):
# app/models/survey.rb
class Survey
belongs_to :user
has_many :questions
accepts_nested_attributes_for :questions
end
# app/models/question.rb
class Question
validates :survey_id, :presence => true
belongs_to :survey
end
有一个表单允许用户同时创建一个调查和调查中的问题(为简单起见,下面的代码将调查视为只有问题):
# app/views/surveys/edit.html.erb
<%= form_for @survey do |f| %>
<%= f.label :name %>
<%= f.text_field :name %><br />
<%= f.fields_for :questions do |builder| %>
<%= builder.label :content, "Question" %>
<%= builder.text_area :content, :rows => 3 %><br />
<% end %>
<%= f.submit "Submit" %>
<% end %>
问题出在控制器上,我想通过强参数来保护问题记录中的survey_id字段,但是这样做的话问题就无法通过验证,因为survey_id是必填字段。
# app/controllers/surveys_controller.rb
class SurveysController
def edit
@survey = Survey.new
Survey.questions.build
end
def create
@survey = current_user.surveys.build(survey_params)
if @survey.save
redirect_to @survey
else
render :new
end
end
private
def survey_params
params.require(:survey).permit(:name, :questions_attributes => [:content])
end
end
我能想到的解决这个问题的唯一方法是从调查中单独构建问题,如下所示:
def create
@survey = current_user.surveys.build(survey_params)
if @survey.save
if params[:survey][:questions_attributes]
params[:survey][:questions_attributes].each_value do |q|
question_params = ActionController::Parameters.new(q)
@survey.questions.build(question_params.permit(:content))
end
end
redirect_to @survey
else
render :new
end
end
private
def survey_params
params.require(:survey).permit(:name)
end
(Rails 4测试版1,Ruby 2)
- 更新**
也许处理这个问题的最好方法是分解出this Code Climate blog post中建议的"Form对象"。不过,由于我对其他观点很好奇,所以我将这个问题保留为开放式
1条答案
按热度按时间nfzehxib1#
所以你遇到的问题是子对象没有通过验证,对吗?当子对象和父对象同时创建时,子对象不可能知道父对象的id来通过验证,这是真的。
以下是您可以解决该问题的方法。请按以下步骤更改您的模型:
此处的区别在于传递给
has_many
关联的:inverse_of
,以及问题现在仅在:survey
上验证,而不是在:survey_id
上验证。:inverse_of
使得当使用关联创建或构建子对象时,它也接收到对创建它的父对象的反向引用。这看起来像是应该自动发生的事情,但不幸的是,除非您指定此选项,否则它不会自动发生。在
:survey
上而不是在:survey_id
上进行验证是一种折衷,验证不再是简单地检查survey_id字段中是否存在非空的内容;它现在实际上检查父对象的存在性的关联。在这种情况下,由于:inverse_of
,它是有帮助的知道,但在其他情况下,它实际上将不得不使用id从数据库加载关联以便验证。这也意味着id不匹配数据库中的任何内容将不会通过验证。