最近业务迭代过程中,再次使用了 default_scope,踩了 unscoped的坑。

ActiveRecord提供了unscoped方法去清除方法调用之前的条件设定。

Returns a scope for the model without the previously set scopes.

class FormSetting < ActiveRecord::Base
  has_many :forms
end

class Form < ActiveRecord::Base
  enum category: { log: 0, checkout: 1 }

  default_scope { log }

  belongs_to :form_setting
end


form_setting.forms.unscoped.checkout

针对上面的代码,按我的错误理解,是仅去除default_scope的log条件,变成查询checkout类型的forms数据。

但实际结果如下:

form_setting.forms.first
#> Form Load (1.6ms)  SELECT  "forms".* FROM "forms" WHERE "forms"."category" = $1 AND "forms"."form_setting_id" = $2  ORDER BY "forms"."id" ASC LIMIT 1  [["category", 0], ["form_setting_id", 111]]

form_setting.forms.unscoped.checkout.first
#> Form Load (2.3ms)  SELECT  "forms".* FROM "forms" WHERE "forms"."category" = $1  ORDER BY "forms"."id" ASC LIMIT 1  [["category", 1]]

unscopedform_setting的条件也去除了,导致数据查询错误。我的预想是错误的,从SQL层面可以更直观的看到,unscoped会将调用链之前的where条件全都去掉,仅使用unscoped调用之后的条件。

form_setting.forms.unscoped等同于Form.unscoped

为了达到调用链上仅去除default_scope的目的,可以将查询修改成如下方式:

form_setting.forms.unscoped.checkout

Form.unscoped.where(form_setting: form_setting).checkout

FYI: