Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

307
Visualizações
Is it possible to add a uniqueness constraint accross two different but associated models?

I have the models Account and User. Both models have an email attribute.

An Account has_many :users and a User belongs_to :account

I would like to validate the uniqueness of the email accross both models when an Account is being created so the Account email is invalid if it's taken by a User (since the account email later becomes the admin user email).

I added a scope to the email constraint in the Account model but it is not working (the form is not being rejected).

Account model:

has_many :users
validates :email,   uniqueness: { scope: :users, case_sensitive: false }

What is the correct way to implement this? Do I need to add an index to the DB?

over 4 years ago · Santiago Trujillo
3 Respostas
Responde à pergunta

0

An application level validation doesn't actually guarentee the uniqueness of your data - it merely catches a lot of the cases where users try to input duplicates and provides feedback.

So while you could naively implement a custom validation that queries both tables it won't even survive the race condition caused by a double clicking senior citizen or any insertion method that circumvents validations.

Flow chart of paralell requests from Thoughtbot

Since there is no way (AFAIK) to create indices across tables so you might want to just restructure your domain and add an "owner" (call it whatever you want) to the accounts table:

class AddOwnerToAccounts < ActiveRecord::Migration[6.1]
  def change
    add_reference :accounts, :owner, null: false, foreign_key: { to_table: 'users' }
  end 
end
class Account < ApplicationRecord
  has_many :users
  belongs_to :owner, 
    class_name: 'User',
    inverse_of: :owned_accounts
  delegate :email, to: :owner
end
class User < ApplicationRecord
  belongs_to :account
  has_many :owned_accounts,
    class_name: 'Account',
    foreign_key: :owner_id,
    inverse_of: :owner
end
over 4 years ago · Santiago Trujillo Relatório

0

This is an alternative method based on a separate table and a polymorphic assocation:

class CreateEmailAddresses < ActiveRecord::Migration[6.1]
  def change
    create_table :email_addresses do |t|
      t.string :email, unique: true
      t.references :entitity, polymorphic: true
      t.timestamps
    end
    add_index :email_addresses, [:entity_type, :entity_id], unique: true
  end
end
class EmailAddress < ApplicationRecord
  validates_uniqueness_of :email
  validates_uniqueness_of :entity_id, scope: :entity_type
  belongs_to :entity, polymorphic: true
end
class User < ApplicationRecord
  has_one :email_address, 
    as: :entity,
    dependent: :destroy
  delegate :email, to: :email_address
  accepts_nested_attributes_for :email_address
end
class Account < ApplicationRecord
  has_one :email_address, 
    as: :entity,
    dependent: :destroy
  delegate :email, to: :email_address
  accepts_nested_attributes_for :email_address
end

It avoids having to restructure your domain or create a user simply to create a email but will cause issues with authentication libraries such as Devise as well as lacking a real foreign key to guarentee referential integrity which can lead to orphaned records.

IMO not a great solution as it most likely will create as many problems as it solves.

over 4 years ago · Santiago Trujillo Relatório

0

This an only be achieved with a custom validation method.

validate :email_unique_for_account_and_user

private

def email_unique_for_account_and_user
  if User.where(email: email).where.not(id: id).exists? || 
     Account.where(email: email).where.not(id: id).exists?
    errors.add(:email, :taken)
  end
end

IMO it is not possible to define a unique index over multiple database tables.

over 4 years ago · Santiago Trujillo Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda