Подтвердить что ты не робот

Создание отношений много-много в Rails

Это упрощенный пример того, чего я пытаюсь достичь, я относительно новичок в Rails и изо всех сил пытаюсь разобраться в отношениях между моделями.

У меня две модели: модель User и модель Category. Пользователь может быть связан со многими категориями. Определенная категория может отображаться в списке категорий для многих пользователей. Если определенная категория удалена, это должно быть отражено в списке категорий для пользователя.

В этом примере:

Моя таблица Categories содержит пять категорий:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| ID | Name                       |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| 1  | Sports                     | 
| 2  | News                       |
| 3  | Entertainment              |
| 4  | Technology                 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Моя таблица Users содержит двух пользователей:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| ID | Name                       |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| 1  | UserA                      | 
| 2  | UserB                      |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Пользователь может выбрать Спорт и Технологии как свои категории

Пользователь может выбрать Новости, Спорт и Развлечения

Категория спорта удалена, и списки категорий UserA и UserB отражают удаление

Я играл с созданием таблицы UserCategories, которая содержит идентификаторы как категории, так и пользователя. Этот вид работал, я мог искать имена категорий, но я не мог получить каскадное удаление для работы, и все решение просто показалось неправильным.

Примеры использования функций belongs_to и has_many, которые я нашел, похоже, обсуждают отображение взаимно-однозначного отношения. Например, комментарии к сообщению в блоге.

  • Как вы представляете это отношение "многие ко многим", используя встроенную функцию Rails?
  • Использует ли отдельную таблицу между двумя жизнеспособными решениями при использовании Rails?
4b9b3361

Ответ 1

Вы хотите отношения has_and_belongs_to_many. Руководство прекрасно описывает, как это работает с диаграммами и всем остальным:

http://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association

Вы получите что-то вроде этого:

# app/models/category.rb
class Category < ActiveRecord::Base
  has_and_belongs_to_many :users
end

# app/models/user.rb
class User < ActiveRecord::Base
  has_and_belongs_to_many :categories
end

Теперь вам нужно создать таблицу соединений для использования Rails. Rails не сделает это автоматически для вас. По сути, это таблица со ссылкой на каждую из категорий и пользователей, без первичного ключа.

Создайте миграцию из CLI следующим образом:

bin/rails g migration CreateCategoriesUsersJoinTable

Затем откройте его и отредактируйте так, чтобы оно соответствовало:

Для Rails 4.0. 2+ (включая Rails 5.2):

def change
  # This is enough; you don't need to worry about order
  create_join_table :categories, :users

  # If you want to add an index for faster querying through this join:
  create_join_table :categories, :users do |t|
    t.index :category_id
    t.index :user_id
  end
end

Rails <4.0.2:

def self.up
  # Model names in alphabetical order (e.g. a_b)
  create_table :categories_users, :id => false do |t|
    t.integer :category_id
    t.integer :user_id
  end

  add_index :categories_users, [:category_id, :user_id]
end

def self.down
  drop_table :categories_users
end

Имея это в виду, запустите миграцию, и вы сможете связать Категории и пользователей со всеми удобными средствами доступа, к которым вы привыкли:

User.categories  #=> [<Category @name="Sports">, ...]
Category.users   #=> [<User @name="UserA">, ...]
User.categories.empty?

Ответ 2

Просто дополнив ответ Coreyward выше: Если у вас уже есть модель, которая имеет отношение belongs_to, has_many и вы хотите создать новое отношение has_and_belongs_to_many используя ту же таблицу, вам потребуется:

rails g migration CreateJoinTableUsersCategories users categories

Затем,

rake db:migrate

После этого вам нужно будет определить ваши отношения:

User.rb:

class Region < ApplicationRecord
  has_and_belongs_to_many :categories
end

Category.rb

class Facility < ApplicationRecord
  has_and_belongs_to_many :users
end

Для того, чтобы заполнить новую таблицу соединений старыми данными, вам необходимо в консоли:

User.all.find_each do |u|
  Category.where(user_id: u.id).find_each do |c|
    u.categories <<  c
  end
end

Вы можете оставить user_id и category_id из таблиц Category и User или создать миграцию, чтобы удалить их.

Ответ 3

Самым популярным является "Монотранзитивная ассоциация", вы можете сделать это:

class Book < ApplicationRecord
  has_many :book_author
  has_many :author, through: :book_author
end

class BookAuthor < ApplicationRecord
  belongs_to :book
  belongs_to :author
end

class Author < ApplicationRecord
  has_many :book_author
  has_many :book, through: :book_author
end

Ассоциация has_many: through часто используется для установки соединения "многие ко многим" с другой моделью. Эта связь указывает на то, что декларирующая модель может быть сопоставлена с нулем или более экземплярами другой модели путем прохождения третьей модели. Например, рассмотрим медицинскую практику, в которой пациенты записываются на прием к врачу. Ссылка: https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association