首页 > 解决方案 > Rails - 如何在哪里使用自定义属性?

问题描述

我有 Order 模型,其中我有 datetime 列start和 int 列到达_dur 、drop_off_dur等。它们是从开始开始的持续时间(以秒为单位)

然后在我的模型中我有

class Order < ApplicationRecord
  def finish_time
    self.start + self.arriving_duration + self.drop_off_duration
  end
  # other  def something_time ... end
end

我希望能够做到这一点:

Order.where(finish_time: Time.now..(Time.now+2.hours) )

但是我当然不能,因为没有finish_time这样的专栏。我怎样才能达到这样的结果?

我在 SA 上阅读了 4 种可能的解决方案:

你有任何想法或一些“最佳实践”如何解决这个问题吗?谢谢!

标签: ruby-on-rails

解决方案


您有不同的选择来实现此行为。

  1. 添加一个附加finish_time列并在您更新/创建时间值时更新它。这可以在 rails 中完成(使用before_validationafter_save回调)或作为 psql 触发器。

    class Order < ApplicationRecord
      before_validation :update_finish_time
    
      private
      def update_finish_time
        self.finish_time = start_time + arriving_duration.seconds + drop_off_duration.seconds
      end
    end
    

    finish_time当您在整个应用程序的许多地方都需要时,这尤其有用。它的缺点是您需要使用额外的代码来管理该列,并且它存储您实际已经拥有的数据。好处是,如果您有很多订单并需要对其进行搜索,您可以轻松地在该列上创建索引。

    一个选项可能是将完成时间更新实现为 postgresql 触发器而不是在 rails 中。这样做的好处是独立于您的 Rails 应用程序(例如,当其他源/脚本也访问您的数据库时),但缺点是将您的业务逻辑拆分到许多地方(ruby 代码、postgres 代码)。

  2. 您的第二个选项是为您的查询添加一个虚拟列。

    def orders_within_the_next_2_hours
      finishing_orders = Order.select("*, (start_time + (arriving_duration + drop_off_duration) * interval '1 second') AS finish_time")
      Order.from("(#{finishing_orders.to_sql}) AS orders").where(finish_time: Time.now..(Time.now+2.hours) )
    end
    

    上面的代码创建了带有附加列finishing_order的表的 SQL 查询。在第二行中,我们将该SQL 用作 FROM 子句(“聪明地”别名为所以 rails 很高兴)。这样我们就可以像普通列一样查询。orderfinish_timefinishing_ordersordersfinish_time

    SQL 是为相对较旧的 postgresql 版本编写的(我猜它适用于 9.3+)。如果您使用make_interval而不是与 SQL 相乘,interval '1 second'可能会更具可读性(但需要更新的 postgresql 版本,我认为 9.4+)。


推荐阅读