Rails – 多索引密钥关联

似乎有许多方法可以处理多个外键关联。 我接触过它的每一种方式都有它们的缺点,而且我是Rails的新手,我确信其他人遇到了类似的情况,我可能正在研究很久以前解决的问题。

我的问题是:

什么是处理多索引键关联的有效方法,同时仍保留所有其他Rails sql修饰符(例如:include等)?

我的情景是:

我有一个表关联如下(简化),用于通过链接将人们连接到其他人:

People +----+-----------+ | id | name | +----+-----------+ | 1 | Joe | +----+-----------+ | 2 | Sally | +----+-----------+ | 3 | Bob | +----+-----------+ Links +----+-----------+---------+ | id | origin_id | rcvd_id | +----+-----------+---------+ | 1 | 2 | 1 | +----+-----------+---------+ | 2 | 1 | 3 | +----+-----------+---------+ | 3 | 3 | 2 | +----+-----------+---------+ 

从上面的Links表的第1行,可以看到Person(Sally = 2)链接到另一个Person(Joe = 1)。

如果我的外键是“origin_id”,我很容易找到所有的人员链接。 但这只会显示人们发起链接。 在我的场景中,我需要查看所有链接,无论它们是由人发起还是接收。 例如,如果我要求所有Sally的链接(Sally = 2),我想要的结果是:

 Links +----+-----------+---------+ | id | origin_id | rcvd_id | +----+-----------+---------+ | 1 | 2 | 1 | +----+-----------+---------+ | 3 | 3 | 2 | +----+-----------+---------+ 

因此我有2个索引键,“origin_id”和“rcvd_id”。

可以解决的一种方法是使用方法:

 class Person  "Link", :foreign_key => :origin_id, :dependent => :destroy has_many :link_rcvds, :class_name => "Link", :foreign_key => :rcvd_id, :dependent => :destroy def links origin_person + rcvd_person end 

但是,这效率不高。 例如,这需要从数据库中收集整个集合,然后才能使用paginate方法(我正在使用will_paginate gem),这会使得分页失败,因为paginate应该通过限制调用的记录数来加速进程。 整个集合完成后不限制记录。

此外,以上内容不允许我调用例如Joe.links(:first).origin_id.name。 不完全是这段代码但意味着我无法在所选链接的origin_id上调用Person详细信息,因为links方法不知道origin_id与People表相关。

到目前为止,最可行的解决方案似乎是:finder_sql。

 class Person  'SELECT * FROM links WHERE (links.origin_id = #{id} or links.rcvd_id = #{id})' 

这给出了Person_id与Links.origin_id或Links.rcvd_id匹配的所有链接。

这个选项的缺点是使用:finder_sql,排除所有其他sql修饰符,因为Rails不知道如何解析和修改你提供的SQL。 例如,我无法使用:include选项和:finder_sql。

所以,现在我正在使用:finder_sql,解决方案。 但似乎可能会以这样的方式完成这种关联,以至于我不需要:finder_sql。 例如,有没有办法编写自定义sql字符串,同时保留Active Record提供的Rails sql修饰符。

关于上述的任何想法?

我确实找到了解决方案,但事实certificate我可能会问错误的问题。 我还没有发现有多个索引键,因为我没有实现一些自定义的sql打破了不同的rails助手。

我想我的问题仍然存在,但我如何解决这个问题就是以不同的方式看问题。 我刚刚创建了关联:

 belongs_to :rcvd_person, :class_name => 'Person', :foreign_key => :rcvd_id belongs_to :origin_person, :class_name => 'Person', :foreign_key => :origin_id 

和自定义的sql语句:

 class Person... has_many :links, :finder_sql => 'SELECT * FROM links WHERE origin_id = #{id} OR rcvd_id = #{id}' end 

然后我能够在我的视图中操纵记录。 如果其他人正在做类似的事情,我做了:

 <% person.links.each do |link| %> <% if link.origin_id == person.id %> <%= link.rcvd_person.given_name %> <% else %> <%= link.origin_person.given_name %> <% end %> <% end %> 

我不确定你是否真的可以支持同一个表中多个键的关联,因为如果你试图创建一个关系,Rails将不知道要设置哪个键。

但是,如果你只想要person.links,Rails 3提供了一种比finder_sql更好的方法

 class Link def self.by_person(person) where("origin_id => :person_id OR rcvd_id => :person_id", :person_id => person.id end end class Person < ActiveRecord::Base # You can include the has_many relationships if you want, or not def links Link.by_person(self) end end 

这可以让你做像@ person.links.limit(3)这样的东西(使用时目前似乎被破坏了:finder_sql)