はじめに
Rails でDB に保存されたenum の値をattribute_before_type_cast
で取得する方法の備忘録です
環境
- macOS 10.15.6
- Ruby 2.5.7
- Rails 5.2.3
参考URL
https://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/BeforeTypeCast.html
https://railsdoc.com/page/options_for_select
https://310nae.com/rails-selectbox/
https://qiita.com/ozackiee/items/17b91e26fad58e147f2e
attribute_before_type_cast とは?
- enum の文字列ではなくDB に保存された実態値を取得できる
- enum 定義した際に利用できるインスタンスメソッド
enum の名前定義の値と実態値について
enum 使用時は値の見え方がRails 上とDB に保存される値が異なるので注意が必要です
Railsのenumはコードの表記上、文字列(またはシンボル)で透過的に扱えるように実装されています
SQLを直接叩いたり、DBのGUIクライアントを使って保存されたレコード情報を見るとわかりますが実際は文字列ではなく数値が保存されています。
enum で定義した実態値はRails 上では名前定義として振る舞います。
# 血液型(blood_type) をenum 定義する
enum blood_type: {
A型: 0,
B型: 1,
O型: 2,
AB型: 3,
# Rails 上での名前定義: DB に保存される実態値
}
動作確認
下記の想定で動作確認を行います
- Person モデルにname カラム, blood_type カラムを作成
- Person モデルでblood_type をenum で定義
# Parson モデルにname カラム, blood_type カラム を作成
create_table "persons", force: :cascade do |t|
t.string "name"
t.integer "blood_type"
end
# model にblood_type をenum で定義
class Person < ApplicationRecord
enum blood_type: {
A型: 0,
B型: 1,
O型: 2,
AB型: 3,
}
end
attribute_before_type_cast メソッド
rails console
を使ってattribite_before_type_cast
の動作確認
$ rails console
> person = Person.create(name: "Alice", blood_type: 0) # person オブジェクトを作成, DB 保存
:name => "Alice"
:blood_type => "A型"
> person.blood_type # rails 上の値を取得
"A型"
> person.blood_type # rails 上の値のクラスを取得
String < Object
> person.blood_type_before_type_cast # DB の値を取得
0
> person.blood_type_before_type_cast # DB の値のクラスを取得
Integer < Numeric
attributes, attributes_before_type_cast メソッド
-
attributes
: インスタンスに対して属性名 => 値
が取得できるインスタンスメソッド -
attributes_before_type_cast
: attributes のbefore 版。DB 上の実態値を属性名 => 値
の形で取得できるインスタンスメソッド
rails console
を使ってattributes
, attributes_before_type_cast
の動作の違いを確認します
$ rails console
> person = Person.create(name: "Alice", blood_type: 0) # person オブジェクトを作成, DB 保存
:name => "Alice"
:blood_type => "A型"
> person.attributes # rails 上の 名前定義を表示
"name" => "Alice"
"blood_type" => "A型"
> person.attributes_type_before_type_cast # DB の実態値を表示
"name" => "Alice"
"blood_type" => 0
使用例
私の場合はenum で定義した値から新規投稿フォームを生成するのに使用しました
# 出力したいHTML
<select>
<option value="0">A型</option>
<option value="1">B型</option>
<option value="2">O型</option>
<option value="3">AB型</option>
</select>
# blood_type をenum で定義
class Person < ApplicationRecord
enum blood_type: {
A型: 0,
B型: 1,
O型: 2,
AB型: 3,
}
end
# person オブジェクトを作成
def new
@person = Person.new
end
デフォルト値(selected: value) の設定にattirbute_before_type_cast
で取得した値を使用しています
# enum の値からフォーム生成
<%= form_with model: @person local: true do |f| %>
<%= f.select :blood_type, options_for_select(Person.blood_types, @person.expense_before_type_cast), {} %>
<%= f.submit %>
<% end %>
# フォーム生成時の各値
<%= form_with model: @モデル名 do |f| %>
<%= f.select :カラム名, options_for_select(モデル名.カラム名複数形, @モデル名.カラム名_before_type_cast), {} %>
<%= f.submit %>
<% end %>
select タグの空カッコ({}) には注意点があります
必ず通常のオプションを設定しない場合でも空のカッコ{},をオプション部分に設定してください。
この空のカッコがないと、HTMLオプションが無視されてしまうので、id/classを設定したのに効いていない!ということになってしまいます。
参考URL: https://310nae.com/rails-selectbox/
# options_for_select の文法
options_for_select(要素の配列 or ハッシュ, デフォルト値, {オプション}, {htmlオプション})
- オプション例
-
include_blank: true
(先頭行を空にする) -
selected: default_value
(デフォルト値)
-
- HTMLオプション例
-
id: id_name
(id を付与) -
class: class_name
(class を付与)
-
まとめ
- enum 使用時にDB に保存された実態値の取得には
attribute_before_type_cast
かattributes_before_type_cast
を使用する - enum の名前定義の値と実態値は取得方法が異なる
enum を使った事が無くポートフォリオ作成時に見事にハマってしまったので良い勉強になりました!
間違っている場合はコメント頂けると助かります!