将时间戳添加到db Rails 5+中的现有表

时间:2017-10-02 07:10:55

标签: ruby-on-rails ruby migration

尝试将Timestamps添加到现有表中。 根据{{​​3}}

以下是迁移中的代码:

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

获取错误:

- add_timestamps(:products,{:null => false}) 铁轨中止了! StandardError:发生错误,此以及所有后续迁移都已取消: SQLite3 :: SQLException:无法使用默认值NULL添加NOT NULL列:ALTER TABLE" products" ADD" created_at" datetime NOT NULL

我还尝试了Api documenation add_timestamps

中的所有解决方案

同样的错误...... Rails 5.1.4 Ruby 2.4.0

6 个答案:

答案 0 :(得分:18)

您不能将具有非空约束的列添加到非空表中,因为表中的现有行会立即具有空值,因此条件失败。

相反,请分三步介绍这些列:

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

  # backfill existing record with created_at and updated_at
  # values making 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 not null constraints
  change_column_null :products, :created_at, false
  change_column_null :products, :updated_at, false
end

答案 1 :(得分:2)

我喜欢@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。如here所述,该命令是执行回滚然后再次迁移回去的快捷方式。

现在,您将看到您的架构已使用相应的created_atupdated_at列进行了更新。

这样做的具体好处是它非常容易实现,您无需创建额外的迁移文件,而是学习使用非常方便的命令;)

答案 2 :(得分:2)

我认为,在迁移过程中使用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

答案 3 :(得分:0)

感谢spicekermann我能够做到这一点

  def change
    add_timestamps :products, null: true
  end

您的解决方案也很好,但为了完整性我们可以在迁移中使用Product类,我们需要将其加载到迁移中,这样做的正确方法是什么? &#34;需要&#34;或其他方法。

答案 4 :(得分:0)

我使用的是Rails 5.0,但这些选项均无效。 rails:db:redo可以使用,但对于大多数人来说不是可行的解决方案。

唯一起作用的是

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

答案 5 :(得分:0)

我遇到了同样的问题。我希望最终结果严格等同于新数据库上的 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