简体   繁体   English

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

[英]Rails 3. How to find by calculated virtual attribute?

I have a dynamically calculated virtual attribute called estimated_time_of_arrival... 我有一个动态计算的虚拟属性,称为estimate_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

I need to calculate the late shipments like this... 我需要这样计算延迟发货...

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

But that would produce an error "no such column estimated_time_of_arrival" 但这会产生错误“没有这样的列estimate_time_of_arrival”

How could I make a query like that work? 我如何进行类似的查询?

----UPDATE---- ----更新----

Ok, so I added a estimated_transit_time column to the shipments table so I won't have to be looking at the transit_time on the places column. 好的,所以我在出货量表中添加了一个Estimated_transit_time列,这样就不必在places列上查看transit_time。 So the function would now be... 所以现在的功能是...

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

and I tried this 我尝试了这个

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

and get error 并得到错误

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

Something like this ought to be equivalent: 像这样的东西应该是等​​效的:

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
  "

You could instead do this in a scope, which is a bit more "Railsy" (and highly recommended): 相反,您可以在一个范围更大的“ 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:...>, ... ]

Bonus 奖金

Does places have an id key? placesid键吗? Rather than having shipments.place_of_loading correspond to places.city you should have shipments.place_of_loading_id correspond to places.id , which would allow you to have models like the following: 而不是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

In addition to making the scope simpler, it will let you do things like: 除了简化scope ,它还可以让您执行以下操作:

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

Just a thought. 只是一个想法。 ;) ;)

Building on Jordans answer: add a class method to your model 基于Jordans的答案:向您的模型添加类方法

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

now all your controllers and other models can just use 现在您所有的控制器和其他型号都可以使用

Shipment.will_arrive_within(7)

and nobody has to know about how you implemented this. 而且没有人知道您如何实施此操作。

see the railscast 215: Advanced Queries in Rails 3 参见railscast 215:Rails 3中的高级查询

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM