【Rails8.0】スプレッドシート等で作成したデータをデータベースに入れる方法

# はじめに
2024年4月から、プログラミングスクールRUNTEQでプログラミングを学んでいるしまたまと申します！
Railsでとあるアーティストの掲示板を作成するにあたり、初期データをseeds.rbファイルで用意しようとしたところ、非常に長くなってしまいました。
```seeds.rb
# 1つ目のイベント
Event.create!(
    title: 'BLT LIVE BOOSTER vol.1',
    place: '恵比寿LIQUIDROOM',
    place_prefecture: 'Tokyo'
    date: '2021-06-03',
    category: 'event'
)

# 2つ目のイベント
Event.create!(
    title: 'ROCK IN JAPAN 2024',
    place: '蘇我スポーツ公園',
    place_prefecture: 'Chiba'
    date: '2024-08-01',
    category: 'fes'
)

# 3つ目のイベント
Event.create!(
    title: 'JAPAN JAM 2024',
    place: '蘇我スポーツ公園',
    place_prefecture: 'Chiba'
    date: '2024-05-01',
    category: 'fes'
)

...
 
 # これを全ライブ分（自分の場合130個）＋全楽曲分も記述しないといけない！？
 ```

長〜いですね。ヒューマンエラー待ったなしです。
そこで調べてみたところ、Excel等の表形式で作成したデータをデータベースに入れる方法が見つかりました。
今回のRUNTEQアドベントカレンダーのテーマが「プログラミングでの"ワクワク"」ということで、「こんなにめんどくさい記述を表にできるんですかっ！？」とワクワクしたので、記事にして投稿します！

# 対象読者
* Railsで大量の初期データを表形式で作成・利用したい方
* 開発環境と本番環境で同じデータを使用したい方

# 環境
* MacBookPro M2
* Rails8.0
* Ruby3.2.3
* Docker
* （デプロイ先）Fly.io

# 前提
* 各テーブルの作成と、リレーション（`belongs_to`や`has_many`等）の記述、使用するenumの設定は済んでいるものとします。
* 今回は「seeds.rbとは？」に関する具体的な説明は省きます。簡単に言うと、初期データを作成するためのファイルです。詳細が気になる方は、下記の記事をご覧ください。
参考ページ：[Railsガイド](https://railsguides.jp/active_record_migrations.html#%E3%83%9E%E3%82%A4%E3%82%B0%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%A8seed%E3%83%87%E3%83%BC%E3%82%BF)
参考ページ：[railsのseedの書き方いろいろ](https://qiita.com/takehanKosuke/items/79a66751fe95010ea5ee)

# 作業の流れ
1. 表形式でのデータ作成
1. csv形式でRailsアプリに保存
1. seeds.rbにデータ作成のためのコードを記述
1. 開発環境のデータベースにデータを作成
1. 本番環境のデータベースにデータを作成

## 1. 表形式でのデータ作成
ExcelやNumbersなど、普段お使いの表作成ソフトをご使用ください。
**なお、必ず「csvで書き出し／エクスポート」ができることをご確認ください。**
今回は[Googleスプレッドシート](https://docs.google.com/spreadsheets/)を使用します。
### ✏️ データ作成
今回は、Eventsテーブルのデータを作成します。下記のように記述しました。![スクリーンショット 2024-12-07 12.18.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806280/989344f5-f4cb-9e26-75b5-beb6ed9cc979.png)

1行目には、テーブルのカラム名を記述しています。
2行目以降に、作成したいデータの内容を記述しています。
* enumを設定しており、データ型をintegerにしているカラムも、enumの**キー**を記述してください。
（例）`enum :category, { tour: 1, one_man: 2, ... }`としている場合、表には`tour`や`one_man`等を入力する（`1`や`2`ではない）。

作成ができたら、名前をつけて保存してください。ファイルの名前は、テーブル名にすることをおすすめします。

## 2. csv形式でRailsアプリに保存
### ✏️ Railsアプリにcsvファイル保存用のディレクトリを作る
Railsアプリのルートディレクトリで、下記コマンドを実行してください。
```
mkdir db/csv
```

### ✏️ 作成した表をcsv形式に変換してアプリに保存する
db/csv内に、先ほど作成した表を、「csv形式で保存」してください。Googleスプレッドシートでは、
``` 
ファイル > ダウンロード > カンマ区切り形式（.csv）
```
で保存できます。

![スクリーンショット 2024-12-10 11.21.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806280/33ee57ec-697f-a29b-cf23-f00bb5c25017.png)

#### （csvファイルにする理由）
csv形式は、表↔︎表形式のデータもRailsアプリ内で取り扱えるようになるなど、互換性が高いため。
参考ページ：[CSVファイルとExcel（エクセル）ファイルの違いとは？](https://data.wingarc.com/csv-excel-2-19747#CSV)

## 3. seeds.rbにデータ作成のためのコードを記述
### ✏️ 記述内容
以下を参考に、データ作成を行うコードを記述してください。

```db/seeds.rb
require "csv"

CSV.foreach('db/csv/events.csv', headers: true) do |row|
  Event.find_or_create_by!(name: row['name'], date: row['date']) do |event|
    event.category = row['category']
    event.name = row['name']
    event.date = row['date']
    event.place = row['place']
    event.place_prefecture = row['place_prefecture']
  end
end
```

参考ページ：[【Rails】rake seedコマンドでCSVファイルからDBに読み込ませる方法](https://qiita.com/mmmasuke/items/545afaf5876d3dc52670)
参考ページ：[Rails｜csvファイルを利用したテストデータの挿入](https://zenn.dev/airiin/articles/f77c371bc93d7e)

### ✏️ コードの解説
```
require 'csv'

CSV.foreach('db/csv/events.csv', headers: true) do |row|
```
csvファイルの各行（1行目を除く）に対して、処理を行うことを宣言しています。
`foreach`は、csvファイルを読み込むためのメソッドです。
* 第一引数（`'db/csv/events.csv'`）で、処理を行いたいcsvファイルがどのパスにあるのかを示しています。
* 第二引数（`headers: true`）で、csvファイルの1行目をヘッダーとして扱うことを指定しています。

参考ページ：[Rubyリファレンスマニュアル singleton method CSV.foreach](https://docs.ruby-lang.org/ja/latest/method/CSV/s/foreach.html)

```
  Event.find_or_create_by!(name: row['name'], date: row['date']) do |event|
```
その行のデータがすでに存在するかを検索し、データを作成するかを判定しています。
`find_or_create_by!`メソッドは、データがすでにある場合はデータ作成をスキップするメソッドです。
* 使用するメリット
  1. データ登録後にcsvファイルに新たにデータを追加してデータ作成コマンドを実行しても、追加分だけがデータベースに登録される。
  2. `create!`メソッドでは生じてしまうデータの重複を防ぐことができる。
* 今回は、引数に`name: row['name'], date: row['date']`を指定しています。`Event.name`（イベント名）と`Event.date`（日時）が一致するデータがデータベースにすでにある場合は、データ作成がスキップされます。

参考ページ：[Railsガイド](https://railsguides.jp/active_record_querying.html#%E6%96%B0%E3%81%97%E3%81%84%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E6%A4%9C%E7%B4%A2%E3%81%BE%E3%81%9F%E3%81%AF%E3%83%93%E3%83%AB%E3%83%89%E3%81%99%E3%82%8B)

```
    event.category = row['category']
    event.name = row['name']
    ...
```
データベースにデータが存在しない場合は、この行に従ってデータの作成が行われます。
> `event.category`の値はcsvの`category`の列から取ってきて、`event.name`の値はcsvの`name`の列から…

といった形でデータが作成され、保存されます。

## 4. 開発環境のデータベースにデータを作成
seeds.rbを記述しただけでは、データベースにデータは保存されません。コンテナ内で、下記コマンドを実行してください。
```
bin/rails db:seed
```
このコマンドにより、seeds.rbの記述に沿ってデータがデータベースに保存されます。

## 5. 本番環境のデータベースにデータを作成
### ⚠️ 注意
今回はFly.ioでデプロイを行なったため、そのときに行なったデータ作成方法を説明します。その他のデプロイ先でのデータ作成については、各デプロイ先の公式ドキュメント等をご確認ください。
### ✏️ Fly.ioでのデータ作成方法
本番環境でもseeds.rbに記述した内容をデータベースに保存する際には、同様に`bin/rails db:seed`コマンドを実行する必要があります。
Fly.ioでは、開発環境と同じように、ご自身のPCのターミナルからコマンドを実行することができます。デプロイ後、下記コマンドを実行してコンソール画面に入ってください。
```
flyctl console
```
その後、下記コマンドを実行してください。
```
bin/rails db:seed
```
これで、本番環境のデータベースにcsvに記述したデータが保存されます！