ある日、『refileで扱う画像をseedデータで設定するにはどうすればいいのですか?』という質問を受けたので調べながら答えたのをこの記事にまとめようと思いました。
設定方法だけでなく、軽く仕組みも調査してみました。
gem refile のgithubはこちら ↓
先ずは設定方法の結論から 🤑
例えば、公式githubのコードを参考にして説明すると...
usersテーブルのカラムに profile_image_id
というものが存在するとします。
その場合は、Userモデルで例えると以下のようになります。
class User < ActiveRecord::Base
# _id は書かないのがポイント
attachment :profile_image
end
app/assets/images/
ディレクトリ以下に任意の画像を置く
今回は smile.jpg というものが存在する前提で話を進めます。
seeds.rbでuserを作成する際にある画像を設定する
# この時、_id を書かないのがポイント
User.create(
任意のカラム名など: 任意の値,
...,
profile_image: File.open("./app/assets/images/smile.jpg")
)
$ rails db:seed
これで、画像の設定が完成しています!
だが しか~~~しっ !、!!
Ruby on Rails をよく分かっている方は違和感を持つことでしょう。
なぜ profile_image
が create の引数にあるのか。。。
DBのカラム名に相当するprofile_image_id
ではないのか。。。
より深く仕組みを知りたい方は続きを読んでみてください。
そもそもrefileの仕組み ~ 公式のREADMEを読んでみる🧐📕
Files are uploaded to a backend. The backend assigns an ID to this file, which will be unique for this file within the backend.
ファイルがuploadされたら、fileにuniqueなidを割り当てる。
By default files will be uploaded to ./tmp/uploads/store
デフォルトではuploadされたファイルはコンパイルか何かされてファイル名がそのuniqueなidになっており、 ./tmp/uplaods/store
に保存される。(ここではstoreと呼びます。)
画像を復元する場合、uniqueなidを元にstoreから画像を復元する仕組みのようです。
class User < ActiveRecord::Base
attachment :profile_image
end
Calling attachment generates a getter and setter with the given name. When you assign a file to the setter, it is uploaded to the cache:
attachment の引数の名前が getter setterのメソッドになるようです。
今回の例でいうと、
user.profile_image
で何かしら値を取得したり、
user.profile_image=
で値を設定できるということです。
ちなみに、ActiveRecordの仕様として
setterメソッドxxx=
は、モデル名.new(xxx: 任意の値)
で設定できるようになっています。(気が向いたらブログかqiitaで詳しく書きます。)
つまり、 以下のコードは同じ結果になるということです。
# インスタンス生成後にsetするパターン。
user = User.new
user.name = 'Refile太郎'
user.name # => Refile太郎
user.save
# ----------------------------------
# インスタンス生成時の引数に値を指定するパターン。
user = User.new(name: 'Refile太郎')
user.name # => Refile太郎
user.save
createメソッドは実はnewメソッドとsaveメソッドを実行してオブジェクトを返却するものです。
以下ソースコード (https://github.com/rails/rails/blob/main/activerecord/lib/active_record/persistence.rb#L33-L41)
なので冒頭の createの引数に なぜ profile_image: 値
が create の引数にある場合、newの引数に profile_image: 値
が渡っていき最終的にsaveされます。
しかし、 profile_image_id
カラムに画像idが保存される仕組みは謎です🤔💭
続いてrefile公式のREADMEから一部コードと説明の抜粋して仕組み追っていきます。
User.new
# 公式では↑のように書いているが
# user = User.new と解釈しておきましょう。
File.open("/some/path", "rb") do |file|
user.profile_image = file # fileオブジェクトをsetしている。
end
# profile_image.id とすると画像idが取得できる。
user.profile_image.id # => "fec421..."
When you call save on the record, the uploaded file is transferred from the cache to the store.
cacheは一時的なファイルデータ(DBなどの永続ストレージに保存されていない状態。)
storeはDBなどの永続ストレージと判断して良さそうです。
storeは永続的なストレージですが、デフォルトで画像fileが保存される ./tmp/uploads/store
のことでした。
説明文を解釈するとuser.profile_image
にfileオブジェクトをセットした後に user.save
すると、画像ファイルが./tmp/uploads/store
に保存される。
もしかして、この時についでにDBのusersテーブルのprofile_image_idカラムに画像idが保存されるんじゃね?!(◎_◎;)
実際に検証してみましょう。
save前はprofile_image_id
がnil
ですが、save後は、
profile_image_id
に画像idが入っていました!
(実際にrefileのコードを見るとsave前にそうする仕組みになっていました。気が向いたらそのことの書きます。。。)
これで、 しかし、 profile_image_id カラムに画像idが保存される仕組みは謎です🤔💭
の謎は解けました。
別の書き方でseedデータを作成するなら🧐💭(全くこの方法で書く必要性はないのですが)
先ほど最後の行で画像idを取得できていました。
# profile_image.id とすると画像idが取得できる。
user.profile_image.id # => "fec421..."
なので user.profile_image_id = 画像id
をした後にsaveしても画像idの保存は可能です。
例えば以下のような書き方でもseedデータを作成できます。
user = User.create(
任意のカラム名など: 任意の値,
...
)
File.open("./app/assets/images/smile.jpg") do |file|
user.profile_image = file
end
# ちなみに、上記の記述はこのように書くことも可能です。
# user.profile_image = File.open("./app/assets/images/smile.jpg")
# 画像idをuser.profile_image_idにsetします。
# しかし以下の記述はなくてよい。
user.profile_image_id = user.profile_image.id
user.save
rails db:seed
まとめ😌
-
モデルに定義した
attachment :xxx
によってfileオブジェクトを格納できるxxx
という getter, setterメソッドが利用可能になる。 -
fileオブジェクトがsetされているならば、
xxx.id
で画像idが取得できる。 -
fileオブジェクトがsetされている状態でsaveすると
xxx_id
カラムに画像idが保存される。
この仕組みをrefileのソースコードを追いながら調査した記事も自分のブログかqiitaで近日公開しようかと思います。(気が向いたら...)