ruby-on-rails 在db Rails 5+中向现有表添加时间戳

sr4lhrrt  于 2023-03-31  发布在  Ruby
关注(0)|答案(6)|浏览(155)

正在尝试将时间戳添加到现有表。根据Api documenation add_timestamps
下面是我在迁移中的代码:

def change
    add_timestamps(:products, null: false)
  end

获取错误:

*-- add_timestamps(:products, {:null=>false})
rails aborted!
StandardError: An error has occurred, this and all later migrations canceled:
SQLite3::SQLException: Cannot add a NOT NULL column with default value NULL: ALTER TABLE "products" ADD "created_at" datetime NOT NULL*

我也尝试了所有的解决方案在此thread
Rails 5.1.4 Ruby 2.4.0

5anewei6

5anewei61#

不能将具有not-null约束的列添加到非空表中,因为表中现有的行将立即具有空值,因此条件失败。
而是分三步引入列:

def change
  # add new column but allow null values
  add_timestamps :products, null: true 

  # backfill existing records with created_at and updated_at
  # values that make clear that the records are faked
  long_ago = DateTime.new(2000, 1, 1)
  Product.update_all(created_at: long_ago, updated_at: long_ago)

  # change to not null constraints
  change_column_null :products, :created_at, false
  change_column_null :products, :updated_at, false
end
oyt4ldly

oyt4ldly2#

在我看来,在迁移中使用ActiveRecord查询甚至SQL操作现有数据是错误的。
正确的rails 5.2+方法是:

class AddTimestampsToCars < ActiveRecord::Migration[5.2]
  def change
    add_timestamps :cars, null: false, default: -> { 'NOW()' }
  end
end

这是一个过程,所以你应该能够设置一个日期在过去,如果你想。
来源:https://github.com/rails/rails/pull/20005

dffbzjpn

dffbzjpn3#

我喜欢@spickermann的方法,因为它考虑到了现有的记录,而且可能您的迁移已经一直进行到生产,他的方法确保了数据的持久性。
尽管如此,你们中的许多人可能会发现自己处于这种情况,但仍处于开发阶段,这意味着没有真实的的敏感数据,你可能会害怕失去......这给了你更多的自由,你可以如何执行表中的更改。
如果您的代码和记录只存在于本地(如果您仍然没有创建记录,跳过步骤1),并且该表是在上次迁移中创建的,我的建议是:
1.-删除该表中的所有记录。
2.-转到您的迁移文件并通过添加t.timestamps对其进行编辑,使其看起来像这样:

class CreateInstitutionalLegals < ActiveRecord::Migration[5.0]
      def change
        create_table :institutional_legals do |t|
          # Your original migration content goes here
          .
          .
          t.timestamps # This is your addition
        end
      end
    end

3.-然后转到控制台并输入rails:db:redo。正如这里所解释的,该命令是执行回滚然后再次迁移回来的快捷方式。
现在,您将看到模式已更新为相应的created_atupdated_at列。
这样做的具体好处是它非常容易做到,您不需要创建额外的迁移文件,并且您可以学习使用非常方便的命令;)

ztigrdn8

ztigrdn84#

我也遇到了同样的问题。我希望最终结果严格等价于新数据库上的add_timestamps :products
我没有运行查询来回填,而是完成了一个3步的过程。

  • 添加允许为空的列并默认为当前时间以进行回填
  • 将约束更改为非空
  • 删除默认值

它是可逆的。

add_column :products, :created_at, :datetime, precision: 6, null: true, default: -> { "CURRENT_TIMESTAMP" }
    add_column :products, :updated_at, :datetime, precision: 6, null: true, default: -> { "CURRENT_TIMESTAMP" }

    change_column_null :products, :created_at, false
    change_column_null :products, :updated_at, false

    change_column_default :products, :created_at, from:  -> { "CURRENT_TIMESTAMP" }, to: nil
    change_column_default :products, :updated_at, from:  -> { "CURRENT_TIMESTAMP" }, to: nil

注意:这是在Rails 6.1和PostgreSQL中实现的

jslywgbw

jslywgbw5#

我在rails 5.0上,这些选项都不起作用。rails:db:redo可以工作,但对大多数人来说不是一个可行的解决方案。
唯一起作用的是

def change
    add_column :products, :created_at, :timestamp
    add_column :products, :updated_at, :timestamp
end
idfiyjo8

idfiyjo86#

我做了类似于@jeremiemv的操作,只是少了几个步骤,如果在添加带有默认值的时间戳后不更改列默认值,那么默认值将作为迁移运行的时间显示在表结构中。
我的解决方案的不同之处还在于,当您提供默认值时,您不需要管理null: true然后false

add_timestamps :products, null: false, default: Time.current
change_column_default :products, :created_at, nil
change_column_default :products, :updated_at, nil

Rails 7和MySQL

相关问题