状況
-
userとroomを紐付けるpermissionsテーブル(部屋に入れる許可証を管理)を作成したい
-
references型でuser, taskをカラムにもつ
-
既にテーブルに存在する(user_id, task_id)のセットが入力された際、保存しない制約
-
このバリデーションが正常に動作するかをコンソールで確認したい
「一意性制約」を使えば実現できるこちらのバリデーション。
今回はこれがちゃんと実装できているか、rails cで立ち上げたコンソールでの操作についての記録です。
環境
-
macOS Catalina 10.15.6
-
ruby 2.6.5
-
Rails 6.0.3.4
-
MySQL : 5.6.47
今回のコード
class Permission < ApplicationRecord
belongs_to :user
belongs_to :task
validates :user_id, uniqueness: { scope: :task_id }
end
ちなみに、こちらはアプリ上での制約であり、正確にはDB側でも制約をかける必要があります (Railsガイドより)。
しかしまだ理解できていないので今回は省略します。
コンソールで入力したコマンド
一意性制約を確認したかったので、以下の流れでコードを実行していきました。
-
Permission.find(数字)で既存のレコードを取得
-
適当な変数に代入する
-
valid?メソッドで保存可能か確認
-
"false"になると予想
ターミナルでコンソールを立ち上げて確認しました。
$ rails c
[1] > permission = Permission.find(1)
=> #<Permission:*** id: 1, user_id: 1, task_id: 1, ***>
[2] > new.valid?
=> true
trueになってしまいました…。
原因を考え、コマンドを変えてみました。
原因とコマンドの変更
【原因】
▶"find"は既存のレコードを探すActiveRecordメソッド
▶これを保存すると上書き保存みたいになる?
【変更】
▶user_id, task_idを直打ちする
$ rails c
[1] > Permission.find(1)
=> #<Permission:*** id: 1, user_id: 1, task_id: 1, ***>
[2] > permission = Permission.new(user_id: 1, task_id: 1)
=> #<Permission:*** id: nil, user_id: 1, task_id: 1, ***>
[3] > permission.valid?
=> false
[4] > permission.errors.full_messages
=> "User has already been taken"
これでしっかりfalseになることを確認できました。
まとめ
-
複数キーの一意性制約はscopeオプションで指定できる
-
確認のためには、新たにレコードを生成すること
少しハマってしまいましたがなんとか乗り越えました。
本当に理解するためには、saveやvalidの裏側で何が動いているかを知る必要がありそうですね。
まだまだ勉強することがいっぱいありますが、1つずつおさえていきます。