Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails creating migration to add columns to table causes error when running rake db:migrate

I have a model created called "users" and i created a new migration to add some columns to the users table. Now when i run rake db:migrate, I get the error below b/c it's trying to create the users table again

$ 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`.....

Why is it trying to create the table again?

Here's the command i used to create the new migration

$ 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

The new migration looks like this:

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

EDIT

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
like image 501
Catfish Avatar asked Oct 31 '12 22:10

Catfish


People also ask

How does rails know which migration to run?

Internally Rails only uses the migration's number (the timestamp) to identify them. Prior to Rails 2.1 the migration number started at 1 and was incremented each time a migration was generated. With multiple developers it was easy for these to clash requiring you to rollback migrations and renumber them.

What does rails generate migration do?

A Rails migration is a tool for changing an application's database schema. Instead of managing SQL scripts, you define database changes in a domain-specific language (DSL). The code is database-independent, so you can easily move your app to a new platform.

Do rails migrations run in transaction?

On databases that support transactions with statements that change the schema, migrations are wrapped in a transaction. If the database does not support this then when a migration fails the parts of it that succeeded will not be rolled back. You will have to rollback the changes that were made by hand.

What is difference between model and migration in rails?

Rails Model (Active Record) works with SQL, and Rails Migration works with DDL. Rails Model supports ways to interact to with the database, while Rails Migration changes the database structure. A migration can change the name of a column in books table.


4 Answers

Rails keeps track of the migrations in the "schema_migrations" table of your database. Unless there is an entry for "20120511224920", which is the Devise migration, it will attempt to run it again, which it appears to already exists.

You can add that manually to the table if that is the case.

like image 192
omarvelous Avatar answered Oct 30 '22 12:10

omarvelous


The error is saying that it's trying to run the original DeviseCreateUsers migration again and can't because the users table already exists.

To fix this, you can run the down migration for DeviseCreateUsers and then run migrations as normal. You can do that with:

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

Where 20121031XXXXXXXX is the date stamp of the migration name. In other words, you'll have a migration named 20120410214815_devise_create_users.rb and you copy the date stamp from the filename and paste it into the command. Here's the Rails Guide on Migrations for reference.

Edit: This is noted in the comments, but just a word of warning. Running the down migration for a table will lose any entries that table has. I assume you're running in development mode, so this shouldn't be a problem. If you're in production, you will need to take extra steps to backup the table data and reload it afterwards, otherwise you're going to have a bad day (or week maybe).

like image 30
GorrillaMcD Avatar answered Oct 30 '22 14:10

GorrillaMcD


Can you try make a fresh database and then migrate it again:

rake db:drop:all
rake db:create:all
rake db:migrate
like image 5
Thanh Avatar answered Oct 30 '22 13:10

Thanh


So from what I have gathered from this:

  • You already had a User model
  • You have a version of this in production
  • You ran a default rails generate devise:install
  • You then ran rails generate devise User

I am hoping that:

  • You use source control
  • You check code in a lot

NOTE: If not, you are about to learn why you need to do so.

Revert your code to before you generated Devise

Hopefully, you can just create a new sandbox of a point right before generating Devise. If not, copy your project directory and do it by hand. The only other option is manually edit all the files that Devise generated.

Rerun your Devise generation

  • readd gem 'devise' to your Gemfile
  • rails generate devise:install
  • rails generate devise MODEL

Make sure that model does not exist! If you don't you get into the problem you are currently having.

Migrate current users from one model to the other

If you can generate a script to completely move authentication information from your old user model to the new, good for you. If you are using a different hashing algorithm from Devise for your current authentication, then you are going to either invalidate all of their passwords and require your users to create a new password using a confirmation code in their email OR you could migrate users as they log in. The first method is clean, complete, and rude. The second method is ugly, incomplete, and silent. Choose your method however you like.

Edit: You could probably find a way to customize Devise to use your algorithm instead. That would probably be even better, but a little more work and fairly brittle.

Another thing is that your authentication model should not be overloaded with account data. You should have a model that only handles authentication which has_a account data model that stores whatever you might want to track about accounts.

like image 2
Michael McGuire Avatar answered Oct 30 '22 12:10

Michael McGuire