🐰

ActiveRecord でテーブルに定義されていない仮想的な属性を定義する

2024/06/05に公開

ActiveRecord を継承したあるモデルに一時的なパラメータを持たせたかったので調べたことを備忘録として残します

結論

  • 型やデフォルト値を持たせたいなら attribute をつかう
  • そうでないなら attr_accessor をつかう

やりたいこと

あるモデル、Post モデルとします。ID を指定すると指定のモデルを引っ張ってくる action を実装していたとします。
ユーザーが like した投稿 ID は PostLike テーブルに保存されています。

Post

ID post_name content created_at updated_at
1 post_1 Hello 2024/06/05 12:05 2024/06/05 12:05
2 post_2 arigato 2024/06/05 12:12 2024/06/05 12:12
3 post_3 Thanks 2024/06/05 13:45 2024/06/05 13:45

PostLike

ID post_id user_id liked_at created_at updated_at
1 1 1 2024/06/05 13:30 2024/06/05 13:30 2024/06/05 13:30
2 1 2 2024/06/05 14:00 2024/06/05 14:00 2024/06/05 14:00
3 2 1 2024/06/06 15:24 2024/06/06 15:24 2024/06/06 15:24

このとき、Post の ID と呼び出し元のユーザーを指定したら、そのユーザーが ID を like したかどうかの情報も返したいです。
liked_at 属性をとるために二つのテーブルを毎回 JOIN するのは WHERE user_id = ? で取得するよりコストが高い気がするのと、serializer にメソッドを作るのもちょっとめんどうでできるのならば Post をそのまま返したいなと考えていました。
とはいえそのために使いもしない liked_at フィールドを Post テーブル上に追加するのはナンセンスだと感じたので、ActiveRecord の一部として使えてなおかつテーブル上に追加されない仮想的な属性を作ることができないか調べました。

attribute を設定する

ActiveRecord を継承したモデルに attribute で属性を設定することができます。

class Post < ActiveRecord::Base
  has_many :rpost_likes, dependent: :destroy

  attribute :liked_at, :datetime, default: -> { Time.now }
end 

ActiveRecord の他のフィールドのように、型や初期値を設定することができるほか、自分でカスタマイズした型を設定することもできます。
ActiveRecord の他のフィールドと同じようにふるまうので、ActiveRecord の機能を活用したい場合によさそうです。

https://api.rubyonrails.org/classes/ActiveRecord/Attributes/ClassMethods.html

attr_accessor を設定する

単純な getter/setter のみが定義された attr_accessor で仮想的な属性を設定することもできます。

class Post < ActiveRecord::Base
  has_many :rpost_likes, dependent: :destroy

  attr_accessor :liked_at
end 

ActiveRecord とは関係ない Ruby の機能なので、型指定や初期値の指定などは提供されていませんが、単純な値を保持する目的ならこちらで十分かもしれません。

https://docs.ruby-lang.org/ja/latest/method/Module/i/attr_accessor.html

参考

https://api.rubyonrails.org/classes/ActiveRecord/Attributes/ClassMethods.html
https://docs.ruby-lang.org/ja/latest/method/Module/i/attr_accessor.html
https://qiita.com/chiyo_i/items/24881002f36fe803e319
https://zenn.dev/kumasaka/articles/96c0f352b2a97f

Discussion