はじめに
Railsのバリーデーションについて、複数のモデルに対して同時保存をする際のバリデーションを設定する際、基礎の理解が甘いまま疎かになってしまっていたので備忘録として残そうと思います。
バリデーションとは?
はじめに、バリデーションというのは入力された値がちゃんと正しいものなのかチェックすることです。
例えば、カナ入力欄にひらがなや漢字が入力されていたり、電話番号入力欄に20桁くらいの数列が入力されているなど、こういったものが送信、登録されてしまうと不都合ですよね。それを登録するために弾く仕組みがバリデーションです。
今回行うこと
複数モデルを同時に登録することのできる簡単なアプリを用意し、バリデーションを設定。エラーメッセージを表示させる。
環境
Rails 5.0.7.2
Ruby 2.5.1
アプリケーションの設計
今回作成したアプリケーションは、一つのツイートが複数の写真を持つことができるようになっています。
別の記事でもほぼ同じものを使用しましたが、今回はそれに追記していく形になります。
テーブル
tweet |
---|
content |
image |
---|
url |
tweet_id |
モデルファイル
class Tweet < ApplicationRecord
has_many :images
accepts_nested_attributes_for :images
#バリデーション記述
validates_associated :images
validates :content, presence: true
validates :images, presence: true
end
class Image < ApplicationRecord
belongs_to :tweet, optional: true
mount_uploader :url, ImageUploader
end
accepts_nested_attributes_for
の記述によって複数モデルへ同時投稿を可能にしています。
今回追記した部分は、tweet.rb
の「バリデーション記述」以下の部分です。
validates_associated :imagesの記述によって、アソシエーションを組んであるモデルに対してのバリデーションを組むことができます。tweetとimageの関係は1対多のため、複数形で記述します。
そして、その下にバリデーションの条件を記述しています。こちらは本題の前提となる部分ですので、割愛させていただきます。
コントローラー
class TweetsController < ApplicationController
def index
@tweets = Tweet.includes(:images).all.order("id DESC")
end
def new
@tweet = Tweet.new
2.times{@tweet.images.build}
end
def create
@tweet = Tweet.new(tweet_params)
@tweet.save
if @tweet.save
redirect_to action: "index"
else
render "new"
end
end
def edit
@tweet = Tweet.find(params[:id])
num = @tweet.images.length
num = 2 - num
num.times{@tweet.images.build}
end
def update
@tweet = Tweet.find(params[:id])
@tweet = @tweet.update(tweet_params)
if @tweet.save
redirect_to action: "index"
else
render "edit"
end
end
private
def tweet_params
params.require(:tweet).permit(
:content,
images_attributes: [:id, :url]
)
end
end
ここはcreateアクション
を見てもらいたいのですが、バリデーションが働き、tweetが保存できなかった場合にnewアクションにrenderされるようになっています。
フォーム
.contents
- if @tweet.errors.any?
.chat-group-form__errors
%h2="#{@tweet.errors.full_messages.count}件のエラーが発生しました。"
%ul
- @tweet.errors.full_messages.each do |message|
%li= message
= form_for @tweet, class: "form" do |f|
.text-box
= f.label :content, "テキスト"
= f.text_area :content, placeholder: "ツイート"
- if @tweet.images
- @tweet.images.each do |img|
.file
= f.fields_for :images, img do |c|
.image-field{id: "#{c.index}"}
- if img.url.present?
= image_tag img.url
- else
.image-blank
= c.file_field :url
.submit
= f.submit "投稿", {class: "btn"}
if @tweet.errors.any?
でエラー文がある場合に全てのエラー文を表示させるようになっています。モデルファイルにvalidates_associated :images
の記述をすれば、ここは特に変わった記述をする必要はありません。
結果
何も入力せず投稿ボタンを押すと、
2件のエラーが発生しました。
Content can't be blank
Images can't be blank
と表示されます。
まとめ
ツイッターのような、一つのツイートに対して複数の写真を登録したいときなどのバリデーションを組むときは、親モデルファイルにvalidates_associated
の記述をし、他は特に変わった記述をせず実装することができる。
まだまだ理解が甘い部分も多いため、間違っている箇所がありましたらご指摘いただけるとありがたいです。
関連リンク
今回で使ったアプリを作成した時の記事→ carrierwaveでmultiple: trueは相性が悪い?