0

我们可以像这样使用 ActiveRelation:

MyModel.where(:field => "test").create => #<Message ... field:"test">

但它不适用于具有多态 has_one 关联的连接:

class RelatedModel < AR::Base
  # has :some_field
  belongs_to :subject, :polymorphic => true
end

class MyModel < AR::Base
  # need some dirty magic here
  # to build default related_model with params from active_relation
  has_one :related_model, :as => :subject, :dependent => :destroy
end

describe MyModel do
  it "should auto-create has_one association with joins" do
    test = MyModel.joins(:related_model).where("related_models.subject_type" => "MyModel", "related_models.some_field" => "chachacha").create
    test.related_model.should_not be_nil
    test.related_model.some_field.should == "chachacha"
    test.related_model.subject_type.should == "MyModel"
    test.related_model.subject_id.should == test.id
    # fails =)
  end
end

是否可以提取 active_relation 参数,将它们传递给 MyModel 以在 before_create 中使用并使用它们构建相关模型?

4

1 回答 1

0

深入研究 ActiveRecord 资源,我发现

ActiveRecord::Relation 用“范围”方法覆盖“创建”。

ActiveRecord::Persistance 'create' 从 ActiveRecord::Core 调用'initialize'。

ActiveRecord::Core 'initialize' 调用 'populate_with_current_scope_attributes'

在 ActiveRecord::Scoping 中声明的这个方法使用在 ActiveRecord::Scoping::Named 中声明的 'scope_attributes'。

scope_attributes 创建关系 'all' 并在其上调用 'scope_for_create'。

'ActiveRecord::Relation 的 'scope_for_create' 仅使用 current_scope 中的 'where_values_hash' 不包含诸如 'related_models.subject_type' 之类的规则(这些值包含在 where_clauses 中)。因此,我们需要在 ActiveRecord::Relation 上与“create”一起使用简单的键值对 wheres。但是 ActiveRecord 不够聪明,无法知道 where 子句中的 'some_field' 应该与连接表一起使用。

我发现它只能通过在 MyModel 的“before_create”中使用 self.class.current_scope.where_clauses 访问 where 选项、解析它们并设置属性来实现。

class MyModel < AR::Base
  before_create :create_default_node
  def create_default_node
    clause = self.class.current_scope.where_clauses.detect{|clause| clause =~ /\`related_models\`.\`some_field\`/}
    value = clause.scan(/\=.+\`([[:word:]]+)\`/).flatten.first
    self.create_node(:some_field => value)
  end
end

但它太脏了,然后我决定找到更简单的解决方案和反向依赖,如 Railscast Pro #394 中所述,将 RelatedModel 功能移动到带有 STI 的 MyModel。实际上,我需要创建如此复杂的关系,因为 RelatedModel 具有所有模型通用的一些功能(充当树)。我决定将“祖先”和“孩子”委托给相关模型。反转依赖解决了这个问题。

class MyModel < AR::Base
  acts_as_tree
  belongs_to :subject, :polymorphic => true
end

class MyModel2 < MyModel
end

class RelatedModel < AR::Base
  # has :some_field
  has_one :my_model, :as => :subject, :dependent => :destroy
end

MyModel.create{|m| m.subject = RelatedModel.create(:some_field => "chachacha")}
MyModel.ancestors # no need to proxy relations
于 2013-10-28T18:41:00.183 回答