1

我有一个方法,旨在根据一组标准从数据库中返回许多项目:

scope :expired_not_marked, lambda { |client|
items = where('items.status > 0 AND items.expires_at < ? AND items.expired_at IS NULL AND (winning_bid_id IS NULL OR winner_id IS NULL)', Time.now)
unless client.nil?
    items.where('items.client_id = ?', client.id)
end
}

它被称为Item.expired_not_marked nil. 当我从 IRB 运行它时,我得到了很多结果,但它显示 SQL 查询被执行为:

SELECT `items`.* FROM `items` 

很明显,这不是原作者的意图。结果,相同的项目被一遍又一遍地处理。

为什么这个坏了,我该如何修复它。where 子句似乎是正确的。上述方法在item.rb模型内。

4

2 回答 2

5

您的问题是您lambda有时会返回nil,而返回的范围nil不会做任何有用的事情。

lambda返回其最后一个表达式的值。在您的情况下,该表达式将是unless. 所以如果client不是nil,它将返回:

items.where('items.client_id = ?', client.id)

一切都会好的。但如果client.nil?为真,unless则将评估为nil并且您的范围将返回nil。我认为你最好用这样的东西:

scope :expired_not_marked, lambda { |client|
  items = where('items.status > 0 AND items.expires_at < ? AND items.expired_at IS NULL AND (winning_bid_id IS NULL OR winner_id IS NULL)', Time.now)
  unless client.nil?
    items = items.where('items.client_id = ?', client.id)
  end
  items
}

这样,您始终拥有清晰、明确且定义明确的返回值。


ActiveRecord 查询接口指南建议您对带有参数的范围使用类方法:

使用类方法是接受范围参数的首选方式。

lambda所以如果方法太吵,你也可以这样做:

def self.expired_not_marked(client)
  items = where('items.status > 0')
  items = items.where('items.expires_at < ?', Time.now)
  items = items.where('items.expired_at IS NULL')
  items = items.where('winning_bid_id IS NULL OR winner_id IS NULL')
  unless client.nil?
    items = items.where('items.client_id = ?', client.id)
  end
  items
}

当然,您不必使用类方法。而且您不必将查询分解where为每个组件的一堆小调用,但这样阅读可能更容易。

于 2013-02-06T18:52:30.250 回答
1

lambda 始终没有返回任何内容。如果没有显式return语句,则 lambda 的最后一个计算表达式将是它的返回值。

scope :expired_not_marked, lambda { |client|
  items = where('items.status > 0 AND items.expires_at < ? AND items.expired_at IS NULL AND (winning_bid_id IS NULL OR winner_id IS NULL)', Time.now)
  unless client.nil?
    items.where('items.client_id = ?', client.id)
  end
  items
}

如果我写这个,我会建议一个scoped对象。这将确保只执行一个 SQL 查询。

scope :expired_not_marked, lambda { |client|
  items = Items.scoped.where('items.status > 0 AND items.expires_at < ? AND items.expired_at IS NULL AND (winning_bid_id IS NULL OR winner_id IS NULL)', Time.now)
  items = items.where('items.client_id = ?', client.id) unless client.nil?
  items.all
}
于 2013-02-06T18:52:50.030 回答