在rails中生成自动增量字段

时间:2012-03-01 09:36:04

标签: ruby-on-rails auto-increment rails-migrations

我有一个模型令牌,它有一个字段token_number,我需要自动递增(从1001开始),当且仅当用户不提供它时。问题是,由于用户可以选择提供此字段,因此无法完全查询数据库并请求最大的token_number。我在这个论坛上找到了一个答案,但我确信必须有一个更好的方法来执行它而不是执行SQL语句? Auto increment a non-primary key field in Ruby on Rails

2 个答案:

答案 0 :(得分:15)

对我来说有趣的问题。遗憾的是,rails并没有提供自动增加列的方法,因此我们必须采用几乎没有自动化的SQL。我在Rails 3.0.7中尝试使用PostgreSQL作为我的数据库并且它有效并且希望这将是有用的:

为token_number创建序列PGSql Documentation

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。 Read about Callbacks here。有了这个,

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序列,并为我分配的第二个令牌。

ruby-1.9.2-p0 > token = Token.new

 => #<Token id: nil, name: nil, token_number: nil, created_at: nil, updated_at: nil> 
ruby-1.9.2-p0 > 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 
ruby-1.9.2-p0 > token = Token.new
 => #<Token id: nil, name: nil, token_number: nil, created_at: nil, updated_at: nil> 
ruby-1.9.2-p0 > token.token_number = 3000
 => 3000 
ruby-1.9.2-p0 > 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 
ruby-1.9.2-p0 > 

答案 1 :(得分:5)

另一种替代方法是将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