【问题标题】:How to enforce uniqueness of an entire row?如何强制整行的唯一性?
【发布时间】:2014-07-21 19:04:03
【问题描述】:

我见过其他 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 时创建,但这似乎是一个不好的解决方法。有没有更简单的“rails 方式”来做到这一点?

【问题讨论】:

  • validates_uniqueness_of :user_id, :scope => [*self.column_names] 可能有效(但它包括 created_atidupdated_at,应该删除)
  • 应该补充一点,我的表基本上是一个加强的连接表,所以没有 id 或时间戳。

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


【解决方案1】:

您需要创建一个自定义验证器 (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 和时间戳之外的所有属性)并将其转换为仅包含值的数组,然后在其上调用 uniq 仅返回 uniq 值和如果大小为 1,那么它们都是相同的值。

更新基于您的表没有 id 或时间戳的评论:

然后你可以简单地做:

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

...因为我很确定它仍然有一个 id,除非你确定它没有然后删除 except 部分。

【讨论】:

  • 你知道有什么方法可以更接近数据库级别吗?我也看到了一些关于使用索引创建唯一值的答案,并且可能比模型验证器更喜欢它,因为我的数据不是用户输入的。
  • 我怀疑是否有一种简单的方法可以在数据库级别执行此操作。数据库索引按列垂直工作,而不是按行水平工作。 column1 上的唯一索引将仅在该列中是唯一的,并且不会关心您是否在 column2 上指定了唯一索引,依此类推。除非您必须编写一个进行检查的触发器:postgresql.org/docs/9.2/static/plpgsql-trigger.html
  • 仅供参考,大多数连接(或类似连接)表没有 ID 列,因为它们是使用 create_table "table_name", id: false 创建的,所以我确定它没有 ID 列
  • 如果我有一个简单的 has_many 和 belongs_to 关系,这是否也会在验证中使用外键?我有许多子记录,相对于它们所属的每个父对象而言,它们应该是唯一的,但如果您比较不同的对象,可能会出现多次
【解决方案2】:

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

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

生成的索引将要求 5 列的每个组合都必须是唯一的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-01-01
    • 1970-01-01
    • 2011-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多