#ruby-on-rails #ruby #database #time #database-migration
#ruby-on-rails #ruby #База данных #время #база данных -миграция
Вопрос:
Простите, если я задаю глупый вопрос, но у меня возникли некоторые проблемы при попытке обновить time
столбец до datetime
.
Вот как schema.rb
выглядит соответствующая часть моего:
create_table "shop_hours", id: :serial, force: :cascade do |t|
t.time "from_hours"
t.time "to_hours"
t.string "day"
t.integer "repair_shop_id"
t.boolean "is_shop_open"
t.integer "chain_id"
t.integer "regions", default: [], array: true
t.index ["repair_shop_id"], name: "index_shop_hours_on_repair_shop_id"
end
Вот пример случайного ShopHour
объекта:
[67] pry(main)> ShopHour.find(439)
#<ShopHour:0x00007ff05462d3a0
id: 439,
from_hours: Sat, 01 Jan 2000 15:00:00 UTC 00:00,
to_hours: Sat, 01 Jan 2000 00:00:00 UTC 00:00,
day: "Friday",
repair_shop_id: 468,
is_shop_open: true,
chain_id: nil,
regions: []>
В конечном счете, я хочу перенести атрибуты from_hours
и to_hours
во все мои ShopHour
таблицы, чтобы они были типа datetime
.
Я также хотел бы обновить дату для каждого from_hours
и to_hours
быть текущим.
Я попробовал эту миграцию, но столкнулся с ошибкой:
class ChangeShopHoursToDateTime < ActiveRecord::Migration[6.0]
def change
change_column :shop_hours, :from_hours, 'timestamp USING CAST(from_hours AS timestamp)'
change_column :shop_hours, :to_hours, 'timestamp USING CAST(to_hours AS timestamp)'
end
end
Вот ошибка, с которой я сталкиваюсь:
== 20201021083719 ChangeShopHoursToDateTime: migrating ========================
-- change_column(:shop_hours, :from_hours, "timestamp USING CAST(from_hours AS timestamp)")
rails aborted!
StandardError: An error has occurred, this and all later migrations canceled:
PG::CannotCoerce: ERROR: cannot cast type time without time zone to timestamp without time zone
LINE 1: ...s" ALTER COLUMN "from_hours" TYPE timestamp USING CAST(from_...
Пожалуйста, дайте мне знать, если я могу предоставить дополнительную информацию. Заранее спасибо!
Ответ №1:
Вы не можете автоматически преобразовать столбец времени в метку времени, поскольку у времени нет компонента даты. Postgres на самом деле правильно запрещает вам делать это, поскольку результат будет неоднозначным — на какую дату он должен быть действительно приведен 12:45
:
- 0 BC?
- начало времени epoc?
- сегодняшняя дата?
На самом деле в Ruby нет класса для представления времени без компонента даты. Основное отличие заключается в том, что Time — это простая оболочка, написанная на C, которая переносит временную метку UNIX, а DateTime лучше в исторические времена. Тот факт, что Rails просто приводит столбец time
базы данных ко времени, начинающемуся с 2000-01-01, на самом деле является просто странным, но прагматичным решением проблемы вместо создания чего-то вроде TimeWithoutDate
класса.
Если вы хотите перенести столбец базы данных из time в timestamp / timestampz, вам нужно сообщить базе данных, на какую дату вы ожидаете, что время будет:
class AddDatetimeColumnsToHours < ActiveRecord::Migration[6.0]
def up
add_column :shop_hours, :opens_at, :datetime
add_column :shop_hours, :closes_at, :datetime
ShopHour.update_all(
[ "closes_at = (timestamp '2000-01-01') to_hour, opens_at = (timestamp '2000-01-01') from_hour" ]
)
end
def down
remove_column :shop_hours, :opens_at
remove_column :shop_hours, :closes_at
end
end
Это добавляет два новых столбца, и вам действительно следует подумать о том, чтобы просто удалить существующий столбец и использовать эту схему именования, поскольку методы, которые начинаются с to_
, являются методами приведения по соглашению в Ruby (например to_s
, to_a
, to_h
) — to_hour
таким образом, это действительно плохое имя.
Комментарии:
1. О боже, какой фантастический ответ. Я хочу поблагодарить вас за то, что вы нашли время ответить так подробно (также, не снисходительно). Это объяснило все, что я хотел знать. Спасибо, что сделали мое утро! редактировать: вы полностью правы в отношении моего использования
to_
. Это … грубо.