我正在尝试了解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。 (我认为)。但是,这似乎有效here和here
我意识到这是一个相当史诗般的问题,所以谢谢你,如果你已经走到了这一步。任何帮助将不胜感激!
答案 0 :(得分:3)
关联设计为可读且可写。他们的很大一部分价值在于你可以做这样的事情:
@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
答案 1 :(得分:1)
您可以为关联添加条件和参数。 最近版本的ActiveRecord提供了 named_scopes 的强大功能,它也适用于相关记录。
来自当前项目
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
答案 2 :(得分:0)
对于像这样的关联,你最终会编写自定义SQL - 没有真正的方法可以处理这样的关联链而不必进行一些相当大的连接,并且实际上没有内置查询生成器使用单行程序处理它的有效方法。
你也可以查看ActiveRecord的:joins参数 - 这可能就是你想要的。
答案 3 :(得分:0)
听起来像nested_has_many_through的工作!很棒的插件,允许你做嵌套的has_many :throughs