如何强制整行的唯一性?

时间:2014-07-21 19:04:04

标签: ruby-on-rails activerecord ruby-on-rails-4

我已经看到其他SO问题,例如 - How do you validate uniqueness of a pair of ids in Ruby on Rails? - 其中描述了添加范围参数以强制实施密钥对的唯一性,即(来自答案)

validates_uniqueness_of :user_id, :scope => [:question_id]

我的问题是你如何对整行数据进行这种验证?

在我的情况下,我有五列,只有全部五列相同才能拒绝数据。该数据不是用户输入的,该表本质上是一个连接表(没有id或时间戳)。

我目前的想法是搜索包含所有列值的记录,并且仅在查询返回nil时才创建,但这似乎是一个糟糕的工作。有没有更简单的“轨道方式”来做到这一点?

2 个答案:

答案 0 :(得分:2)

您需要创建自定义验证器(http://guides.rubyonrails.org/active_record_validations.html#performing-custom-validations):

class TotallyUniqueValidator < ActiveModel::Validator
  def validate(record)
    if record.attributes_for_uniqueness.values.uniq.size == 1
      record.errors[:base] << 'All fields must be unique!'
    end
  end
end

class User
  validates_with TotallyUniqueValidator

  def attributes_for_uniqueness
    attributes.except :created_at, :updated_at, :id
  end
end

这里重要的一点是:

if record.attributes_for_uniqueness.values.uniq.size == 1

这将获取您要检查唯一性的所有属性的哈希值(在这种情况下除了id和timestamps之外的所有内容)并将其转换为仅包含值的数组,然后在其上调用uniq,仅返回uniq值和如果大小为1那么它们都是相同的值。

根据您的表没有ID或时间戳的评论

更新

然后你可以简单地做:

if record.attributes.except(:id).values.uniq.size == 1

...因为我非常确定它仍有id,除非您确定它不会删除except部分。

答案 1 :(得分:0)

您可以在迁移中向表中添加唯一索引:

add_index :widgets, [:column1, :column2, :column3, :column4, :column5], unique: true

结果索引将要求5列的每个组合必须是唯一的。