Да, вам нужно будет определить many-to-many relationship
между книжной полкой и книгой. Есть два способа добиться этого в Rails:
Вариант 1) Используйте has_and_belongs_to_many
См. руководство
Согласно официальной документации has_and_belongs_to_many
ассоциации:
Задает отношение «многие ко многим» с другим классом. Это связывает два класса через промежуточную таблицу соединений. Если таблица соединений не указана явно как опция, она угадывается с использованием лексического порядка имен классов. Таким образом, объединение между Developer и Project даст имя таблицы соединений по умолчанию «developers_projects», потому что «D» предшествует «P» в алфавитном порядке.
Итак, ваши классы должны выглядеть так:
class Bookshelf < ActiveRecord::Base
has_and_belongs_to_many :books
end
class Book < ActiveRecord::Base
has_and_belongs_to_many :bookshelves
has_many :reviews
end
Добавьте генерацию таблицы соединений к своим миграциям:
class CreateBooksBookshelvesJoinTable < ActiveRecord::Migration
def change
create_table :books_bookshelves, id: false do |t|
t.belongs_to :book, index: true
t.belongs_to :bookshelf, index: true
end
end
end
Это создаст таблицу books_bookshelves
в вашей базе данных. Таблица не будет иметь первичного ключа. К вашим моделям будет два внешних ключа Book
и Bookshelf
.
Итак, если вы вызовете self.books
в контексте книжной полки пользователя, вы получите список книг на книжной полке. И наоборот, вызов self.bookshelves
в контексте книги вернет набор книжных полок, которым принадлежит книга.
Проблема с этим подходом заключается в том, что каждый раз, когда вы добавляете новую книгу на книжную полку, в базе данных создается новая запись. Если вас это устраивает, нет более простого варианта, чем использовать has_and_belongs_to_many association
. В противном случае я рекомендую вам выбрать вариант № 2.
Вариант 2) Используйте has_many :through
Другой вариант — использовать has_many, :through association
(см. руководство). Для этого вам придется определить еще одну модель, но в некоторых случаях она может пригодиться (пример см. ниже).
Ваши классы должны выглядеть так:
class Bookshelf < ActiveRecord::Base
has_many :books, through: :books_bookshelves
has_many :books_bookshelves
end
class Book < ActiveRecord::Base
has_many :bookshelves, through: :books_bookshelves
has_many :books_bookshelves
has_many :reviews
end
class BooksBookshelf < ActiveRecord::Base
belongs_to :book
belongs_to :bookshelf
end
Вероятно, самое лучшее в использовании has_many :through association
— это то, что он позволяет вам добавлять пользовательские столбцы в таблицу соединения (например, добавить столбец count
, чтобы отслеживать, сколько книг одного и того же типа есть на книжной полке).
Миграция будет выглядеть почти так же, как та, которую мы использовали в Варианте 1, за исключением того факта, что мы добавляем уникальное ограничение для внешних ключей (обратите внимание, что добавление ограничения необязательно):
class CreateBooksBookshelvesJoinTable < ActiveRecord::Migration
def change
create_table :books_bookshelves, id: false do |t|
t.belongs_to :book, index: true
t.belongs_to :bookshelf, index: true
# add your custom columns here
end
add_index :books_bookshelves, [:book_id, :bookshelf_id], unique: true # to make sure you won't create duplicate records
end
end
При таком подходе добавление нового было бы немного сложнее, поскольку вам нужно было бы убедиться, что вы не вставляете повторяющиеся записи в таблицу соединения. (Однако вы можете удалить уникальное ограничение из миграции, чтобы добиться точно такого же поведения, как при has_and_belongs_to_many
.)
person
Juraj Višňovský
schedule
20.12.2015