Реверсивная миграция для 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
в этом случае рельсы будут жаловаться на эту ошибку. Поскольку он может потерять данные
Другой пример будет из string
→ integer
.
это действительно хорошая статья объясняет то же самое
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
прочитайте эту статью, объясните сценарий