在ActiveRecord :: Relation(STI)上使用`become`

时间:2014-09-17 09:45:51

标签: ruby-on-rails activerecord single-table-inheritance

# Models
class Animal < ActiveRecord::Base
  belongs_to :zoo
end

class Dog < Animal
  belongs_to :leg
end

class Snake < Animal
end

class Leg < ActiveRecord::Base
end

class Zoo
  has_many :animals
end

# Test, which fails with
# Association named 'leg' was not found on Animal; perhaps you misspelled it?
Zoo.first.animals.
         .where(:type => 'Dog')
         .includes(:leg)

在这个例子中,Rails无法知道所查询对象的特定类型(它必须分析where语句,它似乎没有这样做。因此,它失败了,因为关联没有在通用模型Animal上定义,而是在Dog模型上定义。

有没有办法指定要检索的对象的类型,以便该示例有效?

3 个答案:

答案 0 :(得分:1)

我想出了一个(至少是临时的)解决方案。这个猴子补丁扩展了Rails,其功能是在访问集合关联时指定类覆盖。

用法:

Zoo.first.animals(false, Dog)
         .includes(Leg)

猴子补丁(铁轨3!):

# This monkey patch extends the rails collection association reader (i.e. )
# zoo.animals by a second option called `class_override`. Using this option,
# a custom model class for the objects to be retrieved can be specified.
#
# Might only work with rails 3.
#
# @see http://stackoverflow.com/q/25887230
module ActiveRecord
  module Associations
    class CollectionAssociation < Association #:nodoc:
      # Implements the reader method, e.g. foo.items for Foo.has_many :items
      def reader(force_reload = false, class_override = nil)
        if force_reload
          klass.uncached { reload }
        elsif stale_target?
          reload
        end

        if class_override.nil?
          association = self
        else
          reflection = self.reflection.dup
          reflection.instance_variable_set(:@klass, class_override)
          association = self.dup
          association.instance_variable_set(:@reflection, reflection)
        end

        CollectionProxy.new(association)
      end
    end
  end
end

可能也可以使用关联扩展来实现。我正在研究它。

答案 1 :(得分:1)

刚刚在Robert Pankowecki的一篇精彩博客文章中找到了解释可能的解决方案:http://blog.arkency.com/2013/07/sti/

答案 2 :(得分:0)

你可以写:

class Zoo < ActiveRecord::Base
  has_many :animals
  has_many :dogs
end

然后尝试:Zoo.first.dogs.includes(:leg)

如果您需要Animal腿,可以尝试:

class Animal < ActiveRecord::Base
  belongs_to :zoo
  scope :with_legs, -> { where("leg_id IS NOT NULL").includes(:leg) }
end

然后:Zoo.first.animals.with_legs