In Rails, when we have a table
and we want to add or remove columns from that table,
then we write migrations using methods like
add_column
for adding column
and remove_column
for removing a column.
Typical add_column
and remove_column
migrations look like these:
class AddDescriptionToProduct < ActiveRecord::Migration[5.1]
def change
add_column :products, :description, :text
end
end
class RemoveDescriptionFromProduct < ActiveRecord::Migration[5.1]
def change
remove_column :products, :description
end
end
If the column is already present and we try to run add_column
migration
then we get an error like this:
DuplicateColumn: ERROR: column "description" of relation "products" already exists
Similarly, if we try to remove a non-existent column using remove_column
migration,
we get an error like this:
UndefinedColumn: ERROR: column "description" of relation "products" does not exist
Before Rails 6.1
To handle these errors, we can rewrite above migrations like these:
class AddDescriptionToProduct < ActiveRecord::Migration[5.1]
def change
unless ActiveRecord::Base.connection.column_exists?(:products, :description)
add_column :products, :description, :text
end
end
end
class RemoveDescriptionFromProduct < ActiveRecord::Migration[5.1]
def change
if ActiveRecord::Base.connection.column_exists?(:products, :description)
remove_column :products, :description
end
end
end
To simplify these conditions, now Rails has added
support for if_exists/if_not_exists
on remove_column/add_column
in migrations.
With Rails 6.1
Adding a column
class AddDescriptionToProduct < ActiveRecord::Migration[6.1]
def change
add_column :products, :description, :text, if_not_exists: true
end
end
If the column doesn’t exist then the above migration will add the column otherwise it won’t raise an error.
-- add_column(:products, :description, :text, {:if_not_exists=>true})
-> 0.0017s
Removing a column
class RemoveDescriptionFromProduct < ActiveRecord::Migration[6.1]
def change
remove_column :products, :description, if_exists: true
end
end
Similarly, the above migration will remove the column if it exists, else it’ll run the migration without error.
-- remove_column(:products, :description, {:if_exists=>true})
-> 0.0153s