【问题标题】:Rails creating migration to add columns to table causes error when running rake db:migrate运行 rake db:migrate 时,Rails 创建迁移以向表中添加列会导致错误
【发布时间】:2012-10-31 22:37:16
【问题描述】:

我创建了一个名为“users”的模型,并创建了一个新迁移以向 users 表添加一些列。现在,当我运行 rake db:migrate 时,我在 b/c 下面得到错误,它正在尝试再次创建用户表

$ rake db:migrate
==  DeviseCreateUsers: migrating ==============================================
-- create_table(:users)
rake aborted!
An error has occurred, all later migrations canceled:

Mysql::Error: Table 'users' already exists: CREATE TABLE `users`.....

为什么要再次创建表?

这是我用来创建新迁移的命令

$ rails generate migration AddDetailsToUsers home_phone:decimal cell_phone:decimal work_phone:decimal birthday:date home_address:text work_address:text position:string company:string

新的迁移如下所示:

class AddDetailsToUsers < ActiveRecord::Migration
  def change
    add_column :users, :home_phone, :decimal
    add_column :users, :cell_phone, :decimal
    add_column :users, :work_phone, :decimal
    add_column :users, :birthday, :date
    add_column :users, :home_address, :text
    add_column :users, :work_address, :text
    add_column :users, :position, :string
    add_column :users, :company, :string
  end
end

编辑

20120511224920_devise_create_users

class DeviseCreateUsers < ActiveRecord::Migration
  def change
    create_table(:users) do |t|
      ## Database authenticatable
      t.string :email,              :null => false, :default => ""
      t.string :username,           :null => false, :default => ""
      t.string :encrypted_password, :null => false, :default => ""

      ## Recoverable
      t.string   :reset_password_token
      t.datetime :reset_password_sent_at

      ## Rememberable
      t.datetime :remember_created_at

      ## Trackable
      t.integer  :sign_in_count, :default => 0
      t.datetime :current_sign_in_at
      t.datetime :last_sign_in_at
      t.string   :current_sign_in_ip
      t.string   :last_sign_in_ip

      ## Encryptable
      # t.string :password_salt

      ## Confirmable
      # t.string   :confirmation_token
      # t.datetime :confirmed_at
      # t.datetime :confirmation_sent_at
      # t.string   :unconfirmed_email # Only if using reconfirmable

      ## Lockable
      # t.integer  :failed_attempts, :default => 0 # Only if lock strategy is :failed_attempts
      # t.string   :unlock_token # Only if unlock strategy is :email or :both
      # t.datetime :locked_at

      ## Token authenticatable
      # t.string :authentication_token


      t.timestamps
    end

    add_index :users, :email,                :unique => true
    add_index :users, :reset_password_token, :unique => true
    # add_index :users, :confirmation_token,   :unique => true
    # add_index :users, :unlock_token,         :unique => true
    # add_index :users, :authentication_token, :unique => true
  end
end

20120619023856_add_name_to_users

class AddNameToUsers < ActiveRecord::Migration
  def change
    add_column :users, :first_name, :string
    add_column :users, :last_name, :string
  end
end

20121031174720_add_details_to_users.rb

class AddDetailsToUsers < ActiveRecord::Migration
  def change
    add_column :users, :home_phone, :decimal
    add_column :users, :cell_phone, :decimal
    add_column :users, :work_phone, :decimal
    add_column :users, :birthday, :date
    add_column :users, :home_address, :text
    add_column :users, :work_address, :text
    add_column :users, :position, :string
    add_column :users, :company, :string
  end
end

【问题讨论】:

  • 从输出看来,错误发生在 DeviseCreateUsers 期间,而不是在您的新迁移中。您是否有另一个(旧)迁移已经创建了 Users 表?
  • 是的,我愿意。但我认为我应该能够像我一样添加一个新的迁移,并且运行 rake db:migrate 应该只运行当前尚未运行的迁移。
  • 新的迁移甚至没有运行;似乎您还有另外两个都尝试创建用户表(DeviseCreateUsers 和较旧的迁移)。您可能应该将DeviseCreateUsers 更改为使用add_columns 而不是尝试创建表。
  • 我有 3 个迁移,只有一个有创建用户声明,另外 2 个包含添加列。我已经用我的 3 个迁移文件编辑了这个问题。
  • 好的.. 您发布的迁移运行得非常好。 rake db:drop &amp;&amp; rake db:migrate 有效吗?如果您需要数据,请先备份您的数据库..

标签: ruby-on-rails ruby ruby-on-rails-3 migration


【解决方案1】:

Rails 在数据库的“schema_migrations”表中跟踪迁移。除非有“20120511224920”条目,即设计迁移,否则它将尝试再次运行它,它似乎已经存在。

如果是这种情况,您可以手动将其添加到表中。

【讨论】:

  • 我不知道这一点。我运行了一个 rake db:reset,所以现在唯一的迁移是一个新的迁移。如果我知道 schema_migrations 表,我至少可以检查一下。
  • 虽然我不能确定这是否是问题所在,但我猜测 schema_migrations 表中缺少一条记录。
【解决方案2】:

错误是说它正在尝试再次运行原始的 DeviseCreateUsers 迁移并且不能因为用户表已经存在。

要解决此问题,您可以为 DeviseCreateUsers 运行向下迁移,然后正常运行迁移。你可以这样做:

rake db:migrate:down VERSION=20121031XXXXXXXX
rake db:migrate

其中20121031XXXXXXXX 是迁移名称的日期戳。换句话说,您将拥有一个名为 20120410214815_devise_create_users.rb 的迁移,然后从文件名中复制日期戳并将其粘贴到命令中。 Here's the Rails Guide on Migrations for reference.

编辑: 这在 cmets 中有说明,但只是一个警告。运行表的向下迁移将丢失该表的所有条目。我假设您正在开发模式下运行,所以这应该不是问题。如果您在生产中,则需要采取额外的步骤来备份表数据并在之后重新加载,否则您将度过糟糕的一天(或一周)。

【讨论】:

  • 如果我运行向下迁移,那将删除我的表和其中的所有数据是否正确?根据您发布的指南,我应该能够添加一个新的迁移并运行 rake db:migrate 应该能够判断是否已经运行了任何迁移并且这次不运行这些迁移。
  • 你是对的。我假设由于您在开发模式下运行,因此重新创建 User 表是可以接受的。 Rails 应该能够判断您已经运行了哪些迁移。我刚刚阅读了另一个答案的cmets。您是否有两个运行 create_table :users 的迁移?
【解决方案3】:

您能否尝试创建一个新数据库然后再次迁移它:

rake db:drop:all
rake db:create:all
rake db:migrate

【讨论】:

  • 我做了一个 rake db:reset,然后运行 ​​rake:db:migrate,但这仍然不能解释为什么我不能创建一个新的迁移并运行它。 Rails 通常可以很好地处理这个问题。
【解决方案4】:

因此,根据我从中收集到的信息:

  • 您已经有一个用户模型
  • 您有一个正在生产中的版本
  • 您运行了默认的 rails generate devise:install
  • 然后你运行 rails generate devise User

我希望:

  • 您使用源代码管理
  • 你检查代码很多

注意:如果没有,您将了解为什么需要这样做。

将您的代码恢复到生成设计之前的状态

希望您可以在生成设计之前创建一个点的新沙盒。如果没有,请复制您的项目目录并手动完成。唯一的其他选项是手动编辑 Devise 生成的所有文件。

重新运行您的设计生成

  • 将 gem 'devise' 读取到您的 Gemfile 中
  • rails 生成设计:安装
  • rails 生成设计模型

确保该模型不存在!如果你不这样做,你就会陷入你目前遇到的问题。

将当前用户从一种模型迁移到另一种模型

如果您可以生成一个脚本以将身份验证信息从旧用户模型完全移动到新用户模型,那么对您有好处。如果您使用与 Devise 不同的哈希算法进行当前身份验证,那么您将使其所有密码无效并要求您的用户使用其电子邮件中的确认码创建新密码,或者您可以在用户登录时迁移他们第一种方法干净、完整、粗鲁。第二种方法是丑陋的、不完整的、沉默的。选择你喜欢的方法。

编辑:您可能会找到一种方法来自定义设计以使用您的算法。那可能会更好,但需要更多的工作并且相当脆弱。

另一件事是,您的身份验证模型不应因帐户数据而过载。您应该有一个只处理身份验证的模型,该模型具有一个帐户数据模型,该模型存储您可能想要跟踪的有关帐户的任何内容。

【讨论】:

  • 幸运的是我现在不在生产中,所以我最终不得不删除我的数据库数据,但我只是想知道是什么导致 create_table 脚本再次尝试运行而不是正确跳过它并使用 add_columns 运行新迁移
  • 哦,那么您不需要迁移用户,但我仍然会将身份验证数据与帐户数据分开。至于正确性,Rails 假设您希望在迁移中做所有事情。要将用户用作您的身份验证模型,您需要在运行“rails generate devise model”命令之前从数据库中删除旧的。
【解决方案5】:

使用 up 和 down 方法。这对于回滚和运行特定的迁移文件很有用。

请按照语法..

  class AddDetailsToUsers < ActiveRecord::Migration
    def self.up
      add_column :users, :home_phone, :decimal
      add_column :users, :cell_phone, :decimal
      add_column :users, :work_phone, :decimal
      add_column :users, :birthday, :date
      add_column :users, :home_address, :text
      add_column :users, :work_address, :text
      add_column :users, :position, :string
      add_column :users, :company, :string
   end

   def self.down
      remove_column :users, :home_phone
      remove_column :users, :cell_phone
      remove_column :users, :work_phone
      remove_column :users, :birthday
      remove_column :users, :home_address
      remove_column :users, :work_address
      remove_column :users, :position
      remove_column :users, :company
   end
  end


    In this case please try to migrate using version number.

like rake db:migrate:down VERSION=version number #version number 就是你要迁移的版本。

【讨论】:

  • 为什么当我使用类似 rails generate migration AddDetailsToUsers....987654322@ 创建迁移时没有创建 up 和 down 方法
  • 嗨。 up 和 down 方法将在 rails 2.2.2 中生成。
  • Rails 3.1 通过提供新的更改方法使迁移更智能。此方法适用于编写建设性迁移(添加列或表)。迁移知道如何迁移你的数据库,并在迁移回滚时对其进行反转,而无需编写单独的 down 方法。所以看起来你不必担心 def self.down 因为 Rails 现在足够聪明,知道如何回滚它。
【解决方案6】:

我猜你在某个时候运行了rails generate devise user,它生成了DeviseCreateUsers。如果您已经创建了 User 模型和 users 表,您可以从 db/migrate 中删除生成的迁移文件。

【讨论】:

  • 但是如果我删除生成的迁移,当我转移到生产环境并在新数据库上运行迁移时,我会丢失一些迁移。
  • 我想你已经有另一个迁移来创建用户表了。
  • 我确实有另一个迁移来创建用户表。
  • 那么我认为您可以安全地删除 DeviseCreateUsers 的迁移。您可以使用新数据库在本地对其进行测试。
【解决方案7】:

检查一些可能为您的迁移版本提供意外值的环境变量。我在 Stack Overflow 上找到了 an old question(如果它已经过时,请原谅我),其中 db:migrate 正在破坏表,而不是应用现有的新迁移。

他们最终发现环境变量导致 db:migrate 以“0”的版本参数运行,这在功能上等同于 rake db:migrate:down

您的情况是否可能是由于版本被意外更改为包含或匹配之前的迁移DeviseCreateUsers而导致的?

【讨论】:

  • 我不这么认为,因为所有迁移都有不同的时间戳
【解决方案8】:

试试吧

在第一个文件中

create_table(:users), :force => true do |t|

这将覆盖任何其他表

【讨论】:

  • 但这会覆盖我的表吗?从所有其他 cmets 可以看出,我正在尝试将列添加到数据库,而不必删除数据。 Rails 能够处理这个问题。
【解决方案9】:

根据你说的你使用这个命令创建了一个新的迁移

$ rails 生成迁移 AddDetailsToUsers home_phone:decimal cell_phone:decimal work_phone:decimalbirthday:date home_address:text work_address:text position:string company:string

我不确定它是否只是一个错字,但它应该是“AddDetailsToUser”而不是“Users”。请再次检查,我们将能够为您提供帮助。这是用于设计生成的模型。当您提到用户时,它会在 db 中查找用户。

Ruby on Rails 遵循语言约定。table_name 是复数,而 model_name 是单数。您必须在您使用的命令中使用 model_name。

如果你想使用 table_name 然后使用这个

rails g migration add_details_to_users home_phone:decimal......等

【讨论】:

    【解决方案10】:

    如果您需要手动进行一些脏迁移:

    class A < ActiveRecord::Migration
      def up
        add_column :images, :name
      end
    end
    
    A.new.migrate(:up)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-02-05
      • 1970-01-01
      • 1970-01-01
      • 2021-10-05
      • 2012-11-23
      • 1970-01-01
      • 2013-11-28
      相关资源
      最近更新 更多