未定义的方法 `execute_prepared' Rails Paperclip 到 ActiveStorage 的迁移
undefined method `execute_prepared' Rails Paperclip to ActiveStorage migration
我正在尝试将 Rails 应用程序从使用 Paperclip 转换为 ActiveStorage,但我 运行 遇到迁移指南中提供的 ConvertToActiveStorage 迁移脚本的问题。
https://github.com/thoughtbot/paperclip/blob/master/MIGRATING.md#copy-the-database-data-over
以下是我在尝试 运行 将 Paperclip 迁移到 ActiveStorage 时遇到的错误。我不确定 ActiveRecord::Base.connection.execute_prepared
有什么问题
Paperclip 当前保存用户上传文件的路径是 /web/non-public/system/articles/documents/000/000/
,ActiveStorage 应该在迁移后将它们保存到 /web/storage
的新位置。
== 20190123165105 ConvertToActiveStorage: migrating ===========================
-- transaction()
rails aborted!
StandardError: An error has occurred, this and all later migrations canceled:
undefined method `execute_prepared' for #<ActiveRecord::ConnectionAdapters::PostgreSQLAdapter:0x007fd7e9211128>
Did you mean? exec_delete
/web/db/migrate/20190123165105_convert_to_active_storage.rb:45:in `block (4 levels) in up'
/web/db/migrate/20190123165105_convert_to_active_storage.rb:40:in `each'
/web/db/migrate/20190123165105_convert_to_active_storage.rb:40:in `block (3 levels) in up'
/web/db/migrate/20190123165105_convert_to_active_storage.rb:39:in `each'
/web/db/migrate/20190123165105_convert_to_active_storage.rb:39:in `block (2 levels) in up'
/web/db/migrate/20190123165105_convert_to_active_storage.rb:28:in `each'
/web/db/migrate/20190123165105_convert_to_active_storage.rb:28:in `block in up'
/web/db/migrate/20190123165105_convert_to_active_storage.rb:27:in `up'
bin/rails:4:in `require'
bin/rails:4:in `<main>'
Caused by:
NoMethodError: undefined method `execute_prepared' for #<ActiveRecord::ConnectionAdapters::PostgreSQLAdapter:0x007fd7e9211128>
Did you mean? exec_delete
此处列出了指南中提供的迁移脚本。
class ConvertToActiveStorage < ActiveRecord::Migration[5.2]
require 'open-uri'
def up
# postgres
get_blob_id = 'LASTVAL()'
# mariadb
# get_blob_id = 'LAST_INSERT_ID()'
# sqlite
# get_blob_id = 'LAST_INSERT_ROWID()'
active_storage_blob_statement = ActiveRecord::Base.connection.raw_connection.prepare('active_storage_blob_statement', <<-SQL)
INSERT INTO active_storage_blobs (
key, filename, content_type, metadata, byte_size, checksum, created_at
) VALUES (, , , '{}', , , )
SQL
active_storage_attachment_statement = ActiveRecord::Base.connection.raw_connection.prepare('active_storage_attachment_statement', <<-SQL)
INSERT INTO active_storage_attachments (
name, record_type, record_id, blob_id, created_at
) VALUES (, , , #{get_blob_id}, )
SQL
Rails.application.eager_load!
models = ActiveRecord::Base.descendants.reject(&:abstract_class?)
transaction do
models.each do |model|
attachments = model.column_names.map do |c|
if c =~ /(.+)_file_name$/
end
end.compact
if attachments.blank?
next
end
model.find_each.each do |instance|
attachments.each do |attachment|
if instance.send(attachment).path.blank?
next
end
ActiveRecord::Base.connection.execute_prepared(
'active_storage_blob_statement', [
key(instance, attachment),
instance.send("#{attachment}_file_name"),
instance.send("#{attachment}_content_type"),
instance.send("#{attachment}_file_size"),
checksum(instance.send(attachment)),
instance.updated_at.iso8601
])
ActiveRecord::Base.connection.execute_prepared(
'active_storage_attachment_statement', [
attachment,
model.name,
instance.id,
instance.updated_at.iso8601,
])
end
end
end
end
end
def down
raise ActiveRecord::IrreversibleMigration
end
private
def key(instance, attachment)
SecureRandom.uuid
# Alternatively:
# instance.send("#{attachment}_file_name")
# filename = instance.send("#{attachment}_file_name")
# klass = instance.class.table_name
# id = instance.id
# id_partition = ("%09d".freeze % id).scan(/\d{3}/).join("/".freeze)
# "#{klass}/#{attachment.pluralize}/#{id_partition}/original/#{filename}"
end
def checksum(attachment)
# local files stored on disk:
url = attachment.path
Digest::MD5.base64digest(File.read(url))
# remote files stored on another person's computer:
# url = attachment.url
# Digest::MD5.base64digest(Net::HTTP.get(URI(url)))
end
end
我在 运行 迁移时遇到了同样的问题。我不得不更改两个 execute_prepared
语句以使用 exec_prepared
代替:
ActiveRecord::Base.connection.raw_connection.exec_prepared(
'active_storage_blob_statement', [
...
])
ActiveRecord::Base.connection.raw_connection.exec_prepared(
'active_storage_attachment_statement', [
...
])
我不太记得为什么,但我还必须将最后插入的 blob 记录的 ID 传递给附件插入语句。如果你不需要这个,你可以跳过这个:
active_storage_attachment_statement = ActiveRecord::Base.connection.raw_connection.prepare('active_storage_attachment_statement', <<-SQL)
INSERT INTO active_storage_attachments (
name, record_type, record_id, blob_id, created_at
) VALUES (, , , #{"(SELECT max(id) from active_storage_blobs)"}, )
SQL
我正在尝试将 Rails 应用程序从使用 Paperclip 转换为 ActiveStorage,但我 运行 遇到迁移指南中提供的 ConvertToActiveStorage 迁移脚本的问题。 https://github.com/thoughtbot/paperclip/blob/master/MIGRATING.md#copy-the-database-data-over
以下是我在尝试 运行 将 Paperclip 迁移到 ActiveStorage 时遇到的错误。我不确定 ActiveRecord::Base.connection.execute_prepared
Paperclip 当前保存用户上传文件的路径是 /web/non-public/system/articles/documents/000/000/
,ActiveStorage 应该在迁移后将它们保存到 /web/storage
的新位置。
== 20190123165105 ConvertToActiveStorage: migrating ===========================
-- transaction()
rails aborted!
StandardError: An error has occurred, this and all later migrations canceled:
undefined method `execute_prepared' for #<ActiveRecord::ConnectionAdapters::PostgreSQLAdapter:0x007fd7e9211128>
Did you mean? exec_delete
/web/db/migrate/20190123165105_convert_to_active_storage.rb:45:in `block (4 levels) in up'
/web/db/migrate/20190123165105_convert_to_active_storage.rb:40:in `each'
/web/db/migrate/20190123165105_convert_to_active_storage.rb:40:in `block (3 levels) in up'
/web/db/migrate/20190123165105_convert_to_active_storage.rb:39:in `each'
/web/db/migrate/20190123165105_convert_to_active_storage.rb:39:in `block (2 levels) in up'
/web/db/migrate/20190123165105_convert_to_active_storage.rb:28:in `each'
/web/db/migrate/20190123165105_convert_to_active_storage.rb:28:in `block in up'
/web/db/migrate/20190123165105_convert_to_active_storage.rb:27:in `up'
bin/rails:4:in `require'
bin/rails:4:in `<main>'
Caused by:
NoMethodError: undefined method `execute_prepared' for #<ActiveRecord::ConnectionAdapters::PostgreSQLAdapter:0x007fd7e9211128>
Did you mean? exec_delete
此处列出了指南中提供的迁移脚本。
class ConvertToActiveStorage < ActiveRecord::Migration[5.2]
require 'open-uri'
def up
# postgres
get_blob_id = 'LASTVAL()'
# mariadb
# get_blob_id = 'LAST_INSERT_ID()'
# sqlite
# get_blob_id = 'LAST_INSERT_ROWID()'
active_storage_blob_statement = ActiveRecord::Base.connection.raw_connection.prepare('active_storage_blob_statement', <<-SQL)
INSERT INTO active_storage_blobs (
key, filename, content_type, metadata, byte_size, checksum, created_at
) VALUES (, , , '{}', , , )
SQL
active_storage_attachment_statement = ActiveRecord::Base.connection.raw_connection.prepare('active_storage_attachment_statement', <<-SQL)
INSERT INTO active_storage_attachments (
name, record_type, record_id, blob_id, created_at
) VALUES (, , , #{get_blob_id}, )
SQL
Rails.application.eager_load!
models = ActiveRecord::Base.descendants.reject(&:abstract_class?)
transaction do
models.each do |model|
attachments = model.column_names.map do |c|
if c =~ /(.+)_file_name$/
end
end.compact
if attachments.blank?
next
end
model.find_each.each do |instance|
attachments.each do |attachment|
if instance.send(attachment).path.blank?
next
end
ActiveRecord::Base.connection.execute_prepared(
'active_storage_blob_statement', [
key(instance, attachment),
instance.send("#{attachment}_file_name"),
instance.send("#{attachment}_content_type"),
instance.send("#{attachment}_file_size"),
checksum(instance.send(attachment)),
instance.updated_at.iso8601
])
ActiveRecord::Base.connection.execute_prepared(
'active_storage_attachment_statement', [
attachment,
model.name,
instance.id,
instance.updated_at.iso8601,
])
end
end
end
end
end
def down
raise ActiveRecord::IrreversibleMigration
end
private
def key(instance, attachment)
SecureRandom.uuid
# Alternatively:
# instance.send("#{attachment}_file_name")
# filename = instance.send("#{attachment}_file_name")
# klass = instance.class.table_name
# id = instance.id
# id_partition = ("%09d".freeze % id).scan(/\d{3}/).join("/".freeze)
# "#{klass}/#{attachment.pluralize}/#{id_partition}/original/#{filename}"
end
def checksum(attachment)
# local files stored on disk:
url = attachment.path
Digest::MD5.base64digest(File.read(url))
# remote files stored on another person's computer:
# url = attachment.url
# Digest::MD5.base64digest(Net::HTTP.get(URI(url)))
end
end
我在 运行 迁移时遇到了同样的问题。我不得不更改两个 execute_prepared
语句以使用 exec_prepared
代替:
ActiveRecord::Base.connection.raw_connection.exec_prepared(
'active_storage_blob_statement', [
...
])
ActiveRecord::Base.connection.raw_connection.exec_prepared(
'active_storage_attachment_statement', [
...
])
我不太记得为什么,但我还必须将最后插入的 blob 记录的 ID 传递给附件插入语句。如果你不需要这个,你可以跳过这个:
active_storage_attachment_statement = ActiveRecord::Base.connection.raw_connection.prepare('active_storage_attachment_statement', <<-SQL)
INSERT INTO active_storage_attachments (
name, record_type, record_id, blob_id, created_at
) VALUES (, , , #{"(SELECT max(id) from active_storage_blobs)"}, )
SQL