Generate an auto increment field in rails

后端 未结 2 528
猫巷女王i
猫巷女王i 2020-12-08 03:03

I have a model Token, which has a field token_number that I need to auto increment (starting from 1001), if and only if the user does not provide it. The problem is that, si

相关标签:
2条回答
  • 2020-12-08 03:22

    Interesting question for me. Unfortunately, rails doesn't provide a way to auto-increment columns, so we must resort to SQL with little automation. I tried this in Rails 3.0.7 using PostgreSQL as my database and it works and hope this will be useful:

    Creating sequence for 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
    

    Now, since there is a possibility of token_number being set by the user manually, we'll need to generate the token_number only if it is not being set. Read about Callbacks here. With that we have,

    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
    

    A sample run. Please note that for the first token I am not setting token_number and it generates the token_number sequence and for the second I am assigning.

    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 
    
    0 讨论(0)
  • 2020-12-08 03:24

    another alternatives is set nextval as the default value in your postgresql

    this make us not need to write callbacks

    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
    
    0 讨论(0)
提交回复
热议问题