Choosing a good name for a class or a variable isn’t easy at all and sometimes there is a requirement to rename existing tables and models to match the updated product audience.
So how we should do this migration then:
1. Ask why
The first step is to ask why should we rename the model, with a productive mindset and a good set of questions you will be surprised that in most cases a renaming is not required at all!
2. Change the model name
It seems that this case requires to rename the ActiveRecord model, so let’s use the following example:
# app/models/author.rb class Author < ApplicationRecord has_many :books end
# app/models/book.rb class Book < ApplicationRecord belongs_to :author end
# db/schema.rb ActiveRecord::Schema.define(version: 2018_06_09_111650) do create_table "authors", force: :cascade do |t| t.string "name" t.datetime "created_at", null: false t.datetime "updated_at", null: false end create_table "books", force: :cascade do |t| t.string "title" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "author_id" t.index ["author_id"], name: "index_books_on_author_id" end end
As we can see we have an Author model who has many Books and our requirement is to rename Author model to Writer.
We start by renaming the Author model file and mapping the new model to the existing authors table. Also, we need to specify the foreign key for the association to be able to access associated books.
# app/models/author.rb -> app/models/writer.rb class Writer < ApplicationRecord self.table_name = "authors" has_many :books, foreign_key: 'author_id' end
Let’s update the Book model by specifying the existent foreign key. This step is important to maintain the belongs_to association.
class Book < ApplicationRecord belongs_to :writer, foreign_key: 'author_id' end
I guess that you are asking why should we bother with this step instead of renaming the models and tables at the same time.
To answer this good question, we should consider how applications look like in production. Usually, models may have different associations and used in different parts of the application like controllers and services. That’s why we do this process step by step to be sure that our application is still working as expected before changing the tables and columns in the database.
3. Run the test suite
At this point, we must run our test suite and be sure all specs are passing before moving to the last step. This step is very important to be sure that we didn’t break an existent feature during the migration.
4. Rename DB tables and columns
Now we are ready for the last step, so let’s write some migrations. We start by renaming the authors’ table to writers.
class RenameAuthorsToWriters < ActiveRecord::Migration[5.2] def change rename_table :authors, :writers end end
Then we need to rename the foreign key from author_id to writer_id.
class RenameFkInBooks < ActiveRecord::Migration[5.2] def change rename_column :books, :author_id, :writer_id end end
And finally, we clean up our models.
# app/models/writer.rb class Writer < ApplicationRecord has_many :books end
# app/models/book.rb class Book < ApplicationRecord belongs_to :writer end
Here is the updated schema, we can check that renaming the column took care of renaming the index. But we should keep in mind this only valid for Rails version >= 4.0 as stated in this changelog:
In Rails 4.0 when a column or a table is renamed the related indexes are also renamed. If you have migrations which rename the indexes, they are no longer needed.
ActiveRecord::Schema.define(version: 2018_06_09_115643) do create_table "books", force: :cascade do |t| t.string "title" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "writer_id" t.index ["writer_id"], name: "index_books_on_writer_id" end create_table "writers", force: :cascade do |t| t.string "name" t.datetime "created_at", null: false t.datetime "updated_at", null: false end end
That’s it, have fun and keep coding 🙂