Вставьте строковый ключ в перечисление, вызывая обновление столбца при проверке экземпляра в условном выражении. - PullRequest
0 голосов
/ 01 ноября 2018

Я использую Rails 5.0.0.1

Мы хотели, чтобы пустая строка была опцией в enum в одной из наших моделей. После нескольких дней работы в производственном процессе, обнаружив, что что-то было не так и отладив проблему, мы обнаружили: экземпляр этой модели обновлял свой столбец каждый раз, когда код проверял его достоверность (то есть !@model и !!@model). Это было вызвано наличием пары ключей в перечислении с ключом, являющимся пустой строкой {'': 0, 'good': 1}.

Первоначально предполагалось преобразовать пустую строку в nil {'': nil, 'good':1} (что-то не так в потребителе API, который отправляет пустую строку, и мы не хотим его сохранять). После проверки проблемы мы поняли значение nil в хэше enum не является проблемой, только пустая строка в одном из ее ключей .

Теперь, когда мы знаем, что ошибка существует, мы найдем обходной путь. Однако мы хотим знать, почему это происходило, поскольку при создании модели, проверке ее достоверности или в другом месте кода не было ошибок.

Я обнаружил эту похожую ошибку , но речь идет о validates_associated и Foo.create, объяснение проблемы не дано.

Шаги для воспроизведения

Создание модели с целочисленным столбцом rails g model Example my_enum:integer

class CreateExamples < ActiveRecord::Migration[5.0]
  def change
    create_table :examples do |t|
      t.integer :my_enum

      t.timestamps
    end
  end
end

Укажите столбец my_enum как enum в модели:

class Example < ApplicationRecord
  enum my_enum: { '': 0, good: 1}
end

Создайте его экземпляр с помощью my_enum = 'good' и проверьте его истинность

@model = Example.new
@model.my_enum = 'good'
@model.save
@model.my_enum # => 'good'
# so far so good
# the model works as expected if restarting console,
# finding it with Example.first, and so on...

!@model # For some unknown reason updates my_enum to nil
# Expected behavior: to return false
# Actual behavior: updates my_enum and returns true

Повторите ту же настройку и проверьте с помощью bang bang !!, столбец также будет обновлен до nil.

!!@model # Updates my_enum to nil
# Expected behavior: to return true
# Actual behavior: updates my_enum to nil and returns false
# Note that @model being an instance should evaluate to true

Это запрос, выполненный после выполнения !@model или !!@model

(0.2ms)  BEGIN
  SQL (0.4ms)  UPDATE "examples" SET "my_enum" = $1, "updated_at" = $2 WHERE "examples"."id" = $3  [["my_enum", nil], ["updated_at", 2018-11-01 13:50:50 UTC], ["id", 1]]
   (2.8ms)  COMMIT

Почему это происходит? Исправлена ​​ли ошибка в других версиях rails? Какой будет лучшим решением? Разве вы не ожидаете того же поведения, что и мы? Если бы это работало как ожидалось, мы бы проигнорировали все пустые строки, теперь нам может понадобиться before_save: prevent_empty_in_my_enum.

Возможно полезная информация:

ActiveRecord::Base.connection.select_value('SELECT version()')
   (1.7ms)  SELECT version()
=> "PostgreSQL 9.5.7 on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4, 64-bit"

1 Ответ

0 голосов
/ 01 ноября 2018

Правильное использование enum не предназначено для пустых строк. Каждый ключ enum преобразуется в методы def ? и def !, поэтому это не имеет смысла.

Однако обновление столбца без вывода сообщений может быть более важным поведением. выпуск был отправлен в репозиторий Rails .

Если это не так важно, чтобы создать PR, лучшее решение - найти обходной путь для этой конкретной проблемы: предотвратить сохранение пустых строк в базе данных.

Параметр может быть отфильтрован на контроллере, что позволяет отказаться от такого поведения в более поздних версиях API.

Другой вариант - создать метод модели, который предотвращает сохранение параметров blank?.

def my_enum=(value)
  super(value.blank? : nil, value)
end

Это поведение остается таким же в рельсовой версии 5.2.1, что можно увидеть в проблеме.

РЕДАКТИРОВАТЬ:

Проблема была исправлена ​​в PR # 34385 .

...