Rails范围:使用类方法而不是db列查找记录

时间:2016-01-09 09:30:42

标签: ruby-on-rails activerecord model-associations

在我的rails 4应用程序中,我有一个名为Property的模型,它具有以下类方法:

def under_contract?
    self.contracts.last && self.contracts.last.accepted? ? true : false
end

这样做是检查财产是否有与之相关的合同,然后检查该合同是否已被接受。

我想为租用的属性创建一个范围(如果你在它们上使用类方法under_contract?它将返回true)。以下是我尝试做到的事情:

scope :rented, -> {where(under_contract?: true)}

问题是under_contract不是数据库中的一个列,它只是一个类方法,所以我得到一个错误说"没有这样的列"。

我接近这个完全错误还是我只是错过了一些小事?

3 个答案:

答案 0 :(得分:1)

  

类方法

这是一个instance method - 如果它是类,它将是def self.under_contract?

-

  

我接近这个完全错误的

上下文

首先,scope与类方法相同;它初始化类的新实例以返回您的数据。实例方法对已经调用的类执行操作:

#app/models/property.rb
class Property < ActiveRecord::Base
   def under_contract? #-> instance method
      ...
   end

   def self.under_contract #-> class method
      where under_contract: true 
   end
end

以上可以使用如下:

@property = Property.find x
@property.under_contract? #-> instance method

@properties = Property.under_contract #-> class method
@properties.each do |property|
    property.under_contract?
end

差异很微妙但很重要。它是你问题的根源。

-

其次,您不能混合使用类和实例方法。它们的范围完全不同;你不能在类方法上调用实例方法。

您不能将scope与已调用的对象一起使用。你必须要么调用对象&amp;使用实例方法,或使用范围最初调用所需的对象。

修复

这样做:

#app/models/property.rb
class Property < ActiveRecord::Base
   has_many :contracts
   scope :rented, -> { joins(:contracts).where(accepted: true) }

   def under_contract?
      self.contracts.any? && self.contracts.exists(accepted: true)  #-> returns true / false
   end
end

这为您提供了范围,用于从db中提取rented属性:

@properties = Property.rented

...以及确定是否已租用特定属性的实例方法:

@property = Property.find x
@property.under_contract?

答案 1 :(得分:1)

感谢所有帮助。我用下面的代码解决了我的具体问题。

def under_contract
  self.contracts.any? && self.contracts.last.accepted?
end

scope :rented, -> {select { |p| p.under_contract == true}}

答案 2 :(得分:0)

我不知道您对合同的接受?方法看起来像是假设您在合同中有接受:布尔列,那么您可以使用像这样的包含来构建您的范围...

scope :rented, -> { includes(:contracts).where.not(contracts: { property_id: nil }).where(contracts: {accepted: true}) }