2015-02-23 76 views
4

渴望加載的has_many關係比方說,我在滑軌連接到使用STI表的關係,如:Rails的4.2:與STI

class Vehicle < ActiveRecord::Base; end 

class Car < Vehicle; end 

class Truck < Vehicle; end 

class Person < ActiveRecord::Base 
    has_many :cars 
    has_many :trucks 
    has_many :vehicles 
end 

...我想加載一個Person及其所有汽車和卡車在一個查詢中。這不起作用:

# Generates three queries 
p = Person.includes([:cars, trucks]).first 

...這是接近的,但這裏沒有運氣:

# Preloads vehicles in one query 
p = Person.includes(:vehicles).first 
# and this has the correct class (Car or Truck) 
p.vehicles.first 
# but this still runs another query 
p.cars 

我可以做這樣的事情在person.rb:

def cars 
    vehicles.find_all { |v| v.is_a? Car } 
end 

但後來Person#cars不再是集合代理,而我集合代理。

有沒有一個優雅的解決方案呢?

編輯:將此添加到人給我我想要的數組與一個查詢項;這真的非常接近我想要的東西:

def vehicle_hash 
    @vehicle_hash ||= vehicles.group_by {|v| 
    v.type.tableize 
    } 
end 

%w(cars trucks).each do |assoc| 
    define_method "#{assoc}_from_hash".to_sym do 
    vehicle_hash[assoc] || [] 
    end 
end 

,現在我能做的Person.first.cars_from_hash(或找到我的非合成用例一個更好的名字)。

回答

2

當您使用includes時,它將那些加載的記錄存儲在association_cache中,您可以在控制檯中查看這些記錄。當您執行p = Person.includes(:vehicles)時,它將這些記錄作爲關鍵字:vehicles下的關聯存儲。它使用你在包含中傳遞它的任何密鑰。

因此,當您撥打p.cars時,會發現它沒有association_cache中的:cars密鑰,因此必須查看它們。它沒有意識到汽車混入了:vehicles鑰匙。

要通過p.vehiclesp.cars訪問緩存的汽車,需要使用這兩個密鑰緩存它們。

它存儲的不僅僅是一個簡單的數組 - 它是一個關係。所以你不能在Hash中手動存儲記錄。

在您提出的解決方案中,我認爲包括每個密鑰可能是最簡單的代碼明智的。 Person.includes(:cars, :trucks) 3如果每個請求只執行一次,SQL語句就不會那麼糟糕。

如果性能是一個問題,我認爲最簡單的解決方案將非常類似於您的建議。我可能會寫一個新方法find_all_cars,而不是覆蓋關係方法。

雖然,我可能會覆蓋vehicles,並允許其採取一種說法:

def vehicles(sti_type=nil) 
    return super unless sti_type 
    super.find_all { |v| v.type == sti_type } 
end 

編輯

可以得到vehicles通過Rails的緩存,所以你大概可以只依賴於。你define_method S還可以這樣做:

%w(cars trucks).each do |assoc| 
    define_method "preloaded_#{assoc}" do 
    klass = self.class.reflect_on_all_associations.detect { |assn| assn.name.to_s == assoc }.klass 
    vehicles.select { |a| a.is_a? klass } 
    end 
end 

即使你不使用includes,第一時間你怎麼稱呼它,它會緩存的關聯,因爲你select ING,不where ING。當然,您仍然不會收到關係。

這並不是真的很漂亮,但我喜歡它包含在一個不依賴於其他任何方法的方法中。

+0

是的,表現可能是值得注意的;我正在爲兩個父母中的每一個的六個STI課程做這個工作。嗯......參加assocation_cache的實際關係會涉及什麼? – Nate 2015-02-24 02:32:00

+0

我已經將解決​​方案的變體添加到頂端。神智清醒?但好奇的是:爲什麼你會改變車輛來採取STI類型? (在我的情況下,我通常會爲每個請求加載所有STI類型。) – Nate 2015-02-24 03:01:53

+0

看起來和任何一樣好。儘管你仍然沒有得到一個關係。我一直在尋找一種解決方案,添加了最少的方法和最少的複雜性。我的口味似乎增加了你的解決方案太多。我也會添加一個編輯。 – evanbikes 2015-02-24 03:46:34