这有效:
scope :archived, :conditions => "day_id IN (#{Day.where("year_id != #{DateTime.now.year}").collect{ |d| d.id }.join(",")})"
是否有一种更难用的方式来编写IN子句?
答案 0 :(得分:3)
原始范围和amitamb的解决方案中存在隐藏的错误。 scope
是一种类方法,所以为什么这么说:
scope :blahblah, arguments
在解析和加载类时评估arguments
表达式。特别是,当将类加载到Rails环境中时,将评估DateTime.now.year
。因此,如果课程在2012-12-31加载,则where
将为:
where('days.year_id != 2012')
如果您在2013-01-01几小时后使用示波器,它仍将使用2012年作为年份。这个问题有两种解决方案:
使用类方法或lambda作为范围:
scope :archived, -> { joins(:day).where('days.year_id != ?', DateTime.now.year) }
# or
def self.archived
joins(:day).where('days.year_id != ?', DateTime.now.year)
end
将当前年度计算下推到数据库中:
scope :archived, joins(:day).where('days.year_id != extract(year from current_date)')
某些数据库需要一些而不是extract(year from current_date)
,因此您可能希望使用(1)来避免可能的可移植性和时区问题。
此外,您的原始方法在Day.where(...)
部分遇到类似问题,在您的类加载时执行该查询,因此如果days
表在您的应用程序运行时发生更改,那么您将会检查错误的清单。
答案 1 :(得分:0)
你能做这样的事吗?
your_logic = "year_id != #{DateTime.now.year}".collect{ |d| d.id }.join(",")}
scope :archived, :conditions => Day.where(your_logic)["day_id"]
我刚刚首先访问了对象,然后取出day_id IN
来替换day_id
。
答案 2 :(得分:0)
对于简单的重写,您可以执行以下操作
scope :archived, where( :day_id => Day.where("year_id != #{DateTime.now.year}").collect{ |d| d.id }
但是这将生成两个查询和来自rails代码的不必要的连接。理想情况下,您应该通过SQL进行此连接,并仅在一个查询中执行检查。
因此产生的模型应该类似于
class Model
belongs_to :day
scope :archieved, joins(:day).where("days.year_id != #{DateTime.now.year}")
end
有关详细信息,请参阅以下内容
http://guides.rubyonrails.org/active_record_querying.html#specifying-conditions-on-the-joined-tables