Railsのモデルのデフォルト値設定


データを管理する上で、デフォルト値の設定は避けては通れないものです。もちろん、Railsでもデフォルト値を設定することはできますが、少しややこしくなる場面もあります。

DBのデフォルト値

ActiveRecordによるモデルの場合、固定値のデフォルトで構わないのなら、マイグレーションに

migrate.rb
  # create record内で
  t.integer :hoge, default: 8
  # 列追加時
  add_column :table_name, :piyo, :string, default: 'foobar!'

と書いておけば、ActiveRecord側で自動的に読み取って、それを初期値としてくれます。また、null: falseを指定せず、NULLが入りうるテーブルでデフォルトを指定しなかった場合、DB上でDEFAULT NULLとみなされ、Ruby側ではnilとなります。

DBデフォルトに頼れない場合

当然ながら、DBと紐付かないActiveModelを使うような場合、DB頼みのデフォルト値は利用できません。また、単純な固定値ではなく、状況に応じて計算するもの1や、リレーションで設定するデフォルト値のようなものに対しては、DB単体で対応しきれない部分もあります。

モデルのコールバック

ActiveModelやActiveRecordでは、モデルが特定の動作をするときに、コールバックを設定することができます。after_initializeを使えば、初期化の済んだタイミングでコードを呼び出すことができます。

※注(追記):ActiveModel::Callbacksを使えば、ActiveRecordでないモデルにコールバックを仕込むことはできますが、さらにひと手間必要です。自分で立てるモデルなら、コールバックへ頼らずに、initializeから自力で呼び出すほうがスッキリしそうな気もします。

after_init.rb
  after_initialize :set_default, if: :new_record?

  private
  def set_default
    self.some_date ||= Time.zone.today
  end

なお、何も設定しない場合、after_initializeはDBから取り出したものにも走りますので、if: :new_record?は外せません。

gemで解決

とはいえ、初期値が必要になるたびにこれだけ書くのは手間です。さらに調べていたところ、default_value_forというgem(Github)があることがわかりました。これを使えば、

default_value_for.rb
  default_value_for :uuid do
    UuidGenerator.new.generate_uuid
  end

のような形で、デフォルト値を宣言的に書くことができます。


  1. PostgreSQLやSQliteでは式でデフォルト値を設定できるようですが、このような機能がActiveRecordと整合して使えるのかは、筆者はよくわかっていません(そして、筆者がよく使うMySQLではCURRENT_TIMESTAMP以外の設定はできませんので、逆に気にしなくて構わないという状況です)。