【问题标题】:Call boolean instance method as a 'scope'将布尔实例方法称为“范围”
【发布时间】:2017-02-16 16:19:56
【问题描述】:

我有模特

Email

还有一个实例方法

def sent_to_user?(user)
  self.who_to == user
end

who_to 是另一个实例方法,它执行一些复杂的检查。

那里有更多的东西在后台发生,所以我不能轻易地将它变成一个 activerecord 查询。

我想做类似的事情:

scope :sent_to_user, -> (user) { sent_to_user?(user)} 

@user.emails.sent_to_user

只返回那些为“sent_to_user”返回 true 的电子邮件?

试过了

scope :sent_to_user, -> (user) { if sent_to_user?(user)} 

....等等。

不太确定如何构建该范围/类方法

【问题讨论】:

  • 不应该是to_whom吗?
  • 好的。这让我笑了... :)

标签: ruby-on-rails rails-activerecord


【解决方案1】:

您不能(至少不应该)以这种方式使用作用域。范围用于返回 ActiveRecord 关系,附加范围可以链接到这些关系上。如果要使用范围,则应生成必要的 SQL 以在数据库中执行过滤。

如果你想在 Ruby 中过滤结果并返回一个数组,你应该使用一个类级别的方法,而不是一个作用域:

class Email < ActiveRecord::Base
  def self.sent_to_user(user)
    select { |record| record.sent_to_user?(user) }
  end
end

【讨论】:

  • 我收集到了。我已经尝试了一些类方法的变体。猜猜主要的一点是,如果我将它链接起来,它需要放在范围链的末端。
  • @Carpela 是的,或者您可以重现在数据库中执行过滤所涉及的任何逻辑,这通常是更好的选择。
  • 在这种情况下并不容易,因为我正在使用 Mail gem 从文本中解析不同类型的电子邮件地址。不确定 Postgres 是否有一个插件...
【解决方案2】:

你应该在 ActiveRecord 逻辑中写 who_to,比如

范围 :sent_to_user, -> (user) { joins(:recipient)where(recipient_id: user.id)}

【讨论】:

  • who_to 不是一个关联。它比这更复杂一些。 (由于应用程序中的其他限制)
【解决方案3】:

假设用户 has_many :emails 您可以执行以下操作:

class User < ActiveRecord::Base
  has_many :emails

  # ...

  def emails_sent_to_user
    emails.select { |e| e.sent_to_user?(self) }
  end
end

【讨论】:

    【解决方案4】:

    有趣的是,我想出了一种方法来进行检查,这有点小技巧,但在这种情况下有效,可能对某人有用。 公认的解决方案的问题是,尽管它肯定是基于我描述的限制的唯一方法,但性能可能会严重缓慢。

    我现在正在使用:

    scope :sent_to_user, -> (user) {"json_email -> 'to' ILIKE ?", "%#{user.email)}%"}
    

    其中“json_email”是解析为 json 的电子邮件对象(这是我们将它们存储在数据库中的方式)。这减少了使用 Mail gem 的需要并显着提高了性能。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-06-14
      • 2015-11-15
      • 1970-01-01
      • 2014-11-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多