📝
[Rails][ActiveStorage] has_many_attached 使用メモ
ActiveStorageについて
Blobオブジェクトを保存する際に使用します。
has_many_attached
の使用は初めてだったのでメモとして残します。
詳細はRailsガイドを参照。
has_many_attachedについて
1レコードに複数Blobオブジェクトレコードが紐付く形で以下みたいな構造イメージです。
class Post < ApplicationRecord
has_many_attached :files
end
# Postレコードイメージ
{ id: 1, title: "とんこつラーメン" }
# Blobレコードイメージ(Post.find(1).filesのポリモーフィック部分)
{ id: 1, post_id: 1, filename: "王道とんこつラーメン写真", .. }
{ id: 2, post_id: 1, filename: "邪道とんこつラーメン写真", .. }
{ id: 3, post_id: 1, filename: "二刀流とんこつラーメン写真", .. }
Blobオブジェクト一覧取得について
JSON返却する際はBlobオブジェクト一覧取得で
どの関連オブジェクトid(posts.id)が紐付いているか表示するには以下のようにマッピングが必要になります。
post = Post.find(1)
post.files.blobs.map do
{
id: _1.id,
post_id: post.id
filename: _1.filename
}
end
複数Blobオブジェクト更新について
例えばhas_many_attachedを使用していると
Blobオブジェクト1,2,3が登録されている状態で、
Blobオブジェクト4,5を追加したいという以下みたいなケースがあります。
# 変更前
{ id: 1, post_id: 1, filename: "王道とんこつラーメン写真", .. }
{ id: 2, post_id: 1, filename: "邪道とんこつラーメン写真", .. }
{ id: 3, post_id: 1, filename: "二刀流とんこつラーメン写真", .. }
# 変更後
{ id: 1, post_id: 1, filename: "王道とんこつラーメン写真", .. }
{ id: 2, post_id: 1, filename: "邪道とんこつラーメン写真", .. }
{ id: 3, post_id: 1, filename: "二刀流とんこつラーメン写真", .. }
{ id: 4, post_id: 1, filename: "邪道4とんこつラーメン写真", .. }
{ id: 5, post_id: 1, filename: "二刀流5とんこつラーメン写真", .. }
以下のような順番で処理を実行した場合に
1.初回Blob登録
の内容が消されて保存されます。
(知っていればなんてことないですが知らないと少しびっくりします)
# 1.初回Blob登録
Post.new({
post_id: 1,
files: [
{ filename: "王道とんこつラーメン写真", .. },
{ filename: "邪道とんこつラーメン写真", .. },
{ filename: "二刀流とんこつラーメン写真", .. }
]
}).save!
# 2.更新Blob更新
Post.new({
post_id: 1,
files: [
{ filename: "邪道4とんこつラーメン写真", .. },
{ filename: "二刀流5とんこつラーメン写真", .. }
]
}).save!
# 変更後の最終レコード
{ id: 4, post_id: 1, filename: "邪道4とんこつラーメン写真", .. }
{ id: 5, post_id: 1, filename: "二刀流5とんこつラーメン写真", .. }
attach
してから実行すると期待通りに動作します。
# 1.初回Blob登録
Post.new({
post_id: 1,
files: [
{ filename: "王道とんこつラーメン写真", .. },
{ filename: "邪道とんこつラーメン写真", .. },
{ filename: "二刀流とんこつラーメン写真", .. }
]
}).save!
# 2.更新Blob更新
post = Post.find(1)
post.files.attach(
[
{ filename: "邪道4とんこつラーメン写真", .. },
{ filename: "二刀流5とんこつラーメン写真", .. }
]
)
post.save!
# 変更後の最終レコード
{ id: 1, post_id: 1, filename: "王道とんこつラーメン写真", .. }
{ id: 2, post_id: 1, filename: "邪道とんこつラーメン写真", .. }
{ id: 3, post_id: 1, filename: "二刀流とんこつラーメン写真", .. }
{ id: 4, post_id: 1, filename: "邪道4とんこつラーメン写真", .. }
{ id: 5, post_id: 1, filename: "二刀流5とんこつラーメン写真", .. }
ActiveStorageのバリデーションについて
だいたいBlobオブジェクトを保存する際に以下3つは最低制限をかけるかと思います。
- ファイルサイズ
- ContentType
- ファイル拡張子
現状デフォルトのバリデーションでBlobオブジェクトのバリデーションは設定できないので、
自前でバリデーションを書くか、Gemを導入するかになっています。。。
自前バリデーションの場合
class Post < ApplicationRecord
has_many_attached :files
ALLOOWED_FILE = [
".xls",
".xlsm",
".xlsx",
".xltm"
]
validate :valid_file_type
private
def valid_file_type
error_file = files.blobs.any? { ALLOOWED_FILE.exclude?(_1.content_type) }
errors.add(:base, "エラーメッセージ") if error_file
end
end
Gemのバリデーションの場合
Discussion