如何在方法而不是数据库列上使用where子句?

时间:2014-01-22 21:57:49

标签: ruby-on-rails ruby sqlite postgresql activerecord

我经常在Rails遇到的一个问题是:

假设我有invoices表格,其中包含datedays列。

如何检索到期的所有发票?

class Invoice < ActiveRecord::Base

  def self.due
    where("due_date > ?", Date.today) # this doesn't work because there is no database column "due_date"
  end

  private 

  def due_date
    date + days
  end

end

任何人都可以告诉我如何在不必将数据库列due_date添加到我的发票表的情况下执行此操作吗?

感谢您的帮助。

2 个答案:

答案 0 :(得分:2)

在PostgreSQL中,adding an integer to a date添加了许多天:

date '2001-09-28' + integer '7' = date '2001-10-05'

所以你可以简单地说:

where('due_date + days > :today', :today => Date.today)

但是,SQLite根本没有date类型,它将日期存储为ISO 8601字符串。这意味着在日期中添加一个数字将最终连接字符串,这有点无用。 SQLite确实有一个date function

  

日期(时间字符串,修饰符,修饰符,...)
  [...]
  所有五个日期和时间函数都将时间字符串作为参数。时间字符串后跟零个或多个修饰符。

所以你可以说像date('2014-01-22', '+ 11 days')之类的东西来做日期算术。这让你有了这个:

where("date(due_date, '+' || days || ' days') > :today", :today => Date.today)

值得庆幸的是,ISO 8601日期字符串可以正确地比较为字符串,因此>仍可正常工作。

现在你遇到了同一个简单查询的两个版本。您可以检查self.connection区分dev / SQLite和production / PostgreSQL的方式,或者查看Rails.env.production?。这当然会在您的测试套件中留下一个漏洞。

我认为如果你打算在PostgreSQL之上进行部署,你应该停止在SQLite之上开发,你现在应该这样做,以尽量减少痛苦和痛苦。事实是,任何非平凡的应用程序都将与您在生产中使用的数据库结合在一起,或者您将不得不花费大量精力(包括针对您使用的所有不同数据库运行测试套件)来维护数据库的可移植性。数据库独立性在理论上是一个好主意,但完全不切实际,除非有人准备承担这种独立性所需的非平凡成本(时间和财富)。除非您的应用程序是另一个“15分钟博客”玩具,否则ORM不会保护您免受数据库之间的差异。

答案 1 :(得分:1)

您可以执行以下操作:

class Invoice < ActiveRecord::Base

  def self.due
    Invoice.all.select { |invoice| invoice.due_date > Date.today }
  end

  private 

  def due_date
    date + days
  end

end