Определил отношения один к одному между двумя моделями, но все же, кажется, способен построить один ко многим - PullRequest
0 голосов
/ 14 июня 2019

Учитывая две модели Rails с отношением один к одному. WorkItem имеет один элемент ChemicalSafetyFeature, а элемент ChemicalSafetyFeature принадлежит WorkItem

class WorkItem < ActiveRecord::Base
  has_one :chemical_safety_feature
end 

class ChemicalSafetyFeature < ActiveRecord::Base    
  belongs_to :work_item
end 

Я перехожу к консоли rails и создаю WorkItem. Я получаю объект WorkItem с id = 1

WorkItem.create()

Затем я создаю один объект ChemicalSafetyFeature, подобный этому, и проверяю его

ChemicalSafetyFeature.create(work_item: WorkItem.first)

ChemicalSafetyFeature.workItem == WorkItem.first
WorkItem.first.chemical_safety_feature == ChemicalSafetyFeature.first

Но меня удивляет, если я создаю еще один элемент ChemicalSafetyFeature и связываю его с первым рабочим элементом:

ChemicalSafetyFeature.create(work_item = WorkItem.first)

Несмотря на то, что ChemicalSafetyFeature.first.work_item и ChemicalSafetyFeature.find (2) .work_item указывают на первый WorkItem, первый WorkItem указывает только на первый объект ChemicalSafetyFeature. Я ожидаю, что когда я пытаюсь создать второй объект ChemicalSafetyFeature, который связан с первым WorkItem, он должен выдать ошибку. Кажется, что я все еще могу создать два объекта ChemicalSafetyFeature, которые оба ссылаются на первый WorkItem, что означает, что первый WorkItem имеет 2 элемента ChenicalSafetyFeature

1 Ответ

0 голосов
/ 14 июня 2019
Отношение

A has_one не означает, что модель с отношением has_one принадлежит только одному объекту с отношением belongs_to. Это не означает, что существует только одна запись chemical_safety_features с этим work_item_id.

Вы можете применить это с проверкой уникальности:

class ChemicalSafetyFeature < ActiveRecord::Base
  validates :work_item_id, uniqueness: true
  belongs_to :work_item
end

Способ, которым работает эта проверка, заключается в запросе базы данных перед сохранением экземпляра ChemicalSafetyFeature, чтобы проверить, существует ли уже запись с этим work_item_id. Если запись найдена, она не может быть сохранена. Нарушение этого ограничения все еще возможно из-за состояния гонки.

Представьте, что в базе данных нет конфликтующих записей, и два процесса запрашивают в базе данных запись с work_item_id = 5, и ни один из процессов не находит никаких записей. Тогда оба процесса сохранят запись с work_item_id = 5, нарушая ограничение.

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

Создать миграцию с помощью этого add_index вызова:

add_index :chemical_safety_features, :work_item_id, unique: true

С этим индексом база данных будет применять ограничение, и код приложения не сможет нарушить ограничение.

Вы также должны определить work_item_id как внешний ключ в базе данных, используя миграцию add_foreign_key. Это предотвратит вставку значений мусора в это поле. Это также предотвратит потерянные записи - вы не сможете удалить запись из work_items, если в таблице chemical_safety_features есть ссылка на эту запись.

Наконец, вы должны указать rails, что делать, если вы вызываете #destroy для WorkItem экземпляра, который принадлежит ChemicalSafetyFeature. Взгляните на параметр dependent для отношений has_one и has_many.

Например, если вы укажете

class WorkItem < ActiveRecord::Base
  has_one :chemical_safety_feature, dependent: :destroy
end 

Затем вызов #destroy на WorkItem вызовет destroy на соответствующий ChemicalSafetyFeature. Другие значения для опции dependent: :delete, :nullify, :restrict_with_exception и :restrict_with_error.

.

Рекомендуется всегда указывать параметр dependent для всех отношений has_one и has_many.

...