Rails 5.2 在 Postgresql 中使用 UUID 的活动存储
Rails 5.2 ActiveStorage with UUIDs on Postgresql
我们的应用程序使用 uuid 作为主键,在 Postgresql 数据库上。 (标准设置描述 here)。
我们按照描述的过程 here 集成了 ActiveStorage。使用 rails active_storage:install
的标准设置并使用 rails db:migrate
迁移。
我们有一个模型&对应的控制器如下:
# Model
class Message < ApplicationRecord
has_one_attached :image
def filename
image&.attachment&.blob&.filename
end
end
# Controller
class MessagesController < ApplicationController
def create
message = Message.create!(message_params)
redirect_to message
end
private
def message_params
params.require(:message).permit(:title, :content, :image)
end
end
我们观察到前几组图像与模型实例正确关联,但后来我们常常为模型实例获取随机图像,或者根本没有图像。每次,我们重新启动服务器,我们都能正确获得前几张图像,但后来就不可预测了。
不确定出了什么问题,我们在 rails 控制台中调试:
params[:image]
=> #<ActionDispatch::Http::UploadedFile:0x007fcf2fa97b70 @tempfile=#<Tempfile:/var/folders/dt/05ncjr6s52ggc4bk6fs521qw0000gn/T/RackMultipart20180726-8503-vg36kz.pdf>, @original_filename="sample.pdf", @content_type="application/pdf", @headers="Content-Disposition: form-data; name=\"file\"; filename=\"sample.pdf\"\r\nContent-Type: application/pdf\r\n">
在保存实例和检索文件名时,我们得到了一个随机文件,我们之前上传过。
@message = Message.new(message_params)
@message.filename
=> #<ActiveStorage::Filename:0x007fcf32cfd9e8 @filename="sample.pdf">
@message.save
@message.filename
=> #<ActiveStorage::Filename:0x007f82f2ad4ef0 @filename="OtherSamplePdf.pdf">
寻找这种奇怪行为的解释,以及可能的解决方案。
在 activestorage 中逐行运行数小时后 source code,和 运行 相同的命令
@message = Message.new(message_params)
@message.save
一次又一次。我们一次又一次地得到相同的随机结果。然后我们查看了将图像附加到消息时打印的日志 rails,并观察到以下内容:
S3 Storage (363.4ms) Uploaded file to key: KBKeHJARTjnsVjkgSbbii4Bz (checksum: S0GjR1EyvYYbMKh44wqlag==)
ActiveStorage::Blob Create (0.4ms) INSERT INTO "active_storage_blobs" ("key", "filename", "content_type", "metadata", "byte_size", "checksum", "created_at") VALUES (, , , , , , ) RETURNING "id" [["key", "KBKeHJARTjnsVjkgSbbii4Bz"], ["filename", "sample.pdf"], ["content_type", "application/pdf"], ["metadata", "{\"identified\":true}"], ["byte_size", 3028], ["checksum", "S0GjR1EyvYYbMKh44wqlag=="], ["created_at", "2018-07-26 04:54:33.029769"]]
ActiveStorage::Attachment Create (2.7ms) INSERT INTO "active_storage_attachments" ("name", "record_type", "record_id", "blob_id", "created_at") VALUES (, , , , ) RETURNING "id" [["name", "file"], ["record_type", "Message"], ["record_id", "534736"], ["blob_id", "0"], ["created_at", "2018-07-26 05:04:35.958831"]]
record_id
被设置为 534736
,而不是 uuid。这就是我们出错的地方。
活动存储需要我们的消息模型的整数外键,而我们希望它使用 uuids 代替。所以我们必须修复我们的迁移,使用 uuids 而不是整数外键。
解决方案:
class CreateActiveStorageTables < ActiveRecord::Migration[5.2]
def change
create_table :active_storage_blobs, id: :uuid do |t|
t.string :key, null: false
t.string :filename, null: false
t.string :content_type
t.text :metadata
t.bigint :byte_size, null: false
t.string :checksum, null: false
t.datetime :created_at, null: false
t.index [ :key ], unique: true
end
create_table :active_storage_attachments, id: :uuid do |t|
t.string :name, null: false
t.references :record, null: false, polymorphic: true, index: false, type: :uuid
t.references :blob, null: false, type: :uuid
t.datetime :created_at, null: false
t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
end
end
end
希望这对面临类似问题的人有所帮助。干杯!
我在 2020 年的派对上迟到了,但正如 anurag 提到的那样,这是由于 active_storage_attachments
数据库 table 使用了 record_id
的 bigint。我无法迁移所有带有 ActiveStorage 附件的模型以使用 UUID,因此我需要一种同时支持 UUID 和 bigints 的方法。
警告:如果您可以避免这种情况(最有可能通过将所有内容迁移到 UUID),那么我强烈建议您这样做,并且我计划在我们有时间时尽快这样做。
抛开警告,迁移 active_storage_attachments table 以将 record_id 列更改为 text
确实有效。我不得不调整我们的应用程序中我们要加入的几个地方 active_storage_attachments
table 使用 record_id
将值转换为加入中的文本。例如,我在加入具有 UUID ID 的模型时使用了以下代码。
.joins("
LEFT OUTER JOIN active_storage_attachments
ON active_storage_attachments.record_id = documents.id::text
")
希望这能帮助其他陷入半途状态的人,因为并非所有使用模型的 ActiveStorage 都使用 UUID 或 bigint ID。
我也有这个问题。我的模型都使用 UUID。因为我在 ActiveStorage 中没有需要保留的记录,所以我删除并重新创建了 :active_storage_attachments 和 :active_storage_blobs 表。这是我的迁移,以防对任何人有用。使用 Rails 6.0.4.
def change
reversible do |dir|
dir.up do
drop_table :active_storage_attachments
drop_table :active_storage_blobs
create_table "active_storage_blobs", id: :uuid,
default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string :key, null: false
t.string :filename, null: false
t.string :content_type
t.text :metadata
t.bigint :byte_size, null: false
t.string :checksum, null: false
t.datetime :created_at, null: false
t.index [ :key ], unique: true
end
create_table "active_storage_attachments", id: :uuid,
default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string :name, null: false
t.references :record, null: false, polymorphic: true, index: false, type: :uuid
t.references :blob, null: false, type: :uuid
t.datetime :created_at, null: false
t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
t.foreign_key :active_storage_blobs, column: :blob_id
end
end
dir.down do
drop_table :active_storage_attachments
drop_table :active_storage_blobs
# original tables generated by rails
create_table :active_storage_blobs do |t|
t.string :key, null: false
t.string :filename, null: false
t.string :content_type
t.text :metadata
t.bigint :byte_size, null: false
t.string :checksum, null: false
t.datetime :created_at, null: false
t.index [ :key ], unique: true
end
create_table :active_storage_attachments do |t|
t.string :name, null: false
t.references :record, null: false, polymorphic: true, index: false
t.references :blob, null: false
t.datetime :created_at, null: false
t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
t.foreign_key :active_storage_blobs, column: :blob_id
end
end
end
end
我们的应用程序使用 uuid 作为主键,在 Postgresql 数据库上。 (标准设置描述 here)。
我们按照描述的过程 here 集成了 ActiveStorage。使用 rails active_storage:install
的标准设置并使用 rails db:migrate
迁移。
我们有一个模型&对应的控制器如下:
# Model
class Message < ApplicationRecord
has_one_attached :image
def filename
image&.attachment&.blob&.filename
end
end
# Controller
class MessagesController < ApplicationController
def create
message = Message.create!(message_params)
redirect_to message
end
private
def message_params
params.require(:message).permit(:title, :content, :image)
end
end
我们观察到前几组图像与模型实例正确关联,但后来我们常常为模型实例获取随机图像,或者根本没有图像。每次,我们重新启动服务器,我们都能正确获得前几张图像,但后来就不可预测了。
不确定出了什么问题,我们在 rails 控制台中调试:
params[:image]
=> #<ActionDispatch::Http::UploadedFile:0x007fcf2fa97b70 @tempfile=#<Tempfile:/var/folders/dt/05ncjr6s52ggc4bk6fs521qw0000gn/T/RackMultipart20180726-8503-vg36kz.pdf>, @original_filename="sample.pdf", @content_type="application/pdf", @headers="Content-Disposition: form-data; name=\"file\"; filename=\"sample.pdf\"\r\nContent-Type: application/pdf\r\n">
在保存实例和检索文件名时,我们得到了一个随机文件,我们之前上传过。
@message = Message.new(message_params)
@message.filename
=> #<ActiveStorage::Filename:0x007fcf32cfd9e8 @filename="sample.pdf">
@message.save
@message.filename
=> #<ActiveStorage::Filename:0x007f82f2ad4ef0 @filename="OtherSamplePdf.pdf">
寻找这种奇怪行为的解释,以及可能的解决方案。
在 activestorage 中逐行运行数小时后 source code,和 运行 相同的命令
@message = Message.new(message_params)
@message.save
一次又一次。我们一次又一次地得到相同的随机结果。然后我们查看了将图像附加到消息时打印的日志 rails,并观察到以下内容:
S3 Storage (363.4ms) Uploaded file to key: KBKeHJARTjnsVjkgSbbii4Bz (checksum: S0GjR1EyvYYbMKh44wqlag==)
ActiveStorage::Blob Create (0.4ms) INSERT INTO "active_storage_blobs" ("key", "filename", "content_type", "metadata", "byte_size", "checksum", "created_at") VALUES (, , , , , , ) RETURNING "id" [["key", "KBKeHJARTjnsVjkgSbbii4Bz"], ["filename", "sample.pdf"], ["content_type", "application/pdf"], ["metadata", "{\"identified\":true}"], ["byte_size", 3028], ["checksum", "S0GjR1EyvYYbMKh44wqlag=="], ["created_at", "2018-07-26 04:54:33.029769"]]
ActiveStorage::Attachment Create (2.7ms) INSERT INTO "active_storage_attachments" ("name", "record_type", "record_id", "blob_id", "created_at") VALUES (, , , , ) RETURNING "id" [["name", "file"], ["record_type", "Message"], ["record_id", "534736"], ["blob_id", "0"], ["created_at", "2018-07-26 05:04:35.958831"]]
record_id
被设置为 534736
,而不是 uuid。这就是我们出错的地方。
活动存储需要我们的消息模型的整数外键,而我们希望它使用 uuids 代替。所以我们必须修复我们的迁移,使用 uuids 而不是整数外键。
解决方案:
class CreateActiveStorageTables < ActiveRecord::Migration[5.2]
def change
create_table :active_storage_blobs, id: :uuid do |t|
t.string :key, null: false
t.string :filename, null: false
t.string :content_type
t.text :metadata
t.bigint :byte_size, null: false
t.string :checksum, null: false
t.datetime :created_at, null: false
t.index [ :key ], unique: true
end
create_table :active_storage_attachments, id: :uuid do |t|
t.string :name, null: false
t.references :record, null: false, polymorphic: true, index: false, type: :uuid
t.references :blob, null: false, type: :uuid
t.datetime :created_at, null: false
t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
end
end
end
希望这对面临类似问题的人有所帮助。干杯!
我在 2020 年的派对上迟到了,但正如 anurag 提到的那样,这是由于 active_storage_attachments
数据库 table 使用了 record_id
的 bigint。我无法迁移所有带有 ActiveStorage 附件的模型以使用 UUID,因此我需要一种同时支持 UUID 和 bigints 的方法。
警告:如果您可以避免这种情况(最有可能通过将所有内容迁移到 UUID),那么我强烈建议您这样做,并且我计划在我们有时间时尽快这样做。
抛开警告,迁移 active_storage_attachments table 以将 record_id 列更改为 text
确实有效。我不得不调整我们的应用程序中我们要加入的几个地方 active_storage_attachments
table 使用 record_id
将值转换为加入中的文本。例如,我在加入具有 UUID ID 的模型时使用了以下代码。
.joins("
LEFT OUTER JOIN active_storage_attachments
ON active_storage_attachments.record_id = documents.id::text
")
希望这能帮助其他陷入半途状态的人,因为并非所有使用模型的 ActiveStorage 都使用 UUID 或 bigint ID。
我也有这个问题。我的模型都使用 UUID。因为我在 ActiveStorage 中没有需要保留的记录,所以我删除并重新创建了 :active_storage_attachments 和 :active_storage_blobs 表。这是我的迁移,以防对任何人有用。使用 Rails 6.0.4.
def change
reversible do |dir|
dir.up do
drop_table :active_storage_attachments
drop_table :active_storage_blobs
create_table "active_storage_blobs", id: :uuid,
default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string :key, null: false
t.string :filename, null: false
t.string :content_type
t.text :metadata
t.bigint :byte_size, null: false
t.string :checksum, null: false
t.datetime :created_at, null: false
t.index [ :key ], unique: true
end
create_table "active_storage_attachments", id: :uuid,
default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string :name, null: false
t.references :record, null: false, polymorphic: true, index: false, type: :uuid
t.references :blob, null: false, type: :uuid
t.datetime :created_at, null: false
t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
t.foreign_key :active_storage_blobs, column: :blob_id
end
end
dir.down do
drop_table :active_storage_attachments
drop_table :active_storage_blobs
# original tables generated by rails
create_table :active_storage_blobs do |t|
t.string :key, null: false
t.string :filename, null: false
t.string :content_type
t.text :metadata
t.bigint :byte_size, null: false
t.string :checksum, null: false
t.datetime :created_at, null: false
t.index [ :key ], unique: true
end
create_table :active_storage_attachments do |t|
t.string :name, null: false
t.references :record, null: false, polymorphic: true, index: false
t.references :blob, null: false
t.datetime :created_at, null: false
t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
t.foreign_key :active_storage_blobs, column: :blob_id
end
end
end
end