ActiveRecord + SQLite3のBoolean値でハマった。
Boolean値としてtrueが入っている(ように見える)レコードがあるとき、
そのBoolean値を検索条件にするとヒットしないことがある。
Widget.find(1)
# => { id: 1, name: "hoge", valid: true }
Widget.where(valid: true).first
# => nil # あれ?
DBの中身をコマンドラインで直接見てみる。
sqlite> SELECT * FROM widgets;
id name valid
--- ----- ------
1 hoge 1
うーん、SQLite3ってBoolean値を直接サポートしてないからその辺か?
SQLite3側のドキュメントを見ると、以下のように書いてあるから良さそうだけど。
SQLite does not have a separate Boolean storage class. Instead,
Boolean values are stored as integers 0 (false) and 1 (true).
https://www.sqlite.org/datatype3.html
ActiveRecord側を調べてみる。
こんな記事があった。
http://stackoverflow.com/questions/23223821/sqlite-boolean-t-and-f-with-rails-active-record
http://stackoverflow.com/questions/6013121/rails-3-sqlite3-boolean-false
Boolean値は0/1ではなく'f'/'t'らしい。
コマンドラインで変更してみる。
sqlite> UPDATE widgets SET valid = 't';
sqlite> SELECT * FROM widgets;
id name valid
--- ----- ------
1 hoge t
これでいけるか?
Widget.where(valid: true).first
# => { id: 1, name: "hoge", valid: true }
できた。
※補足
データ生成・編集もすべてActiveRecordでやっていれば問題は発生しない(と思う)。
外部(例えばsqlite3のコマンドライン)で作ったデータを使うときには注意が必要なようだ。
追記
Rails 5.2から仕様が変わったとコメントで教えて頂きました。
詳しくはこちの記事を参照してください。
Rails 5.2 で SQLite3 の boolean の値が変わった