Статьи

Перечисляемые типы с ActiveRecord и PostgreSQL

Перечислимый тип (также называемый «enum») — это тип данных с набором именованных элементов, каждый из которых имеет различное значение, которое, среди прочего, может использоваться в качестве констант. Перечисление может быть реализовано на разных языках и в разных средах, и в этой статье мы реализуем перечисление в приложении Ruby on Rails .

Rails, как вы, вероятно, знаете, является средой веб-приложений MVC с открытым исходным кодом. M в MVC — это ActiveRecord , который является структурой реляционного сопоставления объектов . ActiveRecord дает нам возможность представлять модели, их данные и другие операции с базами данных объектно-ориентированным способом. Перечисление обрабатывается специально в ActiveRecord и реализуется с помощью модуля ActiveRecord::Enum .

Rails обеспечивает очень хорошее использование ActiveRecord::Enumперечисляемым типом PostgreSQL . К сожалению, практически нет упоминаний о том, как интегрировать эти технологии. Есть даже утверждение , что в нем нет специальной поддержки перечислимых типов, хотя это возможно.

В этой статье мы рассмотрим, как использовать перечисляемые типы PostgreSQL с ActiveRecord::Enum

Эта статья состоит из 3 разделов:

  • Введение в ActiveRecord::Enum
  • Введение в перечисляемые типы PostgreSQL
  • Интеграция ActiveRecord::Enum

Если вы уже знакомы с любым из вышеперечисленных, не стесняйтесь пропустить его и перейти к тому, что вам нравится.

Введение в ActiveRecord :: Enum

ActiveRecord::Enumпримечаниях к выпуску . Он предоставляет возможность управлять перечислениями, изменять их значения и сопоставлять модель с любым доступным значением перечисления. Прежде чем вы сможете использовать ActiveRecord::Enum

В этой статье будет использован следующий вариант использования ActiveRecord::Enum Мы добавим гендерный атрибут в модель User со следующими возможными значениями:

  • мужчина
  • женский
  • Точно сказать не могу
  • Предпочитаю не разглашать

Миграция базы данных

ActiveRecord::Enum Давайте создадим миграцию базы данных следующим образом:

 bundle exec rails generate migration AddGenderToUsers gender:integer

Приведенный выше код создаст новую миграцию:

 # db/migrate/20150619131527_add_gender_to_users.rb
class AddGenderToUsers < ActiveRecord::Migration
  def change
    add_column :users, :gender, :integer
  end
end

Мы можем добавить значение по умолчанию для миграции. И чтобы повысить производительность приложения, мы также можем добавить индекс к столбцу, добавив следующий код:

 # db/migrate/20150619131527_add_gender_to_users.rb
class AddGenderToUsers < ActiveRecord::Migration
  def change
    # add `default: 3`, and `index: true`
    add_column :users, :gender, :integer, default: 0, index: true
  end
end

Наконец, запустите миграцию:

 bundle exec rake db:migrate

Мы закончили с миграцией базы данных. Далее нам нужно объявить ActiveRecord::Enum

декларация

Существует два вида объявлений для ActiveRecord::Enum Используя наш вариант использования, мы добавим пол в модель User. Первое объявление использует Array :

 # app/models/user.rb
class User < ActiveRecord::Base
  enum gender: [ :male, :female, :not_sure, :prefer_not_to_disclose ]
end

Приведенный выше код будет хранить male Будущие добавления должны быть добавлены в конец массива, а изменение порядка или удаление значений не должны выполняться. Чтобы иметь возможность манипулировать значениями позже, используйте второй тип объявлений, используя Hash

 # app/models/user.rb
class User < ActiveRecord::Base
  enum gender: { male: 0, female: 1, not_sure: 2, prefer_not_to_disclose: 3 }
end

С помощью этой декларации мы можем изменить порядок и удалить любое значение. Значение может начинаться с любого числа, если оно совпадает со значением по умолчанию при переносе базы данных. Будущие дополнения можно поставить в любую позицию.

использование

Существует 3 аспекта использования ActiveRecord::Enum Были предложены вспомогательные методы для выполнения этих трех задач. В нашем случае мы можем изменить пол пользователя следующим образом:

 # mutate enum using the exclamation mark (!) method.
user.male!
user.female!
user.not_sure!
user.prefer_not_to_disclose!

# or mutate enum using value assignment
user.gender = nil
user.gender = "male"
user.gender = "female"
user.gender = "not_sure"
user.gender = "prefer_not_to_disclose"

Чтобы получить значение перечисления:

 # retrieve enum value using the question mark (?) method
user.male?                   # => false
user.female?                 # => false
user.not_sure?               # => false
user.prefer_not_to_disclose? # => true

# or retrieve using the enum name
user.gender                  # => "prefer_not_to_disclose"

Чтобы включить модель User в значения enum, используйте следующие методы:

 # scope using enum values
User.male
User.female
User.not_sure
User.prefer_not_to_disclose

# or we can scope it manually using query with provided class methods
User.where("gender <> ?", User.genders[:prefer_not_to_disclose])

Как упоминалось ранее, ActiveRecord::Enum К сожалению, значение enum в базе данных становится менее значимым и не требует пояснений. Перечисленные значения становятся зависимыми от модели и становятся тесно связанными с этой моделью. Чтобы отсоединить значение enum от модели, нам нужно сохранить его с использованием перечислимого типа в базе данных.

Введение в перечисляемые типы PostgreSQL

PostgreSQL предоставляет Enumerated Type для хранения статического и упорядоченного набора значений. Давайте посмотрим, как это реализовано в PostgreSQL. В нашем случае использования мы можем создать тип пола следующим образом:

 CREATE TYPE gender AS ENUM ('male', 'female', 'not_sure', 'prefer_not_to_disclose');

После создания мы можем использовать тип в нашей таблице, как и любой другой тип. В качестве примера мы можем создать таблицу пользователей с атрибутом типа пола следующим образом.

 CREATE TABLE users (
    name text,
    gender gender
);

Используя перечисление вместо целого, мы получаем безопасность типов. Это означает, что вы не можете добавить значение, которое не является частью значений перечисления. Вот пример функции безопасности типа:

 INSERT INTO users(name, gender) VALUES ('John Doe', 'male');
INSERT INTO users(name, gender) VALUES ('Confused John Doe', 'not_sure');
INSERT INTO users(name, gender) VALUES ('Unknown John Doe', 'unknown');
ERROR: invalid input value for enum gender: "unknown"

Перечисляемый тип чувствителен к регистру, это означает, что maleMALE Пустое пространство также имеет значение.

Интеграция ActiveRecord :: Enum с перечисляемыми типами PostgreSQL

Перед использованием ActiveRecord::Enum

Сначала создадим миграцию базы данных:

 bundle exec rails generate migration AddGenderToUsers gender:gender

Затем отредактируйте сгенерированную миграцию, добавив тип:

 # db/migrate/20150619131527_add_gender_to_users.rb
class AddGenderToUsers < ActiveRecord::Migration
  def up
    execute <<-SQL
      CREATE TYPE gender AS ENUM ('male', 'female', 'not_sure', 'prefer_not_to_disclose');
    SQL

    add_column :users, :gender, :gender, index: true
  end

  def down
    remove_column :users, :gender

    execute <<-SQL
      DROP TYPE gender;
    SQL
  end
end

Как только вы закончите с этим, запустите миграцию:

 bundle exec rake db:migrate

Теперь мы завершили миграцию базы данных. Следующим шагом является объявление enum в модели User. Ранее мы использовали формы Array и Hash для объявления перечисления. Чтобы интеграция работала, нам нужно объявить enum с помощью формы Hash:

 # app/models/user.rb
class User < ActiveRecord::Base
  enum gender: {
    male:                   'male',
    female:                 'female',
    not_sure:               'not_sure',
    prefer_not_to_disclose: 'prefer_not_to_disclose'
  }
end

Наконец, мы можем хранить значения ActiveRecord::Enum В качестве бонуса все вспомогательные методы, предоставляемые ActiveRecord::Enum

Вывод

ActiveRecord::Enum Значение не имеет смысла без модели и не требует пояснений. Другими словами, значение перечисления тесно связано с моделью. Чтобы иметь лучшую архитектуру, нам нужно отцепить значение enum от модели.

Перечисляемые типы PostgreSQL обеспечивают хорошее дополнение для ActiveRecord::Enum У нас могут быть значимые и не требующие пояснений данные, для которых не требуется модель для расшифровки значения. С помощью функции безопасности типов, предоставляемой PostgreSQL, мы можем иметь твердую основу для хранения значений ActiveRecord::Enum

Я надеюсь, что вы нашли это полезным. Спасибо за прочтение