Rails从多态模型加入或预加载belongs_to关联

时间:2011-04-28 07:25:05

标签: ruby-on-rails activerecord join preload polymorphism

我的问题如下。如何从多态模型

加入belongs_to关联

有情况

opinion.rb

class Opinion < ActiveRecord::Base
    belongs_to :opinionable, :polymorphic => true
    belongs_to :category
end

answer.rb

class Answer < ActiveRecord::Base
    has_many :opinions, :as => :opinionable
end

我该怎样做?

  

Opinion.joins(:opinionabe)。所有

它将抛出

  

ArgumentError:您无法在未指定多态类的情况下创建多态的belongs_to连接!

我如何具体说明我想加入哪个班级?

第二个问题。如何预加载?

  

Opinion.preload(:opinionable)。所有

工作正常。它将对belongs_to中的每个类进行查询。

但是。如果我想做像

这样的事情
  

Opinion.preload(:opinionable =&gt;:answer_form).all

存在问题,因为一个模型具有此关联而第二个没有。所以它会抛出异常。

所以我怎么能做像

这样的事情
  

Opinion.preload(:answer =&gt;:answer_form,:another_belongs_to_model).all

谢谢,大卫!

3 个答案:

答案 0 :(得分:15)

其实如果你做的话

belongs_to :opinionable_answer, :foreign_key => :opinionable_id, :class_name => "Answer", conditions: { opinions: { opinionable_type: "Answer"}}

然后你可以做

Opinion.joins(:opinionable_answer).where(answers: { awesome: true})

答案 1 :(得分:14)

您的Opinion模型似乎没有指定opinionable_type:string列。

尝试以这种方式更新迁移:

class CreateOpinions < ActiveRecord::Migration
  def self.up
    create_table :opinions do |t|
      t.integer :opinionable_id
      t.string  :opinionable_type

      # ... other fields

      t.timestamps
    end
  end

  def self.down
    drop_table :opinions
  end
end

这将解决您的第二个问题,Opinion.preload(:opinionable).all应该可以正常运作。

您不能对多态关联进行连接,因为它们可以位于不同的表中,这些表在加载Opinion模型后检测到。这就是为什么模型需要列opinionable_type

如果您尝试这样做,您将获得下一个异常

  

ActiveRecord::EagerLoadPolymorphicError:无法急切加载多态关联:opinionable

UPD:添加魔术连接^ _ ^

class Opinion < ActiveRecord::Base
  belongs_to :opinionable, :polymorphic => true

  belongs_to :opinionable_answer, :foreign_key => :opinionable_id, :class_name => "Answer"

  scope :by_type, lambda { |type| joins("JOIN #{type.table_name} ON #{type.table_name}.id = #{Opinion.table_name}.opinionable_id AND #{Opinion.table_name}.opinionable_type = '#{type.to_s}'") }
end

示例:

Opinion.by_type(Answer).to_sql
  => "SELECT \"opinions\".* FROM \"opinions\" JOIN answers ON answers.id = opinions.opinionable_id AND opinions.opinionable_type = 'Answer'" 

答案 2 :(得分:0)

我知道这个问题很旧,但是我只花了一个小时寻找类似问题的解决方案(第3条),所以使它起作用的唯一方法是在此处说明解决方案:https://stackoverflow.com/a/25966630/6878997

您的情况是:

<body>
    <div> 
        <form action="/Home/MultiDArrayToView" method="post" id="form">
            <input name="s" required/>
            <button type="submit">Submit</button>
        </form>
    </div>
    <script src="~/Scripts/jquery-3.3.1.min.js"></script>
    <script>

        $(document).ready(function () {
            $("#form").submit(function (event) {
                event.preventDefault(); //prevent default action
                var post_url = $(this).attr("action"); //get form action url
                var request_method = $(this).attr("method"); //get form GET/POST method
                var form_data = new FormData(this); //Creates new FormData object
                $.ajax({
                    url: post_url,
                    type: request_method,
                    data: form_data,
                    contentType: false,
                    cache: false,
                    processData: false,
                    datatype: "json",
                    success: function (value) {
                        //here I am getting array from controller method in 'value' but as a 1D array instead I want multidimensional array
                        debugger;
                    }
                });


            });
        });
    </script>
</body>

似乎很棘手,但是通过这种方式,您将能够执行多个链式联接,例如:

class Opinion < ActiveRecord::Base # The true polymorphic association belongs_to :opinionable, polymorphic: true # The trick to solve this problem has_one :self_ref, :class_name => self, :foreign_key => :id has_one :answer, :through => :self_ref, :source => :opinionable, :source_type => Answer end

只要joins(answer: :other_model)不是opinion.opinionableAnswer就会返回opinion.answer

希望对您有所帮助!