ActiveRecord模型中的复杂关联
我试图了解ActiveRecord如何处理比简单的has_many
, belongs_to
等更复杂的关联。
例如,考虑一个用于录制音乐演出的应用程序。 每个Gig都有一个Band,它有一个类型。 每个Gig还有一个Venue,它有一个Region。
在MS Access的粗略表示法中(我突然开始感到非常怀旧),这些关系会像这样呈现出来
1 ∞ 1 ∞ ∞ 1 ∞ 1 Genre ---- Band ---- Gig ---- Venue ---- Region
我希望能够找到,例如,在一个地区玩过的所有乐队,或所有主持某种类型的场地。
理想情况下,我的模型将包含此代码
class Genre has_many :bands has_many :gigs, :through => bands has_many :venues, :through => :gigs, :uniq => true has_many :regions, :through => :venues, :uniq => true end class Band belongs_to :genre has_many :gigs has_many :venues, :through => :gigs, :uniq => true has_many :regions, :through => :venues, :uniq => true end class Gig belongs_to :genre, :through => :band belongs_to :band belongs_to :venue belongs_to :region, :through => :venue end
等Venue
和Region
。
但是,似乎我必须生产这样的东西
class Genre has_many :bands has_many :gigs, :through => bands has_many :venues, :finder_sql => "SELECT DISTINCT venues.* FROM venues " + "INNER JOIN gigs ON venue.id = gig.venue_id " + "INNER JOIN bands ON band.id = gig.band_id " + "WHERE band.genre_id = #{id}" # something even yuckier for regions end class Band belongs_to :genre has_many :gigs has_many :venues, :through => :gigs, :uniq => true # some more sql for regions end class Gig delegate :genre, :to => :band belongs_to :band belongs_to :venue delegate :region, :to => :venue end
我有两个问题 – 一个是普通的,一个是特别的。
一般:
我本以为我想做的事情会经常出现。 我有什么最好的方法来做到这一点,还是有一些我更容易忽视的东西?
特别:
我上面的内容实际上并没有完全奏效! 第二种类型模型中的#{id}
实际上是返回类的id。 (我认为)。 但是,这似乎在这里和这里工作
我意识到这是一个相当史诗般的问题,所以,谢谢你,如果你已经走到了这一步。 任何帮助将不胜感激!
关联设计为可读写 。 它们的很大一部分价值在于你可以做这样的事情:
@band.gigs << Gig.new(:venue => @venue)
听起来,就像你想要一些只读的东西一样。 换句话说,你想要关联场地和流派,但你永远不会这样做:
@venue.genres << Genre.new("post-punk")
因为它没有意义。 如果一个拥有该特定类型的乐队在那里有一个Gig,那么Venue只有一个类型。
协会不适用于此,因为它们必须是可写的。 这是我如何做只读关联:
class Genre has_many :bands def gigs Gig.find(:all, :include => 'bands', :conditions => ["band.genre_id = ?", self.id]) end def venues Venue.find(:all, :include => {:gigs => :band}, :conditions => ["band.genre_id = ?", self.id]) end end
您可以为关联添加条件和参数。 最新版本的ActiveRecord提供了named_scopes的强大function ,它也适用于相关记录。
来自当前的项目
Folder has_many Pages Page has_many Comments # In Page named_scope :commented, :include => "comments", :conditions => ["comments.id IS NULL OR comments.id IS NOT NULL"], :order => "comments.created_at DESC, pages.created_at DESC"
使用这个,我们可以说:
folder.pages.commented
这将在相关记录上进行范围,使用提供的参数进行条件化。
加! named_scopes是可组合的。
更多范围:
named_scope :published, :conditions => ["forum_topics.status = ?", "published"]
并将它们链接在一起:folder.pages.published.commented
对于像这样的关联,你最终会编写自定义SQL – 没有真正的方法可以处理像这样的关联链而不需要做一些相当大的连接,并且实际上没有一种有效的方法内置的查询生成器使用单行程序来处理它。
您也可以查看ActiveRecord的:joins参数 – 这可能会做你想要的。
听起来像nested_has_many_through的工作! 很棒的插件,可以让你做嵌套的has_many :throughs