【问题标题】:Rails 5.2 Error Changing or Removing Table Column (SQLite3::ConstraintException: FOREIGN KEY constraint failed: DROP TABLE)Rails 5.2 更改或删除表列时出错(SQLite3::ConstraintException:FOREIGN KEY 约束失败:DROP TABLE)
【发布时间】:2019-05-04 01:54:32
【问题描述】:

我正在尝试完成一个相当简单的壮举,即更改我的Blog 表中的一列的默认值。我有以下迁移:

class UpdateBlogFields < ActiveRecord::Migration[5.2]
  def change
    change_column :blogs, :freebie_type, :string, default: "None"
  end
end

相当简单,但是当我运行 rake db:migrate 时出现以下错误:

StandardError: An error has occurred, this and all later migrations canceled:
SQLite3::ConstraintException: FOREIGN KEY constraint failed: DROP TABLE "blogs"

每当我尝试更改或删除一列时都会收到此错误,但添加一列时不会。

我的架构如下所示:

  create_table "blogs", force: :cascade do |t|
    t.string "title"
    t.string "teaser"
    t.text "body"
    t.string "category", default: "General"
    t.string "linked_module"
    t.boolean "published", default: false
    t.datetime "published_on"
    t.integer "user_id"
    t.integer "image_id"
    t.integer "pdf_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "slug"
    t.string "cta_read_more", default: "Read More"
    t.string "cta_pdf", default: "Get My Free PDF"
    t.string "cta_video", default: "Watch the Video"
    t.string "convertkit_data_form_toggle"
    t.string "convertkit_href"
    t.integer "pin_image_id"
    t.string "data_pin_description"
    t.string "freebie_filename"
    t.string "video_link"
    t.string "freebie_type", default: "File"
    t.string "freebie_description"
    t.integer "comments_count"
    t.integer "subcategory_id"
    t.boolean "affiliate_links", default: true
    t.boolean "approved", default: false
    t.boolean "submitted", default: false
    t.index ["image_id"], name: "index_blogs_on_image_id"
    t.index ["pdf_id"], name: "index_blogs_on_pdf_id"
    t.index ["pin_image_id"], name: "index_blogs_on_pin_image_id"
    t.index ["slug"], name: "index_blogs_on_slug", unique: true
    t.index ["subcategory_id"], name: "index_blogs_on_subcategory_id"
    t.index ["user_id"], name: "index_blogs_on_user_id"
  end

这似乎是 SQLite 的事情,因为 this postthis one 似乎有类似的问题。但是,这两个帖子都没有涉及实际答案。有没有人成功摆脱这个?

【问题讨论】:

  • 你能显示表blog的架构吗?
  • @devanand 我添加了更多细节和我的架构。你对此有什么理论吗?
  • 我知道这与这里无关,但你为什么使用 SQLite ?您真的应该考虑将 PostgreSQL 或 MySQL 用于生产环境 - 甚至是开发环境。
  • @sloneorzeszki SQLite 用于测试和开发环境;在生产环境中使用gigantic 数据库服务器始终是最佳选择——它们也是开源的,不需要许可证。
  • @sloneorzeszki 这个问题的所有问题都直接因为他们使用的是SQLite。我认为有一些非常正当的理由可以避免它。

标签: sql ruby-on-rails sqlite


【解决方案1】:

首先你需要迁移你的数据库 rake db:migrate 在你的控制台输入这行之后 rails g migration Removevideo_linkFromblogs video_link:string

【讨论】:

  • 我在尝试rake db:migrate 时收到此错误,因此运行它不会解决问题。
【解决方案2】:

更新:

可以通过 Rails 添加新的默认列,而无需使用数据库。在Blog模型中,我们可以使用ActiveRecord::Attributes::ClassMethods::attribute重新定义freebie_type的默认值:

attribute :freebie_type, :string, default: 'None'

这将更改业务逻辑级别的默认设置。因此,它依赖于使用 ActiveRecord 来识别。通过 SQL 操作数据库仍将使用旧的默认值。要在所有情况下更新默认值,请参阅下面的原始答案。

原始答案:

不幸的是,ALTER COLUMN 只是 SQLite 的minimally supported。围绕它的工作是创建一个新表,将信息复制到它,删除旧表,最后重命名新表。这是 Rails 试图做的,但没有首先禁用外键约束。与user_idimage_idpdf_id 的外键关系阻止删除表。

您需要使用 SQL(首选)或 ActiveRecord::Base.connection 手动进行更新。您可以在“修改表中的列”下看到process here。您可以在SQLite Create Table Documentation 中找到所有列可用的选项。

PRAGMA foreign_keys=off;

BEGIN TRANSACTION;

ALTER TABLE table1 RENAME TO _table1_old;

CREATE TABLE table1 (
( column1 datatype [ NULL | NOT NULL ] DEFAULT (<MY_VALUE>),
  column2 datatype [ NULL | NOT NULL ] DEFAULT (<MY_VALUE>),
  ...
);

INSERT INTO table1 (column1, column2, ... column_n)
  SELECT column1, column2, ... column_n
  FROM _table1_old;

COMMIT;

PRAGMA foreign_keys=on;

请确保您已按照所需方式设置了所有列,因为在创建表后您将无法修复它!展望未来,我强烈建议设置 PostgreSQL 或 MySQL2 数据库。它们更强大,更容易修改和维护。

【讨论】:

  • 只是为了赎回我自己,This Post 似乎提供了一种可逆机制,基本上你的答案归结为外键修复,同时仍然利用 Rails 迁移方法来实现回滚兼容性
【解决方案3】:

您可以添加一个初始化程序来猴子修补 sqlite 适配器以使其与 rails 5 一起工作,只需确保您的 sqlite >= 3.8,使用以下代码:

blog/config/initializers/sqlite3_disable_referential_to_rails_5.rb

内容:

require 'active_record/connection_adapters/sqlite3_adapter'

module ActiveRecord
  module ConnectionAdapters
    class SQLite3Adapter < AbstractAdapter

      # REFERENTIAL INTEGRITY ====================================

      def disable_referential_integrity # :nodoc:
        old_foreign_keys = query_value("PRAGMA foreign_keys")
        old_defer_foreign_keys = query_value("PRAGMA defer_foreign_keys")

        begin
          execute("PRAGMA defer_foreign_keys = ON")
          execute("PRAGMA foreign_keys = OFF")
          yield
        ensure
          execute("PRAGMA defer_foreign_keys = #{old_defer_foreign_keys}")
          execute("PRAGMA foreign_keys = #{old_foreign_keys}")
        end
      end

      def insert_fixtures_set(fixture_set, tables_to_delete = [])
        disable_referential_integrity do
          transaction(requires_new: true) do
            tables_to_delete.each {|table| delete "DELETE FROM #{quote_table_name(table)}", "Fixture Delete"}

            fixture_set.each do |table_name, rows|
              rows.each {|row| insert_fixture(row, table_name)}
            end
          end
        end
      end

      private

      def alter_table(table_name, options = {})
        altered_table_name = "a#{table_name}"
        caller = lambda {|definition| yield definition if block_given?}

        transaction do
          disable_referential_integrity do
            move_table(table_name, altered_table_name,
                       options.merge(temporary: true))
            move_table(altered_table_name, table_name, &caller)
          end
        end
      end
    end
  end
end

这里是要点:https://gist.github.com/dante087/3cfa71452229f8125865a3247fa03d51

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-06-10
    • 2018-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-26
    • 1970-01-01
    • 2021-05-30
    相关资源
    最近更新 更多