Как мне это сделать? Model.where ("made_at> = # {Time.now - 5.days}") - PullRequest
18 голосов
/ 17 марта 2012

Меня это некоторое время беспокоило ...

Как мне строка интерполировать a 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 ...

причина, по которой я забочусь о читаемости кода:

# 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 ]

59 голосов
/ 17 марта 2012

Я бы посоветовал не использовать для этого интерполяцию строк, есть много острых краев, и вы, вероятно, получите больше удовольствия, покачивая яблоки в ведре с рыболовными крючками. Вы должны сделать это так:

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
)

Использование (хорошо) именованных заполнителей дает вам удобочитаемость и независимость позиции, которые, по вашему мнению, предлагает интерполяция строк, но приятно обходят проблемы с цитированием, часовым поясом и форматом, которые вызывают у вас силы интерполяции строк.

Но как безопасно использовать интерполяцию строк? Есть несколько вещей, с которыми вы должны справиться:

  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)}")

Не так красиво сейчас? С отметками времени ISO 8601 вы должны безопасно заменить вызовы connection.quote простыми одинарными кавычками:

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

но у вас все еще много шума и уродства, и вы будете развивать вредные привычки.

Мы не проводим вечеринки, как программисты PHP в 1999 году, поэтому не поддавайтесь ложной лени, используя интерполяцию строк в вашем SQL, используйте именованные заполнители.

54 голосов
/ 16 мая 2013

Старый вопрос, но мой любимый метод:

Model.where(created_at: 5.days.ago..Time.current)

намного красивее и удобочитаемее.

Кроме того, В Rails 3.2 введены некоторые вспомогательные методы Active Support для получения некоторых общих диапазонов, Time#all_day, Time#all_week, Time#all_quarter и Time#all_year, так что вы можете, например, сделать:

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