Ruby ActiveRecord 中如何将两个 scope 通过 or 条件合并成一个 scope

zeekerpro · 2022年03月18日 · 最后由 zeekerpro 回复于 2022年03月18日 · 399 次阅读

例如 一个 record 中有两个 scope


class User < ApplicationRecord

scope :onlines,  -> { where(:state => 'online') }

scope :invisibles, -> { where(:state => 'invisible') }

end

现在有一个新的 scope,需要用 or 条件将上述两个 scope 连起来

scope :inlines_or_invisibles, lambda #onlines.or.invisibles#

求 inlines_or_invisibles scope 的写法。

好像不行,只能单独写一个 scope

scope : inlines_or_invisibles, -> { where(state: %w[online invisible]) }
3 楼 已删除
inlines.or(invisibles)
Ddl1st 回复

User.inlines.or(User.invisibles)

这样可以的,不过不太建议。比较建议二楼的做法 scope : inlines_or_invisibles, -> { where(state: %w[online invisible]) }

lanzhiheng 回复

是的,两个 scope 都比较简单的情况下二楼的方式比较合适,但是如果遇到稍微复杂一点的 scope,合并起来就容易代码重复。

不过我刚刚测试了下,如果 scope 中有 join 的情况,三楼的写法在运行的时候也容易报错。例如

# 离线
scope : onlines,  -> { where(:state => 'online') }

# 忙碌, 只有正在写代码才算真的busy
scope :real_busys, lambda {
  joins(:doings)
    .where(:state => 'busy')
    .and(where('doings.thing == coding'))
}

如果直接合并为

scope :onlines_or_real_busy, -> {  onlines.or(real_busys)  }

在执行的时候会报错:

ArgumentError: Relation passed to #or must be structurally compatible. Incompatible values: [:joins]

需要将有 join 提前,写为:

scope :onlines_or_real_busys, -> { real_busys.or(onlines)  }

运行的时候就没问题了。

但是是这么写的话当修改过前面的 scope,比如在 onlines 上 join 其它的 table 时,需要连带修改测试合并的 onlines_or_real_busys,这样代码维护起来就不太好了。

真是 Egg pain ....

7 楼 已删除
需要 登录 后方可回复, 如果你还没有账号请 注册新账号