У цьому посібнику ми розглянемо один-до-одного асоціації в Rails за допомогою has_one та belongs_to. Ми охопимо:
1️⃣ Вступ
2️⃣ Коли використовувати has_one та belongs_to
3️⃣ Налаштування один-до-одного асоціації
4️⃣ Визначення асоціацій
5️⃣ Створення та доступ до даних
6️⃣ Налаштування асоціацій
7️⃣ Запит один-до-одного асоціацій
8️⃣ Обробка видалення (dependent опція)
9️⃣ Повний приклад з міграціями
🔟 Висновок
1️⃣ Вступ
У Rails один-до-одного відношення означає, що один запис в таблиці пов'язаний з рівно одним записом в іншій таблиці.
Це реалізовано за допомогою has_one та belongs_to.
2️⃣ Коли використовувати has_one та belongs_to
| Асоціація | Модель | Чужий ключ? | Мета |
| --- | --- | --- | --- |
| belongs_to | Дочірня модель | ✅ Так | Вказує, що модель належить іншій моделі. |
| has_one | Батьківська модель | ❌ Ні | Вказує, що модель має один пов'язаний запис. |
Приклад: Користувач та Профіль
- Користувач має один профіль (
has_one) - Профіль належить користувачу (
belongs_toі зберігає чужий ключuser_id)
3️⃣ Налаштування один-до-одного асоціації
Крок 1: Створення моделей
Запустіть наступну команду для генерації моделей User та Profile:
rails g model User name:string email:string
rails g model Profile bio:text user:references
user:references автоматично додає чужий ключ user_id до таблиці profiles.
Крок 2: Міграція бази даних
rails db:migrate
4️⃣ Визначення асоціацій
Модель Користувача (has_one)
class User < ApplicationRecord
has_one :profile, dependent: :destroy
end
has_one :profile→ Користувач має один профіль.dependent: :destroy→ Видаляє профіль, коли користувач видаляється.
Модель Профілю (belongs_to)
class Profile < ApplicationRecord
belongs_to :user
end
belongs_to :user → Профіль належить користувачу (містить чужий ключ user_id).
5️⃣ Створення та доступ до даних
Створення користувача з профілем
user = User.create(name: "Alice", email: "[email protected]")
profile = Profile.create(bio: "Software Developer", user: user)
або за допомогою Active Record Association Helper:
user.create_profile(bio: "Software Developer")
Доступ до пов'язаних записів
user.profile.update(bio: "Senior Developer")
Видалення пов'язаних записів
user.destroy # Видаляє користувача та пов'язаний профіль завдяки `dependent: :destroy`
6️⃣ Налаштування асоціацій
Користувацький чужий ключ
За замовчуванням, Rails використовує {model}_id (наприклад, user_id), але ви можете змінити його:
class Profile < ApplicationRecord
belongs_to :user, foreign_key: "account_id"
end
class User < ApplicationRecord
has_one :profile, foreign_key: "account_id"
end
Користувацька назва таблиці
Якщо назва таблиці не відповідає стандартам Rails, вкажіть її вручну:
class Profile < ApplicationRecord
self.table_name = "user_profiles"
end
Користувацький первинний ключ
Якщо User використовує uuid замість id як первинний ключ:
class User < ApplicationRecord
self.primary_key = "uuid"
has_one :profile, foreign_key: "user_uuid"
end
class Profile < ApplicationRecord
belongs_to :user, foreign_key: "user_uuid", primary_key: "uuid"
end
7️⃣ Запит один-до-одного асоціацій
Попереднє завантаження даних (includes)
Уникайте запитів типу N+1:
User.includes(:profile).where(profiles: { bio: "Developer" })
Об'єднання таблиць (joins)
User.joins(:profile).where(profiles: { bio: "Developer" })
Пошук пов'язаних записів
User.find(1).profile
Profile.find(1).user
8️⃣ Обробка видалення (dependent опція)
| Опція | Поведінка |
| --- | --- |
| dependent: :destroy | Видаляє пов'язаний профіль, коли користувач видаляється |
| dependent: :nullify | Встановлює user_id у profiles на NULL |
| dependent: :restrict_with_error | Запобігає видаленню користувача, якщо профіль існує |
Приклад:
class User < ApplicationRecord
has_one :profile, dependent: :nullify
end
9️⃣ Повний приклад з міграціями
Міграція для users
class CreateUsers < ActiveRecord::Migration[7.0]
def change
create_table :users do |t|
t.string :name
t.string :email
t.timestamps
end
end
end
Міграція для profiles
class CreateProfiles < ActiveRecord::Migration[7.0]
def change
create_table :profiles do |t|
t.text :bio
t.references :user, null: false, foreign_key: true
t.timestamps
end
end
end
🔟 Висновок
✅ Основні моменти
- Використовуйте
has_oneна батьківській моделі (не зберігає чужий ключ). - Використовуйте
belongs_toна дочірній моделі (зберігає чужий ключ). - Налаштуйте чужі ключі, назви таблиць та первинні ключі, якщо це необхідно.
- Використовуйте
dependent: :destroyдля управління видаленням. - Оптимізуйте запити за допомогою
includes,joins, та попереднього завантаження (eager loading).
Перекладено з: Ruby on Rails One-to-One Association (hasone & belongsto) - Complete Guide