5
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

polymorphic な私でも validates_uniqueness_of したい

Last updated at Posted at 2014-09-24

TL;DR

ポリモーフィックにしてるカラムを _id_type に分けて、 _id だけを validates_uniqueness_of の対象にして、 _type は scope 側に本来対象としたいカラムと一緒に入れてしまう。

一人のユーザーがたくさんの車を持てるし、一つの車をたくさんのユーザーで共有することもできるけど、一つのユーザーに同じ車を何個も追加できてしまっては困る、というかそれは単なるバグだから防ぎたいという場合。

ひとりのユーザーがひとつの電話を2回以上追加できないようにしたい
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 ないとつらいので、

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 の時点で追加してるか)

参考文献

APIdock の notes に書いてあった

けど、見つけづらかったので改めてまとめました。 StackOverflow の投稿とかヒットしまくるんだけど、ドンピシャではなく。需要がニッチすぎるんや。

というか

データベース側を unique: true にしてれば validates_uniqueness_of なくても不整合は起きないので、ここで頑張んなくても最悪死にはしません。

5
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?