如何确保不通过activerecords将重复的行添加到我的数据库表中?

时间:2014-05-18 13:03:47

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

所以我有一张叫做投票的桌子。

有三列。 VoterID,VoteforID和投票。

每个列的唯一性并不重要,但同一行不能重复两次

例如。这是正确的

Voter_id | Votefor_id |表决

1 | 2 | 1

1 | 3 | 1

1 | 4 | 1

2 | 1 | 2

这是错误的。

Voter_id | Votefor_id |表决

1 | 2 | 1

1 | 2 | 1

1 | 4 | 1

2 | 1 | 2

目前,我在添加记录之前正在执行select语句,以确保不存在相同的行。当然,有一个更好的解决方案。

谢谢。

3 个答案:

答案 0 :(得分:2)

使用uniqueness_validation上的scope参数,例如:

validates :voter_id, uniqueness: { scope: :votefor_id }

范围参数可以是多个:

validates :voter_id, uniqueness: { scope: [:votefor_id, :vote] }

更新:(应用验证与Db约束)

这实际上取决于细节,但对于手头的情况,我个人认为db约束在这里有点过分,因为竞争条件(documented in rails uniqueness validation)发生的可能性很低,因为我们需要

两个记录具有完全相同的voter_id,votefor_id和投票在同一时间提交(我们正在谈论毫秒),如果我可以假设一些关于OP业务逻辑voter通常会是登录用户意味着重复的条目应该只来自他!!

结论:

我有很多使用uniqueness_validation在生产中运行的rails应用程序就好了,只有one time它在一个具有许多同时条目的单个列的唯一性检查情况下失败了我(显然我那里需要db约束)。

如果数据完整性至关重要,您应该确保使用db约束。

或者如果你不介意开销&特别是如果您可以从(voter_id,votefor_id和投票)上的新索引中获益以提高搜索速度,我会说使用db约束。

否则我会说:保持简单并与uniquess_valdation

保持一致

答案 1 :(得分:2)

如果数据完整性至关重要,则不应使用验证来保证唯一性。它可能会失败。保证唯一性的唯一方法是使用数据库约束。这是因为Rails validates_uniqueness可以有竞争条件。

创建迁移以添加索引,或更改现有索引以反映此更改:

换新表:

class CreateVotes < ActiveRecord::Migration
  def change
    create_table :votes do |t|
      t.belongs_to :voter
      t.belongs_to :votefor
      t.string :vote # Choose the correct column type
      t.timestamps
    end
    add_index :votes, [:voter_id, :votefor_id, :vote], unique: true
  end
end

对于现有表格:

class AddUniqueIndexToVotes < ActiveRecord::Migration
  def change
    add_index :votes,  [voter_id, votefor_id, vote], unique: true
  end
end

现在,如果您想向用户提供他们已经投票的反馈,您可以继续按照其他人的建议添加验证:

validates :voter_id, uniqueness: { scope: [:votefor_id, :vote] }

答案 2 :(得分:0)

在表定义中,添加UNIQUE表约束:

CREATE TABLE votes (
  voter_id   int NOT NULL,
  votefor_id int NOT NULL,
  vote       int NOT NULL,
  UNIQUE (voter_id, votefor_id, vote)
);

这样PostgreSQL会检查重复项。如果您尝试INSERT一条记录,其值对于所有三列都与现有记录中的值相同,则会引发错误。