定义了两个模型之间的一对一关系,但似乎仍然能够建立一对多
Defined one to one relationship between 2 models but still seem to be able to build one to many
给定两个具有一对一关系的 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。我得到一个 ID = 1
的 WorkItem 对象
WorkItem.create()
然后我像这样创建一个 ChemicalSafetyFeature 对象并对其进行测试
ChemicalSafetyFeature.create(work_item: WorkItem.first)
ChemicalSafetyFeature.workItem == WorkItem.first
WorkItem.first.chemical_safety_feature == ChemicalSafetyFeature.first
但如果我创建另一个 ChemicalSafetyFeature 并将其 link 到第一个工作项,这让我感到惊讶:
ChemicalSafetyFeature.create(work_item = WorkItem.first)
即使 ChemicalSafetyFeature.first.work_item 和 ChemicalSafetyFeature.find(2).work_item 指向第一个 WorkItem,但第一个 WorkItem 仅指向第一个 ChemicalSafetyFeature 对象。
我的期望是,当我尝试创建与第一个 WorkItem 关联的第二个 ChemicalSafetyFeature 对象时,它应该会引发错误。似乎我仍然可以创建两个 ChemicalSafetyFeature 对象,它们都 link 到第一个 WorkItem,这意味着第一个 WorkItem 有 2 个 ChenicalSafetyFeature 项目
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
有了这个索引,数据库将强制执行约束,应用程序代码将无法违反约束。
您还应该使用 add_foreign_key
迁移将 work_item_id
定义为数据库中的外键。这将防止向该字段插入垃圾值。它还将防止孤立记录 - 如果在 chemical_safety_features
table.
中引用了该记录,您将无法从 work_items
中删除该记录
最后,如果您在属于 ChemicalSafetyFeature
的 WorkItem
实例上调用 #destroy
,您应该告诉 rails 该怎么做。查看 has_one
和 has_many
关系的 dependent
选项。
例如,如果您指定
class WorkItem < ActiveRecord::Base
has_one :chemical_safety_feature, dependent: :destroy
end
然后在 WorkItem
上调用 #destroy
将在相关的 ChemicalSafetyFeature
上调用 destroy
。 dependent
选项的其他值为 :delete
、:nullify
、:restrict_with_exception
和 :restrict_with_error
。
最好始终在所有 has_one
和 has_many
关系上指定 dependent
选项。
给定两个具有一对一关系的 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。我得到一个 ID = 1
的 WorkItem 对象WorkItem.create()
然后我像这样创建一个 ChemicalSafetyFeature 对象并对其进行测试
ChemicalSafetyFeature.create(work_item: WorkItem.first)
ChemicalSafetyFeature.workItem == WorkItem.first
WorkItem.first.chemical_safety_feature == ChemicalSafetyFeature.first
但如果我创建另一个 ChemicalSafetyFeature 并将其 link 到第一个工作项,这让我感到惊讶:
ChemicalSafetyFeature.create(work_item = WorkItem.first)
即使 ChemicalSafetyFeature.first.work_item 和 ChemicalSafetyFeature.find(2).work_item 指向第一个 WorkItem,但第一个 WorkItem 仅指向第一个 ChemicalSafetyFeature 对象。 我的期望是,当我尝试创建与第一个 WorkItem 关联的第二个 ChemicalSafetyFeature 对象时,它应该会引发错误。似乎我仍然可以创建两个 ChemicalSafetyFeature 对象,它们都 link 到第一个 WorkItem,这意味着第一个 WorkItem 有 2 个 ChenicalSafetyFeature 项目
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
有了这个索引,数据库将强制执行约束,应用程序代码将无法违反约束。
您还应该使用 add_foreign_key
迁移将 work_item_id
定义为数据库中的外键。这将防止向该字段插入垃圾值。它还将防止孤立记录 - 如果在 chemical_safety_features
table.
work_items
中删除该记录
最后,如果您在属于 ChemicalSafetyFeature
的 WorkItem
实例上调用 #destroy
,您应该告诉 rails 该怎么做。查看 has_one
和 has_many
关系的 dependent
选项。
例如,如果您指定
class WorkItem < ActiveRecord::Base
has_one :chemical_safety_feature, dependent: :destroy
end
然后在 WorkItem
上调用 #destroy
将在相关的 ChemicalSafetyFeature
上调用 destroy
。 dependent
选项的其他值为 :delete
、:nullify
、:restrict_with_exception
和 :restrict_with_error
。
最好始终在所有 has_one
和 has_many
关系上指定 dependent
选项。