2011-03-30 3 views
10

A avoir deux modèles, "shop" et "product", liés via has_many: through.Rails 3: Validation de l'unicité pour les champs imbriqués_pour

Dans le formulaire d'atelier, il y a des attributs imbriqués pour plusieurs produits, et j'ai quelques problèmes avec la validation de l'unicité du produit. Si je saisis un produit, l'enregistre, puis essaie d'entrer 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 dans 2 lignes du même formulaire imbriqué, le formulaire est accepté - la validation de l'unicité ne se déclenche pas.

Je suppose que c'est un problème assez courant, mais je ne trouve pas de solution simple. Quelqu'un a-t-il des suggestions sur la façon la plus simple de s'assurer que les validations de l'unicité sont respectées dans le même formulaire imbriqué?

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 
+0

Vous pouvez toujours ((http://railswarts.blogspot.com/2007/11/validatesuniquenessof-is- broken-and.html)) back la validation de l'unicité avec un index unique dans votre DB. Cela arrêterait les doublons comme vous le voyez, mais ça ne le ferait pas bien - il lancerait simplement une exception lors de la sauvegarde ... Peut-être pourriez-vous écrire une fonction de validation personnalisée pour prendre soin de cela? –

+0

À quoi ressemble votre «produit»? –

+0

Jeffrey: Modèle de produit ajouté ci-dessus – PlankTon

Répondre

14

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 
+1

Grazie Alberto, ça marche parfaitement bien! Juste une note mineure: je crois que les validateurs personnalisés appartiennent mieux à app/validators/à la place de config/initializers /. –

+0

Vous devez hériter de 'ActiveModel :: EachValidator' pour que cela fonctionne. Sinon, il va juste jeter une erreur. –

+0

Merci beaucoup !, pensé que les validations imbriquées ont été exécutées, mais pas – unmultimedio

17

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 
+0

Salut, j'ai eu le problème similaire et j'ai essayé votre code et j'ai obtenu 'Validateur inconnu: 'NestedAttributesUniquenessValidator''. Une idée? – Vinay

+1

Merci pour cette excellente solution! Il a seulement besoin d'un réglage mineur mineur _destroy clé support pour 'accept_nested_attributes_for: items, allow_destroy: true' cas - c'est la ligne 3 à [my gist] (https://gist.github.com/artemv/4993b128c1a25f06d5d0) –

+2

Awesome - très intelligent Solution. Dans Rails 4, j'avais besoin d'ajouter '' 'to_a''' juste avant' '' uniq''', probablement à cause d'une dépréciation. J'ai légèrement modifié ma version de cette version pour ajouter l'erreur de validation sur chaque objet imbriqué qui est à la fois un doublon et un nouveau (c'est-à-dire que les objets préexistants ne sont pas à blâmer - seulement les doublons nouvellement ajoutés). Mon essentiel est ici: https://gist.github.com/francirp/01d2b82c3000cce626f0f34bdcf5c33c –

Questions connexes