52
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

updated at

Organization

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以外の設定はできませんので、逆に気にしなくて構わないという状況です)。 

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
52
Help us understand the problem. What are the problem?