Rails 5.2 Ошибка изменения или удаления столбца таблицы (SQLite3 :: ConstraintException: не выполнено ограничение FOREIGN KEY: DROP TABLE) - PullRequest
0 голосов
/ 02 декабря 2018

Я пытаюсь выполнить довольно простую операцию по изменению значения по умолчанию для одного из столбцов из моей таблицы 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, потому что этот пост и этот , похоже, имеет аналогичную проблему.Тем не менее, ни один пост не содержит фактического ответа.Кто-нибудь успешно избавился от этого?

Ответы [ 3 ]

0 голосов
/ 02 января 2019

ОБНОВЛЕНИЕ:

Новый столбец по умолчанию может быть добавлен через Rails без использования базы данных.В модели Blog мы можем использовать ActiveRecord :: Attributes :: ClassMethods :: attribute , чтобы переопределить значение по умолчанию для freebie_type:

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

Это изменит значение по умолчанию на уровне бизнес-логики.Следовательно, он зависит от использования ActiveRecord, который будет распознан.Для управления базой данных через SQL все равно будет использоваться старое значение по умолчанию.Чтобы обновить значение по умолчанию во всех случаях, см. Оригинальный ответ ниже.

ОРИГИНАЛЬНЫЙ ОТВЕТ:

К сожалению, ALTER COLUMN только минимально поддерживается в SQLite.Обходной путь - создать новую таблицу, скопировать в нее информацию, удалить старую таблицу и, наконец, переименовать новую таблицу.Это то, что Rails пытается сделать, но без предварительного отключения ограничений внешнего ключа.Отношения внешнего ключа с user_id, image_id и pdf_id предотвращают удаление таблицы.

Вам нужно будет выполнить обновление вручную, используя SQL (предпочтительно) или ActiveRecord::Base.connection.Вы можете увидеть процесс здесь в разделе «Изменить столбец в таблице».Вы можете найти все параметры, доступные для столбцов, в Документация SQLite Создать таблицу .

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.Они намного мощнее и их будет намного легче модифицировать и поддерживать.

0 голосов
/ 07 января 2019

Вы можете добавить инициализатор, чтобы обезьяна исправила адаптер 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

0 голосов
/ 02 декабря 2018

сначала вам нужно перенести вашу базу данных rake db: migrate после того, как наберите эту строку в консоли, g миграции Removevideo_linkFromblogs video_link: string

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...