Rails – validation嵌套属性唯一性与父级的范围父级

我在Rails中对父级父级的嵌套属性进行作用域唯一性validation时遇到问题。

背景

我有一个带有3个型号的rails 4应用程序:

#app/models/account.rb class Account < ActiveRecord::Base has_many :contacts, dependent: :destroy end #app/models/contact.rb class Contact < ActiveRecord::Base belongs_to :account has_many :email_addresses, dependent: :destroy, validate: :true, inverse_of: :contact accepts_nested_attributes_for :email_addresses,allow_destroy: true validates :email_addresses, presence: true end #app/models/email_address.rb class EmailAddress < ActiveRecord::Base belongs_to :contact, inverse_of: :email_addresses validates :label, presence: true validates :contact, presence: true validates :email, uniqueness: true, presence: true validates_email_format_of :email end 

问题

我想创建一个范围, 以确保模型EmailAddress 的属性 :电子邮件在帐户级别是唯一的 (帐户是Contact的父 ,它本身是EmailAddress的父级)。

正如http://guides.rubyonrails.org/active_record_validations.html所建议的,我试过:

  class EmailAddress < ActiveRecord::Base belongs_to :contact, inverse_of: :email_addresses validates :label, presence: true validates :contact, presence: true validates :email, presence: true, uniqueness: { scope: :account, message: "This contact email is already taken" } validates_email_format_of :email end 

这会引发错误“列email_addresses.account不存在”我该怎么办?

谢谢你的帮助!

下面描述了在性能方面更好的选择。 它经过测试,效果很好。

为什么?

当许多电子邮件受到威胁时,映射电子邮件会占用大量资源,因此最好直接使用数据库执行范围。

怎么样?

兑现EmailAddress模型中的account_id并执行beforevalidation方法。

1)创建迁移:

 change_table :email_addresses do |t| t.references :account, index: true end add_index :email_addresses, [:account_id, :email], unique: true 

2)迁移

3)更新EmailAddress模型

 #app/models/email_address.rb class EmailAddress < ActiveRecord::Base belongs_to :contact, inverse_of: :email_addresses belongs_to :account validates :label, presence: true validates :contact, presence: true validates_email_format_of :email validates_uniqueness_of :email, allow_blank: false, scope: :account before_validation do self.account = contact.account if contact end end 

我将提供一种可能的解决方案。 未经测试,但应该可以使用自定义validation和额外关联。

在您的Account模型中:

 has_many :email_addresses, through: :contacts 

在您的EmailAddress模型中:

 validate :uniqueness_of_mail private def uniqueness_of_mail account = contact.account if email in account.email_addresses.map(&:email) errors.add(email, 'Contact already has this email address') false else true end end