使用HABTM和has_many:通过 - 需要帮助识别我错误的地方

时间:2011-08-07 03:41:02

标签: ruby-on-rails activerecord associations has-many has-many-through

大约一小时前,我问了关于轨道协会的问题:
Question on Proper Associations in Rails

这个问题的公认答案让我更深入地思考了人际关系,我想向SO社区介绍这种情况。

我之前的问题使用诗人,诗歌和印刷作为模型......对于这一个让我们使用音乐产业:

模型是:

  • 艺术家
  • 相册
  • 类型

以下陈述应被视为真实:

  1. 一张专辑可以有多位艺术家 - 即Metallica和Pantera发行圣诞专辑
  2. 一首歌可以属于多张专辑 - 即黄色潜水艇在原始专辑中以及披头士乐队的一些“Greatest hits”专辑
  3. 个别歌曲也可以有多位与专辑艺术家不同的“特色”艺术家 - 即Snoop Dogg拥有该专辑但是播放了一首以Harry Connick Jr.为特色的歌曲。或者更好的例子是当DJ发行专辑时ALL歌曲是由其他艺术家。
  4. 艺术家,专辑和歌曲都可归类为多种/不同类型 - 即Brian Setzer Orchestra被归类为“Swing”,其中一张专辑可能是“Swing,Rockabilly”,该专辑中的个人歌曲可能是“Jump Blues”。
  5. 在深入研究这个问题时,我立即看到了诸如Artist& amp;类型可以“重用”。我们不想多次保存艺术家的信息 - 例如,如果我们有一首主要艺术家和特色艺术家的歌曲,艺术家的信息应该存在于DB的“艺术家”表中。

    此外,在查看“特色”艺术家方面(声明#2)时,似乎我们还有一个应该在关联上的附加属性 - 类似于“特色”标志。

    以下是我认为应该如何设置关联 - 以及我的帖子的顶点....这是正确的,我怎样才能让它变得更好?

    class Artist < ActiveRecord::Base
      has_and_belongs_to_many :albums
      has_and_belongs_to_many :genres
      has_many :featurings
      has_many :features, :through => :featurings, :conditions => "featured = true"
    end
    
    class Album < ActiveRecord::Base
      has_and_belongs_to_many :artists
      has_and_belongs_to_many :songs
      has_many :featurings
      has_many :featured_artists, :through => :featurings, :conditions => "featured = true"
    end
    
    class Song < ActiveRecord::Base
      has_and_belongs_to_many :genres
      has_many :artists
      has_many :featurings
      has_many :featured_artists, :through => :featurings, :conditions => "featured = true"
    end
    
    class Genre < ActiveRecord::Base
      has_and_belongs_to_many :artists
      has_and_belongs_to_many :songs
    end
    
    class Featurings < ActiveRecord::Base
      # the db table for this class should have a "featured" boolean.
      belongs_to :artist
      belongs_to :album
      belongs_to :song
    end
    

    像往常一样,非常感谢那些花时间阅读和提供意见的人!非常感谢!

2 个答案:

答案 0 :(得分:5)

这不是一个讨论而是一个问题,但我会尝试解决你的问题。我没有测试你的任何代码,所以这些只是我的想法。

(Artist) has_any_belongs_to_many :genres

你需要一个单独的表吗?信息已经通过歌曲和流派之间的关联存储。除非艺术家可以属于流派,尽管没有所述类型的歌曲,否则你不应该在另一个HABTM协会中重现这些信息。

反之亦然,相反的关联可能是多余的:

(Genre) has_any_belongs_to_many :artists

至于你的特色方面的设计,似乎有一个功能设置为true的功能也是多余的。但是,这是因为命名,所以如果我是你,我会将它重命名为Release(歌曲可以在多个专辑中发布)。如果您认为艺术家和歌曲之间存在特色标志,您应该将标志添加到禁止实体(忘记了这个术语)。

然而,由于它表示为HABTM关联,因此没有介入模型。因此,您必须将此转换为在Song和Artist上使用has_many,其中插入的模型包含belongs_to关联以及特征标志。这实际上是发布模型。

这(再次没有进行过测试)将模型缩减为:

class Artist < ActiveRecord::Base
  has_and_belongs_to_many :albums
  has_many :releases
  has_many :songs, :through => :releases
  has_many :albums, :through => :releases
  has_many :featured_songs, through => :releases, :conditions => "featured = true"
end

class Album < ActiveRecord::Base
  has_and_belongs_to_many :artists
  has_many :releases
  has_many :songs, :through => :releases
  has_many :artists, :through => :releases
end

class Release < ActiveRecord::Base
  belongs_to :artist
  belongs_to :song
  belongs_to :album
  # there should be a featured boolean
end

class Song < ActiveRecord::Base
  has_and_belongs_to_many :genres
  has_many :releases
  has_many :artists, :through => :releases
  has_many :albums, :through => :releases
  has_many :featured_artists, through => :releases, :conditions => "featured = true"
end

class Genre < ActiveRecord::Base
  has_and_belongs_to_many :songs
end

答案 1 :(得分:2)

我不是HABTM协会的粉丝。所以我使用了has_many关联。为了解决属于专辑,歌曲等的类型,我使用了多态关联。解决方案非常复杂。但它满足了所有要求:

class Artist

  has_many :genre_links, :as => :genre_holder
  has_many :genres, :through => :genre_links

  has_many :artist_links

  has_many :albums, :through => :artist_links, 
                    :source => :artist_holder, :source_type => "Album"

  has_many :songs, :through => :artist_links, 
                    :source => :artist_holder, :source_type => "Song"


  has_many :featured_songs, :through => :artist_links, 
                    :source => :artist_holder, :source_type => "Song",
                    :conditions => {:featured => true}

end

使用:source_type选项为相册和歌曲创建关联。

class Genre
  has_many :genre_links
  has_many :albums, :through => :genre_links, 
                    :source => :genre_holder, :source_type => "Album"

  has_many :songs, :through => :genre_links, 
                    :source => :genre_holder, :source_type => "Song"

end

class GenreLink
  belongs_to :genre_holder, :polymorphic => true
  belongs_to :genre
end

class ArtistLink
  # featured
  belongs_to :artist
  belongs_to :artist_holder, :polymorphic => true 
end

我们需要一个自定义SQL来获取专辑的精选歌曲。

class Album < ActiveRecord::Base

  has_many :genre_links, :as => :genre_holder
  has_many :genres, :through => :genre_links

  has_many :artist_links, :as => :artist_holder, 
             :condition => {:featured => false}
  has_many :artists, :through => :artist_links


  has_many :album_songs 
  has_many :songs, :through => :album_songs

  has_many :featured_artists, :class => "Artist", :custom_sql => '
    SELECT A.* FROM artists A WHERE A.id IN (
      SELECT DISTINCT B.artist_id FROM artist_links B 
      WHERE B.artist_holder_type = "Song" AND B.featured = 1 AND
            B.artist_holder_id IN (#{song_ids.join(",")}))' 

end

现在剩下的课程了:

class AlbumSong
  belongs_to :album
  belongs_to :song
end

class Song < ActiveRecord::Base
  has_many :genre_links, :as => :genre_holder
  has_many :genres, :through => :genre_links

  has_many :album_songs 
  has_many :albums, :through => :album_songs

  has_many :artist_links, :as => :artist_holder, 
             :condition => {:featured => :false}
  has_many :artists, :through => :artist_links

  has_many :featured_artist_links, :class => "ArtistLink", 
             :as => :artist_holder, :condition => {:featured => :true }
  has_many :featured_artists, :through => :featured_artist_links, 
             :source => :artist

end 

艺术家可以与专辑和/或歌曲相关联。

album1.artists << artist1
song1.artists << artist1

将歌曲标记为精选:

Rails 3

song1.featured_artists << artist2 

Rails在创建期间自动设置关联的哈希条件参数。所以别无他法。

Rails 2.x

song1.featured_artist_links.create(:featured => true, :artist => artist2)