Question

A a deux modèles, "shop" et "produit", lié via Has_Many: à travers.

Sous la forme de l'atelier, il existe des attributs imbriqués pour plusieurs produits, et j'ai un peu de mal avec la validation de l'unicité du produit. Si j'entre un produit, enregistrez-le, essayez de saisir le même nom pour un nouveau produit, la validation de l'unicité se déclenche avec succès.

Cependant, si j'entre le même nom de produit en 2 lignes de la même forme imbriquée, le formulaire est accepté - la validation de l'unicité ne déclenche pas.

Je suppose que c'est un problème assez courant, mais je ne trouve aucune solution simple. Quiconque a des suggestions sur le moyen le plus simple pour s'assurer que les validations de l'unicité sont respectées le même forme imbriquée?

Edit: modèle de produit inclus ci-dessous

class Product < ActiveRecord::Base

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

  validates_presence_of :name
  validates_uniqueness_of :name
end
Était-ce utile?

La solution

Vous pouvez écrire un validateur personnalisé comme

# 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

Autres conseils

Pour développer la solution d'Alberto, le validateur personnalisé suivant accepte un champ (attribut) pour valider et ajoute des erreurs aux ressources imbriquées.

# 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
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top