Как изменить обнуляемый столбец, чтобы он не обнулялся в миграции Rails?

188

Я создал столбец даты в предыдущей миграции и установил его как обнуляемый. Теперь я хочу изменить его, чтобы он не обнулялся. Как мне поступить так, если в этой базе данных есть пустые строки? Я в порядке с установкой этих столбцов в Time.now, если они в настоящее время нулевые.

Кевин Панг
источник

Ответы:

204

Если вы делаете это в процессе миграции, то, вероятно, вы можете сделать это так:

# Make sure no null value exist
MyModel.where(date_column: nil).update_all(date_column: Time.now)

# Change the column to not allow null
change_column :my_models, :date_column, :datetime, null: false
DanneManne
источник
1
Просто примечание, потому что это заставило меня разорить мою базу данных разработчиков. Скорее всего использовать явный синтаксис хэш, как это: MyModel.update_all({:date_column => Time.now}, {:date_column => nil}). Запрос в вашей исходной форме только что заставил все мои модели иметь нулевое значение в поле.
dimitarvp
Спасибо за обновление. Я знаю, что это был не тот случай, когда я писал этот ответ, но я не могу вспомнить, какую версию Ruby или RoR я использовал в то время.
DanneManne
1
У вас есть метод «вверх» / «вниз» в этой миграции, или вы можете простой метод изменения в миграции?
EE33
2
changeМетод не так подходит для этого случая , потому что (1) update_allметод будет выполняться на обоих мигрируют и потенциального Revert. Это может быть не самым худшим, но потому что (2) миграция не может знать, из чего столбец был изменен при потенциальном возврате. Так что для этого случая я бы придерживался upи down.
DanneManne
2
Для всех, кто интересуется, мой ответ показывает, как сделать это за один шаг.
Рик Смит
167

В Rails 4 это лучшее (DRYer) решение:

change_column_null :my_models, :date_column, false

Чтобы гарантировать отсутствие записей со NULLзначениями в этом столбце, вы можете передать четвертый параметр, который является значением по умолчанию, используемым для записей со NULLзначениями:

change_column_null :my_models, :date_column, false, Time.now
mrbrdo
источник
4
Это вызывает проблемы, когда таблица уже имеет нулевые значения. Смотрите мой ответ
Рик Смит
5
Также доступно в 3.2. Имеет также 4-й параметр для установки значения по умолчанию, где значение равно нулю.
Toxaq
1
Плюс 1 за change_column_null. Однако приведенный выше комментарий Рика Смита указывает на очень веский случай.
0112
Обновлено, чтобы добавить запрос на обновление нулевых значений. 4-й параметр (значение по умолчанию) полезен только в том случае, если вы действительно хотите иметь значение по умолчанию для будущих записей.
mrbrdo
3
На самом деле, в соответствии с документацией Rails 4.2, 4-й параметр НЕ устанавливает значение по умолчанию для будущих записей: «Метод принимает необязательный четвертый аргумент для замены существующих + NULL + s каким-либо другим значением. Обратите внимание, что четвертый аргумент не устанавливает столбец по умолчанию. "
Майк Фишер
70

Rails 4 (другие ответы по Rails 4 имеют проблемы):

def change
  change_column_null(:users, :admin, false, <put a default value here> )
  # change_column(:users, :admin, :string, :default => "")
end

Изменение столбца со значениями NULL в нем, чтобы запретить NULL, вызовет проблемы. Это именно тот код, который будет нормально работать в вашей настройке разработки, а затем падать, когда вы пытаетесь развернуть его в своей рабочей среде LIVE . Сначала вы должны изменить значения NULL на что-то допустимое, а затем запретить значения NULL. 4-е значение в change_column_nullделает именно это. Смотрите документацию для более подробной информации.

Кроме того, я обычно предпочитаю устанавливать значение по умолчанию для поля, поэтому мне не нужно будет указывать значение поля каждый раз, когда я создаю новый объект. Я включил закомментированный код, чтобы сделать это также.

Рик Смит
источник
3
Для Rails 4 это, кажется, самый точный и полный ответ, включая закомментированную настройку по умолчанию.
Майк Фишер
4
Если вы добавляете новый столбец в таблицу и хотите вставить новые значения для нуля, но не хотите добавлять значение по умолчанию для столбца, вы можете сделать это в своей миграции: add_column :users, :admin, :stringthenchange_column_null(:admin, :string, false, "new_value_for_existing_records")
colsen
34

Создайте миграцию, в которой есть change_columnоператор со :default =>значением.

change_column :my_table, :my_column, :integer, :default => 0, :null => false

Смотрите: change_column

В зависимости от механизма базы данных вам может понадобиться change_column_null

jessecurry
источник
1
Это сработало для меня. Использование MySql локально. Когда я запустил и запустил приложение в Heroku (Postgres), оно обрушилось на столбец, который не был нулевым, когда я писал его нулевым - по праву так. Будет работать только «change_column_null», не может использовать «change_column ...: null => false» на MySql. Спасибо.
rtfminc
1
Итак, какова была ваша миграция после change_column_null
js111
1
Postges более строг, чем MySQL - я ожидаю, что это потребует change_column_null.
jessecurry
3
@rtfminc Я настоятельно рекомендую вам использовать один и тот же механизм базы данных при разработке и в производстве, поскольку это позволяет избежать многих проблем, когда дело доходит до крайних случаев.
yagooar
12

Рельсы 4:

def change
  change_column_null(:users, :admin, false )
end
piratebroadcast
источник
1
Пожалуйста, предоставьте описание ваших ответов.
Вахью Кристианто
3

В Rails 4.02+, согласно документации, нет метода, подобного update_all2 аргументам. Вместо этого можно использовать этот код:

# Make sure no null value exist
MyModel.where(date_column: nil).update_all(date_column: Time.now)

# Change the column to not allow null
change_column :my_models, :date_column, :datetime, null: false
jmarceli
источник
2

Вы не можете использовать add_timestamps и null: false, если у вас есть существующие записи, поэтому вот решение:

def change
  add_timestamps(:buttons, null: true)

  Button.find_each { |b| b.update(created_at: Time.zone.now, updated_at: Time.zone.now) }

  change_column_null(:buttons, :created_at, false)
  change_column_null(:buttons, :updated_at, false)
end
Николя Малевр
источник