2012-03-17 17 views
13

Esto me ha estado molestando por un tiempo ...¿Cómo hago esto? Model.where ("created_at> = # {Time.now - 5.days}")

¿Cómo puedo cadena de interpolar undatetime en las consultas de los carriles de 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 ... 

La razón por la que me importa es para la legibilidad del código:

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

Respuesta

56

aconsejaría contra el uso de interpolación de cadenas para esto, hay muchos bordes afilados y es probable que tenga más divertido morder una manzana en un cubo de anzuelos. Usted debe hacerlo de esta manera:

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 
) 

Utilizando (también) los marcadores de posición con nombre que da a la legibilidad y la posición de independencia que usted piensa ofertas de interpolación cadena pero muy bien elude las cotizaciones, las cuestiones zona horaria y el formato que las fuerzas de interpolación de cadenas en usted .

Pero, ¿cómo se puede utilizar con seguridad la interpolación de cadenas? Hay algunas cosas que debe manejar usted mismo:

  1. Citando y escapando.
  2. Formatos de marca de tiempo.
  3. Quizás zonas horarias también.

ActiveRecord se encargará de todas estas tonterías para usted.

No intente hacer la cotización usted mismo, utilice los métodos de cotización del conductor. Tendrá acceso al connection.quote para cotizar correctamente las cadenas.

Cualquier base de datos sabrá qué hacer con ISO 8601 timestamps y hay un método conveniente iso8601 para eso. ISO 8601 también incluye convenientemente la zona horaria y la base de datos debería poder analizar eso (pero si no puede, entonces tendrá que convertir sus horarios a UTC a mano con .utc).

Por lo tanto, para estar seguro:

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

No es tan bonito ahora es? Con la norma ISO 8601 marcas de tiempo que debe estar a salvo la sustitución de las llamadas connection.quote con comillas simples simples:

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

, pero todavía tiene un montón de ruido y la fealdad y podrás desarrollar malos hábitos.

No estamos festejando como programadores de PHP en 1999, así que no ceda a la pereza falsa mediante el uso de la interpolación de cadenas en su SQL, use marcadores de posición con nombre.

+2

Y * que *, señoras y caballeros, es por eso mu tiene 74.2k rep en el momento de escribir esto. Solo puedo golpear el voto popular una vez, pero realmente aprecio la claridad y minuciosidad de tu respuesta. – thewillcole

+4

"balanceándose por las manzanas en un cubo de anzuelos" épica –

45

vieja pregunta, pero mi método favorito es:

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

mucho más bonita y más legible.

Además, Rails 3.2 introduced algunos métodos auxiliares apoyo activo para conseguir algunos rangos comunes, Time#all_day, Time#all_week, Time#all_quarter y Time#all_year, por lo que podría, por ejemplo, hacer:

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

Eso es bastante tonto, Mike. – thewillcole