Rails 3. Как найти по рассчитанному виртуальному атрибуту? - PullRequest
1 голос
/ 18 января 2012

У меня есть динамически вычисляемый виртуальный атрибут с именем оцененным временем_фрагмента ...

shipment.rb
def estimated_time_of_arrival
  if self.etd_origin.nil?
    "N/A: Please enter ETD Origin to calculate ETA Place of Delivery"
  else
    if self.mode == "Air"
      self.etd_origin + 7.days
    else
      self.etd_origin + (Place.find_by_city(self.place_of_loading).transit_time).days
    end
  end
end

Мне нужно рассчитать поздние поставки, как это ...

Shipment.where('estimated_time_of_arrival < ?', Date.today)

Но это привело бы к ошибке "нет такого столбца оцененного_времени_арривала"

Как я могу сделать такой запрос?

---- UPDATE ----

Хорошо, поэтому я добавил столбец оценочный_транзит_тим в таблицу отгрузок, чтобы мне не пришлось смотреть на транзитное время в столбце мест. Так что функция теперь будет ...

shipment.rb
def estimated_time_of_arrival
  if self.etd_origin.nil?
    "N/A: Please enter ETD Origin to calculate ETA Place of Delivery"
  else
    if self.mode == "Air"
      self.etd_origin + 7.days
    else
      self.etd_origin + (self.estimated_transit_time).days
    end
  end
end

и я попробовал это

AirTransitDays = 7
  scope :late, lambda {
    where( %"( etd_origin +
      INTERVAL ( CASE mode
        WHEN 'Air' THEN ?
        ELSE estimated_transit_time
        END
      ) DAYS
    ) < CURRENT_DATE
   ",
   AirTransitDays)
  }

и получите ошибку

ActiveRecord::StatementInvalid: SQLite3::SQLException: near "DAYS": syntax error: SELECT "shipments".* FROM "shipments"  WHERE (( etd_origin +
   INTERVAL ( CASE mode
     WHEN 'Air' THEN 7
     ELSE estimated_transit_time
     END
   ) DAYS
 ) < CURRENT_DATE
) ORDER BY file_number

Ответы [ 2 ]

1 голос
/ 18 января 2012

Примерно так должно быть эквивалентно:

Shipment.
  # This JOIN may be slow with many records if `places.city` isn't
  # indexed--see "Bonus" below
  joins('JOIN places ON shipments.place_of_loading = places.city').
  where %"( etd_origin +
            INTERVAL ( CASE mode
                         WHEN 'Air' THEN 7
                         ELSE places.transit_time
                        END
            ) DAYS
         ) < CURRENT_DATE
  "

Вместо этого вы можете сделать это в области видимости, которая немного более "Railsy" (и настоятельно рекомендуется):

class Shipment < ActiveRecord::Base
  AirTransitDays = 7

  scope :late, lambda {
    joins( 'JOIN places ON shipments.place_of_loading = places.city' ).
    where( %"( etd_origin +
               INTERVAL ( CASE mode
                            WHEN 'Air' THEN ?
                            ELSE places.transit_time
                          END
               ) DAYS
             ) < CURRENT_DATE
      ",
      AirTransitDays
    )
  }

end

# Usage:
Shipment.late.all # => [ #<Shipment id:...>, $<Shipment id:...>, ... ]

Бонус

Имеет ли places ключ id? Вместо того, чтобы shipments.place_of_loading соответствовал places.city, вы должны иметь shipments.place_of_loading_id, соответствующий places.id, что позволило бы иметь модели, подобные следующим:

class Place < ActiveRecord::Base
  has_many :shipments
end

class Shipment < ActiveRecord::Base
  AirTransitDays = 7

  belongs_to :place_of_loading, :class_name => 'Shipment'

  scope :late, lambda {|origin|
    joins( :place_of_loading ).  # <= See? Simpler.
    where( ... )
  }

end

В дополнение к упрощению scope он позволяет выполнять такие действия, как:

@shipment = Shipment.include( :place_of_loading ).where(...).first
@shipment.place_of_loading.city  # => Chicago, IL

Просто мысль. ;)

0 голосов
/ 18 января 2012

Опираясь на ответ Джордана: добавьте метод класса к вашей модели

def self.will_arrive_within(days)
  where(' ... nifty sql here ... ')
end

теперь все ваши контроллеры и другие модели могут просто использовать

Shipment.will_arrive_within(7)

, и никто не должен знать о том, какВы реализовали это.

см. railscast 215: Расширенные запросы в Rails 3

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