私の Rails アプリでは、ユーザーとロールの間に標準的な多対多の関係があります。
class User < ActiveRecord::Base
has_many :user_roles
has_many :roles, :through => :user_roles
end
ユーザーに任意のロールを 1 回だけ割り当てることができるようにしたいと考えています。重複を挿入しようとしても、リクエストを無視する必要があります。エラーをスローしたり、検証の失敗を引き起こしたりすることはありません。私が本当に表現したいのは「セット」です。セットに既に存在する要素を挿入しても効果はありません。{1,2,3} U {1} = {1,2,3}、{1,1,2,3} ではありません。
私は次のようにできることに気づきました:
user.roles << role unless user.roles.include?(role)
またはラッパーメソッド(例add_to_roles(role)
)を作成することによって、しかし私は書くことができるように、関連付けを介して自動化する慣用的な方法を望んでいました:
user.roles << role # automatically checks roles.include?
そして、それは私のために仕事をします。このようにして、重複をチェックしたり、カスタム メソッドを使用したりすることを覚えておく必要はありません。フレームワークに欠けているものはありますか?最初は has_many の :uniq オプションでできると思っていましたが、基本的には「個別に選択する」だけです。
これを宣言的に行う方法はありますか?そうでない場合は、関連付け拡張機能を使用することでしょうか?
デフォルトの動作が失敗する例を次に示します。
>> u = User.create ユーザー作成 (0.6ms) INSERT INTO "users" ("name") VALUES(NULL) => #<ユーザー ID: 3、名前: なし> >> u.roles << Role.first Role Load (0.5ms) SELECT * FROM "roles" LIMIT 1 UserRole Create (0.5ms) INSERT INTO "user_roles" ("role_id", "user_id") VALUES(1, 3) Role Load (0.4ms) SELECT "roles".* FROM "roles" INNER JOIN "user_roles" ON "roles".id = "user_roles".role_id WHERE (("user_roles".user_id = 3)) => [#<ロール ID: 1、名前: "1">] >> u.roles << Role.first Role Load (0.4ms) SELECT * FROM "roles" LIMIT 1 UserRole Create (0.5ms) INSERT INTO "user_roles" ("role_id", "user_id") VALUES(1, 3) => [#<ロール ID: 1、名前: "1">、#<ロール ID: 1、名前: "1">]