警告の意味
※ どのコードも意味が無く申し訳ないですが、生成されるSQLの問題なので意味は伝わると信じてます・・・いいサンプルコードが思いつかなくてすいません。
Rails5から6にアップグレードしたらこんな警告が出るようになった。
DEPRECATION WARNING: Class level methods will no longer inherit scoping from `fixed` in Rails 6.1. To continue using the scoped relation, pass it into the block directly. To instead access the full set of models, as Rails 6.1 will, use `ShiftUser.unscoped`. (called from block in <class:ShiftUser> at /home/circleci/test_dir/app/models/shift_user.rb:10)
問題のコードはこんな感じ。
class ShiftUser < ApplicationRecord
scope :fixed, -> {
ShiftUser.reflected.or(ShiftUser.failed)
}
このコード、私のミスで変なコードになってます。本来意図したコードは下記の感じです。
class ShiftUser < ApplicationRecord
enum status: { building: 1, reflected: 2, failed: 3 }
scope :fixed, -> {
reflected.or(failed)
}
で、現状6.0まではどっちも同じSQLが発行されます。
ShiftUser.where(user_id: [1,2]).fixed
# SELECT `shift_user`.* FROM `shift_user` WHERE `shift_user`.`user_id` IN (1, 2) AND (`shift_user`.`status` = 2 OR `shift_user`.`status` = 3)
ブロック内でShiftUser.reflected
とクラスメソッドから呼んでいるのだからActiveRecordRelationがリセットされそうな感じがするのですが、継承されてるんですね。
この挙動が気持ち悪いので6.1から変えるという話だと思います。たぶんこの辺が関連ISSUEだと思われます。
https://github.com/rails/rails/pull/32380
https://github.com/rails/rails/pull/35186
https://github.com/rails/rails/pull/35280
どう治すか?
ほとんどの場合下記のように修正すれば問題ないでしょう。
class ShiftUser < ApplicationRecord
scope :fixed, -> {
reflected.or(failed)
}
このように直接メソッド呼ぶようにしておかないと6.1になった時に挙動が変わりますね。現状6.0以前バージョンで6.1の挙動を実現したい場合はShiftUser.unscoped
使います。こんな感の2フェーズクエリーでたまに使うかも。
class ShiftUser < ApplicationRecord
scope :some_scope, -> {
where(user_id: ShiftUser.unscoped.pluck(:user_id))
}
現状でこのコードを
class ShiftUser < ApplicationRecord
scope :some_scope, -> {
where(user_id: ShiftUser.pluck(:user_id))
}
こう書いてしまうと下記のように、内側のSQLにもwhere(user_id: [1,2])
がくっついて(たぶん)意図してないコードになります。
ShiftUser.where(user_id: [1,2]).some_scope
# (0.3ms) SELECT `shift_user`.`user_id` FROM `shift_user` WHERE `shift_user`.`user_id` IN (1, 2)
# ShiftUser Load (0.4ms) SELECT `shift_user`.* FROM `shift_user` WHERE `shift_user`.`user_id` IN (1, 2) AND 1=0