Rails关联在after_initialize中为nil

时间:2012-06-02 18:01:12

标签: ruby-on-rails ruby-on-rails-3

我有两个模型,一对多关联。我想在初始化时根据父级的某些状态在子模型上设置默认值。这涉及对需要通过belongs_to关联访问父级的子级进行after_initialize回调。问题是当我使用build方法实例化子进程时,after_initialize回调中与父进程的关联为nil。这是预期的行为吗?我在轨道3.0.6

玩具示例:

class Merchant < ActiveRecord::Base
    has_many :products
end

class Product < ActiveRecord::Base
    belongs_to :merchant

    after_initialize :set_default_value

    def set_default_value
        if merchant.state
            self.foo = some_value
        else
            self.foo = some_other_value
        end
    end
end

在控制器中:

product = merchant.products.build

在对set_default_value的调用中,虽然看起来不应该是商家,但商家是零。

3 个答案:

答案 0 :(得分:1)

我会按如下方式更改代码:

class Product < ActiveRecord::Base
  ...
  def set_default_value(state = merchant.state)
    if state
      self.foo = some_value
    else
      self.foo = some_other_value
    end
  end
end

然后将您的来电者更改为:

product = merchant.products.build(:state => merchant.state)

另外,我发现after_initialize回调很慢。所以另一个选择是将逻辑移动到产品的构建器中。

product = merchant.products.build(:foo => merchant.state ? some_value : some_other_value)

这也消除了代码中的Demeter法则(即产品不应该知道/关心商家的状态)。

答案 1 :(得分:0)

我在轨道2.3上,我可以确认

product = merchant.products.build

不会在after_initialize回调中返回正确的merchant_id关联

但我发现它可以正常使用

product = merchant.products.new

我认为这是通过此提交修复的(我不知道,我对git工作流程并不熟悉):

https://github.com/rails/rails/issues/1842

因为在rails 3.1.11中,它适用于buildnew

答案 2 :(得分:0)

您可能会寻找inverse_of

has_many :products, inverse_of: :merchant