ActiveRecord が DB の DEFAULT で設定した値を attribute の初期値として設定してくれるという挙動を知らなかったため、軽くハマってしまったので忘れないようにメモ。
Migration を使っていない案件で DB の DEFAULT 値を ALTER 文で変更したら急にテストが失敗するようになったが、コードは特に弄っていなかったのでなかなか原因に気付けなかった。
試してみる
例えば Model に以下のようなバリデーションを設定。
class Post < ActiveRecord::Base
has_many :comments
validates :title, presence: true
end
この状態で rails console で Model を new して valid? を呼ぶと以下のようにバリデーションはエラーになる。
% bundle exec rails c
Loading development environment (Rails 4.1.6)
irb(main):001:0> post = Post.new
=> #<Post id: nil, title: nil, body: nil, lock_version: 0, created_at: nil, updated_at: nil>
irb(main):002:0> post.valid?
=> false
irb(main):003:0> post.errors
=> #<ActiveModel::Errors:0x007f88438d3010 @base=#<Post id: nil, title: nil, body: nil, lock_version: 0, created_at: nil, updated_at: nil>, @messages={:title=>["can't be blank"]}>
ここまでは想定通り。
ここで DB 側に以下の ALTER を流してデフォルト値を設定。
-- MySQL
ALTER TABLE `posts` MODIFY COLUMN `title` varchar(255) NOT NULL DEFAULT 'No Title';
-- SQLite
DROP TABLE "posts";
CREATE TABLE "posts" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "title" varchar(255) DEFAULT "No Title", "body" text, "lock_version" integer, "created_at" datetime, "updated_at" datetime);
rails console で relaod! してから再度試すと Model を new した時点で DB の DEFAULT に設定した値が入っている事が分かる。
当然 valid? の結果も今度は true になる。
irb(main):004:0> reload!
Reloading...
=> true
irb(main):005:0> post = Post.new
=> #<Post id: nil, title: "No Title", body: nil, lock_version: 0, created_at: nil, updated_at: nil>
irb(main):006:0> post.valid?
=> true
ハマった時はこの逆で、デフォルト値を数値から NULL に変えたためバリデーションエラーが発生してしまった。
恥ずかしながら今までこの挙動を知らなかったが、知っていれば便利な機能だと思う。
Migration 使ってる環境なら Model 側意識せずに DB のデフォルト値だけ変える事もそう無さそうだし。
設定している所
余りちゃんと読んでいないけど適当に grep してみると lib/active_record/model_schema.rb
の 254 行目辺りで設定してるっぽい。
# Returns a hash where the keys are column names and the values are
# default values when instantiating the AR object for this table.
def column_defaults
@column_defaults ||= Hash[columns.map { |c| [c.name, c.default] }]
end
参考
ruby on rails - How can I set default values in ActiveRecord? - Stack Overflow
http://stackoverflow.com/questions/328525/how-can-i-set-default-values-in-activerecord/328874#328874
how do set default value for ActiveRecord fields? - Ruby Forum
https://www.ruby-forum.com/topic/75429