Rails 3: проверка уникальности для вложенных полей_for
A имеют две модели: "магазин" и "продукт", связанные через has_many: через.
В форме магазина есть вложенные атрибуты для нескольких продуктов, и у меня возникают небольшие проблемы с проверкой уникальности продукта. Если я вхожу в продукт, сохраните его, а затем попробуйте ввести одно и то же имя для нового продукта, триггеры проверки уникальности успешно завершены.
Однако, если я вхожу в одно имя продукта в 2 строки одной и той же вложенной формы, форма принимается - проверка уникальности не запускается.
Я предполагаю, что это довольно распространенная проблема, но я не могу найти простого решения. У кого-нибудь есть предложения относительно самого простого способа гарантировать, что проверки на уникальность выполняются в одной и той же вложенной форме?
Изменить: Модель продукта приведена ниже
class Product < ActiveRecord::Base
has_many :shop_products
has_many :shops, :through => :shop_products
validates_presence_of :name
validates_uniqueness_of :name
end
Ответы
Ответ 1
Вы можете написать специальный валидатор, например
# 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
Ответ 2
Чтобы расширить решение Alberto, следующий пользовательский валидатор принимает поле (атрибут) для проверки и добавляет ошибки в вложенные ресурсы.
# 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
Ответ 3
Я нашел ответ здесь:
https://rails.lighthouseapp.com/projects/8994/tickets/2160-nested_attributes-validates_uniqueness_of-fails