Reputation: 665
I'm on Ruby version 2.6.6 and Ruby on Rails version 6.0.3.2.
A Book belongs to an Author.
An Author has many Books.
Remove the Author model and add it as a column to a Book (of type string).
I created and ran 3 migrations, in the following order:
AddAuthorToBooks
class AddAuthorToBooks < ActiveRecord::Migration[6.0]
def change
add_column :books, :author, :string
end
end
DropAuthors
class DropAuthors < ActiveRecord::Migration[6.0]
def change
drop_table :authors do |t|
t.string "full_name", null: false
t.timestamps null: false
end
end
end
RemoveAuthorForeignKeyFromBooks
class RemoveAuthorForeignKeyFromBooks < ActiveRecord::Migration[6.0]
def change
remove_foreign_key :books, :authors
end
end
Unfortunately, I don't have the schema before I ran the migrations. (I tried checking out an older commit, but the schema file stubbornly refuses to change.)
Here is the current version:
ActiveRecord::Schema.define(version: 2020_08_11_125724) do
create_table "books", force: :cascade do |t|
t.string "title"
t.text "description"
t.string "cover_url"
t.decimal "price"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.integer "author_id", null: false
t.string "author"
t.index ["author_id"], name: "index_books_on_author_id"
end
create_table "books_genres", id: false, force: :cascade do |t|
t.integer "book_id", null: false
t.integer "genre_id", null: false
t.index ["book_id", "genre_id"], name: "index_books_genres_on_book_id_and_genre_id"
t.index ["genre_id", "book_id"], name: "index_books_genres_on_genre_id_and_book_id"
end
create_table "genres", force: :cascade do |t|
t.string "name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "reviews", force: :cascade do |t|
t.string "username"
t.decimal "rating"
t.text "body"
t.integer "book_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["book_id"], name: "index_reviews_on_book_id"
end
add_foreign_key "reviews", "books"
end
The author table has been removed, the add_foreign_key "books", "authors"
(I think that's how it went) is gone, too, but the author_id
stubbornly remains in the books
table.
Furthermore, there's an integer author_id
and an index of the same name.
I thought of just deleting these 2 columns with another migration, but I don't know if that would...
Upvotes: 2
Views: 956
Reputation: 718
`schema.rb`
behaviorI tried checking out an older commit, but the schema file stubbornly refuses to change.
This is quite unusual. Do verify that you're tracking the db/schema.rb
file using git. If it is tracked, there's no reason why checking out an older commit shouldn't return it to the older state. At that point, you should be able to:
$ rails db:drop
$ rails db:create
$ rails db:schema:load
...to load the old schema into the database. Then, you should be able to return to the latest code with git, and run pending migrations after the date at which the older schema was created.
Before writing the below migration, the first step would be to remove any existing relationship written in the Book
class. For example:
# app/models/book.rb
class Book < ApplicationRecord
# The line below should be deleted! Otherwise, it will probably interfere
# with the `book.update!(author: ...)` line in the migration.
belongs_to :author
end
I've taken to writing related migrations in a single file, since they're all related. To me, this looks like:
class MoveAuthorToBooks < ActiveRecord::Migration[6.0]
class Author < ApplicationRecord
end
class Book < ApplicationRecord
end
def up
# Start by adding a string column.
add_column :books, :author, :string
# Let's preserve existing author names.
Book.all.each do |book|
author = Author.find(book.author_id)
book.update!(author: author.name)
end
# Now that the names have been moved to the books table, we don't
# need the relationship to `authors` table anymore. This should
# also delete any related foreign keys - manual foreign key deletion
# should not be required.
remove_column :books, :author_id
# Alternative: If you'd created the `authors_id` column using the
# `add_reference` command, then it's probably best to use the opposite
# `remove_reference` command.
#
#remove_reference :books, :author, index: true, foreign_key: true
# Finally, remove the `authors` table.
drop_table :authors
end
def down
# This can be technically be reversed, but that'll need some more code that
# reverses the action of the `up` function, and it may not be needed.
raise ActiveRecord::IrreversibleMigration
end
end
Upvotes: 2