Pergunta

A tem dois modelos, "loja" e "produto", vinculados via has_many :through.

No formulário da loja existem atributos aninhados para vários produtos e estou tendo alguns problemas com a validação da exclusividade do produto.Se eu inserir um produto, salvá-lo e tentar inserir o mesmo nome para um novo produto, a validação de exclusividade será acionada com êxito.

No entanto, se eu inserir o mesmo nome do produto em 2 linhas do mesmo formulário aninhado, o formulário será aceito - a validação de exclusividade não é acionada.

Suponho que este seja um problema bastante comum, mas não consigo encontrar nenhuma solução simples.Alguém tem alguma sugestão sobre a maneira mais fácil de garantir que as validações de exclusividade sejam obedecidas dentro o mesmo forma aninhada?

Editar:Modelo do produto incluído abaixo

class Product < ActiveRecord::Base

  has_many :shop_products
  has_many :shops, :through => :shop_products

  validates_presence_of :name
  validates_uniqueness_of :name
end
Foi útil?

Solução

Você poderia escrever um validador personalizado como

# app/validators/products_name_uniqueness_validator.rb
class ProductsNameUniquenessValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    record.errors[attribute] << "Products names must be unique" unless value.map(&:name).uniq.size == value.size
  end
end

# app/models/shop.rb
class Shop < ActiveRecord::Base
  validates :products, :products_name_uniqueness => true
end

Outras dicas

Para expandir a solução de Alberto, o validador personalizado a seguir aceita um campo (atributo) para validar e adiciona erros aos recursos aninhados.

# config/initializers/nested_attributes_uniqueness_validator.rb
class NestedAttributesUniquenessValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    unless value.map(&options[:field]).uniq.size == value.size
      record.errors[attribute] << "must be unique"
      duplicates = value - Hash[value.map{|obj| [obj[options[:field]], obj]}].values
      duplicates.each { |obj| obj.errors[options[:field]] << "has already been taken" }
    end
  end
end

# app/models/shop.rb
class Shop < ActiveRecord::Base
  validates :products, :nested_attributes_uniqueness => {:field => :name}
end
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top