我该怎么做呢?Model.where("created_at> =#{Time.now - 5.days}")

the*_*ole 18 activerecord ruby-on-rails rails-activerecord

这一直困扰我一段时间......

我怎样才能串插一个datetime在Rails的ActiveRecord的查询?

# 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 ...
Run Code Online (Sandbox Code Playgroud)

我关心的原因是代码可读性:

# 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)
Run Code Online (Sandbox Code Playgroud)

mu *_*ort 59

我建议不要使用字符串插值,有许多锋利的边缘,你可能会有更多的乐趣在一桶鱼钩中摇摆苹果.你应该这样做:

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
)
Run Code Online (Sandbox Code Playgroud)

使用(好)命名占位符为您提供了字符串插值所提供的可读性和位置独立性,但很好地回避了字符串插值对您的引用,时区和格式问题.

但是你如何安全地使用字符串插值?你必须自己处理一些事情:

  1. 引用和逃避.
  2. 时间戳格式.
  3. 也许时区也是.

ActiveRecord将为您处理所有这些废话.

不要尝试自己做引用,使用驱动程序的引用方法.您可以访问connection.quote正确引用字符串.

任何数据库都知道如何处理ISO 8601时间戳,并且有一个方便的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)}")
Run Code Online (Sandbox Code Playgroud)

现在不是很漂亮吗?使用ISO 8601时间戳,您应该connection.quote使用简单的单引号来安全地替换呼叫:

Model.where("created_at >= '#{(Time.now - 5.days).utc.iso8601}' " + \
         "OR updated_at >= '#{(Time.now - 3.days).utc.iso8601}'")
Run Code Online (Sandbox Code Playgroud)

但你仍然有很多噪音和丑陋,你会养成坏习惯.

我们不会像1999年的PHP程序员一样参与派对,所以不要在SQL中使用字符串插值来放弃虚假的懒惰,使用命名的占位符.

  • "在一桶鱼钩里晃动苹果"史诗般的 (4认同)
  • 而且,*女士们,先生们,这就是为什么mu在撰写本文时有74.2k代表.我只能打一次upvote,但我非常感谢你答案的清晰和彻底. (3认同)

Mik*_*ell 57

老问题,但我最喜欢的方法是:

Model.where(created_at: 5.days.ago..Time.current)
Run Code Online (Sandbox Code Playgroud)

更漂亮,更可读.

此外,Rails的3.2中引入了一些积极的支持辅助方法得到一些常见的一些范围,Time#all_day,Time#all_week,Time#all_quarterTime#all_year,所以你可以为实例做:

Model.where(created_at: Time.current.all_week)
Run Code Online (Sandbox Code Playgroud)

  • 迈克,那真是太糟糕了. (3认同)