简体   繁体   English

Rails迁移:可以使用动态代码进行update_all吗?

[英]Rails migration: update_all with dynamic code is possible?

I'd like to add a new field in a table. 我想在表格中添加一个新字段。

My new "secret_code" field in my User model should be equal to Digest::SHA1.hexdigest([Time.now, rand].join)[1..12]. 我的用户模型中新的“ secret_code”字段应等于Digest :: SHA1.hexdigest([Time.now,rand] .join)[1..12]。

What I'm trying to do is generate a migration that would add the field to the table and also populate my existing users with a (sort of) unique "secret_code". 我想做的是生成一个迁移,该迁移会将字段添加到表中,并用(某种)唯一的“ 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

The problem is that this migration populate all the existing users with the same secret code! 问题在于此迁移将使用相同的密码填充所有现有用户!

One solution would be NOT to use update_all and run a loop to fetch every user and send an update to each one, but in this case my migration would be extremely slow. 一种解决方案是不使用update_all并运行循环以获取每个用户并向每个用户发送更新,但是在这种情况下,我的迁移将非常缓慢。

Is there a way to send a "unique" random value to an update_all method? 有没有一种方法可以将“唯一”随机值发送到update_all方法?

Thanks, Augusto 谢谢,奥古斯托

Try changing it to Digest::SHA1.hexdigest([Time.now, rand].to_s) but personally I'd create a rake task to the above as it's not really a migration. 尝试将其更改为Digest::SHA1.hexdigest([Time.now, rand].to_s)但就我个人而言,我将在上面创建一个rake任务,因为它实际上不是迁移。

Your rake task would do 你的耙子任务会做

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

However, for your migration I'd also add t.string :secret_code, :default => Digest::SHA1.hexdigest([Time.now, rand].to_s) to the attribute so that it is added on newly created records. 但是,对于您的迁移,我还要将t.string :secret_code, :default => Digest::SHA1.hexdigest([Time.now, rand].to_s)到属性,以便将其添加到新创建的记录中。

For MySQL, you could throw this right in your self.up : 对于MySQL,您可以在self.up此权限:

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

PostgreSQL doesn't have SHA1 support by default but it does have MD5 and that's probably good enough for this: PostgreSQL默认不支持SHA1,但是它确实具有MD5,这可能已经足够了:

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

If you have the pgcrypto package installed then you could use SHA1 for both. 如果您安装了pgcrypto软件包,则可以同时使用SHA1。

Both of those will let the database do all the work and avoid all the overhead of round-tripping your whole table. 两者都将使数据库完成所有工作,并避免了往返整个表的所有开销。 You can play around with what you hash a bit as well, if you wanted to mix the time in: 如果您想将时间混合在以下内容中,也可以使用一些哈希值:

md5(random()::varchar || now()::varchar) -- PostgreSQL
sha(rand()            || now()         ) -- MySQL

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM