下一个可用的记录ID

@user = User.new 

@user.id返回nil但我需要在保存之前知道它。 可能吗 ?

不,您在保存之前无法获取ID。 ID号来自数据库,但在您调用save之前,数据库不会分配ID。 所有这些都假设您当然正在使用ActiveRecord。

是的你可以!

我有同样的问题并调查了文档。 事实上,解决这个问题的能力与您的数据库类型密切相关。

OraclePostgresql确实有很多有用的function来轻松解决这个问题。 对于MySQL(oracle)或SkySQL (开源),它似乎更复杂(但仍然可能)。 如果您需要高级数据库工具,我建议您避免使用这些(MySQL / SkySQL)数据库。

首先,您必须尽可能避免在应用程序设计中避免这种情况,因为在保存之前使用ID是危险的。

可能存在以下情况:您没有任何其他选择:例如,当两个表引用自身时,出于安全原因,您不允许在这些表上使用DELETE或UPDATE。

在这种情况下,您可以使用(PostgreSQL,Oracle)数据库nextval函数生成下一个ID号,而无需实际插入新记录。

将它与find_by_sql rails方法结合使用。

例如,要使用postgreSQL和Rails执行此操作,请选择一个rails模型并添加一个类方法(而不是实例方法!)。 这可以通过方法名称开头的“ self ”单词来实现。 self告诉Ruby这个方法只能由类使用,而不能由实例变量(用’new’创建的对象)使用。

我的Rails模型:

 class MyToy < ActiveRecord::Base ... def self.my_next_id_sequence self.find_by_sql "SELECT nextval('my_toys_id_seq') AS my_next_id" end end 

当您使用Rails迁移生成表时,默认情况下,Rails会自动创建一个名为id的列,并将其设置为主键的表 。 为确保不会出现任何“ 重复的主键错误 ”,Rails会自动在数据库中创建一个序列并将其应用于id列。 对于您在表中插入的每个新记录(行),数据库将自行计算新记录的下一个ID。

Rails自动命名此序列, 表名附加“_id_seq” 。 必须将PostgreSQL nextval函数应用于此序列,如此处所述。

现在关于find_by_sql ,如这里所解释的,它将创建一个 包含类的新对象实例 的数组 。 每个对象都将包含SQL语句生成的所有列。 这些列将以属性的forms出现在每个新对象实例中。 即使您的class级模型中不存在这些属性

正如您明智地意识到的那样,我们的nextval函数只返回一个值。 因此,find_by_sql将创建一个包含具有单个属性单个对象实例 的数组 。 为了便于读取此属性的值,我们将使用“my_next_id”命名生成的SQL列,因此我们的属性将具有相同的名称。

就是这样了。 我们可以使用我们的新方法:

 my_resulting_array = MyToy.my_next_id_sequence my_toy_object = my_resulting_array[0] my_next_id_value = my_toy_object.my_next_id 

并使用它来解决我们的死锁情况:

 my_dog = DogModel.create(:name => 'Dogy', :toy_id => my_next_id_value) a_dog_toy = MyToy.new(:my_dog_id => my_dog.id) a_dog_toy.id = my_next_id_value a_dog_toy.save 

请注意,如果您不使用my_next_id_value,则此ID号将永远丢失。 (我的意思是,将来任何记录都不会使用它)。

数据库不会等你使用它。 如果在任何时候某个地​​方,您的应用程序需要在my_table_example中插入一条新记录(可能与我们正在使用my_next_id_sequence同时),数据库将始终在您生成的新记录之后立即为该新记录分配一个ID号。 my_next_id_sequence,考虑到你的my_next_id_value是保留的。 这可能会导致my_table_example中的记录看起来没有按创建时的顺序排序。

我有类似的情况。 我在我的模型上使用find_by_sql调用序列,返回模型数组。 我从arry的第一个对象那里得到了id。 类似下面的东西。

 Class User < ActiveRecord::Base set_primary_key 'user_id' alias user_id= id= def self.get_sequence_id self.find_by_sql "select TEST_USER_ID_SEQ.nextval as contact_id from dual" end end 

在您引用用户模型的类上,

 @users = User.get_sequence_id user = users[0] 

通常,ID会自动从数据库序列中填充。

在rails中,您可以使用after_create事件,该事件使您可以在保存对象后立即访问该对象(因此它具有ID)。 这将涵盖大多数情况。

当使用Oracle时,我有自己想要创建ID的情况(而不是使用序列),在这篇文章中,我提供了我如何做到的详细信息。 简而言之,代码:

 # a small patch as proposed by the author of OracleEnhancedAdapter: http://blog.rayapps.com/2008/05/13/activerecord-oracle-enhanced-adapter/#comment-240 # if a ActiveRecord model has a sequence with name "autogenerated", the id will not be filled in from any sequence ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.class_eval do alias_method :orig_next_sequence_value, :next_sequence_value def next_sequence_value(sequence_name) if sequence_name == 'autogenerated' # we assume id must have gotten a good value before insert! id else orig_next_sequence_value(sequence_name) end end end 

虽然此解决方案特定于Oracle增强,但我假设其他数据库将具有您可以重新定义的类似方法。

所以,虽然它绝对不建议你想要绝对确定为什么你不想使用序列生成的id,如果需要它,它绝对是可能的。 这就是为什么我喜欢ruby和Ruby on Rails! 🙂

在Oracle中,您可以使用此查询获取当前序列值:

 SELECT last_number FROM user_sequences where sequence_name='your_sequence_name'; 

所以在你的模型类中,你可以这样做:

 class MyModel < ActiveRecord::Base self.sequence_name = 'your_sequence_name' def self.my_next_id_sequence get_data = self.find_by_sql "SELECT last_number FROM user_sequences where sequence_name='your_sequence_name'" get_data[0].last_number end end 

最后,在控制器中你可以用这个得到这个值:

 my_sequence_number = MyModel.my_next_id_sequence 

因此,无需使用NEXTVAL获取您的下一个值,您也不会丢失ID。

你可以做的是User.max(id)。 这将返回数据库中的最高ID,然后您可以添加1.虽然可能满足您的需求,但这不可靠。