Мы можем соединить две таблицы с помощью нескольких ассоциаций одновременно. Например:
User has_many: :tasks
иUser has_many: :tasks, through: :projects
. Существует множество потенциальных применений этого метода. Я буду использовать пример приложения для управления задачами, в котором пользователи имеют прямую связь с задачами, которые они создают, и косвенную связь с задачами, находящимися в проектах, которыми с ними поделились другие пользователи.
- Решение TL;DR
- Объяснение
- Шаг 1: Создание таблиц и моделей
- Шаг 2: Многие ко многим: Проекты и пользователи с помощью таблицы Join
- Шаг 3: Один-ко-многим: Прямое объединение пользователей и задач
- Шаг 4: Один-ко-многим: Косвенная ассоциация пользователей и задач через проекты
- Что такое прямая ассоциация?
- Что такое косвенная ассоциация?
Решение TL;DR
Чтобы установить множественные ассоциации между двумя таблицами, используйте другое имя для атрибута и используйте свойство source
для определения связываемой таблицы. Мы можем использовать has_many, :through
для определения косвенной ассоциации и has_many
для создания прямой ассоциации.
has_many :tasks`
has_many :project_tasks, through: :projects, source: :tasks
Далее я рекомендую создать метод модели для доступа ко всем задачам пользователя. Помните, что две коллекции могут пересекаться, поэтому мы будем использовать метод uniq
для удаления дубликатов:
def all_qr_codes
(self.qr_codes + self.shared_qr_codes).uniq
end
Примечание: В своих примерах я использую Rails 7.0.3.1. Другие версии могут не поддерживать этот синтаксис, но принципы остаются в силе.
Объяснение
Это краткий ответ, теперь перейдем к деталям. Давайте создадим отношения между тремя моделями в гипотетическом приложении для управления задачами.
Вот наша конечная цель:
- Пользователи смогут создавать Задачи и Проекты.
- Пользователи смогут добавлять задачи в проекты.
- Пользователи смогут добавлять других пользователей в проекты.Сложная часть:
- Не каждая задача будет частью проекта, поэтому пользователям нужна прямая связь с задачами.
- У пользователей не будет прямой ассоциации с задачами, связанными с проектами, которые были разделены другими пользователями. Для этого потребуется косвенная ассоциация
has_many :through
.
Мы хотим дать Пользователям отношения has_many
с Задачами, отношения has_many
с Проектами, и отношения has_many :through
, связывающие Пользователей с Задачами через Проекты. Однако если вы попытаетесь добавить и то, и другое в модель User, используя имя :tasks
для обеих ассоциаций, Rails не сможет обработать миграцию. Вот почему использование другого имени для отношения has_many :through
работает.
Далее я проведу вас через создание этой схемы от начала до конца. Эти инструкции предполагают, что вы уже создали проект Rails и имеете базовое понимание Ruby on Rails.
Шаг 1: Создание таблиц и моделей
Мы создадим базовые модели для Пользователей, Проектов, Задач. Я рекомендую использовать генераторы Rails, если вы умеете это делать, но вот как должны выглядеть соответствующие миграции базы данных (с минимальным количеством полей). Мы начнем с миграций для создания таблиц для моделей:
class CreateUsers < ActiveRecord::Migration[7.0]
def change
create_table :users do |t|
t.string :username
t.timestamps
end
end
end
class CreateTasks < ActiveRecord::Migration[7.0]
def change
create_table :tasks do |t|
t.string :title
t.belongs_to :user
t.belongs_to :project, optional: true
t.timestamps
end
end
end
class CreateProjects < ActiveRecord::Migration[7.0]
def change
create_table :projects do |t|
t.string :title
t.timestamps
end
end
end
Шаг 2: Многие ко многим: Проекты и пользователи с помощью таблицы Join
Для создания отношения «многие-ко-многим» между пользователями и проектами мы можем использовать таблицу объединения. Вы можете выполнить команду :
rails generate migration CreateProjectsUsersJoinTable projects users
В результате миграции будет создана таблица projects_users. Каждая запись будет хранить 2 внешних ключа, определяющих связь между пользователем и проектом:
class CreateProjectsUsers < ActiveRecord::Migration[7.0]
def change
create_table :projects_users do |t|
t.references :user, foreign_key: true
t.references :project, foreign_key: true
t.timestamps
end
end
end
Нам нужно добавить эти строки в модели (обратите внимание на порядок ассоциаций, мы должны подключиться к projects_users
, прежде чем подключаться к projects
через него):
class User < ActiveRecord::Base
has_many :projects_users
has_many :projects, through: :projects_users
end
class Project < ActiveRecord::Base
has_many :projects_users
has_many :users, through: :projects_users
end
Шаг 3: Один-ко-многим: Прямое объединение пользователей и задач
Давайте добавим has_many :tasks
в модель User и модель Project. Убедитесь, что вы добавили столбцы t.belongs_to :user
и t.belongs_to :project
в таблицу Tasks, которая будет хранить внешние ключи. Затем мы можем добавить эти строки в модели:
class User < ActiveRecord::Base
has_many :projects_users
has_many :projects, through: :projects_users
has_many :tasks
end
class Project < ActiveRecord::Base
has_many :projects_users
has_many :users, through: :projects_users
has_many :tasks
end
class Task < ActiveRecord::Base
belongs_to :user
belongs_to :project
end
Шаг 4: Один-ко-многим: Косвенная ассоциация пользователей и задач через проекты
Наконец, чтобы создать отношение has_many :through
между пользователями и задачами через проекты, мы добавим эту строку в модель User:
class User < ApplicationRecord
has_many :projects_users
has_many :projects, through: :projects_users
has_many :tasks
has_many :project_tasks, through: :projects, source: :tasks
end
И, как я уже говорил, мы можем создать метод модели для запроса всех Задач пользователя. Помните, что эти две коллекции могут пересекаться, поэтому мы воспользуемся методом uniq
для удаления дубликатов:
def all_qr_codes
(self.qr_codes + self.shared_qr_codes).uniq
end
Что такое прямая ассоциация?
Прямая ассоциация — это когда запись имеет столбец, содержащий идентификатор другой записи, с которой она связана. Обычно это относится к отношениям «один-ко-многим», когда дочерняя модель имеет столбец, содержащий ID родительской модели. Вот пример прямой связи «один-ко-многим» в Rails:
class Author < ApplicationRecord
has_many :books
end
class Book < ApplicationRecord
belongs_to :author
end
Что такое косвенная ассоциация?
Косвенные ассоциации используются для определения связи между двумя записями через взаимную запись, с которой они связаны. Это часто используется для создания объединенных таблиц. В следующем примере врачи и пациенты связаны через взаимные записи о приеме. Назначения имеют прямую связь «один-ко-многим» с врачами и пациентами. При использовании «через:» создается косвенная связь «многие-ко-многим» между врачами и пациентами, имеющими общие записи на прием.
class Physician < ApplicationRecord
has_many :appointments
has_many :patients, through: :appointments
end
class Appointment < ApplicationRecord
belongs_to :physician
belongs_to :patient
end
class Patient < ApplicationRecord
has_many :appointments
has_many :physicians, through: :appointments
end
Вот и все! Надеюсь, это объяснение помогло вам настроить вашу базу данных Rails и прояснило, как has_many
и has_many :through
можно использовать для создания прямых и косвенных ассоциаций. Если вы знаете лучшее решение для связи пользователей с задачами через проекты и напрямую, оставьте комментарий и дайте мне знать!