Тип обновления Rails 4 при переходе на наследование одной таблицы

Рельсы 4.0.4, Руби 2.1.2

Я хочу использовать STI так:

User < ActiveRecord::Base
Admin < User

Но на данный момент у меня есть:

User < ActiveRecord::Base
Info < ActiveRecord::Base

Итак, я изменил свои модели, а затем начал писать свою миграцию. В моей миграции я сначала добавляю столбец, чтобы разрешить STI:

add_column :users, :type, :string

Затем я хочу обновить пользователей, которые в настоящее время находятся в базе данных, чтобы они были администраторами.

# Place I'm currently stuck

Затем я перемещаю все свои информационные записи в таблицу пользователей.

Info.all.each { |info| User.create(name: info.name, email: info.email) }

Кажется, все работает, кроме превращения предыдущих пользователей в администраторов. Вот некоторые вещи, которые я пробовал:

# Seems to work, but doesn't actually save type value
User.each do |user|
  user.becomes!(Admin)
  user.save! # evaluates to true, doesn't have any errors
end

# Seems to work, but doesn't actually save type value
# I've also tried a combo of this one and the above one
User.each do |user|
  user.type = "Admin"
  user.save! # evaluates to true, doesn't have any errors
end

User.each do |user|
  user = user.becomes!(Admin)
  user.save! # evaluates to true, doesn't have any errors
end

# Seems to work, but doesn't actually save type value
User.each do |user|
  user.update_attributes(type: "Admin")
end

Каждый раз, когда локальные пользовательские переменные имеют правильный тип («Администратор»), вместе с сохранением оценивается как true, но когда я проверяю Admin.count или проверяю значение типа Users, оно всегда равно нулю. Я знаю, что вы не должны их менять, но это просто перенос данных в STI, и тогда я смогу начать создавать пользователей или администраторов с соответствующим классом.

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


person Tom Prats    schedule 01.10.2014    source источник
comment
Обратите внимание, что объекты в памяти по-прежнему будут относиться к предыдущему классу. Попробуйте перезагрузить объекты, и вы должны увидеть новый класс для этих пользователей.   -  person Nobita    schedule 02.10.2014
comment
Я, выполнив User.where(...) и Admin.all, получил те же результаты, что и до обновлений.   -  person Tom Prats    schedule 02.10.2014


Ответы (2)


Если бы у вас было больше строк в базе данных, User.each стал бы довольно медленным, поскольку он выполняет вызов SQL для каждого пользователя.

Как правило, вы можете использовать User.update_all(field: value) для этого в одном вызове SQL, но есть и другая причина, по которой этого следует избегать: если модель User позже будет удалена, миграция больше не будет выполняться.

Один из способов обновить все строки сразу без ссылки на модель — использовать чистый SQL при миграции:

def up
  execute "UPDATE users SET type = 'Admin' WHERE type IS NULL"
end
person James EJ    schedule 01.08.2015
comment
Определенно согласен, что это было бы намного лучше, чем создавать экземпляры всех этих объектов - person Tom Prats; 01.08.2015

Оказывается, хотя update_attributes не работает для типа (я еще не исследовал почему), update_column работает.

Таким образом, миграция просто становится:

User.each do |user|
  user.update_columns(type: "Admin")
end

Причина, по которой это работает, а другие обновления не работают, возможно, связана с невыполнением обратных вызовов или проверок. У меня нет обратных вызовов, которые бы помешали этому, но, возможно, есть стандартные обратные вызовы Rails для type

http://apidock.com/rails/ActiveRecord/Persistence/update_columns

person Tom Prats    schedule 02.10.2014