Миграция Rails: возможно ли обновление update_all с динамическим кодом?

Я хочу добавить новое поле в таблицу.

Мое новое поле «secret_code» в моей модели пользователя должно быть равно Digest::SHA1.hexdigest([Time.now, rand].join)[1..12].

То, что я пытаюсь сделать, это сгенерировать миграцию, которая добавит поле в таблицу, а также заполнит моих существующих пользователей (своего рода) уникальным «secret_code».

class AddSecretCodeToUsers < ActiveRecord::Migration
  def self.up
    add_column :users, :secret_code, :string
    User.update_all ["secret_code =?", Digest::SHA1.hexdigest([Time.now, rand].join)[1..12]]
  end

  def self.down
    remove_column :users, :secret_code
  end
end

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

Одним из решений было бы НЕ использовать update_all и запускать цикл для получения каждого пользователя и отправки обновления каждому из них, но в этом случае моя миграция будет чрезвычайно медленной.

Есть ли способ отправить «уникальное» случайное значение методу update_all?

Спасибо, Аугусто


person Augusto    schedule 30.09.2011    source источник
comment
Mysql в разработке и postgres в производстве   -  person Augusto    schedule 30.09.2011


Ответы (2)


Попробуйте изменить его на Digest::SHA1.hexdigest([Time.now, rand].to_s), но лично я бы создал задачу rake для вышеуказанного, поскольку на самом деле это не миграция.

Ваша задача грабли сделает

User.all.each do |u|
  u.update_attribute(:secret_code, Digest::SHA1.hexdigest([Time.now, rand].to_s))
end

Однако для вашей миграции я бы также добавил t.string :secret_code, :default => Digest::SHA1.hexdigest([Time.now, rand].to_s) к атрибуту, чтобы он добавлялся во вновь созданные записи.

person Michael De Silva    schedule 30.09.2011
comment
Спасибо, но пропускает ли update_attribute обратные вызовы? В противном случае это будет очень медленно .. - person Augusto; 30.09.2011
comment
Вместо этого вы также можете просто выполнить User.update_all(:secret_code => Digest::SHA1.hexdigest([Time.now, rand].to_s)) в своей задаче rake =) - person Michael De Silva; 30.09.2011
comment
Поскольку вы не можете делать именно то, о чем просите, где-либо, кроме кода, вы должны выполнить команду для каждого пользователя. Это будет медленно, независимо от того, что вы делаете :( Если вы посмотрите на ответ @[mu is too short], у него есть способ сделать это быстро, но вы делаете что-то совершенно другое. - person Travis; 30.09.2011
comment
Спасибо за разъяснения, Трэвис! - person Michael De Silva; 30.09.2011

Для MySQL вы можете добавить это прямо в свой self.up:

connection.execute(%Q{
    update users
    set secret_code = substring(sha1(rand()) from 1 for 12)
})

PostgreSQL по умолчанию не поддерживает SHA1, но у него есть MD5, и этого, вероятно, достаточно для этого:

connection.execute(%Q{
    update users
    set secret_code = substring(md5(random()::varchar) from 1 for 12)
})

Если у вас установлен пакет pgcrypto, вы можете использовать SHA1 для обоих.

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

md5(random()::varchar || now()::varchar) -- PostgreSQL
sha(rand()            || now()         ) -- MySQL
person mu is too short    schedule 30.09.2011