tkymtk's blog

Ruby on Rails及びその周辺について調べたこと。Do whatever you want to do at your own pace.

polymorphic associationをつかってPaperclipで画像を複数アップロード

Paperclipの導入

Gemfileの設定

gem "paperclip", "~> 4.1"

上のやつで、generatorが使えない場合はこれで

gem "paperclip", :git => "git://github.com/thoughtbot/paperclip.git"

$ bundle install

前提

  • FoodモデルとUserモデルがあって、それぞれimageを複数持つ。
  • 最低限必要なことぐらいしか書かないです。

モデルの設定

Foodモデル

class Food < ActiveRecord::Base
  has_many :images, as: :imageable
  accepts_nested_attributes_for :images
end

User モデル

class User < ActiveRecord::Base
  has_many :images, as: :imageable
  accepts_nested_attributes_for :images
end

Imageモデル

class Image < ActiveRecord::Base
  belongs_to :imageable, polymorphic: true
  has_attached_file :image, :styles => { :medium => "300x300>", :thumb => "100x100>" }, :default_url => "/images/:style/missing.png"
  validates_attachment_content_type :image, :content_type => /\Aimage\/.*\Z/
  validates :image, :attachment_presence => true
end

マイグレーションの設定

次のコマンドで以下のマイグレーションを作成

rails generate paperclip image image
コマンド使わなくてもいい。

直接画像を扱うImageモデル

class AddAttachmentImageToImages < ActiveRecord::Migration
  def self.up
    change_table :images do |t|
      t.attachment :image
    end
  end

  def self.down
    drop_attached_file :images, :image
  end
end

画像を持たせたい方のモデル

class CreateFoods < ActiveRecord::Migration
  def change
    create_table :foods do |t|
      t.string :name
      t.text :description

      t.timestamps
    end
  end
end

コントローラーの設定

# /controllers/foods_controller.rb

  def new
    @food = Food.new
    @food.images.build
  end

  private
    def set_food
      @food = Food.find(params[:id])
    end

    def food_params
      params.require(:food).permit(:name, :description, images_attributes: [:image])
    end

ビューの設定

# /views/foods/show.html.erb

<p id="notice"><%= notice %></p>

<p>
  <strong>Name:</strong>
  <%= @food.name %>
</p>

<p>
  <strong>Description:</strong>
  <%= @food.description %>
</p>

<p>
  <strong>Image:</strong>
  <%= image_tag @food.images.first.image.url(:medium) if @food.images.present? %>
</p>

<%= link_to 'Edit', edit_food_path(@food) %> |
<%= link_to 'Back', foods_path %> |
<%= link_to 'Delete', @food, method: :delete, data: { confirm: "you sure?" } %> |

大事なとこ

# ビュ-の書き方
 <%= image_tag @food.images.first.image.url(:medium) if @food.images.present? %>
# コントローラーで`new`のとき`build`しておく
  def new
    @food = Food.new
    @food.images.build
  end

所感

こんな感じでできるはず

間違いがあれば、ご指摘下さると幸いです。