Ruby on Rails 3:结合多个has_many或has_many_through关联的结果

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

  class User “ContactAction”
   has_many:user_actions
   has_many_polymorphs:user_actionables,:from => [:contact_actions,...],:through =>:user_actions
 结束

 class UserAction  true
结束

 class ContactAction <AR :: Base
  belongs_to:用户
  named_scope:pending,...
  named_scope:active,...
结束

这个想法是一个ContactAction加入两个用户(在app中有其他后果),并且总是有一个接收端和一个发送端。 同时,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 => ...

结束

但是所有这些都存在这样的问题:在关联之上不可能使用额外的finder或named_scope,例如@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和/或finders进行进一步处理。

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

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

任何想法将非常感谢。

谢谢!


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

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

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

我不知道我将如何处理更新记录 – 这似乎不可能有一个视图 – 但也许这是第一次开始。

您正在寻找的方法是合并。 如果你有两个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 

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