この記事で書くこと
carrierwave を使った画像アップロード機能で格納するディレクトリや、ファイル名に紐ずくモデルのidを使用していた時に、起きた難しさについて。
結論
モデルごとのプライマリーキーであるidは、勝手にrailsがやってくれるカラムであって、特別な意味、今回であれば画像に関する用途に対して使用するのはよくない。
ユニークな名前にしたいのであれば SecureRandom を使用して image_path_code
などの列を作成し、保存した上で重複したファイル名が使われないようにする。もしくは、頻繁に作成されるものでなければ、timestampを使用するなどの方法もある。
起こったこと
以下のモデルのデータを一括でcsvにて大量に作る機能が必要になった。
class Animal < ApplicationRecord
mount_uploader :animal_icon, AnimalIconUploader
end
class AnimalIconUploader < ApplicationUploader
def store_dir
"uploads/images"
end
def extension_whitelist
%w(jpg jpeg png gif)
end
def filename
"animal_img_#{model.id}#{File.extname(original_filename)}" if original_filename.present?
end
end
csv経由でデータを作ろうとする準備
今回は animals#index
に一括登録のインターフェイスを追加することにする。
routing
中略
post :animal_bulk_imports, to: 'animals#bulk_import', as: :animal_bulk_imports
中略
view
以下を追加
中略
%section
.title
csvから一括で登録する
= form_tag animal_bulk_imports_path, multipart: true do
%p
動物たちのcsvファイル
= file_field_tag :animal_csv
= submit_tag 'インポート', class: 'btn btn-default'
中略
controller
class AnimalController < ApplicationController
中略
def bulk_import
Animal.bulk_import(params[:animal_csv]) if params[:animal_csv]
redirect_to animals_path, notice: 'csvによる登録を受け付けました'
end
private
def animal_bulk_import_params
params.require(:bulk_import_animal).permit(:animal_csv)
end
中略
end
一括でcsvを通じて例えば以下のように画像をセットして保存しようとすると、
animal = Animal.new
# row['animal_icon_url'] には参照可能なurlが記載されているとする
animal.remote_animal_icon_url = row['animal_icon_url']
animal.save
画像に model.id
が使用されているので、正しく保存されず、画像を登録することができない。
ということが起きる。
こうなると、どうするのがいい方法なのかわかっていません。。。
既存データを新しい参照先に書き換えるようなタスクを書いて、既存データを移行してから、対応するしかないのだろうか。。
少なくとも今後作成するアプリケーションでは、画像パスについては model.id
を参照しないようにした方がいい、ということを学びました。