【问题标题】:Rails - How to use custom attribute in where?Rails - 如何在哪里使用自定义属性?
【发布时间】:2018-06-25 16:40:36
【问题描述】:

我有 Order 模型,其中我有 datetime 列 start 和 int 列 arriving_durdrop_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 种可能的解决方案:

  • 急切地加载所有订单并使用过滤器选择它 - 如果有更多订单,这将无法正常工作
  • 每次我需要时都有参数化范围,但这意味着代码重复太多
  • 每次都有 sql 函数,并使用 select() 将其绑定到模型 - 这很痛苦
  • 不知何故使用http://api.rubyonrails.org/classes/ActiveRecord/Attributes/ClassMethods.html#method-i-attribute?但我不知道如何将它用于我的案例,或者它是否能解决我遇到的问题。

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

【问题讨论】:

  • 我只希望该字段保留在数据库中(即使它是从其他属性计算出来的),以免以后创建复杂的查询和调试问题。在模型中添加钩子,如果其中一个字段发生更改,则更新列,这应该可以解决问题
  • 这是正确的,请记住,您正在谈论 Order 模型,它可能会被访问​​很多时间,所以有一个复杂的查询会一直被访问,似乎最佳。 @kiddorails cmets 似乎是解决您的问题的最佳方式。
  • 好的,这是有道理的。我会那样做,谢谢@kiddorails!

标签: ruby-on-rails


【解决方案1】:

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

  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 查询,这是order 表和额外的finish_time 列。在第二行中,我们使用 finishing_orders SQL 作为 FROM 子句(“聪明”别名为 orders,所以 rails 很高兴)。这样我们就可以像查询普通列一样查询finish_time

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

【讨论】:

    猜你喜欢
    • 2022-09-27
    • 1970-01-01
    • 2023-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多