2011-03-30 79 views
10

A有两个模型,“shop”和“product”,通过has_many:through链接。Rails 3:嵌套字段的唯一性验证_

在商店窗体中存在多个产品的嵌套属性,并且我在产品的唯一性验证方面遇到了一些问题。如果我输入产品,保存它,然后尝试为新产品输入相同的名称,则会成功触发唯一性验证。

但是,如果我在同一个嵌套表单的两行中输入相同的产品名称,表单将被接受 - 唯一性验证不会触发。

我猜这是一个相当普遍的问题,但我找不到任何简单的解决方案。任何人有最简单的方法来确保在嵌套表格嵌套表格内符合唯一性验证的任何建议?

编辑:包括以下

class Product < ActiveRecord::Base 

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

    validates_presence_of :name 
    validates_uniqueness_of :name 
end 
+0

您可以随时((http://railswarts.blogspot.com/2007/11/validatesuniquenessof-is- broken-and.html))在您的数据库中使用唯一索引返回唯一性验证。它会停止像你看到的重复,但它不会很好 - 它只会抛出一个异常保存...也许你可以写一个自定义验证功能来照顾这个? – 2011-03-30 07:03:36

+0

你的'产品'看起来像什么? – 2011-03-30 07:03:36

+0

杰弗里:产品型号上面加 – PlankTon 2011-03-30 07:10:00

回答

14

产品型号你可以写像

# 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

格拉齐阿尔贝托,它工作得很好!请注意:我认为自定义验证器更适合应用程序/验证器/而不是config/initializers /。 – 2011-11-17 14:26:45

+0

你需要从'ActiveModel :: EachValidator'继承这个工作。否则,它只会抛出一个错误。 – 2013-07-16 22:08:29

+0

非常感谢!,认为嵌套的验证是运行的,但不是 – unmultimedio 2016-06-10 20:53:21

17

为了扩大在阿尔贝托的解决方案自定义验证,下面的自定义验证接受一个字段(属性)来验证,并向嵌套的资源添加错误。

# 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

嗨,我得到了类似的问题,并尝试你的代码,我得到了'未知的验证:'NestedAttributesUniquenessValidator'。任何想法? – Vinay 2013-07-17 11:25:25

+1

感谢您的一个伟大的解决方案!它只需要一个小小的调整redestroy关键支持'accept_nested_attributes_for:items,allow_destroy:true'的情况 - 这是第3行在[我的要点](https://gist.github.com/artemv/4993b128c1a25f06d5d0) – 2016-03-17 19:06:30

+2

真棒 - 非常聪明解。在Rails 4中,我需要在''uniq'''之前添加'''to_a''',可能是因为弃用。我调整了一下我的版本,在每个嵌套对象上都添加了验证错误,这两个对象都是重复的并且是新的(即预先存在的对象不应该怪罪 - 只是新添加的重复对象)。我的要点在这里:https://gist.github.com/francirp/01d2b82c3000cce626f0f34bdcf5c33c – 2016-04-10 17:01:29