0

我一直在努力让validates_uniqueness_of我的数据库工作。我从 CSV 文件中提取记录,并且我想确保将它们全部记录下来,但是当我下次检查时,如果它们只是重复的,我不想再次保存它们。

示例对象

PlayerStats {session_date, uniform_number, last_name, first_name, throws, throws_caught, throws_dropped, intercepted_throws, defended_throws }

示例记录

2013-01-01, 11A, Jacobsen, Mike, 11, 4, 7, 0 0
2013-01-01, 11A, Jacobsen, Mike, 0, 0, 0, 2, 1

我想保留这两个记录,但是当我尝试像这样进行验证时...

validates_uniqueness_of :uniform_number, :scope => [:session_date, :last_name]

这只会保留例如第一条记录,并认为第二条记录是重复的。我希望它在第二条记录通过保存尝试时也会保存第二条记录。

4

1 回答 1

1

问题是您只验证三个字段的唯一性,而不是所有字段。您应该将所有字段添加到:scope. 但是,如果您有一个包含太多行的表,那将不会有很好的性能。我建议您从要验证其唯一性的所有字段中生成一个令牌,并在令牌上添加唯一性验证。走这条路,您必须在表中再添加一列来存储计算的令牌。不要忘记在列上添加唯一索引以获得最佳性能。之后,以下应该可以解决问题:

before_validate :generate_unique_token

# assuming you named your slug column `unique_token`
validates :unique_token, uniqueness: true

private
# add all fields you want to validate uniqueness on
UNIQUE_FIELDS = [:uniform_number, :session_date, :more_fields] 
def generate_unique_token
  return if self.unique_token.present?
  token_string = ''

  # additional comma is to ensure that [1, 10]  and [11, 0] don't get treated as same input
  UNIQUE_FIELDS.each {|field| token_string << self.send(field).to_s << ','} 

  self.unique_token = token_string
end

这个字符串可以变大,但你不会得到任何错误的碰撞。如果你想控制生成的令牌的大小,你可以这样做:

def generate_unique_token
  token_string = ''
  UNIQUE_FIELDS.each {|field| token_string << self.send(field).to_s << ','} 
  self.unique_token = compress_token(token_string)
end

def compress_token(token_string)
  # you can further compress the token by encoding it in base 62/64
  ::Digest::SHA256.hexdigest(token_string)
end

但请注意,后面的解决方案可能会出现罕见的错误碰撞。

于 2013-07-04T03:05:26.993 回答