我在Rails 4.1应用程序中调试了一些遗留代码,并且我发现了一些令人困惑的结果:
#order.rb
# get the most recent order from the same year we are in
scope :last_from_this_year, -> { where("created_at >= ?", Time.mktime(Time.now.year, 1)).where("created_at < ?", Time.mktime(Time.now.year, 12, 31)).order('payment_id DESC').first }
# orders_controller.rb
prev_payment = Order.last_from_this_year
由于我无法解释的原因,这个范围正在返回包含所有订单记录的ActiveRecord_Relation,尽管事实上它正在调用.first
方法。我期待first
返回一个ActiveRecord对象。
为什么会这样?
更新
当我在控制台中运行 this 时,它按预期工作:
o = Order.where("created_at >= ?", Time.mktime(Time.now.year, 1)).where("created_at < ?", Time.mktime(Time.now.year, 12, 31)).order('payment_id DESC').first
我确切地复制/粘贴了scope
中的内容,并且它有效。因此,为什么它不像预期的范围那样表现出令人困惑。
$ Order.last_from_this_year.to_sql
=> SELECT "orders".* FROM "orders" WHERE (created_at >= '2017-01-01 08:00:00.000000') AND (created_at < '2017-12-31 08:00:00.000000') ORDER BY payment_id DESC LIMIT 1
似乎没错......很奇怪。
答案 0 :(得分:1)
该范围背后的概念是错误。范围的想法是始终返回ActiveRecord :: Relation的实例(对象而不是一个对象的集合),以允许与where
,includes
,{{1}等其他方法进一步链接等等。
如果您只需要从该范围中检索一个对象,则需要删除joins
并将其用作:.first
。
其他解决方案是将该代码移至类方法:
Order.last_from_this_year.first
然后def self.last_from_this_year
where("created_at >= ?", Time.mktime(Time.now.year, 1))
.where("created_at < ?", Time.mktime(Time.now.year, 12, 31))
.order('payment_id DESC')
.first
end
应该像在控制台上一样工作。