I have a problem with the scoped uniqueness validation in Rails for nested attributes with a parent of parent.
Background
I have a rails 4 application with 3 models :
#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).
As suggested at http://guides.rubyonrails.org/active_record_validations.html, I tried :
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 ?
Thanks for you help !
I'll supply one possible solution. Not tested, but it should work, with a custom validation and an extra association.
In your
Account
model:In your
EmailAddress
model: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.
1) Create a migration :
2) Migrate
3) Update the EmailAddress model