How to store enum as string to database in rails
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 endend
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?#=> falseu.female?#=> trueu.sex#=> "female"u[:sex]#=> "fem"u.male!# INSERT transaction...u.sex#=> "male"u[:sex]#=> "mal"
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.rbclass Work < ApplicationRecord ALL_STATES = %w[canceled offering running payment rating done].freeze enum status: ALL_STATES.zip(ALL_STATES).to_hend
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?#=> falsew.offering?#=> truew.status#=> "offering"w[:status]#=> "offering"w.done!# INSERT transaction...w.status#=> "done"w[:status]#=> "done"
enum
in Rails and ENUM
type in MySQL are 2 different things.
enum
in Rails is just a wrapper around yourinteger
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.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'" endend