2012-06-20 42 views
6

使用STI時,當從rails3的has_many關聯中獲取集合時,出現了一些奇怪的行爲。我有:Rails STI與小類的關聯

class Branch < ActiveRecord::Base 
    has_many :employees, class_name: 'User::Employee' 
    has_many :admins, class_name: 'User::BranchAdmin' 
end 

class User < ActiveRecord::Base 
end 

class User::Employee < User 
    belongs_to :branch 
end 

class User::BranchAdmin < User::Employee 
end 

期望的行爲是branch.employees返回所有僱員,包括分支機構的管理員。分支管理員似乎只是當他們被branch.admins訪問該集合下被「裝」,這是從控制檯輸出:

Branch.first.employees.count 
=> 2 

Branch.first.admins.count 
=> 1 

Branch.first.employees.count 
=> 3 

這可以在生成的SQL中可以看出,第一時間:

SELECT COUNT(*) FROM "users" WHERE "users"."type" IN ('User::Employee') AND "users"."branch_id" = 1 

和第二次:

SELECT COUNT(*) FROM "users" WHERE "users"."type" IN ('User::Employee', 'User::BranchAdmin') AND "users"."branch_id" = 1 

我可以只指定解決這個問題:

class Branch < ActiveRecord::Base 
    has_many :employees, class_name: 'User' 
    has_many :admins, class_name: 'User::BranchAdmin' 
end 

,因爲他們都從他們的branch_id找到,但如果我想要做branch.employees.build那麼類將默認爲User,我必須在類型列某處破解這會在控制器的問題。我已經解決了這個問題:

has_many :employees, class_name: 'User::Employee', 
    finder_sql: Proc.new{ 
     %Q(SELECT users.* FROM users WHERE users.type IN   ('User::Employee','User::BranchAdmin') AND users.branch_id = #{id}) 
    }, 
    counter_sql: Proc.new{ 
     %Q(SELECT COUNT(*) FROM "users" WHERE "users"."type" IN ('User::Employee', 'User::BranchAdmin') AND "users"."branch_id" = #{id}) 
    } 

但我真的想避免這種情況發生,如果可能的話。任何人,任何想法?

編輯:

的finder_sql和counter_sql還沒有真正解決了這個問題對我來說,因爲它似乎是家長協會不使用這一點,所以這organisation.employeeshas_many :employees, through: :branches再次只包括在選擇的User::Employee類。

回答

17

基本上,問題只存在於根據需要加載類的開發環境中。 (在生產中,班級被加載並保持可用。)

由於口譯員還沒有看到AdminsEmployee類型,當您第一次運行Employee.find等調用時,問題出現了。

(請注意,它以後使用IN ('User::Employee', 'User::BranchAdmin')

這種情況與每一位使用那些不止一個級別深度模型類的,但只有在開發模式。

子類始終自動加載其父層次結構。基類不會自動加載他們的子級。

哈克修復:

您可以通過顯式地從基類RB文件,要求所有子類迫使開發模式正確的行爲。

+2

這是一個很棒的結果,謝謝。模型結構實際上已經改變了,所以問題消失了,但我不認爲我甚至會認爲它是環境的影響! –

2

你能用:conditions嗎?

class Branch < ActiveRecord::Base 
    has_many :employees, class_name: 'User::Employee', :conditions => {:type => "User::Employee"} 
    has_many :admins, class_name: 'User::BranchAdmin', :conditions => {:type => "User::BranchAdmin"} 
end 

這將是我的首選方法。另一種做法可能是將默認範圍添加到多態模型。

class User::BranchAdmin < User::Employee 
    default_scope where("type = ?", name) 
end 
+0

我嘗試過使用條件,但也遇到了問題。應用程序的結構現在已經改變了,所以我不必擔心這個問題,這可能已經在rails 3.2.7中修復了。 –