これはしばらくの間私を悩ませてきました...
どのようにすればstring interpolate a datetime
in Rails ActiveRecord querys?)
# 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)
これには文字列補間を使用しないことをお勧めします。多くの鋭いエッジがあり、おそらく魚のバケツのバケツでリンゴをより楽しくボブするでしょう。次のようにしてください。
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
)
(適切な)名前付きプレースホルダーを使用すると、文字列補間が提供する読みやすさと位置の独立性が得られますが、文字列補間によって生じる引用、タイムゾーン、およびフォーマットの問題はうまく回避されます。
しかし、どのようにして文字列補間を安全に使用できますか?自分で処理しなければならないことがいくつかあります。
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プログラマーのようにパーティーを開いているわけではないので、SQLで文字列補間を使用して誤った怠惰に屈服せず、名前付きプレースホルダーを使用してください。
古い質問ですが、私が好む方法は:
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)