はじめに
こんにちは、torihaziです。
最近使った rails7の active_storage と呼ばれる機能。
初めて使ったときは右に左にぶつかりながらなんとかという状態だったので
PRの結果帰ってくるまでまっすぐ整備して一本道にできるようにします。
じゃltg
目次
ActiveStorageとは
私の理解は次のとおりです。
これを使えば、モデルにおいて画像を紐づけられるようになる。
です。乱暴かもしれませんが、そう理解しています。
詳しくはマニュアルに丸投げします。
使ってみる
環境構築
Docker環境
環境についてはお任せします。
私は以前作ったDocker環境で行います。ちなみにmac + Docker 前提です。
Dockerとはナンジャという方は下記記事を見てくらはいふぇ。笑
Docker を初めてみる (Docker Desktopを入れる) - Torihaji's Growth Diary
以下作り方です。
cd ~/Document git clone https://github.com/torihazi/rails-docker.git tmp cd tmp docker compose up -d --build docker compose run web rails db:create docker compose run web rails generate scaffold User firstname:string lastname:string email:string docker compose run web rails db:migrate
ここまで行ってhttps://localhost:3000/usersにアクセスすると次の通りになると思います。

ActiveStorage 導入
コンテナ内で下記コマンドを実行するとdb/schema.rbが下記のように変わります。
rails active_storage:install rails db:migrate
db/schema.rb
create_table "active_storage_attachments", force: :cascade do |t|
t.string "name", null: false
t.string "record_type", null: false
t.bigint "record_id", null: false
t.bigint "blob_id", null: false
t.datetime "created_at", null: false
t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
end
create_table "active_storage_blobs", force: :cascade do |t|
t.string "key", null: false
t.string "filename", null: false
t.string "content_type"
t.text "metadata"
t.string "service_name", null: false
t.bigint "byte_size", null: false
t.string "checksum"
t.datetime "created_at", null: false
t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
end
create_table "active_storage_variant_records", force: :cascade do |t|
t.bigint "blob_id", null: false
t.string "variation_digest", null: false
t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
end
create_table "tasks", force: :cascade do |t|
t.string "title"
t.string "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "users", force: :cascade do |t|
t.string "firstname"
t.string "lastname"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
end
3つのテーブルが作られます。
- active_storage_blobs
- active_storage_attachements
- active_storage_variant_records
これらテーブルがあることによって先ほど作った Userモデルに画像を紐づけられるようになります。
例えばプロフィール画像です。
もし仮にUserモデルとこれら3テーブルを紐づけるとするならば関係性は次のようになります。

- Usersテーブルの1レコードは 1つ以上のattachementsと結びつき
- blobsテーブルの1レコードは 1つ以上の attachementsとvariant_recordsと結びつく
になります。
attachementsテーブルは中間テーブルというものに相当します。
多対多 を解消する目的で使われるテーブルです。
画像の紐付け準備(ファイル編集)
試しに画像を1枚アップロードしてみます。
そのために幾つかファイルを編集します。
1枚の画像をUserモデルに紐づけられるようにapp/models/user.rbに下記を追記します。
class User < ApplicationRecord has_one_attached :image (<= ここの名前は :avatarでもなんでも) end
複数画像を紐づけるならばhas_many_attached :imagesとかです。
やりたい人はそっちで。今回はoneです。
次にコントローラとビューを編集します。
app/controllers/users_controller.rb
~~~
def create
@user = User.new(user_params)
if @user.save
redirect_to users_url
else
render 'new'
end
end
~~~
(ファイル末尾, :image追加)
def user_params
params.require(:user).permit(:firstname, :lastname, :email, :image)
end
app/views/users/index.html.erb(まるっきり変えます)
<p style="color: green"><%= notice %></p>
<h1>Users</h1>
<div id="users">
<table class="design01">
<tr>
<th>firstname</th>
<th>lastname</th>
<th>email</th>
<th>Image</th>
</tr>
<% @users.each do |user| %>
<tr>
<td><%= user.firstname %></td>
<td><%= user.lastname %></td>
<td><%= user.email %></td>
<% if user.image.attached? %>
<td><%= image_tag user.image %></td>
<% end %>
</tr>
<% end %>
</table>
</div>
<%= link_to "New user", new_user_path %>
app/views/users/_form.html.erb
form_withの中に下記を追記 <div> <%= form.label :image, style: "display: block" %> <%= form.file_field :image %> </div>
app/assets/stylesheets/application.css
.design01 {
width: 100%;
text-align: center;
border-collapse: collapse;
border-spacing: 0;
}
.design01 th {
padding: 10px;
background: #e9faf9;
border: solid 1px #778ca3;
}
.design01 td {
padding: 10px;
border: solid 1px #778ca3;
}
.design01 img {
width: 100px;
height: auto;
}
ここまでやってlocalhostにアクセスすると次のようになると思います。

New Userをクリックしたときは以下

ここまで来たらあとはアップロードするだけです。
ファイルアップロード
コンソール立ち上げて rails cから追加してもいいですが今回は サイトの方から行います。
適当な値を入力してアップロードします。

成功すると Indexページに戻り、次の画面が表示されると思います。

これでUserモデルの1レコードに紐づく画像を登録することができました。
余談(Fakerも使ってアップロード)
例えば開発環境やテスト環境でとりあえず数十個一気にぶち込みたいなんていう時があると思います。
そんなときは Fakerというgemを入れて次のようにやってみてください。
1. fakerを入れて各種設定をする。
rails7 seed 日本語 訳して使ってみた - Torihaji's Growth Diary
2. サンプル画像をapp/assets/images配下に配置

3.db/seeds.rbを編集
db/seeds.rb
unless Rails.env.production?
50.times do
user = User.new(firstname: Faker::Name.first_name,
lastname: Faker::Name.last_name,
email: Faker::Internet.email(domain: "gmail.com")
)
user.image.attach(
io: File.open(Rails.root.join('app/assets/images/450x300.jpg')),
filename: '450x300.jpg'
)
user.save
end
end
4.コマンド実行
コンテナに入って rails db:seed実行してください。
(気になる人は先ほど追加した1件を削除してから上のコマンド打ってください。私は消してます。)
するとIndexページに50個追加されていると思います。全部は長いので無理です。
気になる人は view ファイルに <%= user.id %>も追加してみてください。

id追加したものがこちらです。50個ありますよね。

ちなみにこれを描画する際に 1 + n問題 というものが発生しています。
今回はサンプルなので無視してます。
気になる人はそれも解決してみてください。
終わりに
いかがだったでしょうか。
active_storageに加えて seedで一気に50個どばっと入れられるようになりました。
まだまだマサラタウンかもしれませんが、これから頑張ります。
初心者なりに頑張ります。
皆さんも頑張ってください。