複数モデル間でのバリデーションについて
初投稿です。至らない点もあるかとは思いますが、よろしくおねがいします。
今回はバリデーションのかけ方を書いていきたいと思います。
検索してもなかなかいい記事がなかったので、簡単にまとめていけたらと思います。
前提
複数モデル間のバリデーションを書いていきます。
モデルはItemモデルとProduct_imageモデルとします。
イメージとしてはフリマアプリのメルカリで商品を出品するところをイメージしていただけたらいいと思います。
商品出品ページのフォームに商品情報を入れ出品ボタンを押して、Itemテーブルとproduct_imageテーブルに保存していきます。
モデル間のアソシエーションはItem対product_image = 1 : 多とします。
モデルの記載
さっそくモデルにアソシエーションとバリデーションを記載していきます。
※ 分かりやすくシンプルにするため、他の機能のアソシエーションやバリデーションは省略しています。
item.rb
class Item < ApplicationRecord # アソシエーション has_many :product_images, dependent: :destroy accepts_nested_attributes_for :product_images, allow_destroy: true # バリデーション validates :product_name, :product_information, :product_status, :price, :product_condition, :shipping_charge, :days_of_ship, :prefecture_id, presence: true validates :product_name, length: { maximum: 40 } validates :product_information, length: { maximum: 1000 } validates :price, numericality: { only_integer: true, greater_than_or_equal_to: 300, less_than_or_equal_to: 9999999 } validates_associated :product_images validate :product_images_number private def product_images_number if product_images.size < 1 errors.add(:product_image, "product_images are blank") end end end
それでは内容を説明していきます。
アソシエーション
ItemモデルとProduct_imagesモデルは1:多の関係なので、
has_many :product_imagesとしています。
- dependent: :destroyオプション :親レコードを削除した際、子レコードも同時に削除する役割があります。
- accepts_nested_attributes_forメソッド :親モデルを通じてネストしたモデルの関連レコードの登録・更新を可能にします。これがないと画像の保存ができません。
- allow_destroy: true → 親レコードに関連した子レコードの削除を許可します。dependent: :destroyとセットです。
バリデーション
各カラムに対してバリデーションを記載していきます。
例)
validates :product_name, :product_information, :product_status, :price, :product_condition, :shipping_charge, :days_of_ship, :prefecture_id, presence: true
上の例では、商品名(product_name)、商品情報(product_information)、、以下略
のカラムに対して、NOT NULL制約、つまり未入力を禁止するバリデーションをかけています。
validates_associated :product_images
上の記載ではItemモデルだけでなく関連付けされたproduct_imageモデルのバリデーションも同時に実行するよという記載になります。
product_images.rb
class ProductImage < ApplicationRecord belongs_to :item, optional: true mount_uploader :Product_image, ImageUploader # バリデーション validates :image, presence: true end
商品画像が投稿されていないときに出品できないようにするには
また、ここからが自分がハマった箇所で最も重要な箇所になるのですが、validates_associated: product_imagesを書いて満足してはいけません。
今のままの記載では、product_imagesがあった場合にしかバリデーションチェックを行ってくれません。
それじゃあ、下の記載は意味ないじゃないかと言われそうです。そのとおりです。
validates :image, presence: true
では、実際に商品画像が投稿されていない場合に投稿できないようにする記載をする必要があります。
それが下のコードで、validate :(メソッド名)でバリデーションのメソッドを作っています。
private以下にメソッドの処理を書いています。
メソッドの中身はparamsで送られてきた値の中で、product_imagesが空の場合は、エラー文に
「product_images are blank」のエラーメッセージを追加しています。
validate :product_images_number private def product_images_number if product_images.size < 1 errors.add(:product_image, "product_images are blank") end end
まとめ
いかがでしたでしょうか?
accepts_nested_attributes_forメソッドを使うと複数モデルへのデータ登録が簡単にできますが、初心者にはかなりハードな内容だと思います。
僕もめちゃくちゃ苦労しましたし、100%理解できているかと言われるとあやしいですが、初心者ながら学んだことをアウトプットしてみました。
なにか解釈の違いなどあると思いますので、コメントでの指摘待ってます!!