【问题标题】:Rails 3: Uniqueness validation for nested fields_forRails 3:嵌套字段的唯一性验证_for
【发布时间】:2011-03-30 06:35:52
【问题描述】:

A 有两个模型,“shop”和“product”,通过 has_many :through 链接。

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

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

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

编辑:下面包含的产品型号

class Product < ActiveRecord::Base

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

  validates_presence_of :name
  validates_uniqueness_of :name
end

【问题讨论】:

  • 你的product是什么样的?
  • 您始终可以 (and should!) 使用数据库中的唯一索引来支持唯一性验证。它会像你看到的那样停止重复,但它不会很好 - 它只会在保存时抛出异常......也许你可以编写一个自定义验证函数来处理这个问题?
  • Jeffrey:上面添加的产品模型
  • 泽维尔:点头干杯。我肯定会在数据库中添加一个索引。看起来自定义验证可能是解决方案......只是有点惊讶似乎没有内置任何东西。

标签: ruby-on-rails ruby-on-rails-3 validates-uniqueness-of


【解决方案1】:

为了扩展 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

【讨论】:

  • 嗨,我遇到了类似的问题并尝试了您的代码,我得到了Unknown validator: 'NestedAttributesUniquenessValidator'。有什么想法吗?
  • 感谢您提供出色的解决方案!它只需要对 accepts_nested_attributes_for :items, allow_destroy: true 案例的 _destroy 密钥支持进行小幅调整 - 这是 my gist 的第 3 行
  • 真棒 - 非常聪明的解决方案。在 Rails 4 中,我需要在 uniq 之前添加 to_a,这可能是因为弃用了。我稍微调整了我的版本,以在每个既是重复又是新的嵌套对象上添加验证错误(即不归咎于预先存在的对象 - 只是新添加的重复项)。我的要点在这里:gist.github.com/francirp/01d2b82c3000cce626f0f34bdcf5c33c
【解决方案2】:

您可以编写一个自定义验证器,例如

# 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

【讨论】:

  • Grazie Alberto,效果很好!只是一个小提示:我相信自定义验证器更适合 app/validators/ 而不是 config/initializers/。
  • 您需要从 ActiveModel::EachValidator 继承才能使其正常工作。否则只会抛出错误。
  • 非常感谢!以为嵌套验证已运行,但没有
【解决方案3】:
猜你喜欢
  • 2014-09-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多