【问题标题】:How to set up this scope with association in Rails?如何在 Rails 中通过关联设置此范围?
【发布时间】:2012-08-23 16:29:07
【问题描述】:

我在编写和测试涉及几个联接和关联的范围时遇到问题。我会尽量保持我的解释简短但尽可能详尽。

我有以下关联:

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

使用ExpertTopic 中的live 范围,我试图缩小与主题相关的专家,其中包含所有实时帖子(通过文章)。

在 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

我不知道我的范围是写错了还是我的测试,我真的很感谢有人在调试方面的帮助!

谢谢!

【问题讨论】:

    标签: sql ruby-on-rails-3 unit-testing scope associations


    【解决方案1】:

    你写道:

    逻辑是,由于@non_live_topic 包含至少一篇未发布的帖子,因此它不被视为实时

    这是不正确的。 live 范围不排除与非实时帖子关联的 ExpertTopics。它只包含与一个或多个实时帖子相关联的ExpertTopics。这意味着,如果实时帖子和非实时帖子都关联,则会包含该帖子。

    要将范围更改为您期望的逻辑,您需要使用排除子句,例如:

    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 或手动编写大型查询。

    【讨论】:

    • 这正是我所需要的。我认为我的逻辑可能存在缺陷,但我需要比我更聪明的人来指出它是什么。非常感谢!如果可以提供如此有用的答案,我会给你两票!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-14
    • 2014-07-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-09
    相关资源
    最近更新 更多