Rails でフォーム送信時のプルダウンメニューの項目をCSV インポートしてDB のデータから表示したい

# はじめに

Rails でフォーム送信時のプルダウンメニューの項目をCSV インポートしてDB のデータを使って表示する為の備忘録です。

# 環境

* macOS 10.15.6
* Ruby 2.5.7
* Rails 5.2.3
* rspec-rails 4.0.1
* capybara 3.32.2

# 参考URL

CSV インポート関連
<https://qiita.com/Ryuta1346/items/c21cb70b9879c66c8639>
<https://qiita.com/SoarTec-lab/items/50e046ea2a2764c12c21>
<https://docs.ruby-lang.org/ja/latest/class/CSV.html>
<https://qiita.com/3yatsu/items/416411c0a8f696dbf99e>
<https://qiita.com/rllllho/items/672e336a03335cba6b34>

プルダウンメニュー関連
<https://qiita.com/HrsUed/items/56677d6c266d8a53ffa7>
<https://qiita.com/kawakami_shotaro/items/11a677bf34136cb7686d>
<https://qiita.com/colorrabbit/items/b58888506e41d1370fd1>
<https://crieit.net/posts/Rails-collection-select>
<https://qiita.com/_akira19/items/c218186983f444c2d794>

国コード一覧CSV
<https://qiita.com/tao_s/items/3yy2b90a2751bfbdd585ea>

# 目標

1. プルダウンメニューの選択肢をCSV インポートする
2. プルダウンメニューの選択項目をDB のデータを使い表示する

見た目のイメージです↓
![form1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377207/d2f1ea33-c018-e1a7-54ef-ce27b703e211.png)

プルダウンメニューの選択肢はDB に保存しているカラムの情報を使って表示します↓
![form2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377207/0a13cffd-a26d-6300-1e19-e9cf6ba164f9.png)


# 実装

CSV インポートとフォームのプルダウン化を分けて解説します。

## 1. プルダウンメニューの選択肢をCSV インポートする

1. 国名一覧保存用にCountry モデル作成
2. db/seeds.rb にインポート処理を追加
3. `rails` コマンドでインポート処理を実行

Ruby のCSV ライブラリを使用してインポート処理を行います。
今回は以下のようなCSV から国名一覧をインポートしました。
作成したCSV ファイルはルートディレクトリに追加しました。

```CSV:country.csv
番号,国・地域名,ISO 3166-1に於ける英語名,数,三字,二字,場所,各行政区分
1,アイスランド,Iceland,352,ISL,IS,北ヨーロッパ,ISO 3166-2:IS
2,アイルランド,Ireland,372,IRL,IE,西ヨーロッパ,ISO 3166-2:IE
```

CSV は[国コード一覧CSV](https://qiita.com/tao_s/items/32b90a2751bfbdd585ea) を使わせて頂きました。

### 1. Country モデル作成

下記のようなモデルを作成しました。
region カラムは今回は使っていません。

| attribute    | type   |
| ------------ | ------ |
| country_name | string |
| region       | string |

```Shell
# Country モデル作成
$ rails g model Country country_name:string region:string
```

### 2. インポート処理を追加

インポート処理はseeds.rb に追記しました。
別途ファイルを作成し[runner コマンドで実行する方法](https://qiita.com/3yatsu/items/416411c0a8f696dbf99e)もあるようです。

```Ruby:seeds.rb
require "csv"

# CSV ファイルへのパスは絶対パスで指定
CSV.foreach("country.csv", headers: true) do |row|
  Country.create!(
    country_name: row["国・地域名"],
    region: row["場所"]
  )
end
```

### 3. インポート処理を実行

`rails` コマンドでCSV インポートを実行します。
seed ファイルに他の処理も書いていたので今回はDB 内を全削除してインポートし直しました。

```Shell
# CSV をインポートしてDB に保存する場合はこれだけ
$ rails db:seed

# DB を消去して全て入れ直す
$ rails db:migrate:reset
$ rails db:seed

# rails コンソールでインポートされたか確認
$ rails c
# インポートした国名の件数を確認
> Country.count
249
```

## 2. プルダウンメニューの選択項目をDB のデータを使い表示する

新規投稿時のフォーム送信のView ファイルを修正します。

```Ruby:view/model_names/new.html.erb
# 修正前
<%= form_with model: @model_name do |f| %>
  <%= f.label :country %>
  <%= f.text_field :country %>

  <%= f.submit "送信" %>
<% end %>
```

```Ruby:view/model_names/new.html.erb
# 修正後
<%= form_with model: @model_name do |f| %>
  <%= f.label :country %>
  <%= f.collection_select :country, Country.all, :id, :country_name, prompt: "国を選択してください" %>

  <%= f.submit "送信" %>
<% end %>
```

```Ruby
# collection_select の文法
<%= f.collection_select <保存先のカラム名>, <表示用の配列データ>, <保存する値のカラム名>,  <表示用のカラム名>, <オプション> %>
```

* 保存先のカラム名: country
* 表示用の配列データ: Country.all
* 保存する値のカラム名: id
* 表示用のカラム名: country_name
* オプション: prompt オプションで`国を選択してください`を表示

`collection_select` の使い方については[こちら](https://qiita.com/colorrabbit/items/b58888506e41d1370fd1)解説が非常に分かりやすいです。

# 学び

* 検索などその後の処理も考慮してインポートを行う
* 選択肢が縦に長くなってしまうので、階層を持たせるなどして使いやすさを考慮した使用に変更したい

# 追記

RSpec でのプルダウンの表示テスト時にtest 環境でもseeds.rb の初期データが必要になったので環境別にseeds.rb を分割しました。
test 環境でのプルダウンの表示がされていない事に気付かずハマってしまったので[備忘録](https://qiita.com/karlley/items/c7183167fc6cc24f5fa4)を追加しました。