2011-03-05 30 views
0

是否有任何方式使用ActiveRecord :: Relation爲左外連接指定多個條件?ActiveRecord ::具有更多條件的關係連接不僅僅是外鍵

以下面的SQL語句爲例。任何人都可以使用ActiveRecord :: Relation對象來重寫這個?

SELECT `texts`.*, `text_translations`.translation FROM `texts` LEFT OUTER JOIN `text_translations` ON `text_translations`.`id` = `texts`.`id` AND `text_translations`.`locale` = 'en' 

有沒有辦法在ActiveRecord 3.0.3+下做到這一點?

在此先感謝。

回答

1

首先您應該考慮使用rails/activerecord符合關係。這意味着在text_translations表的外鍵應該叫text_id

這樣創建模型和關聯:

class Text < ActiveRecord::Base 

    # all possible translations! 
    has_many :text_translations 

    scope :with_translation_for, lambda { |lang| { 
    :select  => "texts.*, tt.translation", 
    :joins  => "LEFT OUTER JOIN text_translations AS tt ON tt.text_id = texts.id AND tt.locale = #{ActiveRecord::Base.sanitize(lang)}" 
    }} 


    # return nil if translation hasn't been loaded, otherwise you get a nasty NoMethod exception 
    def translation 
    read_attribute(:translation) 
    end 

end 

class TextTranslation < ActiveRecord::Base 
    # every translation belongs to a text 
    belongs_to :text 

    # define a scope for the language 
    scope :language, lambda { |lang| where(['locale = ?', lang]) } 

end 

如何使用:

texts = Text.with_translation_for('en') 
texts.each do |c_text| 
    unless c_text.translation.nil? 
     puts c_text.translation 
    else 
     puts "No translation available!" 
    end 
end 

現在對於正反兩方面,使用LEFT OUTER join的方式會加載你全部文本,即使沒有翻譯爲所需語言的文本。這意味着你不會得到「TextTranslation」模型對象。

另一種方式是隻加載具有所需翻譯的文本。你可以不喜歡它:

texts = Text.includes(:text_translations).where(:text_translations => {:locale => 'en'}) 

現在texts[i].text_translations將返回所有TextTranslations模型對象的數組,這個文本相匹配的區域設置成「en」。但是在語言環境「en」中沒有翻譯的文本不會顯示出來。

編輯

連接到您的評論:

有關的關係用.join(:tablename)的問題是,它會導致INNER JOIN,所以這是不是一種選擇。您必須明確聲明LEFT加入。另一件事是,如果你使用類似Text.includes(:text_translations).where(['text_translations.locale = ?', 'en'])的東西,這個條件將被作爲整體應用於SQL查詢,而不是可能的LEFT加入本身。什麼,你其實可以做的就是申報協會,如

has_many :english_translations, :class_name => 'TextTranslation', :conditions => ['locale = ?', 'en'] 

這樣你就可以管理由預先加載只加載英文譯本(不含任何加入的話):

Text.includes(:english_translations).all 

Checkt了這一點:

+0

首先感謝您的見解,但首要問題是如何使用Relation在左外連接上附加多個條件,從而避免硬編碼_:joins =>「左外連接text_translations AS tt ON tt.text_id = texts.id AND tt.locale =#{ActiveRecord :: Base.sanitize(lang)}「_在代碼中。 – azazeal 2011-03-06 15:41:56

+0

看看我的編輯,我希望這會回答你的問題 – sled 2011-03-06 16:58:03

+1

有一種方法來聲明一個左外連接(使用meta_where)__。joins(:translations.outer)__。無論如何,我認爲大多數人只會硬編碼,所以感謝您的意見。 – azazeal 2011-03-07 13:32:03

相關問題