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

时间:2012-03-17 07:11:37

标签: ruby-on-rails activerecord rails-activerecord

这一直困扰着我......

如何在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)

2 个答案:

答案 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)命名占位符可以为您提供字符串插值所提供的可读性和位置独立性,但可以很好地回避字符串插值对您的引用,时区和格式问题。

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

  1. 引用和转义。
  2. 时间戳格式。
  3. 也许是时区。
  4. 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_dayTime#all_weekTime#all_quarterTime#all_year,所以你可以举例说明做:

Model.where(created_at: Time.current.all_week)