Rails 5.2 ActiveStorage с UUID на Postgresql

У нас есть наше приложение, использующее первичные ключи uuid в базе данных Postgresql. (Стандартная установка описана здесь).

Мы интегрировали 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"> 

Ищем объяснение этому странному поведению, а также возможное решение.


person anurag    schedule 26.07.2018    source источник


Ответы (2)


После нескольких часов построчного просмотра исходного кода активного хранилища и запуска одного и того же команды

@message = Message.new(message_params)
@message.save

опять и опять. Мы снова и снова получали одни и те же случайные результаты. Затем мы просмотрели рельсы журналов, напечатанные при прикреплении изображения к сообщению, и заметили следующее:

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 ($1, $2, $3, $4, $5, $6, $7) 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 ($1, $2, $3, $4, $5) 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. Вот где мы ошиблись.

Активное хранилище ожидало целочисленный внешний ключ для нашей модели сообщений, и мы хотели, чтобы вместо него использовались uuid. Поэтому нам пришлось исправить нашу миграцию, чтобы использовать uuid вместо целочисленных внешних ключей.

Решение:

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

Надеюсь, это поможет, кто-то сталкивается с подобными проблемами. ваше здоровье!

person anurag    schedule 26.07.2018
comment
Привет, Анураг. У меня такая же проблема. Проблема в том, что у меня есть одна модель, использующая целое число в качестве идентификатора, и еще одна модель, использующая uuid в качестве идентификатора. Обе модели с прикрепленными файлами. Таким образом, активное хранилище не может управлять обоими типами в поле record_id в active_storage_attachments. Это плохое ограничение Active Storage. Мне нужно преобразовать мои 2 модели с идентификатором как uuid. - person alex.bour; 13.12.2018
comment
У нас есть одна модель документа с has_one attachment, и мы используем ее для прикрепления любого документа через foreign_key к разным моделям вместо того, чтобы иметь вложения в моделях. Таким образом, мы ограничили урон только одной моделью, и это сработало без проблем. Хотя я открыт для альтернативных реализаций. Дай мне знать. - person anurag; 13.12.2018
comment
Спасибо за Ваш ответ. На самом деле у меня есть 2 модели с has_one_attachment (изображения и фотографии), и я не хочу их объединять, так как они не владеют одними и теми же полями. Ну, это не проблема, наконец, я меняю тип идентификатора как для UUID, так и просто меняю тип record_it на UUID в active_storage_attachments. - person alex.bour; 13.12.2018
comment
Если вы уже применили миграцию по умолчанию, вот некоторые миграции, которые вы можете применить, чтобы исправить таблица вложений постфактум. - person Kyle Fox; 04.02.2019
comment
ты спасаешь жизнь - person Welp; 07.08.2020
comment
ты спас жизнь - person Chris Hough; 09.10.2020

Я опоздал на вечеринку, посвященную этому в 2020 году, но, как упомянул Анураг, это связано с тем, что таблица БД active_storage_attachments использует bigint для record_id. Я не смог перенести все модели с вложениями ActiveStorage для использования UUID, поэтому мне нужен был способ одновременной поддержки как UUID, так и bigint.

Предупреждение: если вы можете избежать этого (скорее всего, переведя все на UUID), то я настоятельно рекомендую это сделать, и я планирую сделать это, как только у нас будет время.

Помимо предупреждений, миграция таблицы active_storage_attachments для изменения столбца record_id на text действительно работает. Мне пришлось настроить несколько мест в нашем приложении, где мы объединялись с таблицей active_storage_attachments, используя record_id для приведения значения к тексту в объединении. Например, я использовал следующий код при присоединении к модели с идентификатором UUID.

      .joins("
         LEFT OUTER JOIN active_storage_attachments
         ON active_storage_attachments.record_id = documents.id::text
       ")

Надеюсь, это поможет кому-то еще, кто застрял на полпути, когда не все модели, использующие ActiveStorage, используют UUID или идентификаторы bigint.

person Haegin    schedule 30.09.2020