具有has_many关联的rails中的named_scope

时间:2009-07-06 17:07:55

标签: ruby-on-rails activerecord associations named-scope

我正在尝试使用Rails的魔力来实现我认为相当复杂的查询,而不会在代码中看到很多丑陋的SQL。

由于我的数据库处理的是相当专业的生物医学模型,我将把以下内容翻译成更真实的场景。

我有一本模型书

has_many:章节

的章节

belongs_to:book

比如说章节有一个名称属性,名称可以是前言,介绍和附录,一本书可以有一个名为前言的章节和一个名为引言的章节,但没有章节命名附录。实际上是这些的任意组合

我希望找到所有包含章节命名前言和介绍的书籍。

目前我有一个named_scope如下

Book
  named_scope :has_chapter?, lambda { |chapter_names|
    condition_string_array = []
    chapter_names.size.times{condition_string_array << "chapters.name = ?"}
    condition_string = condition_string_array.join(" OR ")
    {:joins => [:chapters]  , :conditions => [condition_string, * chapter_names]}
  }

如果我打电话给Book。 has_chapter? [“前言”,“介绍”]这将找到我所有的书籍,其中有一章名为前言或介绍。我怎么能做类似的事情才能找到两者章节序言和介绍?

我不熟悉SQL,所以不太确定需要什么样的连接以及是否可以在命名范围内实现。

非常感谢

安东尼

3 个答案:

答案 0 :(得分:8)

  

我希望找到所有书籍   将章节命名为前言和   介绍

我使用的是mysql,而不是postgres,但我认为postgres支持子查询?你可以尝试类似的东西:

class Book

  named_scope :has_preface, :conditions => "books.id IN (SELECT book_id FROM chapters WHERE chapters.name = 'preface')"
  named_scope :has_introduction, :conditions => "books.id IN (SELECT book_id FROM chapters WHERE chapters.name = 'introduction')"

end

然后:

Book.has_preface.has_introduction

答案 1 :(得分:2)

我喜欢将代码压缩到

condition_string = Array.new(chapter_names.size, "chapters.name=?").join(" AND ")

这确实适用于带有“OR”的连接。但是AND连接将不起作用,因为这意味着连接的单行中的chapters.name必须是“前言”和“引言”,例如

进行原始“OR”加入的更简洁的方法是

 named_scope :has_chapter?, lambda { | chapter_names |
   {:joins => [: chapters]  , :conditions => { :chapters => {:name => chapter_names}}}
 }

感谢Rails论坛上的Duplex(Post link) 为了实现我原来的问题,虽然DUPLEX建议这个

named_scope :has_chapters, lambda { |chapter_names|
  {:joins => :chapters  , :conditions => { :chapters => {:name => chapter_names}},
   :select => "books.*, COUNT(chapters.id) AS c_count",
   :group => "books.id", :having => "c_count = #{chapter_names.is_a?(Array) ? chapter_names.size : 1}" }
}

这可能适用于某种SQL,但不适用于PostgreSQL。我不得不使用以下

named_scope : has_chapter?, lambda { | chapter_names |
  {:joins => :chapters  , :conditions => { :chapters => {:name => chapter_names}},
   :select => "books.* , COUNT(chapters.id)",
   :group => Book.column_names.collect{|column_name| "books.#{column_name}"}.join(","), :having => "COUNT(chapters.id) = #{chapter_names.is_a?(Array) ? chapter_names.size : 1}" }
}

代码

Book.column_names.collect{|column_name| "books.#{column_name}"}.join(",")

是必需的,因为在PostgreSQL中你不能选择所有带有书籍的列。*然后是GROUP BY书籍。*每列必须单独命名:(

答案 2 :(得分:1)

  

我希望找到所有书籍   将章节命名为前言和   介绍。

     

目前我有一个named_scope   如下

您可以在没有named_scope的情况下执行此操作。

class Book < ActiveRecord::Base
  has_many :chapters

  def self.has_chapter?(chapter_names)
    Chapter.find_all_by_name(chapter_names, :include => [:book]).map(&:book)
  end
end