chris P
chris P

Reputation: 6589

How do I database level contraints to my rails application long after I've migrated everything?

1) I've created an entire Rails JSON API as my first project. It's fairly sizeable, and I put off database level constraints at the time. It's time to do it now.

I have 30+ or so migrations. For example

    class CreateItems < ActiveRecord::Migration
      def change
        create_table :items do |t|
          t.string :name
          t.text :description

          t.timestamps
        end
      end
    end

When I go in and edit

t.string :name

to be

t.string :name, null: false

and run a bundle exec db:reset db:migrate it doesn't work. What do I need to do to add db level constraints to my migrations files, and re migrate them?

2) Also, my 30+ migrations all have a "def change" and that's it. I believe somewhere a long time ago I saw something like def up, def down or something like that. It looks like it was used for rollbacks. Do I need to have "def down" for all of my migrations? Is that how I solve #1? Put "def down" in all of my 30 migrations, rollback the entire thing, then put in my db level constraints, and migrate the whole thing again?

Upvotes: 0

Views: 70

Answers (1)

Ege Ersoz
Ege Ersoz

Reputation: 6571

You have two options: you can write new migrations to make changes to the existing columns. In console:

rails g migration add_null_to_name

In the migration file:

class ChangeNumbericFieldInMyTable < ActiveRecord::Migration
  def self.up
    change_column :my_table, :name, :string, :null => false
  end

  def self.down
    change_column :my_table, :name, :string
end

Rinse and repeat for all the changes you need to make. Note that you can define them in the same migration file, but then if you had to rollback in the future it would roll back all of them.

Your second option is to roll everything back (or just start over), fix your migration methods and remigrate. This might be a cleaner approach, but that's not necessarily a good thing: it would also delete the existing data, if you have any.

I know I said you have two options, but you actually also have a third option, which is to to rely on model validations instead. These are more appropriate in most situations and can make testing much easier. Database validations are more reserved for complex scenarios where the database is used by other applications in addition to yours, and a few other edge cases.

As for your second question, every migration is a two-way street. When you migrate something, you should also be able to roll it back. In a lot of cases Rails can guess the reverse operation. For example, if you add_column in a migration, the opposite of that is obviously remove_column so you don't need to define it explicitly. But certain operations, such as change_column in the example above, don't have an obvious reverse. Rails can understand that something changed, but it can't tell what it changed from. So in those situations you need to explicitly define the down operation. In the case above, it's changing the column back to a state where it doesn't have a validation.

Upvotes: 1

Related Questions