Rails 3: validation de l'unicité pour les champs imbriqués_ pour
-
14-11-2019 - |
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
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
J'ai trouvé la réponse ici: