Rails after_initialize仅限于“new”

时间:2012-03-07 22:42:56

标签: ruby-on-rails model nested-attributes

我有以下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的要求?

由于

6 个答案:

答案 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_initializeafter_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方法。 请注意,在上面的代码中,属性是在构建产品后分配的。 在这样的设置中,属性赋值可以影响创建的产品实例。