【问题标题】:How do you concatenate two active record results to return a new result that can further be filtered?如何连接两个活动记录结果以返回可以进一步过滤的新结果?
【发布时间】:2011-09-15 21:52:57
【问题描述】:

想象一下场景......

#models/user.rb
class User < ActiveRecord::Base
    has_many :accounts, :conditions => { :active => 1 }
end

#models/account.rb
class Account < ActiveRecord::Base
    belongs_to :user

    def public_accounts
        Account.all :conditions => { public => true }
    end
end

现在想象一下,我想将 User(:id).accounts 与 Account.public_accounts 连接起来,以显示用户可用的所有帐户的列表。

所以您认为我可以将 User 模型更新为如下所示。

#models/user.rb
class User < ActiveRecord::Base
    has_many :accounts, :conditions => { :active => 1 }

    def all_accounts
        self.accounts + Account.public
    end
end

但是,现在我将无法使用 all() 方法,因为它不再属于那种类型的对象。

在控制器中我想这样做...

#controllers/accounts_controller.rb
def search_all
    User.find(params[:user_id]).all_accounts.all(
        :offset => params[:offset],
        :limit => params[:limit]
    )
end

想法?

更新#1: 范围不适用于我的场景。我简化了我的场景以试图表达我的观点。如前所述,我需要一种方法来组合两个活动记录结果并保留在我的控制器中进一步过滤它们的能力。

所以问题是,“为什么?”原因是,我试图将两组记录组合成一个完整的集合,其中一个集合根本与用户无关。

我已经重构了上面的场景,试图展示一个更精确的例子,而不会变得过于复杂。

【问题讨论】:

  • 你添加的方法是all_accounts,为什么不能使用all
  • KandadaBoggu,试试看。它不起作用。

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


【解决方案1】:

这可能是使用scopes 的好场景。

您可以在 Account 模型中定义活动和非活动范围,然后使用以下内容:

User.accounts
User.accounts.active
User.accounts.inactive

您甚至可以将范围链接在一起,因此您可以执行以下操作:

User.accounts.active.paid_up

【讨论】:

  • 我的问题是并且仍然是如何连接到活动记录结果。由于没有回答,我对我的场景进行了修改,以尝试更接近为什么我“相信”我需要这样做。谢谢,但如果可以的话,我会使用范围。
【解决方案2】:

jklina 的answer 是正确的,在这种情况下最好使用作用域。范围提供了一种含糖的语法并且更具可读性。我将详细说明设置:

class User < AR::Base
  has_many :accounts
end

class Account < AR::Base
  belongs_to :user

  scope :active, where(:active => true)
  scope :inactive, where(:active => false)
end

然后您将访问帐户范围,如 jklina 所示:User.find(1).accounts.active 等。 访问用户的所有帐户,例如:User.find(1).accounts

更新:

我修正了一些错误并在下面添加了更多内容。

根据您对问题的更新,我认为您需要将public 方法设为类上的方法:

class Accounts < AR::Base
  ...
  # This is essentially a scope anyways
  def self.public
    where(:public => true)
  end
end

class User < AR::Base
  ...
  # This should return all the users accounts
  # and any public accounts
  def all_accounts
    Account.where("user_id = ? OR public is true", self.id)
  end
end

【讨论】:

  • 我什至没有考虑在用户中做一个地方。这是一个好主意,不幸的是,“公开是真的”似乎行不通。当我尝试这样做时会引发错误
  • 我会尝试回去做那件事。我真的了解了自我。执行此操作后在方法上声明。这可能就是缺少的一切。
  • 重要提示,在 Rails 4 范围内需要符号和 lambda,这也支持 il rails 3,所以我建议使用 scope :active, -&gt; { where(active: true) } 来保持它的工作(并且它与更复杂的范围更一致) )。
【解决方案3】:

让我们看看链中的返回值:

User.find(params[:user_id]) # returns an instance of User class
User.find(params[:user_id]).all_accounts # returns an array

Array 类没有名为all 的实例方法,这就是您看到此错误的原因。这不是错误。

你为什么不试试这个:

class User
  has_many :accounts, :conditions => { :active => 1 }
  has_many :all_accounts :conditions => ["(active = ? OR public = ?)", 
                           true, true]
end

现在你可以:

User.find(params[:user_id]).all_accounts.all(:limit => 10, :offset => 2)

【讨论】:

  • 更新了我的答案看看。
  • 那行不通。它不是一个活跃的 OR 公众。这是 AccountID = user.AccountID 或 public。
【解决方案4】:

您尝试访问两个不同的表并将 LIMIT/OFFSET 作为组合联合应用于它们。除非您在 SQL 层而不是在 ActiveRecord 层逻辑地组合它们,否则这种情况不会发生。

听起来像是写出 SQL,也许使用 UNION 然后使用 find_by_sql 可能是最好的。

【讨论】:

  • 似乎是这样。我讨厌那样做……宁可在模型中有逻辑。
  • 然后你也许可以编写一个 SQL 视图来封装你的查询并使它看起来好像它是一个你的模型可以执行的简单查询
猜你喜欢
  • 2012-01-03
  • 1970-01-01
  • 2016-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-26
  • 2011-02-08
  • 1970-01-01
相关资源
最近更新 更多