Rails 中是否可以否定范围?
我的类名为 Collection
的范围如下:
scope :with_missing_coins, joins(:coins).where("coins.is_missing = ?", true)
我可以运行 Collection.with_missing_coins.count
并获取结果 - 它效果很好! 目前,如果我想获得不丢失硬币的收藏,我会添加另一个范围:
scope :without_missing_coins, joins(:coins).where("coins.is_missing = ?", false)
我发现自己写了很多这些“相反”的范围。是否可以在不牺牲可读性或诉诸 lambda/方法(采用 true
或 false
作为参数)的情况下获得作用域的相反结果?
像这样的东西:
Collection.!with_missing_coins
I have the following scope for my class called Collection
:
scope :with_missing_coins, joins(:coins).where("coins.is_missing = ?", true)
I can run Collection.with_missing_coins.count
and get a result back -- it works great!
Currently, if I want to get collections without missing coins, I add another scope:
scope :without_missing_coins, joins(:coins).where("coins.is_missing = ?", false)
I find myself writing a lot of these "opposite" scopes. Is it possible to get the opposite of a scope without sacrificing readability or resorting to a lambda/method (that takes true
or false
as a parameter)?
Something like this:
Collection.!with_missing_coins
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
在 Rails 4.2 中,您可以执行以下操作:
它将使用子查询。
In Rails 4.2, you can do:
It'll use a subquery.
我不会为此使用单个作用域,而是使用两个作用域:
这样,当使用这些作用域时,就可以清楚地了解发生的情况。根据 Numbers1311407 的建议,目前还不清楚
with_missing_coins
的false
参数在做什么。我们应该尝试编写尽可能清晰的代码,如果这意味着偶尔不再那么热衷于 DRY,那就这样吧。
I wouldn't use a single scope for this, but two:
That way, when these scopes are used then it's explicit what's happening. With what numbers1311407 suggests, it is not immediately clear what the
false
argument towith_missing_coins
is doing.We should try to write code as clear as possible and if that means being less of a zealot about DRY once in while then so be it.
尽管我不认为诉诸 lambda 方法是一个问题,但范围本身并没有“逆转”。
然后:
There's no "reversal" of a scope per se, although I don't think resorting to a lambda method is a problem.
then:
您可以通过编程方式执行此操作,无需子查询:
另请参阅:https://makandracards.com/makandra/486959-how-to-negate-scope-conditions-in-rails
请注意,这只适用适用于 AR 查询,不适用于 SQL 查询。 例如
where('updated_at < ?', 1.hour.ago)
不会被否定。You can do this programmatically, without a subquery:
See also: https://makandracards.com/makandra/486959-how-to-negate-scope-conditions-in-rails
Note this only works with AR queries and does not work with SQL queries. E.g.
where('updated_at < ?', 1.hour.ago)
would not be negated.TL;DR
更多详细信息
Rails 7 引入
invert_where
它允许您反转整个 where 子句,而不是手动应用条件。
请小心,比较这些变体
并看看这个
invert_where
反转其之前的所有 where 条件。因此,最好避免在作用域中使用此方法,因为它可能会导致意外结果最好显式使用此方法,并且仅在需要反转所有先前条件之后立即在 where 链的开头使用
TL;DR
More details
Rails 7 introduced
invert_where
It allows you to invert an entire where clause instead of manually applying conditions
Be careful, compare these variants
And look at this
invert_where
inverts all where conditions before it. Therefore it's better to avoid using this method in scopes because it could lead to unexpected resultsMuch better to use this method explicitly and only in the beginning of where chain immediately after it is necessary to invert all the previous conditions
对于 Rails 7,您可以使用
invert_where
:但是;
Collection.with_something.without_missing_coins
也会反转with_something
,返回“没有某些内容”的行。您需要注意范围和条件的顺序:Collection.without_missing_coins.with_something
。!= true
,而不是= false
),因此否定范围也将包含 null 值;除非该字段定义为null: false
。因此,明确地编写它们可能会更好、更清晰。如果你想合并它们,也许你可以使用另一个参数化范围:
With Rails 7, you can use
invert_where
:However;
Collection.with_something.without_missing_coins
will invertwith_something
too, returning rows "without something". You'll need to watch out for the order of scopes and conditions:Collection.without_missing_coins.with_something
.!= true
, rather than= false
), the negated scope will include null values too; unless the field is defined asnull: false
.So it's probably better and clearer to write them explicitly. If you want to merge them, maybe you can use another parameterized scope:
这可能可行,没有进行太多测试。使用rails 5我猜rails 3有where_values方法而不是where_clause。
用法
Model.not(:scope_name)
this might just work, did not test it much. uses rails 5 I guess rails 3 has where_values method instead of where_clause.
usage
Model.not(:scope_name)
@bill-lipa 的答案很好,但当你想要的范围涉及关联或附件时要小心。
如果我们有以下范围来选择所有附加简历的用户:
如果
with_resume
为空,则以下否定将导致异常:因此您需要首先检查以获得您想要的结果:
The answer by @bill-lipa is good, but beware when the scopes you want involve associations or attachments.
If we have the following scope to select all users with a resume attached:
The following negation will cause an exception if
with_resume
is empty:Therefore you'll need to check first to get the results you want:
更新。现在 Rails 6 添加了方便且漂亮的负枚举方法。
博客文章
此处
Update . Now Rails 6 adds convenient and nifty negative enum methods.
Blog post
here