2012-02-02 84 views
0

說我有兩個班,使用ActiveRecord執行此操作的正確方法?

形象和信用

class Image < ActiveRecord::Base 
    belongs_to :credit 
    accepts_nested_attributes_for :credit 
end 

class Credit < ActiveRecord::Base 
    #has a field called name 
    has_many :images 
end 

我希望有一個信貸創建圖像時,關聯作用有點像一個標籤。從本質上講,我希望像Credit.find_or_create_by_name這樣的行爲,但是在使用Credit的客戶端代碼中,如果它只是一個Create,它會更清潔。我似乎無法找到一種方法將其烘焙到模型中。當你創建一個圖像,你可以做什麼

class Image < ActiveRecord::Base 
    belongs_to :credit 

    attr_accessor :credit_name 
    after_create { Credit.associate_object(self) } 
end 

class Credit < ActiveRecord::Base 
    #has a field called name 
    has_many :images 

    def self.associate_object(object, association='images') 
    credit = self.find_or_create_by_name(object.credit_name) 
    credit.send(association) << object 
    credit.save 
    end 

然後是像

Image.create(:attr1 => 'value1', :attr2 => 'value2', ..., :credit_name => 'some_name') 

,它會帶你送入名稱:

+0

「信用」就像電影上的信用(歸屬),而不是賬戶上的信用(付款),對嗎? – jefflunt 2012-02-02 19:20:47

+0

是的,對不起。在新聞編輯室工作2.5年,扭曲了我的行話。 – 2012-02-02 19:22:57

回答

0

試試這個:credit_name值並在after_create回調中使用它。

請注意,如果你決定有信用以後相關聯的不同對象(假設一類叫做Text),你可以這樣做仍然使用這種方法,像這樣:

class Text < ActiveRecord::Base 
    belongs_to :credit 

    attr_accessor :credit_name 
    before_create { Credit.associate_object(self, 'texts') } 
end 

雖然在這一點上你可能會考慮爲所有屬於credit的類創建一個SuperClass,並讓超類處理關聯。你可能也想看看polymorphic relationships

+0

這可以工作。但我希望把這個邏輯放在信用,而不是圖像,因爲最終,更多的東西將獲得信用。 – 2012-02-02 19:25:34

+0

我不確定你的意思。我以爲你想要創建一個圖像時發生的行動?如果你想通過信用做到這一點,那麼你總是可以通過像'some_credit.images << Image.create(:attr1 =>'value1')'然後'some_credit.save'或類似的東西來創建你的圖像? – Batkins 2012-02-02 19:39:16

+0

當ANYING創建信用時,我希望它發生。最終會有一些附加信用的東西。 – 2012-02-02 19:40:57

0

這可能比它的價值更麻煩,而且很危險,因爲它涉及重寫Credit類的initialize方法,但我認爲這可能有效。我對你的建議是嘗試我之前建議的解決方案,並將這些寶石拋棄或修改,以便他們可以使用你的方法。這就是說,這裏什麼都不做:

首先你需要一種方法來獲取信用初始值設定項的方法調用者。我們使用一個類I found on the web called CallChain,但我們會根據我們的目的對其進行修改。你可能想把它放在你的lib文件夾中。

class CallChain 
    require 'active_support' 

    def self.caller_class 
    caller_file.split('/').last.chomp('.rb').classify.constantize 
    end 

    def self.caller_file(depth=1) 
    parse_caller(caller(depth+1).first).first 
    end 

    private 

    #Stolen from ActionMailer, where this was used but was not made reusable 
    def self.parse_caller(at) 
    if /^(.+?):(\d+)(?::in `(.*)')?/ =~ at 
     file = Regexp.last_match[1] 
     line = Regexp.last_match[2].to_i 
     method = Regexp.last_match[3] 
     [file, line, method] 
    end 
    end 
end 

現在我們需要覆蓋Credit類初始化,因爲當你從另一個類Credit.newCredit.create呼叫(在這種情況下,你Image類),它是從調用這個類的初始化。您還需要確保當您撥打Credit.createCredit.new時,您將:caller_class_id => self.id提供給屬性參數,因爲我們無法從初始化程序獲取它。

class Credit < ActiveRecord::Base 
    #has a field called name 
    has_many :images 
    attr_accessor :caller_class_id 

    def initialize(args = {}) 
    super 
    # only screw around with this stuff if the caller_class_id has been set 
    if caller_class_id 
     caller_class = CallChain.caller_class 
     self.send(caller_class.to_param.tableize) << caller_class.find(caller_class_id) 
    end 
    end 
end 

現在我們有一個設置,我們可以在我們的Image類的簡單方法,這將創建一個新的Credit和設置的關聯正常,像這樣:

class Image < ActiveRecord::Base 
    belongs_to :credit 
    accepts_nested_attributes_for :credit 

    # for building 
    def build_credit 
    Credit.new(:attr1 => 'val1', etc.., :caller_class_id => self.id) 
    end 

    # for creating 
    # if you wanted to have this happen automatically you could make the method get called by an 'after_create' callback on this class. 
    def create_credit 
    Credit.create(:attr1 => 'val1', etc.., :caller_class_id => self.id) 
    end 
end 

同樣,我真的止跌這不推薦,但我想看看是否有可能。如果您不介意覆蓋Credit上的initialize方法,請嘗試一下,我相信這是符合您所有條件的解決方案。

相關問題