【发布时间】:2017-12-25 19:58:14
【问题描述】:
我想知道对 Rails 中的两个相关模型属性实施唯一性约束的最佳方法它们都不是主键
class Parent > ApplicationRecord
has_many :children
:name
end
class Child > ApplicationRecord
:name
end
我想强制 (parent.name, child.name) 对每个父母都是唯一的。例如
-
允许
(parent1, child1)和(parent2, child1) -
(parent1, child1)和(parent1, child1)是违规行为
理想情况下,我会在 Postgres 中强制执行此操作,但是我只看到了在同一个表的多个列上添加唯一性约束的选项。
另外,我为 Rails 编写了一个自定义验证器,可以满足我的要求,但这很麻烦。需要有更好的解决方案...
为了完整起见,这里是约束验证器,它需要将children 函数添加到返回子列表的模型中。
class NamePairValidator < ActiveModel::Validator
def validate(record)
record.children.values.each do |model_children|
names = model_children.to_a.collect {|model| model.name}
if (names.select{|name| names.count(name) > 1 }.size > 0)
record.errors[:name] << 'Path leading to this resource has no unique name'
end
end
end
end
(在 Parent.rb 中)
def children
{children: :children}
end
迁移:
class CreateDomains < ActiveRecord::Migration[5.0]
def change
create_table :domains do |t|
t.string :name
t.string :domain_type
t.timestamps
end
end
end
class CreateSubjects < ActiveRecord::Migration[5.0]
def change
create_table :subjects do |t|
t.string :name
t.string :subject_type
t.timestamps
end
end
end
class CreateJoinTableDomainSubject < ActiveRecord::Migration[5.0]
def change
create_join_table :domains, :subjects do |t|
t.index [:domain_id, :subject_id]
t.index [:subject_id, :domain_id]
end
end
end
【问题讨论】:
-
你能展示你是如何为你的两个模型编写迁移文件的吗?因为您还可以添加索引以确保数据库级别的完整性,例如robots.thoughtbot.com/the-perils-of-uniqueness-validations
-
如果您想在数据库级别执行此操作,您可以将
parent_id列和child_id更改为parent_name和child_name列并在它们上添加唯一约束。使用一些 ActiveRecord 黑客可以将这些列用作外键。我不确定这个解决方案只是一个想法。 -
谢谢!我想你有一个数据透视表!这就是为什么尼廷的回答不起作用的原因。请查看此链接:guides.rubyonrails.org/…。它可能会帮助您处理语法和关系
-
很遗憾,我的解决方案不适用于数据透视表。
-
有什么建议可以实现超过 2 个级别的通用解决方案吗?例如A有很多B,B有很多C,C有很多D。A、B、C、D都有名字属性。该路径类似于 REST-full 资源的路径。所有 A、B、C 和 D 型号也有一个 ID 字段。例如a/b 必须是唯一的,而且 a/b/c 和 a/b/c/d 也一样
标签: ruby-on-rails ruby postgresql validation ruby-on-rails-5