2010-08-06 51 views
4

似乎有很多方法可以處理多重外鍵關聯。我接觸過的每種方式都有他們的缺點,而且由於我是Rails的新手,我相信其他人也會遇到類似的情況,我可能正在研究很久以前解決的問題。Rails - 多重索引密鑰關聯

我的問題是:

什麼是處理多索引鍵關聯的有效途徑,同時仍然保留了所有其他的Rails SQL調節劑(如:包括等)?

我的情況是:

我有一個表的關聯如下(簡體),用於通過鏈路連接的人給其他人:

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

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

從上面的鏈接的第1行表中,人們可以看到一個人(Sally = 2)與另一個人(Joe = 1)相關聯。

如果我的外鍵是「origin_id」,我很容易找到所有人員鏈接。但這隻會顯示人們發起鏈接。在我的情況下,我需要查看所有鏈接,無論它們是由某個人發起還是收到。例如,如果我要問所有的Sally的鏈接(莎莉= 2),結果我想應該是:

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

因此,我有2個索引鍵,既「origin_id」和「rcvd_id」。這可以解決

一種方法是使用一個方法:

class Person < ActiveRecord::Base 
    has_many :link_origins, :class_name => "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 

然而,這是沒有效率的。例如,這需要從數據庫中收集整個集合,然後才能使用分頁方法(我使用的是will_paginate gem),因爲paginate應該通過限制所調用記錄的數量來加快進程速度。整個收集完成後不限制記錄。

此外,上述不會允許我打電話給例如Joe.links(:first).origin_id.name。不完全是此代碼,但意味着我無法在選定鏈接的origin_id上調用Person詳細信息,因爲links方法不知道origin_id與People表相關。

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

class Person < ActiveRecord::Base 
    has_many :links, :finder_sql => '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。例如,有沒有辦法在保留Active Record提供的Rails sql修飾符的同時編寫自定義的sql字符串。

以上任何想法?

回答

3

我確實找到了解決辦法,但事實證明我可能會問錯誤的問題。我沒有發現有多個索引鍵,因爲我沒有實現一些自定義的SQL打破不同的鐵路助手。

我想我的問題仍然存在,但我如何解決這個問題是以不同的方式來看問題。我剛剛創建的關聯,因爲它們是:

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 %> 
1

我不知道你能真正支持與多個按鍵關聯在同一個表,因爲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時似乎被破壞了)

+0

謝謝Lelon。還沒有跳過它的Rails3(我知道,尷尬),但會很快看到這一點。 – Oscar 2010-12-06 05:14:15