我在编写和测试涉及几个连接和关联的范围时遇到了麻烦。我会尽量保持我的解释简短但尽可能彻底。
我有以下关联:
ExpertTopic > Topic > Articles > Posts
和以下代码:
class Topic < ActiveRecord::Base
has_many :articles, :order => "position", :dependent => :destroy
has_many :posts, :through => :articles
has_many :expert_topic, :dependent => :delete_all
has_many :experts, :through => :expert_topic
end
和
class ExpertTopic < ActiveRecord::Base
belongs_to :topic, :inverse_of => :expert_topic
belongs_to :expert, :inverse_of => :expert_topic
scope :live, joins(:topic => {:articles => :post})
.where("topics.article_count > ? AND posts.live = ?", 0, true)
end
live
范围内的ExpertTopic
范围,我正在尝试缩小与主题相关的专家及其中的所有实时帖子(通过文章)。 < / p>
在Rails控制台中ExpertTopic.live.to_sql
是:
"SELECT `experts_topics`.* FROM `experts_topics` INNER JOIN
`topics` ON `topics`.`id` = `experts_topics`.`topic_id` INNER JOIN
`articles` ON `articles`.`topic_id` = `topics`.`id` INNER JOIN
`posts` ON `posts`.`id` = `articles`.`post_id` WHERE
(topics.article_count > 0 AND posts.live = 1)"
我正在使用expert_topic_spec.rb
中的以下代码测试我的范围:
describe ExpertTopic do
before do
@post1 = FactoryGirl.create(:pending_post)
@post2 = FactoryGirl.create(:live_post)
@post3 = FactoryGirl.create(:pending_post)
@post4 = FactoryGirl.create(:live_post)
@non_live_topic = FactoryGirl.create(:topic_with_posts, :posts => [@post1, @post2, @post3])
@live_topic = FactoryGirl.create(:topic_with_posts, :posts => [@post2, @post4])
FactoryGirl.create(:expert_topic, topic_id: @non_live_topic.id)
FactoryGirl.create(:expert_topic, topic_id: @live_topic.id)
end
it 'finds and returns only expert with live topic' do
ExpertTopic.all.count.should == 2
ExpertTopic.live.uniq.count.should == 1
end
end
逻辑是,由于@non_live_topic
包含至少一个不存在的帖子,因此不会被视为实时,因此不应通过调用ExpertTopic.live
返回。但是,最后一个断言失败是因为ExpertTopic.live.uniq.count
返回2
而不是1
。
我不知道我的作用域是否写得不正确或是否是我的测试,我真的很感谢有人帮助调试!
谢谢!
答案 0 :(得分:2)
您写道:
逻辑是,因为@non_live_topic包含至少一个不存在的帖子,所以它不被视为实时
这不正确。 live
范围不排除与非实时帖子相关联的ExpertTopic
。它只包含与一个或多个实时帖子相关联的ExpertTopic
个。这意味着如果关联的是实时和非实时帖子,它将被包含在内。
要将范围更改为您希望的逻辑,您需要使用排除子句,例如:
scope :live, lambda {
non_live_sql = joins(:topic => {:articles => :post})
.where("topics.article_count > ? AND posts.live = ?", 0, false)
.select('expert_topics.id').to_sql
joins(:topic).where("topics.article_count > ? AND expert_topics.id NOT IN (#{non_live_sql})", 0)
}
在SQL中还有其他方法可以排除项目,但这可能是最简单的构建在rails中而不涉及像Squeel这样的DSL或者手工编写大型查询。