attachment_fuプラグインで画像アップロード
前提
RMMagicをインストールしておく。
インストール
railsアプリケーションのトップでscript/pluginを実行してインストール
>ruby script/plugin install http://svn.techno-weenie.net/projects/plugins/attachment_fu
今回の例
既存の商品モデル(product)の編集ページに対してその商品写真(photo)を加えるという例を作ってみる。
http://d.hatena.ne.jp/cuspos/20071110を参照させていただきました。
Model生成とmigrate
アップロードデータを扱うモデルを作成。
今回は商品(product)の商品画像(photo)のモデルを作成。
nyaago@kyonkyon: 501 $./script/generate model photo exists app/models/ exists test/unit/ exists test/fixtures/ create app/models/photo.rb create test/unit/photo_test.rb create test/fixtures/photos.yml exists db/migrate create db/migrate/20090607125051_create_photos.rb
それから、migration。
Photoテーブルを生成する。各カラムは、fu_attachmentでのおきまりのよう。
class CreatePhotos < ActiveRecord::Migration def self.up create_table :photos do |t| t.column :parent_id, :integer t.column :content_type, :string t.column :filename, :string t.column :thumbnail, :string t.column :size, :integer t.column :width, :integer t.column :height, :integer t.timestamps end end def self.down drop_table :photos end end
それから、商品テーブル側に、photoへの参照をつけるmigrate
nyaago@kyonkyon: 502 $./script/generate migration mod_product exists db/migrate create db/migrate/20090607125553_mod_product.rb
class ModProduct < ActiveRecord::Migration def self.up add_column(:products, :photo_id, :integer, :null => true) end def self.down remove_column(:products, :photo_id) end end
rake db:migrate
model
photoモデルの実装を行う。
class Photo < ActiveRecord::Base has_attachment :content_type => :image, :storage => :file_system, :max_size => 1.megabyte, :thumbnails => { :thumb => '100x100>', :small => '50x50>' } validates_as_attachment def full_filename(thumbnail = nil) file_system_path = (thumbnail ? thumbnail_class : self).attachment_options[:path_prefix].to_s File.join(RAILS_ROOT, file_system_path, *partitioned_path(thumbnail_name_for(thumbnail))) end end
-
- has_attachmentメソッドで画像の保存方法を指定。
今回、使っているのは以下のオプション
:content-type ファイルタイプ指定。今回は画像。他に何が指定できるのかは調べていないけど。
:storage ストレージタイプ。今回がファイルシステム。他に:db_file(db), :s3(amazon s3)がある。(プラグインのtechnoweenie/attachment_fu_/backends以下に対応するクラスが定義されている。)
:max_size ファイルの最大サイズ
:thumbnails これを指定すると、サムネイルを作ってくれる。
あと今回使ってないオプション
:resize_to リサイズして登録される。
-
-
- validates_as_attachmentメソッドにより、保存時のvalidationが実行されるようになる。
-
-
-
- full_filenameメソッドをオーバーライドすることにより、ファイルの保存場所を変更できる。今回、デフォルト実装のままだけど後で変更したくなりそうなので、ここの定義しているみている。
-
商品(product)モデル側も修正。
class Product < ActiveRecord::Base belongs_to :photo .. 省略 ..
商品からそれに関する写真を参照するためbelongs_to属性を指定。
views
以下、関係あるところだけを引用。
<div> <% unless @product.photo.nil? then %> <%= image_tag(@product.photo.public_filename()) %> <% end %> </div> <div> <label for="pthto">写真を追加:</label> <%= f.file_field :uploaded_data %> </div>
-
- モデルのpublic_filenameメソッドにより、画像へのurlが返されるので、その結果を使ってimageタグを作っている。
- file_fieldメソッドで ファイルアップロードタグを作っている。:uploaded_dataという名前を指定して作ってやるのが標準。そうすることにより。controller側ではPhoto.new(params[:photo] )としてmodelを作ってsaveするだけで、画像の保存までできる。
controller
参照元商品とこのphotoを保存するための定義を行う。
# productの更新を行う。 # Validationでエラーがあった場合は、再度入力ページ(edit)を表示する。 def update begin if params[:id] and params[:id].to_i > 0 # for update product = Product.find_by_id(params[:id]) if product.nil? then redirect_to :controller => '/error', :action => 'index' end photo = prepare_photo_for_update(product) product.attributes = params[:product].dup.delete_if { |key, value| key.to_sym == :uploaded_data } else # for insert photo = prepare_photo_for_insert product = Product.new() product.attributes = params[:product].dup.delete_if { |key, value| key.to_sym == :uploaded_data } product.photo_id = photo.id end # validate unless photo.nil? unless photo.valid? prepare_for_edit(product) return render(:action => "edit") end end unless product.valid? prepare_for_edit(product) return render(:action => "edit") end # save Product.transaction { unless photo.nil? if(!photo.save) prepare_for_edit(product) return render(:action => "edit") end product.photo_id = photo.id end if(!product.save) prepare_for_edit(product) return render(:action => "edit") end } rescue => e p e.inspect logger.debug(e.inspect) return redirect_to_error(e.inspect) end #このあと省略(表示ページへのリダイレクトをする) end private # 商品挿入時のphotoモデル生成 def prepare_photo_for_insert return nil if params[:product][:uploaded_data].blank? photo = Photo.new() # puts params[:product][:uploaded_data] photo.uploaded_data = params[:product][:uploaded_data] photo end # 商品変更時のphotoモデル生成 def prepare_photo_for_update(product) photo = if product.photo_id then Photo.find_by_id(product.photo_id) else nil end if photo.nil? Photo.new(:uploaded_data => params[:product][:uploaded_data]) else photo.uploaded_data = params[:product][:uploaded_data] photo end end # 編集ページを開く場合の準備 def prepare_for_edit(product) @product = product if @photo = Photo.find_by_id(product.photo_id) else @photo = nil end end
上記処理についての備考。
課題
以下は、これから検討。
-
- 画像削除する。
- 保存確認ページを実装する場合はどうする?
- 商品に対する複数Photoの保存(has_many アソシエーション)。
- 一覧データ+画像ファイル群からバッチで登録する。
- 保存場所をアプリケーション以下とは別の場所(または別ホスト)に保存できるか?