Kintarou'sBlog

プログラミング学習中。学習内容のアウトプットや読書で学んだことなど随時投稿!

【Rails】Active Storageを利用して画像保存機能を実装する

こんにちは😊Kintarouです。

現在エンジニア転職を目指してTECH CAMPにてプログラミング学習中です👨‍🎓
夢はフリーランスエンジニアになって働く人が働きやすいシステムを作ること!
と、愛する妻と海外移住すること🗽

プログラミングや読んでいる本のことなど、ブログに書いていきます!
twitter : https://twitter.com/ryosuke_angry


今回参考にさせて頂いたサイト様🙇‍♂️

Rails5でDBに自動生成されるテーブル、"ar_internal_metadata" とは何なのか - Qiita

【初心者向け】Railsのポリモーフィック関連付けを理解しよう - Qiita


Active Storage導入前の下準備

画像のサイズや色などを加工してからアップロードするために、ImageMagick(画像変換ツール)とimage_processing(画像サイズ調整用Gem)をインストールします。

brewでインストールする場合

#ターミナル
brew install imagemagick
#Gemfileの最下部

gem 'mini_magick'
gem 'image_processing', '~> 1.2'
#ターミナル
bundle install

ここでサーバーを再起動しておきましょう。

Active Storageをインストールする

rails active_storage:install

rails db:migrate

以上の操作で、DB内に
- active_storage_attachments
- active_storage_blobs
- ar_internal_metadata
の3つのテーブルが作成されます。
以下、それぞれのテーブルの役割を簡単にまとめておきます。

active_storage_attachments

別に作成されているactive_storage_blobsと、画像を紐づけるモデルとの中間テーブルに当たるモデルです。
なんでモデルを指定してないのに中間テーブルができるの?という疑問に関しては後述するポリモーフィック関連というのが関連しているようです。

active_storage_blobs

添付されたファイルに対応するモデルです。key、ファイル名、content_type、メタデータ、サイズなどを管理しています。

ar_internal_metadata

Rails 5から追加されているDBの不用意な削除を防止するための機能に必要なテーブルです。production時にdrop:allやpurge:allなどすると例外を発生してくれるようです。

画像を紐づけるモデルとのアソシエーションを記述する

こちらは例として、messagesというテーブルに紐づけると仮定します。

class Message < ApplicationRecord
  has_one_attached :image  #Active Storageとのアソシエーション
end

このhas_one_attachedにより、各レコードと画像ファイルを1対1の関係で紐付けます。
※もし複数画像ファイルと紐付けたい場合はhas_many_attached :imagesとなります。

この記述だけでActive Storageのモデルと紐付けられたの?と思うかもしれませんが、active_storage_attachmentsのポリモーフィック関連がそれを可能にしているようです。

ポリモーフィック関連

ポリモーフィックとは、多様な(多様性)という意味だそうです。
複数の異なる型に対して共通のインターフェースを提供することのようですね。
よく見るとマイグレーションファイル内にpolymorphic: trueという記載があります。

こんな簡単に紐付けられるなら全部ポリモーフィック関連にすればいいじゃん!と思いそうですが、こちらDBにとっては一つのアンチパターンになるようです。
※1つのカラムで複数テーブルのidを参照しており、外部キーが設定できない。など
不用意には使わない方がいいということですね。

コントローラーで画像の保存を許可しておく

※仮にmessagesのコントローラーに記載した場合

#省略

  private

  def message_params
      params.require(:message).permit(:image)
  end

※複数画像を保存する場合はpermit(images : )として配列形式に格納させるようにします。
※また、複数画像を保存する場合にfile_field :imageを:imagesに、name属性も'message[images]
'に変えましょう。(モデル名は実際に紐づけるモデル名です)

画像を表示する場合

今回の場合、messageに紐付けられているので以下表記で表示できます。

<%= image_tag message.image.variant(resize:'500*500') %>

※variant以降の記述は、元画像データを指定されたサイズに変換して表示するためのものです。元画像データのサイズそのままで良い場合は記載不要です。
※複数画像を表示する場合、上記までの記述からimages内に配列として格納されているはずなのでeachメソッドなどで表示するようにしてみて下さい。

以上、どなたかの参考になれば幸いです😊