作成中のアプリケーションから、画像アップローダー「CarrierWave」を使えるようにする
画像投稿機能を実装するために必要な画像アップローダーとして、今回は、「CarieerWave」という画像アップローダーを用います。CarrierWaveをRailsアプリケーションで用いるためには、Railsアプリケーション側にcarrierwave
というgemが必要となります。
早速Gemfile
を更新していきましょう。
source 'https://rubygems.org'
gem 'rails', '5.1.6'
gem 'bcrypt', '3.1.12'
gem 'faker', '1.7.3'
+ gem 'carrierwave', '1.2.2'
+ gem 'mini_magick', '4.7.0'
gem 'will_paginate', '3.1.6'
...略
group :production do
gem 'pg', '0.20.0'
+ gem 'fog', '1.42'
end
...略
とりあえず現時点で必要になるのはcarrierwave
のみです。mini_magickや
fog`というgemは、今後追加していく機能で使用するgemです。
Gemfileを更新したので、当然ながらbundle install
を実行する必要があります。
# bundle install
...略
Fetching gem metadata from https://rubygems.org/........
Fetching gem metadata from https://rubygems.org/.
Resolving dependencies....
...略
Fetching mime-types-data 3.2019.1009
Installing mime-types-data 3.2019.1009
Fetching mime-types 3.3.1
Installing mime-types 3.3.1
Fetching carrierwave 1.2.2
Installing carrierwave 1.2.2
...略
Fetching mini_magick 4.7.0
Installing mini_magick 4.7.0
...略
Bundle complete! 29 Gemfile dependencies, 88 gems now installed.
Gems in the group production were not installed.
Bundled gems are installed into `/usr/local/bundle`
bundle install
は特に問題なく完了できたようです。
Railsのジェネレーターで画像アップローダーを生成する
# rails generate uploader Picture
Running via Spring preloader in process 15235
create app/uploaders/picture_uploader.rb
少なくともcarrierwave
(または他の画像アップローダー)gemがインストールされていないと、rails generate uploader
コマンドは正常に完了しません。
アップロードされた画像をMicropostモデルに関連付けるようにする
マイクロポストに画像を関連付けるために必要な属性の追加
マイクロポストの画像投稿機能は「画像はマイクロポストに紐付けされる」という実装内容になるため、アップロードされた画像はMicropostモデルに関連付けられるのが自然な実装モデルです。実際には、「RDB上microposts
テーブルのpicture
カラムに、アップロードされた画像のファイル名を格納する」という形の実装をします。picture
カラムにはファイル名が入るので、型はstring
となります。
picture
カラムを追加した新たなマイクロポストのデータモデルは、以下のようになります。
データモデルに変更を加えたので、対応するマイグレーションファイルの生成、ならびに生成したマイグレーションのRDBへの適用が必要となります。なお今回は、生成したマイグレーションファイルの内容に手を加える部分はありません。
# rails generate migration add_picture_to_microposts picture:string
Running via Spring preloader in process 15244
invoke active_record
create db/migrate/20200105225338_add_picture_to_microposts.rb
# rails db:migrate
== [timestamp] AddPictureToMicroposts: migrating ===========================
-- add_column(:microposts, :picture, :string)
-> 0.0183s
== [timestamp] AddPictureToMicroposts: migrated (0.0199s) ==================
Micropostモデル側でCarrierWaveを使うために必要な実装
画像と関連付けたモデルをCarrierWave側に伝えるためには、モデル側でmount_uploader
メソッドを呼び出します。mount_uploader
の第1引数は「属性名を指すシンボル」、第2引数は「アップローダーのクラス名」を取ります。例えば「モデル側の属性名がpicture
、アップローダー名がPictureUploader
」であれば、mount_uploader
メソッドは以下のように呼び出されます。
mount_uploader :picture, PictureUploader
app/models/micropost.rb
全体の変更内容は以下のようになります。
class Micropost < ApplicationRecord
belongs_to :user
default_scope -> { order(created_at: :desc) }
+ mount_uploader :picture, PictureUploader
validates :user_id, presence: true
validates :content, presence: true, length: { maximum: 140 }
end
現時点で、テストスイートは全体が成功するはずです。
# rails test
Running via Spring preloader in process 15277
Started with run options --seed 12165
61/61: [=================================] 100% Time: 00:00:13, Time: 00:00:13
Finished in 13.04142s
61 tests, 329 assertions, 0 failures, 0 errors, 0 skips
マイクロポスト投稿フォームに画像アップローダーを追加する
Homeページに画像アップローダーを表示させるためには、マイクロポスト投稿フォームに画像アップローダーを追加するのが自然です。このような場合に使うのはfile_field
メソッドとなります。早速app/views/shared/_micropost_form.html.erb
に画像アップローダーの実装を追加しましょう。
<%= form_for(@micropost) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Compose new micropost..." %>
</div>
<%= f.submit "Post", class: "btn btn-primary" %>
+ <span class="picture">
+ <%= f.file_field :picture %>
+ </span>
<% end %>
MicropostモデルのWebから更新できる属性に、新たにpicture
を追加する
Webから画像をアップロードしてマイクロポストに紐付けられるようにするためには、Micropostコントローラーで用いているStrong Parameters機能で、Webからの更新を許可するパラメーターにpicture
を追加する必要があります。変更対象のファイルはapp/controllers/microposts_controller.rb
ですね。
class MicropostsController < ApplicationController
...略
private
def micropost_params
- params.require(:micropost).permit(:content)
+ params.require(:micropost).permit(:content, :picture)
end
...略
end
マイクロポストの画像表示を追加する
マイクロポストに画像を関連付けても、その画像がフィード画面に表示されないのでは意味がありません。というわけで、Micropostパーシャル側にも「マイクロポストの画像表示」のための新たな実装が必要となります。Micropostパーシャルの実体であるapp/views/microposts/_micropost.html.erb
は、以下のように変更していきます。
<li id="micropost-<%= micropost.id %>">
<%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
<span class="user"><%= link_to micropost.user.name, micropost.user %></span>
- <span class="content"><%= micropost.content %></span>
+ <span class="content">
+ <%= micropost.content %>
+ <%= image_tag micropost.picture.url if micropost.picture? %>
+ </span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
<% if current_user?(micropost.user) %>
<%= link_to "delete", micropost, method: :delete, data: { confirm: "You sure?" } %>
<% end %>
</span>
</li>
「マイクロポストに画像が関連付けられていなければ、画像を表示する部分の描画そのものを行わない」というのがポイントです。「マイクロポストに画像が関連付けられているか否か」というのは、picture?
というメソッドの戻り値によってわかるようになっています。
picture?
メソッドは、CarrierWaveによって自動的に生成されるメソッドです。メソッド名は、モデル側で定義した画像の属性名をもとにして、CarrierWaveによって自動的に決定されます。