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