6

我有以下模型。用户有UserAction,一个可能的UserAction可以是一个ContactAction(UserAction是一个多态)。有喜欢的LoginAction等其他行动,让Ruby on Rails 3:结合多个has_many或has_many_through关联的结果

 
class User < AR::Base 
    has_many :contact_requests, :class_name => "ContactAction" 
    has_many :user_actions 
    has_many_polymorphs :user_actionables, :from => [:contact_actions, ...], :through => :user_actions 
end 

class UserAction < AR::Base 
belongs_to :user 
belongs_to :user_actionable, :polymorphic => true 
end 

class ContactAction < AR::Base 
belongs_to :user 
named_scope :pending, ... 
named_scope :active, ... 
end 

的想法是,一个ContactAction连接两个用户(以及其他后果的应用程序内),总是有一个接收和发送端。同时,ContactAction可以具有不同的状态,例如,已过期,待处理等。

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

我现在想要的是一种方式,以加入两种类型的ContactAction。即@user.contact_actions_or_requests。我试过如下:

 
class User 

def contact_actions_or_requests 
    self.contact_actions + self.contact_requests 
end 

# or 
has_many :contact_actions_or_requests, :finder_sql => ..., :counter_sql => ... 

end 

,但所有这些都认为这是不可能使用额外的发现者或named_scopes上的关联,例如顶部的问题@user.contact_actions_or_requests.find(...)@user.contact_actions_or_requests.expired

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

因为我需要这个协会在字面上几十个地方,这将是一个重大的麻烦写(和维护!)自定义SQL的每一个案件。

我宁愿用Rails来解决这个问题,但我也接受其他的建议(如PostgreSQL 8.3版本的程序或东西simliar)。最重要的是,最终,我可以像使用任何其他关联一样使用Rails的便利功能,更重要的是,也可以嵌套它们。

任何想法将是非常赞赏。

谢谢!


为了提供一个排序的答案我自己的问题:

我可能会解决这个使用数据库视图,并根据需要添加相应的关联。针对上述情况,我可以

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

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

+0

目前我应用程序周围散布着find_by_sql调用的混乱。我还没有更新这个使用ARel,尤其是#arel_table方法(请参阅下面的注释)。当我这样做时,我会在这里发布我的结果。 – Jens 2013-01-09 20:24:26

+0

你是如何解决这个问题的? – 2014-11-20 11:02:10

+0

我仍然使用finder_sql和counter_sql,但是将SQL字符串提取到一个类方法中,然后通过proc包含它。这比以前要干净得多,尽管我现在不得不重新考虑我们正在转向Rails 4并且不推荐使用finder_sql。 – Jens 2014-12-18 13:51:56

回答

2

您正在查找的方法是合并。如果你有两个ActiveRecord :: Relations,r1和r2,你可以调用r1。合并(r2)以获得组合这两者的新ActiveRecord :: Relation对象。

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

假设你有一个Page模型。它有正常的created_at和的updated_at属性,所以我们可以有这样的范围: :更新 - > {这里( '!created_at =的updated_at')} :not_updated - > {这里( '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 

,结合你想在一个查询中收集不同的特质......

+0

当我们还在使用没有ARel的Rails 2.3,因此不允许链接find_by_sql调用时,我们询问了原始问题。现在,对于ARel,实际上可以(使用#arel_table)使用“OR”链接查询(在这种情况下这是必需的)。您的解决方案仍然使用“AND”链接,这不是我所需要的... – Jens 2013-01-09 20:23:22

+0

您可能会理解我对“仍在使用Rails 2.3”的困惑,因为问题是使用Rails 3标记的。尽管提供手动组合查询的单独作用域的选项仍然有效,除非存在尚未指定的约束条件? – 2013-01-09 20:32:31

+0

是的。对于那个很抱歉。我正在寻找实际执行升级的动机。 :)关于你的建议,不'pending.active'选择所有正在挂起和活动的条目(而不是“或”)?链接范围 - 以我的经验 - 使用逻辑AND,在此不起作用。使用arel_table(在我的情况下)也很困难,因为我需要在查询中使用Postgres函数,而(afaik)不能用ARel表示。但我会继续努力,一旦找到答案,我会把我的解决方案放在这里。 – Jens 2013-01-11 14:32:49