web-dev-qa-db-ja.com

Rails-子オブジェクトのネストされた属性と強力なパラメーターを使用して親オブジェクトIDを設定する方法は?

Railscast 196-197:Nested Model Form に示されているような状況になっています。しかし、私はこのアプローチと強力なパラメーターとの間に衝突を経験しました。フォームを通じて割り当て可能にしたくないので、ユーザーが子レコードを所有していない親レコードに関連付けるのを防ぐため、子オブジェクトの親レコードIDフィールドにデータを入力する良い方法がわかりません。 )。私は解決策を持っていますが(以下のコードを参照)、これは一種のことのように思われますRailsは私のために賢くて簡単な方法を持っているかもしれません。

これがコードです...

Has_many子オブジェクト(質問と呼ぶ)の親オブジェクト(Surveyと呼びます)があります。

# 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)

[〜#〜]更新[〜#〜]

おそらく、この問題を処理する最良の方法は、 このCode Climateブログの投稿 で提案されている「フォームオブジェクト」を除外することです。私は他の観点に興味があるので、私は質問を開いたままにします

35
Nathan Wallace

したがって、実行中の問題は、子オブジェクトが検証に合格しないことですよね?子オブジェクトが親と同時に作成された場合、子オブジェクトは検証に合格するために親のIDを認識できなかった可能性があります。これはtrueです。

これがその問題を解決する方法です。次のようにモデルを変更します。

# app/models/survey.rb
class Survey
    belongs_to :user
    has_many :questions, :inverse_of => :survey
    accepts_nested_attributes_for :questions
end

# app/models/question.rb
class Question
    validates :survey, :presence => true
    belongs_to :survey
end

ここでの違いは:inverse_ofアソシエーションに渡されるhas_manyであり、質問は:surveyではなく:survey_idのみで検証されるようになりました。

:inverse_ofは、関連付けを使用して子オブジェクトが作成または構築されるときに、それを作成した親への後方参照も受け取るようにします。これは自動的に発生するはずのように見えますが、このオプションを指定しない限り、残念ながら発生しません。

:surveyではなく:survey_idで検証することは、妥協のようなものです。検証では、survey_idフィールドに空白でないものが存在するかどうかを確認するだけではなくなりました。親オブジェクトの存在の関連を実際にチェックするようになりました。この場合、:inverse_ofが原因で役立つことがわかりますが、検証するために、実際にはIDを使用してデータベースから関連付けをロードする必要があります。これは、データベース内の何にも一致しないIDは検証に合格しないことも意味します。

お役に立てば幸いです。

72
Comatose Turtle