データを管理する上で、デフォルト値の設定は避けては通れないものです。もちろん、Railsでもデフォルト値を設定することはできますが、少しややこしくなる場面もあります。
DBのデフォルト値
ActiveRecordによるモデルの場合、固定値のデフォルトで構わないのなら、マイグレーションに
# 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_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 :uuid do
UuidGenerator.new.generate_uuid
end
のような形で、デフォルト値を宣言的に書くことができます。
-
PostgreSQLやSQliteでは式でデフォルト値を設定できるようですが、このような機能がActiveRecordと整合して使えるのかは、筆者はよくわかっていません(そして、筆者がよく使うMySQLでは
CURRENT_TIMESTAMP
以外の設定はできませんので、逆に気にしなくて構わないという状況です)。 ↩