How to store enum as string to database in rails

前端 未结 5 1081
广开言路
广开言路 2021-02-05 07:46

How do I create a migration in ruby where the default is a string rather than an Integer, I want to store enum into the database, but I do not want to store it as Integer, becau

相关标签:
5条回答
  • 2021-02-05 08:09

    Reading the enum documentation, you can see Rails use the value index of the Array explained as:

    Note that when an Array is used, the implicit mapping from the values to database integers is derived from the order the values appear in the array.

    But it is also stated that you can use a Hash:

    it's also possible to explicitly map the relation between attribute and database integer with a Hash.

    With the example:

    class Conversation < ActiveRecord::Base  
      enum status: { active: 0, archived: 1 }  
    end
    

    So I tested using Rails 4.2.4 and sqlite3 and created an User class with a string type for sex type and a Hash in the enum with string values(I am using fem and mal values to differ from female and male):

    Migration:

    class CreateUsers < ActiveRecord::Migration
      def change
        create_table :users do |t|
          t.string :sex, default: 'fem'
        end
      end
    end  
    

    Model:

    class User < ActiveRecord::Base
      enum sex: { female: 'fem', male: 'mal' }
    end
    

    And in console:

    u = User.new
    #=>  #<User id: nil, sex: "fem">
    u.male?
    #=> false
    u.female?
    #=> true
    u.sex
    #=> "female"
    u[:sex]
    #=> "fem"
    u.male!
    # INSERT transaction...
    u.sex
    #=> "male"
    u[:sex]
    #=> "mal"
    
    0 讨论(0)
  • 2021-02-05 08:15

    To my knowledge it is not possible with standard Rails enum. Look at https://github.com/lwe/simple_enum, it is more functionally rich, and also allows storing of enum values as strings to DB (column type string, i.e. varchar in terms of DB).

    0 讨论(0)
  • 2021-02-05 08:27

    enum in Rails and ENUM type in MySQL are 2 different things.

    1. enum in Rails is just a wrapper around your integer column so it's easier for you to use strings in queries, rather than integers. But on database level it's all converted to integers (automatically by Rails), since that's the type of the column.

    2. ENUM type in MySQL is vendor-specific column type (for example, SQLite doesn't support it, but PostgreSQL does). In MySQL :

    An ENUM is a string object with a value chosen from a list of permitted values that are enumerated explicitly in the column specification at table creation time.

    CREATE TABLE shirts (
        name VARCHAR(40),
        size ENUM('x-small', 'small', 'medium', 'large', 'x-large')
    );
    INSERT INTO shirts (name, size) VALUES ('dress shirt','large'), ('t-shirt','medium'),
      ('polo shirt','small');
    SELECT name, size FROM shirts WHERE size = 'medium';
    +---------+--------+
    | name    | size   |
    +---------+--------+
    | t-shirt | medium |
    +---------+--------+
    

    For the migration, you need to do this:

    class AddSexToUsers < ActiveRecord::Migration
      def change
        add_column :users, :sex, "ENUM('female', 'male') DEFAULT 'female'"
      end
    end
    
    0 讨论(0)
  • 2021-02-05 08:29

    Take a look at this Gist, Rails doesn't provide it out of the box so you have to use a concern:

    https://gist.github.com/mani47/86096220ccd06fe46f0c09306e9d382d

    0 讨论(0)
  • 2021-02-05 08:31

    I normally do the following:

    # in the migration in db/migrate/…
    def self.up
      add_column :works, :status, :string, null: false, default: 'offering'
    end
    
    # in app/models/work.rb
    class Work < ApplicationRecord
      ALL_STATES = %w[canceled offering running payment rating done].freeze
    
      enum status: ALL_STATES.zip(ALL_STATES).to_h
    end
    

    By using a hash as argument for enum (see docs) this stores strings in the database. At the same time this allows you to use all the cool Rails helper methods:

    w = Work.new
    #=>  #<Work id: nil, status: "offering">
    w.rating?
    #=> false
    w.offering?
    #=> true
    w.status
    #=> "offering"
    w[:status]
    #=> "offering"
    w.done!
    # INSERT transaction...
    w.status
    #=> "done"
    w[:status]
    #=> "done"
    
    0 讨论(0)
提交回复
热议问题