这一直困扰着我......
如何在Rails ActiveRecord查询中字符串插入一个datetime
?
# Works, but supeh ugleh:
Model.where("created_at >= ?", Time.now - 5.days)
# How do I do this?
Model.where("created_at >= #{Time.now - 5.days}")
# As is, it produces the following error message:
# ActiveRecord::StatementInvalid: PG::Error: ERROR: syntax error at or near ...
我关心的原因是代码可读性:
# I like this better:
Model.where("created_at >= #{Time.now - 5.days} OR" + \
"updated_at >= #{Time.now - 3.days}")
# than this:
Model.where("created_at >= ? OR updated_at >= ?", Time.now - 5.days, Time.now - 3.days)
答案 0 :(得分:58)
我建议不要使用字符串插值,有许多锋利的边缘,你可能会有更多的乐趣在一桶鱼钩中摇摆苹果。你应该这样做:
Model.where(
'created_at >= :five_days_ago or updated_at >= :three_days_ago',
:five_days_ago => Time.now - 5.days,
:three_days_ago => Time.now - 3.days
)
使用(well)命名占位符可以为您提供字符串插值所提供的可读性和位置独立性,但可以很好地回避字符串插值对您的引用,时区和格式问题。
但是你如何安全地使用字符串插值?你必须自己处理一些事情:
ActiveRecord将为您处理所有这些废话。
不要尝试自己做引用,使用驱动程序的引用方法。您可以访问connection.quote
以正确引用字符串。
任何数据库都知道如何处理ISO 8601 timestamps,并且有一个方便的iso8601
方法。 ISO 8601还方便地包含时区,数据库应该能够解析(但如果不能,那么你必须用.utc
手动将你的时间转换为UTC。)
所以,为了安全起见:
Model.where("created_at >= #{connection.quote((Time.now - 5.days).utc.iso8601)} " + \
"OR updated_at >= #{connection.quote((Time.now - 3.days).utc.iso8601)}")
现在不是很漂亮吗?使用ISO 8601时间戳,您应该使用简单的单引号替换connection.quote
次来安全:
Model.where("created_at >= '#{(Time.now - 5.days).utc.iso8601}' " + \
"OR updated_at >= '#{(Time.now - 3.days).utc.iso8601}'")
但你仍然有很多噪音和丑陋,你会养成坏习惯。
我们不会像1999年的PHP程序员一样参与派对,所以不要在SQL中使用字符串插值,使用命名占位符来放弃虚假的懒惰。
答案 1 :(得分:52)
老问题,但我最喜欢的方法是:
Model.where(created_at: 5.days.ago..Time.current)
更漂亮,更易读。
另外,Rails 3.2 introduced一些Active Support助手方法可以获得一些常见范围Time#all_day
,Time#all_week
,Time#all_quarter
和Time#all_year
,所以你可以举例说明做:
Model.where(created_at: Time.current.all_week)