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

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

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

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

Q&A

解決済

1回答

1148閲覧

[Rails6] updateで発生したバリデーションエラーをindexで表示したい

necocoa

総合スコア209

Ruby on Rails

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

0グッド

0クリップ

投稿2020/11/17 08:24

編集2020/11/17 08:39

前提

Ruby 2.7.2
Rails 6.0.3.4
gem 'bootstrap_form'を利用

状況をシンプルにするためにscafforldでpost title:stringのみ作った状況です。
実際にやりたいのはpostではありませんが、同意であり、わかりやすいのでpostとして進めています。
使っていないcreateやdestroyは消してあります。

実現したいこと

  1. indexにて、postsの個別の編集ができる
  2. indexから編集した時、遷移先はindex
  3. 失敗時、バリデーションエラーがviewに反映される
  4. 遷移後のURLはindex
  5. editにて個別編集もできる
  6. @posts = Post.allの量は多くないものとする(100以下)

上記の条件をクリアした実装はできたが、良い実装なのか不安です。

実装方法

例: indexからpost(id:1)のupdateが失敗した時

  1. flash[:error]post.errorsの値を渡す
  2. redirect_to posts_pathでindexにリダイレクトする
  3. indexの@postsからposts(id:1)を探す
  4. @posts(id:1)にerrorsを追加(flash[:error]からエラー内容を取得する)
  5. @posts(id:1).errorsに値が入っているため、view上にエラーが反映する

疑問

1.画面の遷移先をrefererで確認して条件分岐しているが、もっと良いやり方がないか?

ruby

1referer_uri = URI.parse(request.referer) 2if referer_uri.path == posts_path 3 ... 4end

2.redirect_toでindexにリダイレクトし、バリデーションエラー内容をflash[:error]で渡しているが、よい方法がないか?
render :indexだと、URLがposts/1になる。
その状態でリロードするとeditに遷移してしまうので、URLはpostsにしたい。

ruby

1def index 2 @posts = Post.all 3 @posts.each { |post| post.if_eq_add_errors_to_self(flash[:error]['post']) } if flash[:error]&.key?('post') 4end 5 6def update 7 @post = Post.find(params[:id]) 8 if @post.update(post_params) 9 redirect_to posts_path, notice: 'Post was successfully updated.' 10 else 11 redirect_to posts_path, flash: { error: @post.flash_error_hash } 12 end 13end

該当のソースコード

posts_controller.rb

ruby

1class PostsController < ApplicationController 2 def index 3 @posts = Post.all 4 @posts.each { |post| post.if_eq_add_errors_to_self(flash[:error]['post']) } if flash[:error]&.key?('post') 5 end 6 7 def edit 8 @post = Post.find(params[:id]) 9 end 10 11 def update 12 @post = Post.find(params[:id]) 13 referer_uri = URI.parse(request.referer) 14 15 if @post.update(post_params) 16 if referer_uri.path == posts_path 17 redirect_to posts_path, notice: 'Post was successfully updated.' 18 else 19 redirect_to @post, notice: 'Post was successfully updated.' 20 end 21 elsif referer_uri.path == posts_path 22 redirect_to posts_path, flash: { error: @post.flash_error_hash } 23 else 24 render :edit 25 end 26 end 27 28 private 29 30 def post_params 31 params.require(:post).permit(:title) 32 end 33end 34

post.rb

ruby

1class Post < ApplicationRecord 2 validates :title, presence: true 3 4 def if_eq_add_errors_to_self(flash_error_post) 5 return unless id == flash_error_post['id'] 6 7 flash_error_post['details'].each do |col_key, details| 8 details.each { |detail| errors.add(col_key, detail['error'].to_sym) } 9 end 10 end 11 12 def flash_error_hash 13 { post: { id: id, details: errors.details } } 14 end 15end

index.html.erb

erb

1<%= render partial: 'form', collection: @posts, as: 'post' %>

edit.html.erb

erb

1<%= render 'form', post: @post %>

_form.html.erb

erb

1<%= bootstrap_form_with(model: post, local: true, inline_errors: false) do |f| %> 2 <%= f.text_field :title %> 3 <%= f.errors_on :title %> 4 <%= f.submit '更新する' %> 5<% end %> 6

実際の画面イメージ

イメージ説明

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

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

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

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

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

no1knows

2020/11/17 14:24

> 良い実装なのか不安です。 良い実装の定義を質問に追記してください。
necocoa

2020/11/18 01:44

実現したいことに書いてしまったのでわかりづらくなってしまったかもしれません。 聞きたいこととしては、疑問に書いてある2点です。
guest

回答1

0

ベストアンサー

1.画面の遷移先をrefererで確認して条件分岐しているが、もっと良いやり方がないか?
「良い」の判断基準、物差しが何なのかを言っていただかないと比べられないです。
他に方法は無いか なら沢山答えがあるとは思いますが。
link情報に indexなのかeditなのかも持たせるという方法もあります。

2.redirect_toでindexにリダイレクトし、バリデーションエラー内容をflash[:error]で渡しているが、よい方法がないか?
ここはちょっと、、ですね。flashをそのまま表示ではまずいのですか?

どうしても errorsで渡したいなら
@posts = Post.all
して、
@post = @posts.select{|post| post.id == params[:id].to_i}.first
して、その@postをなおし、saveして render action: :index

select でなく find にすれば firstは要らないけど、find が RelationのfindかEnumrableのかどっちにとられるか分からないので日和った

投稿2020/11/25 09:16

編集2020/11/25 09:17
winterboum

総合スコア23329

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

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

necocoa

2020/11/26 09:24

ありがとうございます! `@post = @posts.select{|post| post.id == params[:id].to_i}.first` `@post = @posts.find{|post| post.id == params[:id].to_i}` は同意でした。 詳しく実装みてないですが、findにblockを渡すとselect{}.firstと同意になるようです。(rubocop上で同意だよっていう指摘があったので合ってると思います。)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問