はじめに
概要
この記事では、CarrierWaveという強力なgemを用いてファイルアップロード機能を実装する方法について学びます。CarrierWaveを使うことで、Railsアプリケーションに画像や他のファイルをアップロードする機能を簡単かつ効率的に追加できます。本記事では、ユーザー詳細ページの実装も行います。
目標
- CarrierWave gemの導入と設定方法を理解し、Railsアプリケーションに適用する。
- 画像ファイルのアップロード、保存、表示のための基本的な流れを習得する。
- Userモデルに画像アップロード機能を統合し、学習メモ一覧、ユーザー詳細ページでの画像表示を実現する。
事前準備
まずは、以下のコマンドを実行して、データベースをリセットしておきましょう。
1
rails db:migrate:reset
Railsサーバーを起動したら、Webブラウザを使用してアプリケーションのルートページにアクセスします。そこからヘッダーにある「アカウント登録」リンクをクリックして、アカウント登録画面を表示しましょう。
以下の情報でユーザーネーム、メールアドレス、パスワード、パスワードの確認を入力フォームに記入し、登録するボタンをクリックしてユーザーを新規登録しましょう。
- ユーザーネーム:
ありす
- メールアドレス:
hoge@example.com
- パスワード:
000000
- パスワードの確認:パスワードを入力
アカウント登録が完了したら、ログイン状態で学習メモを新規作成してみましょう。「新規作成」リンクをクリックして学習メモの投稿フォームにアクセスします。
以下の内容で学習メモを作成しましょう。
- タイトル:
URLを構成する基本的な要素
- 本文:
URLの基本的な構成要素には、プロトコル(Scheme)、ドメイン名(Host)、そしてパス(Path)の3つがある。
学習メモを投稿した後、ありすさんの学習メモが一覧に追加されて表示されることを確認しましょう。
これで事前準備は全て完了しました。ありすさんのユーザーでログインした状態で、次に進みましょう。
ファイルアップロード機能の概要
ファイルアップロード機能とは
ファイルアップロード機能は、ユーザーがアプリケーションに画像や文書などのファイルをアップロードできるようにする機能です。ユーザーは自分のデバイスからファイルを選択し、アプリケーションに送信することができます。
例えば、「ファイルを選択」ボタンなどをクリックすると、ファイルエクスプローラー(FinderやWindowsエクスプローラーなど)が開き、ユーザーはアップロードしたいファイルを選んでアプリケーションに送信できます。
このようなファイルアップロード機能により、ユーザーはアプリケーション内でプロフィール画像を設定したり、商品画像をアップロードしたりすることができます。
ファイルアップロード機能があれば、ユーザーがアプリケーション内で直接ファイルを共有し、多様なコンテンツを作り出せるのがいいね!
アプリケーションによっては、画像に限らず、文書や音声ファイルなど、さまざまな種類のファイルアップロードをサポートする場合があるよ。本章では、画像ファイルのアップロード機能を実装するよ!
画像ファイルの管理方法
ファイルアップロード機能で、特に画像ファイルをうまく扱うことは、アプリケーションの使いやすさやデータの整理にとても重要です。画像ファイルを管理する際に最も大切なのは、データベースに直接画像を保存しないということです。
画像をデータベースに保存すると、次のような問題が起きる可能性があります。
データベースのサイズが大きくなる: 画像や動画はファイルサイズが大きいので、これらをデータベースに入れると、データベースのサイズが膨大になりすぎて、アプリの動きが遅くなることがあります。
バックアップや復元が難しくなる: データベースのサイズが大きくなると、バックアップや復元が複雑になり、時間もかかります。
データベースの動作が遅くなる: たくさんの画像データがデータベースに入っていると、データを読み書きする速度が遅くなり、アプリ全体の速度に影響します。
そのため、通常は画像ファイルをデータベースに保存するのではなく、アプリケーションサーバー上など(※1)の特定のディレクトリに保存します。そして、データベースには、その画像ファイルへの参照情報(例えば、ファイルパスやファイル名)のみを保存します。
開発環境時のアプリケーションサーバー上に保存される例
画像ファイルへの参照情報でファイル名がデータベースに保存される例
この方法により、画像ファイルを効率的に管理し、アプリケーションのパフォーマンスを維持できます。Railsでは「CarrierWave」というgemを利用することで、ファイルアップロード機能を簡単に実装することができます。
ファイルアップロード機能の基本実装
学習メモアプリケーションに「CarrierWave」を活用して、画像ファイルのアップロード機能を実装します。CarrierWaveを使用することで、画像ファイルのアップロード、保存、表示などの処理が容易になります。
具体的には、アカウント登録時にユーザーが自身のアバター画像を「任意」でアップロードできる機能を追加します。また、ユーザーが後から自分のアバターを編集できるようにすることも含めます。
現在、ユーザーが投稿した学習メモのヘッダーにはデフォルトのアイコンが表示されていますが、ユーザーがアバターを登録している場合はそのアバターを表示し、登録していない場合はデフォルトのアイコンを表示させます。
それでは、手を動かしながら画像ファイルアップロード機能を実装していきましょう!
1.CarrierWaveの導入
Gemfileに以下の1行を追加し、ターミナルでアプリケーションのルートディレクトリに移動して、bundle install
コマンドを実行し、CarrierWaveをインストールしましょう。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby '2.7.5'
gem 'rails', '~> 6.1.3', '>= 6.1.3.1'
gem 'mysql2', '0.5.3'
gem 'puma', '5.5.2'
gem 'sass-rails', '6.0.0'
gem 'webpacker', '5.4.3'
gem 'turbolinks', '5.2.1'
gem 'jbuilder', '2.11.5'
gem 'bootsnap', '1.10.1', require: false
gem 'net-http'
gem 'devise', '4.9.3'
gem 'devise-i18n', '1.12.0'
gem 'carrierwave', '~> 2.0'
group :development, :test do
gem 'byebug', '11.1.3'
gem 'pry-rails', '0.3.9'
end
group :development do
gem 'web-console', '4.2.0'
# gem 'rack-mini-profiler', '2.3.3'
gem 'listen', '3.7.1'
gem 'spring', '3.1.1'
end
group :test do
gem 'capybara', '3.36.0'
gem 'selenium-webdriver', '4.1.0'
gem 'webdrivers', '5.0.0'
end
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
Gemfileに追加した後は、ターミナルで以下を実行しましょう。
1
bundle install
サーバーを起動している場合は、再起動しておきましょう。
2.実装手順の確認
ユーザーがアカウントを登録または編集する際にアバターを追加できるようにするためには、まずUser
モデルにアバター属性を追加する必要があります。Userモデルにはdeviseが適用されているため、以前のユーザーネームの追加手順と同様に、Applicationコントローラ内でのパラメータ許可の設定を行います。
アバターは任意の属性であるため、User
モデルでのバリデーションやエラーメッセージの設定は不要です。さらに、UserモデルでCarrierWaveの画像アップロード機能を利用できるように設定を追加する必要があります。実装手順は以下の通りです。
マイグレーションファイルの作成: Userモデルにアバターのカラムを追加するためのマイグレーションファイルを生成します。
マイグレーションの実行: 生成したマイグレーションファイルを使ってデータベースにアバターのカラムを追加します。
ビューファイルの編集: 新規登録やアカウント情報編集のフォームにアバター画像をアップロードするためのフィールドを追加します。
コントローラーでのパラメータ設定:
Application
コントローラーで、アバター画像に関連するパラメータをストロングパラメータに設定します。モデルにアップローダーのマウント:
User
モデルにCarrierWaveのアップローダーをマウントします。これにより、アバター画像のアップロード機能がUser
モデルに組み込まれます。学習メモのヘッダー表示の調整: 学習メモのヘッダーにおいて、ユーザーがアバターを登録している場合はそのアバターを表示し、登録していない場合はデフォルトのアイコンを表示させます。
それでは実際に手を動かしながら、Userモデルにアバター属性を追加して、画像アップロード機能を実装してみましょう。
3.画像アップロード機能の実装
avatarカラムの追加
新しい属性をUserモデルに追加するには、最初にマイグレーションファイルを作成して、データベースに新しいカラムを追加する必要があります。ユーザー情報を管理するusersテーブルにavatar
という新しいカラムを追加します。
見やすさを考慮してavatarカラムをusernameカラムの左側に移動しています。
avatar
カラムには、ユーザーがアップロードしたアバター画像の「ファイル名」が保存される予定です。そのため、avatar
カラムのデータ型はstring
と指定します。
ターミナルで以下のコマンドを実行し、マイグレーションファイルを生成し、データベースに変更を適用しましょう。
1
2
rails g migration AddAvatarToUsers avatar:string
rails db:migrate
マイグレーションを実行した後、phpMyAdminを使用してusers
テーブルを確認すると、avatar
カラムが追加されていることを確認できます。
実際のカラムの並び順はこの通りである必要はありません。
アバター項目をフォームへ追加
ユーザーが新規登録やアカウント情報を編集する際、フォームにアバターの項目を含める必要があります。このアバター項目は、新規登録とアカウント編集の際にのみ必要であり、ログインフォームには不要です。
そのため、以下の新規登録用と編集用ビューファイルにアバター項目を追加します。
- ユーザー新規登録画面:
app/views/devise/registrations/new.html.erb
- ユーザー情報編集画面:
app/views/devise/registrations/edit.html.erb
アカウント登録と編集のビューファイルにアバターの項目を追加しましょう。アバターの追加にあわせて、適切なスタイリングも行います。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<h2>アカウント登録</h2>
<%= form_with model: resource, as: resource_name, url: registration_path(resource_name), local: true, class: 'form' do |f| %>
<%= render 'shared/validation_error', instance: resource %>
<div class="form__group">
<%= f.label :username, 'ユーザーネーム(必須):' %>
<%= f.text_field :username, autofocus: true, autocomplete: 'username', class: 'form__input' %>
</div>
<div class="form__group">
<%= f.label :email, 'メールアドレス(必須):' %>
<%= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'form__input' %>
</div>
<div class="form__group">
<%= f.label :avatar, 'アバター(任意):' %>
<%= f.file_field :avatar, class: 'form__file' %>
</div>
<div class="form__group">
<%= f.label :password, 'パスワード(必須):' %>
<%= f.password_field :password, autocomplete: 'new-password', placeholder: '6文字以上で入力してください', class: 'form__input' %>
</div>
<div class="form__group">
<%= f.label :password_confirmation, 'パスワード確認(必須):' %>
<%= f.password_field :password_confirmation, autocomplete: 'new-password', class: 'form__input' %>
</div>
<div class="form__submit">
<%= f.submit '登録する', class: 'form__button' %>
</div>
<% end %>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<h2>マイページ編集</h2>
<%= form_with model: resource, url: registration_path(resource_name), local: true, method: :put, class: 'form' do |f| %>
<%= render 'shared/validation_error', instance: resource %>
<div class="form__group">
<%= f.label :username, 'ユーザーネーム(必須):' %>
<%= f.text_field :username, autofocus: true, autocomplete: 'username', class: 'form__input' %>
</div>
<div class="form__group">
<%= f.label :email, 'メールアドレス(必須):' %>
<%= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'form__input' %>
</div>
<div class="form__group">
<%= f.label :avatar, 'アバター(任意):' %>
<%= f.file_field :avatar, class: 'form__file' %>
</div>
<div class="form__group">
<%= f.label :password, 'パスワード:' %>
<%= f.password_field :password, autocomplete: 'new-password', placeholder: '空欄のままなら変更しません', class: 'form__input' %>
</div>
<div class="form__group">
<%= f.label :password_confirmation, 'パスワード確認:' %>
<%= f.password_field :password_confirmation, autocomplete: 'new-password', class: 'form__input' %>
</div>
<div class="form__group">
<%= f.label :current_password, '現在のパスワード(必須):' %>
<%= f.password_field :current_password, autocomplete: 'current-password', placeholder: '変更を反映するには現在のパスワードを入力してください', class: 'form__input' %>
</div>
<div class="form__submit">
<%= f.submit '更新する', class: 'form__button' %>
</div>
<% end %>
layout.scss
ファイルを開いて、以下の2箇所にスタイルを追加しましょう。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// ...省略...
.form {
margin: 40px 0 25px;
&__group {
margin-bottom: 20px;
display: flex;
flex-direction: column;
align-items: flex-start;
}
&__input,
&__textarea {
width: 100%;
height: 42px;
border: 1px solid #ddd;
border-radius: 2px;
padding: 10px 16px;
margin: 8px 0;
&:focus-visible {
outline: -webkit-focus-ring-color auto 1px;
}
&.form__textarea {
height: 240px;
}
}
&__file {
margin: 8px 0;
}
&__submit {
text-align: center;
padding-top: 15px;
font-weight: 700;
margin-bottom: 40px;
}
&__button {
border-radius: 4px;
padding: 10px 30px;
text-align: center;
}
}
// ...省略...
Webブラウザでアプリケーションのルートページにアクセスしましょう。現在はありすさんとしてログインしているため、ルートページは以下のように表示されます。次に、ヘッダーにある「マイページ編集」リンクをクリックして、アカウント編集画面に進みます。
アカウント編集画面には、アバターを追加するための新しい項目がフォームに含まれていることがわかります。
ありすさんのユーザーからログアウトしましょう。
その後、「アカウント登録」リンクをクリックしてアカウント登録画面を表示してください。そうすると、以下のようにアバターの項目が追加されているのが確認できます。
現段階では、「ファイルを選択」ボタンをクリックして画像をアップロードしても、コントローラーでのパラメータ許可やモデルでのCarrierWave設定が完了していないので、アバターはまだ登録できません。次に実際にアバター画像を登録できるか確かめてみます。
以下の情報で登録フォームに必要情報を入力し、ユーザーを新規登録してみてください。アバター画像は、リンクからダウンロードしてアップロードを試みてください。
- ユーザーネーム:
たけし
- メールアドレス:
fuga@example.com
- アバター:こちらの画像(user_sample_avatar.png)をダウンロードしてください。
- パスワード:
111111
- パスワードの確認:パスワードを入力
以下のように「ファイルを選択」をクリックして画像をアップロードすると、「選択されてません」という表示が画像のファイル名user_sample_avatar.png
に変わります。必要な情報を入力し終えたら、「登録する」ボタンをクリックしてください。
ユーザーを新規登録した後、以下のようにルートページにリダイレクトされ、成功のメッセージが表示されます。しかし、実際にはアバターは登録されていません。
phpMyAdminでusersテーブルのavatar
カラムを確認しましょう。
ここでは、たけしさんのアバター画像のuser_sample_avatar.png
が保存されていることを期待しますが、実際には以下のようにNULL
となっていることがわかります。
このようにusersテーブルにavatar
カラムを追加し、ビューフォームにアバターの入力欄を設けただけでは画像が保存されないことが確認できました。画像をデータベースに正しく保存するためには、さらにいくつかのステップを完了する必要があります。
まず、Userモデルにユーザーネームを追加した時と同様に、Applicationコントローラで画像データを許可するための設定を追加しましょう。
Applicationコントローラでパラメータ設定
ユーザーネームを追加した際に学んだ内容ですが、少し振り返ります。
deviseを使ったUserモデルでは、メールアドレスやパスワードなどのデフォルト属性は特別な設定なしで使えます。しかし、新しく追加した属性は、Applicationコントローラ内で明示的に許可する設定が必要です。許可にはconfigure_permitted_parameters
メソッドを利用します。
1
2
3
4
5
6
class ApplicationController < ActionController::Base
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:アクション名, keys: [:カラム名1, :カラム名2, ...])
end
end
上記で指定したアクションでの処理中に、keys:
で指定したカラムのパラメータが許可され、データベースに保存されます。
今回は、ユーザーが新規登録(sign_up)とアカウント情報を更新(account_update)する際に、アバターがデータベースに保存されるようにします。
application_controller.rb
ファイルを開くと、すでに新規登録と更新でユーザーネームの設定が行われていることを確認できます。こちらにアバターも追加していきます。
1
2
3
4
5
6
7
8
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:username])
devise_parameter_sanitizer.permit(:account_update, keys: [:username])
end
end
以下のように:username
の後にカンマで区切り、 :avatar
を追加しましょう。
1
2
3
4
5
6
7
8
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:username, :avatar])
devise_parameter_sanitizer.permit(:account_update, keys: [:username, :avatar])
end
end
マイページ編集画面で「ファイルを選択」をクリックし、user_sample_avatar.png
をアップロードします。そして、「現在のパスワード」欄に111111
を入力し、「更新する」ボタンをクリックしてアバター画像を更新しましょう。
ユーザーを更新した後、以下のようにルートページにリダイレクトされ、成功のメッセージが表示されます。しかし、実際にはデータベースにアバター画像のファイル名は登録されていない状態です。
先ほどビューファイルにアバターの項目を追加し、ユーザー登録を行った際の確認で、usersテーブルのavatarカラムはNULL
となっていました。
今回のApplicationコントローラでのパラメータ許可設定を行った後、マイページ編集画面でuser_sample_avatar.png
をアップロードして更新したため、avatarカラムにはそのファイル名が保存されていることが期待されます。
しかし、phpMyAdminでusersテーブルのavatarカラムを確認すると、以下のように画像のファイル名ではなく、「ActionDispatch::Http::UploadedFile」というインスタンスのメタデータが文字列として保存されていることが確認できます。
ActionDispatch::Http::UploadedFile
のインスタンスはアップロードされたファイルの一時情報を保持していますが、これを直接データベースに保存すると、ファイル自体ではなくインスタンスのメタデータが文字列として保存されてしまいます。これではファイルを適切に管理やアクセスすることが困難になります。
次にCarrierWaveの機能を使用可能にして、アップロードされたファイルを効果的に管理できるように適切な設定を行う必要があります。
UserモデルにCarrierWaveのアップローダーをマウント
CarrierWaveを利用すると、画像ファイルのアップロード、保存、表示などを簡単に行うことができます。この機能を使用するためには、CarrierWaveのアップローダークラスを生成し、対象のモデルにアップローダークラスをマウントする(関連付ける)必要があります。
CarrierWaveのアップローダークラスには、アップロードされたファイルに対して行うさまざまな処理がデフォルトで定義されています。
CarrierWaveのアップローダークラスを生成するには、rails g uploader
コマンドを使用します。このコマンドを実行することにより、ファイルアップロード機能に必要な設定を含んだクラスファイルがアプリケーション内に作成されます。
今回は、画像ファイルのアップロードに特化した設定を行うアップローダークラスが必要なので、コマンドにImage
という名前を指定して実行しましょう。
1
rails g uploader Image
上記を実行すると、画像アップロードに関連する設定ファイルが生成されます。
app/uploaders/image_uploader.rb
ファイルを開きましょう。
生成されたImageUploader
クラス内には、以下のようにデフォルトで設定されている項目がいくつかあります。
設定項目の内容は、以下の通りです。
storage :file
- アップロードされたファイルの保存先を「アプリケーションのサーバー上のファイルシステム」に指定。store_dir
メソッド - アップロードされたファイルが保存されるディレクトリのパスを定義。デフォルトでは、ファイルはuploads
ディレクトリ下の、アップローダーがマウントされたモデルのクラス名とIDに基づいたパスに保存される。
これらの設定は、次にUserモデル内の特定のカラムにマウントする際にそのまま利用します。設定に基づいた実際の画像ファイルの保存先は、あとでアップロード操作を実行した際に確認します。
CarrierWaveで提供されているmount_uploader
メソッドを使用することで、アップローダークラスをモデル内の特定の属性(カラム)に関連付けることができます。これにより、そのカラムに画像やファイルアップロード機能が追加されます。
1
mount_uploader :カラム名, 関連付けたいアップローダークラス名
今回であれば、Userモデル内で以下を定義することで、avatar
属性にアップロードされたファイルがImageUploader
クラスで定義された方法にしたがって処理されるようになります。
app/models/user.rb
ファイルを開いて、以下の1行を追加しましょう。
1
mount_uploader :avatar, ImageUploader
これで設定されたディレクトリにユーザーのアバター画像が保存され、ファイル名がデータベースに正しく登録されたり更新されたりするようになるね!
次は、画像がどこに保存されるかと、データベースには本当にファイル名が保存されるかを確認していこう!
マイページ編集画面で「ファイルを選択」をクリックし、user_sample_avatar.png
をアップロードします。そして、「現在のパスワード」欄に111111
を入力し、「更新する」ボタンをクリックしてアバター画像を更新しましょう。
ユーザーを更新した後、以下のようにルートページにリダイレクトされ、成功のメッセージが表示されます。今回はUserモデルのavatar
属性に画像アップロード機能を使えるように設定してあるので、正常に保存されているはずです。
phpMyAdminを使用して、たけしさんのアバター画像のファイル名がavatar
カラムに正しく保存されているか確認しましょう。
以下のようにuser_sample_avatar.png
が格納されているはずです。
ImageUploader
クラスがUserモデルのavatar
属性にマウントされているため、avatar
にアップロードされた画像ファイルはImageUploader
クラスに定義されている設定に従って処理されます。
アップロードされた画像ファイルの具体的な保存先はImageUploader
クラス内のstore_dir
メソッド内で定義されています。
store_dir
メソッドのデフォルト設定は、アップローダークラスがマウントされたモデルの情報と属性に基づいて画像ファイルの保存先ディレクトリを定義します。
この設定により、アップロードされた画像はモデル名、マウントされた属性名、およびモデルのIDを含む構造化されたディレクトリパスに保存されるようになります。このパスの最上位の階層は、Railsアプリケーションのpublic
ディレクトリ内に位置します。
今回であれば、ユーザーがアバター画像をアップロードした場合、その画像ファイルはpublic
ディレクトリ内のuploads/user/avatar/{user_id}
ディレクトリ内に保存されます。ここで{user_id}
はそのユーザーのid
に置き換えられます。
たけしさんのid
は2
なので、以下のディレクトリ内に保存されています。
このようにアップロードされた画像ファイルは、アップローダークラスの設定に基づいたディレクトリ内に保存され、データベースのavatarカラムにはファイル名が保存されます。
次に、CarrierWaveを利用して保存された画像をビューに表示する方法について学びます。
CarrierWaveで画像をビューに表示する方法
ビューで画像を表示するためには、CarrierWaveが提供するurl
メソッドとヘルパーメソッドーのimage_tagを組み合わせて使用します。
まずurl
メソッドは、CarrierWaveによってアップローダークラスがモデルの属性にマウントされた際に使用できます。このメソッドは、アップロードされた画像ファイルへの具体的なURLパスを取得することが可能になります。
例えば、以下のようにuser
変数がたけしさんのUserインスタンスを保持している場合、user.avatar.url
を呼び出すことで、たけしさんのアバター画像ファイルの保存場所へのURLを取得できます。
CarrierWaveは、アップロードされたファイルを管理するために内部的にパスを生成し、それをurl
メソッドを通じて提供します。このURLは、Railsアプリケーション内のpublic
ディレクトリからの相対パスです。
上記の場合は、以下のようにimage_tag
メソッドの第一引数に指定することで、ビュー内で画像を表示することができます。
1
<%= image_tag user.avatar.url, alt: 'ユーザーアイコン' %>
それでは、このあと実際にアバター画像を表示できるように変更していきます。
現在、ユーザーが投稿した学習メモのヘッダーには、以下のようにデフォルトのアイコンが表示されています。
この表示はapp/views/memos/_memo.html.erb
ファイルでimage_tag
メソッドを使用してuser_icon.png
を表示させています。
今度は、ユーザーがアバターを設定している場合は、そのアバターを表示し、未設定の場合はデフォルトのアイコンを表示するように変更しましょう。
部分テンプレート_memo.html.erb
内でのアバター画像のパスの取得方法を理解するために少し振り返ります。まず、部分テンプレート内のmemo
にはindex.html.erb
から各学習メモのインスタンスが渡されます。
このmemo
オブジェクトを通じて、関連づけられたユーザーの情報を取得することができます。例えばmemo.user
を使うと、そのメモに紐付いたユーザーの情報を取得でき、さらにmemo.user.username
を使うと、該当ユーザーのユーザーネームを取得できます。
これは、Memoモデル内に定義されているbelongs_to :user
によって、メモが特定のユーザーに関連づけられているためです。
そのため、メモを投稿したユーザーのアバター画像にはmemo.user.avatar.url
を使用してアクセスすることができます。
1
<%= image_tag memo.user.avatar.url, alt: 'ユーザーのアイコン' %>
_memo.html.erb
ファイルを編集しましょうメモ投稿者がアバターを設定している場合、そのアバター画像を表示し、アバターが未設定の場合はデフォルトのアイコンを表示したい場合は、以下のように条件分岐を使います。
app/views/memos/_memo.html.erb
ファイルを開いて、ユーザーアイコンの箇所を変更しましょう。
1
2
3
4
5
<% if memo.user.avatar.present? %>
<%= image_tag memo.user.avatar.url, alt: 'ユーザーのアバター' %>
<% else %>
<%= image_tag 'user_icon', alt: 'ユーザーアイコン' %>
<% end %>
まず、学習メモを新規作成してみましょう。たけしさんでログインしている状態です。「新規作成」リンクをクリックして学習メモの投稿フォームにアクセスします。
以下の内容で学習メモを作成しましょう。
タイトル:プロトコルについて
本文:プロトコルは、インターネット上で情報をやり取りするときの「取り決め」や「ルール」を指す。URLの先頭にある「https」(または「http」)がこれに該当する。これはWebサイトとの情報のやり取りにhttpsというプロトコルを使用することを示す。
学習メモを投稿した後、たけしの学習メモが一覧に追加されて、アバター画像が表示されることが確認できます。
次に進む前に、たけしさんの学習メモをもう1つを作成しましょう。
以下の内容で学習メモを作成しましょう。
タイトル:ドメイン名について
本文:ドメイン名は、Webサイトの名前やアドレスのようなもので、インターネット上の特定の場所(サーバーの位置)を示す。
特定のファイル形式のみを許可する設定
アバター画像としてアップロードを許可するのは画像ファイルのみに限定する必要があります。現状では、任意のファイルタイプがアップロード可能となってしまい、例えばsample.txt
のような非画像ファイルもアップロードできてしまいます。
実際に動作を体験したい方は、こちらリンクからファイル(sample.txt)をダウンロードして試してみましょう。
アバターに非画像ファイルがアップロードされる例
非画像ファイルが登録されてしまうと、以下のように画面が崩れてしまいます。
この問題を解決するため、Userモデルのavatar属性にマウントされるImageUploader
クラスを編集し、特定の画像ファイル形式のみを許可するように設定します。
app/uploaders/image_uploader.rb
ファイルを開いて、以下の38行目からのextension_allowlist
メソッドのコメントを解除し、%w(jpg jpeg png)
に変更して、許可するファイル形式を制限しましょう。
この変更により、jpg
、jpeg
、png
の拡張子を持つファイルのみがアップロードできるようになり、その他のファイル形式は許可されません。
この段階では、エラーメッセージが英語で表示されるため、「Translation missing」というメッセージが表示されている部分があります。次に日本語化を行います。
エラーメッセージを日本語化するために翻訳ファイルを編集しましょう。config/locales/ja.yml
ファイルを開き、適切な場所にハイライト内容を追加してください。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
ja:
errors:
format: "%{attribute}%{message}"
messages:
extension_whitelist_error: "は許可されている拡張子(jpg, jpeg, png)のみアップロード可能です。"
activerecord:
attributes:
memo:
title: "タイトル"
content: "内容"
user:
email: "メールアドレス"
password: "パスワード"
password_confirmation: "パスワード確認"
username: "ユーザーネーム"
avatar: "アバター"
errors:
models:
memo:
attributes:
title:
blank: "を入力してください。"
too_long: "は30文字以内で入力してください。"
content:
blank: "を入力してください。"
user:
attributes:
email:
blank: "を入力してください。"
password:
blank: "を入力してください。"
taken: "は既に使用されています。"
blank: "が入力されていません。"
too_short: "は%{count}文字以上に設定して下さい。"
too_long: "は%{count}文字以下に設定して下さい。"
invalid: "は有効でありません。"
confirmation: "が内容とあっていません。"
password_confirmation:
blank: "を入力してください。"
taken: "は既に使用されています。"
blank: "が入力されていません。"
too_short: "は%{count}文字以上に設定して下さい。"
too_long: "は%{count}文字以下に設定して下さい。"
invalid: "は有効でありません。"
confirmation: "がパスワードと一致しません。"
current_password:
blank: "を入力してください。"
username:
blank: "を入力してください。"
これで、アバター画像としてjpg
、jpeg
、png
の拡張子を持つファイルのみがアップロード可能になり、その他のファイル形式は拒否されるように設定されました。また、不適切なファイル形式でアップロードを試みた場合には、日本語でエラーメッセージが表示されるようになります。
動作を確認した方は、たけしさんのアバター画像をuser_sample_avatar.png
に戻して次に進みましょう。
ユーザー詳細ページの実装
最後に、学習メモ一覧画面で、各投稿者のユーザーネームにリンクを追加し、そのユーザーが投稿した学習メモの一覧を表示するユーザー詳細ページに遷移するように実装します。
まず、ルーティングを設定しましょう。
ユーザーの詳細ページにアクセスするためのルーティングが必要です。config/routes.rb
ファイルを開き、ユーザー詳細ページ用のルーティングを追加します。そのためには、以下のようにresources :users, only: :show
の行をファイルに追加してください。
1
2
3
4
5
6
Rails.application.routes.draw do
root 'memos#index'
devise_for :users
resources :memos
resources :users, only: :show
end
次に、ユーザー詳細用のコントローラを設定します。まだusersコントローラが存在しないので、次のコマンドでusers
コントローラを生成してください。
1
rails g controller users
その後、app/controllers/users_controller.rb
ファイルを開いて、show
アクションを設定します。加えて、ユーザーの情報を取得するためのプライベートメソッドset_user
を定義し、before_actionでshowアクション実行前にこれを呼び出します。
1
2
3
4
5
6
7
8
9
10
11
12
class UsersController < ApplicationController
before_action :set_user, only: :show
def show
end
private
def set_user
@user = User.find(params[:id])
end
end
Userモデルにメモとの関連付けを定義しましょう。app/models/user.rb
ファイルを開き、Userモデルにメモとの1対多の関連付けを追加します。
以下のようにhas_many :memos
という行を追加することで、1つのユーザーが複数のメモを持つことができるようになります。これにより、例えば@user.memos
を使って、特定のユーザーに紐づくすべてのメモ情報を取得することが可能になります。
app/models/user.rb
ファイルにhas_many :memos
を追加しましょう。
app/views/users
ディレクトリ内にshow.html.erb
ファイルを作成しましょう。
次にshow.html.erb
ファイルを開き、以下のコードを追加して、ユーザーが持つ学習メモ一覧を表示しましょう。
1
2
3
<h2><%=@user.username %>さんの学習メモ一覧</h2>
<%= render @user.memos %>
render
メソッドに@user.memos
を渡すことで、_memo.html.erb
部分テンプレートを活用して、関連付けられたメモを一覧で表示できます。
app/views/memos/_memo.html.erb
ファイル内で、ユーザー名を表示している部分をlink_to
メソッドを使用してリンクに変更します。これにより、ユーザーネームをクリックすると、そのユーザーの詳細ページへ遷移できるようになります。
1
<span class="memo__username"><%= memo.user.username %></span>
上記を以下に変更しましょう。
1
<span class="memo__username"><%= link_to memo.user.username, user_path(memo.user) %></span>
最後に、学習メモ一覧画面で各ユーザーネームが表示される部分に設定したリンクを確認します。ユーザーネームのリンクをクリックすることで、そのユーザーが投稿した学習メモの一覧が表示される詳細ページに遷移するか確認しましょう。
「たけし」のリンクをクリックした後、たけしさんが投稿した学習メモの一覧が表示されれば、リンク設定は完了です。
以下の画面に遷移するはずです。
これでCarrierWaveを利用したファイルアップロード機能とユーザー詳細の実装は完了です!お疲れ様でした!
この記事で学んだことをTwitterに投稿して、アウトプットしよう!
Twitterの投稿画面に遷移します