在ActiveRecord中创build重写id

是否有任何方式来重写创build模型的ID值? 就像是:

Post.create(:id => 10, :title => 'Test') 

将是理想的,但显然不会工作。

ID只是attr_protected,这就是为什么你不能使用批量分配来设置它。 但是,手动设置时,它只是工作:

 o = SomeObject.new o.id = 8888 o.save! o.reload.id # => 8888 

我不确定最初的动机是什么,但是我在将ActiveHash模型转换为ActiveRecord时执行此操作。 ActiveHash允许您在ActiveRecord中使用相同的belongs_to语义,但是不必进行迁移和创build表,并且每次调用都会导致数据库的开销,您只需将数据存储在yml文件中即可。 数据库中的外键引用yml中的内存中的id。

ActiveHash非常适合那些不经常更换,而且只能由开发人员更改的挑选表和小表。 所以当从ActiveHash到ActiveRecord时,最简单的办法就是保持所有的外键引用都是一样的。

尝试

 a_post = Post.new do |p| p.id = 10 p.title = 'Test' p.save end 

这应该给你你想要的。

你也可以使用这样的东西:

 Post.create({:id => 10, :title => 'Test'}, :without_protection => true) 

虽然如文件所述 ,这将绕过大规模的分配安全。

对于Rails 4:

 Post.create(:title => 'Test').update_column(:id, 10) 

其他Rails 4答案没有为我工作。 他们中的许多似乎改变时检查使用Rails控制台,但是当我检查MySQL数据库中的值,他们保持不变。 其他答案有时只有工作。

至less对于MySQL来说,除非你使用update_column否则在自动增量id号下面分配一个id是行不通的。 例如,

 p = Post.create(:title => 'Test') p.id => 20 # 20 was the id the auto increment gave it p2 = Post.create(:id => 40, :title => 'Test') p2.id => 40 # 40 > the next auto increment id (21) so allow it p3 = Post.create(:id => 10, :title => 'Test') p3.id => 10 # Go check your database, it may say 41. # Assigning an id to a number below the next auto generated id will not update the db 

如果你改变create使用new + save你将仍然有这个问题。 手动更改idp.id = 10也会产生这个问题。

一般来说,我会使用update_column更改id即使它花费额外的数据库查询,因为它会一直工作。 这是一个错误,可能在您的开发环境中不会显示,但可以悄悄地破坏您的生产数据库,一直说它正在工作。

事实上,结果是做了以下工作:

 p = Post.new(:id => 10, :title => 'Test') p.save(false) 

正如Jeff指出的那样,id的行为就好像是attr_protected。 为了防止这种情况,您需要覆盖默认的受保护属性列表。 在属性信息可以来自外部的任何地方要小心。 ID字段是默认保护的原因。

 class Post < ActiveRecord::Base private def attributes_protected_by_default [] end end 

(使用ActiveRecord 2.3.5进行testing)

我们可以覆盖attributes_protected_by_default

 class Example < ActiveRecord::Base def self.attributes_protected_by_default # default is ["id", "type"] ["type"] end end e = Example.new(:id => 10000) 
 Post.create!(:title => "Test") { |t| t.id = 10 } 

这并不意味着我通常想要做的事情,但是如果你需要用一组固定的id来填充一个表格(比如使用rake任务创build默认值),那么它就会工作的很好想要覆盖自动递增(以便每次运行任务时,表格都使用相同的ID填充):

 post_types.each_with_index do |post_type| PostType.create!(:name => post_type) { |t| t.id = i + 1 } end 

把这个create_with_id函数放在你的seeds.rb的顶部,然后用它来做你的对象创build,在需要显式id的地方。

 def create_with_id(clazz, params) obj = clazz.send(:new, params) obj.id = params[:id] obj.save! obj end 

像这样使用它

 create_with_id( Foo, {id:1,name:"My Foo",prop:"My other property"}) 

而不是使用

Foo.create({id:1,name:"My Foo",prop:"My other property"})

这种情况是一个类似的问题,必须用一种自定义date来覆盖id

 # in app/models/calendar_block_group.rb class CalendarBlockGroup < ActiveRecord::Base ... before_validation :parse_id def parse_id self.id = self.date.strftime('%d%m%Y') end ... end 

接着 :

 CalendarBlockGroup.create!(:date => Date.today) # => #<CalendarBlockGroup id: 27072014, date: "2014-07-27", created_at: "2014-07-27 20:41:49", updated_at: "2014-07-27 20:41:49"> 

callback工作正常。

祝你好运!。

在使用Postgresql 9.5.3的Rails 4.2.1中,只要没有id = 10的行, Post.create(:id => 10, :title => 'Test')可以工作。

对于Rails 3,最简单的方法是在without_protection细化中使用new ,然后save

 Post.new({:id => 10, :title => 'Test'}, :without_protection => true).save 

对于种子数据,绕过可以这样做的validation是有意义的:

 Post.new({:id => 10, :title => 'Test'}, :without_protection => true).save(validate: false) 

实际上,我们已经在执行种子文件之前立即声明了一个辅助方法到ActiveRecord :: Base:

 class ActiveRecord::Base def self.seed_create(attributes) new(attributes, without_protection: true).save(validate: false) end end 

现在:

 Post.seed_create(:id => 10, :title => 'Test') 

对于Rails 4,你应该使用StrongParams而不是受保护的属性。 如果是这样的话,您只需简单地分配和保存,而不需要将任何标志传递给new标志:

 Post.new(id: 10, title: 'Test').save # optionally pass `{validate: false}` 

你可以通过sql插入id:

  arr = record_line.strip.split(",") sql = "insert into records(id, created_at, updated_at, count, type_id, cycle, date) values(#{arr[0]},#{arr[1]},#{arr[2]},#{arr[3]},#{arr[4]},#{arr[5]},#{arr[6]})" ActiveRecord::Base.connection.execute sql