我有4个型号,让我们说:
class Photo < ActiveRecord::Base
belongs_to :photoable, polymorphic: true
end
class User < ActiveRecord::Base
has_one :photo, as: :photoable
end
class Company < ActiveRecord::Base
has_one :photo, as: :photoable
has_many :products
end
class Products < ActiveRecord::Base
belongs_to :company
end
因此,查询Photo.all.includes(:photoable)
有效。
但是,如果我使用Photo.all.includes(photoable: :products)
,只有所有加载的照片都属于公司才有效。如果关系包含用户和公司的照片,则会出现此错误:
ActiveRecord::ConfigurationError (Association named 'products' was not found; perhaps you misspelled it?):
这是因为用户与产品没有关系。
是否有任何方法可以将用户和产品公司急切加载到照片关系中?
修改
此问题与Eager load polymorphic 不重复。正如我在下面评论的那样,在这个问题中,我希望对具有不同关联的多态关联做一个急切的加载(一个有产品而另一个没有关联)。在该问题中,OP对表名使用了错误的名称。
答案 0 :(得分:1)
Rails 6增加了对这种情况的支持,现在只有在为对象定义了嵌套关联时才预加载嵌套关联,您甚至可以混合来自不同类的关联:
Photo.all.includes(photoable: [:products, :posts, :some_undefined])
完整的可运行示例:
require "bundler/inline"
gemfile(!ENV['SKIP_INSTALL']) do
source "https://rubygems.org"
# rails 5 gives the error, run with RAILS5=1 to see
gem "activerecord", ENV['RAILS5'] && "~>5.0" || "~>6.0"
gem "sqlite3"
end
require "active_record"
require "minitest/autorun"
require "logger"
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Schema.define do
create_table(:photos){|t| t.references :photoable, polymorphic: true }
create_table(:users)
create_table(:companies)
create_table(:products){|t| t.references :company }
create_table(:posts){|t| t.references :user }
end
# --- Models:
class Photo < ActiveRecord::Base
belongs_to :photoable, polymorphic: true
end
class User < ActiveRecord::Base
has_one :photo, as: :photoable
has_many :posts
end
class Company < ActiveRecord::Base
has_one :photo, as: :photoable
has_many :products
end
class Product < ActiveRecord::Base
belongs_to :company
end
class Post < ActiveRecord::Base
belongs_to :user
end
# --- Test:
class SomeTest < Minitest::Test
def setup
2.times{
Company.create!.tap{|company|
company.products.create!
company.create_photo!
}
User.create!.tap{|user|
user.posts.create!
user.create_photo!
}
}
end
def test_association_stuff
rel = Photo.all.includes(photoable: [:products, :posts, :some_undefined])
arr = rel.to_a # fetch all records here
ActiveRecord::Base.logger.extend(Module.new{
define_method(:debug){|msg| raise "should not log from now on" }
})
assert_kind_of(Product, arr.first.photoable.products.first)
assert_kind_of(Post, arr.last.photoable.posts.first)
end
end
答案 1 :(得分:0)
我有同样的问题,并找到了解决方案。
您可以在查找后调用急切加载:
photos = Photo.all.includes(:photoable)
photos_with_products_association = photos.select{|p| p.photoable.is_a?(Company)}
ActiveRecord::Associations::Preloader.new.preload(
photos_with_products_association,
:products
)
......或更通用的
photos_with_products_association = photos.select do |p|
p.class.reflections.keys.include?(:products)
end
答案 2 :(得分:0)
您加入公司照片时很奇怪,而不是相反。为什么要这么做?你真的需要视图或控制器中的那些公司产品吗?
SELECT * from photos
LEFT JOIN users AS u ON u.id = photos.photoable_id AND
photos.photoable_type = 'User'
LEFT JOIN companies AS c ON c.id = photos.phottoable_id AND
photos.photoable_type = 'Company'
LEFT JOIN products AS p ON p.company_id = c.id
答案 3 :(得分:0)
您可以向Photo
模型添加特定关联:
class Photo < ActiveRecord::Base
belongs_to :photoable, polymorphic: true
belongs_to :user, -> { where(photoable_type: 'User' ) }, foreign_key: :photoable_id
belongs_to :company, -> { where(photoable_type: 'Company' ) }, foreign_key: :photoable_id
end
之后,您可以预加载嵌套的特定关联:
photos = Photo.all.includes(:photoable, company: :products)
可以访问这样的记录:
photos.each do |photo|
puts photo.photoable.inspect
puts photo.company.products.inspect if photo.photoable_type == "Company"
end
此方法加载company
关联两次,但不执行n + 1次查询。
答案 4 :(得分:0)
class Products < ActiveRecord::Base
belongs_to :company
end
应该是
class Product < ActiveRecord::Base
belongs_to :company
end
然后它会起作用。