ポリモーフィック関連/Active Storage/ActiveStorage::Blob/ActiveStorage::Attachment/has_one_attached
ポリモーフィック関連
ポリモーフィック関連とは、ある1つのカラムが複数のテーブルを参照しているようなパターンの関連を表したもので、
例)Q&Aアプリなどのcommentsテーブルをイメージするとよい
同じようなカラムを持っているテーブルが複数ある場合、
例)teacher_id, student_id 等をイメージ
それらを1つのテーブルで管理してしまうことができる。複数のクラスのインターフェースを統一し、同じように扱えるようにする。 一つのモデルを同じインターフェースを持ったものが扱う関連の仕方。 ダックタイピングというコード概念からの考えで、インタフェースを統一することで冗長なコードがなくなり、スケーラビリティを担保できる。
(例)ポリモーフィック関連を使わなかった場合
class Commment < ApplicationRecord belongs_to :teacher belongs_to :student # メッセージを送るモデルが増えたら、それに従いbelongs_toの数も増えていく end class Teacher < ApplicationRecord has_many :comments end class Student < ApplicationRecord has_many :comments end
(実行例) teacher = Teacher.find(params[:teacher_id]) teacher.comments => #select * from comments where teacher_id = ? comment = Comment.find(params[:comment_id]) comment.teacher => #select * from teachers where teacher_id = ?
(例)ポリモーフィック関連を使用した場合
class Comment < ApplicationRecord belongs_to :commentable, polymorphic: true # メッセージを送るモデルが増えてもbelongs_toの数は増えない end class Teacher < ApplicationRecord has_many :comments, as: :commentable end class Student < ApplicationRecord has_many :comments, as: :commentable end
(実行例) # commentableを通すことで、TeacherとStudentのどちらであるかを意識する必要がない。 Comment.find(params[:id]).commentable # コメント送信者モデル側からは通常のhas_many関連として扱える Teacher.find(params[:id]).comments Student.find(params[:id]).comments
commentableなモデルを追加する時にも上記のように追加モデルを書くだけでよく、既存コードをいじる必要はありません。
ダックタイピングとかポリモーフィック関連ってなんのこっちゃわからなかったけど久しぶり読んでみたら、すごくコードがすっきりできた。
Active Storageで画像を添付する
Taskモデルに画像を添付したかったので、Active Storageを使用することにした。
$ bundle exec rails active_storage:install $ bundle exec rails db:migrate
コマンドを実行しインストールすると、
Active Storageで使用する
- active_storage_blobs テーブル
- active_storage_attachments テーブル
二つのテーブルを生成するマイグレーションファイルができます。
# This migration comes from active_storage (originally 20170806125915) class CreateActiveStorageTables < ActiveRecord::Migration[5.2] def change create_table :active_storage_blobs do |t| t.string :key, null: false t.string :filename, null: false t.string :content_type t.text :metadata t.bigint :byte_size, null: false t.string :checksum, null: false t.datetime :created_at, null: false t.index [ :key ], unique: true end create_table :active_storage_attachments do |t| t.string :name, null: false t.references :record, null: false, polymorphic: true, index: false t.references :blob, null: false t.datetime :created_at, null: false t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true t.foreign_key :active_storage_blobs, column: :blob_id end end end
ActiveStorage::Blob
ActiveStorage::Blobモデルは添付されたファイルに対応していて、active_storage_blobsテーブルに添付画像のデータが管理されていると考えてください。
またファイルの実体をデータベース外で管理することを前提にしていて、識別key
、ファイル名
、コンテンツタイプ
、メタデータ
、サイズ
などを管理している。
ActiveStorage::Attachment
ActiveStorage::Attachmentモデルは 「①アプリ内の画像添付するモデル(ここでいうと、Taskモデル)」と「②ActiveStorage::Blobモデル」との中間テーブルの役割を果たしているテーブルのモデルです。
①とはFKカラム名をFK値として保持しており、ポリモーフィック関連となっていて、一方②は直接的なidのみで紐付けされている。
勝手なイメージ
それぞれのモデルとActiveStorage::Attachmentモデルで外部キーの関連で画像の関連付けをして、画像のデータなどの細かい情報に関してはActiveStorage::Blobモデルを別に作成して、そっちで管理するようにしているんだろうなぁ。と思っています。
本来、ActiveStorage::Attachmentモデル
とActiveStorage::Blobモデル
を別々にしなくてもいいけど、一緒にするとテーブル構造がカラム が多くなって見づらいから、別々にしたよ。みたいな感じで考えて落ち着きました。
Taskモデルに画像添付できるようにする
# Taskモデルにファイルを添付するためにimageを関連付けする class Task < ApplicationRecord has_one_attached :image end # 添付の有無を確認する task.image.attached? # => true