Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Rails: Active Record属性のデフォルト値はコールバックよりもdefaultオプションで設定しよう(翻訳)

概要

原著者の許諾を得て翻訳・公開いたします。

日本語タイトルは内容に即したものにしました。
以下のAPIドキュメントもどうぞ。

Rails: ActiveRecord標準のattributes APIドキュメント(翻訳)

Rails: Active Record属性のデフォルト値はコールバックよりもdefaultオプションで設定しよう(翻訳)

Active Recordモデルのインスタンスにデフォルト値を設定する必要が生じたことのある人は、おそらくコールバックでデフォルト値を設定したことがあるでしょう。

実はRails 5.0からもっとよい方法があるのですが、最近まで気づいていませんでした(Mosesに感謝します!)

参考: §2.3 Active Record属性API -- Ruby on Rails 5.0 リリースノート - Railsガイド

🔗 以下のように書くよりも

デフォルト値をコールバックで設定する。

class Message
  before_validation :assign_delivered_at

  # ...

  private

  def assign_delivered_at
    delivered_at ||= Time.zone.now
  end
end

🔗 以下のように書こう

Active RecordのAttributes APIを使う。

class Message
  attribute :delivered_at, default: -> { Time.zone.now }

  # ...
end

🔗 そうする理由

コールバックを使う正当な理由があったとしても、コールバックは理解しにくくなる可能性があります。一般にコールバックを減らす方が、後になって驚かされることも少なくなります。

attribute構文は簡潔かつ明快であり、Railsではこの方法で振る舞いを実行することが推奨されています。

Attributes APIには、他にも「属性の変更トラッキング」「型キャスト」「データベースを用いない属性の追加」といった豊富な機能が揃っていますが、ここではデフォルト値の設定機能だけを使っています。

🔗 そうしない理由があるとすれば

データ整合性を理想的なものにするには、データベーススキーマでデフォルト値を設定する方がより望ましくなります。

デフォルト値をデータベースレベルで設定すれば、Active Recordはその値を保存前の新しいモデルに取り込むので、defaultオプションによる方法はおそらく不要です。

データベースレベルでデフォルト値を設定したうえで、さらに上述のdefaultオプションによる方法でActive Recordモデルにデフォルト値を設定すると、Model.newを呼び出したときにデータベースが設定したデフォルト値が上書きされる点に注意が必要です。Railsでデフォルト値を何らかの理由で意図的に変更するのでない限り、データベースレベルのデフォルト値に加えてdefaultオプションによるデフォルト値も指定する必要はないでしょう。

関連記事

Rails: ActiveRecord標準のattributes APIドキュメント(翻訳)

Rails: データベースクエリにRangeを渡してコードを明確にしよう(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。