说我有两个班,
形象和信用
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
我希望在创建Image时关联一个Credit,它有点像标记。本质上,我想要像Credit.find_or_create_by_name这样的行为,但是在使用Credit的客户端代码中,如果它只是一个Create,它会更加清晰。我似乎无法找到将其烘焙到模型中的方法。
答案 0 :(得分:0)
试试这个:
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')
它会将您输入:credit_name
值的名称用于after_create
回调中。
请注意,如果您决定稍后使用与Credit相关联的其他对象(假设有一个名为Text
的类),您仍然可以像这样使用此方法:
class Text < ActiveRecord::Base
belongs_to :credit
attr_accessor :credit_name
before_create { Credit.associate_object(self, 'texts') }
end
虽然在那时你可能会考虑为所有属于信用的类创建一个SuperClass,并且让超类处理关联。您可能还想查看polymorphic relationships。
答案 1 :(得分:0)
这可能比它的价值更麻烦,而且很危险,因为它涉及覆盖Credit
类的initialize
方法,但我认为这可能有效。我给你的建议是尝试我之前建议的解决方案,抛弃这些宝石或修改它们,以便他们可以使用你的方法。话虽如此,这里什么都没有:
首先,您需要一种方法来获取Credit初始化程序的方法调用程序。我们使用类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.new
类)调用Credit.create
或Image
时,它正在调用该类的初始值设定项。您还需要确保在拨打Credit.create
或Credit.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
同样,我真的不会推荐这个,但我想看看是否有可能。如果您不介意覆盖initialize
上的Credit
方法,请尝试一下,我相信这是一个符合您所有标准的解决方案。