代码之家  ›  专栏  ›  技术社区  ›  Codebeef

在ActiveRecord中创建时重写ID

  •  100
  • Codebeef  · 技术社区  · 16 年前

    是否有任何方法可以在创建时重写模型的ID值?类似:

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

    很理想,但显然行不通。

    13 回复  |  直到 7 年前
        1
  •  114
  •   Jeff Dean    15 年前

    ID只是属性保护,这就是为什么你不能使用批量分配来设置它。但是,当手动设置时,它仅起作用:

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

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

    ActiveHash非常适合选择列表和小表,这些表很少更改,而且只由开发人员更改。因此,当从activeHash转到activeRecord时,最简单的方法就是保持所有的外键引用相同。

        2
  •  28
  •   PJ Davis    16 年前

    尝试

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

    这会给你你想要的。

        3
  •  27
  •   Samuel Heaney    11 年前

    您也可以使用类似的方法:

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

    尽管如 docs ,这将绕过批量分配安全。

        4
  •  17
  •   Rick Smith Robert Truitt    9 年前

    轨道4:

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

    其他Rails 4答案 为我工作。他们中的许多人 出现 在使用Rails控制台进行检查时更改,但在检查MySQL数据库中的值时,它们保持不变。其他的答案有时只能奏效。

    至少对于MySQL,指定 id 在自动递增ID号下方 工作,除非你用 update_column . 例如,

    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 你仍然会有这个问题。手动更改 身份证件 喜欢 p.id = 10 也会产生这个问题。

    一般来说,我会使用 UpDead列 改变 身份证件 即使它需要额外的数据库查询,因为它可以一直工作。这是一个错误,可能不会出现在开发环境中,但会在说生产数据库正在工作的同时悄悄地破坏生产数据库。

        5
  •  6
  •   Codebeef    16 年前

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

    p = Post.new(:id => 10, :title => 'Test')
    p.save(false)
    
        6
  •  6
  •   Nic Benders    15 年前

    正如杰夫指出的那样,ID的行为就像是被ATTR保护了一样。要防止这种情况发生,需要重写默认受保护属性的列表。在属性信息可能来自外部的任何地方都要小心执行此操作。出于某种原因,ID字段是默认保护的。

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

    (用ActiveRecord 2.3.5测试)

        7
  •  6
  •   Remo user510319    13 年前

    我们可以通过默认值覆盖受保护的属性

    class Example < ActiveRecord::Base
        def self.attributes_protected_by_default
            # default is ["id", "type"]
            ["type"]
        end
    end
    
    e = Example.new(:id => 10000)
    
        8
  •  5
  •   Sean Cameron    12 年前
    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
    
        9
  •  2
  •   Darren Hicks    12 年前

    放这个 创建一个 函数位于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"})

        10
  •  1
  •   CristianOrellanaBak    10 年前

    这是一个类似的问题,必须覆盖 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">
    

    回电工作正常。

    祝你好运!.

        11
  •  0
  •   PinnyM    9 年前

    对于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,您应该使用strong参数而不是受保护的属性。如果是这种情况,您只需在不向传递任何标志的情况下分配和保存 新的 :

    Post.new(id: 10, title: 'Test').save      # optionally pass `{validate: false}`
    
        12
  •  0
  •   Nikola    8 年前

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

        13
  •  0
  •   wxianfeng    7 年前

    可以按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