Перечислимый тип (также называемый «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"
Перечисляемый тип чувствителен к регистру, это означает, что male
MALE
Пустое пространство также имеет значение.
Интеграция 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
Я надеюсь, что вы нашли это полезным. Спасибо за прочтение