0

Мы можем использовать ActiveRelation как это:Использование ActiveRelation с: включается на полиморфного has_one неявно создать отношения

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?

ответ

0

Погружение в источниках ActiveRecord я обнаружил, что

ActiveRecord :: Связь охватывает «создать» методом «обзорного».

ActiveRecord :: Persistance 'create' calls 'initialize' из ActiveRecord :: Core.

ActiveRecord :: остова 'Initialize' называет 'populate_with_current_scope_attributes'

Этот метод, объявленный в ActiveRecord :: Scoping использует 'scope_attributes' объявленные в ActiveRecord :: Scoping :: Названы.

scope_attributes создает отношение «все» и вызывает «scope_for_create» на нем.

'ActiveRecord :: Relation' scope_for_create 'использует только' where_values_hash 'из current_scope, который не содержит таких правил, как' related_models.subject_type '(эти значения содержатся в where_clauses). Поэтому нам нужно иметь простые ключевые значения для использования с «create» в ActiveRecord :: Relation. Но ActiveRecord не достаточно умен, чтобы знать, что 'some_field' в where where должно использоваться с таблицей join.

Я нашел, что он может быть реализован только путем доступа к параметрам с self.class.current_scope.where_clauses в 'before_create' на MyModel, анализе их и настройке атрибутов.

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 в 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 
Смежные вопросы