【问题标题】:Reversible migration for change_column_default from not having any default in Rails从 Rails 中没有任何默认值的 change_column_default 的可逆迁移
【发布时间】:2015-10-11 16:58:08
【问题描述】:

Rails guides to active record migrations 说你可以做

change_column_default :products, :approved, from: true, to: false

我在 Rails 中有一个类似于以下内容的 change 方法:

change_column_default :people, :height, from: nil, to: 0

旨在从没有任何默认值变为默认值为零。

但是,当我尝试回滚时,我得到了

ActiveRecord::IrreversibleMigration: ActiveRecord::IrreversibleMigration

考虑到我给 Rails 一个 fromto,它为什么不接受它?

我使用的是 Rails 4.2.0。

【问题讨论】:

  • Rails 是否试图避免类型强制问题?
  • 我在method definition 中看不到任何fromto 参数
  • @JonathanAllard 假设 fromto 是哈希键,而不是可选参数,它们不会出现在方法定义中。
  • @JonathanAllard 你怎么知道你链接的方法是由ActiveRecord::Migration 中的change_column_default 调用调用的方法?

标签: ruby-on-rails ruby-on-rails-4 rails-activerecord rails-migrations


【解决方案1】:

在 Rails 5+ 中使用 fromtoadded

问题中链接的指南适用于 Edge Rails,而不是 Rails 的已发布版本。

change_column_default 的可逆语法是拉取请求 20018 的结果。拉取请求还更新了 Edge Rails 的 Rails 指南。

来自activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb

-      def change_column_default(table_name, column_name, default)
+      # Passing a hash containing +:from+ and +:to+ will make this change
+      # reversible in migration:
+      #
+      #   change_column_default(:posts, :state, from: nil, to: "draft")
+      #
+      def change_column_default(table_name, column_name, default_or_changes)

拉取请求于 2015 年 6 月 27 日提出,比截至 2015 年 8 月 1 日发布的任何 Rails 版本都更新。

migration for Rails 4.2.3 的文档反映了可逆语法尚不可用的事实:

change_column_default :products, :approved, false

【讨论】:

    【解决方案2】:

    如果你使用mysql作为适配器,那么根据这个链接http://apidock.com/rails/ActiveRecord/ConnectionAdapters/AbstractMysqlAdapter/change_column_default,你的change_column_default迁移运行是这样的

    def change_column_default(table_name, column_name, default) #:nodoc:
     column = column_for(table_name, column_name)
     change_column table_name, column_name, column.sql_type, :default => default
    end
    

    因此,当您调用 change_column_default 并根据此链接时,您会看到它在自身内部调用 change_column http://edgeguides.rubyonrails.org/active_record_migrations.html change_column 迁移是不可逆转的。

    这说明了为什么你会得到ActiveRecord::IrreversibleMigration: ActiveRecord::IrreversibleMigration

    因此,如果您想使用change_column_default 运行迁移,您必须添加def updef down

    我建议使用 change_column,因为它已在 change_column_default 中调用。

    def up
     change_column :people, :height, :integer, default: 0
    end
    
    def down
     change_column :people, :height, :integer, default: nil
    end
    

    【讨论】:

    • 这个答案并不完美,但比其他答案好(显然,我不能将赏金奖励给自己)。
    • 是的@AndrewGrimm,关于完美答案,您是对的。我添加了edgeguides.rubyonrails.org 作为参考,尚未重新发布。仍然为了添加信息,我检查了此链接 api.rubyonrails.org/classes/ActiveRecord/Migration/… 以查看可逆迁移列表。我看到change_column 在列表中不可用。最后感谢您的赏金,也感谢您的回答。有帮助..:)
    【解决方案3】:

    如果您对迁移文件有疑问,请将您的迁移更改为此格式。

    change_column_default :table_name, :column_name, from: nil, to: "something"
    

    【讨论】:

      【解决方案4】:

      截至 2016 年 10 月,此功能(使用 to:from: 使 change_column_default 可逆)现已在 5.x 分支上可用。不幸的是,它不适用于 4.2.x 或更低版本。 :(

      验证:git tag --contains f9c841927ac3d1daea2a9cebf08b18e844e5eec5 在 rails 项目中。

      【讨论】:

        【解决方案5】:

        首先Jonathan Allardfromto 不在方法源中,这意味着change_column_default 不接受它。就是这样:

        def sum(a)
          return a
        end
        

        现在,如果您尝试将两个变量传递给它,例如 sum(a, b) 或任何它不会接受该权利的东西。这就是您在上面使用fromto 尝试做的事情。

        现在正确的语法是:

        change_column_default(:people, :height, 0)
        

        该方法不接受fromto(因为它是这样定义的,即使它们是哈希键,如果该方法在任何地方都没有使用该键值对,那么它是没有用的它)并且如果它是一个新列,显然它将具有默认值nil(如果之前未设置)并假设列height如果类型为integer并且你给它默认值a它会将 0 存储为默认值(不是 100% 确定,但已尝试从 rails 控制台执行此操作)。关注当前的默认值是什么并不重要,它只需要新的默认值。因此,如果当前默认值为0 而您将其设置为nil,rails 将不会抱怨。它是您的数据库,您希望如何处理它。如果你做错了事情,比如将string分配给boolean,数据库就会中断它,那么它显然会抛出错误。

        现在,一旦此迁移运行,它会将默认值设置为 0 现在 rails 不知道之前的默认值是什么。因为它已经消失了,它没有在任何地方存储它。这就是为什么change_column_default 是不可逆转的迁移。如果您尝试回滚它,它会在 change 方法的情况下为您提供ActiveRecord::IrreversibleMigration: ActiveRecord::IrreversibleMigration。表示您使用过的时间:

        def change
          change_column_default(:people, :height, 0)
        end
        

        这就是为什么对于这种迁移我们使用方法updown

        def up
          change_column_default(:people, :height, 0)
        end
        
        def down
          change_column_default(:people, :height, nil)
        end
        

        希望这会有所帮助。

        【讨论】:

        • from: nil, to: 0{:from => nil, :to => 0} 一样,都是一个对象。
        • 其实我认为你没有明白我的意思,change_column_default 只接受一个值来设置列的默认值,现在你写的是from: nil 所以它没有源代码中编写的代码将处理您的from: nilto: 0
        【解决方案6】:

        只是补充几点。如果您遇到不支持可逆 change_column_default 的 Rails 版本,解决问题的一种方法是:

        def change
            # If your dataset is small, just cache in memory, if large, consider file dump here:
            cache = Table.all
            # Full column def important for reversibility
            remove_column :table, :column, :type, { config_hash }
            # Re-add the column with new default:
            add_column  :table, :column, :type, { config_hash, default: 0 }
            # Now update the data with cached records (there might be more efficient ways, of course):
            cache.each do |rec|
                Table.find(rec.id).update(column: rec.column)
            end
        end
        

        【讨论】:

          【解决方案7】:

          当你尝试将数据类型从value (0) 降级到nil 在这种情况下,rails 会报这个错误。因为它可能会丢失数据

          另一个例子是string -> integer

          这真的是good article explain the same

          更新

          您的反向迁移似乎在command_recorder (activerecord-4.2.0/lib/active_record/migration/command_recorder.rb#76) 中调用了change_column_default,请检查您是否将nil 设置为默认值。

          更新2

          事实证明,如果你在迁移中使用change,它们必须能够可逆

          但是 change_column 不能通过change 方法恢复。因此,您将不得不使用带有 updown 的旧 Rails 迁移方法

          阅读本文解释scenario

          【讨论】:

          • 0nil 不是数据类型。此外,您提到了不可逆迁移的一般概念,但您没有解释为什么此更改会是不可逆迁移。
          • 我应该在哪里将nil 设置为默认值?
          • 我调用的方法是change_column_default,而不是change_column
          猜你喜欢
          • 2012-08-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-04-17
          • 1970-01-01
          • 2015-08-12
          • 1970-01-01
          相关资源
          最近更新 更多