すでにメンバーの場合は

無料会員登録

GitHubアカウントで登録 Pikawakaが許可なくTwitterやFacebookに投稿することはありません。

登録がまだの方はこちらから

Pikawakaにログイン

GitHubアカウントでログイン Pikawakaが許可なくTwitterやFacebookに投稿することはありません。

Rails

【Rails】active_hashを使って疑似モデルを作ろう

ぴっかちゃん
ぴっかちゃん

active_hashは疑似モデルをつくることができるRailsのgemです。わざわざテーブルを作る必要がなくなるので内容が変わらない情報を扱うときに便利です。

モデルファイル | active_hashの定義
1
2
3
4
5
class モデルクラス名 < ActiveHash::Base
  self.data = [
      {カラム名: , カラム名: }, {カラム名: , カラム名: },
  ]
end

active_hashを導入しよう

この章では、active_hashの導入方法について解説します。

gemをインストールしよう

まずはgemをインストールしましょう。
Gemfileに下記のようにactive_hashを追加します。

Gemfile | gemの追加
1
gem 'active_hash'

その後、ターミナルでbundle installを実行してください。
これでactive_hashが使えるようになりました。

モデルファイルを作ろう

次にactive_hashのgemを使ってモデルファイルを作成して行きます。
active_hashを使ってのモデル作成は下記の順番で説明して行きます。

  1. モデルファイル作成の際の注意点
  2. 手動でモデルファイルを作成する方法
  3. touchコマンドを使ってモデルファイルを作成する方法

1. モデルファイル作成の際の注意点

モデルを作成する際の注意すべき点としてはactive_hashで使用するモデルファイルは通常のrails g model モデル名コマンドで作成しないということです。
なぜならactive_hashを使う場合は下記のスニペットのようにApplicationRecordを継承するのではなく、ActiveHash::Baseを継承する必要があるからです。

モデルファイル | active_hashの定義
1
2
3
4
5
class モデルクラス名 < ActiveHash::Base
  self.data = [
      {カラム名: , カラム名: }, {カラム名: , カラム名: },
  ]
end

ApplicationRecordを継承しないので、rails g model モデル名コマンドで作成しません。
詳しくは、下記の「active_hashを使ったモデルデータ定義方法」で詳しく説明します。

2. 手動でモデルファイルを作成する方法

それでは実際にモデルファイルを作成していきましょう。
ここでは手動でモデルファイルを作成する方法でファイルを作成してみます。
appフォルダにあるmodelsフォルダに作成したいモデル名でファイルを作成してください。

今回は例として都道府県を扱うモデルであるPrefectureモデルのファイルを作成してみます。
都道府県は47あり、名前も数も変化するものではありません。
このような場合、わざわざデータベースにprefecturesテーブルを作成するまでもありませんね。
このように、ActiveRecordのメソッドが使えるモデルとしての機能は使いたいがテーブルを用意するまでもないときにactive_hashを使うと非常に便利です。

ファイル名はモデル名.rbです。
今回はprefecture.rbとなります。

モデルファイル作成

3. touchコマンドを使ってモデルファイルを作成する方法

ここではtouchコマンドを使ってモデルファイルを作成する方法を紹介します。
この場合はターミナルで下記のコマンドを実行します。

ターミナル | touchコマンドでファイルを作成
1
$ touch app/models/prefecture.rb

どちらかの方法でモデルファイルが作成できれば準備は完了です。

実際に記述をしていこう

この章では、作成したモデルファイルに記述する方法について解説します。
先ほど作成したprefecture.rbを例にして記述していきます。

active_hashを使ったモデルデータ定義方法

それでは先ほど作成したモデルファイルを編集していきましょう。
最初は何も書かれていないのでまずはモデルクラスを定義します。

prefecture.rb | クラスの定義
1
2
3
class Prefecture < ActiveHash::Base

end

ポイントはActiveHash::Baseクラスを継承していることです。
active_hashのgemをインストールするとActiveHash::Baseクラスを使えるようになります。
ActiveHash::BaseクラスにはActiveRecord風のメソッドが定義されているので、実際のモデルクラスとほぼ同じことができるようになります。

次にこのモデルで使うテーブルに当たる記述をしていきます。

prefecture.rb | クラスの定義
1
2
3
4
5
class Prefecture < ActiveHash::Base

  self.data = [ ]

end

self.dataでテーブルを作成するイメージです。
[ ]の中にハッシュ形式でカラムと値を定義します。

モデルファイル | active_hashの定義
1
2
3
4
5
class モデルクラス名 < ActiveHash::Base
  self.data = [
      {カラム名: , カラム名: }, {カラム名: , カラム名: }
  ]
end

上の例ではカラムは2つですが、もちろん何個でも追加することができます。

今回は都道府県を扱うモデルとして作成したので、実際に都道府県の情報を入れていきます。

prefecture.rb | 都道府県のデータの追加
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Prefecture < ActiveHash::Base

  self.data = [
      {id: 1, name: '北海道'}, {id: 2, name: '青森県'}, {id: 3, name: '岩手県'},
      {id: 4, name: '宮城県'}, {id: 5, name: '秋田県'}, {id: 6, name: '山形県'},
      {id: 7, name: '福島県'}, {id: 8, name: '茨城県'}, {id: 9, name: '栃木県'},
      {id: 10, name: '群馬県'}, {id: 11, name: '埼玉県'}, {id: 12, name: '千葉県'},
      {id: 13, name: '東京都'}, {id: 14, name: '神奈川県'}, {id: 15, name: '新潟県'},
      {id: 16, name: '富山県'}, {id: 17, name: '石川県'}, {id: 18, name: '福井県'},
      {id: 19, name: '山梨県'}, {id: 20, name: '長野県'}, {id: 21, name: '岐阜県'},
      {id: 22, name: '静岡県'}, {id: 23, name: '愛知県'}, {id: 24, name: '三重県'},
      {id: 25, name: '滋賀県'}, {id: 26, name: '京都府'}, {id: 27, name: '大阪府'},
      {id: 28, name: '兵庫県'}, {id: 29, name: '奈良県'}, {id: 30, name: '和歌山県'},
      {id: 31, name: '鳥取県'}, {id: 32, name: '島根県'}, {id: 33, name: '岡山県'},
      {id: 34, name: '広島県'}, {id: 35, name: '山口県'}, {id: 36, name: '徳島県'},
      {id: 37, name: '香川県'}, {id: 38, name: '愛媛県'}, {id: 39, name: '高知県'},
      {id: 40, name: '福岡県'}, {id: 41, name: '佐賀県'}, {id: 42, name: '長崎県'},
      {id: 43, name: '熊本県'}, {id: 44, name: '大分県'}, {id: 45, name: '宮崎県'},
      {id: 46, name: '鹿児島県'}, {id: 47, name: '沖縄県'}
  ]

end

これで擬似的にデータベースにprefecturesテーブルが作成され、idカラムとnameカラムに値が入った状態と同じ状態を作り出すことができました。

試しにコンソールモードでactive_hashのallメソッドを使ってみましょう。

ターミナル | コンソールで確認
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[1] pry(main)> Prefecture.all
=> #<ActiveHash::Relation:0x00007ff88ee50220
 @all_records=
  [#<Prefecture:0x00007ff88ca0d8b8 @attributes={:id=>1, :name=>"北海道"}>,
   #<Prefecture:0x00007ff88ca0d570 @attributes={:id=>2, :name=>"青森県"}>,
   #<Prefecture:0x00007ff88ca0d1d8 @attributes={:id=>3, :name=>"岩手県"}>,
   #<Prefecture:0x00007ff88ca0ccb0 @attributes={:id=>4, :name=>"宮城県"}>,
   #<Prefecture:0x00007ff88ca0c850 @attributes={:id=>5, :name=>"秋田県"}>,
   #<Prefecture:0x00007ff88ca0c490 @attributes={:id=>6, :name=>"山形県"}>,
   #<Prefecture:0x00007ff88ca0c1e8 @attributes={:id=>7, :name=>"福島県"}>,
   #<Prefecture:0x00007ff88b727cf8 @attributes={:id=>8, :name=>"茨城県"}>,
   #<Prefecture:0x00007ff88b727848 @attributes={:id=>9, :name=>"栃木県"}>,
   #<Prefecture:0x00007ff88b7274d8 @attributes={:id=>10, :name=>"群馬県"}>,
   #<Prefecture:0x00007ff88b7271e0 @attributes={:id=>11, :name=>"埼玉県"}>,
# 以下省略

このようにデータベースに登録してある時と同じ挙動になるのが確認できました。

active_hashのメソッド一覧

上の例のようにActiveHash::Baseクラスを継承することにより、このクラスで定義されているメソッドを使うことができました。
ActiveRecordで定義されているメソッドと同じ名前ですが、厳密には全く同じではないので注意しましょう。
ここではactive_hashで定義されている主なメソッドを紹介していきます。

allメソッド

全てのレコードを取得するメソッドです。
先ほどの例のようにテーブルに登録したデータ全てを取得することができます。

ActiveRecordのallメソッドはallメソッドを徹底解説!を参照してください。

countメソッド

レコードの件数を取得するメソッドです。

コンソール | countメソッド
1
2
[1] pry(main)> Prefecture.count
=> 47

ActiveRecordのcountメソッドは世界で一番分りやすく詳しいcountメソッドの使い方を参照してください。

firstメソッド

最初のレコードを取得するメソッドです。

コンソール | firstメソッド
1
2
[1] pry(main)> Prefecture.first
=> #<Prefecture:0x00007ff88e843918 @attributes={:id=>1, :name=>"北海道"}>

lastメソッド

最後のレコードを取得するメソッドです。

コンソール | lastメソッド
1
2
[1] pry(main)> Prefecture.last
=> #<Prefecture:0x00007ff88e8a3e08 @attributes={:id=>47, :name=>"沖縄県"}>

findメソッド

引数に指定したidのレコードを取得するメソッドです。

コンソール | findメソッド
1
2
[1] pry(main)> Prefecture.find(2)
=> #<Prefecture:0x00007ff88e842b30 @attributes={:id=>2, :name=>"青森県"}>

この時、引数に配列でidを渡すと複数のレコードを取得することができます。

コンソール | 複数渡した場合
1
2
3
4
[1] pry(main)> Prefecture.find([3, 5, 34])
=> [#<Prefecture:0x00007ff88e841e38 @attributes={:id=>3, :name=>"岩手県"}>,
 #<Prefecture:0x00007ff88e84bd98 @attributes={:id=>5, :name=>"秋田県"}>,
 #<Prefecture:0x00007ff88e88b808 @attributes={:id=>34, :name=>"広島県"}>]

ActiveRecordのfindメソッドはfindメソッドを使って指定したレコードを取得しよう!を参照してください。

find_by_idメソッド

引数に指定したidのレコードを1件だけ取得するメソッドです。

コンソール | find_by_idメソッド
1
2
[1] pry(main)> Prefecture.find_by_id(2)
=> #<Prefecture:0x00007ff88e842b30 @attributes={:id=>2, :name=>"青森県"}>

findメソッドとの違いは引数に指定したidのレコードが存在しなかったときの挙動です。

  • findメソッド - レコードがない場合は、例外が発生する(エラーが表示される)
  • find_by_idメソッド - レコードがない場合は、nilが返る
コンソール | 2つのメソッドの違い
1
2
3
4
5
[1] pry(main)> Prefecture.find(89)
ActiveHash::RecordNotFound: Couldn't find Prefecture with ID=89
from /Users/hoge/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/active_hash-3.1.0/lib/active_hash/relation.rb:55:in `find'
[2] pry(main)> Prefecture.find_by_id(89)
=> nil

ActiveRecordのfind_by_idメソッドはfind_by_idメソッドの使い方と他のfind系メソッドとの違いを参照してください。

find_byメソッド

検索でヒットしたレコードの初めの一件だけを返すメソッドです。

find_byメソッドの基本構文
1
モデル名.find_by(条件)

例えば東京都のレコードを取得したい場合は下記のように記述します。

コンソール | find_byメソッド
1
2
[1] pry(main)> Prefecture.find_by(name: "東京都")
=> #<Prefecture:0x00007ff88b4b9d18 @attributes={:id=>13, :name=>"東京都"}>

このようにカラム名: 検索したい値とするとその最初の1件を取得することができます。
存在しない場合はnilが返ります。

コンソール | find_byメソッド
1
2
[1] pry(main)> Prefecture.find_by(name: "東京")
=> nil

ActiveRecordのfind_byメソッドは世界で一番詳しいfind_byメソッドのいろいろな使い方を参照してください。

whereメソッド

テーブル内の条件に一致したレコードを配列の形で取得することができるメソッドです。

whereメソッドの基本構文
1
モデル名.where("条件")

下記のように条件を指定すると条件に当てはまるレコード全てを配列の形で取得することができます。

コンソール | whereメソッド
1
2
3
4
5
6
7
8
9
10
11
12
13
[1] pry(main)> Prefecture.where(id: 10)
=> #<ActiveHash::Relation:0x00007ff88b2ddda0
 @all_records=
  [#<Prefecture:0x00007ff89014b3e8 @attributes={:id=>1, :name=>"北海道"}>,
   #<Prefecture:0x00007ff89014ae48 @attributes={:id=>2, :name=>"青森県"}>,
   #<Prefecture:0x00007ff89014a5d8 @attributes={:id=>3, :name=>"岩手県"}>,
   #<Prefecture:0x00007ff890149ef8 @attributes={:id=>4, :name=>"宮城県"}>,
   #<Prefecture:0x00007ff890149340 @attributes={:id=>5, :name=>"秋田県"}>,
   #<Prefecture:0x00007ff890148f30 @attributes={:id=>6, :name=>"山形県"}>,
   #<Prefecture:0x00007ff890148918 @attributes={:id=>7, :name=>"福島県"}>,
   #<Prefecture:0x00007ff890153cf0 @attributes={:id=>8, :name=>"茨城県"}>,
   #<Prefecture:0x00007ff890153598 @attributes={:id=>9, :name=>"栃木県"}>,
# 以下省略

上の例だとidが10のレコードを取得します。
コンソールでの出力結果だとバグのせいか全てのレコードが取得されているように見えますが実際にはしっかりとレコードを取得できています。

コンソール | whereメソッド
1
2
[1] pry(main)> Prefecture.where(id: 10).first
=> #<Prefecture:0x00007ff890152f80 @attributes={:id=>10, :name=>"群馬県"}>

ActiveRecordで定義されているwhereメソッドとは若干異なるようで、下記のような指定だとエラーになるようです。

コンソール | whereメソッド
1
2
3
[1] pry(main)> Prefecture.where("id > ?", 5)
ArgumentError: wrong number of arguments (given 2, expected 0..1)
from /Users/hoge/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/active_hash-3.1.0/lib/active_hash/relation.rb:18:in `where'

orderメソッド

データベースから取得してきた値を並び替えることができるメソッドです。

orderメソッドの基本構文
1
モデル名.order(:並び替えたいカラム名)

カラム名だけ指定すると昇順になります。

コンソール | orderメソッド
1
2
3
4
5
6
7
8
9
10
11
[1] pry(main)> Prefecture.order(:id)
=> #<ActiveHash::Relation:0x00007ff88ef39830
 @all_records=
  [#<Prefecture:0x00007ff88b5be358 @attributes={:id=>1, :name=>"北海道"}>,
   #<Prefecture:0x00007ff88b5be010 @attributes={:id=>2, :name=>"青森県"}>,
   #<Prefecture:0x00007ff88b5bdd18 @attributes={:id=>3, :name=>"岩手県"}>,
   #<Prefecture:0x00007ff88b5bd9f8 @attributes={:id=>4, :name=>"宮城県"}>,
   #<Prefecture:0x00007ff88b5bd728 @attributes={:id=>5, :name=>"秋田県"}>,
   #<Prefecture:0x00007ff88b5bd458 @attributes={:id=>6, :name=>"山形県"}>,
   #<Prefecture:0x00007ff88b5bcdf0 @attributes={:id=>7, :name=>"福島県"}>,
# 以下省略

降順に並び替えたい場合は下記のように記述します。

コンソール | orderメソッド
1
2
3
4
5
6
7
8
9
10
11
[1] pry(main)> Prefecture.order(id: :desc)
=> #<ActiveHash::Relation:0x00007ff89002a720
 @all_records=
  [#<Prefecture:0x00007ff88ec6dca0 @attributes={:id=>47, :name=>"沖縄県"}>,
   #<Prefecture:0x00007ff88ec6e128 @attributes={:id=>46, :name=>"鹿児島県"}>,
   #<Prefecture:0x00007ff88ec6e920 @attributes={:id=>45, :name=>"宮崎県"}>,
   #<Prefecture:0x00007ff88ec6f190 @attributes={:id=>44, :name=>"大分県"}>,
   #<Prefecture:0x00007ff88ec6f6e0 @attributes={:id=>43, :name=>"熊本県"}>,
   #<Prefecture:0x00007ff88ec6ff28 @attributes={:id=>42, :name=>"長崎県"}>,
   #<Prefecture:0x00007ff88ec60d98 @attributes={:id=>41, :name=>"佐賀県"}>,
# 以下省略

ActiveRecordのorderメソッドはorderメソッドを使って取得したデータを並び替えよう!を参照してください。

インスタンスを生成しよう

active_hashで作成したモデルも通常のモデルクラスと同じようにインスタンスを作成できます。

コンソール | インスタンスの生成
1
2
[1] pry(main)> city = Prefecture.find_by(name: "東京都")
=> #<Prefecture:0x00007ff88b5c65f8 @attributes={:id=>13, :name=>"東京都"}>

インスタンスメソッドを使おう

生成したインスタンスはカラム名をインスタンスメソッドとして使うことができます。

コンソール | インスタンスの生成
1
2
3
4
[1] pry(main)> city = Prefecture.find_by(name: "東京都")
=> #<Prefecture:0x00007ff88b5c65f8 @attributes={:id=>13, :name=>"東京都"}>
[2] pry(main)> city.name
=> "東京都"

他にもactive_hashにはインスタンスメソッドが定義されています。
あまり使うことはないかもしれませんが気になる方は公式サイトでチェックしておきましょう。

アソシエーションを定義しよう

active_hashで作成したモデルにアソシエーションを定義する時は通常の記述と異なります。
今回はUserモデルとアソシエーションを組むときの例を見てみましょう。
このときの2つのテーブルの関係性はユーザーは1つの都道府県に属していて、都道府県はたくさんのユーザーを持っているので1対多の関係となります。

Prefectureモデル側には下記のように記述します。

prefecture.rb | アソシエーションの追加
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Prefecture < ActiveHash::Base

  self.data = [
      {id: 1, name: '北海道'}, {id: 2, name: '青森県'}, {id: 3, name: '岩手県'},
      {id: 4, name: '宮城県'}, {id: 5, name: '秋田県'}, {id: 6, name: '山形県'},
      {id: 7, name: '福島県'}, {id: 8, name: '茨城県'}, {id: 9, name: '栃木県'},
      {id: 10, name: '群馬県'}, {id: 11, name: '埼玉県'}, {id: 12, name: '千葉県'},
      {id: 13, name: '東京都'}, {id: 14, name: '神奈川県'}, {id: 15, name: '新潟県'},
      {id: 16, name: '富山県'}, {id: 17, name: '石川県'}, {id: 18, name: '福井県'},
      {id: 19, name: '山梨県'}, {id: 20, name: '長野県'}, {id: 21, name: '岐阜県'},
      {id: 22, name: '静岡県'}, {id: 23, name: '愛知県'}, {id: 24, name: '三重県'},
      {id: 25, name: '滋賀県'}, {id: 26, name: '京都府'}, {id: 27, name: '大阪府'},
      {id: 28, name: '兵庫県'}, {id: 29, name: '奈良県'}, {id: 30, name: '和歌山県'},
      {id: 31, name: '鳥取県'}, {id: 32, name: '島根県'}, {id: 33, name: '岡山県'},
      {id: 34, name: '広島県'}, {id: 35, name: '山口県'}, {id: 36, name: '徳島県'},
      {id: 37, name: '香川県'}, {id: 38, name: '愛媛県'}, {id: 39, name: '高知県'},
      {id: 40, name: '福岡県'}, {id: 41, name: '佐賀県'}, {id: 42, name: '長崎県'},
      {id: 43, name: '熊本県'}, {id: 44, name: '大分県'}, {id: 45, name: '宮崎県'},
      {id: 46, name: '鹿児島県'}, {id: 47, name: '沖縄県'}
  ]

  include ActiveHash::Associations
  has_many :users

end

ActiveHash::Associationsモジュールをincludeすることによって、モジュールに定義してあるhas_manyメソッドをActivRecordのアソシエーションのメソッドの様に使う事が出来ます。
includeについて詳しくはincludeの基本的な使い方の記事を参照ください。

Userモデル側には下記のように記述します。

user.rb | アソシエーションの追加
1
2
3
4
5
6
class User < ApplicationRecord

  extend ActiveHash::Associations::ActiveRecordExtensions
  belongs_to_active_hash :prefecture

end

通常であればbelongs_toになりますが、active_hashを使って作成したモデルに対してアソシエーションを組む場合はbelongs_to_active_hashを使います。

ActiveHash::Associations::ActiveRecordExtensionsモジュールをUserクラスにextendする事でこのモジュールに定義してあるbelongs_to_active_hashメソッドをUserのクラスメソッドとして使用する事が出来ています。
extendについては、モジュールの記事を参考にしてみてください。

このようにいつものアソシエーションとは書き方が異なるので注意しましょう。

アソシエーションを定義することにより、下記のように簡単にテーブルに紐付く情報を取得することができます。

コンソール | アソシエーションを使ってデータを取得
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[1] pry(main)> prefecture = Prefecture.find_by(name: "北海道")
=> <Prefecture:0x00007ff89081aa20 @attributes={:id=>1, :name=>"北海道"}>
[2] pry(main)> prefecture.users
   (1.4ms)  SET NAMES utf8,  @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'),  @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
  Item Load (0.6ms)  SELECT `users`.* FROM `users` WHERE `users`.`prefecture_id` = 1
=> [#<User:0x00007ff88eb29588
  id: 1,
  name: "ぴっか",
  prefecture_id: 1,
  created_at: Sat, 06 Jun 2020 16:25:06 JST +09:00,
  updated_at: Sat, 06 Jun 2020 16:25:06 JST +09:00>]
# 北海道に属しているユーザーを取得、複数人いればその分のレコードを取得

[3] pry(main)> user = User.first
  User Load (0.5ms)  SELECT `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
=> [#<User:0x00007ff88eb29588
  id: 1,
  name: "ぴっか",
  prefecture_id: 1,
  created_at: Sat, 06 Jun 2020 16:25:06 JST +09:00,
  updated_at: Sat, 06 Jun 2020 16:25:06 JST +09:00>]
[33] pry(main)> user.prefecture
=> <Prefecture:0x00007ff89081aa20 @attributes={:id=>1, :name=>"北海道"}>
# ユーザーが属している都道府県のレコードを取得

このようにアソシエーションを定義すると簡単にレコードを取得することができます。

都道府県を選択するセレクトボックスを作ろう

今回は都道府県を管理するためactive_hashを使い、Prefectureモデルを作成しました。
都道府県を管理するテーブルは主にユーザーがどの都道府県に所属しているかや、どこに配送するかなどといったように投稿フォームでセレクトボックスを使い入力することになると思います。

この章では実際の投稿フォームの作り方を解説していきます。

collection_selectでセレクトボックスを作ろう

投稿フォームはform_withform_forといったフォームを作成するヘルパーメソッドを使って作っていきます。

セレクトボックスを作るにはselectメソッドcollection_selectメソッドを使って作成します。
データベースに保存されているデータを使う場合はcollection_selectメソッドの方が便利なので、今回はこちらを使ってフォームを作成していきます。

collection_selectメソッドは下記のように記述します。

ビューファイル | collection_selectメソッドの定義
1
<%= form.collection_select(保存されるカラム名, オブジェクトの配列, カラムに保存される項目, 選択肢に表示されるカラム名, {オプション}, {htmlオプション} ) %>

今回だと下記のような記述になります。

ビューファイル | collection_selectメソッドの定義
1
<%= form.collection_select(:prefecture_id, Prefecture.all, :id, :name ) %>

このままだと最初に表示されたとき、一番最初の北海道が表示されます。
もしユーザーがここを飛ばして登録をすると北海道として登録されてしまいます。

北海道が表示

ですので第5引数にオプションとして下記のように記述を追加します。

ビューファイル | collection_selectメソッドの定義
1
<%= form.collection_select(:prefecture_id, Prefecture.all, :id, :name,{include_blank: "---"} ) %>

include_blankを使うと一番最初に値のない選択肢を追加することができます。
コンパイルされると下記のようなコードになります。

html | include_blankの結果
1
2
3
4
5
6
7
8
9
10
<select name="user[prefecture_id]" >
  <option value="">---</option>
  <option value="1">北海道</option>
  <option value="2">青森県</option>
  <option value="3">岩手県</option>
  <option value="4">宮城県</option>
  <option value="5">秋田県</option>
  <option value="6">山形県</option>
  〜省略〜
</select>

下記のように表示されます。

---が表示

これを設定することで、選択されていないときは値が渡らないため入力必須のときなどはバリデーションで登録できないようにすることができます。

このように記述するとこのようなセレクトボックスを作ることができます。

完成したセレクトボックス

フォームにスタイルを付けたいときは第六引数にclassやidなどのhtml属性をつけてあげましょう。

ビューファイル | collection_selectメソッドの定義
1
<%= form.collection_select(:prefecture_id, Prefecture.all, :id, :name,{include_blank: "---"}, {class: "hoge", id: "fuga"} ) %>

より詳しい書き方はform_withの記事のcollection_selectを参照してください。

データベースに保存しよう

それではセレクトタグで選択した都道府県をusersテーブルに保存する流れをみていきましょう。

コントローラーのcreateアクションでは下記のように記述します。
※今回はユーザー名と都道府県だけを保存する場合を想定しています

users_controller | コントローラー内の記述
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  def create
    @user = User.new(user_params)
    if @user.save
      redirect_to root_path, notice: "登録に成功しました"
    else
      redirect_to new_user_path, alert: @user.errors.full_messages
    end
  end

  private 

  def user_params
    params.require(:user).permit(:name, :prefecture_id)
  end

フォームで送信したprefecture_idはparamsの中に入っているので、ストロングパラメーターを作成し、それをcreateアクションで保存すればOKです。

ターミナル | paramsの中身
1
2
[1] pry(#<Users::Controller>)> params
=> <ActionController::Parameters {"authenticity_token"=>"〜略〜", "user"=><ActionController::Parameters {"name"`=>"pikawaka", "prefecture_id"=>"13"} permitted: false>, "commit"=>"送信", "controller"=>"users", "action"=>"create"} permitted: false>

上のコードはあくまで一例です。
リダイレクト先などはアプリにあったものに変えてみてください。

データを追加したり内容が変わらないものであれば、わざわざテーブルを作らずにactive_hashで管理すると非常に便利です。
何かテーブルを作るときはactive_hashで作ったほうがよいかどうか考えてから作成するようにしましょう!

この記事のまとめ

  • active_hashは疑似モデルを作成できるgemです
  • わざわざテーブルを作成する必要がなくなるので便利です
  • テーブル内の内容が変わらないときなどはactive_hashを使いましょう

1

わかった!