中間テーブルのフラグ的カラムを使ってhas_many :throughのなかの特別なn個を絞る

ふと思いついたActiveRecordの便利な使い方です。

たとえばuserとgroupがmembershipを介して多対多で、グループから所属ユーザをひく関連が欲しい場合、ActiveRecordを使うと次のように書きます。

class Group < AR::Base
  has_many :memberthips
  has_many :users, through: :memberhips
end

class Membership < AR::Base
  belongs_to :user
  belongs_to :group
end

class User < AR::Base
end

このとき、グループにはリーダーとなるユーザが一人いるという場合、membershipsにその関係を持たせることが一般的だと思います。

で、オプションをいい感じに指定すると、その特別なリレーションも設定できます。

class Group < AR::Base
  has_one :leader_membership, class_name: 'Membersihp', conditions: {role: 'leader'}
  has_one :leader, through: :leader_membership, source: :user

  # みんなを引くのはそれはそれでママ残す
  has_many :memberthips
  has_many :users, through: :memberhips
end

普通といえば普通なんですが、たとえばリーダーだけをeager loadingしたい場合など、DB上でせっかく作った関連をちゃんとアプリでも活用したい場合に役立ちます。

2011-03-11T00:17追記

id:ursmから「同一のレコードに対して複数の関連を貼る場合、identity mapがないと更新しようとしたレコード(をマップしたオブジェクト)がワケのわかんないことになってはまるよ」という指摘あり。なので、has_oneのほうはreadonly:true とかにしておくといいかもしれません。

ある程度長いことRailsを使っている人は、関連がらみのidentity mismatchで一度くらいは泣いたことがあると思いますが、この記事で紹介しているケースでも注意ということで。
Rails 3.1ではidentity mapが入るかもしれないという噂。