Help us understand the problem. What is going on with this article?

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

More than 5 years have passed since last update.

データを管理する上で、デフォルト値の設定は避けては通れないものです。もちろん、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以外の設定はできませんので、逆に気にしなくて構わないという状況です)。 

jkr_2255
qiitadon
Qiitadon(β)から生まれた Qiita ユーザー・コミュニティです。
https://qiitadon.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away