Rails 3.如何通过计算的虚拟属性查找?

时间:2012-01-17 21:25:43

标签: ruby-on-rails ruby activerecord

我有一个名为estimated_time_of_arrival ...

的动态计算虚拟属性
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)

但是这会产生错误“没有这样的列估计_time_of_arrival”

我怎样才能进行类似的查询?

---- ---- UPDATE

好的,所以我在shipment表中添加了一个estimated_transit_time列,因此我不必查看places列上的transit_time。所以函数现在是......

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 个答案:

答案 0 :(得分:1)

这样的事情应该是等价的:

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与[{1}}对应,而不是让places.cityshipments.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

只是一个想法。 ;)

答案 1 :(得分:0)

以Jordans为基础回答:为模型添加一个类方法

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

现在所有的控制器和其他型号都可以使用

Shipment.will_arrive_within(7)

并且没有人知道你是如何实现这一点的。

请参阅railscast 215: Advanced Queries in Rails 3