在ActiveRecord中覆盖create上的id

时间:2009-01-10 19:44:58

标签: ruby-on-rails activerecord

有没有办法在创建时覆盖模型的id值?类似的东西:

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

会很理想,但显然不会起作用。

12 个答案:

答案 0 :(得分:116)

id只是attr_protected,这就是为什么你不能使用质量赋值来设置它。但是,手动设置时,它只是起作用:

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

我不确定最初的动机是什么,但我在将ActiveHash模型转换为ActiveRecord时这样做。 ActiveHash允许您在ActiveRecord中使用相同的belongs_to语义,但不是进行迁移和创建表,而是在每次调用时产生数据库的开销,您只需将数据存储在yml文件中。数据库中的外键引用yml中的内存中的ID。

ActiveHash非常适用于不经常更改且仅由开发人员更改的选项列表和小型表。因此,当从ActiveHash转到ActiveRecord时,最简单的方法就是保持所有外键引用相同。

答案 1 :(得分:29)

尝试

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

应该可以为您提供所需的信息。

答案 2 :(得分:27)

您也可以使用以下内容:

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

尽管如docs中所述,这将绕过质量分配安全性。

答案 3 :(得分:6)

实际上,事实证明做以下工作:

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

答案 4 :(得分:6)

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

class Post < ActiveRecord::Base

  private

  def attributes_protected_by_default
    []
  end
end

(使用ActiveRecord 2.3.5测试)

答案 5 :(得分:6)

我们可以覆盖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)

答案 6 :(得分:5)

Post.create!(:title => "Test") { |t| t.id = 10 }

这并不是我通常想要做的事情,但是如果你需要使用一组固定的id填充表格(例如使用rake任务创建默认值时)它会很好用并且你想要覆盖自动递增(这样每次运行任务时,表都会填充相同的ID):

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

答案 7 :(得分:2)

将此 create_with_id 函数放在seeds.rb的顶部,然后使用它来创建需要显式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"})

答案 8 :(得分:1)

此案例是一个类似的问题,必须使用某种自定义日期覆盖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">

回调功能正常。

祝你好运!

答案 9 :(得分:0)

对于Rails 3,最简单的方法是使用new进行without_protection优化,然后使用save

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

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

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}`

答案 10 :(得分:0)

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

答案 11 :(得分:0)

你可以通过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