Rails 3 лучший способ написать предложение IN в области видимости? - PullRequest
1 голос
/ 27 марта 2012

Это работает:

scope :archived, :conditions => "day_id IN (#{Day.where("year_id != #{DateTime.now.year}").collect{ |d| d.id }.join(",")})"

Есть ли более простой способ писать предложения IN?

Ответы [ 3 ]

3 голосов
/ 28 марта 2012

В исходной области видимости и решении amitamb есть скрытая ошибка.scope - это метод класса, так почему вы говорите так:

scope :blahblah, arguments

выражение arguments вычисляется во время анализа и загрузки класса.В частности, DateTime.now.year будет оцениваться, когда класс загружается в среду Rails.Следовательно, если класс загружен 2012-12-31, то where будет:

where('days.year_id != 2012')

, и если вы используете область спустя несколько часов 2013-01-01, он будетпо-прежнему использовать 2012 год как год.Есть два решения этой проблемы:

  1. Используйте метод класса или лямбду для области:

    scope :archived, -> { joins(:day).where('days.year_id != ?', DateTime.now.year) }
    # or
    def self.archived
        joins(:day).where('days.year_id != ?', DateTime.now.year)
    end
    
  2. Нажмите расчет текущего годавниз в базу данных:

    scope :archived, joins(:day).where('days.year_id != extract(year from current_date)')
    

Некоторые базы данных захотят что-то вместо extract(year from current_date), поэтому вы можете использовать (1) , чтобы избежать возможногопроблемы переносимости и часового пояса.

Кроме того, ваш оригинальный подход испытывает аналогичные проблемы с частью Day.where(...), этот запрос выполняется во время загрузки вашего класса, поэтому, если таблица days изменяется во время работы вашего приложения,тогда вы будете проверять неправильный список.

0 голосов
/ 28 марта 2012

Для простого переписывания вы можете сделать что-то вроде следующего:

scope :archived, where( :day_id => Day.where("year_id != #{DateTime.now.year}").collect{ |d| d.id }

Но это сгенерирует два запроса и ненужное соединение из вашего кода rails. В идеале вы должны сделать это соединение через SQL и выполнить проверку только в одном запросе.

Таким образом, полученная модель должна выглядеть примерно так:

class Model

  belongs_to :day

  scope :archieved, joins(:day).where("days.year_id != #{DateTime.now.year}")

end

См. Ниже для получения дополнительной информации

http://guides.rubyonrails.org/active_record_querying.html#specifying-conditions-on-the-joined-tables

0 голосов
/ 27 марта 2012

Можете ли вы сделать что-то подобное?

your_logic = "year_id != #{DateTime.now.year}".collect{ |d| d.id }.join(",")}
scope :archived, :conditions => Day.where(your_logic)["day_id"]

Я просто заменил day_id IN, сначала получив доступ к объекту, затем вытянув day_id.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...