10
4

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.

DEPRECATION WARNING: Class level methods will no longer inherit scoping from `fixed` in Rails 6.1.

Last updated at Posted at 2019-10-21

警告の意味

※ どのコードも意味が無く申し訳ないですが、生成される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
10
4
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
10
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?