【问题标题】:Complex associations in ActiveRecord modelsActiveRecord 模型中的复杂关联
【发布时间】:2009-04-22 17:00:20
【问题描述】:

我试图了解 ActiveRecord 如何处理比简单的has_manybelongs_to 等更复杂的关联。

例如,考虑一个用于录制音乐演出的应用程序。每个演出都有一个乐队,它有一个流派。每个 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

VenueRegion 以此类推。

但是,我似乎必须制作类似的东西

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。 (我认为)。然而,这似乎工作herehere

我意识到这是一个相当史诗般的问题,所以如果你能做到这一点,谢谢。任何帮助将不胜感激!

【问题讨论】:

    标签: sql ruby-on-rails activerecord


    【解决方案1】:

    关联被设计为可读和可写。它们的很大一部分价值在于您可以执行以下操作:

    @band.gigs << Gig.new(:venue => @venue)
    

    不过,这听起来像是您想要一些只读的东西。换句话说,您想将 Venues 和 Genres 关联起来,但您永远不会这样做:

    @venue.genres << Genre.new("post-punk")
    

    因为它没有意义。只有当具有特定流派的乐队在那里有演出时,场地才具有流派。

    关联对此不起作用,因为它们必须是可写的。以下是我如何进行只读关联:

    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
    

    【讨论】:

      【解决方案2】:

      您可以向关联中添加条件和参数。 最新版本的 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

      【讨论】:

        【解决方案3】:

        对于这样的关联,您最终会编写自定义 SQL —— 没有真正的方法可以处理这样的关联链而无需进行一些相当大量的连接,而且真的没有内置查询生成器使用单线处理它的有效方法。

        您也可以查看 ActiveRecord 的 :joins 参数 -- 这可能会满足您的需求。

        【讨论】:

          【解决方案4】:

          听起来像是nested_has_many_through 的工作!很棒的插件,可以让你做嵌套has_many :throughs

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-01-31
            • 2021-08-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多