ActiveRecord много-много ли мне сделать другую таблицу для хранения информации об отношениях? - PullRequest
0 голосов
/ 26 мая 2018

У меня есть таблицы albums и artists, которые должны соотноситься со многими из многих: каждый альбом может принадлежать нескольким артистам, и у каждого артиста может быть несколько альбомов.Но мне нужно знать, является ли художник главным художником или просто автором.

В настоящее время я просто использую столбец artists в таблице albums для хранения идентификатора артиста в разделенных точкой с запятой строках (формат: 3; 6; 343; 56; 1).Сначала должен быть указан основной идентификатор артиста, остальное просто авторы.

В настоящее время я получаю доступ к вкладу исполнителя в альбомы по запросу с ключевыми словами .where и LIKE.Затем отфильтруйте результат массива, чтобы исключить префикс (идентификатор исполнителя), основываясь на этом ответе.

@albums_ = Album.where("artists LIKE :prefix", prefix: "%#{params[:id]}%")
@albums_contribute_to = @albums_.select { |album| !album.artists.start_with?("#{params[:id]}") }

Есть ли более эффективный способ сделать это?

Ответы [ 4 ]

0 голосов
/ 26 мая 2018

Согласитесь с Джошуа и Пшемеком, это простое решение.вот как это использовать:

class Appearance < ActiveRecord::Base
  belongs_to :artist
  belongs_to :album
  # has attribute "primary"
end

class Artist < ActiveRecord::Base
  has_many :appearances
  has_many :albums, through: :appearances
end

class Album < ActiveRecord::Base
  has_many :appearances
  has_many :artists, through: :appearances
end

# create album with 2 artists
Album.create(
  name: "foo_bar",
  appearances: [
    Appearance.new(artist: Artist.create(name: "foo"), primary: true),
    Appearance.new(artist: Artist.create(name: "bar"))
  ]
)

# list artists
album = Album.first
album.appearances.order("piramary desc").each do |appearance|
  puts "#{appearance.artist.name} #{appearance.primary? ? "(Primary)" : ""}"
end
0 голосов
/ 26 мая 2018

Я действительно не думаю, что вам нужна отдельная модель, которая объединяет Album и Artist.Таким образом, вы можете использовать метод has_and_belong_to_many (HABTM), который исходит от ActiveRecord и используется для переходных отношений многих ко многим .Вам также понадобится таблица, объединяющая две модели, и вы легко сможете создать таблицу с миграцией.

Я настоятельно рекомендую вам прочитать эту статью .Это будет действительно полезно для вас.

0 голосов
/ 26 мая 2018

Немного поиграв с этим, большая идея заключалась в том, чтобы поместить некоторую дополнительную информацию в таблицу соединений (в моем примере я называю это primary).А затем прочитайте документы , чтобы понять, как сказать ActiveRecord их использовать.

# setup active record
require 'active_record'
ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:'

# some tables (migrations / schema)
ActiveRecord::Schema.define do
  create_table(:artists) { |t| t.string :name}
  create_table(:albums)  { |t| t.string :name }
  create_table :appearances do |t|
    t.integer :artist_id
    t.integer :album_id
    t.boolean :primary, default: false # <-- join table specifies who is primary
  end
end

# some models
class Artist < ActiveRecord::Base
  has_many :appearances
  has_many :albums, through: :appearances
end   

class Appearance < ActiveRecord::Base
  belongs_to :artist
  belongs_to :album
end

class Album < ActiveRecord::Base
  # three associations to appearances
  has_many :all_appearances,                                   class_name: 'Appearance'
  has_many :primary_appearances,  -> { where primary: true  }, class_name: 'Appearance'
  has_many :featured_appearances, -> { where primary: false }, class_name: 'Appearance'

  # three associations to artists
  has_many :all_artists,      through: 'all_appearances',      source: 'artist'
  has_many :primary_artists,  through: 'primary_appearances',  source: 'artist'
  has_many :featured_artists, through: 'featured_appearances', source: 'artist'
end

# some artists
dre  = Artist.create! name: 'Dr. Dre'
dogg = Artist.create! name: 'Snoop Dogg'
slim = Artist.create! name: 'Eminem'

# some albums
weed = Album.create! name: 'The Chronic 2001',
                     primary_artists:  [dre],
                     featured_artists: [dogg, slim]

show = Album.create! name: 'The Eminem Show',
                     primary_artists:  [slim],
                     featured_artists: [dre]

# it understands the associations
weed.all_artists.pluck :name      # => ["Dr. Dre", "Snoop Dogg", "Eminem"]
weed.primary_artists.pluck :name  # => ["Dr. Dre"]
weed.featured_artists.pluck :name # => ["Snoop Dogg", "Eminem"]

# might be nice to add similar scoped associations to get from Artist to Album
weed               # => #<Album id: 1, name: "The Chronic 2001">
  .primary_artists # => #<ActiveRecord::Associations::CollectionProxy [#<Artist id: 1, name: "Dr. Dre">]>
  .first           # => #<Artist id: 1, name: "Dr. Dre">
  .albums          # => #<ActiveRecord::Associations::CollectionProxy [#<Album id: 1, name: "The Chronic 2001">, #<Album id: 2, name: "The Eminem Show">]>
  .last            # => #<Album id: 2, name: "The Eminem Show">
  .primary_artists # => #<ActiveRecord::Associations::CollectionProxy [#<Artist id: 3, name: "Eminem">]>
  .first           # => #<Artist id: 3, name: "Eminem">
0 голосов
/ 26 мая 2018

Это типичный случай использования объединенной таблицы.Если вы хотите создать такие отношения.То, что вы делаете, создаст проблемы только в долгосрочной перспективе, так как вам придется хранить эту информацию в памяти, что тоже медленно.

В ruby ​​на рельсах это отношение должно что-то вроде этого:

class Album < ApplicationRecord
  has_many :album_artists
  has_many :artists, through: :album_artists
end

class AlbumArtist < ApplicationRecord
  belongs_to :artist
  belongs_to :album
end

class Artist < ApplicationRecord
  has_many :album_artists
  has_many :albums, through: :album_artists
end   

Если вы не хотите делать это с ruby ​​на рельсах, вам нужно реализовать пользовательский запрос, но правила те же, вы должны создать некую объединенную таблицу.

...