質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails 6

Ruby on Rails 6は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Q&A

解決済

1回答

1512閲覧

Railsにてネスト化されたパラメータのカスタムバリデーション

fresh_fish

総合スコア20

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails 6

Ruby on Rails 6は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

0グッド

0クリップ

投稿2020/08/24 01:29

編集2020/08/24 14:52

createアクションでのみ作動するカスタムバリデーションを実装しようと思ったのですがcocoonを使っているためパラメータが複雑でよくわからなくなってしまいました。
ログで確認したパラメータはこうなっております

"band"=>{"name"=>"Beatles", "relationships_attributes"=>{"0"=>{"user_id"=>"2", "part"=>"Gt1", "_destroy"=>"false"}, "1598231822108"=>{"user_id"=>"0", "part"=>"Vo1", "_destroy"=>"false"}}}

ここからuser_id == current_user に一致する配列が一つでもあればtrue,そうでなければfalseというコードはどう書けばいいでしょうか

def band_params params.require(:band).permit( :name, relationships_attributes: [ :id, :part, :band_id, :user_id, :_destroy ] )

試したこと

最初の構想ではこういうバリデーションを作ろうとしていました

class BandCreateValidator < ActiveModel::Validator def validate(record) record.each do |r| end end

回答を参考にして

サーバーログで確認したパラメータ
"band"=>{"name"=>"Beatles", "relationships_attributes"=>{"0"=>{"user_id"=>"2", "part"=>"Gt1", "_destroy"=>"false"}, "1598231822108"=>{"user_id"=>"0", "part"=>"Vo1", "_destroy"=>"false"}}}

と実際のパラメータ
<ActionController::Parameters {"name"=>"Beatles", "relationships_attributes"=><ActionController::Parameters {"0"=><ActionController::Parameters {"part"=>"Gt1", "user_id"=>"2", "_destroy"=>"false"} permitted: true>, "1598279704474"=><ActionController::Parameters {"part"=>"Vo1", "user_id"=>"0", "_destroy"=>"false"} permitted: true>} permitted: true>} permitted: true>

が違ったためrelations.any?{|k,v| v["user_id"].to_i == current_user.id}では
エラー undefined method `any?' for #ActionController::Parameters:0x00007fb8b09e55c8
が起きてしまいました。
そこでセキュリティ的に問題はありますがto_unsafe_hを付けてみました

bands_controller.rb . . . def create @band = Band.new(band_params) if validate_on_create(band_params) if @band.save redirect_to @band flash[:notice] = 'バンドの登録に成功しました' else flash[:alert] = 'バンドの登録に失敗しました(save)' redirect_to new_band_path end else flash[:alert] = 'バンドの登録に失敗しました(validate)' redirect_to new_band_path end end . . private def validate_on_create(relations) relations.to_unsafe_h.any?{|k,v| v["user_id"].to_i == current_user.id} end

このように記述したところなんとか動いたのですが、本来通るはずであるuser_id == current_userでも'バンドの登録に失敗しました(validate)'と表示され通りません(ページの描写は出来ている)
そこで validate_on_craeteメソッドから意図的に to_unsafe_h を外してエラーを起こしbetter_errorでデバッグしてみました

コンソール >> relations => <ActionController::Parameters {"name"=>"Beatles", "relationships_attributes"=><ActionController::Parameters {"0"=><ActionController::Parameters {"part"=>"Gt1", "user_id"=>"2", "_destroy"=>"false"} permitted: true>, "1598279704474"=><ActionController::Parameters {"part"=>"Vo1", "user_id"=>"0", "_destroy"=>"false"} permitted: true>} permitted: true>} permitted: true> >> relations.any? !! #<NoMethodError: undefined method `any?' for #<ActionController::Parameters:0x00007fb8b09e55c8>> >> relations.to_unsafe_h => {"name"=>"Beatles", "relationships_attributes"=>{"0"=>{"part"=>"Gt1", "user_id"=>"2", "_destroy"=>"false"}, "1598279704474"=>{"part"=>"Vo1", "user_id"=>"0", "_destroy"=>"false"}}} >> relations.to_unsafe_h.any? => true >> relations.to_unsafe_h.any?{|k,v| v["user_id"].to_i == 2} => false ##コンソールではcurrent_user.idが使えないため代わりに同じ値の2を使用しています

一体なぜここがFalseになるのでしょうか?

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

Validatorはmodelにデータを反映した後にvalidateするものです。

createでのみとするなら

def validate_on_create validates_presence_of :password end

の様にするか
, on: :create optionするかでできます。

ですので、その params で modelに反映できていればよいのですが、
「user_id == current_user に一致する配列が一つでもあれば」というのは厄介ですね。current_userはmodelでは参照できない。

愚直にparamsを調べるなら
"band"=>{"name"=>"Beatles", "relationships_attributes"=>{"0"=>{"user_id"=>"2", "part"=>"Gt1", "_destroy"=>"false"}, "1598231822108"=>{"user_id"=>"0", "part"=>"Vo1", "_destroy"=>"false"}}}
ということは
relations = params[:band][:relationships_attributes]{"0"=>{"user_id"=>"2", "part"=>"Gt1", "_destroy"=>"false"}, "1598231822108"=>{"user_id"=>"0", "part"=>"Vo1", "_destroy"=>"false"}}ですから
relations.any?{|k,v| v["user_id"].to_i == current_user.id}
で行けます。

投稿2020/08/24 10:44

winterboum

総合スコア23347

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fresh_fish

2020/08/24 14:55

回答ありがとうございます 参考にさせて頂きなんとか近いところまでは行ったのですが肝心のところで失敗してしまいました 質問を編集し情報を追加しましたのでもう一度見てもらえると助かります よろしくお願いします
winterboum

2020/08/24 21:12

私の回答は relations = params[:band][:relationships_attributes] です [:relationships_attributes] が落ちています relations.to_unsafe_h[:relationships_attributes].any? ですね relations.to_unsafe_h["relationships_attributes"].any? でないとだめかも。to_hしてるので
fresh_fish

2020/08/25 02:37

relations.to_unsafe_h[:relationships_attributes].any?で無事解決しました ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問