我有以下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
的要求?
谢谢
将逻辑放入控制器中可能是您所说的最佳答案,但您可以通过执行以下操作来使
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
当然
after_initialize :add_product, if: :new_record?
是这里最干净的方式。
将条件保留在 add_product 函数之外
如果您这样做
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?
似乎没有以任何明显的方式影响性能。
除了使用
after_initialize
,不如使用 after_create
怎么样?
after_create :create_product
def create_product
self.product = Product.new
save
end
这看起来能解决您的问题吗?
看来你们关系很亲密。您应该能够完全取消 after_initialize 调用,但首先我相信,如果您的 Sport 模型与 :product 具有“has_one”关系,正如您所指出的,那么您的 Product 模型也应该“belong_to”运动。将其添加到您的产品模型中
belongs_to: :sport
下一步,您现在应该能够像这样实例化一个运动模型
@sport = @product.sport.create( ... )
这是基于 Ruby on Rails 指南中的 Association Basics 的信息,如果我不完全正确,你可以通读一下
after_initialize :add_product, unless: :persisted?
你应该像这样覆盖初始化方法
class Sport < ActiveRecord::Base
# ...
def initialize(attributes = {})
super
self.build_product
self.attributes = attributes
end
# ...
end
从数据库加载记录时,永远不会调用Initialize方法。 请注意,在上面的代码中,属性是在产品构建后分配的。 在这种设置中,属性分配会影响创建的产品实例。
before_create
非常适合这种特殊情况。
你看,如果你想在即将创建的记录上启动一些代码块,你应该使用
before_create
钩子,因为它只会被调用一次 - 当记录创建时。更新时和保存时都没有。仅一次 - 创建记录时。愿上帝保佑你。
my_model = Model.create(**record_params) # this will trigger before_create hook
my_model.update(**some_params) # this will not trigger before_create hook