Подтвердить что ты не робот

ActiveRecord Find By Year, Day или Month в поле Date

У меня есть модель ActiveRecord, у которой есть атрибут даты. Можно ли использовать этот атрибут даты для поиска по Году, Дню и Месяцу:

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

или просто найти find_by_date (2012-12-1).

Я надеялся, что смогу избежать создания атрибутов Год, Месяц и День.

4b9b3361

Ответ 1

Предполагая, что ваш "атрибут даты" является датой (а не полной меткой времени), тогда простой where предоставит вам вашу "находку по дате":

Model.where(:date_column => date)

Вы не хотите find_by_date_column, так как это даст не более одного результата.

Для запросов года, месяца и дня вы хотите использовать функцию extract SQL:

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)

Однако, если вы используете SQLite, вам нужно будет возиться с strftime, поскольку он не знает, что такое extract:

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)

Спецификаторы формата %m и %d в некоторых случаях будут добавлять ведущие нули и могут путать тесты равенства, поэтому cast(... as int) принудительно форматирует строки в числах.

ActiveRecord не будет защищать вас от всех различий между базами данных, поэтому, как только вы сделаете что-то нетривиальное, вам либо придется создать свой собственный уровень переносимости (уродливый, но иногда необходимый), привяжите себя к ограниченному набору баз данных (реалистично, если вы не выпускаете что-то, что должно работать в любой базе данных), или выполняйте всю свою логику в Ruby (безумный для любого нетривиального количества данных).

Запросы года, месяца и дня месяца будут довольно медленными на больших таблицах. Некоторые базы данных позволяют добавлять индексы к результатам функций, но ActiveRecord слишком глуп, чтобы понять их, поэтому при попытке их использовать будет большой беспорядок; поэтому, если вы обнаружите, что эти запросы слишком медленные, вам придется добавить три дополнительных столбца, которые вы пытаетесь избежать.

Если вы собираетесь использовать эти запросы много, вы можете добавить области для них, но рекомендуемый способ добавления области с аргументом просто добавить метод класса:

Использование метода класса является предпочтительным способом принятия аргументов для областей. Эти методы все равно будут доступны для объектов ассоциации...

Итак, у вас будут методы класса, которые выглядят так:

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

Ответ 2

Независимая версия базы данных...

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

Ответ 3

Для MySQL попробуйте это

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

Ответ 4

В вашей модели:

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

В вашем контроллере:

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

Ответ 5

Я думаю, что самый простой способ сделать это - это область действия:

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

Используйте его следующим образом:

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

Это вернет все модели с полем date_field между началом и концом дня для даты отправки.

Ответ 6

Попробуйте следующее:

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

Ответ 7

Простая реализация для Just The Year

app/models/your_model.rb

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

Usage

Usage
Model.created_in( 1984 )

Ответ 8

Мне нравится ответ BSB, но как область

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)

Ответ 9

Это будет другое:

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

Хорошо работает для меня в SQLite.

Ответ 10

Нет необходимости в методе extract, это работает как прелесть в 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)

Ответ 11

Еще одна короткая область, как показано ниже:

  class Leave
    scope :by_year, -> (year) {
      where(date:
                Date.new(year).beginning_of_year..Date.new(year).end_of_year)
    }
  end

Назовите сферу:

Leave.by_year(2020)