【问题标题】:How to completely remove a model and its associations in Rails?如何在 Rails 中完全删除模型及其关联?
【发布时间】:2020-12-04 10:24:51
【问题描述】:

说明

我正在使用 Ruby 2.6.6 版Ruby on Rails 6.0.3.2 版

型号

  1. 书籍
  2. 作者

关联

一本书属于作者。
一位作者拥有许多书籍。

目标

删除 Author 模型并将其作为列添加到 Book(字符串类型)。

我做了什么

我按以下顺序创建并运行了 3 个迁移:

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

架构

不幸的是,我在运行迁移之前没有架构。 (我尝试检查一个较旧的提交,但架构文件顽固地拒绝更改。)

这是当前版本:

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

问题

author 表已被删除,add_foreign_key "books", "authors"(我认为是这样的)也已消失,但 author_id 顽固地保留在 books 表中。

此外,还有一个整数 author_id 和一个同名的索引。

我的计划

我想通过另一个迁移删除这 2 列,但我不知道这是否会......

  1. ...修复问题并彻底清除旧的 Author 模型,
  2. ...这是干净/推荐的做事方式。如果需要,我可以回滚迁移并尝试更好的方法。

【问题讨论】:

    标签: ruby-on-rails ruby database database-migration rails-migrations


    【解决方案1】:

    关于不寻常的`schema.rb` 行为

    我尝试检查一个较旧的提交,但架构文件顽固地拒绝更改。

    这很不寻常。请确认您正在使用 git 跟踪 db/schema.rb 文件。如果它被跟踪,则没有理由签出较旧的提交不应将其返回到较旧的状态。此时,您应该能够:

    $ rails db:drop
    $ rails db:create
    $ rails db:schema:load
    

    ...将旧模式加载到数据库中。然后,您应该能够使用 git 返回到最新代码,并在创建旧模式的日期之后运行挂起的迁移。

    关于一种更简洁的实现方式

    在编写以下迁移之前,第一步是删除 Book 类中编写的任何现有关系。例如:

    # 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
    

    我已经开始在一个文件中编写相关的迁移,因为它们都是相关的。对我来说,这看起来像:

    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
    

    【讨论】:

    • 相比def change,使用up/down方法有什么优势?
    • @verified_tinker 首先,它实际上可以让您为 above 编写一个真正的可逆迁移。 change 函数将无法处理诸如将数据从一个表传输到另一个表之类的事情。基本上只要up 进程与down 不同。
    • 对不起,我刚刚实现了代码,它仍然是一样的:外键和 author_id(s) 仍然存在。我确实删除了 belongs_to 关联。这可能是因为我之前删除了 Author 模型吗?
    • 不。我重置为模型存在的提交,也删除了它的has_many,并运行了迁移,但仍然没有骰子。
    • @verified_tinker 糟糕,我完全忘记了author_id 是在books 中,而不是book_idauthors 中。在删除 authors 表之前,我添加了一个缺失的步骤(以及一种可能的替代方式)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-01
    • 1970-01-01
    • 2011-03-04
    • 1970-01-01
    相关资源
    最近更新 更多