Як змінити нульовий стовпчик на нульовий під час міграції 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

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

4
Це спричиняє проблеми, коли в таблиці вже є нульові значення. Дивіться мою відповідь
Рік Сміт

5
Також доступний в 3.2. Має і 4-й параметр для встановлення за замовчуванням, де значення є нульовим.
toxaq

1
Плюс 1 за change_column_null. Однак коментар Ріка Сміта вказує на дуже вагомий випадок.
0112

Оновлено, щоб додати запит на оновлення нульових значень. Четвертий параметр (значення за замовчуванням) корисний лише тоді, коли ви дійсно хочете мати за замовчуванням також і майбутні записи.
мрбрдо

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. Четверте значення у change_column_nullробить саме це. Детальнішу інформацію див. У документації .

Крім того, я, як правило, віддаю перевагу встановити значення поля за замовчуванням для поля, тому мені не потрібно вказувати значення поля кожного разу, коли я створюю новий об'єкт. Я включив коментований код, щоб зробити це також.


3
Для Rails 4 це, як видається, є найточнішою та найповнішою відповіддю, включаючи налаштування за замовчуванням, коментовані за замовчуванням.
Майк Фішер

4
Якщо ви додаєте новий стовпець у таблицю і хочете вставити нові значення для null, але не хочете додавати значення стовпця за замовчуванням, це можна зробити під час міграції: add_column :users, :admin, :stringтодіchange_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


1
Це працювало для мене. Використання MySql локально. При натисканні та запуску програми в Heroku (Postgres) вона вибилася на стовпчик, який не був нульовим, коли я писав це нуль - справедливо. Тільки "change_column_null" працював би не міг використовувати "change_column ...: null => false" на MySql. Дякую.
rtfminc

1
тож якою була ваша міграція після change_column_null
js111

1
Повідомлення більш суворі, ніж MySQL - я б очікував, що це зажадає change_column_null.
1212 року

3
@rtfminc Я настійно рекомендую використовувати один і той же механізм баз даних у розробці та на виробництві, оскільки це дозволяє уникнути багатьох проблем, коли справа стосується кращих справ.
yagooar


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

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.