Как мне это сделать? Model.where (created_at ›= # {Time.now - 5.days})

Это меня уже давно беспокоит ...

Как я могу интерполировать строку 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)

person thewillcole    schedule 17.03.2012    source источник


Ответы (2)


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

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, используйте именованные заполнители.

person mu is too short    schedule 17.03.2012
comment
И именно поэтому, дамы и господа, на момент написания этой статьи у mu было 74,2 тысячи репутации. Я могу проголосовать только один раз, но я очень ценю ясность и обстоятельность вашего ответа. - person thewillcole; 17.03.2012
comment
подпрыгивание яблок в ведре с рыболовными крючками эпопея - person Drazen; 29.03.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)
person Mike Campbell    schedule 16.05.2013