Реверсивная миграция для change_column_default из-за отсутствия значения по умолчанию в Rails

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

change_column_default :products, :approved, from: true, to: false

У меня есть метод change в Rails, похожий на следующий:

change_column_default :people, :height, from: nil, to: 0

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

Однако, когда я пытаюсь вернуть его обратно, я получаю

ActiveRecord::IrreversibleMigration: ActiveRecord::IrreversibleMigration

Учитывая, что я даю Rails a from и to, почему он не принимает его?

Я использую Rails 4.2.0.

Ответы

Ответ 1

Руководство, связанное с вопросом, относится к Edge Rails, а не к выпущенной версии Rails.

Реверсивный синтаксис для change_column_default является результатом запроса pull 20018. Запрос pull также обновил направляющие Rails для Edge Rails.

От activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb:

-      def change_column_default(table_name, column_name, default)
+      # Passing a hash containing +:from+ and +:to+ will make this change
+      # reversible in migration:
+      #
+      #   change_column_default(:posts, :state, from: nil, to: "draft")
+      #
+      def change_column_default(table_name, column_name, default_or_changes)

Запрос на перенос был сделан 27 июня 2015 года, который является более поздним, чем любая версия Rails, выпущенная по состоянию на 1 августа 2015 года.

Документация для миграции для Rails 4.2.3 отражает тот факт, что обратимый синтаксис пока недоступен:

change_column_default :products, :approved, false

Ответ 2

если вы используете mysql в качестве адаптера, то согласно этой ссылке http://apidock.com/rails/ActiveRecord/ConnectionAdapters/AbstractMysqlAdapter/change_column_default, миграция change_column_default выполняется следующим образом

def change_column_default(table_name, column_name, default) #:nodoc:
 column = column_for(table_name, column_name)
 change_column table_name, column_name, column.sql_type, :default => default
end

так как вы видите, что он вызывает change_column внутри себя, когда вы вызываете change_column_default и согласно этой ссылке http://edgeguides.rubyonrails.org/active_record_migrations.html  change_column миграция необратима.

Это показывает, почему вы получаете ActiveRecord::IrreversibleMigration: ActiveRecord::IrreversibleMigration

Итак, если вы хотите выполнить миграцию с помощью change_column_default, вам нужно добавить def up и def down.

Я бы предложил использовать change_column, поскольку он уже был вызван в файле change_column_default.

def up
 change_column :people, :height, :integer, default: 0
end

def down
 change_column :people, :height, :integer, default: nil
end

Ответ 3

Прежде всего как Джонатан Аллард сказал, что from и to не находятся в источнике метода, что означает, что change_column_default не принимает его, Это просто так:

def sum(a)
  return a
end

Теперь, если вы попробуете передать ему две переменные типа sum(a, b) или что-то другое, оно не примет это право. Это то, что вы пытаетесь сделать выше, используя from и to.

Теперь правильный синтаксис:

change_column_default(:people, :height, 0)

Метод не принимает from и to (поскольку он определен как таковой, даже если они являются хеш-ключами, если метод не использует эту пару значений ключа нигде, тогда это бесполезно для это), и если это новый столбец, очевидно, что он будет иметь значение по умолчанию nil (если оно не задано раньше) и предположим, что если столбец height, если тип integer, и вы даете ему значение по умолчанию a, оно будет сохраните 0 в качестве значения по умолчанию (не на 100% уверен, но попытались сделать это из консоли rails). Для рельсов не имеет значения, каково значение по умолчанию, оно просто требует нового значения по умолчанию. Поэтому, если текущее значение по умолчанию 0 и вы установите его на nil, рельсы не будут жаловаться. Это ваша база данных, и вы хотите, что с ней делать. Просто, если база данных прерывает ее, если вы делаете что-то не так, как присваивание string до boolean, тогда она явно выдает ошибку.

Теперь, как только эта миграция будет выполнена, она установит значение по умолчанию на 0. Теперь rails не знает, что такое предыдущее значение по умолчанию. Поскольку он ушел, и он нигде не хранит его. Вот почему change_column_default - необратимая миграция. И если вы попытаетесь отбросить его, он даст вам ActiveRecord::IrreversibleMigration: ActiveRecord::IrreversibleMigration в случае метода change. Значит, когда вы использовали:

def change
  change_column_default(:people, :height, 0)
end

Поэтому для такого рода миграции мы используем метод up и down:

def up
  change_column_default(:people, :height, 0)
end

def down
  change_column_default(:people, :height, nil)
end

Надеюсь, что это поможет.

Ответ 4

По состоянию на октябрь 2016 эта функция (с использованием to: и from: для change_column_default должна быть обратимой) теперь доступна на ветке 5.x. К сожалению, он недоступен 4.2.x или ниже.: (

Проверка: git tag --contains f9c841927ac3d1daea2a9cebf08b18e844e5eec5 в проекте rails.

Ответ 5

Просто добавьте несколько точек. Если вы застряли в версии Rails, не поддерживающей обратимое change_column_default, один из способов преодолеть проблему:

def change
    # If your dataset is small, just cache in memory, if large, consider file dump here:
    cache = Table.all
    # Full column def important for reversibility
    remove_column :table, :column, :type, { config_hash }
    # Re-add the column with new default:
    add_column  :table, :column, :type, { config_hash, default: 0 }
    # Now update the data with cached records (there might be more efficient ways, of course):
    cache.each do |rec|
        Table.find(rec.id).update(column: rec.column)
    end
end

Ответ 6

При попытке понизить тип данных от value (0) до nil в этом случае рельсы будут жаловаться на эту ошибку. Поскольку он может потерять данные

Другой пример будет из stringinteger.

это действительно хорошая статья объясняет то же самое

UPDATE

Кажется, что ваши обратные вызовы переходов change_column_default в command_recorder (activerecord-4.2.0/lib/active_record/migration/command_recorder.rb#76) проверяют, установлено ли значение nil в качестве значения по умолчанию.

UPDATE2

получается, что, если вы используете change при миграции, они должны иметь возможность обратимого

но change_column не обратимо с помощью метода change. Поэтому вам придется использовать старый метод миграции рельсов с помощью up и down

прочитайте эту статью, объясните сценарий