在Rails中生成自增字段

24
我有一个名为Token的模型,其中有一个token_number字段,需要自动增加(从1001开始),前提是用户没有提供它。问题在于,由于用户可以选择提供此字段,我无法准确地查询数据库并询问最大的token_number。我在这个论坛上找到了一个答案,但我相当肯定必须有比执行SQL语句更好的方法来解决它?在Ruby on Rails中自动递增非主键字段
2个回答

25

对我来说是个有趣的问题。不幸的是,Rails没有提供自动增量列的方法,因此我们必须借助SQL进行一些手动操作。我在Rails 3.0.7中使用PostgreSQL作为数据库尝试了这种方法,并且它有效,希望这对你有用:

为token_number创建序列 PGSql文档

class CreateTokens < ActiveRecord::Migration

  def self.up
    create_table :tokens do |t|
      t.string :name
      t.integer :token_number

      t.timestamps
    end

    execute "CREATE SEQUENCE tokens_token_number_seq START 1001"
  end

  def self.down
    drop_table :tokens

    execute "DROP SEQUENCE tokens_token_number_seq"
  end
end

现在,由于用户可能手动设置token_number,我们需要仅在未设置时生成token_number。点击此处了解回调函数。因此,我们有以下内容:

class Token < ActiveRecord::Base
  # Generate the sequence no if not already provided.
  before_validation(:on => :create) do
    self.application_no = next_seq unless attribute_present?("application_no")
  end

  private
    def next_seq(column = 'application_no')
      # This returns a PGresult object [http://rubydoc.info/github/ged/ruby-pg/master/PGresult]
      result = Token.connection.execute("SELECT nextval('tokens_token_number_seq')")

      result[0]['nextval']
    end 
end

一个样例运行。请注意,对于第一个标记,我没有设置token_number,它会生成token_number序列;而对于第二个标记,我正在进行指定。

token = Token.new
# => #<Token id: nil, name: nil, token_number: nil, created_at: nil, updated_at: nil> 

token.save
  SQL (0.8ms)  BEGIN
  SQL (1.7ms)  SELECT nextval('tokens_token_number_seq')
  SQL (6.6ms)   SELECT tablename
 FROM pg_tables
 WHERE schemaname = ANY (current_schemas(false))

  SQL (33.7ms)  INSERT INTO "tokens" ("name", "token_number", "created_at", "updated_at") VALUES (NULL, 1001, '2012-03-02 12:04:00.848863', '2012-03-02 12:04:00.848863') RETURNING "id"
  SQL (15.9ms)  COMMIT
# => true 

token = Token.new
# => #<Token id: nil, name: nil, token_number: nil, created_at: nil, updated_at: nil> 

token.token_number = 3000
# => 3000 

token.save
  SQL (0.8ms)  BEGIN
  SQL (1.5ms)  INSERT INTO "tokens" ("name", "token_number", "created_at", "updated_at") VALUES (NULL, 3000, '2012-03-02 12:04:22.924834', '2012-03-02 12:04:22.924834') RETURNING "id"
  SQL (19.2ms)  COMMIT
# => true 

жңүжІЎжңүеҸҜиғҪеңЁдҪҝз”Ёnextvalж—¶з»“еҗҲй»ҳи®ӨеҖјжқҘйҒҝе…ҚдҪҝз”Ёеӣһи°ғеҮҪж•°пјҹ - freemanoid
如果您使用的是PostgreSQL,您可以使用serial数据类型,避免执行SECUENCE创建步骤。 要在迁移中实现此功能,请将t.integer :token_number更新为t.column :token_number, :serial - Zeaneth

7

另一种替代方法是将nextval设置为您的postgresql中的默认值。

这使我们不需要编写回调函数。

class CreateTokens < ActiveRecord::Migration

  def self.up
    create_table :tokens do |t|
      t.string :name
      t.integer :token_number

      t.timestamps
    end

    execute "CREATE SEQUENCE tokens_token_number_seq START 1001"
    execute "ALTER TABLE tokens ALTER COLUMN token_number SET DEFAULT NEXTVAL('tokens_token_number_seq')"
  end

  def self.down
    drop_table :tokens

    execute "DROP SEQUENCE tokens_token_number_seq"
  end
end

这个方法运行良好,但是当我创建一个新记录时,它不会直接返回auto_increment字段: #<Device id: 3, name: "a23234fsdg", token_number: nil, created_at: "2021-05-28 11:30:36.238238000 +0200", updated_at: "2021-05-28 11:30:36.238238000 +0200"> 即使它已经正确插入到数据库中。有没有一种方法可以在不对新对象调用“reload”的情况下获取此字段? - Vi.
@Joe 如果你通过控制台创建它,create! 的返回值是 nil,要使其也返回自动生成的值,可以通过回调或修补 active record persistence 来实现,但我认为你不需要这样做,因为在 Web 上它会自动工作 / 自动重新加载。 - buncis
还有,请检查一下,建议在测试或控制台中进行重新加载。https://dev59.com/a1XTa4cB1Zd3GeqPzjpb - buncis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接