懒惰创建Rails关联的最佳模式是什么?

时间:2011-07-19 00:37:53

标签: ruby-on-rails lazy-loading

我有一个模型,比方说Cat,我想创建一个新模型OwnerCat has_one :owner,但是当我创建Cat模型时,Owner模型尚不存在。

不依靠为每个Owner回填新的Cat模型,我希望有一个简单的方法,所以如果我致电@cat.owner.something@cat.owner不存在然而,它会动态调用@cat.create_owner并将其返回。

我已经看到这样做了几种不同的方式,但我想知道什么是最常用的Rails解决方法,因为我需要经常这样做。

4 个答案:

答案 0 :(得分:3)

我之前没有看到过这个,但还是决定尝试一下。

我首先在owner模型中对关联方法Cat进行了别名,以保留原始方法的备份。如果原始方法返回nil,我会覆盖owner方法以调用build_owner方法(通过关联返回新的Owner对象)。否则,返回original_owner_method对象。

class Cat < ActiveRecord::Base
  has_one :owner

  alias :original_owner_method :owner

  def owner
    if original_owner_method.nil? 
      build_owner
    else
      original_owner_method
    end
  end

现在如果你打电话: cat = Cat.first

假设它没有所有者,它将在您调用时构建一个新的Owner对象: cat.owner.name

它将返回nil,但仍然在链的cat.owner部分构建所有者对象,而不调用method_missing。

答案 1 :(得分:1)

我不会在第一次访问时创建所有者,而是使用回调来创建所有者。这可以确保所有者永远不会为零,如果回调失败,它将自动回滚Cat创建。

class Cat < ActiveRecord::Base
  before_create :create_owner

private

  def create_owner
    return true unless owner.nil?

    create_owner(:default => 'stuff')
  end

end

更新:虽然我仍然会为新应用程序推荐上述方法,但由于您已有现有记录,因此可能需要更多类似的内容:

class Cat < ActiveRecord::Base
  def owner
    super || create_owner(:default => 'stuff')
  end
end

答案 2 :(得分:1)

对于这类问题的一般方法,我认为这是最“红宝石”

def owner
  @owner ||= create_owner
end

在rails中,我会采取类似这样的方式

def owner
  @owner ||= Owner.find_or_create(cat: self)
end

但总的来说,我会尝试找出一种方法来使用Cat#create_owner或Owner#create_cat,如果可以的话,尽量避免整个问题。

答案 3 :(得分:0)

根据我的经验,覆盖来自.directive("colorTag", function(){ return { restrict: "A", scope: { value: "=colorTag" }, link: function (scope, element, attrs) { var colors = new App.Colors(); element.css("background-color", stringToColor(scope.value)); element.css("color", contrastColor(scope.value)); // Destroy scope, because it's no longer needed. scope.$destroy(); } }; }) 的默认属性getter / setter是一种危险的做法 - 有龙。我将通过示例解释哪个让我失望。

我使用this answer中建议的ActiveRecord::Base模式。你最终可能会遇到像这样的棘手问题:

super || create_association

我错误地期望Rails魔术用它新创建的From: /Users/mec/Sites/zipmark/service/spec/models/vendor_application_spec.rb @ line 39 : 34: subject.read_attribute(:ledger_id).should be_blank 35: end 36: 37: it "lazily creates the association" do 38: subject.ledger => 39: binding.pry 40: subject.reload.ledger_id.should be_present 41: end 42: end 43: end 44: [1] pry(#<RSpec::Core::ExampleGroup>)> subject.ledger #<Ledger:0x007fc3c30ad398> { :id => "cf0ac70e-ce23-4648-bf3f-85f56fdb123a", :created_at => Wed, 30 Sep 2015 17:56:18 UTC +00:00, :updated_at => Wed, 30 Sep 2015 17:56:18 UTC +00:00, :description => "Freshbooks Ledger" } [2] pry(#<RSpec::Core::ExampleGroup>)> subject.reload.ledger_id nil 记录来更新手头的记录(self)。我最终将重载的ledger方法重写为以下内容:

#ledger