2012-02-02 61 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方法,请尝试一下,我相信这是符合您所有条件的解决方案。

相关问题