TL;DR
ポリモーフィックにしてるカラムを _id
と _type
に分けて、 _id
だけを validates_uniqueness_of
の対象にして、 _type
は scope 側に本来対象としたいカラムと一緒に入れてしまう。
例
一人のユーザーがたくさんの車を持てるし、一つの車をたくさんのユーザーで共有することもできるけど、一つのユーザーに同じ車を何個も追加できてしまっては困る、というかそれは単なるバグだから防ぎたいという場合。
class UserCar < ActiveRecord::Base
belongs_to :user
belongs_to :car, polymorphic: true
validates_presence_of :user, :car
# こう書きたいところだけど、これは動かない
# validates_uniqueness_of :car, scope: :user
# ので、 _id と _type に分けて、 _id を対象に、 _type を scope に入れる
validates_uniqueness_of :car_id, scope: [ :car_type, :user_id ]
end
この状態で user.car << fit
とかやった時に発行される、既存行の確認クエリはこんな感じ。
SELECT 1 AS one FROM `user_cars` WHERE (`user_cars`.`car_id` = 1 AND `user_cars`.`car_type` = 'Compact' AND `user_cars`.`user_id` = 1) LIMIT 1
単純に、対象カラムも scope も全部まとめて WHERE に食わせてるだけなので、「いやいや User も polymorphic で…」って場合も scope にさらに :user_type
を足せば大丈夫。
Index ないとつらいので、
add_index :user_cars, [ :user_id, :car_type, :car_id ], unique: true
add_index :user_cars, [ :car_type, :car_id, :user_id ], unique: true
とかになっているとよいですね。(というか、普通は has_many :through
の時点で追加してるか)
参考文献
けど、見つけづらかったので改めてまとめました。 StackOverflow の投稿とかヒットしまくるんだけど、ドンピシャではなく。需要がニッチすぎるんや。
というか
データベース側を unique: true
にしてれば validates_uniqueness_of
なくても不整合は起きないので、ここで頑張んなくても最悪死にはしません。