Рельсы 5 + Храм + Полиморфная модель + Симпатичное расположение
Я использую Shrine для прямой загрузки на S3 и пытаюсь использовать pretty_location
Плагин, чтобы установить местоположение в моем S3 ведро.
У меня есть модель документа имеет file_data
текстовый атрибут и подключен к FileUploader:
class Document < ApplicationRecord
belongs_to :documentable, polymorphic: true
include FileUploader[:file]
validates :file, presence: true
end
Другие модели связаны с моделью документа по следующим причинам:
module Documentable
extend ActiveSupport::Concern
included do
has_one :document, as: :documentable, dependent: :destroy
accepts_nested_attributes_for :document, allow_destroy: true
end
end
Это мой FileUploader:
class FileUploader < Shrine
Attacher.promote { |data| PromoteJob.perform_later(data) }
Attacher.delete { |data| DeleteJob.perform_later(data) }
plugin :upload_options, cache: {acl: "public-read"}
plugin :upload_options, store: {acl: "public-read"}
plugin :logging, logger: Rails.logger
plugin :pretty_location
plugin :processing
plugin :delete_promoted
plugin :recache
plugin :restore_cached_data
plugin :delete_raw
plugin :validation_helpers
def generate_location(io, context = {})
# do something depending on context[:record].documentable
end
end
При загрузке файлов из файловой системы пользователя через клиентский браузер через вложенные атрибуты все работает, как и ожидалось, и я могу создать красивое место в моем контейнере S3.
Однако у меня есть другая модель, в которой я пытаюсь загрузить на S3 файл PDF, который создается в бэкэнде со следующей настройкой.
class Invoice < ApplicationRecord
has_one :documents, as: :documentable, dependent: :destroy
end
Модель Invoice не использует проблему, так как я хочу, чтобы она подключалась к полиморфному документу с has_many
ассоциация.
class Api::V1::InvoicesController < Api::V1::BaseController
def upload_pdf
pdf = InvoicePdf.new(@invoice)
attachment = pdf.render
file = StringIO.new(attachment)
file.class.class_eval { attr_accessor :original_filename, :content_type }
file.original_filename = "invoice_#{@invoice.reference}.pdf"
file.content_type = "application/pdf"
@document = @invoice.documents.new(file: file)
if @document.save
render "documents/show.json", status: :created
else
render json: { errors: @document.errors }, status: :unprocessable_entity
end
end
end
Загрузка работает нормально, и я могу загрузить PDF в свою корзину S3, но я не могу создать красивое местоположение, потому что, когда я нахожусь внутри generate_location
метод context[:record]
оба documentable_type
и documentable_id
являются nil
,
Это странное поведение, так как в консоли rails я вижу, что ассоциация правильно установлена после загрузки (без pretty_location), выполнив Invoice.last.documents.file.url
,
Я пытался создать запись документа по-разному, пытался использовать тот же documentable
беспокойство, которое работает для других моделей, но результат всегда тот же, и у меня закончились идеи.
Кто-нибудь знает, почему documentable_type
а также documentable_id
не передаются в context
объект внутри FileUploader?
1 ответ
Вышеуказанная настройка на самом деле работает. Я использовал точку останова внутри generate_location
Метод FileUploader и API ломались, потому что этот метод возвращал ноль.
После исправления этого, первый раз он запустился documentable
было все еще ноль, но метод будет работать во второй раз с documentable
атрибуты присутствуют.
def generate_location(io, context = {})
return "" unless context[:record].documentable
path = if context[:record].documentable_type == "SomeClass"
# do something
elsif context[:record].documentable_type == "OtherClass"
# do something else
else
# do something else
end
return path
end