Rails
MySQL

カラム型TINYINTの場合にActiveRecord::Enumを使いたい

More than 1 year has passed since last update.

ハマったのでメモ。

まあRailsで1から作っている場合は遭遇しないかもしれませんが・・・。


ActiveRecord::Enumとは

割愛。

http://ruby-rails.hatenadiary.com/entry/20150710/1436461745


問題

class User < ApplicationRecord

enum state: { pending: 0, active: 1 }
end

user = User.new(state: :pending)
# => #<User:0x007fbdbf882a78 id: nil, state: 'pending'>
user.save!
# => true
user
# => #<User:0x007fbdbf882a78 id: 1, state: nil>

例外も発生せず、データベースを直接覗いてもデータはきちんと入っているのに、save!するとstatenilになった!?!?


原因

stateカラムがTINYINTだった。ActiveRecordは、カラムがTINYINT(1)の場合、自動的にbooleanとみなしてキャストするらしい。今回の場合(enumの場合)、enumのvalueにしていたのが数値だったために「(キャストした値)true/falseに該当するlabelがない」=> nilとなっていた様子。


解決策

class User < ApplicationRecord

attribute :state, ActiveRecord::Type::Integer.new
enum state: { pending: 0, active: 1 }
end

これでActiveRecordがstateカラムを整数値としてキャストしてくれるようになる。

参考(https://blog.kazu69.net/2015/10/22/active-record-tinyint/)

(ちなみにリンク先で「TINYINT(1)はbooleanと同じなので0 か 1 しかとらない」とありますが、TINYINT(1)は場合-128~127 の値をとれます。booleanも同様)