我正在尝试完成一个相当简单的壮举,即更改我的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 post和this one似乎有类似的问题。但是,两个帖子均未涉及实际答案。有人成功摆脱了这个吗?
答案 0 :(得分:4)
更新:
可以通过Rails添加新的默认列,而不必使用数据库。在Blog
模型中,我们可以使用ActiveRecord::Attributes::ClassMethods::attribute重新定义freebie_type
的默认值:
attribute :freebie_type, :string, default: 'None'
这将在业务逻辑级别更改默认值。因此,它取决于使用ActiveRecord进行识别。通过SQL操作数据库仍将使用旧的默认值。要在所有情况下更新默认设置,请参见下面的原始答案。
原始答案:
不幸的是,SQLite仅ALTER COLUMN
是minimally supported。解决该问题的方法是创建一个新表,将信息复制到其中,删除旧表,最后重命名新表。这是Rails试图做的,但是没有首先禁用外键约束。与user_id
,image_id
和pdf_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数据库。它们功能更强大,并且修改和维护起来也更加容易。
答案 1 :(得分:2)
您可以添加一个初始化程序来对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
答案 2 :(得分:-3)
首先,您需要在控制台中键入以下内容后迁移数据库rake db:migrate。g migration Removevideo_linkFromblogs video_link:string