ActiveRecord - "范围"中的"第一个"方法返回多个记录

use*_*809 5 activerecord ruby-on-rails

我是Rails的新手,我有一个奇怪的问题.

这是一个代码示例:

class News < ActiveRecord::Base
  scope :pinned, -> { where(pinned: true).first }
end
Run Code Online (Sandbox Code Playgroud)

如果有带"固定"标志的记录没有问题,当我呼叫News.pinned返回单个记录时.

我在日志中看到了这个查询:

SELECT `news`.* 
FROM `news` 
WHERE `news`.`pinned` = 1 
ORDER BY `news`.`id` ASC 
LIMIT 1
Run Code Online (Sandbox Code Playgroud)

但是如果没有带有"固定"标志的记录,当我调用News.pinned下两个查询时执行:

SELECT `news`.* 
FROM `news` 
WHERE `news`.`pinned` = 1 
ORDER BY `news`.`id` ASC 
LIMIT 1

SELECT `news`.* FROM `news`
Run Code Online (Sandbox Code Playgroud)

谢谢!

RMa*_*ste 12

Friendy,这是ActiveRecord的方法"范围":

1   # File activerecord/lib/active_record/scoping/named.rb, line 145
2   def scope(name, body, &block)
3     extension = Module.new(&block) if block
4
5     # Check body.is_a?(Relation) to prevent the relation actually being
6     # loaded by respond_to?
7     if body.is_a?(Relation) || !body.respond_to?(:call)
8       ActiveSupport::Deprecation.warn(
9         "Using #scope without passing a callable object is deprecated. For "                "example `scope :red, where(color: 'red')` should be changed to "                "`    scope :red, -> { where(color: 'red') }`. There are numerous gotchas "                "in the former usage and it makes the implementation more complicated "                "and     buggy. (If you prefer, you can just define a class method named "                "`self.red`.)"
10      )
11    end
12
13    singleton_class.send(:define_method, name) do |*args|
14      if body.respond_to?(:call)
15        scope = all.scoping { body.call(*args) }
16        scope = scope.extending(extension) if extension
17      else
18        scope = body
19      end
20
21      scope || all
22    end
23  end
Run Code Online (Sandbox Code Playgroud)

如果范围是"nil"则返回第21行,然后返回"all".
在你的情况下,当你在第15行没有记录的情况下调用"News.pinned"时,第一次参考运行并且范围接收"nil",所以当它到达第21行时,作为范围是"nil","all"运行第二次咨询并返回所有寄存器.

我通过删除第21行的"全部"来测试它覆盖方法"范围",我只有一个查询

绕过这个用途:

class News < ActiveRecord::Base
  def self.pinned
    where(pinned: true).first
  end
end
Run Code Online (Sandbox Code Playgroud)