我有以下2个型号
class Sport < ActiveRecord::Base
has_many :charts, order: "sortWeight ASC"
has_one :product, :as => :productable
accepts_nested_attributes_for :product, :allow_destroy => true
end
class Product < ActiveRecord::Base
belongs_to :category
belongs_to :productable, :polymorphic => true
end
如果没有产品,运动就不可能存在,所以在sports_controller.rb
我有:
def new
@sport = Sport.new
@sport.product = Product.new
...
end
我尝试使用after_initialize
:
after_initialize :create_product
def create_product
self.product = Product.new
end
我很快就了解到,只要模型被实例化(即来自after_initialize
调用),就会调用find
。所以这不是我想要的行为。
我应该如何建模所有sport
都有product
的要求?
由于
答案 0 :(得分:60)
如您所述,将逻辑放在控制器中可能是最好的答案,但您可以通过执行以下操作让after_initialize
工作:
after_initialize :add_product
def add_product
self.product ||= Product.new
end
这样,它只会在没有产品的情况下设置产品。它可能不值得开销和/或不如控制器中的逻辑那么清晰。
编辑:根据Ryan的回答,以下表现可能会更好:
after_initialize :add_product
def add_product
self.product ||= Product.new if self.new_record?
end
答案 1 :(得分:40)
当然after_initialize :add_product, if: :new_record?
是最干净的方式。
将条件保留在add_product函数
之外答案 2 :(得分:27)
如果您执行self.product ||= Product.new
,则每次执行find
时仍会搜索产品,因为它需要检查是否为零。因此,它不会做任何急切的加载。为了在创建新记录时执行此操作,您只需在设置产品之前检查它是否为新记录。
after_initialize :add_product
def add_product
self.product ||= Product.new if self.new_record?
end
我做了一些基本的基准测试,检查if self.new_record?
似乎没有以任何明显的方式影响性能。
答案 3 :(得分:2)
而不是使用after_initialize
,after_create
怎么样?
after_create :create_product
def create_product
self.product = Product.new
save
end
这看起来会解决您的问题吗?
答案 4 :(得分:1)
看起来你非常接近。你应该可以完全取消after_initialize调用,但首先我相信如果你的Sport模型与你所指示的产品有“has_one”关系,那么你的产品模型也应该“属于”运动。将其添加到您的产品型号
belongs_to: :sport
下一步,您现在应该可以像这样实例化运动模型
@sport = @product.sport.create( ... )
这是基于来自Ruby on Rails指南的Association Basics的信息,如果我不完全正确,您可以阅读这些信息
答案 5 :(得分:1)
您应该覆盖初始化方法,如
class Sport < ActiveRecord::Base
# ...
def initialize(attributes = {})
super
self.build_product
self.attributes = attributes
end
# ...
end
从数据库加载记录时,永远不会调用Initialize方法。 请注意,在上面的代码中,属性是在构建产品后分配的。 在这样的设置中,属性赋值可以影响创建的产品实例。