tutorial rails que ejemplos descargar curso caracteristicas ruby-on-rails ruby-on-rails-3 activerecord rails-activerecord

ruby on rails - rails - Búsqueda de ActiveRecord por año, día o mes en un campo de fecha



ruby on rails tutorial (10)

Tengo un modelo de ActiveRecord que tiene un atributo de fecha. ¿Es posible utilizar ese atributo de fecha para buscar por año, día y mes?

Model.find_by_year(2012) Model.find_by_month(12) Model.find_by_day(1)

o simplemente es posible encontrar_by_date (2012-12-1).

Esperaba poder evitar crear atributos de Año, Mes y Día.


Implementación simple para solo el año

app / models / your_model.rb

scope :created_in, ->(year) { where( "YEAR( created_at ) = ?", year ) }

Uso

Model.created_in(1984)


Creo que la forma más fácil de hacerlo es un alcance:

scope :date, lambda {|date| where(''date_field > ? AND date_field < ?'', DateTime.parse(date).beginning_of_day, DateTime.parse(date).end_of_day}

Úselo así:

Model.date(''2012-12-1'').all

Eso devolverá todos los modelos con un campo de fecha entre el comienzo y el final del día para la fecha que envía.


En tu modelo:

scope :by_year, lambda { |year| where(''extract(year from created_at) = ?'', year) }

En tu controlador:

@courses = Course.by_year(params[:year])


Esta sería otra:

def self.by_year(year) where("published_at >= ? and published_at <= ?", "#{year}-01-01", "#{year}-12-31") end

Funciona muy bien para mí en SQLite.


Me gusta la respuesta de BSB pero como un alcance

scope :by_year, (lambda do |year| dt = DateTime.new(year.to_i, 1, 1) boy = dt.beginning_of_year eoy = dt.end_of_year where("quoted_on >= ? and quoted_on <= ?", boy, eoy) end)


No es necesario extract método, esto funciona como un encanto en postgresql:

text_value = "1997" # or text_value = ''1997-05-19'' or ''1997-05'' or ''05'', etc sql_string = "TO_CHAR(date_of_birth::timestamp, ''YYYY-MM-DD'') ILIKE ''%#{text_value.strip}%''" YourModel.where(sql_string)


Para MySQL, pruebe esto

Model.where("MONTH(date_column) = 12") Model.where("YEAR(date_column) = 2012") Model.where("DAY(date_column) = 24")


Prueba esto:

Model.where("MONTH(date_column) = ? and DAY(date_column) = ?", DateTime.now.month, DateTime.now.day)


Suponiendo que su "atributo de fecha" es una fecha (en lugar de una marca de tiempo completa), entonces un simple where le dará su "buscar por fecha":

Model.where(:date_column => date)

No quiere find_by_date_column porque eso le dará como máximo un resultado.

Para las consultas de año, mes y día, querrá usar la función SQL de extract :

Model.where(''extract(year from date_column) = ?'', desired_year) Model.where(''extract(month from date_column) = ?'', desired_month) Model.where(''extract(day from date_column) = ?'', desired_day_of_month)

Sin embargo, si está utilizando SQLite, tendría que perder el tiempo con strftime ya que no sabe qué extract es:

Model.where("cast(strftime(''%Y'', date_column) as int) = ?", desired_year) Model.where("cast(strftime(''%m'', date_column) as int) = ?", desired_month) Model.where("cast(strftime(''%d'', date_column) as int) = ?", desired_day_of_month)

Los especificadores de formato %m y %d agregarán ceros iniciales en algún caso y eso puede confundir las pruebas de igualdad, por lo tanto, el cast(... as int) para forzar las cadenas formateadas a los números.

ActiveRecord no lo protegerá de todas las diferencias entre bases de datos, por lo que tan pronto como haga algo no trivial, o bien deberá construir su propia capa de portabilidad (feo pero a veces necesario), vincúlese a un conjunto limitado de bases de datos (realistas a menos estás liberando algo que tiene que ejecutarse en cualquier base de datos), o haz toda tu lógica en Ruby (loco por cualquier cantidad de datos no triviales).

Las consultas de año, mes y día serán bastante lentas en las tablas grandes. Algunas bases de datos le permiten agregar índices a los resultados de las funciones, pero ActiveRecord es demasiado estúpido para comprenderlas, por lo que si usted trata de utilizarlas, será un gran desastre. por lo tanto, si encuentra que estas consultas son demasiado lentas, tendrá que agregar las tres columnas adicionales que intenta evitar.

Si vas a utilizar estas consultas mucho, entonces podrías agregar ámbitos para ellas, pero la forma recomendada de agregar un alcance con un argumento es simplemente agregar un método de clase:

Usar un método de clase es la forma preferida de aceptar argumentos para ámbitos. Estos métodos seguirán estando accesibles en los objetos de asociación ...

Entonces tendrías métodos de clase que se ven así:

def self.by_year(year) where(''extract(year from date_column) = ?'', year) end # etc.


Versión independiente de la base de datos ...

def self.by_year(year) dt = DateTime.new(year) boy = dt.beginning_of_year eoy = dt.end_of_year where("published_at >= ? and published_at <= ?", boy, eoy) end