Rails: SELECTするカラムを追加するscopeを定義する

こんにちは、鈴木です。

 

Rails で SELECT するカラムを追加する scope を定義する方法をご紹介します。

 

やりたいこと

SELECT するカラムを指定するには、以下のように select メソッドを使用します。

他のカラムから計算した値が必要な場合、例えば姓(family_name)と名(given_name)を結合した値を full_name という名前で欲しいという場合は次のようになるでしょう。

複数個所で使う場合は scope として定義しておくと便利です。

scope を活用することでコードがすっきりし、みんなハッピー!と思いきや。

次のコードを見てください。

実行すると「ActiveModel::MissingAttributeError: missing attribute: birthday」という例外が発生します。

当たり前ではあるのですが、select で full_name しか指定していないので、birthday にアクセスしようとすると例外が発生します。

 

しかし、そうではなくて、full_name 「も」SELECT してきてほしいんです。

full_name 「だけ」SELECT するのではなく、full_name「も」SELECT してほしいんです。

 

このように全てのカラムと計算した値(full_name)を SELECT したい、というケースは時々発生します。

以下のように対応することもできますが、あまり綺麗な解決ではありません。

これならどうでしょうか?

これだと以下のように、本当に指定したカラムだけ必要な場合に対応できません(select_full_name の中で select('*, ...') としているため)。

以上の課題を解決する方法はあるのでしょうか。

 

SELECTするカラムを追加するscopeを定義する

いきなり答えですが、以下のように実装することができます。

(1) では現在の scope を取得しています。何も scope が指定されていない場合は nil が返されるので代わりに relation を取得します。

(2) では、if scope.select_values.blank? によって既に select が行われているか判断しています。select_values は select で指定されたカラムの一覧を返すメソッドです。

(3) でようやく本来やりたかった select('family_name || given_name AS full_name') を指定します。

こうすることで、既に select が指定されている場合はそれに追加、そうでなければ select('*') した上で追加する、ということが実現できます。

以下のように add_select_field というメソッドに分割しておけば、同様の処理を行う場合に便利でしょう。

Enjoy Rails!!

 

Comments are closed, but you can leave a trackback: Trackback URL.