[英]Rails - Validate Nested Attributes Uniqueness with scope parent of parent
I have a problem with the scoped uniqueness validation in Rails for nested attributes with a parent of parent. 我在Rails中对父级父级的嵌套属性进行作用域唯一性验证时遇到问题。
Background 背景
I have a rails 4 application with 3 models : 我有一个带有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
Issue 问题
I want make a scope, so as to make sure the attribute :email of the model EmailAddress is unique at the Account Level (Account is parent of Contact, which is itself parent of EmailAddress). 我想创建一个范围, 以确保模型EmailAddress 的属性 :电子邮件在帐户级别是唯一的 (帐户是Contact的父级 ,它本身是EmailAddress的父级)。
As suggested at http://guides.rubyonrails.org/active_record_validations.html , I tried : 正如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
This raises the error "column email_addresses.account does not exist" What should I do ? 这会引发错误“列email_addresses.account不存在”我该怎么办?
Thanks for you help ! 谢谢你的帮助!
A better option in terms of performances is described below. 下面描述了在性能方面更好的选择。 It is tested and works just fine.
经过测试,效果很好。
Why? 为什么?
Mapping emails can consume a lot of ressources when a lot of emails are at stake, so its better to perform the scope directly with the database. 当许多电子邮件受到威胁时,映射电子邮件会占用大量资源,因此最好直接使用数据库执行范围。
How? 怎么样?
Cashing the account_id in the EmailAddress model and performing a before validation method. 兑现EmailAddress模型中的account_id并执行before验证方法。
1) Create a migration : 1)创建迁移:
change_table :email_addresses do |t|
t.references :account, index: true
end
add_index :email_addresses, [:account_id, :email], unique: true
2) Migrate 2)迁移
3) Update the EmailAddress model 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
I'll supply one possible solution. 我将提供一种可能的解决方案。 Not tested, but it should work, with a custom validation and an extra association.
未经测试,但应该可以使用自定义验证和额外关联。
In your Account
model: 在您的
Account
模型中:
has_many :email_addresses, through: :contacts
In your EmailAddress
model: 在您的
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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.