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 で使用し、RelatedModel をビルドすることは可能ですか?

4

1 に答える 1

0

ActiveRecordソースに飛び込むと、

ActiveRecord::Relation は、「スコープ」メソッドで「作成」をカバーします。

ActiveRecord::Persistance 'create' は、ActiveRecord::Core から 'initialize' を呼び出します。

ActiveRecord::Core の「初期化」は「populate_with_current_scope_attributes」を呼び出します

ActiveRecord::Scoping で宣言されたこのメソッドは、ActiveRecord::Scoping::Named で宣言された「scope_attributes」を使用します。

scope_attributes はリレーション「all」を作成し、その上で「scope_for_create」を呼び出します。

「ActiveRecord::Relation」の「scope_for_create」は、「related_models.subject_type」などのルールを含まない current_scope の「where_values_hash」のみを使用します (この値は where_clauses に含まれています)。したがって、ActiveRecord::Relation の「create」で使用する単純なキーと値の場所が必要です。しかし、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 にはすべてのモデルに共通の機能 (ツリーとして機能する) があるため、実際にはこのような複雑な関係の作成が必要でした。「祖先」と「子」を 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 に答える