Чтобы избежать условий гонки, вы можете использовать автоинкрементный столбец.
Сначала создайте автоинкрементный столбец:
class AddSetupCodeToExposures < ActiveRecord::Migration[5.2]
def change
add_column :exposures, :setup_code, :serial
end
end
Этот пример для PostgreSQL.В MySQL вы можете создать вторичный столбец с автоинкрементом только в том случае, если вы используете MyISAM, а не INNODB (зачем вам?), Поэтому вам нужно будет найти другое решение.
Возможно, вы можете использовать столбец ID, так какв любом случае он автоматически увеличивается.
Отображение целого числа в буквы алфавита довольно прост в Ruby:
irb(main):008:0> ALPHABET = ('A'..'Z').to_a.unshift(nil)
=> [nil, "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
irb(main):009:0> ALPHABET[1]
=> "A"
irb(main):010:0> ALPHABET[26]
=> "Z"
В модели мы могли бы обрабатывать приведение значений с помощью пользовательского установщикаи getter:
class Exposure < ApplicationRecord
# This creates a array of the 24 ASCII letters A..Z
# adding null to the beginning lets us treat it as a 1 indexed array
ALPHABET = ('A'..'Z').to_a.unshift(nil)
def setup_code
# the default value here handles out of range values
self.class.integer_to_letter(super || 0) || "default_value"
end
def setup_code=(value)
super self.class.integer_to_letter(value)
end
def self.integer_to_letter(integer)
ALPHABET[integer]
end
def self.letter_to_integer(letter)
ALPHABET.index(letter)
end
end
Одна ошибка с автоматически увеличивающимися столбцами и значениями по умолчанию для базы данных заключается в том, что столбец не заполняется при вставке записи:
irb(main):005:0> e = Exposure.create
(0.2ms) BEGIN
Exposure Create (0.7ms) INSERT INTO "exposures" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", "2018-09-27 12:26:24.672016"], ["updated_at", "2018-09-27 12:26:24.672016"]]
(0.6ms) COMMIT
=> #<Exposure id: 3, created_at: "2018-09-27 12:26:24", updated_at: "2018-09-27 12:26:24", setup_code: nil>
irb(main):006:0> e.setup_code
=> "default_value"
irb(main):007:0> e.reload
Exposure Load (0.7ms) SELECT "exposures".* FROM "exposures" WHERE "exposures"."id" = $1 LIMIT $2 [["id", 3], ["LIMIT", 1]]
=> #<Exposure id: 3, created_at: "2018-09-27 12:26:24", updated_at: "2018-09-27 12:26:24", setup_code: 3>
irb(main):008:0> e.setup_code
=> "C"
Поскольку ActiveRecord возвращает только id
столбец при вставке.
Изменение существующего столбца на последовательный
Это на самом деле не меняет столбец - вместо этого используется старый трюк с переименованием для создания нового столбца и перемещения старых значений вновый столбец.
# rename the old column
class RenameExposuresSequenceCode < ActiveRecord::Migration[5.2]
def change
rename_column :exposures, :setup_code, :old_setup_code
end
end
# add the new column
class AddSequencedSetupCodeToExposures < ActiveRecord::Migration[5.2]
def change
add_column :exposures, :setup_code, :serial
end
end
# convert the existing values
class ConvertOldSetupCodes < ActiveRecord::Migration[5.2]
def up
Exposure.find_each do |e|
converted_code = Exposure.letter_to_integer(e.old_setup_code)
e.update_attribute(setup_code: converted_code) if converted_code
end
end
def down
Exposure.find_each do |e|
converted_code = Exposure.integer_to_letter(e.setup_code)
e.update_attribute(old_setup_code: converted_code) if converted_code
end
end
end
# remove the old column
class RemoveOldSetupCodeFromExposures < ActiveRecord::Migration[5.2]
def change
remove_column :exposures, :old_setup_code, :string
end
end