7

我有以下型号。用户有 UserAction,一个可能的 UserAction 可以是 ContactAction(UserAction 是一种多态)。还有其他操作,例如 LoginAction 等。所以

类用户 < AR::Base
  has_many :contact_requests, :class_name => "ContactAction"
  has_many :user_actions
  has_many_polymorphs :user_actionables, :from => [:contact_actions, ...], :through => :user_actions
 结尾

类 UserAction < AR::Base
 属于_to:用户
 belongs_to :user_actionable, :polymorphic => true
结尾

类 ContactAction < AR::Base
 属于_to:用户
 命名范围:待定,...
 命名范围:活动,...
结尾

这个想法是一个 ContactAction 加入两个用户(在应用程序中具有其他后果)并且总是有一个接收端和一个发送端。同时,一个 ContactAction 可以有不同的状态,例如过期、待处理等。

我可以说@user.contact_actions.pending@user.contact_requests.expired列出用户发送或接收的所有待处理/过期请求。这工作正常。

我现在想要的是一种加入这两种类型的 ContactAction 的方法。即@user.contact_actions_or_requests。我尝试了以下方法:

类用户

 def contact_actions_or_requests
  self.contact_actions + self.contact_requests
 结尾

 # 或者
 has_many :contact_actions_or_requests, :finder_sql => ..., :counter_sql => ...

结尾

但所有这些都有一个问题,即不可能在关联之上使用额外的查找器或命名范围,例如@user.contact_actions_or_requests.find(...)@user.contact_actions_or_requests.expired

基本上,我需要一种方法来表达具有两条不同路径的 1:n 关联。一个是User -> ContactAction.user_id,另一个是User -> UserAction.user_id -> UserAction.user_actionable_id -> ContactAction.id。然后将结果 (ContactActions) 加入一个列表中,以便使用 named_scopes 和/或查找器进行进一步处理。

由于我在几十个地方都需要这种关联,因此为每种情况编写(和维护!)自定义 SQL 将是一个很大的麻烦。

我更愿意在 Rails 中解决这个问题,但我也愿意接受其他建议(例如 PostgreSQL 8.3 过程或类似的东西)。重要的是,最后,我可以像使用任何其他关联一样使用 Rails 的便利功能,更重要的是,还可以嵌套它们。

任何想法将不胜感激。

谢谢!


为我自己的问题提供某种答案:

我可能会使用数据库视图来解决这个问题,并根据需要添加适当的关联。对于以上,我可以

  • 使用 finder_sql 中的 SQL 创建视图,
  • 将其命名为“contact_actions_or_requests”,
  • 修改 SELECT 子句以添加 user_id 列,
  • 添加一个app/models/ContactActionsOrRequests.rb,
  • 然后将“has_many :contact_actions_or_requests”添加到 user.rb。

我还不知道我将如何处理更新记录——这似乎是不可能的——但也许这是第一次开始。

4

1 回答 1

2

您正在寻找的方法是合并。如果你有两个 ActiveRecord::Relations,r1 和 r2,你可以调用 r1.merge(r2) 来获得一个新的 ActiveRecord::Relation 对象,它将两者结合起来。

这是否对您有用,很大程度上取决于您的范围是如何设置的,以及您是否可以更改它们以产生有意义的结果。让我们看几个例子:

假设您有一个 Page 模型。它有正常的 created_at 和 updated_at 属性,所以我们可以有这样的范围: :updated -> { where('created_at != updated_at') } :not_updated -> { where('created_at = updated_at') }

如果你把它从数据库中拉出来,你会得到:

r1 = Page.updated # SELECT `pages`.* FROM `pages` WHERE (created_at != updated_at)
r2 = Page.not_updated # SELECT `pages`.* FROM `pages` WHERE (created_at = updated_at)
r1.merge(r2) # SELECT `pages`.* FROM `pages` WHERE (created_at != updated_at) AND (created_at = updated_at)
=> []

所以它确实结合了这两种关系,但没有以一种有意义的方式。另一个:

r1 = Page.where( :name => "Test1" ) # SELECT `pages`.* FROM `pages` WHERE `pages`.`name` = 'Test1'
r2 = Page.where( :name => "Test2" ) # SELECT `pages`.* FROM `pages` WHERE `pages`.`name` = 'Test2'
r1.merge(r2) # SELECT `pages`.* FROM `pages` WHERE `pages`.`name` = 'Test2'

因此,它可能对您有用,但可能不适用,具体取决于您的情况。

另一种推荐的方法是在您的模型上创建一个新范围:

class ContactAction < AR::Base
  belongs_to :user
  scope :pending, ...
  scope :active, ...
  scope :actions_and_requests, pending.active # Combine existing logic
  scope :actions_and_requests, -> { ... } # Or, write a new scope with custom logic
end

这结合了您想要在一个查询中收集的不同特征......

于 2013-01-08T09:41:50.683 回答