我想将对象的网址从/object/123
替换为/object/f8a3b2
,因此我不会使用id
列来生成uid
创建对象时手动创建列。
我可以使用可用的uuid库来生成uid,但是如何确保生成的值始终是唯一的?
我在uid列上有一个唯一的索引集,所以如果它不是唯一的,它应该抛出一个错误 - 我可以在模型级别捕获它,创建另一个uid,然后再试一次吗?
答案 0 :(得分:4)
可能有更好的方法可以做到这一点,但是我想到的第一件事就是在您的活动记录模型上使用validates_uniqueness_of
方法。
在你的模特中:
class MyUniqueModel < ActiveRecord::Base
validates_uniqueness_of :uuid_column
end
在您的控制器中
@uniqueObject = MyUniqueModel.new(modelParams)
@uniqueObject.uuid_column = generate_uuid() # whatever method you use for generating your uuid
while !uniqueObject.save # if at first it didn't save...
uniqueObject.uuid_column = generate_uuid() # try, try again
end # should break out of the loop if the save succeeded.
如果您对模型进行了其他验证,那么您在我的建议中遇到的问题是确定保存失败是由您显然非唯一的uuid还是其他一些验证错误引起的。这意味着您必须通过失败的列过滤循环体,以确定是否应该重新生成另一个UUID。
答案 1 :(得分:1)
如果您正在使用uuid gem,则无需担心其独特性,因为这已经为您解决了。但是,uuid将生成长128位的唯一ID。所以,如果你想要更短的东西,你可能不想使用它。
如果您想要,也就是说,8位唯一代码也是随机的,您可以在我的皮质项目under the new_unique_code
method中看到这样一个系统的实现。
这样做会生成一个8位字母数字代码来表示应用程序中的资源。生成给定代码后,我尝试执行.find_by_code
,如果返回nil
,我知道它已经在使用 NOT 。
在我的特定情况下,8位字母数字代码为我提供了数百万可能代码的密钥空间(我忘记了多少)。根据我的应用程序的使用情况,代码冲突的可能性(使用的代码与可用代码的百分比)足够小,可能需要为给定资源多次生成新代码。
这样做的结果是,需要为给定资源多次生成新代码是非常罕见的,因此几乎不需要额外的时间。
我的解决方案的一个限制是,如果您有多个应用服务器,但只有一个数据库服务器,则可以让两个独立的应用服务器生成相同的代码,然后才能在集中式数据库中创建新的资源记录。这会产生一种情况,即可能存在代码冲突,除了保存到数据库的第一条记录之外的所有记录都将被标记为无效(由于代码列上的唯一性约束)。此特定应用程序仅在单个应用服务器实例上运行(并且可能只会运行)。这是我所知道的一个已知的权衡,所以我可以冒险。
但是,如果我想解决这个问题,我会限制每个应用服务器的随机代码范围,以便它们永远不会意外重叠。例如,假设我生成的随机代码在值00000000-99999999之间以8位数字代码形式出现。如果我有三个运行此应用程序的应用程序服务器,我只会将服务器#1限制为生成代码00000000-33333333,服务器#2到33333334-66666666,服务器#3到66666667-99999999。这样他们就可以保证永远不会重叠代码,你仍然可以整天生成独特的随机密钥。
如何将给定服务器限制在给定范围内有点偏离主题,但有些可用选项是:
答案 2 :(得分:0)
使用Rails Validations验证唯一性:
class MyModel < ActiveRecord::Base
validates :id, uniqueness: true
end
答案 3 :(得分:0)
你应该使用
validates :uid, :uniqueness => true
我必须在此建议中添加警告,因为仍有一种情况会导致数据库因重复的uid列值而出错。您可以在Michael Hartl's RoR tutorial
中找到有关它的更多信息为了在重复的uid上捕获并重试保存,您可以执行以下操作:
model = Model.create(:uid => generate_uid)
if model.errors[:uid].any?
model.create(:uid => generate_uid)
end