我想知道在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中强制执行此操作,但是我只看到了在同一个表的多个列上添加唯一性约束的选项。
或者,我已经编写了一个自定义验证器,用于执行我想要的操作,但这很麻烦。需要一个更好的解决方案......
为了完整性,这里是约束验证器,它需要一个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
答案 0 :(得分:1)
我刚刚在我的代码中使用了类似的
validates :child_name, uniqueness: { scope: :parent_id }
更多..
(i)https://apidock.com/rails/ActiveRecord/Validations/ClassMethods/validates_uniqueness_of
答案 1 :(得分:0)
由铁路上的红宝石官方文件the-has-many-through-association进行了操作:
class CreateAppointments < ActiveRecord::Migration[5.0]
def change
create_table :domains do |t|
t.string :name, null: false
t.string :domain_type
t.timestamps
end
create_table :subjects do |t|
t.string :name, null: false
t.string :subject_type
t.timestamps
end
create_table :fields do |t|
t.belongs_to :domain, index: true
t.belongs_to :subject, index: true
t.timestamps
end
end
end
注意强>
我选择了JoinTableDomainSubject
的{{1}}重新命名,以便更具可读性。
我还强制Field
字段不是name
来检查唯一性。
(在迁移文件中添加nil
,在两个模型中添加null: false
)
现在专门的课程:
validates :name, presence: true
由于模型已关联,我可以使用class Subject < ApplicationRecord
has_many :fields
has_many :domains, through: :fields
validates :name, presence: true
end
class Domain < ApplicationRecord
has_many :fields
has_many :subjects, through: :fields
validates :name, presence: true
end
class Field < ApplicationRecord
belongs_to :domain
belongs_to :subject
validate :domain_and_subject_names_uniqueness
private
def domain_and_subject_names_uniqueness
if class.includes(:domain, subject)
.where(domain: { name: domain.name }, subject: { name: subject.name })
.exists?
errors.add :field, 'duplicity on names'
end
end
end
访问给定Field.first.domain
的{{1}}模型,反之亦然 。