大阪でRailsを中心に学習している薬剤師エンジニア(初学者)こと、ヨマ(@yoma_2003)です!
Fromオブジェクトパターンを使用したActiveStorageの拡張子バリデーションを設定する方法をまとめます。
※おことわり※
断定口調でまとめますが、初学者であるため間違い等あればご指摘頂けると嬉しいです。
はじめに
やりたいこと
以下の様にPostモデルとCommentモデルに同時にデータを登録するため、PostCommentモデルというFormオブジェクトを作成した時、
PostCommentモデル(Formオブジェクト)において、Postモデルで保存する画像データに拡張子のバリデーションを設定したい。
class PostComment
include ActiveModel::Model
attr_accessor :post_name, :image, :comment_name, :content
def save
post = Post.create(name: post_name, image: image)
Comment.create(name: comment_name, content: content, post_id: post.id)
end
end
class Post < ApplicationRecord
has_one_attached :image
has_many :comments
end
class Comment < ApplicationRecord
belongs_to :post
end
結論
PostCommentモデル(Formオブジェクト)に以下の様にバリデーションを自作する。
validate :post_image_validation
def post_image_validation
if !self.image.content_type.in?(%('image/jpeg image/png'))
self.errors.add(:image, 'は JPEG 形式または PNG 形式のみ選択してください')
end
end
解説
1. バリデーションを自作する方法
ActiveStorageにはバリデーション機能が存在せず、以下の様に自作します。
validate :post_image_validation
def post_image_validation
#バリデーション処理を定義
end
2. モデルから作成されるインスタンスの画像データオブジェクトの違いについて
モデルから作成されるインスタンスの画像データにはcontent_type
カラムというファイル形式を格納する場所があり、ここに指定のファイル形式が含まれているかどうかを検証しますが、「ActiveStorageとアソシエーションを組む通常のモデルのインスタンス」と「Formオブジェクトのインスタンス」では作成される画像データのオブジェクト形式が異なり、格納されている場所も異なります。
投稿データをPostモデルのみに保存する場合
※PostモデルはActiveStorageとアソシエーションを組む、通常のモデル
作成された画像データ
→ActiveStorageクラスのオブジェクト
としてPostモデルインスタンスのimageカラム内のblobカラムに格納されます。
pry(#<PostsController>)> @post.image.blob
=> #<ActiveStorage::Blob:0x00007fa8ab1b5458
id: nil,
key: nil,
filename: "test.png",
content_type: "image/png",
metadata: {"identified"=>true},
byte_size: 5000,
checksum: "・・・",
created_at: nil>
Postモデルにバリデーションを設定すればよいので、以下の様にバリデーションを設定します。
validate :post_image_validation
def post_image_validation
if !self.image.blob.content_type.in?(%('image/jpeg image/png'))
self.errors.add(:image, 'は JPEG 形式または PNG 形式のみ選択してください')
end
end
Formオブジェクトを経由してPostモデルに保存する場合 (今回の場合)
※PostCommentモデルはFormオブジェクト
作成された画像データ
→ActionDispatchクラスのオブジェクト
としてPostCommentモデルインスタンスのimageカラムに格納されます。
pry(#<PostsController>)> @post_comment.image
=> #<ActionDispatch::Http::UploadedFile:0x00007fa8ab1ed290
@content_type="image/png",
@headers="Content-Disposition: form-data; name=\"post_comment[image]\"; filename=\"test.png\"\r\nContent-Type: image/png\r\n",
@original_filename="test.png",
@tempfile=#<File:/var/folders/mf/000000000000/T/RackMultipart20220816-6841-0000.png>>
※ActiveStorageとのアソシエーションを定義していないPostCommentモデルでは、アップロードされた画像データはActionDispatchオブジェクトとしてインスタンス内にデータが格納されます。
PostCommentモデルにバリデーションを設定すればよいので、以下の様にバリデーションを設定します。
validate :post_image_validation
def post_image_validation
if !self.image.content_type.in?(%('image/jpeg image/png'))
self.errors.add(:image, 'は JPEG 形式または PNG 形式のみ選択してください')
end
end
おわりに
今回、ActiveStorageのバリデーションに関する記事はいくつか見つけられましたが、Formオブジェクトでのバリデーションに関する記事はあまりみつからなかったためまとめてみました。
正直、タイトルはギリギリ語弊がありそうな感じがします、、、
参考