Как сохранить перечисление как строку в базу данных в рельсах

Как создать миграцию в ruby, где значение по умолчанию является строкой, а не целым, я хочу сохранить перечисление в базе данных, но я не хочу хранить его как Integer, потому что тогда нет смысла другое приложение, которое хочет использовать одну и ту же таблицу. Как мне сделать default: "female" вместо default:0

class AddSexToUsers < ActiveRecord::Migration
  def change
    add_column :users, :sex, :integer, default: 0
  end
end
class User < ActiveRecord::Base
  enum sex: [:female, :male]
  has_secure_password
end

I

Ответ 1

Считывая документацию enum, вы можете видеть, что Rails использует индекс значений Array, который объясняется как:

Обратите внимание, что при использовании массива неявное сопоставление значений с целыми числами базы данных происходит из порядка, в котором значения отображаются в массиве.

Но также говорится, что вы можете использовать Hash:

также можно явно сопоставить отношение между атрибутом и целым числом базы данных с хэшем.

В примере:

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

Итак, я тестировал Rails 4.2.4 и sqlite3 и создал класс User с типом string для типа пола и Hash в enum с string значениями (я использую fem и малые значения отличаются от женщин и мужчин):

Миграция:

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

Модель:

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

И в консоли:

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"

Ответ 2

enum в Rails и enum type в MySQL есть две разные вещи.

  • enum в Rails - это всего лишь оболочка вокруг столбца integer, поэтому вам проще использовать строки в запросах, а не целые. Но на уровне базы данных все преобразуется в целые числа (автоматически Rails), так как это тип столбца.

  • enum Тип в MySQL - тип столбца, специфичного для поставщика (например, SQLite не поддерживает его, но PostgreSQL делает). В MySQL:

ENUM - это строковый объект со значением, выбранным из списка допустимых значений, которые явно перечислены в спецификации столбца во время создания таблицы.

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 |
+---------+--------+

Для миграции вам необходимо сделать следующее:

class AddSexToUsers < ActiveRecord::Migration
  def change
    add_column :users, :sex, "ENUM('female', 'male') DEFAULT 'female'"
  end
end

Ответ 3

Обычно я делаю следующее:

# 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

Используя хэш в качестве аргумента для enum (см. документы), эта строка сохраняет строки в базе данных. В то же время это позволяет вам использовать все классные вспомогательные методы Rails:

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"

Ответ 5

Насколько мне известно, это невозможно в стандартном Rails перечислении. Посмотрите https://github.com/lwe/simple_enum, он более функционально богат, а также позволяет хранить значения enum в виде строк в DB (строка типа столбца, т.е. varchar in термины БД).