Rails唯一列有重复

时间:2012-08-13 08:59:13

标签: ruby-on-rails ruby validation postgresql

这是我模型的精简版。

model Paper
  PAPER_STARTING_NUMBER = 1

  validate_uniqueness_of :number, :allow_blank => true
  before_create :alocate_paper_number

  def alocate_paper_number
    return true if self.number.present?
    p_number = Paper.maximum('number') || Paper::PAPER_STARTING_NUMBER 
    self.number = p_number >= Paper::PAPER_STARTING_NUMBER ? p_number+1 : Paper::PAPER_STARTING_NUMBER 
    return true 
  end
end

问题是我在数字列中有重复项。 任何想法为什么以及如何在不改变回调的情况下解决这个问题。 我知道我可以在数据库上添加唯一性验证或在该列上创建序列,还有其他想法吗?

3 个答案:

答案 0 :(得分:2)

首先,您必须了解回调的顺序:

( - )保存

( - )有效

(1)before_validation

( - )验证

(2)after_validation

(3)before_save

(4)before_create

( - )创建

(5)after_create

(6)after_save

(7)after_commit

正如您所看到的,它验证了您的number属性的唯一性,然后before_create可以自行处理,与您的验证要完成的内容相反。

关于更清洁的架构,我会将这两个想法放在您的自定义模型中,因为用户可能无法选择该数字。它只是一个增量器,对吗?

def alocate_paper_number
  p_number = Paper.maximum('number') || Paper::PAPER_STARTING_NUMBER 
  self.number = p_number + 1
end

单独的片段会阻止重复,因为它总是向上递增(除非,数字可能会以另一种方式向我不知道),而且也没有理由返回所有这些真相。它真的够了!

答案 1 :(得分:2)

这是de docs。 validate_uniqueness_of TRIES使其独一无二。但是如果两个进程同时添加一个记录,它们都可以包含相同的数字。

如果您想保证唯一性,请让数据库执行此操作。但是因为每个数据库的不同,Rails不支持它。

这里解释:http://guides.rubyonrails.org/active_record_validations_callbacks.html#uniqueness

使用解决方案:“为避免这种情况,您必须在数据库中创建唯一索引。”

答案 2 :(得分:1)

我是如何修复的(请记住我无法返回验证错误) 我在数字列上添加了一个uniquness索引(如mu和Hugo建议的那样) 因为我无法在控制器中返回验证错误

class PaperController < ApplicationController
  def create
    begin
      @paper.save
    rescue ActiveRecord::RecordNotUnique
      @paper.number = nil
      create
    end
  end
end
相关问题