Ruby on rails Rails-使用父对象的作用域验证嵌套属性的唯一性
我对Rails中父项为parent的嵌套属性的作用域唯一性验证有问题 背景 我有一个rails 4应用程序,有3种型号:Ruby on rails Rails-使用父对象的作用域验证嵌套属性的唯一性,ruby-on-rails,scope,nested-attributes,validates-uniqueness-of,Ruby On Rails,Scope,Nested Attributes,Validates Uniqueness Of,我对Rails中父项为parent的嵌套属性的作用域唯一性验证有问题 背景 我有一个rails 4应用程序,有3种型号: #app/models/account.rb class Account < ActiveRecord::Base has_many :contacts, dependent: :destroy end #app/models/contact.rb class Contact < ActiveRecord::Base belongs_to :accoun
#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
#app/models/account.rb
类帐户
问题
我想创建一个范围,,以确保属性:型号EmailAddress的电子邮件在帐户级别是唯一的(帐户是联系人的父级,而联系人本身就是EmailAddress的父级)
正如在会议上建议的那样,我尝试:
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
class EmailAddress
这会引发错误“列email_addresses.account不存在”
我该怎么办
谢谢你的帮助 我将提供一个可能的解决方案。未测试,但它应该可以使用自定义验证和额外关联。
在您的
帐户中
型号:
has_many :email_addresses, through: :contacts
validate :uniqueness_of_mail
private
def uniqueness_of_mail
account = contact.account
if account.email_addresses.map(&:email).includes? email
errors.add(email, 'Contact already has this email address')
false
else
true
end
end
在您的电子邮件地址中
型号:
has_many :email_addresses, through: :contacts
validate :uniqueness_of_mail
private
def uniqueness_of_mail
account = contact.account
if account.email_addresses.map(&:email).includes? email
errors.add(email, 'Contact already has this email address')
false
else
true
end
end
下文介绍了性能方面更好的选择。它经过测试,工作正常 为什么? 当大量电子邮件处于危险中时,映射电子邮件可能会消耗大量资源,因此最好直接使用数据库执行范围 怎么做? 在EmailAddress模型中兑现帐户\u id并执行验证前方法 1) 创建迁移:
change_table :email_addresses do |t|
t.references :account, index: true
end
add_index :email_addresses, [:account_id, :email], unique: true
2) 迁移
3) 更新电子邮件地址模型
#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
#app/models/email_address.rb
类EmailAddress
可能是这项工作。将其添加到您的电子邮件地址模型验证:电子邮件,:唯一性=>{:scope=>:contact\u id}
事实上,我希望为帐户而不是联系人设置一个范围。您应该将所属的\u账户添加到电子邮件地址
模型以实现这一点。这将使我的数据库架构复杂化。有没有更好的方法在不改变db模式的情况下实现这个范围?我建议您在account.rb中添加一个嵌套的关联。更具体地说,有很多:电子邮件地址,通过::contacts
开始。感谢您的回答。它很好用。只是一个问题:如果在电子邮件地址中我们没有添加“属于”\u to:account,是否会出现问题?我认为您不能有一个属于”\u to:account
,因为它是派生关联。但是您可以执行def account\n contact.account\n end
来拥有@emailaddress.account
。否则,它就是@emailaddress.contact.account
现在的状态。在account.email\u addresses.map(&:email)
在我最新版本的ruby/rails上失败。尝试account.email\u addresses.map(&:email)。包括?改为发电子邮件。这确实是一个很好的解决方案。然而,我的解决方案的地址范围非常小,所以这里唯一的实际区别是索引。不过,这是一个更好的解决方案。向上投票。谢谢:)Ruby Racer